Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

Commit

Permalink
add go to definition support to ts/js
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesbirtles committed May 5, 2019
1 parent 4653e9e commit 8e20576
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Currently Supported:
- Formatting (via [prettier](https://github.com/prettier/prettier))
- Symbols in Outline panel
- Autocompletions
- Go to definition

## How can I use it?

Expand Down
10 changes: 10 additions & 0 deletions src/api/fragmentPositions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ColorPresentation,
SymbolInformation,
Location,
LocationLink,
} from './interfaces';

export function mapRangeToParent(fragment: Fragment, range: Range): Range {
Expand Down Expand Up @@ -90,3 +91,12 @@ export function mapSymbolInformationToParent(
): SymbolInformation {
return { ...info, location: mapLocationToParent(fragment, info.location) };
}

export function mapLocationLinkToParent(fragment: Fragment, def: LocationLink): LocationLink {
return LocationLink.create(
def.targetUri,
def.targetRange,
def.targetSelectionRange,
def.originSelectionRange ? mapRangeToParent(fragment, def.originSelectionRange) : undefined,
);
}
14 changes: 14 additions & 0 deletions src/api/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
SymbolInformation,
Location,
SymbolKind,
DefinitionLink,
LocationLink,
} from 'vscode-languageserver-types';
import { Document } from './Document';

Expand All @@ -43,6 +45,8 @@ export {
SymbolInformation,
Location,
SymbolKind,
DefinitionLink,
LocationLink,
};

export type Resolvable<T> = T | Promise<T>;
Expand Down Expand Up @@ -135,6 +139,16 @@ export namespace DocumentSymbolsProvider {
}
}

export interface DefinitionsProvider {
getDefinitions(document: Document, position: Position): Resolvable<DefinitionLink[]>;
}

export namespace DefinitionsProvider {
export function is(obj: any): obj is DefinitionsProvider {
return typeof obj.getDefinitions === 'function';
}
}

export interface Fragment extends Document {
details: FragmentDetails;

Expand Down
24 changes: 23 additions & 1 deletion src/api/wrapFragmentPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ColorPresentation,
DocumentSymbolsProvider,
SymbolInformation,
DefinitionsProvider,
} from './interfaces';
import { Document } from './Document';
import {
Expand All @@ -28,6 +29,7 @@ import {
mapRangeToFragment,
mapColorPresentationToParent,
mapSymbolInformationToParent,
mapLocationLinkToParent,
} from './fragmentPositions';
import { Host, OnRegister } from './Host';

Expand Down Expand Up @@ -108,13 +110,18 @@ export function wrapFragmentPlugin<P extends Plugin>(
plugin.getCompletions = async function(
document: Document,
position: Position,
triggerCharacter?: string,
): Promise<CompletionItem[]> {
const fragment = getFragment(document);
if (!fragment || !fragment.isInFragment(position)) {
return [];
}

const items = await getCompletions(fragment, fragment.positionInFragment(position));
const items = await getCompletions(
fragment,
fragment.positionInFragment(position),
triggerCharacter,
);
return items.map(item => mapCompletionItemToParent(fragment, item));
};
}
Expand Down Expand Up @@ -189,5 +196,20 @@ export function wrapFragmentPlugin<P extends Plugin>(
};
}

if (DefinitionsProvider.is(plugin)) {
const getDefinitions: DefinitionsProvider['getDefinitions'] = plugin.getDefinitions.bind(
plugin,
);
plugin.getDefinitions = async function(document, position) {
const fragment = getFragment(document);
if (!fragment || !fragment.isInFragment(position)) {
return [];
}

const items = await getDefinitions(fragment, fragment.positionInFragment(position));
return items.map(item => mapLocationLinkToParent(fragment, item));
};
}

return plugin;
}
19 changes: 19 additions & 0 deletions src/lib/documents/DocumentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
TextDocumentIdentifier,
CompletionItem,
TextEdit,
DefinitionLink,
} from 'vscode-languageserver-types';
import { PluginHost, ExecuteMode } from '../PluginHost';
import { flatten } from '../../utils';
Expand Down Expand Up @@ -205,4 +206,22 @@ export class DocumentManager extends PluginHost {
),
);
}

