Skip to content

Commit 5d230c1

Browse files
committed
Use declaration maps to queue more projects
1 parent 4f25a86 commit 5d230c1

11 files changed

+224
-25
lines changed

internal/ls/findallreferences.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,24 @@ type ReferenceEntry struct {
107107
textRange *lsproto.Range
108108
}
109109

110+
func (entry *SymbolAndEntries) canUseDefinitionSymbol() bool {
111+
if entry.definition == nil {
112+
return false
113+
}
114+
115+
switch entry.definition.Kind {
116+
case definitionKindSymbol, definitionKindThis:
117+
return entry.definition.symbol != nil
118+
case definitionKindTripleSlashReference:
119+
// !!! TODO : need to find file reference instead?
120+
// May need to return true to indicate this to be file search instead and might need to do for import stuff as well
121+
// For now
122+
return false
123+
default:
124+
return false
125+
}
126+
}
127+
110128
func (l *LanguageService) getRangeOfEntry(entry *ReferenceEntry) *lsproto.Range {
111129
return l.resolveEntry(entry).textRange
112130
}
@@ -416,6 +434,77 @@ func getSymbolScope(symbol *ast.Symbol) *ast.Node {
416434

417435
// === functions on (*ls) ===
418436

437+
type position struct {
438+
uri lsproto.DocumentUri
439+
pos lsproto.Position
440+
}
441+
442+
var _ lsproto.HasTextDocumentPosition = (*position)(nil)
443+
444+
func (nld *position) TextDocumentURI() lsproto.DocumentUri { return nld.uri }
445+
func (nld *position) TextDocumentPosition() lsproto.Position { return nld.pos }
446+
447+
func getFileAndStartPosFromDeclaration(declaration *ast.Node) (*ast.SourceFile, core.TextPos) {
448+
file := ast.GetSourceFileOfNode(declaration)
449+
name := core.OrElse(ast.GetNameOfDeclaration(declaration), declaration)
450+
startPos := getStartPosFromNode(name, file)
451+
452+
return file, startPos
453+
}
454+
455+
func (l *LanguageService) GetNonLocalDefinition(ctx context.Context, entry *SymbolAndEntries) lsproto.HasTextDocumentPosition {
456+
if !entry.canUseDefinitionSymbol() {
457+
return nil
458+
}
459+
460+
program := l.GetProgram()
461+
checker, done := program.GetTypeChecker(ctx)
462+
defer done()
463+
emitResolver := checker.GetEmitResolver()
464+
for _, d := range entry.definition.symbol.Declarations {
465+
if emitResolver.IsDeclarationVisible(d) {
466+
file, startPos := getFileAndStartPosFromDeclaration(d)
467+
fileName := file.FileName()
468+
return &position{
469+
uri: lsconv.FileNameToDocumentURI(fileName),
470+
pos: l.converters.PositionToLineAndCharacter(file, startPos),
471+
}
472+
}
473+
}
474+
return nil
475+
}
476+
477+
func (l *LanguageService) ForEachOriginalDefinitionLocation(
478+
ctx context.Context,
479+
entry *SymbolAndEntries,
480+
cb func(lsproto.DocumentUri, lsproto.Position),
481+
) {
482+
if !entry.canUseDefinitionSymbol() {
483+
return
484+
}
485+
486+
program := l.GetProgram()
487+
for _, d := range entry.definition.symbol.Declarations {
488+
file, startPos := getFileAndStartPosFromDeclaration(d)
489+
fileName := file.FileName()
490+
if tspath.IsDeclarationFileName(fileName) {
491+
// Map to ts position
492+
mapped := l.tryGetSourcePosition(file.FileName(), startPos)
493+
if mapped != nil {
494+
cb(
495+
lsconv.FileNameToDocumentURI(mapped.FileName),
496+
l.converters.PositionToLineAndCharacter(l.getScript(mapped.FileName), core.TextPos(mapped.Pos)),
497+
)
498+
}
499+
} else if program.IsSourceFromProjectReference(l.toPath(fileName)) {
500+
cb(
501+
lsconv.FileNameToDocumentURI(fileName),
502+
l.converters.PositionToLineAndCharacter(file, startPos),
503+
)
504+
}
505+
}
506+
}
507+
419508
func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lsproto.DocumentUri, documentPosition lsproto.Position, isRename bool) (*ast.Node, []*SymbolAndEntries, bool) {
420509
// `findReferencedSymbols` except only computes the information needed to return reference locations
421510
program, sourceFile := l.getProgramAndFile(uri)

internal/ls/languageservice.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/microsoft/typescript-go/internal/ls/lsutil"
99
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
1010
"github.com/microsoft/typescript-go/internal/sourcemap"
11+
"github.com/microsoft/typescript-go/internal/tspath"
1112
)
1213

