-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Turn requestfilesystem into a single layer providing project.FileHandle lookups so it can be used as the top layer above editor overlays #64263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
48c16ed
6fabb24
6d1a6f2
4fc5311
c68004a
35e51c0
eedfd0d
b31161f
b81b437
949733f
70c35f2
792d0f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,72 +1,102 @@ | ||||||||||||||
| package requestfilesystem | ||||||||||||||
|
|
||||||||||||||
| import ( | ||||||||||||||
| "github.com/microsoft/TypeScript/tsc/internal/ls/lsconv" | ||||||||||||||
| "cmp" | ||||||||||||||
| "slices" | ||||||||||||||
|
|
||||||||||||||
| "github.com/microsoft/TypeScript/tsc/internal/project" | ||||||||||||||
| "github.com/microsoft/TypeScript/tsc/internal/tspath" | ||||||||||||||
| "github.com/microsoft/TypeScript/tsc/internal/vfs" | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| func addFileChanges(summary *project.FileChangeSummary, request *RequestFileSystem, baseFS vfs.FS, currentDirectory string) { | ||||||||||||||
| func getFileSourceLayerChanges( | ||||||||||||||
| request *RequestFileSystem, | ||||||||||||||
| base *requestFileSystem, | ||||||||||||||
| currentDirectory string, | ||||||||||||||
| useCaseSensitiveNames bool, | ||||||||||||||
| ) project.FileSourceLayerChanges { | ||||||||||||||
| if base != nil { | ||||||||||||||
| useCaseSensitiveNames = base.useCaseSensitiveNames | ||||||||||||||
| } | ||||||||||||||
| toPath := func(fileName string) tspath.Path { | ||||||||||||||
| return tspath.ToPath(fileName, currentDirectory, baseFS.UseCaseSensitiveFileNames()) | ||||||||||||||
| return tspath.ToPath(fileName, currentDirectory, useCaseSensitiveNames) | ||||||||||||||
| } | ||||||||||||||
| baseRequestFS := getRequestFileSystem(baseFS) | ||||||||||||||
| addChange := func(fileName string, deleted bool) { | ||||||||||||||
| uri := lsconv.FileNameToDocumentURI(fileName) | ||||||||||||||
| if deleted { | ||||||||||||||
| if baseFS.FileExists(fileName) || baseFS.DirectoryExists(fileName) { | ||||||||||||||
| summary.Deleted.Add(uri) | ||||||||||||||
| changes := make(map[tspath.Path]project.FileSourceLayerChange) | ||||||||||||||
| add := func(fileName string, structural bool, shadowsDescendants bool) { | ||||||||||||||
| absolutePath := tspath.GetNormalizedAbsolutePath(fileName, currentDirectory) | ||||||||||||||
| path := toPath(absolutePath) | ||||||||||||||
| existing := changes[path] | ||||||||||||||
| existing.Path = absolutePath | ||||||||||||||
| existing.Structural = existing.Structural || structural | ||||||||||||||
| existing.ShadowsDescendants = existing.ShadowsDescendants || shadowsDescendants | ||||||||||||||
| changes[path] = existing | ||||||||||||||
| } | ||||||||||||||
| addWithAliases := func(fileName string, structural bool, shadowsDescendants bool) { | ||||||||||||||
| add(fileName, structural, shadowsDescendants) | ||||||||||||||
| if base != nil { | ||||||||||||||
| for _, alias := range base.aliasesForPath(fileName) { | ||||||||||||||
| add(alias, structural, shadowsDescendants) | ||||||||||||||
| } | ||||||||||||||
| return | ||||||||||||||
| } | ||||||||||||||
| if baseFS.FileExists(fileName) { | ||||||||||||||
| summary.Changed.Add(uri) | ||||||||||||||
| } else { | ||||||||||||||
| summary.Created.Add(uri) | ||||||||||||||
| } | ||||||||||||||
| addRequestDescendants := func(fileName string) { | ||||||||||||||
| if base == nil { | ||||||||||||||
| return | ||||||||||||||
| } | ||||||||||||||
| node, _ := base.paths.lookup(base.toPath(fileName)) | ||||||||||||||
| node.walkFiles(func(file *requestFile) { | ||||||||||||||
| addWithAliases(file.fileName, false, false) | ||||||||||||||
|
andrewbranch marked this conversation as resolved.
|
||||||||||||||
| }) | ||||||||||||||
| } | ||||||||||||||
| addChangeAndAliases := func(fileName string, deleted bool) { | ||||||||||||||
| addChange(fileName, deleted) | ||||||||||||||
| if baseRequestFS != nil { | ||||||||||||||
| for _, alias := range baseRequestFS.aliasesForPath(fileName) { | ||||||||||||||
| addChange(alias, deleted) | ||||||||||||||
| } | ||||||||||||||
| shadowsRequestSymlinks := func(fileName string) bool { | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this name of this mean? Is
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, sorry, I didn't notice this was in |
||||||||||||||
| if base == nil { | ||||||||||||||
| return false | ||||||||||||||
| } | ||||||||||||||
| node, _ := base.paths.lookup(base.toPath(fileName)) | ||||||||||||||
| return node != nil && node.hasSymlinks | ||||||||||||||
| } | ||||||||||||||
| overlayFiles := make(map[tspath.Path]struct{}, len(request.Files)) | ||||||||||||||
|
|
||||||||||||||
| files := make(map[tspath.Path]struct{}, len(request.Files)) | ||||||||||||||
| for fileName := range request.Files { | ||||||||||||||
| absoluteFileName := tspath.GetNormalizedAbsolutePath(fileName, currentDirectory) | ||||||||||||||
| overlayFiles[toPath(absoluteFileName)] = struct{}{} | ||||||||||||||
| addChangeAndAliases(absoluteFileName, false) | ||||||||||||||
| absolutePath := tspath.GetNormalizedAbsolutePath(fileName, currentDirectory) | ||||||||||||||
| if shadowsRequestSymlinks(absolutePath) { | ||||||||||||||
| return project.FileSourceLayerChanges{InvalidateAll: true} | ||||||||||||||
| } | ||||||||||||||
| files[toPath(absolutePath)] = struct{}{} | ||||||||||||||
| addRequestDescendants(absolutePath) | ||||||||||||||
| addWithAliases(absolutePath, false, false) | ||||||||||||||
| } | ||||||||||||||
| for _, removedPath := range request.RemovedPaths { | ||||||||||||||
| absoluteFileName := tspath.GetNormalizedAbsolutePath(removedPath, currentDirectory) | ||||||||||||||
| if _, replaced := overlayFiles[toPath(absoluteFileName)]; replaced { | ||||||||||||||
| absolutePath := tspath.GetNormalizedAbsolutePath(removedPath, currentDirectory) | ||||||||||||||
| if _, replaced := files[toPath(absolutePath)]; replaced { | ||||||||||||||
| continue | ||||||||||||||
| } | ||||||||||||||
| addChangeAndAliases(absoluteFileName, true) | ||||||||||||||
| } | ||||||||||||||
| // Replacing a listing or a symlink can change every cached descendant. | ||||||||||||||
| // Delete events expand through the snapshot's cached directory tree and create | ||||||||||||||
| // events that refresh wildcard roots and previously missing module resolutions. | ||||||||||||||
| addReplacement := func(path string) { | ||||||||||||||
| absolutePath := tspath.GetNormalizedAbsolutePath(path, currentDirectory) | ||||||||||||||
| addChangeAndAliases(absolutePath, true) | ||||||||||||||
| summary.Created.Add(lsconv.FileNameToDocumentURI(absolutePath)) | ||||||||||||||
| if baseRequestFS != nil { | ||||||||||||||
| for _, alias := range baseRequestFS.aliasesForPath(absolutePath) { | ||||||||||||||
| summary.Created.Add(lsconv.FileNameToDocumentURI(alias)) | ||||||||||||||
| } | ||||||||||||||
| if shadowsRequestSymlinks(absolutePath) { | ||||||||||||||
| return project.FileSourceLayerChanges{InvalidateAll: true} | ||||||||||||||
| } | ||||||||||||||
| addRequestDescendants(absolutePath) | ||||||||||||||
| addWithAliases(absolutePath, true, true) | ||||||||||||||
|
Comment on lines
+73
to
+77
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's really nothing else to be done for a "removed" file? Maybe I'm out of my depth, but how is removing a host file done with these recursive calls? |
||||||||||||||
| } | ||||||||||||||
| for directoryName := range request.Directories { | ||||||||||||||
| addReplacement(directoryName) | ||||||||||||||
| if shadowsRequestSymlinks(directoryName) { | ||||||||||||||
| return project.FileSourceLayerChanges{InvalidateAll: true} | ||||||||||||||
| } | ||||||||||||||
| addRequestDescendants(directoryName) | ||||||||||||||
| addWithAliases(directoryName, true, false) | ||||||||||||||
| } | ||||||||||||||
| for linkName := range request.Symlinks { | ||||||||||||||
| addReplacement(linkName) | ||||||||||||||
| if shadowsRequestSymlinks(linkName) { | ||||||||||||||
| return project.FileSourceLayerChanges{InvalidateAll: true} | ||||||||||||||
| } | ||||||||||||||
| addRequestDescendants(linkName) | ||||||||||||||
| addWithAliases(linkName, true, true) | ||||||||||||||
| } | ||||||||||||||
| if summary.Changed.Len()+summary.Created.Len()+summary.Deleted.Len() > 0 { | ||||||||||||||
| summary.IncludesWatchChangeOutsideNodeModules = true | ||||||||||||||
|
|
||||||||||||||
| result := make([]project.FileSourceLayerChange, 0, len(changes)) | ||||||||||||||
| for _, change := range changes { | ||||||||||||||
| result = append(result, change) | ||||||||||||||
| } | ||||||||||||||
|
Comment on lines
+94
to
97
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
| slices.SortFunc(result, func(left project.FileSourceLayerChange, right project.FileSourceLayerChange) int { | ||||||||||||||
| return cmp.Compare(left.Path, right.Path) | ||||||||||||||
| }) | ||||||||||||||
| return project.FileSourceLayerChanges{Changes: result} | ||||||||||||||
| } | ||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,15 @@ import ( | |
| "gotest.tools/v3/assert" | ||
| ) | ||
|
|
||
| func TestFileChangesIncludeDirectoryTombstones(t *testing.T) { | ||
| func changeMap(changes []project.FileSourceLayerChange) map[string][2]bool { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you just doing this to statically avoid duplicates? You could just do a list of |
||
| result := make(map[string][2]bool, len(changes)) | ||
| for _, change := range changes { | ||
| result[change.Path] = [2]bool{change.Structural, change.ShadowsDescendants} | ||
| } | ||
| return result | ||
| } | ||
|
|
||
| func TestFileSourceLayerChangesIncludeDirectoryTombstones(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| base, err := newRequestFileSystem(&RequestFileSystem{ | ||
|
|
@@ -23,30 +31,35 @@ func TestFileChangesIncludeDirectoryTombstones(t *testing.T) { | |
| }, vfstest.FromMap(map[string]string{}, true), "/") | ||
| assert.NilError(t, err) | ||
|
|
||
| var summary project.FileChangeSummary | ||
| addFileChanges(&summary, &RequestFileSystem{ | ||
| changes := changeMap(getFileSourceLayerChanges(&RequestFileSystem{ | ||
| Kind: KindLayer, | ||
| Files: map[string]string{"/replaced.ts": "new"}, | ||
| RemovedPaths: []string{"removed", "/missing", "/replaced.ts"}, | ||
| }, base, "/") | ||
| assert.Assert(t, !summary.InvalidateAll) | ||
| assert.Assert(t, summary.IncludesWatchChangeOutsideNodeModules) | ||
| assert.Equal(t, summary.Deleted.Len(), 2) | ||
| assert.Assert(t, summary.Deleted.Has("file:///removed")) | ||
| assert.Assert(t, summary.Deleted.Has("file:///alias")) | ||
| assert.Equal(t, summary.Changed.Len(), 1) | ||
| assert.Assert(t, summary.Changed.Has("file:///replaced.ts")) | ||
| }, base, "/", true).Changes) | ||
| assert.DeepEqual(t, changes, map[string][2]bool{ | ||
| "/alias": {true, true}, | ||
| "/alias/nested/file.ts": {}, | ||
| "/missing": {true, true}, | ||
| "/removed": {true, true}, | ||
| "/removed/nested/file.ts": {}, | ||
| "/replaced.ts": {}, | ||
| }) | ||
| } | ||
|
|
||
| func TestFileSourceLayerChangesIncludeDirectoryReplacedByFile(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| changes := getFileSourceLayerChanges(&RequestFileSystem{ | ||
| Kind: KindLayer, | ||
| Files: map[string]string{"/replaced": "new"}, | ||
| }, nil, "/", true).Changes | ||
| assert.DeepEqual(t, changes, []project.FileSourceLayerChange{{Path: "/replaced"}}) | ||
| } | ||
|
|
||
| func TestFileChangesIncludeListingsAndSymlinks(t *testing.T) { | ||
| func TestFileSourceLayerChangesIncludeListingsAndSymlinks(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| base := vfstest.FromMap(map[string]string{ | ||
| "/dir/old.ts": "old listing", | ||
| "/link/old.ts": "old target", | ||
| }, true) | ||
| var summary project.FileChangeSummary | ||
| addFileChanges(&summary, &RequestFileSystem{ | ||
| changes := changeMap(getFileSourceLayerChanges(&RequestFileSystem{ | ||
| Kind: KindLayer, | ||
| Directories: map[string]RequestDirectoryEntries{ | ||
| "/dir": {}, | ||
|
|
@@ -55,18 +68,15 @@ func TestFileChangesIncludeListingsAndSymlinks(t *testing.T) { | |
| "/link": {Target: "/target"}, | ||
| "/new": {Target: "/host", Host: true}, | ||
| }, | ||
| }, base, "/") | ||
| assert.Assert(t, !summary.InvalidateAll) | ||
| assert.Equal(t, summary.Deleted.Len(), 2) | ||
| assert.Assert(t, summary.Deleted.Has("file:///dir")) | ||
| assert.Assert(t, summary.Deleted.Has("file:///link")) | ||
| assert.Equal(t, summary.Created.Len(), 3) | ||
| assert.Assert(t, summary.Created.Has("file:///dir")) | ||
| assert.Assert(t, summary.Created.Has("file:///link")) | ||
| assert.Assert(t, summary.Created.Has("file:///new")) | ||
| }, nil, "/", true).Changes) | ||
| assert.DeepEqual(t, changes, map[string][2]bool{ | ||
| "/dir": {true, false}, | ||
| "/link": {true, true}, | ||
| "/new": {true, true}, | ||
| }) | ||
| } | ||
|
|
||
| func TestFileChangesIncludeRecursiveSymlinkAliases(t *testing.T) { | ||
| func TestFileSourceLayerChangesIncludeRecursiveSymlinkAliases(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| base, err := newRequestFileSystem(&RequestFileSystem{ | ||
|
|
@@ -78,18 +88,17 @@ func TestFileChangesIncludeRecursiveSymlinkAliases(t *testing.T) { | |
| }, vfstest.FromMap(map[string]string{}, true), "/") | ||
| assert.NilError(t, err) | ||
|
|
||
| var summary project.FileChangeSummary | ||
| addFileChanges(&summary, &RequestFileSystem{ | ||
| changes := getFileSourceLayerChanges(&RequestFileSystem{ | ||
| Kind: KindLayer, | ||
| Files: map[string]string{"/dir/file.ts": "new"}, | ||
| }, base, "/") | ||
| assert.Equal(t, summary.Changed.Len(), 2) | ||
| assert.Assert(t, summary.Changed.Has("file:///dir/file.ts")) | ||
| assert.Assert(t, summary.Changed.Has("file:///dir/link/file.ts")) | ||
| assert.Equal(t, summary.Created.Len(), 0) | ||
| }, base, "/", true).Changes | ||
| assert.DeepEqual(t, changes, []project.FileSourceLayerChange{ | ||
| {Path: "/dir/file.ts"}, | ||
| {Path: "/dir/link/file.ts"}, | ||
| }) | ||
| } | ||
|
|
||
| func TestFileChangesIncludeRootSymlinkAliases(t *testing.T) { | ||
| func TestFileSourceLayerChangesIncludeRootSymlinkAliases(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| base, err := newRequestFileSystem(&RequestFileSystem{ | ||
|
|
@@ -100,17 +109,37 @@ func TestFileChangesIncludeRootSymlinkAliases(t *testing.T) { | |
| }, | ||
| }, vfstest.FromMap(map[string]string{}, true), "/") | ||
| assert.NilError(t, err) | ||
| content, ok := base.ReadFile("/link/file.ts") | ||
| assert.Assert(t, ok) | ||
| assert.Equal(t, content, "old") | ||
|
|
||
| var summary project.FileChangeSummary | ||
| addFileChanges(&summary, &RequestFileSystem{ | ||
| changes := getFileSourceLayerChanges(&RequestFileSystem{ | ||
| Kind: KindLayer, | ||
| Files: map[string]string{"/file.ts": "new"}, | ||
| }, base, "/") | ||
| assert.Equal(t, summary.Changed.Len(), 2) | ||
| assert.Assert(t, summary.Changed.Has("file:///file.ts")) | ||
| assert.Assert(t, summary.Changed.Has("file:///link/file.ts")) | ||
| assert.Equal(t, summary.Created.Len(), 0) | ||
| }, base, "/", true).Changes | ||
| assert.DeepEqual(t, changes, []project.FileSourceLayerChange{ | ||
| {Path: "/file.ts"}, | ||
| {Path: "/link/file.ts"}, | ||
| }) | ||
| } | ||
|
|
||
| func TestFileSourceLayerChangesInvalidateWhenReplacingSymlink(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| base, err := newRequestFileSystem(&RequestFileSystem{ | ||
| Kind: KindFull, | ||
| Files: map[string]string{"/target/file.ts": "old"}, | ||
| Symlinks: map[string]RequestSymlink{ | ||
| "/link": {Target: "/target"}, | ||
| }, | ||
| }, vfstest.FromMap(map[string]string{}, true), "/") | ||
| assert.NilError(t, err) | ||
|
|
||
| for _, request := range []*RequestFileSystem{ | ||
| {Kind: KindLayer, RemovedPaths: []string{"/link"}}, | ||
| {Kind: KindLayer, Files: map[string]string{"/link": "replacement"}}, | ||
| {Kind: KindLayer, Directories: map[string]RequestDirectoryEntries{"/link": {}}}, | ||
| {Kind: KindLayer, Symlinks: map[string]RequestSymlink{"/link": {Target: "/other"}}}, | ||
| } { | ||
| changes := getFileSourceLayerChanges(request, base, "/", true) | ||
| assert.Assert(t, changes.InvalidateAll) | ||
| assert.Equal(t, len(changes.Changes), 0) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not clear on what this means.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like
?