Skip to content

Commit 23c3bfe

Browse files
committed
Add tests
1 parent db139cc commit 23c3bfe

File tree

3 files changed

+248
-6
lines changed

3 files changed

+248
-6
lines changed

internal/project/project.go

+19-5
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,15 @@ func (p *Project) updateWatchers() {
270270
}
271271
}
272272

273+
func (p *Project) onWatchedFileCreated(fileName string) {
274+
if p.kind == KindConfigured {
275+
if p.rootFileNames.Has(p.toPath(fileName)) || p.parsedCommandLine.MatchesFileName(fileName, p.comparePathsOptions) {
276+
p.pendingConfigReload = true
277+
p.markAsDirty()
278+
}
279+
}
280+
}
281+
273282
func (p *Project) getOrCreateScriptInfoAndAttachToProject(fileName string, scriptKind core.ScriptKind) *ScriptInfo {
274283
if scriptInfo := p.host.GetOrCreateScriptInfoForFile(fileName, p.toPath(fileName), scriptKind); scriptInfo != nil {
275284
scriptInfo.attachToProject(p)
@@ -467,16 +476,21 @@ func (p *Project) setRootFiles(rootFileNames []string) {
467476
newRootScriptInfos := make(map[tspath.Path]struct{}, len(rootFileNames))
468477
for _, file := range rootFileNames {
469478
scriptKind := p.getScriptKind(file)
470-
scriptInfo := p.host.GetOrCreateScriptInfoForFile(file, p.toPath(file), scriptKind)
471-
newRootScriptInfos[scriptInfo.path] = struct{}{}
472-
if _, isRoot := p.rootFileNames.Get(scriptInfo.path); !isRoot {
479+
path := p.toPath(file)
480+
// !!! updateNonInferredProjectFiles uses a fileExists check, which I guess
481+
// could be needed if a watcher fails?
482+
scriptInfo := p.host.GetOrCreateScriptInfoForFile(file, path, scriptKind)
483+
newRootScriptInfos[path] = struct{}{}
484+
isAlreadyRoot := p.rootFileNames.Has(path)
485+
486+
if !isAlreadyRoot && scriptInfo != nil {
473487
p.addRoot(scriptInfo)
474488
if scriptInfo.isOpen {
475489
// !!!
476490
// s.removeRootOfInferredProjectIfNowPartOfOtherProject(scriptInfo)
477491
}
478-
} else {
479-
p.rootFileNames.Set(scriptInfo.path, file)
492+
} else if !isAlreadyRoot {
493+
p.rootFileNames.Set(path, file)
480494
}
481495
}
482496

internal/project/service.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,12 @@ func (s *Service) OnWatchedFilesChanged(changes []lsproto.FileEvent) error {
255255
// !!! s.handleSourceMapProjects(info)
256256
}
257257
} else {
258-
// must be wildcard watcher?
258+
if change.Type != lsproto.FileChangeTypeCreated {
259+
panic("unexpected file change type")
260+
}
261+
for _, project := range s.configuredProjects {
262+
project.onWatchedFileCreated(fileName)
263+
}
259264
}
260265
}
261266

internal/project/service_test.go

+223
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/microsoft/typescript-go/internal/bundled"
1212
"github.com/microsoft/typescript-go/internal/core"
1313
"github.com/microsoft/typescript-go/internal/ls"
14+
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
1415
"github.com/microsoft/typescript-go/internal/project"
1516
"github.com/microsoft/typescript-go/internal/vfs"
1617
"github.com/microsoft/typescript-go/internal/vfs/vfstest"
@@ -221,6 +222,228 @@ func TestService(t *testing.T) {
221222
assert.Assert(t, x1 != x2)
222223
})
223224
})
225+
226+
t.Run("Watch", func(t *testing.T) {
227+
t.Parallel()
228+
229+
t.Run("change open file", func(t *testing.T) {
230+
t.Parallel()
231+
service, host := setup(files)
232+
service.OpenFile("/home/projects/TS/p1/src/x.ts", files["/home/projects/TS/p1/src/x.ts"], core.ScriptKindTS, "")
233+
service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"], core.ScriptKindTS, "")
234+
_, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts")
235+
programBefore := project.GetProgram()
236+
237+
filesCopy := maps.Clone(files)
238+
filesCopy["/home/projects/TS/p1/src/x.ts"] = `export const x = 2;`
239+
host.replaceFS(filesCopy)
240+
service.OnWatchedFilesChanged([]lsproto.FileEvent{
241+
{
242+
Type: lsproto.FileChangeTypeChanged,
243+
Uri: "file:///home/projects/TS/p1/src/x.ts",
244+
},
245+
})
246+
247+
assert.Equal(t, programBefore, project.GetProgram())
248+
})
249+
250+
t.Run("change closed program file", func(t *testing.T) {
251+
t.Parallel()
252+
service, host := setup(files)
253+
service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"], core.ScriptKindTS, "")
254+
_, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts")
255+
programBefore := project.GetProgram()
256+
257+
filesCopy := maps.Clone(files)
258+
filesCopy["/home/projects/TS/p1/src/x.ts"] = `export const x = 2;`
259+
host.replaceFS(filesCopy)
260+
service.OnWatchedFilesChanged([]lsproto.FileEvent{
261+
{
262+
Type: lsproto.FileChangeTypeChanged,
263+
Uri: "file:///home/projects/TS/p1/src/x.ts",
264+
},
265+
})
266+
267+
assert.Check(t, project.GetProgram() != programBefore)
268+
})
269+
270+
t.Run("change config file", func(t *testing.T) {
271+
t.Parallel()
272+
files := map[string]string{
273+
"/home/projects/TS/p1/tsconfig.json": `{
274+
"compilerOptions": {
275+
"noLib": true,
276+
"strict": false
277+
}
278+
}`,
279+
"/home/projects/TS/p1/src/x.ts": `export declare const x: number | undefined;`,
280+
"/home/projects/TS/p1/src/index.ts": `
281+
import { x } from "./x";
282+
let y: number = x;`,
283+
}
284+
285+
service, host := setup(files)
286+
service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"], core.ScriptKindTS, "")
287+
_, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts")
288+
program := project.GetProgram()
289+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 0)
290+
291+
filesCopy := maps.Clone(files)
292+
filesCopy["/home/projects/TS/p1/tsconfig.json"] = `{
293+
"compilerOptions": {
294+
"noLib": false,
295+
"strict": true
296+
}
297+
}`
298+
host.replaceFS(filesCopy)
299+
service.OnWatchedFilesChanged([]lsproto.FileEvent{
300+
{
301+
Type: lsproto.FileChangeTypeChanged,
302+
Uri: "file:///home/projects/TS/p1/tsconfig.json",
303+
},
304+
})
305+
306+
program = project.GetProgram()
307+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 1)
308+
})
309+
310+
t.Run("delete explicitly included file", func(t *testing.T) {
311+
t.Parallel()
312+
files := map[string]string{
313+
"/home/projects/TS/p1/tsconfig.json": `{
314+
"compilerOptions": {
315+
"noLib": true,
316+
},
317+
"files": ["src/index.ts", "src/x.ts"]
318+
}`,
319+
"/home/projects/TS/p1/src/x.ts": `export declare const x: number | undefined;`,
320+
"/home/projects/TS/p1/src/index.ts": `import { x } from "./x";`,
321+
}
322+
service, host := setup(files)
323+
service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"], core.ScriptKindTS, "")
324+
_, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts")
325+
program := project.GetProgram()
326+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 0)
327+
328+
filesCopy := maps.Clone(files)
329+
delete(filesCopy, "/home/projects/TS/p1/src/x.ts")
330+
host.replaceFS(filesCopy)
331+
service.OnWatchedFilesChanged([]lsproto.FileEvent{
332+
{
333+
Type: lsproto.FileChangeTypeDeleted,
334+
Uri: "file:///home/projects/TS/p1/src/x.ts",
335+
},
336+
})
337+
338+
program = project.GetProgram()
339+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 1)
340+
assert.Check(t, program.GetSourceFile("/home/projects/TS/p1/src/x.ts") == nil)
341+
})
342+
343+
t.Run("delete wildcard included file", func(t *testing.T) {
344+
t.Parallel()
345+
files := map[string]string{
346+
"/home/projects/TS/p1/tsconfig.json": `{
347+
"compilerOptions": {
348+
"noLib": true
349+
},
350+
"include": ["src"]
351+
}`,
352+
"/home/projects/TS/p1/src/index.ts": `let x = 2;`,
353+
"/home/projects/TS/p1/src/x.ts": `let y = x;`,
354+
}
355+
service, host := setup(files)
356+
service.OpenFile("/home/projects/TS/p1/src/x.ts", files["/home/projects/TS/p1/src/x.ts"], core.ScriptKindTS, "")
357+
_, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/x.ts")
358+
program := project.GetProgram()
359+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/x.ts"))), 0)
360+
361+
filesCopy := maps.Clone(files)
362+
delete(filesCopy, "/home/projects/TS/p1/src/index.ts")
363+
host.replaceFS(filesCopy)
364+
service.OnWatchedFilesChanged([]lsproto.FileEvent{
365+
{
366+
Type: lsproto.FileChangeTypeDeleted,
367+
Uri: "file:///home/projects/TS/p1/src/index.ts",
368+
},
369+
})
370+
371+
program = project.GetProgram()
372+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/x.ts"))), 1)
373+
})
374+
375+
t.Run("create explicitly included file", func(t *testing.T) {
376+
t.Parallel()
377+
files := map[string]string{
378+
"/home/projects/TS/p1/tsconfig.json": `{
379+
"compilerOptions": {
380+
"noLib": true
381+
},
382+
"files": ["src/index.ts", "src/y.ts"]
383+
}`,
384+
"/home/projects/TS/p1/src/index.ts": `import { y } from "./y";`,
385+
}
386+
service, host := setup(files)
387+
service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"], core.ScriptKindTS, "")
388+
_, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts")
389+
program := project.GetProgram()
390+
391+
// Initially should have an error because y.ts is missing
392+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 1)
393+
394+
// Add the missing file
395+
filesCopy := maps.Clone(files)
396+
filesCopy["/home/projects/TS/p1/src/y.ts"] = `export const y = 1;`
397+
host.replaceFS(filesCopy)
398+
service.OnWatchedFilesChanged([]lsproto.FileEvent{
399+
{
400+
Type: lsproto.FileChangeTypeCreated,
401+
Uri: "file:///home/projects/TS/p1/src/y.ts",
402+
},
403+
})
404+
405+
// Error should be resolved
406+
program = project.GetProgram()
407+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 0)
408+
assert.Check(t, program.GetSourceFile("/home/projects/TS/p1/src/y.ts") != nil)
409+
})
410+
411+
t.Run("create wildcard included file", func(t *testing.T) {
412+
t.Parallel()
413+
files := map[string]string{
414+
"/home/projects/TS/p1/tsconfig.json": `{
415+
"compilerOptions": {
416+
"noLib": true
417+
},
418+
"include": ["src"]
419+
}`,
420+
"/home/projects/TS/p1/src/index.ts": `import { z } from "./z";`,
421+
}
422+
service, host := setup(files)
423+
service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"], core.ScriptKindTS, "")
424+
_, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts")
425+
program := project.GetProgram()
426+
427+
// Initially should have an error because z.ts is missing
428+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 1)
429+
430+
// Add a new file through wildcard inclusion
431+
filesCopy := maps.Clone(files)
432+
filesCopy["/home/projects/TS/p1/src/z.ts"] = `export const z = 1;`
433+
host.replaceFS(filesCopy)
434+
service.OnWatchedFilesChanged([]lsproto.FileEvent{
435+
{
436+
Type: lsproto.FileChangeTypeCreated,
437+
Uri: "file:///home/projects/TS/p1/src/z.ts",
438+
},
439+
})
440+
441+
// Error should be resolved and the new file should be included in the program
442+
program = project.GetProgram()
443+
assert.Equal(t, len(program.GetSemanticDiagnostics(program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 0)
444+
assert.Check(t, program.GetSourceFile("/home/projects/TS/p1/src/z.ts") != nil)
445+
})
446+
})
224447
}
225448

226449
func setup(files map[string]string) (*project.Service, *projectServiceHost) {

0 commit comments

Comments
 (0)