1314
type LanguageService struct {
@@ -29,6 +30,10 @@ func NewLanguageService(
2930
}
3031
}
3132

33+
func (l *LanguageService) toPath(fileName string) tspath.Path {
34+
return tspath.ToPath(fileName, l.program.GetCurrentDirectory(), l.UseCaseSensitiveFileNames())
35+
}
36+
3237
func (l *LanguageService) GetProgram() *compiler.Program {
3338
return l.program
3439
}

internal/ls/utilities.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,10 @@ func createRangeFromNode(node *ast.Node, file *ast.SourceFile) core.TextRange {
417417
return core.NewTextRange(scanner.GetTokenPosOfNode(node, file, false /*includeJSDoc*/), node.End())
418418
}
419419

420+
func getStartPosFromNode(node *ast.Node, file *ast.SourceFile) core.TextPos {
421+
return core.TextPos(scanner.GetTokenPosOfNode(node, file, false /*includeJSDoc*/))
422+
}
423+
420424
func (l *LanguageService) createLspRangeFromBounds(start, end int, file *ast.SourceFile) *lsproto.Range {
421425
lspRange := l.converters.ToLSPRange(file, core.NewTextRange(start, end))
422426
return &lspRange

internal/lsp/server.go

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -604,40 +604,42 @@ func registerMultiProjectReferenceRequestHandler[Req lsproto.HasTextDocumentPosi
604604
params = req.Params.(Req)
605605
}
606606
// !!! sheetal: multiple projects that contain the file through symlinks
607-
defaultProject, defaultLs, allProjects, err := s.session.GetProjectsForFile(ctx, params.TextDocumentURI())
607+
defaultProject, defaultLs, allProjects, err := s.session.GetLanguageServiceAndProjectsForFile(ctx, params.TextDocumentURI())
608608
if err != nil {
609609
return err
610610
}
611611
defer s.recover(req)
612612

613613
var queue []projectAndTextDocumentPosition
614614
results := make(map[tspath.Path]Resp)
615-
defaultDefinitions := make([]*ls.SymbolAndEntries, 0) // Single location
615+
var defaultDefinition lsproto.HasTextDocumentPosition
616616

617617
searchPosition := func(project *project.Project, ls *ls.LanguageService, uri lsproto.DocumentUri, position lsproto.Position) (Resp, error) {
618618
originalNode, symbolsAndEntries, ok := ls.ProvideSymbolsAndEntries(ctx, uri, position, info.Method == lsproto.MethodTextDocumentRename)
619619
if ok {
620620
for _, entry := range symbolsAndEntries {
621-
if project == defaultProject {
622-
// !!! TODO sheetal
623-
// store default definition if it can be visible in other projects
624-
// Use this to determine if we should load the projects from solution to find references
625-
626-
// definitionKindSymbol Possible
627-
// definitionKindThis Possible
628-
// definitionKindTripleSlashReference Yes - look for file names
629-
defaultDefinitions = append(defaultDefinitions, entry)
621+
// Find the default definition that can be in another project
622+
// Later we will use this load ancestor tree that references this location and expand search
623+
if project == defaultProject && defaultDefinition == nil {
624+
defaultDefinition = ls.GetNonLocalDefinition(ctx, entry)
630625
}
631-
632-
// TODOS: sheetal
633-
634-
// Original location:
635-
// If file included was dts -> try toi see if we can use source map and find ts file
636-
// If its ts file - if this is ts file for "d.ts" of referenced project use this location
637-
// Get default configured project for this file
638-
639-
// Also get all the projects - existing for this file and add to queue this definition location
640-
// including its symlinks
626+
ls.ForEachOriginalDefinitionLocation(ctx, entry, func(uri lsproto.DocumentUri, position lsproto.Position) {
627+
// Get default configured project for this file
628+
defProjects, errProjects := s.session.GetProjectsForFile(ctx, uri)
629+
if errProjects != nil {
630+
return
631+
}
632+
for _, defProject := range defProjects {
633+
// Optimization: don't enqueue if will be discarded
634+
if _, alreadyDone := results[defProject.Id()]; !alreadyDone {
635+
queue = append(queue, projectAndTextDocumentPosition{
636+
project: defProject,
637+
Uri: uri,
638+
Position: position,
639+
})
640+
}
641+
}
642+
})
641643
}
642644
}
643645
return fn(s, ctx, ls, params, originalNode, symbolsAndEntries)
@@ -692,7 +694,7 @@ func registerMultiProjectReferenceRequestHandler[Req lsproto.HasTextDocumentPosi
692694
}
693695
}
694696

695-
if defaultDefinitions != nil {
697+
if defaultDefinition != nil {
696698
// !!! TODO: sheetal
697699
// load ancestor projects for all the projects that can reference the ones already seen
698700
// for all projects
@@ -1102,7 +1104,7 @@ func combineRenameResponse(defaultProject *project.Project, results map[tspath.P
11021104
// ChangeAnnotations *map[string]*ChangeAnnotation `json:"changeAnnotations,omitzero"`
11031105

11041106
if resp, ok := results[defaultProject.Id()]; ok {
1105-
if resp.WorkspaceEdit != nil {
1107+
if resp.WorkspaceEdit != nil && resp.WorkspaceEdit.Changes != nil {
11061108
for doc, changes := range *resp.WorkspaceEdit.Changes {
11071109
seenSet := collections.Set[lsproto.TextEdit]{}
11081110
seenChanges[doc] = &seenSet
@@ -1117,7 +1119,7 @@ func combineRenameResponse(defaultProject *project.Project, results map[tspath.P
11171119
}
11181120
for projectID, resp := range results {
11191121
if projectID != defaultProject.Id() {
1120-
if resp.WorkspaceEdit != nil {
1122+
if resp.WorkspaceEdit != nil && resp.WorkspaceEdit.Changes != nil {
11211123
for doc, changes := range *resp.WorkspaceEdit.Changes {
11221124
seenSet, ok := seenChanges[doc]
11231125
if !ok {

internal/project/session.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const (
3131
UpdateReasonDidChangeCompilerOptionsForInferredProjects
3232
UpdateReasonRequestedLanguageServicePendingChanges
3333
UpdateReasonRequestedLanguageServiceProjectNotLoaded
34+
UpdateReasonRequestedLanguageServiceForFileNotOpen
3435
UpdateReasonRequestedLanguageServiceProjectDirty
3536
)
3637

@@ -422,7 +423,7 @@ func (s *Session) GetLanguageService(ctx context.Context, uri lsproto.DocumentUr
422423
return languageService, nil
423424
}
424425

425-
func (s *Session) GetProjectsForFile(ctx context.Context, uri lsproto.DocumentUri) (*Project, *ls.LanguageService, []*Project, error) {
426+
func (s *Session) GetLanguageServiceAndProjectsForFile(ctx context.Context, uri lsproto.DocumentUri) (*Project, *ls.LanguageService, []*Project, error) {
426427
snapshot, project, defaultLs, err := s.getSnapshotAndDefaultProject(ctx, uri)
427428
if err != nil {
428429
return nil, nil, nil, err
@@ -432,6 +433,24 @@ func (s *Session) GetProjectsForFile(ctx context.Context, uri lsproto.DocumentUr
432433
return project, defaultLs, allProjects, nil
433434
}
434435

436+
func (s *Session) GetProjectsForFile(ctx context.Context, uri lsproto.DocumentUri) ([]*Project, error) {
437+
// !!! sheetal : should not retain this project? probably
438+
snapshot, overlays, updatedSnapshot := s.getSnapshot(ctx, uri, "")
439+
if !updatedSnapshot {
440+
// The current snapshot does not have an up to date project for the URI,
441+
// so we need to update the snapshot to ensure the project is loaded.
442+
// !!! Allow multiple projects to update in parallel
443+
snapshot = s.UpdateSnapshot(ctx, overlays, SnapshotChange{
444+
reason: UpdateReasonRequestedLanguageServiceForFileNotOpen,
445+
requestedURIs: []lsproto.DocumentUri{uri},
446+
})
447+
}
448+
449+
// !!! TODO: sheetal: Get other projects that contain the file with symlink
450+
allProjects := snapshot.GetProjectsContainingFile(uri)
451+
return allProjects, nil
452+
}
453+
435454
func (s *Session) GetLanguageServiceForProjectWithFile(ctx context.Context, project *Project, uri lsproto.DocumentUri) *ls.LanguageService {
436455
snapshot, overlays, updatedSnapshot := s.getSnapshot(ctx, "", project.Id())
437456
// update the snapshot

internal/project/snapshot.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ func (s *Snapshot) Clone(ctx context.Context, change SnapshotChange, overlays ma
198198
logger.Logf("Reason: RequestedLanguageService (pending file changes) - %v, %v", change.requestedURIs, change.requestedProjectUpdates)
199199
case UpdateReasonRequestedLanguageServiceProjectNotLoaded:
200200
logger.Logf("Reason: RequestedLanguageService (project not loaded) - %v", change.requestedURIs)
201+
case UpdateReasonRequestedLanguageServiceForFileNotOpen:
202+
logger.Logf("Reason: RequestedLanguageService (file not open) - %v", change.requestedURIs)
201203
case UpdateReasonRequestedLanguageServiceProjectDirty:
202204
logger.Logf("Reason: RequestedLanguageService (project dirty) - %v, %v", change.requestedURIs, change.requestedProjectUpdates)
203205
}

testdata/baselines/reference/fourslash/findAllReferences/isDefinitionAcrossModuleProjects.baseline.jsonc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,27 @@
4343
// FC() { },
4444
// };
4545

46+
// === /home/src/workspaces/project/a2/index.ts ===
47+
// import { NS } from "../b";
48+
// import { [|I|] } from "../c";
49+
//
50+
// declare module "../b" {
51+
// export namespace NS {
52+
// export function FA();
53+
// }
54+
// }
55+
//
56+
// declare module "../c" {
57+
// export interface [|I|] {
58+
// FA();
59+
// }
60+
// }
61+
//
62+
// const ia: [|I|] = {
63+
// FA: NS.FA,
64+
// FC() { },
65+
// };
66+
4667
// === /home/src/workspaces/project/c/index.ts ===
4768
// export namespace NS {
4869
// export function FC() {}
@@ -97,6 +118,27 @@
97118

98119

99120
// === findAllReferences ===
121+
// === /home/src/workspaces/project/a/index.ts ===
122+
// import { NS } from "../b";
123+
// import { [|I|] } from "../c";
124+
//
125+
// declare module "../b" {
126+
// export namespace NS {
127+
// export function FA();
128+
// }
129+
// }
130+
//
131+
// declare module "../c" {
132+
// export interface [|I|] {
133+
// FA();
134+
// }
135+
// }
136+
//
137+
// const ia: [|I|] = {
138+
// FA: NS.FA,
139+
// FC() { },
140+
// };
141+
100142
// === /home/src/workspaces/project/a2/index.ts ===
101143
// import { NS } from "../b";
102144
// import { [|I|] } from "../c";

testdata/baselines/reference/lspservertests/findAllRefs/find-refs-to-decl-in-other-proj-when-proj-is-loaded-and-refd-proj-loading-is-disabled-and-proj-ref-redirects-are-disabled-and-a-decl-map-is-present.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,15 @@ Config File Names::
122122
// import { [|B|] } from "../b/lib";
123123
// const b: /*FIND ALL REFS*/[|B|] = new [|B|]();
124124

125+
// === /user/username/projects/myproject/b/helper.ts ===
126+
// import { [|B|] } from ".";
127+
// const b: [|B|] = new [|B|]();
128+
129+
// === /user/username/projects/myproject/b/index.ts ===
130+
// export class [|B|] {
131+
// M() {}
132+
// }
133+
125134
// === /user/username/projects/myproject/b/lib/index.d.ts ===
126135
// export declare class [|B|] {
127136
// M(): void;

testdata/baselines/reference/lspservertests/findAllRefs/find-refs-to-decl-in-other-proj-when-proj-is-loaded-and-refd-proj-loading-is-disabled-and-proj-ref-redirects-are-enabled-and-a-decl-map-is-present.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,15 @@ Config File Names::
122122
// import { [|B|] } from "../b/lib";
123123
// const b: /*FIND ALL REFS*/[|B|] = new [|B|]();
124124

125+
// === /user/username/projects/myproject/b/helper.ts ===
126+
// import { [|B|] } from ".";
127+
// const b: [|B|] = new [|B|]();
128+
129+
// === /user/username/projects/myproject/b/index.ts ===
130+
// export class [|B|] {
131+
// M() {}
132+
// }
133+
125134
// === /user/username/projects/myproject/b/lib/index.d.ts ===
126135
// export declare class [|B|] {
127136
// M(): void;

testdata/baselines/reference/lspservertests/findAllRefs/find-refs-to-decl-in-other-proj-when-proj-is-loaded-and-refd-proj-loading-is-enabled-and-proj-ref-redirects-are-disabled-and-a-decl-map-is-present.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,15 @@ Config File Names::
122122
// import { [|B|] } from "../b/lib";
123123
// const b: /*FIND ALL REFS*/[|B|] = new [|B|]();
124124

125+
// === /user/username/projects/myproject/b/helper.ts ===
126+
// import { [|B|] } from ".";
127+
// const b: [|B|] = new [|B|]();
128+
129+
// === /user/username/projects/myproject/b/index.ts ===
130+
// export class [|B|] {
131+
// M() {}
132+
// }
133+
125134
// === /user/username/projects/myproject/b/lib/index.d.ts ===
126135
// export declare class [|B|] {
127136
// M(): void;

0 commit comments

Comments
 (0)