Skip to content

Commit 66ab80d

Browse files
authored
Multiproject requests like find all refs, rename and workspace symbols (#1991)
1 parent 6ba0ed7 commit 66ab80d

File tree

125 files changed

+22109
-907
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+22109
-907
lines changed

internal/compiler/program.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,19 @@ func (p *Program) GetParseFileRedirect(fileName string) string {
145145
return p.projectReferenceFileMapper.getParseFileRedirect(ast.NewHasFileName(fileName, p.toPath(fileName)))
146146
}
147147

148-
func (p *Program) ForEachResolvedProjectReference(
149-
fn func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int),
150-
) {
151-
p.projectReferenceFileMapper.forEachResolvedProjectReference(fn)
148+
func (p *Program) GetResolvedProjectReferences() []*tsoptions.ParsedCommandLine {
149+
return p.projectReferenceFileMapper.getResolvedProjectReferences()
150+
}
151+
152+
func (p *Program) RangeResolvedProjectReference(f func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) bool) bool {
153+
return p.projectReferenceFileMapper.rangeResolvedProjectReference(f)
154+
}
155+
156+
func (p *Program) RangeResolvedProjectReferenceInChildConfig(
157+
childConfig *tsoptions.ParsedCommandLine,
158+
f func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) bool,
159+
) bool {
160+
return p.projectReferenceFileMapper.rangeResolvedProjectReferenceInChildConfig(childConfig, f)
152161
}
153162

154163
// UseCaseSensitiveFileNames implements checker.Program.
@@ -905,13 +914,13 @@ func (p *Program) verifyProjectReferences() {
905914
p.programDiagnostics = append(p.programDiagnostics, diag)
906915
}
907916

908-
p.ForEachResolvedProjectReference(func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) {
917+
p.RangeResolvedProjectReference(func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) bool {
909918
ref := parent.ProjectReferences()[index]
910919
// !!! Deprecated in 5.0 and removed since 5.5
911920
// verifyRemovedProjectReference(ref, parent, index);
912921
if config == nil {
913922
createDiagnosticForReference(parent, index, diagnostics.File_0_not_found, ref.Path)
914-
return
923+
return true
915924
}
916925
refOptions := config.CompilerOptions()
917926
if !refOptions.Composite.IsTrue() || refOptions.NoEmit.IsTrue() {
@@ -928,6 +937,7 @@ func (p *Program) verifyProjectReferences() {
928937
createDiagnosticForReference(parent, index, diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoFileName, ref.Path)
929938
p.hasEmitBlockingDiagnostics.Add(p.toPath(buildInfoFileName))
930939
}
940+
return true
931941
})
932942
}
933943

internal/compiler/projectreferencefilemapper.go

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ func (mapper *projectReferenceFileMapper) getParseFileRedirect(file ast.HasFileN
4646
}
4747

