Skip to content

Commit d8df834

Browse files
committed
Test better, extract directory tree to object heirarchy instead of doing multiple map lookups
1 parent 20ea871 commit d8df834

4 files changed

Lines changed: 1733 additions & 514 deletions

File tree

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
package requestfilesystem
2+
3+
import (
4+
"io/fs"
5+
"maps"
6+
"slices"
7+
"strings"
8+
"time"
9+
10+
"github.com/microsoft/TypeScript/tsc/internal/tspath"
11+
"github.com/microsoft/TypeScript/tsc/internal/vfs"
12+
)
13+
14+
type requestFallback uint8
15+
16+
const (
17+
requestFallbackInherit requestFallback = iota
18+
requestFallbackAllowed
19+
requestFallbackMissing
20+
)
21+
22+
type requestEntry interface {
23+
isRequestEntry()
24+
}
25+
26+
type requestFile struct {
27+
fileName string
28+
content string
29+
}
30+
31+
type requestSymlink struct {
32+
linkName string
33+
target string
34+
host bool
35+
}
36+
37+
type requestDirectory struct {
38+
directoryName string
39+
listing *vfs.Entries
40+
}
41+
42+
func (*requestFile) isRequestEntry() {}
43+
func (*requestSymlink) isRequestEntry() {}
44+
func (*requestDirectory) isRequestEntry() {}
45+
46+
func (file *requestFile) Name() string { return tspath.GetBaseFileName(file.fileName) }
47+
func (file *requestFile) Size() int64 { return int64(len(file.content)) }
48+
func (file *requestFile) Mode() fs.FileMode { return 0o444 }
49+
func (file *requestFile) ModTime() time.Time { return time.Time{} }
50+
func (file *requestFile) IsDir() bool { return false }
51+
func (file *requestFile) Sys() any { return nil }
52+
func (file *requestFile) Type() fs.FileMode { return file.Mode().Type() }
53+
func (file *requestFile) Info() (fs.FileInfo, error) { return file, nil }
54+
55+
func (directory *requestDirectory) Name() string {
56+
return tspath.GetBaseFileName(directory.directoryName)
57+
}
58+
func (directory *requestDirectory) Size() int64 { return 0 }
59+
func (directory *requestDirectory) Mode() fs.FileMode { return fs.ModeDir | 0o555 }
60+
func (directory *requestDirectory) ModTime() time.Time { return time.Time{} }
61+
func (directory *requestDirectory) IsDir() bool { return true }
62+
func (directory *requestDirectory) Sys() any { return nil }
63+
func (directory *requestDirectory) Type() fs.FileMode { return directory.Mode().Type() }
64+
func (directory *requestDirectory) Info() (fs.FileInfo, error) { return directory, nil }
65+
66+
var (
67+
_ vfs.FileInfo = (*requestFile)(nil)
68+
_ vfs.DirEntry = (*requestFile)(nil)
69+
_ vfs.FileInfo = (*requestDirectory)(nil)
70+
_ vfs.DirEntry = (*requestDirectory)(nil)
71+
)
72+
73+
type requestPathNode struct {
74+
entry requestEntry
75+
fallback requestFallback
76+
children map[tspath.Path]*requestPathNode
77+
hasSymlinks bool
78+
}
79+
80+
func (node *requestPathNode) replacesSubtree() bool {
81+
switch node.entry.(type) {
82+
case *requestFile, *requestSymlink:
83+
return true
84+
}
85+
return false
86+
}
87+
88+
func requestPathAncestors(path tspath.Path) []tspath.Path {
89+
var paths []tspath.Path
90+
for {
91+
paths = append(paths, path)
92+
parent := tspath.Path(tspath.GetDirectoryPath(string(path)))
93+
if parent == path {
94+
break
95+
}
96+
path = parent
97+
}
98+
slices.Reverse(paths)
99+
return paths
100+
}
101+
102+
func (node *requestPathNode) ensure(path tspath.Path) *requestPathNode {
103+
for _, ancestor := range requestPathAncestors(path) {
104+
if node.children == nil {
105+
node.children = make(map[tspath.Path]*requestPathNode)
106+
}
107+
child := node.children[ancestor]
108+
if child == nil {
109+
child = &requestPathNode{}
110+
node.children[ancestor] = child
111+
}
112+
node = child
113+
}
114+
return node
115+
}
116+
117+
func (node *requestPathNode) lookup(path tspath.Path) (*requestPathNode, requestFallback) {
118+
fallback := requestFallbackInherit
119+
for _, ancestor := range requestPathAncestors(path) {
120+
if node == nil {
121+
break
122+
}
123+
if node.fallback != requestFallbackInherit {
124+
fallback = node.fallback
125+
}
126+
node = node.children[ancestor]
127+
}
128+
if node != nil && node.fallback != requestFallbackInherit {
129+
fallback = node.fallback
130+
}
131+
return node, fallback
132+
}
133+
134+
func (node *requestPathNode) walkSymlinks(visit func(tspath.Path, *requestSymlink)) {
135+
if node == nil || !node.hasSymlinks {
136+
return
137+
}
138+
for path, child := range node.children {
139+
if symlink, ok := child.entry.(*requestSymlink); ok {
140+
visit(path, symlink)
141+
}
142+
child.walkSymlinks(visit)
143+
}
144+
}
145+
146+
func (node *requestPathNode) entries() (vfs.Entries, bool) {
147+
if node == nil {
148+
return vfs.Entries{}, false
149+
}
150+
directory, ok := node.entry.(*requestDirectory)
151+
if !ok {
152+
return vfs.Entries{}, false
153+
}
154+
if directory.listing != nil {
155+
return cloneEntries(*directory.listing), true
156+
}
157+
var entries vfs.Entries
158+
for _, child := range node.children {
159+
switch entry := child.entry.(type) {
160+
case *requestFile:
161+
entries.Files = append(entries.Files, tspath.GetBaseFileName(entry.fileName))
162+
case *requestDirectory:
163+
entries.Directories = append(entries.Directories, tspath.GetBaseFileName(entry.directoryName))
164+
}
165+
}
166+
slices.Sort(entries.Files)
167+
slices.Sort(entries.Directories)
168+
return entries, true
169+
}
170+
171+
func composeRequestPaths(base *requestPathNode, overlay *requestPathNode, fallback requestFallback, caseSensitive bool) *requestPathNode {
172+
if overlay == nil {
173+
return base
174+
}
175+
if overlay.fallback != requestFallbackInherit {
176+
fallback = overlay.fallback
177+
base = nil
178+
}
179+
if overlay.replacesSubtree() {
180+
base = nil
181+
}
182+
var result requestPathNode
183+
if base != nil {
184+
result = *base
185+
}
186+
result.children = maps.Clone(result.children)
187+
if overlay.fallback != requestFallbackInherit || overlay.replacesSubtree() {
188+
result.fallback = fallback
189+
}
190+
previousDirectory, _ := result.entry.(*requestDirectory)
191+
overlayDirectory, _ := overlay.entry.(*requestDirectory)
192+
if overlay.entry != nil {
193+
result.entry = overlay.entry
194+
if overlayDirectory != nil && overlayDirectory.listing == nil && previousDirectory != nil {
195+
result.entry = &requestDirectory{directoryName: overlayDirectory.directoryName, listing: previousDirectory.listing}
196+
}
197+
}
198+
for path, child := range overlay.children {
199+
if result.children == nil {
200+
result.children = make(map[tspath.Path]*requestPathNode)
201+
}
202+
result.children[path] = composeRequestPaths(result.children[path], child, fallback, caseSensitive)
203+
}
204+
if directory, ok := result.entry.(*requestDirectory); ok && directory.listing != nil && (overlayDirectory == nil || overlayDirectory.listing == nil) {
205+
entries := cloneEntries(*directory.listing)
206+
equal := func(left string, right string) bool {
207+
return tspath.GetCanonicalFileName(left, caseSensitive) == tspath.GetCanonicalFileName(right, caseSensitive)
208+
}
209+
for path, child := range overlay.children {
210+
name := tspath.GetBaseFileName(string(path))
211+
if child.fallback == requestFallbackMissing || child.replacesSubtree() {
212+
entries.Files = slices.DeleteFunc(entries.Files, func(entry string) bool { return equal(entry, name) })
213+
entries.Directories = slices.DeleteFunc(entries.Directories, func(entry string) bool { return equal(entry, name) })
214+
for entry := range entries.Symlinks {
215+
if equal(entry, name) {
216+
delete(entries.Symlinks, entry)
217+
}
218+
}
219+
}
220+
switch entry := child.entry.(type) {
221+
case *requestFile:
222+
entries = mergeEntries(entries, vfs.Entries{Files: []string{tspath.GetBaseFileName(entry.fileName)}}, equal)
223+
case *requestDirectory:
224+
entries = mergeEntries(entries, vfs.Entries{Directories: []string{tspath.GetBaseFileName(entry.directoryName)}}, equal)
225+
}
226+
}
227+
result.entry = &requestDirectory{directoryName: directory.directoryName, listing: &entries}
228+
}
229+
_, result.hasSymlinks = result.entry.(*requestSymlink)
230+
for _, child := range result.children {
231+
result.hasSymlinks = result.hasSymlinks || child.hasSymlinks
232+
}
233+
return &result
234+
}
235+
236+
func (node *requestPathNode) firstSymlink(path tspath.Path) (tspath.Path, *requestSymlink) {
237+
for _, ancestor := range requestPathAncestors(path) {
238+
if node == nil {
239+
break
240+
}
241+
node = node.children[ancestor]
242+
if node != nil {
243+
if symlink, ok := node.entry.(*requestSymlink); ok {
244+
return ancestor, symlink
245+
}
246+
}
247+
}
248+
return "", nil
249+
}
250+
251+
func (node *requestPathNode) containsFileAncestor(path tspath.Path) bool {
252+
for _, ancestor := range requestPathAncestors(path) {
253+
if node == nil {
254+
return false
255+
}
256+
node = node.children[ancestor]
257+
if ancestor != path && node != nil {
258+
if _, ok := node.entry.(*requestFile); ok {
259+
return true
260+
}
261+
}
262+
}
263+
return false
264+
}
265+
266+
func requestPathContains(parent tspath.Path, path tspath.Path) bool {
267+
return path == parent || strings.HasPrefix(string(path), tspath.EnsureTrailingDirectorySeparator(string(parent)))
268+
}

0 commit comments

Comments
 (0)