Skip to content

Commit 1feadc4

Browse files
committed
Add tests
1 parent 44c8935 commit 1feadc4

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

0 commit comments

Comments
 (0)