4848
func (mapper *projectReferenceFileMapper) getResolvedProjectReferences() []*tsoptions.ParsedCommandLine {
49+
if mapper.opts.Config.ConfigFile == nil {
50+
return nil
51+
}
4952
refs, ok := mapper.referencesInConfigFile[mapper.opts.Config.ConfigFile.SourceFile.Path()]
5053
var result []*tsoptions.ParsedCommandLine
5154
if ok {
@@ -106,32 +109,50 @@ func (mapper *projectReferenceFileMapper) getResolvedReferenceFor(path tspath.Pa
106109
return config, ok
107110
}
108111

109-
func (mapper *projectReferenceFileMapper) forEachResolvedProjectReference(
110-
fn func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int),
111-
) {
112+
func (mapper *projectReferenceFileMapper) rangeResolvedProjectReference(
113+
f func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) bool,
114+
) bool {
112115
if mapper.opts.Config.ConfigFile == nil {
113-
return
116+
return false
114117
}
115118
seenRef := collections.NewSetWithSizeHint[tspath.Path](len(mapper.referencesInConfigFile))
116119
seenRef.Add(mapper.opts.Config.ConfigFile.SourceFile.Path())
117120
refs := mapper.referencesInConfigFile[mapper.opts.Config.ConfigFile.SourceFile.Path()]
118-
mapper.forEachResolvedReferenceWorker(refs, fn, mapper.opts.Config, seenRef)
121+
return mapper.rangeResolvedReferenceWorker(refs, f, mapper.opts.Config, seenRef)
119122
}
120123

121-
func (mapper *projectReferenceFileMapper) forEachResolvedReferenceWorker(
124+
func (mapper *projectReferenceFileMapper) rangeResolvedReferenceWorker(
122125
references []tspath.Path,
123-
fn func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int),
126+
f func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) bool,
124127
parent *tsoptions.ParsedCommandLine,
125128
seenRef *collections.Set[tspath.Path],
126-
) {
129+
) bool {
127130
for index, path := range references {
128131
if !seenRef.AddIfAbsent(path) {
129132
continue
130133
}
131134
config, _ := mapper.configToProjectReference[path]
132-
fn(path, config, parent, index)
133-
mapper.forEachResolvedReferenceWorker(mapper.referencesInConfigFile[path], fn, config, seenRef)
135+
if !f(path, config, parent, index) {
136+
return false
137+
}
138+
if !mapper.rangeResolvedReferenceWorker(mapper.referencesInConfigFile[path], f, config, seenRef) {
139+
return false
140+
}
134141
}
142+
return true
143+
}
144+
145+
func (mapper *projectReferenceFileMapper) rangeResolvedProjectReferenceInChildConfig(
146+
childConfig *tsoptions.ParsedCommandLine,
147+
f func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) bool,
148+
) bool {
149+
if childConfig == nil || childConfig.ConfigFile == nil {
150+
return false
151+
}
152+
seenRef := collections.NewSetWithSizeHint[tspath.Path](len(mapper.referencesInConfigFile))
153+
seenRef.Add(childConfig.ConfigFile.SourceFile.Path())
154+
refs := mapper.referencesInConfigFile[childConfig.ConfigFile.SourceFile.Path()]
155+
return mapper.rangeResolvedReferenceWorker(refs, f, mapper.opts.Config, seenRef)
135156
}
136157