async getDefinitions(
textDocument: TextDocumentIdentifier,
position: Position,
): Promise<DefinitionLink[]> {
const document = this.documents.get(textDocument.uri);
if (!document) {
throw new Error('Cannot call methods on an unopened document');
}

return flatten(
await this.execute<DefinitionLink[]>(
'getDefinitions',
[document, position],
ExecuteMode.Collect,
),
);
}
}
47 changes: 43 additions & 4 deletions src/plugins/TypeScriptPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import ts, { NavigationTree } from 'typescript';
import URL from 'vscode-uri';
import {
DiagnosticsProvider,
Document,
Expand All @@ -16,6 +15,9 @@ import {
SymbolInformation,
CompletionsProvider,
CompletionItem,
DefinitionsProvider,
DefinitionLink,
LocationLink,
} from '../api';
import {
convertRange,
Expand All @@ -25,14 +27,17 @@ import {
getCommitCharactersForScriptElement,
} from './typescript/utils';
import { getLanguageServiceForDocument, CreateDocument } from './typescript/service';
import { pathToUrl } from '../utils';
import { TextDocument } from '../lib/documents/TextDocument';

export class TypeScriptPlugin
implements
DiagnosticsProvider,
HoverProvider,
OnRegister,
DocumentSymbolsProvider,
CompletionsProvider {
CompletionsProvider,
DefinitionsProvider {
public static matchFragment(fragment: Fragment) {
return fragment.details.attributes.tag == 'script';
}
Expand All @@ -50,7 +55,7 @@ export class TypeScriptPlugin
onRegister(host: Host) {
this.host = host;
this.createDocument = (fileName, content) => {
const uri = URL.file(fileName).toString();
const uri = pathToUrl(fileName);
const document = host.openDocument({
languageId: '',
text: content,
Expand Down Expand Up @@ -172,7 +177,6 @@ export class TypeScriptPlugin
triggerCharacter: triggerCharacter as any,
},
);
console.log(completions);

if (!completions) {
return [];
Expand All @@ -188,4 +192,39 @@ export class TypeScriptPlugin
};
});
}

getDefinitions(document: Document, position: Position): DefinitionLink[] {
const lang = getLanguageServiceForDocument(document, this.createDocument);

const defs = lang.getDefinitionAndBoundSpan(
document.getFilePath()!,
document.offsetAt(position),
);

if (!defs || !defs.definitions) {
return [];
}

const docs = new Map<string, Document>([[document.getFilePath()!, document]]);

return defs.definitions
.map(def => {
let defDoc = docs.get(def.fileName);
if (!defDoc) {
defDoc = new TextDocument(
pathToUrl(def.fileName),
ts.sys.readFile(def.fileName) || '',
);
docs.set(def.fileName, defDoc);
}

return LocationLink.create(
pathToUrl(def.fileName),
convertRange(defDoc, def.textSpan),
convertRange(defDoc, def.textSpan),
convertRange(document, defs.textSpan),
);
})
.filter(res => !!res) as DefinitionLink[];
}
}
2 changes: 1 addition & 1 deletion src/plugins/typescript/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export function createLanguageService(
return doc;
}

return ts.ScriptSnapshot.fromString(ts.sys.readFile(fileName) || '');
return ts.ScriptSnapshot.fromString(this.readFile!(fileName) || '');
},
getCurrentDirectory: () => workspacePath,
getDefaultLibFileName: ts.getDefaultLibFilePath,
Expand Down
2 changes: 2 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export function startServer() {
documentFormattingProvider: true,
colorProvider: true,
documentSymbolProvider: true,
definitionProvider: true,
},
};
});
Expand Down Expand Up @@ -82,6 +83,7 @@ export function startServer() {
manager.getColorPresentations(evt.textDocument, evt.range, evt.color),
);
connection.onDocumentSymbol(evt => manager.getDocumentSymbols(evt.textDocument));
connection.onDefinition(evt => manager.getDefinitions(evt.textDocument, evt.position));

manager.on('documentChange', async document => {
const diagnostics = await manager.getDiagnostics({ uri: document.getURL() });
Expand Down
4 changes: 4 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export function urlToPath(stringUrl: string): string | null {
return url.fsPath.replace(/\\/g, '/');
}

export function pathToUrl(path: string) {
return URL.file(path).toString();
}

export function flatten<T>(arr: T[][]): T[] {
return arr.reduce((all, item) => [...all, ...item], []);
}

0 comments on commit 8e20576

Please sign in to comment.