diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index bd31ca0764d..6ad08cfc974 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -16,6 +16,7 @@ "Programming Languages" ], "dependencies": { + "jsonc-parser": "^2.0.1", "semver": "4.3.6", "vscode-extension-telemetry": "0.0.17", "vscode-nls": "^3.2.4" @@ -40,7 +41,11 @@ "onCommand:javascript.goToProjectConfig", "onCommand:typescript.goToProjectConfig", "onCommand:typescript.openTsServerLog", - "onCommand:workbench.action.tasks.runTask" + "onCommand:workbench.action.tasks.runTask", + "workspaceContains:**/tsconfig.json", + "workspaceContains:**/jsconfig.json", + "workspaceContains:**/tsconfig.*.json", + "workspaceContains:**/jsconfig.*.json" ], "main": "./out/extension", "contributes": { @@ -676,4 +681,4 @@ } ] } -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index 521da08e6dc..2a6dd787d06 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -32,6 +32,10 @@ export function activate( context.subscriptions.push(new TypeScriptTaskProviderManager(lazyClientHost.map(x => x.serviceClient))); context.subscriptions.push(new LanguageConfigurationManager()); + import('./features/tsconfig').then(module => { + context.subscriptions.push(module.register()); + }); + const supportedLanguage = [].concat.apply([], standardLanguageDescriptions.map(x => x.modeIds).concat(plugins.map(x => x.languages))); function didOpenTextDocument(textDocument: vscode.TextDocument): boolean { if (isSupportedDocument(supportedLanguage, textDocument)) { diff --git a/extensions/typescript-language-features/src/features/tsconfig.ts b/extensions/typescript-language-features/src/features/tsconfig.ts new file mode 100644 index 00000000000..b4d27138927 --- /dev/null +++ b/extensions/typescript-language-features/src/features/tsconfig.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as jsonc from 'jsonc-parser'; +import * as vscode from 'vscode'; +import { join, dirname } from 'path'; + +class TsconfigLinkProvider implements vscode.DocumentLinkProvider { + + public provideDocumentLinks( + document: vscode.TextDocument, + _token: vscode.CancellationToken + ): vscode.ProviderResult { + const root = jsonc.parseTree(document.getText()); + if (!root) { + return null; + } + + return this.getNodes(root).map(node => + new vscode.DocumentLink( + this.getRange(document, node), + this.getTarget(document, node))); + } + + private getNodes(root: jsonc.Node): ReadonlyArray { + const nodes: jsonc.Node[] = []; + const extendsNode = jsonc.findNodeAtLocation(root, ['extends']); + if (this.isPathValue(extendsNode)) { + nodes.push(extendsNode); + } + + const referencesNode = jsonc.findNodeAtLocation(root, ['references']); + if (referencesNode && referencesNode.type === 'array' && referencesNode.children) { + for (const child of referencesNode.children) { + const path = jsonc.findNodeAtLocation(child, ['path']); + if (this.isPathValue(path)) { + nodes.push(path); + } + } + } + + return nodes; + } + + private isPathValue(extendsNode: jsonc.Node | undefined): extendsNode is jsonc.Node { + return extendsNode && extendsNode.type === 'string' && extendsNode.value; + } + + private getTarget(document: vscode.TextDocument, node: jsonc.Node): vscode.Uri { + return vscode.Uri.file(join(dirname(document.uri.fsPath), node!.value)); + } + + private getRange(document: vscode.TextDocument, node: jsonc.Node) { + const offset = node!.offset; + const start = document.positionAt(offset + 1); + const end = document.positionAt(offset + (node!.length - 1)); + return new vscode.Range(start, end); + } +} + +export function register() { + const patterns: vscode.GlobPattern[] = [ + '**/[jt]sconfig.json', + '**/[jt]sconfig.*.json', + ]; + + const selector: vscode.DocumentSelector = patterns.map((pattern): vscode.DocumentFilter => ({ + language: 'jsonc', + pattern: pattern + })); + return vscode.languages.registerDocumentLinkProvider(selector, new TsconfigLinkProvider()); +} diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 39422f56063..3f1d2b4ed86 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -835,6 +835,10 @@ json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" +jsonc-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.1.tgz#9d23cd2709714fff508a1a6679d82135bee1ae60" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"