137158
func (mapper *projectReferenceFileMapper) getSourceToDtsIfSymlink(file ast.HasFileName) *tsoptions.SourceOutputAndProjectReference {

internal/execute/tsctests/sys.go

Lines changed: 27 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ package tsctests
33
import (
44
"fmt"
55
"io"
6-
"io/fs"
76
"maps"
8-
"slices"
97
"strconv"
108
"strings"
119
"sync"
@@ -14,8 +12,10 @@ import (
1412
"github.com/microsoft/typescript-go/internal/collections"
1513
"github.com/microsoft/typescript-go/internal/compiler"
1614
"github.com/microsoft/typescript-go/internal/core"
15+
"github.com/microsoft/typescript-go/internal/execute"
1716
"github.com/microsoft/typescript-go/internal/execute/incremental"
1817
"github.com/microsoft/typescript-go/internal/execute/tsc"
18+
"github.com/microsoft/typescript-go/internal/testutil/fsbaselineutil"
1919
"github.com/microsoft/typescript-go/internal/testutil/harnessutil"
2020
"github.com/microsoft/typescript-go/internal/testutil/stringtestutil"
2121
"github.com/microsoft/typescript-go/internal/tsoptions"
@@ -95,6 +95,20 @@ func NewTscSystem(files FileMap, useCaseSensitiveFileNames bool, cwd string) *Te
9595
}
9696
}
9797

98+
func GetFileMapWithBuild(files FileMap, commandLineArgs []string) FileMap {
99+
sys := newTestSys(&tscInput{
100+
files: maps.Clone(files),
101+
}, false)
102+
execute.CommandLine(sys, commandLineArgs, sys)
103+
sys.fs.writtenFiles.Range(func(key string) bool {
104+
if text, ok := sys.fsFromFileMap().ReadFile(key); ok {
105+
files[key] = text
106+
}
107+
return true
108+
})
109+
return files
110+
}
111+
98112
func newTestSys(tscInput *tscInput, forIncrementalCorrectness bool) *TestSys {
99113
cwd := tscInput.cwd
100114
if cwd == "" {
@@ -114,6 +128,11 @@ func newTestSys(tscInput *tscInput, forIncrementalCorrectness bool) *TestSys {
114128
}, currentWrite)
115129
sys.env = tscInput.env
116130
sys.forIncrementalCorrectness = forIncrementalCorrectness
131+
sys.fsDiffer = &fsbaselineutil.FSDiffer{
132+
FS: sys.fs.FS.(iovfs.FsWithSys),
133+
DefaultLibs: func() *collections.SyncSet[string] { return sys.fs.defaultLibs },
134+
WrittenFiles: &sys.fs.writtenFiles,
135+
}
117136

118137
// Ensure the default library file is present
119138
sys.ensureLibPathExists("lib.d.ts")
@@ -126,24 +145,12 @@ func newTestSys(tscInput *tscInput, forIncrementalCorrectness bool) *TestSys {
126145
return sys
127146
}
128147

129-
type diffEntry struct {
130-
content string
131-
mTime time.Time
132-
isWritten bool
133-
symlinkTarget string
134-
}
135-
136-
type snapshot struct {
137-
snap map[string]*diffEntry
138-
defaultLibs *collections.SyncSet[string]
139-
}
140-
141148
type TestSys struct {
142149
currentWrite *strings.Builder
143150
programBaselines strings.Builder
144151
programIncludeBaselines strings.Builder
145152
tracer *harnessutil.TracerForBaselining
146-
serializedDiff *snapshot
153+
fsDiffer *fsbaselineutil.FSDiffer
147154
forIncrementalCorrectness bool
148155

149156
fs *testFs
@@ -171,11 +178,11 @@ func (s *TestSys) FS() vfs.FS {
171178
}
172179

173180
func (s *TestSys) fsFromFileMap() iovfs.FsWithSys {
174-
return s.fs.FS.(iovfs.FsWithSys)
181+
return s.fsDiffer.FS
175182
}
176183

177184
func (s *TestSys) mapFs() *vfstest.MapFS {
178-
return s.fsFromFileMap().FSys().(*vfstest.MapFS)
185+
return s.fsDiffer.MapFs()
179186
}
180187

181188
func (s *TestSys) ensureLibPathExists(path string) {
@@ -223,8 +230,8 @@ func (s *TestSys) OnEmittedFiles(result *compiler.EmitResult, mTimesCache *colle
223230
if result != nil {
224231
for _, file := range result.EmittedFiles {
225232
modTime := s.mapFs().GetModTime(file)
226-
if s.serializedDiff != nil {
227-
if diff, ok := s.serializedDiff.snap[file]; ok && diff.mTime.Equal(modTime) {
233+
if serializedDiff := s.fsDiffer.SerializedDiff(); serializedDiff != nil {
234+
if diff, ok := serializedDiff.Snap[file]; ok && diff.MTime.Equal(modTime) {
228235
// Even though written, timestamp was reverted
229236
continue
230237
}
@@ -500,83 +507,7 @@ func (s *TestSys) clearOutput() {
500507
}
501508

502509
func (s *TestSys) baselineFSwithDiff(baseline io.Writer) {
503-
// todo: baselines the entire fs, possibly doesn't correctly diff all cases of emitted files, since emit isn't fully implemented and doesn't always emit the same way as strada
504-
snap := map[string]*diffEntry{}
505-
506-
diffs := map[string]string{}
507-
508-
for path, file := range s.mapFs().Entries() {
509-
if file.Mode&fs.ModeSymlink != 0 {
510-
target, ok := s.mapFs().GetTargetOfSymlink(path)
511-
if !ok {
512-
panic("Failed to resolve symlink target: " + path)
513-
}
514-
newEntry := &diffEntry{symlinkTarget: target}
515-
snap[path] = newEntry
516-
s.addFsEntryDiff(diffs, newEntry, path)
517-
continue
518-
} else if file.Mode.IsRegular() {
519-
newEntry := &diffEntry{content: string(file.Data), mTime: file.ModTime, isWritten: s.fs.writtenFiles.Has(path)}
520-
snap[path] = newEntry
521-
s.addFsEntryDiff(diffs, newEntry, path)
522-
}
523-
}
524-
if s.serializedDiff != nil {
525-
for path := range s.serializedDiff.snap {
526-
if fileInfo := s.mapFs().GetFileInfo(path); fileInfo == nil {
527-
// report deleted
528-
s.addFsEntryDiff(diffs, nil, path)
529-
}
530-
}
531-
}
532-
var defaultLibs collections.SyncSet[string]
533-
if s.fs.defaultLibs != nil {
534-
s.fs.defaultLibs.Range(func(libPath string) bool {
535-
defaultLibs.Add(libPath)
536-
return true
537-
})
538-
}
539-
s.serializedDiff = &snapshot{
540-
snap: snap,
541-
defaultLibs: &defaultLibs,
542-
}
543-
diffKeys := slices.Collect(maps.Keys(diffs))
544-
slices.Sort(diffKeys)
545-
for _, path := range diffKeys {
546-
fmt.Fprint(baseline, "//// ["+path+"] ", diffs[path], "\n")
547-
}
548-
fmt.Fprintln(baseline)
549-
s.fs.writtenFiles = collections.SyncSet[string]{} // Reset written files after baseline
550-
}
551-
552-
func (s *TestSys) addFsEntryDiff(diffs map[string]string, newDirContent *diffEntry, path string) {
553-
var oldDirContent *diffEntry
554-
var defaultLibs *collections.SyncSet[string]
555-
if s.serializedDiff != nil {
556-
oldDirContent = s.serializedDiff.snap[path]
557-
defaultLibs = s.serializedDiff.defaultLibs
558-
}
559-
// todo handle more cases of fs changes
560-
if oldDirContent == nil {
561-
if s.fs.defaultLibs == nil || !s.fs.defaultLibs.Has(path) {
562-
if newDirContent.symlinkTarget != "" {
563-
diffs[path] = "-> " + newDirContent.symlinkTarget + " *new*"
564-
} else {
565-
diffs[path] = "*new* \n" + newDirContent.content
566-
}
567-
}
568-
} else if newDirContent == nil {
569-
diffs[path] = "*deleted*"
570-
} else if newDirContent.content != oldDirContent.content {
571-
diffs[path] = "*modified* \n" + newDirContent.content
572-
} else if newDirContent.isWritten {
573-
diffs[path] = "*rewrite with same content*"
574-
} else if newDirContent.mTime != oldDirContent.mTime {
575-
diffs[path] = "*mTime changed*"
576-
} else if defaultLibs != nil && defaultLibs.Has(path) && s.fs.defaultLibs != nil && !s.fs.defaultLibs.Has(path) {
577-
// Lib file that was read
578-
diffs[path] = "*Lib*\n" + newDirContent.content
579-
}
510+
s.fsDiffer.BaselineFSwithDiff(baseline)
580511
}
581512

582513
func (s *TestSys) writeFileNoError(path string, content string, writeByteOrderMark bool) {

internal/fourslash/_scripts/updateFailing.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ function main() {
1313
const go = which.sync("go");
1414
let testOutput: string;
1515
try {
16-
testOutput = cp.execFileSync(go, ["test", "./internal/fourslash/tests/gen"], { encoding: "utf-8" });
16+
testOutput = cp.execFileSync(go, ["test", "-v", "./internal/fourslash/tests/gen"], { encoding: "utf-8" });
1717
}
1818
catch (error) {
1919
testOutput = (error as { stdout: string; }).stdout as string;
2020
}
2121
const panicRegex = /^panic/m;
2222
if (panicRegex.test(testOutput)) {
2323
fs.writeFileSync(failingTestsPath, oldFailingTests, "utf-8");
24-
throw new Error("Unrecovered panic detected in tests");
24+
throw new Error("Unrecovered panic detected in tests\n" + testOutput);
2525
}
2626
const failRegex = /--- FAIL: ([\S]+)/gm;
2727
const failingTests: string[] = [];

0 commit comments

Comments
 (0)