Skip to content

Commit d9e969c

Browse files
authored
Emit occurrences for string literals (#184)
* Emit occurrences for string literals Previously, scip-typescript only emitted occurrences for identifiers. Now, we additionally emit occurrences for string literals so that features like "Go to definition" work with `import` statements and other locations where string literals reference actual symbols. * Prettier * Remove cwd from documentation signatures for stable SCIP output * Use replace instead of replaceAll for Node v14 compatibility * Include the file extension in the module name
1 parent 92b952c commit d9e969c

28 files changed

+165
-11
lines changed

.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"editor.formatOnSave": true
3+
}

snapshots/input/syntax/src/import.ts

+4
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ export function useEverything(): string {
1111
newFunction()
1212
)
1313
}
14+
15+
export function dynamicImport(): Promise<void> {
16+
return import('./function').then(c => c.newFunction())
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
interface SomeInterface {
2+
a: number
3+
b: number
4+
c: number
5+
}
6+
// "Go to definition" does not work for the 'a', 'b' and 'c' string literals
7+
// below when using tsserver so it's fine that scip-typescript does not emit
8+
// occurrences here either.
9+
export type OmitInterface = Omit<SomeInterface, 'a' | 'b'>
10+
export type PickInterface = Pick<SomeInterface, 'b' | 'c'>

snapshots/output/multi-project/packages/a/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export function a(): string {
2+
// definition @example/a 1.0.0 src/`index.ts`/
3+
//documentation ```ts\nmodule "index.ts"\n```
24
// ^ definition @example/a 1.0.0 src/`index.ts`/a().
35
// documentation ```ts\nfunction a(): string\n```
46
return ''

snapshots/output/multi-project/packages/b/src/b.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { a } from '@example/a/src'
2+
// definition @example/b 1.0.0 src/`b.ts`/
3+
//documentation ```ts\nmodule "b.ts"\n```
24
// ^ reference @example/a 1.0.0 src/`index.ts`/a().
5+
// ^^^^^^^^^^^^^^^^ reference @example/a 1.0.0 src/`index.ts`/
36

47
export function b() {
58
// ^ definition @example/b 1.0.0 src/`b.ts`/b().

snapshots/output/pure-js/src/main.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
function fib(n) {
2+
// definition pure-js 1.0.0 src/`main.js`/
3+
//documentation ```ts\nmodule "main.js"\n```
24
// ^^^ definition pure-js 1.0.0 src/`main.js`/fib().
35
// documentation ```ts\nfunction fib(n: any): any\n```
46
// ^ definition pure-js 1.0.0 src/`main.js`/fib().(n)

snapshots/output/react/src/LoaderInput.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import React from 'react'
2+
// definition react-example 1.0.0 src/`LoaderInput.tsx`/
3+
//documentation ```ts\nmodule "LoaderInput.tsx"\n```
24
// ^^^^^ reference @types/react 17.0.0 `index.d.ts`/React/
5+
// ^^^^^^^ reference @types/react 17.0.0 `index.d.ts`/
36

47
/** Takes loading prop, input component as child */
58
interface Props {

snapshots/output/react/src/MyTSXElement.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import React from 'react'
2+
// definition react-example 1.0.0 src/`MyTSXElement.tsx`/
3+
//documentation ```ts\nmodule "MyTSXElement.tsx"\n```
24
// ^^^^^ reference @types/react 17.0.0 `index.d.ts`/React/
5+
// ^^^^^^^ reference @types/react 17.0.0 `index.d.ts`/
36

47
export interface MyProps {}
58
// ^^^^^^^ definition react-example 1.0.0 src/`MyTSXElement.tsx`/MyProps#

snapshots/output/react/src/UseMyTSXElement.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import React from "react";
2+
// definition react-example 1.0.0 src/`UseMyTSXElement.tsx`/
3+
//documentation ```ts\nmodule "UseMyTSXElement.tsx"\n```
24
// ^^^^^ reference @types/react 17.0.0 `index.d.ts`/React/
5+
// ^^^^^^^ reference @types/react 17.0.0 `index.d.ts`/
36

47
import { MyProps, MyTSXElement } from "./MyTSXElement";
58
// ^^^^^^^ reference react-example 1.0.0 src/`MyTSXElement.tsx`/MyProps#
69
// ^^^^^^^^^^^^ reference react-example 1.0.0 src/`MyTSXElement.tsx`/MyTSXElement.
10+
// ^^^^^^^^^^^^^^^^ reference react-example 1.0.0 src/`MyTSXElement.tsx`/
711

812
export const _: React.FunctionComponent<MyProps> =
913
// ^ definition react-example 1.0.0 src/`UseMyTSXElement.tsx`/_.

snapshots/output/syntax/src/accessors.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
class C {
2+
// definition syntax 1.0.0 src/`accessors.ts`/
3+
//documentation ```ts\nmodule "accessors.ts"\n```
24
// ^ definition syntax 1.0.0 src/`accessors.ts`/C#
35
// documentation ```ts\nclass C\n```
46
_length: number = 0

snapshots/output/syntax/src/class.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export class Class {
2+
// definition syntax 1.0.0 src/`class.ts`/
3+
//documentation ```ts\nmodule "class.ts"\n```
24
// ^^^^^ definition syntax 1.0.0 src/`class.ts`/Class#
35
// documentation ```ts\nclass Class\n```
46
public classProperty: string

snapshots/output/syntax/src/enum.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export enum Enum {
2+
// definition syntax 1.0.0 src/`enum.ts`/
3+
//documentation ```ts\nmodule "enum.ts"\n```
24
// ^^^^ definition syntax 1.0.0 src/`enum.ts`/Enum#
35
// documentation ```ts\nenum Enum\n```
46
A,
+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export function newFunction(): void {}
2+
// definition syntax 1.0.0 src/`function.ts`/
3+
//documentation ```ts\nmodule "function.ts"\n```
24
// ^^^^^^^^^^^ definition syntax 1.0.0 src/`function.ts`/newFunction().
35
// documentation ```ts\nfunction newFunction(): void\n```
46

snapshots/output/syntax/src/import.ts

+23
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import { Class } from './class'
2+
// definition syntax 1.0.0 src/`import.ts`/
3+
//documentation ```ts\nmodule "import.ts"\n```
24
// ^^^^^ reference syntax 1.0.0 src/`class.ts`/Class#
5+
// ^^^^^^^^^ reference syntax 1.0.0 src/`class.ts`/
36
import { Enum } from './enum'
47
// ^^^^ reference syntax 1.0.0 src/`enum.ts`/Enum#
8+
// ^^^^^^^^ reference syntax 1.0.0 src/`enum.ts`/
59
import { newFunction } from './function'
610
// ^^^^^^^^^^^ reference syntax 1.0.0 src/`function.ts`/newFunction().
11+
// ^^^^^^^^^^^^ reference syntax 1.0.0 src/`function.ts`/
712
import { newInterface as renamedInterface } from './interface'
813
// ^^^^^^^^^^^^ reference syntax 1.0.0 src/`interface.ts`/newInterface().
914
// ^^^^^^^^^^^^^^^^ reference syntax 1.0.0 src/`interface.ts`/newInterface().
15+
// ^^^^^^^^^^^^^ reference syntax 1.0.0 src/`interface.ts`/
1016

1117
export function useEverything(): string {
1218
// ^^^^^^^^^^^^^ definition syntax 1.0.0 src/`import.ts`/useEverything().
@@ -27,3 +33,20 @@
2733
)
2834
}
2935

36+
export function dynamicImport(): Promise<void> {
37+
// ^^^^^^^^^^^^^ definition syntax 1.0.0 src/`import.ts`/dynamicImport().
38+
// documentation ```ts\nfunction dynamicImport(): Promise<void>\n```
39+
// ^^^^^^^ reference typescript 4.8.4 lib/`lib.es5.d.ts`/Promise#
40+
// ^^^^^^^ reference typescript 4.8.4 lib/`lib.es2015.iterable.d.ts`/Promise#
41+
// ^^^^^^^ reference typescript 4.8.4 lib/`lib.es2015.promise.d.ts`/Promise.
42+
// ^^^^^^^ reference typescript 4.8.4 lib/`lib.es2015.symbol.wellknown.d.ts`/Promise#
43+
// ^^^^^^^ reference typescript 4.8.4 lib/`lib.es2018.promise.d.ts`/Promise#
44+
return import('./function').then(c => c.newFunction())
45+
// ^^^^^^^^^^^^ reference syntax 1.0.0 src/`function.ts`/
46+
// ^^^^ reference typescript 4.8.4 lib/`lib.es5.d.ts`/Promise#then().
47+
// ^ definition local 3
48+
// documentation ```ts\n(parameter) c: typeof import("/src/function")\n```
49+
// ^ reference local 3
50+
// ^^^^^^^^^^^ reference syntax 1.0.0 src/`function.ts`/newFunction().
51+
}
52+

snapshots/output/syntax/src/inheritance.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { Overloader } from './overload'
2+
// definition syntax 1.0.0 src/`inheritance.ts`/
3+
//documentation ```ts\nmodule "inheritance.ts"\n```
24
// ^^^^^^^^^^ reference syntax 1.0.0 src/`overload.d.ts`/Overloader#
5+
// ^^^^^^^^^^^^ reference syntax 1.0.0 src/`overload.d.ts`/
36

47
export interface Superinterface {
58
// ^^^^^^^^^^^^^^ definition syntax 1.0.0 src/`inheritance.ts`/Superinterface#

snapshots/output/syntax/src/interface.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export interface Interface {
2+
// definition syntax 1.0.0 src/`interface.ts`/
3+
//documentation ```ts\nmodule "interface.ts"\n```
24
// ^^^^^^^^^ definition syntax 1.0.0 src/`interface.ts`/Interface#
35
// documentation ```ts\ninterface Interface\n```
46
property: string

snapshots/output/syntax/src/issue-45.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export namespace example {
2+
// definition syntax 1.0.0 src/`issue-45.d.ts`/
3+
//documentation ```ts\nmodule "issue-45.d.ts"\n```
24
// ^^^^^^^ definition syntax 1.0.0 src/`issue-45.d.ts`/example/
35
// documentation ```ts\nexample: typeof example\n```
46
class Server {

snapshots/output/syntax/src/local.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export function local(): string {
2+
// definition syntax 1.0.0 src/`local.ts`/
3+
//documentation ```ts\nmodule "local.ts"\n```
24
// ^^^^^ definition syntax 1.0.0 src/`local.ts`/local().
35
// documentation ```ts\nfunction local(): string\n```
46
const a = 'a'

snapshots/output/syntax/src/module.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
declare module 'a:b' {
2+
// definition syntax 1.0.0 src/`module.d.ts`/
3+
//documentation ```ts\nmodule "module.d.ts"\n```
4+
// ^^^^^ definition syntax 1.0.0 src/`module.d.ts`/`'a:b'`/
5+
// documentation ```ts\n'a:b': typeof import("a:b")\n```
26
function hello(): string
37
// ^^^^^ definition syntax 1.0.0 src/`module.d.ts`/`'a:b'`/hello().
48
// documentation ```ts\nfunction hello(): string\n```

snapshots/output/syntax/src/namespace.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
declare namespace a {
2+
// definition syntax 1.0.0 src/`namespace.d.ts`/
3+
//documentation ```ts\nmodule "namespace.d.ts"\n```
24
// ^ definition syntax 1.0.0 src/`namespace.d.ts`/a/
35
// documentation ```ts\na: typeof a\n```
46
function hello(): string

snapshots/output/syntax/src/overload.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export interface Overloader {
2+
// definition syntax 1.0.0 src/`overload.d.ts`/
3+
//documentation ```ts\nmodule "overload.d.ts"\n```
24
// ^^^^^^^^^^ definition syntax 1.0.0 src/`overload.d.ts`/Overloader#
35
// documentation ```ts\ninterface Overloader\n```
46
onLiteral(param: 'a'): void

snapshots/output/syntax/src/property-assignment-reference.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import {
2+
// definition syntax 1.0.0 src/`property-assignment-reference.ts`/
3+
//documentation ```ts\nmodule "property-assignment-reference.ts"\n```
24
propertyAssignment,
35
// ^^^^^^^^^^^^^^^^^^ reference syntax 1.0.0 src/`property-assignment.ts`/propertyAssignment().
46
shorthandPropertyAssignment,
57
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference syntax 1.0.0 src/`property-assignment.ts`/shorthandPropertyAssignment().
68
} from './property-assignment'
9+
// ^^^^^^^^^^^^^^^^^^^^^^^ reference syntax 1.0.0 src/`property-assignment.ts`/
710

811
export function run(): string {
912
// ^^^ definition syntax 1.0.0 src/`property-assignment-reference.ts`/run().

snapshots/output/syntax/src/property-assignment.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export function propertyAssignment() {
2+
// definition syntax 1.0.0 src/`property-assignment.ts`/
3+
//documentation ```ts\nmodule "property-assignment.ts"\n```
24
// ^^^^^^^^^^^^^^^^^^ definition syntax 1.0.0 src/`property-assignment.ts`/propertyAssignment().
35
// documentation ```ts\nfunction propertyAssignment(): { a: string; }\n```
46
return { a: 'a' }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
interface SomeInterface {
2+
// definition syntax 1.0.0 src/`string-literals.ts`/
3+
//documentation ```ts\nmodule "string-literals.ts"\n```
4+
// ^^^^^^^^^^^^^ definition syntax 1.0.0 src/`string-literals.ts`/SomeInterface#
5+
// documentation ```ts\ninterface SomeInterface\n```
6+
a: number
7+
// ^ definition syntax 1.0.0 src/`string-literals.ts`/SomeInterface#a.
8+
// documentation ```ts\n(property) a: number\n```
9+
b: number
10+
// ^ definition syntax 1.0.0 src/`string-literals.ts`/SomeInterface#b.
11+
// documentation ```ts\n(property) b: number\n```
12+
c: number
13+
// ^ definition syntax 1.0.0 src/`string-literals.ts`/SomeInterface#c.
14+
// documentation ```ts\n(property) c: number\n```
15+
}
16+
// "Go to definition" does not work for the 'a', 'b' and 'c' string literals
17+
// below when using tsserver so it's fine that scip-typescript does not emit
18+
// occurrences here either.
19+
export type OmitInterface = Omit<SomeInterface, 'a' | 'b'>
20+
// ^^^^^^^^^^^^^ definition syntax 1.0.0 src/`string-literals.ts`/OmitInterface#
21+
// documentation ```ts\ntype OmitInterface\n```
22+
// ^^^^ reference typescript 4.8.4 lib/`lib.es5.d.ts`/Omit#
23+
// ^^^^^^^^^^^^^ reference syntax 1.0.0 src/`string-literals.ts`/SomeInterface#
24+
export type PickInterface = Pick<SomeInterface, 'b' | 'c'>
25+
// ^^^^^^^^^^^^^ definition syntax 1.0.0 src/`string-literals.ts`/PickInterface#
26+
// documentation ```ts\ntype PickInterface\n```
27+
// ^^^^ reference typescript 4.8.4 lib/`lib.es5.d.ts`/Pick#
28+
// ^^^^^^^^^^^^^ reference syntax 1.0.0 src/`string-literals.ts`/SomeInterface#
29+

snapshots/output/syntax/src/type-alias.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
type S = string
2+
// definition syntax 1.0.0 src/`type-alias.ts`/
3+
//documentation ```ts\nmodule "type-alias.ts"\n```
24
// ^ definition syntax 1.0.0 src/`type-alias.ts`/S#
35
// documentation ```ts\ntype S\n```
46

snapshots/output/syntax/src/type-parameter.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export function typeParameter<A, B>(parameter: A, parameter2: B): [A, B] {
2+
// definition syntax 1.0.0 src/`type-parameter.ts`/
3+
//documentation ```ts\nmodule "type-parameter.ts"\n```
24
// ^^^^^^^^^^^^^ definition syntax 1.0.0 src/`type-parameter.ts`/typeParameter().
35
// documentation ```ts\nfunction typeParameter<A, B>(parameter: A, parameter2: B): [A, B]\n```
46
// ^ definition syntax 1.0.0 src/`type-parameter.ts`/typeParameter().[A]

src/FileIndexer.ts

+43-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import path from 'path'
2+
13
import * as ts from 'typescript'
24

5+
import { ProjectOptions } from './CommandLineOptions'
36
import { Counter } from './Counter'
47
import {
58
metaDescriptor,
@@ -25,22 +28,48 @@ export class FileIndexer {
2528
private localCounter = new Counter()
2629
private propertyCounters: Map<string, Counter> = new Map()
2730
private localSymbolTable: Map<ts.Node, LsifSymbol> = new Map()
31+
private workingDirectoryRegExp: RegExp
2832
constructor(
2933
public readonly checker: ts.TypeChecker,
34+
public readonly options: ProjectOptions,
3035
public readonly input: Input,
3136
public readonly document: lsif.lib.codeintel.lsiftyped.Document,
3237
public readonly globalSymbolTable: Map<ts.Node, LsifSymbol>,
3338
public readonly packages: Packages,
3439
public readonly sourceFile: ts.SourceFile
35-
) {}
40+
) {
41+
this.workingDirectoryRegExp = new RegExp(options.cwd, 'g')
42+
}
3643
public index(): void {
44+
this.emitSourceFileOccurrence()
3745
this.visit(this.sourceFile)
3846
}
47+
private emitSourceFileOccurrence(): void {
48+
const symbol = this.lsifSymbol(this.sourceFile)
49+
if (symbol.isEmpty()) {
50+
return
51+
}
52+
this.document.occurrences.push(
53+
new lsif.lib.codeintel.lsiftyped.Occurrence({
54+
range: [0, 0, 0],
55+
symbol: symbol.value,
56+
symbol_roles: lsiftyped.SymbolRole.Definition,
57+
})
58+
)
59+
const moduleName =
60+
this.sourceFile.moduleName || path.basename(this.sourceFile.fileName)
61+
this.document.symbols.push(
62+
new lsiftyped.SymbolInformation({
63+
symbol: symbol.value,
64+
documentation: ['```ts\nmodule "' + moduleName + '"\n```'],
65+
})
66+
)
67+
}
3968
private visit(node: ts.Node): void {
40-
if (ts.isIdentifier(node)) {
69+
if (ts.isIdentifier(node) || ts.isStringLiteralLike(node)) {
4170
const sym = this.getTSSymbolAtLocation(node)
4271
if (sym) {
43-
this.visitIdentifier(node, sym)
72+
this.visitSymbolOccurrence(node, sym)
4473
}
4574
}
4675
ts.forEachChild(node, node => this.visit(node))
@@ -52,6 +81,7 @@ export class FileIndexer {
5281
// This code is directly based off src/services/goToDefinition.ts.
5382
private getTSSymbolAtLocation(node: ts.Node): ts.Symbol | undefined {
5483
const symbol = this.checker.getSymbolAtLocation(node)
84+
5585
// If this is an alias, and the request came at the declaration location
5686
// get the aliased symbol instead. This allows for goto def on an import e.g.
5787
// import {A, B} from "mod";
@@ -71,10 +101,10 @@ export class FileIndexer {
71101
return symbol
72102
}
73103

74-
private visitIdentifier(identifier: ts.Identifier, sym: ts.Symbol): void {
75-
const range = Range.fromNode(identifier).toLsif()
104+
private visitSymbolOccurrence(node: ts.Node, sym: ts.Symbol): void {
105+
const range = Range.fromNode(node).toLsif()
76106
let role = 0
77-
const isDefinition = this.declarationName(identifier.parent) === identifier
107+
const isDefinition = this.declarationName(node.parent) === node
78108
if (isDefinition) {
79109
role |= lsiftyped.SymbolRole.Definition
80110
}
@@ -92,7 +122,7 @@ export class FileIndexer {
92122
})
93123
)
94124
if (isDefinition) {
95-
this.addSymbolInformation(identifier, sym, declaration, lsifSymbol)
125+
this.addSymbolInformation(node, sym, declaration, lsifSymbol)
96126
this.handleShorthandPropertyDefinition(declaration, range)
97127
// Only emit one symbol for definitions sites, see https://github.com/sourcegraph/lsif-typescript/issues/45
98128
break
@@ -137,14 +167,19 @@ export class FileIndexer {
137167
}
138168
}
139169

170+
private hideWorkingDirectory(value: string): string {
171+
return value.replace(this.workingDirectoryRegExp, '')
172+
}
140173
private addSymbolInformation(
141174
node: ts.Node,
142175
sym: ts.Symbol,
143176
declaration: ts.Node,
144177
symbol: LsifSymbol
145178
): void {
146179
const documentation = [
147-
'```ts\n' + this.signatureForDocumentation(node, sym) + '\n```',
180+
'```ts\n' +
181+
this.hideWorkingDirectory(this.signatureForDocumentation(node, sym)) +
182+
'\n```',
148183
]
149184
const docstring = sym.getDocumentationComment(this.checker)
150185
if (docstring.length > 0) {

0 commit comments

Comments
 (0)