Merge branch 'neoclide:master' into master

This commit is contained in:
サポミク 2022-06-21 02:58:20 +09:00 committed by GitHub
commit 7d076e2bf2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 2237 additions and 876 deletions

View file

@ -41,7 +41,7 @@ tsserver understand your code.
installation.
**Note:** tsserver could be quite slow to initialize on big project, exclude
unneunnecessary files in your jsconfig.json/tsconfig.json.
unnecessary files in your jsconfig.json/tsconfig.json.
**Note:** if you're using WSL, copy you project files from mounted dirs to linux home otherwise tsserver will not work properly.
@ -100,10 +100,19 @@ Almost the same as VSCode.
- Code refactor using code actions.
- Find references.
- Signature help.
- Call hierarchy.
- Selection range.
- Semantic tokens.
- Rename symbols support.
- Automatic tag closing.
- Rename imports on file rename, require
[watchman](https://facebook.github.io/watchman/) installed in your \$PATH.
- Search for workspace symbols.
- Inlay hints support using virtual text feature of neovim, which requires:
- TypeScript >= 4.4.0
- Neovim >= 0.4.0
- Enabled by options starts with `typescript.inlayHints` or
`javascript.inlayHints`.
Tsserver module first resolved from your local workspace. If it's not found, use
tsserver from `tsserver.tsdk` configuration or use bundled tsserver with this
@ -117,6 +126,8 @@ for guide of coc.nvim's configuration.
- `tsserver.enable`:Enable tsserver extension, default: `true`
- `tsserver.locale`:Locale of tsserver, default: `""`
- `tsserver.ignoreLocalTsserver`:Always use tsserver module from tsserver.tsdk
or coc-tsserver extension.
- `tsserver.typingsCacheLocation`:Folder path for cache typings, default: `""`
- `tsserver.formatOnType`:Run format on type special characters., default:
`true`
@ -128,10 +139,9 @@ for guide of coc.nvim's configuration.
- `tsserver.log`:Log level of tsserver, default: `"off"`
- `tsserver.trace.server`:Trace level of tsserver, default: `"off"`
- `tsserver.pluginPaths`:Folders contains tsserver plugins, default: `[]`
- `tsserver.debugPort`:Debug port number of tsserver
- `tsserver.watchOptions`:Configure which watching strategies should be used to
keep track of files and directories. Requires using TypeScript 3.8+ in the
workspace, default: undefined.
workspace, default: `undefined`
- `tsserver.reportStyleChecksAsWarnings` default: `true`
- `tsserver.implicitProjectConfig.checkJs`:Enable checkJs for implicit project,
default: `false`
@ -139,27 +149,38 @@ for guide of coc.nvim's configuration.
experimentalDecorators for implicit project, default: `false`
- `tsserver.disableAutomaticTypeAcquisition`:Disable download of typings,
default: `false`
- `tsserver.useBatchedBufferSync`: use batched buffer synchronize support.
- `tsserver.useBatchedBufferSync`: use batched buffer synchronize support, default: `true`
- `tsserver.enableTracing`: Enables tracing TS server performance to a
directory. These trace files can be used to diagnose TS Server performance
issues. The log may contain file paths, source code, and other potentially
sensitive information from your project, default: `false`
- `typescript.check.npmIsInstalled`: Check if npm is installed for [Automatic
Type
Acquisition](https://code.visualstudio.com/docs/nodejs/working-with-javascript#_typings-and-automatic-type-acquisition).
- `typescript.updateImportsOnFileMove.enable`:Enable update imports on file
move., default: `true`
- `typescript.implementationsCodeLens.enable`:Enable codeLens for
implementations, default: `true`
- `typescript.referencesCodeLens.enable`:Enable codeLens for references,
default: `true`
- `typescript.preferences.importModuleSpecifier` default: `"auto"`
- `typescript.referencesCodeLens.showOnAllFunctions`: Enable/disable references CodeLens on all functions in typescript files. Default: `false`
- `typescript.preferences.importModuleSpecifier` default: `"shortest"`
- `typescript.preferences.importModuleSpecifierEnding` default: `"auto"`
- `typescript.preferences.quoteStyle` default: `"single"`
- `typescript.preferences.includePackageJsonAutoImports`: Enable/disable
searching `package.json` dependencies for available auto imports, default:
`"auto"`
- `typescript.suggestionActions.enabled`:Enable/disable suggestion diagnostics
for TypeScript files in the editor. Requires using TypeScript 2.8 or newer in
the workspace., default: `true`
- `typescript.validate.enable`:Enable/disable TypeScript validation., default:
`true`
- `typescript.showUnused`: show unused variable hint, default: `true`.
- `typescript.autoClosingTags`: Enable/disable autoClosing of JSX tags, default: `false`.
- `typescript.autoClosingTags`: Enable/disable autoClosing of JSX tags, default: `true`
- `typescript.suggest.enabled` default: `true`
- `typescript.suggest.paths`:Enable/disable suggest paths in import statement
and require calls, default: `true`
- `typescript.suggest.autoImports`:Enable/disable auto import suggests.,
- `typescript.suggest.autoImports`:Enable/disable auto import suggests,
default: `true`
- `typescript.suggest.completeFunctionCalls`:Enable snippet for method
suggestion, default: `true`
@ -168,6 +189,13 @@ for guide of coc.nvim's configuration.
TypeScript 4.3+ in the workspace, default: `true`
- `typescript.suggest.includeCompletionsWithSnippetText`: Enable snippet completions
from TS Server. Requires using TypeScript 4.3+ in the workspace, default: `true`
- `typescript.suggest.classMemberSnippets.enabled`: Enable/disable
snippet completions for class members. Requires using TypeScript 4.5+ in the
workspace, default: `true`
- `typescript.suggest.jsdoc.generateReturns`: Enable/disable generating
`@return` annotations for JSDoc templates. Requires using TypeScript 4.2+ in
the workspace. default: `true`
- `typescript.suggest.includeAutomaticOptionalChainCompletions`: default: `true`
- `typescript.format.enabled`:Enable/disable format of typescript files.
- `typescript.format.insertSpaceAfterCommaDelimiter` default: `true`
- `typescript.format.insertSpaceAfterConstructor` default: `false`
@ -193,14 +221,15 @@ for guide of coc.nvim's configuration.
- `typescript.format.insertSpaceAfterTypeAssertion` default: `false`
- `typescript.format.placeOpenBraceOnNewLineForFunctions` default: `false`
- `typescript.format.placeOpenBraceOnNewLineForControlBlocks` default: `false`
- `typescript.suggest.includeAutomaticOptionalChainCompletions`: default: `true`
- `javascript.format.enabled`: Enable/disable format for javascript files.
- `javascript.showUnused`: show unused variable hint.
- `javascript.autoClosingTags`: Enable/disable autoClosing of JSX tags, default: `false`.
- `typescript.inlayHints`: inlayHints related options.
- `javascript.format.enabled`: Enable/disable format for javascript files, default: `true`
- `javascript.showUnused`: show unused variable hint, default: `true`
- `javascript.autoClosingTags`: Enable/disable autoClosing of JSX tags, default: `true`
- `javascript.updateImportsOnFileMove.enable` default: `true`
- `javascript.implementationsCodeLens.enable` default: `true`
- `javascript.referencesCodeLens.enable` default: `true`
- `javascript.preferences.importModuleSpecifier` default: `"auto"`
- `javascript.referencesCodeLens.showOnAllFunctions`: Enable/disable references CodeLens on all functions in JavaScript files default: `false`
- `javascript.preferences.importModuleSpecifier` default: `"shortest"`
- `javascript.preferences.importModuleSpecifierEnding` default: `"auto"`
- `javascript.preferences.quoteStyle` default: `"single"`
- `javascript.validate.enable`: Enable/disable JavaScript validation., default:
@ -219,6 +248,13 @@ for guide of coc.nvim's configuration.
- `javascript.suggest.includeCompletionsForImportStatements`: Enable/disable
auto-import-style completions on partially-typed import statements. Requires
using TypeScript 4.3+ in the workspace, default: `true`
- `javascript.suggest.jsdoc.generateReturns`: Enable/disable generating
`@return` annotations for JSDoc templates. Requires using TypeScript 4.2+ in
the workspace. default: `true`
- `javascript.suggest.classMemberSnippets.enabled`: Enable/disable
snippet completions for class members. Requires using TypeScript 4.5+ in the
workspace, default: `true`
- `javascript.suggest.includeAutomaticOptionalChainCompletions`: default: `true`
- `javascript.format.insertSpaceAfterCommaDelimiter` default: `true`
- `javascript.format.insertSpaceAfterConstructor` default: `false`
- `javascript.format.insertSpaceAfterSemicolonInForStatements` default: `true`
@ -243,10 +279,22 @@ for guide of coc.nvim's configuration.
- `javascript.format.insertSpaceAfterTypeAssertion` default: `false`
- `javascript.format.placeOpenBraceOnNewLineForFunctions` default: `false`
- `javascript.format.placeOpenBraceOnNewLineForControlBlocks` default: `false`
- `javascript.suggest.includeAutomaticOptionalChainCompletions`: default: `true`
- `javascript.inlayHints`: inlayHints related options.
Configurations are the same as with VSCode. Try completion with `tsserver`,
`typescript` or `javascript` in your `coc-settings.json`.
### Added on 1.10.0
- `javascript.suggest.completeJSDocs` `typescript.suggest.completeJSDocs`:
Enable/disable suggestion to complete JSDoc comments. default: `true`
### Added on 1.10.1
- `typescript.suggest.objectLiteralMethodSnippets.enabled`
`javascript.suggest.objectLiteralMethodSnippets.enabled`:
Enable/disable snippet completions for methods in object literals. Requires using TypeScript 4.7+ in the workspace
Configurations are the same as with VSCode. Install
[coc-json](https://github.com/neoclide/coc-json) and try completion with
`tsserver`, `typescript` or `javascript` in your
`coc-settings.json`.
## Related extensions

View file

@ -1,161 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
let net = require('net');
let fs = require('fs');
let ENABLE_LOGGING = false;
let log = (function () {
if (!ENABLE_LOGGING) {
return function () { }; // tslint:disable-line
}
let isFirst = true;
let LOG_LOCATION = 'C:\\stdFork.log';
return function log(str) {
if (isFirst) {
isFirst = false;
fs.writeFileSync(LOG_LOCATION, str + '\n');
return;
}
fs.appendFileSync(LOG_LOCATION, str + '\n');
};
})();
let stdInPipeName = process.env['STDIN_PIPE_NAME']; // tslint:disable-line
let stdOutPipeName = process.env['STDOUT_PIPE_NAME']; // tslint:disable-line
let stdErrPipeName = process.env['STDERR_PIPE_NAME']; // tslint:disable-line
log('STDIN_PIPE_NAME: ' + stdInPipeName);
log('STDOUT_PIPE_NAME: ' + stdOutPipeName);
log('STDERR_PIPE_NAME: ' + stdErrPipeName);
(function () {
log('Beginning stdout redirection...');
// Create a writing stream to the stdout pipe
let stdOutStream = net.connect(stdOutPipeName);
// unref stdOutStream to behave like a normal standard out
stdOutStream.unref();
process.__defineGetter__('stdout', function () {
return stdOutStream;
});
// Create a writing stream to the stderr pipe
let stdErrStream = net.connect(stdErrPipeName);
// unref stdErrStream to behave like a normal standard out
stdErrStream.unref();
process.__defineGetter__('stderr', function () {
return stdErrStream;
});
let fsWriteSyncString = function (// tslint:disable-line
fd, str, _position, encoding) {
// fs.writeSync(fd, string[, position[, encoding]])
let buf = Buffer.from(str, encoding || 'utf8');
return fsWriteSyncBuffer(fd, buf, 0, buf.length); // tslint:disable-line
};
let fsWriteSyncBuffer = function (// tslint:disable-line
fd, buffer, off, len) {
off = Math.abs(off | 0);
len = Math.abs(len | 0);
// fs.writeSync(fd, buffer, offset, length[, position])
let buffer_length = buffer.length;
if (off > buffer_length) {
throw new Error('offset out of bounds');
}
if (len > buffer_length) {
throw new Error('length out of bounds');
}
if (((off + len) | 0) < off) {
throw new Error('off + len overflow');
}
if (buffer_length - off < len) {
// Asking for more than is left over in the buffer
throw new Error('off + len > buffer.length');
}
let slicedBuffer = buffer;
if (off !== 0 || len !== buffer_length) {
slicedBuffer = buffer.slice(off, off + len);
}
if (fd === 1) {
stdOutStream.write(slicedBuffer);
}
else {
stdErrStream.write(slicedBuffer);
}
return slicedBuffer.length;
};
// handle fs.writeSync(1, ...)
let originalWriteSync = fs.writeSync;
fs.writeSync = function (// tslint:disable-line
fd, data, _position, _encoding) {
if (fd !== 1 && fd !== 2) {
return originalWriteSync.apply(fs, arguments);
}
// usage:
// fs.writeSync(fd, buffer, offset, length[, position])
// OR
// fs.writeSync(fd, string[, position[, encoding]])
if (data instanceof Buffer) {
return fsWriteSyncBuffer.apply(null, arguments);
}
// For compatibility reasons with fs.writeSync, writing null will write "null", etc
if (typeof data !== 'string') {
data += '';
}
return fsWriteSyncString.apply(null, arguments);
};
log('Finished defining process.stdout, process.stderr and fs.writeSync');
})();
(function () {
// Begin listening to stdin pipe
let server = net.createServer(function (stream) {
// Stop accepting new connections, keep the existing one alive
server.close();
log('Parent process has connected to my stdin. All should be good now.');
process.__defineGetter__('stdin', function () {
return stream;
});
// Remove myself from process.argv
process.argv.splice(1, 1);
// Load the actual program
let program = process.argv[1];
log('Loading program: ' + program);
// Unset the custom environmental variables that should not get inherited
delete process.env['STDIN_PIPE_NAME']; // tslint:disable-line
delete process.env['STDOUT_PIPE_NAME']; // tslint:disable-line
delete process.env['STDERR_PIPE_NAME']; // tslint:disable-line
require(program);
log('Finished loading program.');
let stdinIsReferenced = true;
let timer = setInterval(function () {
let listenerCount = stream.listeners('data').length +
stream.listeners('end').length +
stream.listeners('close').length +
stream.listeners('error').length;
// log('listenerCount: ' + listenerCount)
if (listenerCount <= 1) {
// No more "actual" listeners, only internal node
if (stdinIsReferenced) {
stdinIsReferenced = false;
// log('unreferencing stream!!!')
stream.unref();
}
}
else {
// There are "actual" listeners
if (!stdinIsReferenced) {
stdinIsReferenced = true;
stream.ref();
}
}
// log(
// '' + stream.listeners('data').length +
// ' ' + stream.listeners('end').length +
// ' ' + stream.listeners('close').length +
// ' ' + stream.listeners('error').length
// )
}, 1000);
if (timer.unref) { // tslint:disable-line
timer.unref(); // tslint:disable-line
}
});
server.listen(stdInPipeName, function () {
// signal via stdout that the parent process can now begin writing to stdin pipe
process.stdout.write('ready');
});
})();

260
history.md Normal file
View file

@ -0,0 +1,260 @@
# 1.10.5
- Fix a fold issue #380
# 1.10.2
- Fix snippet completion not work for optional complete item.
# 1.10.1
- Avoid unnecessary fetch of format option.
- Add `typescript.suggest.objectLiteralMethodSnippets.enabled`
# 1.10.0
- Support jsdoc completion.
- Add configurations `javascript.suggest.completeJSDocs` and `typescript.suggest.completeJSDocs`.
# 1.9.15
- Fix uri for `zipfile`.
# 1.9.14
- Add javascript snippets
- Fix command `tsserver.restart` not work
# 1.9.11
- Resued resolved tsserver path after `:CocRestart`
# 1.9.10
- Watch for `tsserver.enable` configuration to change service state.
- Fix tsserver not work well with `:CocList services`
# 1.9.9
- Use documentChanges for workspaceEdit.
# 1.9.8
- Log to output when document content exceed limit of semantic tokens.
# 1.9.7
- Change default of `javascript.autoClosingTags` and `typescript.autoClosingTags` to `true`.
# 1.9.6
- Rework codeLens related.
# 1.9.5
- Change 'allImportsAreUnused' diagnostic kind to warning.
# 1.9.4
- Improve file pattern for config file.
# 1.9.2
- Inlay hints support (#335)
# 1.9.1
- use `TSS_DEBUG` & `TSS_DEBUG_BRK` for debug port
# 1.9.0
- Add semanticTokens support #313
- Add jsxAttributeCompletionStyle settings #319
- Add command `tsserver.sortImports` #322
- Add suggest.classMemberSnippets.enabled configuration cd16da8
- Add suggest.jsdoc.generateReturns configuration 5a8c68f
- Add typescript.preferences.includePackageJsonAutoImports configuration 4d78b61
- Add tsserver.enableTracing configuration 43e6f62
- Add typescript.check.npmIsInstalled configuration 3bd84b1
# 1.8.3
- Support deprecated tag for document symbols, diagnostic, workspace symbols.
# 1.8.2
- Support call hierarchy.
- Support `tags` and access modifier for document symbols.
- Support return `DefinitionLink[]` for definition provider.
# 1.8.1
- Support `tsserver.tsconfigPath` configuration.
# 1.8.0
- Support [Import Statement Completions](https://devblogs.microsoft.com/typescript/announcing-typescript-4-3/#import-statement-completions)
# 1.7.0
- Support tag closing for JSX
# 1.6.4
- Support `typescript.format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces` and `ypescript.format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces`
# 1.6.2
- Support languages from plugins.
# 1.5.5
- Support `typescript.preferences.useAliasesForRenames` and `javascript.preferences.useAliasesForRenames`
# 1.5.3
- Support the new path of Yarn v2 pnpify SDK.
- Us `tsserver.pluginPaths` replace `tsserver.pluginRoot`.
# 1.5.0
- Support @ts-expect-error directive on tsserver v390.
- Support `tsserver.watchOptions` configuration.
# 1.4.13
- Add `preferences.importModuleSpecifierEnding` configuration.
- Change `preferences.importModuleSpecifier` default to `auto`.
# 1.4.12
- Support `tsserver.maxTsServerMemory` configuration.
# 1.4.9
- Support semicolons format option.
# 1.4.8
- support `format.enabled` configuration
# 1.4.3
- Use global tsc when local tsc not foun
# 1.4.0
- remove noSemicolons preferences
# 1.3.15
- Add missing option "auto" to importModuleSpecifier
# 1.3.11
- Add `tsserver.ignoreLocalTsserver` configuration.
# 1.3.6
- Support `b:coc_tsserver_disable`
# 1.3.2
- fix suggestionActions.enabled configuration not working
# 1.3.1
- fix validate.enable not work sometimes
# 1.3.0
- Loading status.
- Batched buffer synchronize.
- Configuration for showUnused variable.
- Smart selection support.
- Support 'auto' as quoteStyle.
- Support 'validateDefaultNpmLocation'.
# 1.1.30
- rework of typescriptService, support interuptGetErr
# 1.1.29
- Support plugin feature.
# 1.1.28
- add codeAction provider for import missing node builtin modules.
# 1.1.26
- Add install module codeAction for module not found diagnostic.
- Rework `tsserver.watchBuild`, use background process, support statusline.
# 1.1.25
- Support autofix of node modules import
# 1.1.23
- Add command `tsserver.executeAutofix`
# 1.1.13
- Add triggerCharacters for SignatureHelp
# 1.1.12
- Add typescript snippets from VSCode
# 1.1.11
- Fix throw error of "No content available" on completion.
# 1.1.10
- Support projectRootPath for document
# 1.1.9
- Support commitCharacters of completion items
# 1.1.8
- Add status bar support.
# 1.1.7
- Add settings `javascript.validate.enable` and `typescript.validate.enable`
# 1.1.6
- Fix suggestionActions.enabled not works
# 1.1.5
- Use quickfix list for watchBuild errors
# 1.1.4
- Fix organizeImports not working sometimes
# 1.1.3
- Remove settings with `commaAfterImport`, use `typescript.preferences.noSemicolons` and `javasscript.preferences.noSemicolons` instead.
# 1.1.2
- Support diagnostic of config file.
# 1.1.1
- Remove unnecessary use of workspace terminal.
# 1.1.0
- Support rename import path: https://code.visualstudio.com/updates/v1_28#_rename-import-path
- Use new `suggest` for completion configuration: https://code.visualstudio.com/updates/v1_28#_new-settings-for-jsts-suggestions
- Convert to async function: https://code.visualstudio.com/updates/v1_28#_convert-to-async-function
- Remove semicolons on format: set `typescript.preferences.noSemicolons` to true

View file

@ -1,6 +1,6 @@
{
"name": "coc-tsserver",
"version": "1.8.6",
"version": "1.10.5",
"description": "tsserver extension for coc.nvim",
"main": "lib/index.js",
"publisher": "chemzqm",
@ -224,6 +224,12 @@
],
"description": "Trace level of tsserver"
},
"tsserver.enableTracing": {
"type": "boolean",
"default": false,
"description": "Enables tracing TS server performance to a directory. These trace files can be used to diagnose TS Server performance issues. The log may contain file paths, source code, and other potentially sensitive information from your project.",
"scope": "window"
},
"tsserver.pluginPaths": {
"type": "array",
"default": [],
@ -232,10 +238,6 @@
},
"description": "Folders contains tsserver plugins"
},
"tsserver.debugPort": {
"type": "number",
"description": "Debug port number of tsserver"
},
"tsserver.reportStyleChecksAsWarnings": {
"type": "boolean",
"default": true
@ -260,6 +262,12 @@
"default": true,
"description": "Use batched buffer sync support."
},
"typescript.check.npmIsInstalled": {
"type": "boolean",
"default": true,
"markdownDescription": "Check if npm is installed for [Automatic Type Acquisition](https://code.visualstudio.com/docs/nodejs/working-with-javascript#_typings-and-automatic-type-acquisition).",
"scope": "window"
},
"typescript.showUnused": {
"type": "boolean",
"default": true,
@ -285,6 +293,12 @@
"default": true,
"description": "Enable codeLens for references"
},
"typescript.referencesCodeLens.showOnAllFunctions": {
"type": "boolean",
"default": false,
"description": "Enable/disable references CodeLens on all functions in typescript files.",
"scope": "window"
},
"typescript.preferences.importModuleSpecifier": {
"type": "string",
"default": "shortest",
@ -314,6 +328,38 @@
"description": "Preferred path ending for auto imports.",
"scope": "resource"
},
"typescript.preferences.jsxAttributeCompletionStyle": {
"type": "string",
"enum": [
"auto",
"braces",
"none"
],
"markdownEnumDescriptions": [
"Insert `={}` or `=\"\"` after attribute names based on the prop type.",
"Insert `={}` after attribute names.",
"Only insert attribute names."
],
"default": "auto",
"description": "Preferred style for JSX attribute completions.",
"scope": "resource"
},
"typescript.preferences.includePackageJsonAutoImports": {
"type": "string",
"enum": [
"auto",
"on",
"off"
],
"enumDescriptions": [
"Search dependencies based on estimated performance impact.",
"Always search dependencies.",
"Never search dependencies."
],
"default": "auto",
"markdownDescription": "Enable/disable searching `package.json` dependencies for available auto imports.",
"scope": "window"
},
"typescript.preferences.quoteStyle": {
"type": "string",
"default": "auto",
@ -374,6 +420,18 @@
"description": "Enable/disable snippet completions from TS Server. Requires using TypeScript 4.3+ in the workspace.",
"scope": "resource"
},
"typescript.suggest.classMemberSnippets.enabled": {
"type": "boolean",
"default": true,
"description": "Enable/disable snippet completions for class members. Requires using TypeScript 4.5+ in the workspace",
"scope": "resource"
},
"typescript.suggest.jsdoc.generateReturns": {
"type": "boolean",
"default": true,
"markdownDescription": "Enable/disable generating `@return` annotations for JSDoc templates. Requires using TypeScript 4.2+ in the workspace.",
"scope": "resource"
},
"typescript.format.enabled": {
"type": "boolean",
"default": true,
@ -471,7 +529,8 @@
},
"typescript.autoClosingTags": {
"type": "boolean",
"default": false
"default": true,
"description": "Enable/disable automatic closing of JSX tags."
},
"javascript.showUnused": {
"type": "boolean",
@ -495,6 +554,12 @@
"type": "boolean",
"default": true
},
"javascript.referencesCodeLens.showOnAllFunctions": {
"type": "boolean",
"default": false,
"description": "Enable/disable references CodeLens on all functions in JavaScript files.",
"scope": "window"
},
"javascript.preferences.importModuleSpecifier": {
"type": "string",
"default": "shortest",
@ -524,6 +589,22 @@
"description": "Preferred path ending for auto imports.",
"scope": "resource"
},
"javascript.preferences.jsxAttributeCompletionStyle": {
"type": "string",
"enum": [
"auto",
"braces",
"none"
],
"markdownEnumDescriptions": [
"Insert `={}` or `=\"\"` after attribute names based on the prop type.",
"Insert `={}` after attribute names.",
"Only insert attribute names."
],
"default": "auto",
"description": "Preferred style for JSX attribute completions.",
"scope": "resource"
},
"javascript.preferences.quoteStyle": {
"type": "string",
"default": "auto",
@ -578,6 +659,18 @@
"description": "Enable/disable auto-import-style completions on partially-typed import statements. Requires using TypeScript 4.3+ in the workspace.",
"scope": "resource"
},
"javascript.suggest.classMemberSnippets.enabled": {
"type": "boolean",
"default": true,
"description": "Enable/disable snippet completions for class members. Requires using TypeScript 4.5+ in the workspace",
"scope": "resource"
},
"javascript.suggest.jsdoc.generateReturns": {
"type": "boolean",
"default": true,
"markdownDescription": "Enable/disable generating `@return` annotations for JSDoc templates. Requires using TypeScript 4.2+ in the workspace.",
"scope": "resource"
},
"javascript.format.enabled": {
"type": "boolean",
"default": true,
@ -650,12 +743,117 @@
"javascript.suggest.includeAutomaticOptionalChainCompletions": {
"type": "boolean",
"default": true,
"description": "%configuration.suggest.includeAutomaticOptionalChainCompletions%",
"description": "Enable/disable showing completions on potentially undefined values that insert an optional chain call. Requires TS 3.7+ and strict null checks to be enabled.",
"scope": "resource"
},
"typescript.inlayHints.parameterNames.enabled": {
"type": "string",
"enum": [
"none",
"literals",
"all"
],
"enumDescriptions": [
"Disable parameter name hints.",
"Enable parameter name hints only for literal arguments.",
"Enable parameter name hints for literal and non-literal arguments."
],
"default": "none",
"description": "Enable/disable inlay hints of parameter names.",
"scope": "resource"
},
"typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": {
"type": "boolean",
"default": true,
"description": "Suppress parameter name hints on arguments whose text is identical to the parameter name.",
"scope": "resource"
},
"typescript.inlayHints.parameterTypes.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of parameter types.",
"scope": "resource"
},
"typescript.inlayHints.variableTypes.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of variable types.",
"scope": "resource"
},
"typescript.inlayHints.propertyDeclarationTypes.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of property declarations.",
"scope": "resource"
},
"typescript.inlayHints.functionLikeReturnTypes.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of return type for function signatures.",
"scope": "resource"
},
"typescript.inlayHints.enumMemberValues.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of enum member values.",
"scope": "resource"
},
"javascript.inlayHints.parameterNames.enabled": {
"type": "string",
"enum": [
"none",
"literals",
"all"
],
"enumDescriptions": [
"Disable parameter name hints.",
"Enable parameter name hints only for literal arguments.",
"Enable parameter name hints for literal and non-literal arguments."
],
"default": "none",
"description": "Enable/disable inlay hints of parameter names.",
"scope": "resource"
},
"javascript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": {
"type": "boolean",
"default": true,
"description": "Suppress parameter name hints on arguments whose text is identical to the parameter name.",
"scope": "resource"
},
"javascript.inlayHints.parameterTypes.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of parameter types.",
"scope": "resource"
},
"javascript.inlayHints.variableTypes.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of variable types.",
"scope": "resource"
},
"javascript.inlayHints.propertyDeclarationTypes.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of property declarations.",
"scope": "resource"
},
"javascript.inlayHints.functionLikeReturnTypes.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of return type for function signatures.",
"scope": "resource"
},
"javascript.inlayHints.enumMemberValues.enabled": {
"type": "boolean",
"default": false,
"description": "Enable/disable inlay hints of enum member values.",
"scope": "resource"
},
"javascript.autoClosingTags": {
"type": "boolean",
"default": false
"default": true,
"description": "Enable/disable automatic closing of JSX tags."
},
"javascript.format.semicolons": {
"type": "string",
@ -667,6 +865,28 @@
"insert",
"remove"
]
},
"javascript.suggest.completeJSDocs": {
"type": "boolean",
"default": true,
"description": "Enable/disable suggestion to complete JSDoc comments."
},
"typescript.suggest.completeJSDocs": {
"type": "boolean",
"default": true,
"description": "Enable/disable suggestion to complete JSDoc comments."
},
"javascript.suggest.objectLiteralMethodSnippets.enabled": {
"type": "boolean",
"default": true,
"description": "Enable/disable snippet completions for methods in object literals. Requires using TypeScript 4.7+ in the workspace",
"scope": "resource"
},
"typescript.suggest.objectLiteralMethodSnippets.enabled": {
"type": "boolean",
"default": true,
"description": "Enable/disable snippet completions for methods in object literals. Requires using TypeScript 4.7+ in the workspace",
"scope": "resource"
}
}
},
@ -678,20 +898,28 @@
{
"language": "typescriptreact",
"path": "./snippets/typescript.json"
},
{
"language": "javascript",
"path": "./snippets/javascript.json"
},
{
"language": "javascriptreact",
"path": "./snippets/javascript.json"
}
]
},
"author": "chemzqm@gmail.com",
"license": "MIT",
"devDependencies": {
"@types/node": "^10.12.0",
"coc.nvim": "^0.0.81-next.6",
"esbuild": "^0.8.29",
"semver": "^7.3.2",
"@types/node": "^12.12.12",
"coc.nvim": "^0.0.81-next.25",
"esbuild": "^0.14.11",
"semver": "^7.3.5",
"vscode-languageserver-protocol": "^3.16.0",
"which": "^2.0.2"
},
"dependencies": {
"typescript": "^4.3.5"
"typescript": "^4.7.2"
}
}

