Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions _packages/native-preview/src/api/async/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,25 @@ export class Checker {
return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined);
}

getSymbolOfSourceFile(file: DocumentIdentifier): Promise<Symbol | undefined>;
getSymbolOfSourceFile(files: readonly DocumentIdentifier[]): Promise<(Symbol | undefined)[]>;
async getSymbolOfSourceFile(fileOrFiles: DocumentIdentifier | readonly DocumentIdentifier[]): Promise<Symbol | (Symbol | undefined)[] | undefined> {
if (Array.isArray(fileOrFiles)) {
const data = await this.client.apiRequest<(SymbolResponse | null)[]>("getSymbolsOfSourceFiles", {
snapshot: this.snapshotId,
project: this.project.id,
files: fileOrFiles,
});
return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined);
}
const data = await this.client.apiRequest<SymbolResponse | null>("getSymbolOfSourceFile", {
snapshot: this.snapshotId,
project: this.project.id,
file: fileOrFiles as DocumentIdentifier,
});
return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined;
}

/**
* Get the type of a symbol. Always returns a type; for symbols whose type
* cannot be determined the checker yields the error type (use
Expand Down
19 changes: 19 additions & 0 deletions _packages/native-preview/src/api/sync/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,25 @@ export class Checker {
return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined);
}

getSymbolOfSourceFile(file: DocumentIdentifier): Symbol | undefined;
getSymbolOfSourceFile(files: readonly DocumentIdentifier[]): (Symbol | undefined)[];
getSymbolOfSourceFile(fileOrFiles: DocumentIdentifier | readonly DocumentIdentifier[]): Symbol | (Symbol | undefined)[] | undefined {
if (Array.isArray(fileOrFiles)) {
const data = this.client.apiRequest<(SymbolResponse | null)[]>("getSymbolsOfSourceFiles", {
snapshot: this.snapshotId,
project: this.project.id,
files: fileOrFiles,
});
return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined);
}
const data = this.client.apiRequest<SymbolResponse | null>("getSymbolOfSourceFile", {
snapshot: this.snapshotId,
project: this.project.id,
file: fileOrFiles as DocumentIdentifier,
});
return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined;
}

/**
* Get the type of a symbol. Always returns a type; for symbols whose type
* cannot be determined the checker yields the error type (use
Expand Down
47 changes: 47 additions & 0 deletions _packages/native-preview/test/async/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,53 @@ describe("Snapshot", () => {
}
});

test("getSymbolOfSourceFile", async () => {
const api = spawnAPI();
try {
const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const moduleSymbol = await project.checker.getSymbolOfSourceFile("/src/foo.ts");
assert.ok(moduleSymbol);
const exports = await moduleSymbol.getExports();
assert.ok(exports.has(escapeLeadingUnderscores("foo")));
}
finally {
await api.close();
}
});

test("getSymbolOfSourceFile returns undefined for a non-module file", async () => {
const api = spawnAPI({
"/tsconfig.json": "{}",
"/src/script.ts": `const x = 1;`,
});
try {
const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const symbol = await project.checker.getSymbolOfSourceFile("/src/script.ts");
assert.equal(symbol, undefined);
}
finally {
await api.close();
}
});

test("getSymbolOfSourceFile batched", async () => {
const api = spawnAPI();
try {
const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const symbols = await project.checker.getSymbolOfSourceFile(["/src/index.ts", "/src/foo.ts"]);
assert.equal(symbols.length, 2);
assert.ok(symbols[0]);
assert.ok(symbols[1]);
assert.strictEqual(symbols[1], await project.checker.getSymbolOfSourceFile("/src/foo.ts"));
}
finally {
await api.close();
}
});

test("getTypeOfSymbol", async () => {
const api = spawnAPI();
try {
Expand Down
47 changes: 47 additions & 0 deletions _packages/native-preview/test/sync/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,53 @@ describe("Snapshot", () => {
}
});

test("getSymbolOfSourceFile", () => {
const api = spawnAPI();
try {
const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const moduleSymbol = project.checker.getSymbolOfSourceFile("/src/foo.ts");
assert.ok(moduleSymbol);
const exports = moduleSymbol.getExports();
assert.ok(exports.has(escapeLeadingUnderscores("foo")));
}
finally {
api.close();
}
});

test("getSymbolOfSourceFile returns undefined for a non-module file", () => {
const api = spawnAPI({
"/tsconfig.json": "{}",
"/src/script.ts": `const x = 1;`,
});
try {
const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const symbol = project.checker.getSymbolOfSourceFile("/src/script.ts");
assert.equal(symbol, undefined);
}
finally {
api.close();
}
});

test("getSymbolOfSourceFile batched", () => {
const api = spawnAPI();
try {
const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" });
const project = snapshot.getProject("/tsconfig.json")!;
const symbols = project.checker.getSymbolOfSourceFile(["/src/index.ts", "/src/foo.ts"]);
assert.equal(symbols.length, 2);
assert.ok(symbols[0]);
assert.ok(symbols[1]);
assert.strictEqual(symbols[1], project.checker.getSymbolOfSourceFile("/src/foo.ts"));
}
finally {
api.close();
}
});

test("getTypeOfSymbol", () => {
const api = spawnAPI();
try {
Expand Down
16 changes: 16 additions & 0 deletions internal/api/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ const (
MethodGetDefaultProjectForFile Method = "getDefaultProjectForFile"
MethodGetSymbolAtPosition Method = "getSymbolAtPosition"
MethodGetSymbolsAtPositions Method = "getSymbolsAtPositions"
MethodGetSymbolOfSourceFile Method = "getSymbolOfSourceFile"
MethodGetSymbolsOfSourceFiles Method = "getSymbolsOfSourceFiles"
MethodGetSymbolAtLocation Method = "getSymbolAtLocation"
MethodGetSymbolsAtLocations Method = "getSymbolsAtLocations"
MethodGetTypeOfSymbol Method = "getTypeOfSymbol"
Expand Down Expand Up @@ -400,6 +402,8 @@ var unmarshalers = map[Method]func([]byte) (any, error){
MethodGetSymbolsAtPositions: unmarshallerFor[GetSymbolsAtPositionsParams],
MethodGetSymbolAtLocation: unmarshallerFor[GetSymbolAtLocationParams],
MethodGetSymbolsAtLocations: unmarshallerFor[GetSymbolsAtLocationsParams],
MethodGetSymbolOfSourceFile: unmarshallerFor[GetSymbolOfSourceFileParams],
MethodGetSymbolsOfSourceFiles: unmarshallerFor[GetSymbolsOfSourceFilesParams],
MethodGetTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams],
MethodGetTypesOfSymbols: unmarshallerFor[GetTypesOfSymbolsParams],
MethodGetDeclaredTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams],
Expand Down Expand Up @@ -602,6 +606,18 @@ type GetSymbolsAtPositionsParams struct {
Positions []uint32 `json:"positions"`
}

type GetSymbolOfSourceFileParams struct {
Snapshot SnapshotID `json:"snapshot"`
Project ProjectID `json:"project"`
File DocumentIdentifier `json:"file"`
}

type GetSymbolsOfSourceFilesParams struct {
Snapshot SnapshotID `json:"snapshot"`
Project ProjectID `json:"project"`
Files []DocumentIdentifier `json:"files"`
}

type GetSymbolAtLocationParams struct {
Snapshot SnapshotID `json:"snapshot"`
Project ProjectID `json:"project"`
Expand Down
47 changes: 47 additions & 0 deletions internal/api/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,10 @@ func (s *Session) HandleRequest(ctx context.Context, method string, params json.
return s.handleGetSymbolAtLocation(ctx, parsed.(*GetSymbolAtLocationParams))
case string(MethodGetSymbolsAtLocations):
return s.handleGetSymbolsAtLocations(ctx, parsed.(*GetSymbolsAtLocationsParams))
case string(MethodGetSymbolOfSourceFile):
return s.handleGetSymbolOfSourceFile(ctx, parsed.(*GetSymbolOfSourceFileParams))
case string(MethodGetSymbolsOfSourceFiles):
return s.handleGetSymbolsOfSourceFiles(ctx, parsed.(*GetSymbolsOfSourceFilesParams))
case string(MethodGetTypeOfSymbol):
return s.handleGetTypeOfSymbol(ctx, parsed.(*GetTypeOfSymbolParams))
case string(MethodGetTypesOfSymbols):
Expand Down Expand Up @@ -1314,6 +1318,49 @@ func (s *Session) handleGetSymbolAtPosition(ctx context.Context, params *GetSymb
return setup.newSymbolResponse(symbol), nil
}

// handleGetSymbolOfSourceFile returns the module symbol for a source file, if any.
// For non-module (script) files, returns nil.
func (s *Session) handleGetSymbolOfSourceFile(ctx context.Context, params *GetSymbolOfSourceFileParams) (*SymbolResponse, error) {
setup, err := s.setupChecker(ctx, params.Snapshot, params.Project)
if err != nil {
return nil, err
}
defer setup.done()

sourceFile := setup.program.GetSourceFile(params.File.ToFileName())
if sourceFile == nil {
return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, params.File)
}

symbol := setup.checker.GetSymbolAtLocation(sourceFile.AsNode())
if symbol == nil {
return nil, nil
}
return setup.newSymbolResponse(symbol), nil
}

// handleGetSymbolsOfSourceFiles returns the module symbols for multiple source files.
func (s *Session) handleGetSymbolsOfSourceFiles(ctx context.Context, params *GetSymbolsOfSourceFilesParams) ([]*SymbolResponse, error) {
setup, err := s.setupChecker(ctx, params.Snapshot, params.Project)
if err != nil {
return nil, err
}
defer setup.done()

results := make([]*SymbolResponse, len(params.Files))
for i, file := range params.Files {
sourceFile := setup.program.GetSourceFile(file.ToFileName())
if sourceFile == nil {
return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, file)
}
symbol := setup.checker.GetSymbolAtLocation(sourceFile.AsNode())
if symbol != nil {
results[i] = setup.newSymbolResponse(symbol)
}
}
return results, nil
}

// handleGetSymbolsAtPositions returns symbols at multiple positions in a file.
func (s *Session) handleGetSymbolsAtPositions(ctx context.Context, params *GetSymbolsAtPositionsParams) ([]*SymbolResponse, error) {
setup, err := s.setupChecker(ctx, params.Snapshot, params.Project)
Expand Down