194
snippets/javascript.json Normal file
View file

@ -0,0 +1,194 @@
{
"define module": {
"prefix": "define",
"body": [
"define([",
"\t'require',",
"\t'${1:dependency}'",
"], function(require, ${2:factory}) {",
"\t'use strict';",
"\t$0",
"});"
],
"description": "define module"
},
"For Loop": {
"prefix": "for",
"body": [
"for (let ${1:index} = 0; ${1:index} < ${2:array}.length; ${1:index}++) {",
"\tconst ${3:element} = ${2:array}[${1:index}];",
"\t$TM_SELECTED_TEXT$0",
"}"
],
"description": "For Loop"
},
"For-Each Loop": {
"prefix": "foreach",
"body": [
"${1:array}.forEach(${2:element} => {",
"\t$TM_SELECTED_TEXT$0",
"});"
],
"description": "For-Each Loop"
},
"For-In Loop": {
"prefix": "forin",
"body": [
"for (const ${1:key} in ${2:object}) {",
"\tif (Object.hasOwnProperty.call(${2:object}, ${1:key})) {",
"\t\tconst ${3:element} = ${2:object}[${1:key}];",
"\t\t$TM_SELECTED_TEXT$0",
"\t}",
"}"
],
"description": "For-In Loop"
},
"For-Of Loop": {
"prefix": "forof",
"body": [
"for (const ${1:iterator} of ${2:object}) {",
"\t$TM_SELECTED_TEXT$0",
"}"
],
"description": "For-Of Loop"
},
"Function Statement": {
"prefix": "function",
"body": [
"function ${1:name}(${2:params}) {",
"\t$TM_SELECTED_TEXT$0",
"}"
],
"description": "Function Statement"
},
"If Statement": {
"prefix": "if",
"body": [
"if (${1:condition}) {",
"\t$TM_SELECTED_TEXT$0",
"}"
],
"description": "If Statement"
},
"If-Else Statement": {
"prefix": "ifelse",
"body": [
"if (${1:condition}) {",
"\t$TM_SELECTED_TEXT$0",
"} else {",
"\t",
"}"
],
"description": "If-Else Statement"
},
"New Statement": {
"prefix": "new",
"body": [
"const ${1:name} = new ${2:type}(${3:arguments});$0"
],
"description": "New Statement"
},
"Switch Statement": {
"prefix": "switch",
"body": [
"switch (${1:key}) {",
"\tcase ${2:value}:",
"\t\t$0",
"\t\tbreak;",
"",
"\tdefault:",
"\t\tbreak;",
"}"
],
"description": "Switch Statement"
},
"While Statement": {
"prefix": "while",
"body": [
"while (${1:condition}) {",
"\t$TM_SELECTED_TEXT$0",
"}"
],
"description": "While Statement"
},
"Do-While Statement": {
"prefix": "dowhile",
"body": [
"do {",
"\t$TM_SELECTED_TEXT$0",
"} while (${1:condition});"
],
"description": "Do-While Statement"
},
"Try-Catch Statement": {
"prefix": "trycatch",
"body": [
"try {",
"\t$TM_SELECTED_TEXT$0",
"} catch (${1:error}) {",
"\t",
"}"
],
"description": "Try-Catch Statement"
},
"Set Timeout Function": {
"prefix": "settimeout",
"body": [
"setTimeout(() => {",
"\t$TM_SELECTED_TEXT$0",
"}, ${1:timeout});"
],
"description": "Set Timeout Function"
},
"Set Interval Function": {
"prefix": "setinterval",
"body": [
"setInterval(() => {",
"\t$TM_SELECTED_TEXT$0",
"}, ${1:interval});"
],
"description": "Set Interval Function"
},
"Import external module.": {
"prefix": "import statement",
"body": [
"import { $0 } from \"${1:module}\";"
],
"description": "Import external module."
},
"Region Start": {
"prefix": "#region",
"body": [
"//#region $0"
],
"description": "Folding Region Start"
},
"Region End": {
"prefix": "#endregion",
"body": [
"//#endregion"
],
"description": "Folding Region End"
},
"Log to the console": {
"prefix": "log",
"body": [
"console.log($1);"
],
"description": "Log to the console"
},
"Log warning to console": {
"prefix": "warn",
"body": [
"console.warn($1);"
],
"description": "Log warning to the console"
},
"Log error to console": {
"prefix": "error",
"body": [
"console.error($1);"
],
"description": "Log error to the console"
}
}

View file

@ -1,7 +1,5 @@
import { commands, ExtensionContext, services, workspace } from 'coc.nvim'
import { ExtensionContext, services } from 'coc.nvim'
import TsserverService from './server'
import { AutoFixCommand, Command, ConfigurePluginCommand, FileReferencesCommand, OpenTsServerLogCommand, ReloadProjectsCommand, TypeScriptGoToProjectConfigCommand } from './server/commands'
import { OrganizeImportsCommand } from './server/organizeImports'
import { PluginManager } from './utils/plugins'
interface API {
@ -9,39 +7,10 @@ interface API {
}
export async function activate(context: ExtensionContext): Promise<API> {
let { subscriptions, logger } = context
const config = workspace.getConfiguration().get<any>('tsserver', {})
if (!config.enable) return
let { subscriptions } = context
const pluginManager = new PluginManager()
const service = new TsserverService(pluginManager)
function registCommand(cmd: Command): void {
let { id, execute } = cmd
subscriptions.push(commands.registerCommand(id as string, execute, cmd))
}
registCommand(new ConfigurePluginCommand(pluginManager))
registCommand(new AutoFixCommand(service))
registCommand(new ReloadProjectsCommand(service))
registCommand(new FileReferencesCommand(service))
registCommand(new OpenTsServerLogCommand(service))
registCommand(new TypeScriptGoToProjectConfigCommand(service))
registCommand(new OrganizeImportsCommand(service))
registCommand({
id: 'tsserver.restart',
execute: (): void => {
// tslint:disable-next-line:no-floating-promises
service.stop().then(() => {
setTimeout(() => {
service.restart()
}, 100)
})
}
})
service.start().then(() => {
subscriptions.push(services.regist(service))
}, e => {
logger.error(`Error on service start:`, e)
})
const service = new TsserverService(pluginManager, context.subscriptions)
subscriptions.push(services.regist(service))
return {
configurePlugin: (pluginId: string, configuration: {}): void => {

View file

@ -46,7 +46,8 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider
public constructor(
protected client: ITypeScriptServiceClient,
private cachedResponse: CachedNavTreeResponse
private cachedResponse: CachedNavTreeResponse,
protected modeId: string
) {}
public get onDidChangeCodeLenses(): Event<void> {
@ -116,38 +117,31 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider
)
}
}
protected getSymbolRange(
document: TextDocument,
item: Proto.NavigationTree
): Range | null {
if (!item) {
return null
}
}
// TS 3.0+ provides a span for just the symbol
if ((item as any).nameSpan) {
return typeConverters.Range.fromTextSpan((item as any).nameSpan)
}
export function getSymbolRange(
document: TextDocument,
item: Proto.NavigationTree
): Range | null {
if (item.nameSpan) {
return typeConverters.Range.fromTextSpan(item.nameSpan)
}
// In older versions, we have to calculate this manually. See #23924
const span = item.spans && item.spans[0]
if (!span) {
return null
}
// In older versions, we have to calculate this manually. See #23924
const span = item.spans && item.spans[0]
if (!span) {
return null
}
const range = typeConverters.Range.fromTextSpan(span)
const text = document.getText(range)
const range = typeConverters.Range.fromTextSpan(span)
const text = document.getText(range)
const identifierMatch = new RegExp(
`^(.*?(\\b|\\W))${escapeRegExp(item.text || '')}(\\b|\\W)`,
'gm'
)
const match = identifierMatch.exec(text)
const prefixLength = match ? match.index + match[1].length : 0
const startOffset = document.offsetAt(range.start) + prefixLength
return {
start: document.positionAt(startOffset),
end: document.positionAt(startOffset + item.text.length)
}
const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${escapeRegExp(item.text || '')}(\\b|\\W)`, 'gm')
const match = identifierMatch.exec(text)
const prefixLength = match ? match.index + match[1].length : 0
const startOffset = document.offsetAt(range.start) + prefixLength
return {
start: document.positionAt(startOffset),
end: document.positionAt(startOffset + item.text.length)
}
}

View file

@ -345,9 +345,7 @@ export default class BufferSyncSupport {
}
public listen(): void {
if (this.listening) {
return
}
if (this.listening) return
this.listening = true
workspace.onDidOpenTextDocument(
this.openTextDocument,

View file

@ -251,7 +251,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
}
const detail = details[0]
if (!item.detail && detail.displayParts.length) {
item.detail = Previewer.plain(detail.displayParts)
item.detail = Previewer.plainWithLinks(detail.displayParts)
}
item.documentation = this.getDocumentation(detail)
const { command, additionalTextEdits } = this.getCodeActions(detail, filepath)
@ -259,7 +259,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
item.additionalTextEdits = additionalTextEdits
if (detail && item.insertTextFormat == InsertTextFormat.Snippet) {
const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, position, token)
if (shouldCompleteFunction) {
if (shouldCompleteFunction && !item.insertText) {
this.createSnippetOfFunctionCall(item, detail)
}
}
@ -354,12 +354,12 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
private getDocumentation(detail: Proto.CompletionEntryDetails): MarkupContent | undefined {
let documentation = ''
if (detail.source) {
const importPath = `'${Previewer.plain(detail.source)}'`
const importPath = `'${Previewer.plainWithLinks(detail.source)}'`
const autoImportLabel = `Auto import from ${importPath}`
documentation += `${autoImportLabel}\n`
}
let parts = [
Previewer.plain(detail.documentation),
Previewer.plainWithLinks(detail.documentation),
Previewer.tagsMarkdownPreview(detail.tags)
]
parts = parts.filter(s => s && s.trim() != '')

View file

@ -38,6 +38,7 @@ const getSymbolKind = (kind: string): SymbolKind => {
return SymbolKind.Variable
case PConst.Kind.constructSignature:
case PConst.Kind.constructorImplementation:
return SymbolKind.Constructor
case PConst.Kind.function:
case PConst.Kind.localFunction:
return SymbolKind.Function

View file

@ -40,6 +40,9 @@ export interface SuggestOptions {
readonly importStatementSuggestions: boolean
readonly includeCompletionsForImportStatements: boolean
readonly includeCompletionsWithSnippetText: boolean
readonly includeCompletionsWithClassMemberSnippets: boolean
readonly generateReturnInDocTemplate: boolean
readonly includeCompletionsWithObjectLiteralMethodSnippets: boolean
}
export default class FileConfigurationManager {
@ -85,7 +88,13 @@ export default class FileConfigurationManager {
}
public async ensureConfigurationForDocument(document: TextDocument, token: CancellationToken): Promise<void> {
let opts = await workspace.getFormatOptions(document.uri)
let opts: { insertSpaces: boolean, tabSize: number }
let cached = this.cachedMap.get(document.uri)
if (cached) {
opts = { insertSpaces: cached.formatOptions.convertTabsToSpaces, tabSize: cached.formatOptions.tabSize }
} else {
opts = await workspace.getFormatOptions(document.uri)
}
return this.ensureConfigurationOptions(document, opts.insertSpaces, opts.tabSize, token)
}
@ -159,9 +168,12 @@ export default class FileConfigurationManager {
paths: config.get<boolean>('paths', true),
completeFunctionCalls: config.get<boolean>('completeFunctionCalls', true),
autoImports: config.get<boolean>('autoImports', true),
includeCompletionsWithObjectLiteralMethodSnippets: config.get<boolean>('suggest.objectLiteralMethodSnippets.enabled', true),
generateReturnInDocTemplate: config.get<boolean>('jsdoc.generateReturns', true),
importStatementSuggestions: config.get<boolean>('importStatements', true),
includeCompletionsForImportStatements: config.get<boolean>('includeCompletionsForImportStatements', true),
includeCompletionsWithSnippetText: config.get<boolean>('includeCompletionsWithSnippetText', true),
includeCompletionsWithClassMemberSnippets: config.get<boolean>('classMemberSnippets.enabled', true),
includeAutomaticOptionalChainCompletions: config.get<boolean>('includeAutomaticOptionalChainCompletions', true)
}
}
@ -170,17 +182,31 @@ export default class FileConfigurationManager {
if (this.client.apiVersion.lt(API.v290)) {
return {}
}
const config = workspace.getConfiguration(`${language}.preferences`, uri)
const config = workspace.getConfiguration(language, uri)
const preferencesConfig = workspace.getConfiguration(`${language}.preferences`, uri)
const suggestConfig = this.getCompleteOptions(language)
// getImportModuleSpecifierEndingPreference available on ts 2.9.0
const preferences: Proto.UserPreferences & { importModuleSpecifierEnding?: string } = {
quotePreference: this.getQuoteStyle(config),
importModuleSpecifierPreference: getImportModuleSpecifier(config) as any,
importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(config),
const preferences: Proto.UserPreferences = {
quotePreference: this.getQuoteStyle(preferencesConfig),
importModuleSpecifierPreference: getImportModuleSpecifier(preferencesConfig) as any,
importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(preferencesConfig),
jsxAttributeCompletionStyle: getJsxAttributeCompletionStyle(preferencesConfig),
allowTextChangesInNewFiles: uri.startsWith('file:'),
allowRenameOfImportPath: true,
providePrefixAndSuffixTextForRename: config.get<boolean>('renameShorthandProperties', true) === false ? false : config.get<boolean>('useAliasesForRenames', true),
includeCompletionsForImportStatements: this.getCompleteOptions(language).includeCompletionsForImportStatements,
includeCompletionsWithSnippetText: this.getCompleteOptions(language).includeCompletionsWithSnippetText,
// can't support it with coc.nvim by now.
provideRefactorNotApplicableReason: false,
providePrefixAndSuffixTextForRename: preferencesConfig.get<boolean>('renameShorthandProperties', true) === false ? false : preferencesConfig.get<boolean>('useAliasesForRenames', true),
generateReturnInDocTemplate: suggestConfig.generateReturnInDocTemplate,
includeCompletionsForImportStatements: suggestConfig.includeCompletionsForImportStatements,
includeCompletionsWithClassMemberSnippets: suggestConfig.includeCompletionsWithClassMemberSnippets,
includeCompletionsWithSnippetText: suggestConfig.includeCompletionsWithSnippetText,
// @ts-expect-error until 4.7
includeCompletionsWithObjectLiteralMethodSnippets: suggestConfig.includeCompletionsWithObjectLiteralMethodSnippets,
includeAutomaticOptionalChainCompletions: suggestConfig.includeAutomaticOptionalChainCompletions,
useLabelDetailsInCompletionEntries: true,
allowIncompleteCompletions: true,
displayPartsForJSDoc: true,
...getInlayHintsPreferences(config),
}
return preferences
}
@ -220,3 +246,41 @@ function getImportModuleSpecifierEndingPreference(config: WorkspaceConfiguration
default: return 'auto'
}
}
function getJsxAttributeCompletionStyle(config: WorkspaceConfiguration) {
switch (config.get<string>('jsxAttributeCompletionStyle')) {
case 'braces': return 'braces'
case 'none': return 'none'
default: return 'auto'
}
}
export class InlayHintSettingNames {
static readonly parameterNamesSuppressWhenArgumentMatchesName = 'inlayHints.parameterNames.suppressWhenArgumentMatchesName'
static readonly parameterNamesEnabled = 'inlayHints.parameterTypes.enabled'
static readonly variableTypesEnabled = 'inlayHints.variableTypes.enabled'
static readonly propertyDeclarationTypesEnabled = 'inlayHints.propertyDeclarationTypes.enabled'
static readonly functionLikeReturnTypesEnabled = 'inlayHints.functionLikeReturnTypes.enabled'
static readonly enumMemberValuesEnabled = 'inlayHints.enumMemberValues.enabled'
}
export function getInlayHintsPreferences(config: WorkspaceConfiguration) {
return {
includeInlayParameterNameHints: getInlayParameterNameHintsPreference(config),
includeInlayParameterNameHintsWhenArgumentMatchesName: !config.get<boolean>(InlayHintSettingNames.parameterNamesSuppressWhenArgumentMatchesName, true),
includeInlayFunctionParameterTypeHints: config.get<boolean>(InlayHintSettingNames.parameterNamesEnabled, false),
includeInlayVariableTypeHints: config.get<boolean>(InlayHintSettingNames.variableTypesEnabled, false),
includeInlayPropertyDeclarationTypeHints: config.get<boolean>(InlayHintSettingNames.propertyDeclarationTypesEnabled, false),
includeInlayFunctionLikeReturnTypeHints: config.get<boolean>(InlayHintSettingNames.functionLikeReturnTypesEnabled, false),
includeInlayEnumMemberValueHints: config.get<boolean>(InlayHintSettingNames.enumMemberValuesEnabled, false),
} as const
}
function getInlayParameterNameHintsPreference(config: WorkspaceConfiguration) {
switch (config.get<string>('inlayHints.parameterNames.enabled')) {
case 'none': return 'none'
case 'literals': return 'literals'
case 'all': return 'all'
default: return undefined
}
}

View file

@ -45,16 +45,22 @@ export default class TypeScriptFoldingProvider implements FoldingRangeProvider {
): FoldingRange | undefined {
const range = typeConverters.Range.fromTextSpan(span.textSpan)
const kind = TypeScriptFoldingProvider.getFoldingRangeKind(span)
let { start, end } = range
// Workaround for #49904
if (span.kind === 'comment') {
let doc = workspace.getDocument(document.uri)
const line = doc.getline(range.start.line)
const line = doc.getline(start.line)
if (line.match(/\/\/\s*#endregion/gi)) {
return undefined
}
} else if (span.kind === 'code') {
let doc = workspace.getDocument(document.uri)
if (end.line > start.line && /^\s*}/.test(doc.getline(end.line))) {
end.line -= 1
end.character = doc.getline(end.line).length
}
}
let { start, end } = range
return FoldingRange.create(start.line, end.line, start.character, end.character, kind)
}

View file

@ -7,7 +7,7 @@ import { HoverProvider } from 'coc.nvim'
import { CancellationToken, Hover, MarkedString, Position } from 'vscode-languageserver-protocol'
import * as Proto from '../protocol'
import { ITypeScriptServiceClient } from '../typescriptService'
import { tagsMarkdownPreview } from '../utils/previewer'
import { markdownDocumentation } from '../utils/previewer'
import * as typeConverters from '../utils/typeConverters'
export default class TypeScriptHoverProvider implements HoverProvider {
@ -42,14 +42,16 @@ export default class TypeScriptHoverProvider implements HoverProvider {
}
private static getContents(data: Proto.QuickInfoResponseBody): MarkedString[] { // tslint:disable-line
const parts = []
const parts: MarkedString[] = []
if (data.displayString) {
// const displayParts: string[] = []
parts.push({ language: 'typescript', value: data.displayString })
}
const tags = tagsMarkdownPreview(data.tags)
parts.push(data.documentation + (tags ? '\n\n' + tags : ''))
const markup = markdownDocumentation(data.documentation, data.tags)
parts.push({
language: 'markdown',
value: markup.value
})
return parts
}
}

View file

@ -7,7 +7,7 @@ import { TextDocument } from 'coc.nvim'
import * as Proto from '../protocol'
import * as PConst from '../protocol.const'
import * as typeConverters from '../utils/typeConverters'
import { TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'
import { TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider'
export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider {
public async resolveCodeLens(
@ -21,43 +21,39 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
filepath,
codeLens.range.start
)
try {
const response = await this.client.execute('implementation', args, token, { lowPriority: true })
if (response && response.type == 'response' && response.body) {
const locations = response.body
.map(reference => {
return {
uri: this.client.toResource(reference.file),
range: {
start: typeConverters.Position.fromLocation(reference.start),
end: {
line: reference.start.line,
character: 0
}
}
}
})
// Exclude original from implementations
.filter(
location => !(
location.uri.toString() === uri &&
location.range.start.line === codeLens.range.start.line &&
location.range.start.character ===
codeLens.range.start.character
)
)
codeLens.command = this.getCommand(locations, codeLens)
return codeLens
const response = await this.client.execute('implementation', args, token, { lowPriority: true })
if (response.type !== 'response' || !response.body) {
codeLens.command = {
title: response.type === 'cancelled'
? 'cancelled'
: 'could not determine implementation',
command: ''
}
} catch {
// noop
}
codeLens.command = {
title: '0 implementations',
command: ''
return codeLens
}
const locations = response.body
.map(reference => {
return {
uri: this.client.toResource(reference.file),
range: {
start: typeConverters.Position.fromLocation(reference.start),
end: {
line: reference.start.line,
character: 0
}
}
}
})
// Exclude original from implementations
.filter(
location => !(
location.uri.toString() === uri &&
location.range.start.line === codeLens.range.start.line &&
location.range.start.character ===
codeLens.range.start.character
)
)
codeLens.command = this.getCommand(locations, codeLens)
return codeLens
}
@ -84,7 +80,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
): Range | null {
switch (item.kind) {
case PConst.Kind.interface:
return super.getSymbolRange(document, item)
return getSymbolRange(document, item)
case PConst.Kind.class:
case PConst.Kind.method:
@ -92,7 +88,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
if (item.kindModifiers.match(/\babstract\b/g)) {
return super.getSymbolRange(document, item)
return getSymbolRange(document, item)
}
break
}

View file

@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken, Disposable, disposeAll, Emitter, Event, InlayHint, InlayHintKind, InlayHintsProvider, Range, TextDocument, workspace } from 'coc.nvim'
import type * as Proto from '../protocol'
import { ITypeScriptServiceClient } from '../typescriptService'
import API from '../utils/api'
import { LanguageDescription } from '../utils/languageDescription'
import * as typeConverters from '../utils/typeConverters'
import FileConfigurationManager, { getInlayHintsPreferences } from './fileConfigurationManager'
export default class TypeScriptInlayHintsProvider implements InlayHintsProvider {
public static readonly minVersion = API.v440
private disposables: Disposable[] = []
private readonly _onDidChangeInlayHints = new Emitter<void>()
public readonly onDidChangeInlayHints: Event<void> = this._onDidChangeInlayHints.event
constructor(
private readonly language: LanguageDescription,
private readonly client: ITypeScriptServiceClient,
private readonly fileConfigurationManager: FileConfigurationManager,
) {
let section = `${language.id}.inlayHints`
workspace.onDidChangeConfiguration(async e => {
if (e.affectsConfiguration(section)) {
this._onDidChangeInlayHints.fire()
}
}, null, this.disposables)
// When a JS/TS file changes, change inlay hints for all visible editors
// since changes in one file can effect the hints the others.
workspace.onDidChangeTextDocument(e => {
let doc = workspace.getDocument(e.textDocument.uri)
if (language.languageIds.includes(doc.languageId)) {
this._onDidChangeInlayHints.fire()
}
}, null, this.disposables)
}
public dispose(): void {
this._onDidChangeInlayHints.dispose()
disposeAll(this.disposables)
}
async provideInlayHints(document: TextDocument, range: Range, token: CancellationToken): Promise<InlayHint[]> {
const filepath = this.client.toOpenedFilePath(document.uri)
if (!filepath) return []
if (!areInlayHintsEnabledForFile(this.language, document)) {
return []
}
const start = document.offsetAt(range.start)
const length = document.offsetAt(range.end) - start
await this.fileConfigurationManager.ensureConfigurationForDocument(document, token)
const response = await this.client.execute('provideInlayHints', { file: filepath, start, length }, token)
if (response.type !== 'response' || !response.success || !response.body) {
return []
}
return response.body.map(hint => {
return {
label: hint.text,
position: typeConverters.Position.fromLocation(hint.position),
kind: fromProtocolInlayHintKind(hint.kind),
paddingLeft: hint.whitespaceBefore,
paddingRight: hint.whitespaceAfter,
}
})
}
}
function fromProtocolInlayHintKind(kind: Proto.InlayHintKind): InlayHintKind {
switch (kind) {
case 'Parameter': return 2
case 'Type': return 1
case 'Enum': return undefined
default: return undefined
}
}
function areInlayHintsEnabledForFile(language: LanguageDescription, document: TextDocument) {
const config = workspace.getConfiguration(language.id, document.uri)
const preferences = getInlayHintsPreferences(config)
return preferences.includeInlayParameterNameHints === 'literals' ||
preferences.includeInlayParameterNameHints === 'all' ||
preferences.includeInlayEnumMemberValueHints ||
preferences.includeInlayFunctionLikeReturnTypeHints ||
preferences.includeInlayFunctionParameterTypeHints ||
preferences.includeInlayPropertyDeclarationTypeHints ||
preferences.includeInlayVariableTypeHints
}

View file

@ -0,0 +1,121 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken, CompletionItem, CompletionItemKind, CompletionItemProvider, InsertTextFormat, Position, Range, SnippetString, TextDocument, workspace } from 'coc.nvim'
import { ITypeScriptServiceClient } from '../typescriptService'
import { LanguageDescription } from '../utils/languageDescription'
import * as typeConverters from '../utils/typeConverters'
import FileConfigurationManager from './fileConfigurationManager'
const defaultJsDoc = new SnippetString(`/**\n * $0\n */`)
function createCompleteItem(document: TextDocument, position: Position): CompletionItem {
const line = document.lineAt(position.line).text
const prefix = line.slice(0, position.character).match(/\/\**\s*$/)
const suffix = line.slice(position.character).match(/^\s*\**\//)
const start = Position.create(position.line, prefix ? position.character - prefix[0].length : position.character)
const range = Range.create(start, Position.create(start.line, start.character + (suffix ? suffix[0].length : 0)))
let insert = `/** */`
return {
label: insert,
kind: CompletionItemKind.Text,
insertTextFormat: InsertTextFormat.Snippet,
detail: 'JSDoc comment',
sortText: `\0`,
textEdit: {
newText: insert,
range
}
}
}
export class JsDocCompletionProvider implements CompletionItemProvider {
constructor(
private readonly client: ITypeScriptServiceClient,
private readonly language: LanguageDescription,
private readonly fileConfigurationManager: FileConfigurationManager,
) {}
public async provideCompletionItems(
document: TextDocument,
position: Position,
token: CancellationToken
): Promise<CompletionItem[] | undefined> {
if (!workspace.getConfiguration(this.language.id, document.uri).get('suggest.completeJSDocs')) {
return undefined
}
const file = this.client.toOpenedFilePath(document.uri)
if (!file) {
return undefined
}
if (!this.isPotentiallyValidDocCompletionPosition(document, position)) {
return undefined
}
const response = await this.client.interruptGetErr(async () => {
await this.fileConfigurationManager.ensureConfigurationForDocument(document, token)
const args = typeConverters.Position.toFileLocationRequestArgs(file, position)
return this.client.execute('docCommentTemplate', args, token)
})
if (response.type !== 'response' || !response.body) {
return undefined
}
const item = createCompleteItem(document, position)
// Workaround for #43619
// docCommentTemplate previously returned undefined for empty jsdoc templates.
// TS 2.7 now returns a single line doc comment, which breaks indentation.
if (response.body.newText === '/** */') {
item.textEdit.newText = defaultJsDoc.value
} else {
item.textEdit.newText = templateToSnippet(response.body.newText).value
}
return [item]
}
private isPotentiallyValidDocCompletionPosition(
document: TextDocument,
position: Position
): boolean {
// Only show the JSdoc completion when the everything before the cursor is whitespace
// or could be the opening of a comment
const line = document.lineAt(position.line).text
const prefix = line.slice(0, position.character)
if (!/^\s*$|\/\*\s*$|^\s*\/\*+\s*$/.test(prefix)) {
return false
}
// And everything after is possibly a closing comment or more whitespace
const suffix = line.slice(position.character)
return /^\s*(\*+\/)?\s*$/.test(suffix)
}
}
export function templateToSnippet(template: string): SnippetString {
// TODO: use append placeholder
let snippetIndex = 1
template = template.replace(/\*\s$/gm, '*')
template = template.replace(/\$/g, '\\$')
template = template.replace(/^[ \t]*(?=(\/|[ ]\*))/gm, '')
template = template.replace(/^(\/\*\*\s*\*[ ]*)$/m, (x) => x + `\$0`)
template = template.replace(/\* @param([ ]\{\S+\})?\s+(\S+)[ \t]*$/gm, (_param, type, post) => {
let out = '* @param '
if (type === ' {any}' || type === ' {*}') {
out += `{\$\{${snippetIndex++}:*\}} `
} else if (type) {
out += type + ' '
}
out += post + ` \${${snippetIndex++}}`
return out
})
template = template.replace(/\* @returns[ \t]*$/gm, `* @returns \${${snippetIndex++}}`)
return new SnippetString(template)
}

View file

@ -1,8 +1,8 @@
import { CodeActionProvider, CodeActionProviderMetadata, commands, TextDocument, window, workspace } from 'coc.nvim'
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CodeActionProvider, Uri, CodeActionProviderMetadata, commands, TextDocument, window, workspace } from 'coc.nvim'
import { CancellationToken, CodeAction, CodeActionContext, CodeActionKind, Range, WorkspaceEdit } from 'vscode-languageserver-protocol'
import { Command, registCommand } from '../commands'
import Proto from '../protocol'
@ -55,13 +55,22 @@ class ApplyRefactoringCommand implements Command {
}
private async toWorkspaceEdit(body: Proto.RefactorEditInfo): Promise<WorkspaceEdit> {
for (const edit of body.edits) {
await workspace.createFile(edit.fileName, { ignoreIfExists: true })
}
let workspaceEdit = typeConverters.WorkspaceEdit.fromFileCodeEdits(
this.client,
body.edits
)
let documentChanges = workspaceEdit.documentChanges = workspaceEdit.documentChanges || []
for (const edit of body.edits) {
let resource = this.client.toResource(edit.fileName)
if (Uri.parse(resource).scheme === 'file') {
// should create file first.
documentChanges.unshift({
kind: 'create',
uri: resource,
options: { ignoreIfExists: true }
})
}
}
return workspaceEdit
}
}

View file

@ -2,15 +2,16 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken, CodeLens, Range } from 'vscode-languageserver-protocol'
import { TextDocument } from 'coc.nvim'
import { CancellationToken, Position, Range } from 'vscode-languageserver-protocol'
import { TextDocument, workspace, CodeLens } from 'coc.nvim'
import { ExecutionTarget } from '../typescriptService'
import * as Proto from '../protocol'
import * as PConst from '../protocol.const'
import * as typeConverters from '../utils/typeConverters'
import { TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'
import { TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider'
export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvider {
public resolveCodeLens(
public async resolveCodeLens(
codeLens: CodeLens,
token: CancellationToken
): Promise<CodeLens> {
@ -20,47 +21,35 @@ export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBase
filepath,
codeLens.range.start
)
return this.client
.execute('references', args, token, {
lowPriority: true
})
.then(response => {
if (!response || response.type != 'response' || !response.body) {
throw codeLens
}
let response = await this.client.execute('references', args, token, {
lowPriority: true,
executionTarget: ExecutionTarget.Semantic
})
if (!response || response.type != 'response' || !response.body) {
codeLens.command = {
title: response.type === 'cancelled'
? 'cancelled'
: 'could not determine references',
command: ''
}
return codeLens
}
const locations = response.body.refs
.map(reference =>
typeConverters.Location.fromTextSpan(
this.client.toResource(reference.file),
reference
)
)
.filter(
location =>
// Exclude original definition from references
!(
location.uri.toString() === uri &&
location.range.start.line === codeLens.range.start.line &&
location.range.start.character ===
codeLens.range.start.character
)
)
const locations = response.body.refs
.filter(reference => !reference.isDefinition)
.map(reference =>
typeConverters.Location.fromTextSpan(
this.client.toResource(reference.file),
reference
)
)
codeLens.command = {
title: locations.length === 1 ? '1 reference' : `${locations.length} references`,
command: locations.length ? 'editor.action.showReferences' : '',
arguments: [uri, codeLens.range.start, locations]
}
return codeLens
})
.catch(() => {
codeLens.command = {
title: '0 references',
command: ''
}
return codeLens
})
codeLens.command = {
title: locations.length === 1 ? '1 reference' : `${locations.length} references`,
command: locations.length ? 'editor.action.showReferences' : '',
arguments: [uri, codeLens.range.start, locations]
}
return codeLens
}
protected extractSymbol(
@ -69,37 +58,68 @@ export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBase
parent: Proto.NavigationTree | null
): Range | null {
if (parent && parent.kind === PConst.Kind.enum) {
return super.getSymbolRange(document, item)
return getSymbolRange(document, item)
}
switch (item.kind) {
case PConst.Kind.function: {
const showOnAllFunctions = workspace.getConfiguration(this.modeId).get<boolean>('referencesCodeLens.showOnAllFunctions')
if (showOnAllFunctions) {
return getSymbolRange(document, item)
}
}
// fallthrough
case PConst.Kind.const:
case PConst.Kind.let:
case PConst.Kind.variable:
case PConst.Kind.function:
// Only show references for exported variables
if (!item.kindModifiers.match(/\bexport\b/)) {
break
if (/\bexport\b/.test(item.kindModifiers)) {
return getSymbolRange(document, item)
}
// fallthrough
break
case PConst.Kind.class:
if (item.text === '<class>') {
break
}
// fallthrough
return getSymbolRange(document, item)
case PConst.Kind.method:
case PConst.Kind.memberVariable:
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
case PConst.Kind.constructorImplementation:
case PConst.Kind.interface:
case PConst.Kind.type:
case PConst.Kind.enum:
return super.getSymbolRange(document, item)
return getSymbolRange(document, item)
case PConst.Kind.method:
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
case PConst.Kind.constructorImplementation:
case PConst.Kind.memberVariable:
// Don't show if child and parent have same start
// For https://github.com/microsoft/vscode/issues/90396
if (parent &&
comparePosition(typeConverters.Position.fromLocation(parent.spans[0].start), typeConverters.Position.fromLocation(item.spans[0].start)) == 0
) {
return null
}
// Only show if parent is a class type object (not a literal)
switch (parent?.kind) {
case PConst.Kind.class:
case PConst.Kind.interface:
case PConst.Kind.type:
return getSymbolRange(document, item)
}
break
}
return null
}
}
export function comparePosition(position: Position, other: Position): number {
if (position.line > other.line) return 1
if (other.line == position.line && position.character > other.character) return 1
if (other.line == position.line && position.character == other.character) return 0
return -1
}

View file

@ -16,7 +16,7 @@ export default class TypeScriptRenameProvider implements RenameProvider {
public constructor(
private readonly client: ITypeScriptServiceClient,
private readonly fileConfigurationManager: FileConfigurationManager
) { }
) {}
public async prepareRename(
document: TextDocument,
@ -60,8 +60,8 @@ export default class TypeScriptRenameProvider implements RenameProvider {
}
if (this.client.apiVersion.gte(API.v310)) {
if ((renameInfo as any).fileToRename) {
const edits = await this.renameFile((renameInfo as any).fileToRename, newName, token)
if (renameInfo.fileToRename) {
const edits = await this.renameFile(renameInfo.fileToRename, newName, token)
if (edits) {
return edits
} else {

View file

@ -0,0 +1,294 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, Range, SemanticTokens, SemanticTokensBuilder, TextDocument, workspace } from 'coc.nvim'
import { SemanticTokensLegend } from 'vscode-languageserver-protocol'
import * as Proto from '../protocol'
import { ExecConfig, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'
import API from '../utils/api'
// as we don't do deltas, for performance reasons, don't compute semantic tokens for documents above that limit
const CONTENT_LENGTH_LIMIT = 100000
/**
* Prototype of a DocumentSemanticTokensProvider, relying on the experimental `encodedSemanticClassifications-full` request from the TypeScript server.
* As the results retured by the TypeScript server are limited, we also add a Typescript plugin (typescript-vscode-sh-plugin) to enrich the returned token.
* See https://github.com/aeschli/typescript-vscode-sh-plugin.
*/
export default class TypeScriptDocumentSemanticTokensProvider implements DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider {
public static readonly minVersion = API.v370
constructor(private readonly client: ITypeScriptServiceClient) {}
getLegend(): SemanticTokensLegend {
return {
tokenTypes,
tokenModifiers
}
}
private logIgnored(uri: string): void {
this.client.logger.warn(`${uri} content length exceed limit 100000`)
}
async provideDocumentSemanticTokens(document: TextDocument, token: CancellationToken): Promise<SemanticTokens | null> {
const file = this.client.toOpenedFilePath(document.uri)
if (!file || document.getText().length > CONTENT_LENGTH_LIMIT) {
this.logIgnored(document.uri)
return null
}
return this._provideSemanticTokens(document, { file, start: 0, length: document.getText().length }, token)
}
async provideDocumentRangeSemanticTokens(document: TextDocument, range: Range, token: CancellationToken): Promise<SemanticTokens | null> {
const file = this.client.toOpenedFilePath(document.uri)
if (!file || (document.offsetAt(range.end) - document.offsetAt(range.start) > CONTENT_LENGTH_LIMIT)) {
this.logIgnored(document.uri)
return null
}
const start = document.offsetAt(range.start)
const length = document.offsetAt(range.end) - start
return this._provideSemanticTokens(document, { file, start, length }, token)
}
async _provideSemanticTokens(document: TextDocument, requestArg: Proto.EncodedSemanticClassificationsRequestArgs, token: CancellationToken): Promise<SemanticTokens | null> {
const file = this.client.toOpenedFilePath(document.uri)
if (!file) {
return null
}
const versionBeforeRequest = document.version
requestArg.format = '2020'
const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', requestArg, token, {
cancelOnResourceChange: document.uri
})
if (response.type !== 'response' || !response.body) {
return null
}
const versionAfterRequest = document.version
if (versionBeforeRequest !== versionAfterRequest) {
// cannot convert result's offsets to (linecol) values correctly
// a new request will come in soon...
//
// here we cannot return null, because returning null would remove all semantic tokens.
// we must throw to indicate that the semantic tokens should not be removed.
// using the string busy here because it is not logged to error telemetry if the error text contains busy.
// as the new request will come in right after our response, we first wait for the document activity to stop
await waitForDocumentChangesToEnd(document)
throw new Error('Canceled')
}
const doc = workspace.getDocument(document.uri)
const tokenSpan = response.body.spans
const builder = new SemanticTokensBuilder()
let i = 0
while (i < tokenSpan.length) {
const offset = tokenSpan[i++]
const length = tokenSpan[i++]
const tsClassification = tokenSpan[i++]
let tokenModifiers = 0
let tokenType = getTokenTypeFromClassification(tsClassification)
if (tokenType !== undefined) {
// it's a classification as returned by the typescript-vscode-sh-plugin
tokenModifiers = getTokenModifierFromClassification(tsClassification)
} else {
// typescript-vscode-sh-plugin is not present
tokenType = tokenTypeMap[tsClassification]
if (tokenType === undefined) {
continue
}
}
// we can use the document's range conversion methods because the result is at the same version as the document
const startPos = document.positionAt(offset)
const endPos = document.positionAt(offset + length)
for (let line = startPos.line; line <= endPos.line; line++) {
const startCharacter = (line === startPos.line ? startPos.character : 0)
const endCharacter = (line === endPos.line ? endPos.character : doc.getline(line).length)
builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, tokenModifiers)
}
}
return builder.build()
}
}
function waitForDocumentChangesToEnd(document: TextDocument) {
let version = document.version
return new Promise<void>((s) => {
const iv = setInterval(_ => {
if (document.version === version) {
clearInterval(iv)
s()
}
version = document.version
}, 400)
})
}
function getTokenTypeFromClassification(tsClassification: number): number | undefined {
if (tsClassification > TokenEncodingConsts.modifierMask) {
return (tsClassification >> TokenEncodingConsts.typeOffset) - 1
}
return undefined
}
function getTokenModifierFromClassification(tsClassification: number) {
return tsClassification & TokenEncodingConsts.modifierMask
}
// typescript encodes type and modifiers in the classification:
// TSClassification = (TokenType + 1) << 8 + TokenModifier
const enum TokenType {
class = 0,
enum = 1,
interface = 2,
namespace = 3,
typeParameter = 4,
type = 5,
parameter = 6,
variable = 7,
enumMember = 8,
property = 9,
function = 10,
method = 11,
_ = 12
}
const enum TokenModifier {
declaration = 0,
static = 1,
async = 2,
readonly = 3,
defaultLibrary = 4,
local = 5,
_ = 6
}
const enum TokenEncodingConsts {
typeOffset = 8,
modifierMask = 255
}
const tokenTypes: string[] = []
tokenTypes[TokenType.class] = 'class'
tokenTypes[TokenType.enum] = 'enum'
tokenTypes[TokenType.interface] = 'interface'
tokenTypes[TokenType.namespace] = 'namespace'
tokenTypes[TokenType.typeParameter] = 'typeParameter'
tokenTypes[TokenType.type] = 'type'
tokenTypes[TokenType.parameter] = 'parameter'
tokenTypes[TokenType.variable] = 'variable'
tokenTypes[TokenType.enumMember] = 'enumMember'
tokenTypes[TokenType.property] = 'property'
tokenTypes[TokenType.function] = 'function'
tokenTypes[TokenType.method] = 'method'
const tokenModifiers: string[] = []
tokenModifiers[TokenModifier.async] = 'async'
tokenModifiers[TokenModifier.declaration] = 'declaration'
tokenModifiers[TokenModifier.readonly] = 'readonly'
tokenModifiers[TokenModifier.static] = 'static'
tokenModifiers[TokenModifier.local] = 'local'
tokenModifiers[TokenModifier.defaultLibrary] = 'defaultLibrary'
export namespace ExperimentalProtocol {
export interface IExtendedTypeScriptServiceClient {
execute<K extends keyof ExperimentalProtocol.ExtendedTsServerRequests>(
command: K,
args: ExperimentalProtocol.ExtendedTsServerRequests[K][0],
token: CancellationToken,
config?: ExecConfig
): Promise<ServerResponse.Response<ExperimentalProtocol.ExtendedTsServerRequests[K][1]>>
}
/**
* A request to get encoded semantic classifications for a span in the file
*/
export interface EncodedSemanticClassificationsRequest extends Proto.FileRequest {
arguments: EncodedSemanticClassificationsRequestArgs
}
/**
* Arguments for EncodedSemanticClassificationsRequest request.
*/
export interface EncodedSemanticClassificationsRequestArgs extends Proto.FileRequestArgs {
/**
* Start position of the span.
*/
start: number
/**
* Length of the span.
*/
length: number
}
export const enum EndOfLineState {
None,
InMultiLineCommentTrivia,
InSingleQuoteStringLiteral,
InDoubleQuoteStringLiteral,
InTemplateHeadOrNoSubstitutionTemplate,
InTemplateMiddleOrTail,
InTemplateSubstitutionPosition,
}
export const enum ClassificationType {
comment = 1,
identifier = 2,
keyword = 3,
numericLiteral = 4,
operator = 5,
stringLiteral = 6,
regularExpressionLiteral = 7,
whiteSpace = 8,
text = 9,
punctuation = 10,
className = 11,
enumName = 12,
interfaceName = 13,
moduleName = 14,
typeParameterName = 15,
typeAliasName = 16,
parameterName = 17,
docCommentTagName = 18,
jsxOpenTagName = 19,
jsxCloseTagName = 20,
jsxSelfClosingTagName = 21,
jsxAttribute = 22,
jsxText = 23,
jsxAttributeStringLiteralValue = 24,
bigintLiteral = 25,
}
export interface EncodedSemanticClassificationsResponse extends Proto.Response {
body?: {
endOfLineState: EndOfLineState
spans: number[]
}
}
export interface ExtendedTsServerRequests {
'encodedSemanticClassifications-full': [ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs, ExperimentalProtocol.EncodedSemanticClassificationsResponse]
}
}
// mapping for the original ExperimentalProtocol.ClassificationType from TypeScript (only used when plugin is not available)
const tokenTypeMap: number[] = []
tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = TokenType.class
tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = TokenType.enum
tokenTypeMap[ExperimentalProtocol.ClassificationType.interfaceName] = TokenType.interface
tokenTypeMap[ExperimentalProtocol.ClassificationType.moduleName] = TokenType.namespace
tokenTypeMap[ExperimentalProtocol.ClassificationType.typeParameterName] = TokenType.typeParameter
tokenTypeMap[ExperimentalProtocol.ClassificationType.typeAliasName] = TokenType.type
tokenTypeMap[ExperimentalProtocol.ClassificationType.parameterName] = TokenType.parameter

View file

@ -60,13 +60,13 @@ export default class TypeScriptSignatureHelpProvider implements SignatureHelpPro
private convertSignature(item: Proto.SignatureHelpItem): SignatureInformation {
let parameters = item.parameters.map(p => {
return {
label: Previewer.plain(p.displayParts),
label: Previewer.plainWithLinks(p.displayParts),
documentation: Previewer.markdownDocumentation(p.documentation, [])
}
})
let label = Previewer.plain(item.prefixDisplayParts)
label += parameters.map(parameter => parameter.label).join(Previewer.plain(item.separatorDisplayParts))
label += Previewer.plain(item.suffixDisplayParts)
let label = Previewer.plainWithLinks(item.prefixDisplayParts)
label += parameters.map(parameter => parameter.label).join(Previewer.plainWithLinks(item.separatorDisplayParts))
label += Previewer.plainWithLinks(item.suffixDisplayParts)
return {
label,
documentation: Previewer.markdownDocumentation(

View file

@ -5,13 +5,10 @@
import { CancellationTokenSource, Disposable, disposeAll, Position, Range, snippetManager, events, workspace, InsertChange } from 'coc.nvim'
import * as Proto from '../protocol'
import { ITypeScriptServiceClient } from '../typescriptService'
import API from '../utils/api'
import SnippetString from '../utils/SnippetString'
import * as typeConverters from '../utils/typeConverters'
export default class TagClosing implements Disposable {
public static readonly minVersion = API.v300
private static _configurationLanguages: Record<string, string> = {
'javascriptreact': 'javascript',
'typescriptreact': 'typescript',
@ -21,27 +18,21 @@ export default class TagClosing implements Disposable {
private _disposed = false
private _timeout: NodeJS.Timer | undefined = undefined
private _cancel: CancellationTokenSource | undefined = undefined
private lastInsert: string
constructor(
private readonly client: ITypeScriptServiceClient,
private readonly descriptionLanguageId: string
) {
events.on('InsertCharPre', character => {
this.lastInsert = character
}, null, this._disposables)
events.on('TextChangedI', this.onChange, this, this._disposables)
events.on('TextChangedP', this.onChange, this, this._disposables)
events.on('TextInsert', this.onInsertChange, this, this._disposables)
}
private async onChange(bufnr: number, change: InsertChange): Promise<void> {
private async onInsertChange(bufnr: number, change: InsertChange, lastInsert: string): Promise<void> {
let doc = workspace.getDocument((bufnr))
if (!doc || !doc.attached) return
let enabled = this.isEnabled(doc.filetype, doc.uri)
if (!enabled) return
let { pre, changedtick, lnum } = change
if (!pre.endsWith('/') && !pre.endsWith('>')) return
if (!pre.endsWith(this.lastInsert)) return
if (lastInsert !== '/' && lastInsert != '>') return
if (pre.length > 1 && pre[pre.length - 2] == '>') return
const filepath = this.client.toOpenedFilePath(doc.uri)
if (!filepath) return
@ -73,7 +64,6 @@ export default class TagClosing implements Disposable {
return
}
if (this._disposed) return
const insertion = response.body
if (doc.changedtick === changedtick) {
snippetManager.insertSnippet(
@ -82,7 +72,7 @@ export default class TagClosing implements Disposable {
Range.create(position, position)
)
}
}, 50)
}, 30)
}
private isEnabled(languageId: string, uri: string): boolean {

View file

@ -1,24 +1,10 @@
import { commands, disposeAll, StatusBarItem, TaskOptions, Uri, window, workspace } from 'coc.nvim'
import { commands, Disposable, disposeAll, StatusBarItem, TaskOptions, Uri, window, workspace } from 'coc.nvim'
import path from 'path'
import { Disposable, Location } from 'vscode-languageserver-protocol'
import TypeScriptServiceClient from '../typescriptServiceClient'
const countRegex = /Found\s+(\d+)\s+error/
const errorRegex = /^(.+)\((\d+),(\d+)\):\s(\w+)\sTS(\d+):\s*(.+)$/
interface ErrorItem {
location: Location
text: string
type: string
}
enum TscStatus {
INIT,
COMPILING,
RUNNING,
ERROR,
}
export default class WatchProject implements Disposable {
private disposables: Disposable[] = []
public static readonly id: string = 'tsserver.watchBuild'
@ -119,7 +105,7 @@ export default class WatchProject implements Disposable {
return
}
const tsconfigPath = workspace.getConfiguration('tsserver').get<string>('tsconfigPath', 'tsconfig.json');
const tsconfigPath = workspace.getConfiguration('tsserver').get<string>('tsconfigPath', 'tsconfig.json')
let find = await workspace.findUp([tsconfigPath])
if (!find) {
window.showMessage(`${tsconfigPath} not found!`, 'error')

View file

@ -1,6 +1,8 @@
import { disposeAll, IServiceProvider, ServiceStat, workspace, WorkspaceConfiguration } from 'coc.nvim'
import { commands, disposeAll, IServiceProvider, ServiceStat, workspace, WorkspaceConfiguration } from 'coc.nvim'
import { Disposable, DocumentSelector, Emitter, Event } from 'vscode-languageserver-protocol'
import { PluginManager } from '../utils/plugins'
import { AutoFixCommand, Command, ConfigurePluginCommand, FileReferencesCommand, OpenTsServerLogCommand, ReloadProjectsCommand, TypeScriptGoToProjectConfigCommand } from './commands'
import { OrganizeImportsCommand, SourceImportsCommand } from './organizeImports'
import TypeScriptServiceClientHost from './typescriptServiceClientHost'
import { LanguageDescription, standardLanguageDescriptions } from './utils/languageDescription'
@ -10,23 +12,69 @@ export default class TsserverService implements IServiceProvider {
public enable: boolean
// supported language types
public selector: DocumentSelector
public state = ServiceStat.Initial
public _state = ServiceStat.Initial
public clientHost: TypeScriptServiceClientHost
private _onDidServiceReady = new Emitter<void>()
public readonly onServiceReady: Event<void> = this._onDidServiceReady.event
private readonly disposables: Disposable[] = []
private descriptions: LanguageDescription[] = []
constructor(private pluginManager: PluginManager) {
constructor(private pluginManager: PluginManager, private readonly subscriptions: Disposable[]) {
const config = workspace.getConfiguration('tsserver')
const enableJavascript = !!config.get<boolean>('enableJavascript')
const enableJavascript = config.get<boolean>('enableJavascript', true)
this.enable = config.get<boolean>('enable')
this.descriptions = standardLanguageDescriptions.filter(o => {
return enableJavascript ? true : o.id != 'javascript'
})
workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('tsserver')) {
const config = workspace.getConfiguration('tsserver')
let enable = this.enable
this.enable = config.get<boolean>('enable', true)
if (enable !== this.enable) {
if (this.enable) {
void this.start()
} else {
void this.stop()
}
}
}
})
this.selector = this.descriptions.reduce((arr, c) => {
return arr.concat(c.modeIds)
return arr.concat(c.languageIds)
}, [])
this.registCommands()
}
// public state = ServiceStat.Initial
public get state(): ServiceStat {
if (this.clientHost) {
return this.clientHost.serviceClient.state
}
return this._state
}
private registCommands(): void {
let { subscriptions } = this
const registCommand = (cmd: Command): void => {
let { id, execute } = cmd
subscriptions.push(commands.registerCommand(id as string, execute, cmd))
}
registCommand(new ConfigurePluginCommand(this.pluginManager))
registCommand(new AutoFixCommand(this))
registCommand(new ReloadProjectsCommand(this))
registCommand(new FileReferencesCommand(this))
registCommand(new OpenTsServerLogCommand(this))
registCommand(new TypeScriptGoToProjectConfigCommand(this))
registCommand(new OrganizeImportsCommand(this))
registCommand(new SourceImportsCommand(this))
registCommand({
id: 'tsserver.restart',
execute: (): void => {
this.restart()
}
})
}
public get config(): WorkspaceConfiguration {
@ -51,44 +99,43 @@ export default class TsserverService implements IServiceProvider {
})
}
public start(): Promise<void> {
if (this.clientHost) return
this.state = ServiceStat.Starting
this.clientHost = new TypeScriptServiceClientHost(this.descriptions, this.pluginManager)
this.disposables.push(this.clientHost)
public async start(): Promise<void> {
if (!this.enable || this._state == ServiceStat.Starting) return
this._state = ServiceStat.Starting
if (this.clientHost) {
let client = this.clientHost.serviceClient
client.restartTsServer()
return
}
let tscPath = await workspace.nvim.getVar('Tsserver_path') as string | null
this.clientHost = new TypeScriptServiceClientHost(this.descriptions, this.pluginManager, tscPath)
let client = this.clientHost.serviceClient
return new Promise(resolve => {
let started = false
client.onTsServerStarted(() => {
Object.defineProperty(this, 'state', {
get: () => {
return this.clientHost.serviceClient.state
}
})
await new Promise(resolve => {
client.onReady(() => {
this._onDidServiceReady.fire(void 0)
if (!started) {
started = true
resolve()
}
resolve(undefined)
})
})
}
public dispose(): void {
disposeAll(this.disposables)
}
public async restart(): Promise<void> {
if (!this.clientHost) return
let client = this.clientHost.serviceClient
await client.restartTsServer()
if (!this.enable) return
await this.stop()
await this.start()
}
public async stop(): Promise<void> {
if (!this.clientHost) return
this.clientHost.reset()
let client = this.clientHost.serviceClient
await client.stop()
return
this.clientHost?.dispose()
this.clientHost = null
this._state = ServiceStat.Stopped
}
public dispose(): void {
void this.stop()
this._onDidServiceReady.dispose()
disposeAll(this.disposables)
}
}

View file

@ -19,16 +19,19 @@ import FormattingProvider from './features/formatting'
import HoverProvider from './features/hover'
import ImplementationsCodeLensProvider from './features/implementationsCodeLens'
import ImportfixProvider from './features/importFix'
import TypeScriptInlayHintsProvider from './features/inlayHints'
import InstallModuleProvider from './features/moduleInstall'
import QuickfixProvider from './features/quickfix'
import RefactorProvider from './features/refactor'
import ReferenceProvider from './features/references'
import ReferencesCodeLensProvider from './features/referencesCodeLens'
import RenameProvider from './features/rename'
import SemanticTokensProvider from './features/semanticTokens'
import SignatureHelpProvider from './features/signatureHelp'
import SmartSelection from './features/smartSelect'
import TagClosing from './features/tagClosing'
import UpdateImportsOnFileRenameHandler from './features/updatePathOnRename'
import { JsDocCompletionProvider } from './features/jsDocCompletion'
import { OrganizeImportsCodeActionProvider } from './organizeImports'
import TypeScriptServiceClient from './typescriptServiceClient'
import API from './utils/api'
@ -49,13 +52,8 @@ export default class LanguageProvider {
) {
workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables)
this.configurationChanged()
let initialized = false
client.onTsServerStarted(async () => { // tslint:disable-line
if (!initialized) {
initialized = true
this.registerProviders(client, typingsStatus)
}
client.onReady(() => {
this.registerProviders(client, typingsStatus)
})
}
@ -77,14 +75,20 @@ export default class LanguageProvider {
client: TypeScriptServiceClient,
typingsStatus: TypingsStatus
): void {
let languageIds = this.description.modeIds
let clientId = `tsserver-${this.description.id}`
let languageIds = this.description.languageIds
let clientId = `tsc-${this.description.id}`
this._register(
languages.registerCompletionItemProvider(clientId, 'TSC', languageIds,
new CompletionItemProvider(client, typingsStatus, this.fileConfigurationManager, this.description.id),
CompletionItemProvider.triggerCharacters
)
)
this._register(
languages.registerCompletionItemProvider(`tsc-${this.description.id}-jsdoc`, 'TSC', languageIds,
new JsDocCompletionProvider(client, this.description, this.fileConfigurationManager),
['*', ' ']
)
)
if (this.client.apiVersion.gte(API.v230)) {
this._register(languages.registerCompletionItemProvider(
`${this.description.id}-directive`,
@ -107,9 +111,18 @@ export default class LanguageProvider {
this._register(languages.registerDocumentRangeFormatProvider(languageIds, formatProvider))
this._register(languages.registerOnTypeFormattingEditProvider(languageIds, formatProvider, [';', '}', '\n', String.fromCharCode(27)]))
this._register(languages.registerCodeActionProvider(languageIds, new InstallModuleProvider(client), 'tsserver'))
if (typeof languages['registerCallHierarchyProvider'] === 'function') {
if (this.client.apiVersion.gte(API.v380) && typeof languages['registerCallHierarchyProvider'] === 'function') {
this._register(languages.registerCallHierarchyProvider(languageIds, new CallHierarchyProvider(client)))
}
if (this.client.apiVersion.gte(API.v370)) {
const provider = new SemanticTokensProvider(client)
if (typeof languages['registerDocumentSemanticTokensProvider'] === 'function') {
this._register(languages.registerDocumentSemanticTokensProvider(languageIds, provider, provider.getLegend()))
}
if (typeof languages['registerDocumentRangeSemanticTokensProvider'] === 'function') {
this._register(languages.registerDocumentRangeSemanticTokensProvider(languageIds, provider, provider.getLegend()))
}
}
let { fileConfigurationManager } = this
let conf = fileConfigurationManager.getLanguageConfiguration(this.id)
@ -145,10 +158,10 @@ export default class LanguageProvider {
'tsserver', [CodeActionKind.QuickFix]))
let cachedResponse = new CachedNavTreeResponse()
if (this.client.apiVersion.gte(API.v206) && conf.get<boolean>('referencesCodeLens.enable')) {
this._register(languages.registerCodeLensProvider(languageIds, new ReferencesCodeLensProvider(client, cachedResponse)))
this._register(languages.registerCodeLensProvider(languageIds, new ReferencesCodeLensProvider(client, cachedResponse, this.description.id)))
}
if (this.client.apiVersion.gte(API.v220) && conf.get<boolean>('implementationsCodeLens.enable')) {
this._register(languages.registerCodeLensProvider(languageIds, new ImplementationsCodeLensProvider(client, cachedResponse)))
this._register(languages.registerCodeLensProvider(languageIds, new ImplementationsCodeLensProvider(client, cachedResponse, this.description.id)))
}
if (this.client.apiVersion.gte(API.v350)) {
this._register(languages.registerSelectionRangeProvider(languageIds, new SmartSelection(this.client)))
@ -156,16 +169,34 @@ export default class LanguageProvider {
if (this.client.apiVersion.gte(API.v300)) {
this._register(new TagClosing(this.client, this.description.id))
}
if (this.client.apiVersion.gte(API.v440)) {
if (typeof languages.registerInlayHintsProvider === 'function') {
let provider = new TypeScriptInlayHintsProvider(this.description, this.client, this.fileConfigurationManager)
this._register(provider)
this._register(languages.registerInlayHintsProvider(languageIds, provider))
} else {
this.client.logger.error(`languages.registerInlayHintsProvider is not a function, inlay hints won't work`)
}
}
}
public handles(resource: string, doc: TextDocument): boolean {
if (doc && this.description.modeIds.indexOf(doc.languageId) >= 0) {
if (doc && this.description.languageIds.includes(doc.languageId)) {
return true
}
const base = path.basename(Uri.parse(resource).fsPath)
return this.handlesConfigFile(Uri.parse(resource))
}
private handlesConfigFile(uri: Uri): boolean {
const base = path.basename(uri.fsPath)
return !!base && (!!this.description.configFilePattern && this.description.configFilePattern.test(base))
}
public handlesUri(resource: Uri): boolean {
const ext = path.extname(resource.path).slice(1).toLowerCase()
return this.description.standardFileExtensions.includes(ext) || this.handlesConfigFile(resource)
}
private get id(): string { // tslint:disable-line
return this.description.id
}

View file

@ -19,9 +19,10 @@ export class OrganizeImportsCommand implements Command {
) {
}
private async _execute(client: TypeScriptServiceClient, document: TextDocument): Promise<WorkspaceEdit | TextEdit[] | null> {
private async _execute(client: TypeScriptServiceClient, document: TextDocument, sortOnly = false): Promise<WorkspaceEdit | TextEdit[] | null> {
let file = client.toPath(document.uri)
const args: Proto.OrganizeImportsRequestArgs = {
skipDestructiveCodeActions: sortOnly,
scope: {
type: 'file',
args: {
@ -38,7 +39,7 @@ export class OrganizeImportsCommand implements Command {
client,
response.body
)
let keys = Object.keys(edit.changes)
let keys = Object.keys(edit.changes || {})
if (keys.length == 1) {
let doc = workspace.getDocument(keys[0])
if (doc) {
@ -49,7 +50,7 @@ export class OrganizeImportsCommand implements Command {
if (edit) await workspace.applyEdit(edit)
}
public async execute(document?: TextDocument): Promise<void> {
public async execute(document?: TextDocument, sortOnly = false): Promise<void> {
let client = await this.service.getClientHost()
if (!document) {
let doc = await workspace.document
@ -61,10 +62,14 @@ export class OrganizeImportsCommand implements Command {
}
document = doc.textDocument
}
await this._execute(client.serviceClient, document)
await this._execute(client.serviceClient, document, sortOnly)
}
}
export class SourceImportsCommand extends OrganizeImportsCommand {
public readonly id = 'tsserver.sortImports'
}
export class OrganizeImportsCodeActionProvider implements CodeActionProvider {
// public static readonly minVersion = API.v280
@ -91,11 +96,16 @@ export class OrganizeImportsCodeActionProvider implements CodeActionProvider {
}
await this.fileConfigManager.ensureConfigurationForDocument(document, token)
const action = CodeAction.create('Organize Imports', {
const organizeImportsAction = CodeAction.create('Organize Imports', {
title: '',
command: 'tsserver.organizeImports',
arguments: [document]
}, CodeActionKind.SourceOrganizeImports)
return [action]
const sortImportsAction = CodeAction.create('Sort Imports', {
title: '',
command: 'tsserver.sortImports',
arguments: [document, true]
}, 'source.sortImports')
return [organizeImportsAction, sortImportsAction]
}
}

View file

@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import cp from 'child_process'
import { Disposable } from 'vscode-languageserver-protocol'
import * as Proto from './protocol'
import { Reader } from './utils/wireProtocol'
export interface ToCancelOnResourceChanged {
readonly resource: string
cancel(): void
}
export default class ForkedTsServerProcess implements Disposable {
private readonly _reader: Reader<Proto.Response>
constructor(private childProcess: cp.ChildProcess) {
this._reader = new Reader<Proto.Response>(this.childProcess.stdout)
}
public readonly toCancelOnResourceChange = new Set<ToCancelOnResourceChanged>()
public onExit(cb: (err: any, signal: string) => void): void {
this.childProcess.on('exit', cb)
}
public write(serverRequest: Proto.Request): void {
this.childProcess.stdin.write(
JSON.stringify(serverRequest) + '\r\n',
'utf8'
)
}
public onData(handler: (data: Proto.Response) => void): void {
this._reader.onData(handler)
}
public onError(handler: (err: Error) => void): void {
this.childProcess.on('error', handler)
this._reader.onError(handler)
}
public kill(): void {
this.toCancelOnResourceChange.clear()
this.childProcess.kill()
this._reader.dispose()
}
public dispose(): void {
this.toCancelOnResourceChange.clear()
this._reader.dispose()
}
}

View file

@ -34,7 +34,7 @@ export interface TypeScriptServerPlugin {
readonly languages: string[]
}
export enum ExectuionTarget {
export enum ExecutionTarget {
Semantic,
Syntax
}
@ -43,7 +43,7 @@ export type ExecConfig = {
readonly lowPriority?: boolean
readonly nonRecoverable?: boolean
readonly cancelOnResourceChange?: string
readonly executionTarget?: ExectuionTarget
readonly executionTarget?: ExecutionTarget
}
export interface TypeScriptRequestTypes {
@ -84,6 +84,7 @@ export interface TypeScriptRequestTypes {
'provideCallHierarchyIncomingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyIncomingCallsResponse]
'provideCallHierarchyOutgoingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyOutgoingCallsResponse]
'fileReferences': [Proto.FileRequestArgs, Proto.FileReferencesResponse]
'provideInlayHints': [Proto.InlayHintsRequestArgs, Proto.InlayHintsResponse]
}
export interface ITypeScriptServiceClient {

View file

@ -2,13 +2,12 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import cp from 'child_process'
import { Document, ServiceStat, Uri, window, workspace } from 'coc.nvim'
import fs from 'fs'
import os from 'os'
import path from 'path'
import { CancellationToken, CancellationTokenSource, Disposable, Emitter, Event } from 'vscode-languageserver-protocol'
import * as fileSchemes from '../utils/fileSchemess'
import * as fileSchemes from '../utils/fileSchemes'
import { PluginManager } from '../utils/plugins'
import { CallbackMap } from './callbackMap'
import BufferSyncSupport from './features/bufferSyncSupport'
@ -20,50 +19,12 @@ import { ExecConfig, ITypeScriptServiceClient, ServerResponse } from './typescri
import API from './utils/api'
import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'
import Logger from './utils/logger'
import { fork, getTempDirectory, getTempFile, IForkOptions, makeRandomHexString } from './utils/process'
import { fork, getTempDirectory, createTempDirectory, getTempFile, IForkOptions, makeRandomHexString } from './utils/process'
import Tracer from './utils/tracer'
import { inferredProjectConfig } from './utils/tsconfig'
import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider'
import VersionStatus from './utils/versionStatus'
import { ICallback, Reader } from './utils/wireProtocol'
interface ToCancelOnResourceChanged {
readonly resource: string
cancel(): void
}
class ForkedTsServerProcess {
constructor(private childProcess: cp.ChildProcess) {}
public readonly toCancelOnResourceChange = new Set<ToCancelOnResourceChanged>()
public onError(cb: (err: Error) => void): void {
this.childProcess.on('error', cb)
}
public onExit(cb: (err: any) => void): void {
this.childProcess.on('exit', cb)
}
public write(serverRequest: Proto.Request): void {
this.childProcess.stdin.write(
JSON.stringify(serverRequest) + '\r\n',
'utf8'
)
}
public createReader(
callback: ICallback<Proto.Response>,
onError: (error: any) => void
): void {
// tslint:disable-next-line:no-unused-expression
new Reader<Proto.Response>(this.childProcess.stdout, callback, onError)
}
public kill(): void {
this.childProcess.kill()
}
}
import ForkedTsServerProcess, { ToCancelOnResourceChanged } from './tsServerProcess'
export interface TsDiagnostics {
readonly kind: DiagnosticKind
@ -72,6 +33,8 @@ export interface TsDiagnostics {
}
export default class TypeScriptServiceClient implements ITypeScriptServiceClient {
private token: number = 0
private noRestart = false
public state = ServiceStat.Initial
public readonly logger: Logger = new Logger()
public readonly bufferSyncSupport: BufferSyncSupport
@ -84,14 +47,13 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
private versionProvider: TypeScriptVersionProvider
private tsServerLogFile: string | null = null
private tsServerProcess: ForkedTsServerProcess | undefined
private servicePromise: Thenable<ForkedTsServerProcess> | null
private lastError: Error | null
private lastStart: number
private numberRestarts: number
private cancellationPipeName: string | null = null
private _callbacks = new CallbackMap<Proto.Response>()
private _requestQueue = new RequestQueue()
private _pendingResponses = new Set<number>()
private _onReady?: { promise: Promise<void>; resolve: () => void; reject: () => void }
private versionStatus: VersionStatus
private readonly _onTsServerStarted = new Emitter<API>()
@ -108,13 +70,20 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
constructor(
public readonly pluginManager: PluginManager,
public readonly modeIds: string[]
public readonly modeIds: string[],
private readonly tscPathVim: string | undefined
) {
this.pathSeparator = path.sep
this.lastStart = Date.now()
this.servicePromise = null
this.lastError = null
this.numberRestarts = 0
let resolve: () => void
let reject: () => void
const p = new Promise<void>((res, rej) => {
resolve = res
reject = rej
})
this._onReady = { promise: p, resolve: resolve!, reject: reject! }
this.fileConfigurationManager = new FileConfigurationManager(this)
this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace()
this.versionProvider = new TypeScriptVersionProvider(this._configuration)
@ -130,7 +99,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
}, null, this.disposables)
this.bufferSyncSupport = new BufferSyncSupport(this, modeIds)
this.onTsServerStarted(() => {
this.onReady(() => {
this.bufferSyncSupport.listen()
})
@ -163,17 +132,20 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
return this._configuration
}
public onReady(f: () => void): Promise<void> {
return this._onReady!.promise.then(f)
}
public dispose(): void {
if (this.servicePromise) {
this.servicePromise
.then(childProcess => {
childProcess.kill()
})
.then(undefined, () => void 0)
}
this.tsServerProcess.kill()
this.diagnosticsManager.dispose()
this.bufferSyncSupport.dispose()
this.logger.dispose()
this._onTsServerStarted.dispose()
this._onProjectLanguageServiceStateChanged.dispose()
this._onDidBeginInstallTypings.dispose()
this._onDidEndInstallTypings.dispose()
this._onTypesInstallerInitializationFailed.dispose()
this._onResendModelsRequested.dispose()
this.versionStatus.dispose()
}
@ -186,40 +158,29 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
this.logger.error(message, data)
}
public restartTsServer(): Promise<any> {
const start = () => {
this.servicePromise = this.startService(true)
return this.servicePromise
}
if (this.servicePromise) {
return Promise.resolve(this.servicePromise.then(childProcess => {
this.state = ServiceStat.Stopping
this.info('Killing TS Server')
this.isRestarting = true
childProcess.kill()
this.servicePromise = null
}).then(start))
} else {
return Promise.resolve(start())
public restartTsServer(): void {
if (this.tsServerProcess) {
this.state = ServiceStat.Stopping
this.info('Killing TS Server')
this.isRestarting = true
this.tsServerProcess.kill()
}
this.startService(true)
}
public stop(): Promise<void> {
if (!this.servicePromise) return
return new Promise((resolve, reject) => {
this.servicePromise.then(childProcess => {
if (this.state == ServiceStat.Running) {
this.info('Killing TS Server')
childProcess.onExit(() => {
resolve()
})
childProcess.kill()
this.servicePromise = null
} else {
return new Promise(resolve => {
let { tsServerProcess } = this
if (tsServerProcess && this.state == ServiceStat.Running) {
this.info('Killing TS Server')
tsServerProcess.onExit(() => {
resolve()
}
}, reject)
})
this.noRestart = true
tsServerProcess.kill()
} else {
resolve()
}
})
}
@ -253,125 +214,101 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
return this._tscPath
}
private service(): Thenable<ForkedTsServerProcess> {
if (this.servicePromise) {
return this.servicePromise
}
if (this.lastError) {
return Promise.reject<ForkedTsServerProcess>(this.lastError)
}
return this.startService().then(() => {
if (this.servicePromise) {
return this.servicePromise
}
})
}
public ensureServiceStarted(): void {
if (!this.servicePromise) {
this.startService().catch(err => {
window.showMessage(`TSServer start failed: ${err.message}`, 'error')
this.error(`Service start failed: ${err.stack}`)
})
if (!this.tsServerProcess) {
this.startService()
}
}
private async startService(resendModels = false): Promise<ForkedTsServerProcess> {
private startService(resendModels = false): ForkedTsServerProcess | undefined {
const { ignoreLocalTsserver } = this.configuration
let currentVersion: TypeScriptVersion
if (!ignoreLocalTsserver) currentVersion = this.versionProvider.getLocalVersion()
if (this.tscPathVim) currentVersion = this.versionProvider.getVersionFromTscPath(this.tscPathVim)
if (!currentVersion && !ignoreLocalTsserver) currentVersion = this.versionProvider.getLocalVersion()
if (!currentVersion || !fs.existsSync(currentVersion.tsServerPath)) {
this.info('Local tsserver not found, using bundled tsserver with coc-tsserver.')
currentVersion = this.versionProvider.getDefaultVersion()
}
if (!currentVersion || !currentVersion.isValid) {
if (this.configuration.globalTsdk) {
window.showMessage(`Can not find typescript module, in 'tsserver.tsdk': ${this.configuration.globalTsdk}`, 'error')
window.showErrorMessage(`Can not find typescript module, in 'tsserver.tsdk': ${this.configuration.globalTsdk}`)
} else {
window.showMessage(`Can not find typescript module, run ':CocInstall coc-tsserver' to fix it!`, 'error')
window.showErrorMessage(`Can not find typescript module, run ':CocInstall coc-tsserver' to fix it!`)
}
return
}
this._apiVersion = currentVersion.version
this._tscPath = currentVersion.tscPath
workspace.nvim.setVar('Tsserver_path', this._tscPath, true)
this.versionStatus.onDidChangeTypeScriptVersion(currentVersion)
this.lastError = null
const tsServerForkArgs = await this.getTsServerArgs(currentVersion)
const debugPort = this._configuration.debugPort
const maxTsServerMemory = this._configuration.maxTsServerMemory
const options = {
execArgv: [
...(debugPort ? [`--inspect=${debugPort}`] : []), // [`--debug-brk=5859`]
...(maxTsServerMemory ? [`--max-old-space-size=${maxTsServerMemory}`] : []),
],
cwd: workspace.root
}
this.servicePromise = this.startProcess(currentVersion, tsServerForkArgs, options, resendModels)
return this.servicePromise
const tsServerForkArgs = this.getTsServerArgs(currentVersion)
const options = { execArgv: this.getExecArgv() }
return this.startProcess(currentVersion, tsServerForkArgs, options, resendModels)
}
private startProcess(currentVersion: TypeScriptVersion, args: string[], options: IForkOptions, resendModels: boolean): Promise<ForkedTsServerProcess> {
private getExecArgv(): string[] {
const args: string[] = []
const debugPort = getDebugPort()
if (debugPort) {
const isBreak = process.env[process.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK'] !== undefined
const inspectFlag = isBreak ? '--inspect-brk' : '--inspect'
args.push(`${inspectFlag}=${debugPort}`)
}
const maxTsServerMemory = this._configuration.maxTsServerMemory
if (maxTsServerMemory) {
args.push(`--max-old-space-size=${maxTsServerMemory}`)
}
return args
}
private startProcess(currentVersion: TypeScriptVersion, args: string[], options: IForkOptions, resendModels: boolean): ForkedTsServerProcess {
const myToken = ++this.token
this.state = ServiceStat.Starting
return new Promise((resolve, reject) => {
try {
fork(
currentVersion.tsServerPath,
args,
options,
this.logger,
(err: any, childProcess: cp.ChildProcess | null) => {
if (err || !childProcess) {
this.state = ServiceStat.StartFailed
this.lastError = err
this.error('Starting TSServer failed with error.', err.stack)
return
}
this.state = ServiceStat.Running
this.info('Started TSServer', JSON.stringify(currentVersion, null, 2))
const handle = new ForkedTsServerProcess(childProcess)
this.tsServerProcess = handle
this.lastStart = Date.now()
handle.onError((err: Error) => {
this.lastError = err
this.error('TSServer errored with error.', err)
this.error(`TSServer log file: ${this.tsServerLogFile || ''}`)
window.showMessage(`TSServer errored with error. ${err.message}`, 'error')
this.serviceExited(false)
})
handle.onExit((code: any) => {
if (code == null) {
this.info('TSServer normal exit')
} else {
this.error(`TSServer exited with code: ${code}`)
}
this.info(`TSServer log file: ${this.tsServerLogFile || ''}`)
this.serviceExited(!this.isRestarting)
this.isRestarting = false
})
handle.createReader(
msg => {
this.dispatchMessage(msg)
},
error => {
this.error('ReaderError', error)
}
)
resolve(handle)
this.serviceStarted(resendModels)
this._onTsServerStarted.fire(currentVersion.version)
}
)
} catch (e) {
reject(e)
}
})
try {
let childProcess = fork(currentVersion.tsServerPath, args, options, this.logger)
this.state = ServiceStat.Running
this.info('Starting TSServer', JSON.stringify(currentVersion, null, 2))
const handle = new ForkedTsServerProcess(childProcess)
this.tsServerProcess = handle
this.lastStart = Date.now()
handle.onError((err: Error) => {
if (this.token != myToken) return
window.showErrorMessage(`TypeScript language server exited with error. Error message is: ${err.message}`)
this.error('TSServer errored with error.', err)
this.error(`TSServer log file: ${this.tsServerLogFile || ''}`)
window.showMessage(`TSServer errored with error. ${err.message}`, 'error')
this.serviceExited(false)
})
handle.onExit((code: any, signal: string) => {
handle.dispose()
if (this.token != myToken) return
if (code == null) {
this.info(`TSServer exited. Signal: ${signal}`)
} else {
this.error(`TSServer exited with code: ${code}. Signal: ${signal}`)
}
this.info(`TSServer log file: ${this.tsServerLogFile || ''}`)
this.serviceExited(!this.isRestarting)
this.isRestarting = false
})
handle.onData(msg => {
this.dispatchMessage(msg)
})
this.serviceStarted(resendModels)
this._onReady!.resolve()
this._onTsServerStarted.fire(currentVersion.version)
return handle
} catch (err) {
this.state = ServiceStat.StartFailed
this.error('Starting TSServer failed with error.', err.stack)
return undefined
}
}
public async openTsServerLogFile(): Promise<boolean> {
const isRoot = process.getuid && process.getuid() == 0
let echoErr = (msg: string) => {
window.showMessage(msg, 'error')
window.showErrorMessage(msg)
}
if (isRoot) {
echoErr('Log disabled for root user.')
@ -408,6 +345,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
preferences: {
providePrefixAndSuffixTextForRename: true,
allowRenameOfImportPath: true,
includePackageJsonAutoImports: this._configuration.includePackageJsonAutoImports
},
watchOptions
}
@ -448,12 +386,15 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
private serviceExited(restart: boolean): void {
this.state = ServiceStat.Stopped
this.servicePromise = null
this.tsServerLogFile = null
this._callbacks.destroy('Service died.')
this._callbacks = new CallbackMap<Proto.Response>()
this._requestQueue = new RequestQueue()
this._pendingResponses = new Set<number>()
if (this.noRestart) {
this.noRestart = false
return
}
if (restart) {
const diff = Date.now() - this.lastStart
this.numberRestarts++
@ -470,7 +411,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
}
}
if (startService) {
this.startService(true) // tslint:disable-line
this.startService(true)
}
}
}
@ -482,7 +423,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
public toOpenedFilePath(uri: string, options: { suppressAlertOnFailure?: boolean } = {}): string | undefined {
if (!this.bufferSyncSupport.ensureHasBuffer(uri)) {
if (!options.suppressAlertOnFailure) {
console.error(`Unexpected resource ${uri}`)
this.error(`Unexpected resource ${uri}`)
}
return undefined
}
@ -490,6 +431,9 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
}
public toResource(filepath: string): string {
if (filepath.includes('zipfile:')) {
return filepath.replace(/.*zipfile:/, 'zipfile://');
}
if (this._apiVersion.gte(API.v213)) {
if (filepath.startsWith(this.inMemoryResourcePrefix + 'untitled:')) {
let resource = Uri.parse(filepath)
@ -599,13 +543,14 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
}
private fatalError(command: string, error: any): void {
console.error(`A non-recoverable error occured while executing tsserver command: ${command}`)
this.error(`A non-recoverable error occured while executing tsserver command: ${command}`)
if (this.state === ServiceStat.Running) {
this.info('Killing TS Server by fatal error:', error)
this.service().then(service => {
service.kill()
})
let { tsServerProcess } = this
if (tsServerProcess) {
this.tsServerProcess = undefined
tsServerProcess.kill()
}
}
}
@ -630,7 +575,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined
private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>
private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
if (this.servicePromise == null) {
if (!this.tsServerProcess) {
return Promise.resolve(undefined)
}
this.bufferSyncSupport.beforeCommand(command)
@ -678,16 +623,15 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
if (requestItem.expectsResponse && !requestItem.isAsync) {
this._pendingResponses.add(requestItem.request.seq)
}
this.service().then(childProcess => {
try {
childProcess.write(serverRequest)
} catch (err) {
const callback = this.fetchCallback(serverRequest.seq)
if (callback) {
callback.onError(err)
}
if (!this.tsServerProcess) return
try {
this.tsServerProcess.write(serverRequest)
} catch (err) {
const callback = this.fetchCallback(serverRequest.seq)
if (callback) {
callback.onError(err)
}
})
}
}
private tryCancelRequest(seq: number, command: string): boolean {
@ -722,7 +666,6 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
if (!callback) {
return undefined
}
this._pendingResponses.delete(seq)
return callback
}
@ -840,8 +783,9 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
}
}
private async getTsServerArgs(currentVersion: TypeScriptVersion): Promise<string[]> {
private getTsServerArgs(currentVersion: TypeScriptVersion): string[] {
const args: string[] = []
args.push('--allowLocalPluginLoads')
if (this.apiVersion.gte(API.v250)) {
@ -859,10 +803,10 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
args.push('--cancellationPipeName', this.cancellationPipeName + '*')
}
const logDir = getTempDirectory()
if (this.apiVersion.gte(API.v222)) {
const isRoot = process.getuid && process.getuid() == 0
if (this._configuration.tsServerLogLevel !== TsServerLogLevel.Off && !isRoot) {
const logDir = getTempDirectory()
if (logDir) {
this.tsServerLogFile = path.join(logDir, `tsserver.log`)
this.info('TSServer log file :', this.tsServerLogFile)
@ -881,6 +825,16 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
}
}
if (this._configuration.enableTsServerTracing) {
let tsServerTraceDirectory = createTempDirectory(`tsserver-trace-${makeRandomHexString(5)}`)
if (tsServerTraceDirectory) {
args.push('--traceDirectory', tsServerTraceDirectory)
this.info('TSServer trace directory :', tsServerTraceDirectory)
} else {
this.error('Could not create TSServer trace directory')
}
}
if (this.apiVersion.gte(API.v230)) {
const pluginNames = this.pluginManager.plugins.map(x => x.name)
let pluginPaths = this._configuration.tsServerPluginPaths
@ -954,11 +908,8 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
public configurePlugin(pluginName: string, configuration: {}): any {
if (this.apiVersion.gte(API.v314)) {
if (!this.servicePromise) return
this.servicePromise.then(() => {
// tslint:disable-next-line: no-floating-promises
this.executeWithoutWaitingForResponse('configurePlugin', { pluginName, configuration })
})
if (!this.tsServerProcess) return
this.executeWithoutWaitingForResponse('configurePlugin', { pluginName, configuration })
}
}
@ -1001,3 +952,15 @@ function getQueueingType(
}
return lowPriority ? RequestQueueingType.LowPriority : RequestQueueingType.Normal
}
function getDebugPort(): number | undefined {
let debugBrk = process.env[process.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK']
let value = debugBrk || process.env[process.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK']
if (value) {
const port = parseInt(value)
if (!isNaN(port)) {
return port
}
}
return undefined
}

View file

@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { disposeAll, languages, TextDocument, Uri, workspace } from 'coc.nvim'
import { ConfigurationChangeEvent, disposeAll, languages, TextDocument, Uri, workspace } from 'coc.nvim'
import { CancellationToken, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Disposable, Position, Range } from 'vscode-languageserver-protocol'
import { flatten } from '../utils/arrays'
import { PluginManager } from '../utils/plugins'
@ -22,6 +22,7 @@ import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus'
const styleCheckDiagnostics = [
6133, // variable is declared but never used
6138, // property is declared but its value is never read
6192, // allImportsAreUnused
7027, // unreachable code detected
7028, // unused label
7029, // fall through case in switch
@ -37,7 +38,7 @@ export default class TypeScriptServiceClientHost implements Disposable {
private readonly fileConfigurationManager: FileConfigurationManager
private reportStyleCheckAsWarnings = true
constructor(descriptions: LanguageDescription[], pluginManager: PluginManager) {
constructor(descriptions: LanguageDescription[], pluginManager: PluginManager, tscPath: string | null) {
let timer: NodeJS.Timer
const handleProjectChange = () => {
if (timer) clearTimeout(timer)
@ -56,7 +57,7 @@ export default class TypeScriptServiceClientHost implements Disposable {
packageFileWatcher.onDidChange(handleProjectChange, this, this.disposables)
const allModeIds = this.getAllModeIds(descriptions, pluginManager)
this.client = new TypeScriptServiceClient(pluginManager, allModeIds)
this.client = new TypeScriptServiceClient(pluginManager, allModeIds, tscPath)
this.disposables.push(this.client)
this.client.onDiagnosticsReceived(({ kind, resource, diagnostics }) => {
this.diagnosticsReceived(kind, resource, diagnostics).catch(e => {
@ -102,39 +103,43 @@ export default class TypeScriptServiceClientHost implements Disposable {
)
this.languagePerId.set(description.id, manager)
}
const languageIds = new Set<string>()
for (const plugin of pluginManager.plugins) {
if (plugin.configNamespace && plugin.languages.length) {
this.client.ensureServiceStarted()
this.client.onReady(() => {
const languageIds = new Set<string>()
for (const plugin of pluginManager.plugins) {
if (plugin.configNamespace && plugin.languages.length) {
this.registerExtensionLanguageProvider({
id: plugin.configNamespace,
languageIds: Array.from(plugin.languages),
diagnosticSource: 'ts-plugin',
diagnosticLanguage: DiagnosticLanguage.TypeScript,
diagnosticOwner: 'typescript',
standardFileExtensions: [],
isExternal: true
})
} else {
for (const language of plugin.languages) {
languageIds.add(language)
}
}
}
if (languageIds.size) {
this.registerExtensionLanguageProvider({
id: plugin.configNamespace,
modeIds: Array.from(plugin.languages),
id: 'typescript-plugins',
languageIds: Array.from(languageIds.values()),
diagnosticSource: 'ts-plugin',
diagnosticLanguage: DiagnosticLanguage.TypeScript,
diagnosticOwner: 'typescript',
standardFileExtensions: [],
isExternal: true
})
} else {
for (const language of plugin.languages) {
languageIds.add(language)
}
}
}
if (languageIds.size) {
this.registerExtensionLanguageProvider({
id: 'typescript-plugins',
modeIds: Array.from(languageIds.values()),
diagnosticSource: 'ts-plugin',
diagnosticLanguage: DiagnosticLanguage.TypeScript,
diagnosticOwner: 'typescript',
isExternal: true
})
}
this.client.ensureServiceStarted()
})
this.client.onTsServerStarted(() => {
this.triggerAllDiagnostics()
})
workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables)
this.configurationChanged()
}
@ -155,10 +160,6 @@ export default class TypeScriptServiceClientHost implements Disposable {
this.ataProgressReporter.dispose()
}
public reset(): void {
this.fileConfigurationManager.reset()
}
public get serviceClient(): TypeScriptServiceClient {
return this.client
}
@ -174,17 +175,20 @@ export default class TypeScriptServiceClientHost implements Disposable {
return this.languagePerId.get(languageId)
}
private configurationChanged(): void {
const config = workspace.getConfiguration('tsserver')
this.reportStyleCheckAsWarnings = config.get('reportStyleChecksAsWarnings', true)
private configurationChanged(e?: ConfigurationChangeEvent): void {
if (!e || e.affectsConfiguration('tsserver')) {
const config = workspace.getConfiguration('tsserver')
this.reportStyleCheckAsWarnings = config.get('reportStyleChecksAsWarnings', true)
}
}
public async findLanguage(uri: string): Promise<LanguageProvider> {
try {
let doc = this.client.getDocument(uri)
if (!doc) return undefined
let languages = Array.from(this.languagePerId.values())
return languages.find(language => language.handles(uri, doc.textDocument))
// possible not opened
if (doc) return languages.find(language => language.handles(uri, doc.textDocument))
return languages.find(language => language.handlesUri(Uri.parse(uri)))
} catch {
return undefined
}
@ -289,7 +293,7 @@ export default class TypeScriptServiceClientHost implements Disposable {
private getAllModeIds(descriptions: LanguageDescription[], pluginManager: PluginManager): string[] {
const allModeIds = flatten([
...descriptions.map(x => x.modeIds),
...descriptions.map(x => x.languageIds),
...pluginManager.plugins.map(x => x.languages)
])
return allModeIds

View file

@ -35,6 +35,7 @@ export default class API {
public static readonly v340 = API.fromSimpleString('3.4.0')
public static readonly v345 = API.fromSimpleString('3.4.5')
public static readonly v350 = API.fromSimpleString('3.5.0')
public static readonly v370 = API.fromSimpleString('3.7.0')
public static readonly v380 = API.fromSimpleString('3.8.0')
public static readonly v381 = API.fromSimpleString('3.8.1')
public static readonly v390 = API.fromSimpleString('3.9.0')
@ -42,6 +43,7 @@ export default class API {
public static readonly v401 = API.fromSimpleString('4.0.1')
public static readonly v420 = API.fromSimpleString('4.2.0')
public static readonly v430 = API.fromSimpleString('4.3.0')
public static readonly v440 = API.fromSimpleString('4.4.0')
public static fromVersionString(versionString: string): API {
let version = semver.valid(versionString)

View file

@ -59,6 +59,9 @@ export function convertCompletionEntry(
insertText = label
insertTextFormat = InsertTextFormat.Snippet
}
if (tsEntry.isSnippet) {
insertTextFormat = InsertTextFormat.Snippet
}
let textEdit: TextEdit | null = null
if (tsEntry.replacementSpan) {
@ -73,7 +76,7 @@ export function convertCompletionEntry(
if (tsEntry.kindModifiers) {
const kindModifiers = new Set(tsEntry.kindModifiers.split(/,|\s+/g))
if (kindModifiers.has(PConst.KindModifiers.optional)) {
insertText = label
insertText = insertText ?? label
label += '?'
}
@ -136,6 +139,7 @@ function convertKind(kind: string): CompletionItemKind {
case PConst.Kind.memberSetAccessor:
return CompletionItemKind.Field
case PConst.Kind.function:
case PConst.Kind.localFunction:
return CompletionItemKind.Function
case PConst.Kind.method:
case PConst.Kind.constructSignature:

View file

@ -40,14 +40,25 @@ export namespace TsServerLogLevel {
export class TypeScriptServiceConfiguration {
private _configuration: WorkspaceConfiguration
private _includePackageJsonAutoImports: 'auto' | 'on' | 'off'
private constructor() {
this._configuration = workspace.getConfiguration('tsserver')
this._includePackageJsonAutoImports = workspace.getConfiguration('typescript').get<'auto' | 'on' | 'off'>('preferences.includePackageJsonAutoImports')
workspace.onDidChangeConfiguration(() => {
this._configuration = workspace.getConfiguration('tsserver')
this._includePackageJsonAutoImports = workspace.getConfiguration('typescript').get<'auto' | 'on' | 'off'>('preferences.includePackageJsonAutoImports')
})
}
public get enableTsServerTracing(): boolean {
return this._configuration.get<boolean>('enableTracing', false)
}
public get includePackageJsonAutoImports(): 'auto' | 'on' | 'off' {
return this._includePackageJsonAutoImports
}
public get locale(): string | null {
return this._configuration.get<string | null>('locale', null)
}
@ -97,10 +108,6 @@ export class TypeScriptServiceConfiguration {
return this._configuration.get<number>('maxTsServerMemory', 0)
}
public get debugPort(): number | null {
return this._configuration.get<number>('debugPort', parseInt(process.env['TSS_DEBUG'], 10))
}
public get npmLocation(): string | null {
let path = this._configuration.get<string>('npm', '')
if (path) return workspace.expand(path)

View file

@ -3,16 +3,18 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as languageModeIds from './languageModeIds'
import path from 'path'
import { Uri } from 'coc.nvim'
export interface LanguageDescription {
readonly id: string
readonly diagnosticSource: string
readonly diagnosticLanguage: DiagnosticLanguage
readonly modeIds: string[]
readonly configFile?: string
readonly languageIds: string[]
readonly isExternal?: boolean
readonly diagnosticOwner: string
readonly configFilePattern?: RegExp
readonly standardFileExtensions: ReadonlyArray<string>,
}
export const enum DiagnosticLanguage {
@ -25,19 +27,45 @@ export const standardLanguageDescriptions: LanguageDescription[] = [
id: 'typescript',
diagnosticSource: 'ts',
diagnosticOwner: 'typescript',
modeIds: [languageModeIds.typescript, languageModeIds.typescriptreact,
languageModeIds.typescripttsx, languageModeIds.typescriptjsx],
diagnosticLanguage: DiagnosticLanguage.TypeScript,
configFile: 'tsconfig.json',
configFilePattern: /^tsconfig(\..*)?\.json$/gi
languageIds: [languageModeIds.typescript, languageModeIds.typescriptreact, languageModeIds.typescripttsx, languageModeIds.typescriptjsx],
configFilePattern: /^tsconfig(\..*)?\.json$/gi,
standardFileExtensions: [
'ts',
'tsx',
'cts',
'mts'
]
},
{
id: 'javascript',
diagnosticSource: 'ts',
diagnosticOwner: 'typescript',
modeIds: [languageModeIds.javascript, languageModeIds.javascriptreact, languageModeIds.javascriptjsx],
diagnosticLanguage: DiagnosticLanguage.JavaScript,
configFile: 'jsconfig.json',
configFilePattern: /^jsconfig(\..*)?\.json$/gi
languageIds: [languageModeIds.javascript, languageModeIds.javascriptreact, languageModeIds.javascriptjsx], diagnosticLanguage: DiagnosticLanguage.JavaScript,
configFilePattern: /^jsconfig(\..*)?\.json$/gi,
standardFileExtensions: [
'js',
'jsx',
'cjs',
'mjs',
'es6',
'pac',
]
}
]
export function isTsConfigFileName(fileName: string): boolean {
return /^tsconfig\.(.+\.)?json$/i.test(path.basename(fileName))
}
export function isJsConfigOrTsConfigFileName(fileName: string): boolean {
return /^[jt]sconfig\.(.+\.)?json$/i.test(path.basename(fileName))
}
export function doesResourceLookLikeATypeScriptFile(resource: Uri): boolean {
return /\.(tsx?|mts|cts)$/i.test(resource.fsPath)
}
export function doesResourceLookLikeAJavaScriptFile(resource: Uri): boolean {
return /\.(jsx?|mjs|cjs)$/i.test(resource.fsPath)
}

View file

@ -51,12 +51,24 @@ export default class Logger {
this.logLevel('Error', message, data)
}
private now(): string {
const now = new Date()
return padLeft(now.getUTCHours() + '', 2, '0')
+ ':' + padLeft(now.getMinutes() + '', 2, '0')
+ ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds()
}
public logLevel(level: string, message: string, data?: any): void {
this.output.appendLine(
`[${level} - ${new Date().toLocaleTimeString()}] ${message}`
`[${level} - ${this.now()}] ${message}`
)
if (data) {
this.output.appendLine(this.data2String(data))
}
}
}
function padLeft(s: string, n: number, pad = ' ') {
return pad.repeat(Math.max(0, n - s.length)) + s
}

View file

@ -94,20 +94,15 @@ function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined {
return label + (text.match(/\r\n|\n/g) ? ' \n' + text : `${text}`)
}
export function plain(parts: Proto.SymbolDisplayPart[]): string {
if (!parts || !parts.length) return ''
return parts.map(part => part.text).join('')
}
export function tagsMarkdownPreview(tags: Proto.JSDocTagInfo[]): string {
return (tags || []).map(getTagDocumentation).join(' \n\n')
}
export function markdownDocumentation(
documentation: Proto.SymbolDisplayPart[],
documentation: Proto.SymbolDisplayPart[] | string,
tags: Proto.JSDocTagInfo[]
): MarkupContent {
let out = plain(documentation)
let out = plainWithLinks(documentation)
const tagsPreview = tagsMarkdownPreview(tags)
if (tagsPreview) {
out = out + ('\n\n' + tagsPreview)
@ -118,6 +113,12 @@ export function markdownDocumentation(
}
}
export function plainWithLinks(
parts: readonly Proto.SymbolDisplayPart[] | string,
): string {
return processInlineTags(convertLinkTags(parts))
}
/**
* Convert `@link` inline tags to markdown links
*/

View file

@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import cp from 'child_process'
import net from 'net'
import os from 'os'
import path from 'path'
import fs from 'fs'
@ -24,47 +23,40 @@ export function makeRandomHexString(length: number): string {
return result
}
export function getTempDirectory(): string {
export function getTempDirectory(): string | undefined {
let dir = path.join(os.tmpdir(), `coc.nvim-${process.pid}`)
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
try {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
}
} catch (e) {
return undefined
}
return dir
}
function generatePipeName(): string {
return getPipeName(makeRandomHexString(40))
}
function getPipeName(name: string): string {
const fullName = 'coc-tsc-' + name
if (process.platform === 'win32') {
return '\\\\.\\pipe\\' + fullName + '-sock'
}
const tmpdir = getTempDirectory()
// Mac/Unix: use socket file
return path.join(tmpdir, fullName + '.sock')
}
export function getTempFile(name: string): string {
export function getTempFile(name: string): string | undefined {
const fullName = 'coc-nvim-' + name
return path.join(getTempDirectory(), fullName + '.sock')
let dir = getTempDirectory()
if (!dir) return undefined
return path.join(dir, fullName + '.sock')
}
function generatePatchedEnv(
env: any,
stdInPipeName: string,
stdOutPipeName: string,
stdErrPipeName: string
): any {
export function createTempDirectory(name: string) {
let dir = getTempDirectory()
if (!dir) return undefined
let res = path.join(dir, name)
try {
fs.mkdirSync(res)
} catch (e) {
return undefined
}
return res
}
function generatePatchedEnv(env: any, modulePath: string): any {
const newEnv = Object.assign({}, env)
// Set the two unique pipe names and the electron flag as process env
newEnv['STDIN_PIPE_NAME'] = stdInPipeName // tslint:disable-line
newEnv['STDOUT_PIPE_NAME'] = stdOutPipeName // tslint:disable-line
newEnv['STDERR_PIPE_NAME'] = stdErrPipeName // tslint:disable-line
newEnv['TSS_LOG'] = `-level verbose -file ${path.join(os.tmpdir(), 'coc-nvim-tsc.log')}` // tslint:disable-line
newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..')
// Ensure we always have a PATH set
newEnv['PATH'] = newEnv['PATH'] || process.env.PATH // tslint:disable-line
return newEnv
@ -75,88 +67,14 @@ export function fork(
args: string[],
options: IForkOptions,
logger: Logger,
callback: (error: any, cp: cp.ChildProcess | null) => void
): void {
let callbackCalled = false
const resolve = (result: cp.ChildProcess) => {
if (callbackCalled) {
return
}
callbackCalled = true
callback(null, result)
}
const reject = (err: any) => {
if (callbackCalled) {
return
}
callbackCalled = true
callback(err, null)
}
// Generate three unique pipe names
const stdInPipeName = generatePipeName()
const stdOutPipeName = generatePipeName()
const stdErrPipeName = generatePipeName()
const newEnv = generatePatchedEnv(
process.env,
stdInPipeName,
stdOutPipeName,
stdErrPipeName
)
newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..') // tslint:disable-line
let childProcess: cp.ChildProcess
// Begin listening to stderr pipe
let stdErrServer = net.createServer(stdErrStream => {
// From now on the childProcess.stderr is available for reading
childProcess.stderr = stdErrStream
})
stdErrServer.listen(stdErrPipeName)
// Begin listening to stdout pipe
let stdOutServer = net.createServer(stdOutStream => {
// The child process will write exactly one chunk with content `ready` when it has installed a listener to the stdin pipe
stdOutStream.once('data', (_chunk: Buffer) => {
// The child process is sending me the `ready` chunk, time to connect to the stdin pipe
childProcess.stdin = net.connect(stdInPipeName) as any
// From now on the childProcess.stdout is available for reading
childProcess.stdout = stdOutStream
resolve(childProcess)
})
})
stdOutServer.listen(stdOutPipeName)
let serverClosed = false
const closeServer = () => {
if (serverClosed) {
return
}
serverClosed = true
stdOutServer.close()
stdErrServer.close()
}
): cp.ChildProcess {
// Create the process
logger.info('Forking TSServer', `PATH: ${newEnv['PATH']} `)
const bootstrapperPath = path.resolve(__dirname, '../bin/tsserverForkStart')
childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), {
logger.info('Forking TSServer', `PATH: ${modulePath} `)
let childProcess = cp.fork(modulePath, args, {
silent: true,
env: newEnv,
cwd: undefined,
env: generatePatchedEnv(process.env, modulePath),
execArgv: options.execArgv
})
childProcess.once('error', (err: Error) => {
closeServer()
reject(err)
})
childProcess.once('exit', (err: Error) => {
closeServer()
reject(err)
})
return childProcess
}

View file

@ -6,6 +6,7 @@
* Helpers for converting FROM LanguageServer types language-server ts types
*/
import * as language from 'vscode-languageserver-protocol'
import { TextDocumentEdit } from 'vscode-languageserver-protocol'
import Proto from '../protocol'
import * as PConst from '../protocol.const'
import { ITypeScriptServiceClient } from '../typescriptService'
@ -98,14 +99,20 @@ export namespace WorkspaceEdit {
client: ITypeScriptServiceClient,
edits: Iterable<Proto.FileCodeEdits>
): language.WorkspaceEdit {
let changes = {}
let documentChanges: TextDocumentEdit[] = []
for (const edit of edits) {
let uri = client.toResource(edit.fileName)
changes[uri] = edit.textChanges.map(change => {
return TextEdit.fromCodeEdit(change)
documentChanges.push({
textDocument: {
uri,
version: null
},
edits: edit.textChanges.map(change => {
return TextEdit.fromCodeEdit(change)
})
})
}
return { changes }
return { documentChanges }
}
}

View file

@ -1,4 +1,4 @@
import { StatusBarItem, window } from 'coc.nvim'
import { StatusBarItem, workspace, window } from 'coc.nvim'
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
@ -110,10 +110,19 @@ export class AtaProgressReporter {
}
}
private onTypesInstallerInitializationFailed() { // tslint:disable-line
private async onTypesInstallerInitializationFailed() { // tslint:disable-line
this.statusItem.hide()
if (!this._invalid) {
window.showMessage('Could not install typings files for JavaScript language features. Please ensure that NPM is installed', 'error')
const config = workspace.getConfiguration('typescript')
if (config.get<boolean>('check.npmIsInstalled', true)) {
const dontShowAgain = "Don't Show Again"
const selected = await window.showWarningMessage(
"Could not install typings files for JavaScript language features. Please ensure that NPM is installed or configure 'typescript.npm' in your user settings. visit https://go.microsoft.com/fwlink/?linkid=847635 to learn more.",
dontShowAgain)
if (selected === dontShowAgain) {
config.update('check.npmIsInstalled', false, true)
}
}
}
this._invalid = true
}

View file

@ -106,6 +106,15 @@ export class TypeScriptVersionProvider {
return undefined
}
public getVersionFromTscPath(tscPath: string): TypeScriptVersion | undefined {
if (!tscPath || !fs.existsSync(tscPath)) return undefined
let libFolder = path.resolve(tscPath, '../../lib')
if (fs.existsSync(libFolder)) {
let version = new TypeScriptVersion(libFolder)
if (version.isValid) return version
}
}
public getLocalVersion(): TypeScriptVersion | undefined {
let folders = workspace.workspaceFolders.map(f => Uri.parse(f.uri).fsPath)
for (let p of folders) {

View file

@ -2,7 +2,9 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { disposeAll } from 'coc.nvim'
import stream from 'stream'
import { Disposable, Emitter } from 'vscode-languageserver-protocol'
const DefaultSize = 8192
const ContentLength = 'Content-Length: '
@ -99,18 +101,30 @@ export interface ICallback<T> {
(data: T): void // tslint:disable-line
}
export class Reader<T> {
export class Reader<T> implements Disposable {
private readonly buffer: ProtocolBuffer = new ProtocolBuffer()
private nextMessageLength = -1
private disposables: Disposable[] = []
public constructor(
private readonly readable: stream.Readable,
private readonly callback: ICallback<T>,
private readonly onError: (error: any) => void
) {
this.readable.on('data', (data: Buffer) => {
private readonly _onError = new Emitter<Error>()
public readonly onError = this._onError.event
private readonly _onData = new Emitter<T>()
public readonly onData = this._onData.event
public constructor(readable: stream.Readable) {
const onData = (data: Buffer) => {
this.onLengthData(data)
}
readable.on('data', onData)
this.disposables.push({
dispose: () => {
readable.off('data', onData)
}
})
this.disposables.push(this._onError)
this.disposables.push(this._onData)
}
private onLengthData(data: Buffer): void {
@ -129,10 +143,14 @@ export class Reader<T> {
}
this.nextMessageLength = -1
const json = JSON.parse(msg)
this.callback(json)
this._onData.fire(json)
}
} catch (e) {
this.onError(e)
this._onError.fire(e)
}
}
public dispose(): void {
disposeAll(this.disposables)
}
}

View file

@ -8,6 +8,7 @@
"allowUnreachableCode": true,
"allowUnusedLabels": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"noImplicitAny": false,
"noImplicitReturns": false,
"noUnusedLocals": false,

163
yarn.lock
View file

@ -2,35 +2,153 @@
# yarn lockfile v1
"@types/node@^10.12.0":
version "10.17.44"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.44.tgz#3945e6b702cb6403f22b779c8ea9e5c3f44ead40"
integrity sha512-vHPAyBX1ffLcy4fQHmDyIUMUb42gHZjPHU66nhvbMzAWJqHnySGZ6STwN3rwrnSd1FHB0DI/RWgGELgKSYRDmw==
"@types/node@^12.12.12":
version "12.20.41"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.41.tgz#81d7734c5257da9f04354bd9084a6ebbdd5198a5"
integrity sha512-f6xOqucbDirG7LOzedpvzjP3UTmHttRou3Mosx3vL9wr9AIQGhcPgVnqa8ihpZYnxyM1rxeNCvTyukPKZtq10Q==
coc.nvim@^0.0.81-next.6:
version "0.0.81-next.6"
resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.81-next.6.tgz#c3ee7079a66702ebb3b06d4c2bf333d9306ec561"
integrity sha512-VT+DhygyTIzu9IRrwCUljMzfNfh8TeXqqrvFsBE0E8cUwERgCAIvRbBMEDfqaaI+XFgyuwNRwbX5kEvfjG/u3g==
coc.nvim@^0.0.81-next.25:
version "0.0.81-next.25"
resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.81-next.25.tgz#8f84b7c71b742e111d330fb553b0df604d4929ec"
integrity sha512-c0OOZQSjgKLGNhIpKzlxkPiPmMCmYHSVcCDNA26BqFX8X0iWt3xXqwbxKiE54zfIsz0wFqL59iBVGUSBaqHGpA==
esbuild@^0.8.29:
version "0.8.29"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.29.tgz#cc20fb752e0905a3546d68ae1be58f9b97044c39"
integrity sha512-UDsEoeXuctVgG2hEts1Hwq2jYDGqV7nksEHEZaiCy2v+lXF5ButX4ErPAJAFi5ZNKKW+6Pom93pArV7hki6HnQ==
esbuild-android-arm64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.11.tgz#b8b34e35a5b43880664ac7a3fbc70243d7ed894f"
integrity sha512-6iHjgvMnC/SzDH8TefL+/3lgCjYWwAd1LixYfmz/TBPbDQlxcuSkX0yiQgcJB9k+ibZ54yjVXziIwGdlc+6WNw==
esbuild-darwin-64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.11.tgz#ba805de98c0412e50fcd0636451797da157b0625"
integrity sha512-olq84ikh6TiBcrs3FnM4eR5VPPlcJcdW8BnUz/lNoEWYifYQ+Po5DuYV1oz1CTFMw4k6bQIZl8T3yxL+ZT2uvQ==
esbuild-darwin-arm64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.11.tgz#4d3573e448af76ce33e16231f3d9f878542d6fe8"
integrity sha512-Jj0ieWLREPBYr/TZJrb2GFH8PVzDqiQWavo1pOFFShrcmHWDBDrlDxPzEZ67NF/Un3t6sNNmeI1TUS/fe1xARg==
esbuild-freebsd-64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.11.tgz#9294e6ab359ec93590ab097b0f2017de7c78ab4d"
integrity sha512-C5sT3/XIztxxz/zwDjPRHyzj/NJFOnakAanXuyfLDwhwupKPd76/PPHHyJx6Po6NI6PomgVp/zi6GRB8PfrOTA==
esbuild-freebsd-arm64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.11.tgz#ae3e0b09173350b66cf8321583c9a1c1fcb8bb55"
integrity sha512-y3Llu4wbs0bk4cwjsdAtVOesXb6JkdfZDLKMt+v1U3tOEPBdSu6w8796VTksJgPfqvpX22JmPLClls0h5p+L9w==
esbuild-linux-32@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.11.tgz#ddadbc7038aa5a6b1675bb1503cf79a0cbf1229a"
integrity sha512-Cg3nVsxArjyLke9EuwictFF3Sva+UlDTwHIuIyx8qpxRYAOUTmxr2LzYrhHyTcGOleLGXUXYsnUVwKqnKAgkcg==
esbuild-linux-64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.11.tgz#d698e3ce3a231ddfeec6b5df8c546ae8883fcd88"
integrity sha512-oeR6dIrrojr8DKVrxtH3xl4eencmjsgI6kPkDCRIIFwv4p+K7ySviM85K66BN01oLjzthpUMvBVfWSJkBLeRbg==
esbuild-linux-arm64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.11.tgz#85faea9fa99ad355b5e3b283197a4dfd0a110fe7"
integrity sha512-+e6ZCgTFQYZlmg2OqLkg1jHLYtkNDksxWDBWNtI4XG4WxuOCUErLqfEt9qWjvzK3XBcCzHImrajkUjO+rRkbMg==
esbuild-linux-arm@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.11.tgz#74cbcf0b8a22c8401bcbcd6ebd4cbf2baca8b7b4"
integrity sha512-vcwskfD9g0tojux/ZaTJptJQU3a7YgTYsptK1y6LQ/rJmw7U5QJvboNawqM98Ca3ToYEucfCRGbl66OTNtp6KQ==
esbuild-linux-mips64le@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.11.tgz#490429211a3233f5cbbd8575b7758b897e42979a"
integrity sha512-Rrs99L+p54vepmXIb87xTG6ukrQv+CzrM8eoeR+r/OFL2Rg8RlyEtCeshXJ2+Q66MXZOgPJaokXJZb9snq28bw==
esbuild-linux-ppc64le@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.11.tgz#fc79d60710213b5b98345f5b138d48245616827a"
integrity sha512-JyzziGAI0D30Vyzt0HDihp4s1IUtJ3ssV2zx9O/c+U/dhUHVP2TmlYjzCfCr2Q6mwXTeloDcLS4qkyvJtYptdQ==
esbuild-linux-s390x@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.11.tgz#ca4b93556bbba6cc95b0644f2ee93c982165ba07"
integrity sha512-DoThrkzunZ1nfRGoDN6REwmo8ZZWHd2ztniPVIR5RMw/Il9wiWEYBahb8jnMzQaSOxBsGp0PbyJeVLTUatnlcw==
esbuild-netbsd-64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.11.tgz#edb340bc6653c88804cac2253e21b74258fce165"
integrity sha512-12luoRQz+6eihKYh1zjrw0CBa2aw3twIiHV/FAfjh2NEBDgJQOY4WCEUEN+Rgon7xmLh4XUxCQjnwrvf8zhACw==
esbuild-openbsd-64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.11.tgz#caeff5f946f79a60ce7bcf88871ca4c71d3476e8"
integrity sha512-l18TZDjmvwW6cDeR4fmizNoxndyDHamGOOAenwI4SOJbzlJmwfr0jUgjbaXCUuYVOA964siw+Ix+A+bhALWg8Q==
esbuild-sunos-64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.11.tgz#90ce7e1749c2958a53509b4bae7b8f7d98f276d6"
integrity sha512-bmYzDtwASBB8c+0/HVOAiE9diR7+8zLm/i3kEojUH2z0aIs6x/S4KiTuT5/0VKJ4zk69kXel1cNWlHBMkmavQg==
esbuild-windows-32@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.11.tgz#d067f4ce15b29efba6336e6a23597120fafe49ec"
integrity sha512-J1Ys5hMid8QgdY00OBvIolXgCQn1ARhYtxPnG6ESWNTty3ashtc4+As5nTrsErnv8ZGUcWZe4WzTP/DmEVX1UQ==
esbuild-windows-64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.11.tgz#13e86dd37a6cd61a5276fa2d271342d0f74da864"
integrity sha512-h9FmMskMuGeN/9G9+LlHPAoiQk9jlKDUn9yA0MpiGzwLa82E7r1b1u+h2a+InprbSnSLxDq/7p5YGtYVO85Mlg==
esbuild-windows-arm64@0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.11.tgz#e8edfdf1d712085e6dc3fba18a0c225aaae32b75"
integrity sha512-dZp7Krv13KpwKklt9/1vBFBMqxEQIO6ri7Azf8C+ob4zOegpJmha2XY9VVWP/OyQ0OWk6cEeIzMJwInRZrzBUQ==
esbuild@^0.14.11:
version "0.14.11"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.11.tgz#ac4acb78907874832afb704c3afe58ad37715c27"
integrity sha512-xZvPtVj6yecnDeFb3KjjCM6i7B5TCAQZT77kkW/CpXTMnd6VLnRPKrUB1XHI1pSq6a4Zcy3BGueQ8VljqjDGCg==
optionalDependencies:
esbuild-android-arm64 "0.14.11"
esbuild-darwin-64 "0.14.11"
esbuild-darwin-arm64 "0.14.11"
esbuild-freebsd-64 "0.14.11"
esbuild-freebsd-arm64 "0.14.11"
esbuild-linux-32 "0.14.11"
esbuild-linux-64 "0.14.11"
esbuild-linux-arm "0.14.11"
esbuild-linux-arm64 "0.14.11"
esbuild-linux-mips64le "0.14.11"
esbuild-linux-ppc64le "0.14.11"
esbuild-linux-s390x "0.14.11"
esbuild-netbsd-64 "0.14.11"
esbuild-openbsd-64 "0.14.11"
esbuild-sunos-64 "0.14.11"
esbuild-windows-32 "0.14.11"
esbuild-windows-64 "0.14.11"
esbuild-windows-arm64 "0.14.11"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
semver@^7.3.2:
version "7.3.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
typescript@^4.3.5:
version "4.3.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
semver@^7.3.5:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
dependencies:
lru-cache "^6.0.0"
typescript@^4.7.2:
version "4.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4"
integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==
vscode-jsonrpc@6.0.0:
version "6.0.0"
@ -56,3 +174,8 @@ which@^2.0.2:
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==