From d8c54562799c93f875f860a3afed94fabfb16e79 Mon Sep 17 00:00:00 2001 From: Heyward Fann Date: Mon, 15 Jul 2024 18:39:00 +0800 Subject: [PATCH] feat(lsp): inlineCompletion Closes #5071 --- package-lock.json | 40 ++++---- package.json | 6 +- src/__tests__/client/features.test.ts | 25 ++++- src/__tests__/client/server/testServer.js | 101 +++++++++++--------- src/language-client/client.ts | 9 +- src/language-client/features.ts | 15 +-- src/language-client/inlineCompletion.ts | 81 ++++++++++++++++ src/languages.ts | 13 ++- src/provider/index.ts | 70 ++++++-------- src/provider/inlineCompletionItemManager.ts | 36 +++++++ tsconfig.json | 4 +- 11 files changed, 271 insertions(+), 129 deletions(-) create mode 100644 src/language-client/inlineCompletion.ts create mode 100644 src/provider/inlineCompletionItemManager.ts diff --git a/package-lock.json b/package-lock.json index 2a3b4f77633..99a557fe852 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,9 +32,9 @@ "unidecode": "^1.0.1", "unzip-stream": "^0.3.4", "uuid": "^9.0.1", - "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-protocol": "^3.17.6-next.7", "vscode-languageserver-textdocument": "^1.0.11", - "vscode-languageserver-types": "^3.17.5", + "vscode-languageserver-types": "^3.17.6-next.4", "vscode-uri": "^3.0.8", "which": "^4.0.0" }, @@ -58,7 +58,7 @@ "eslint-plugin-jsdoc": "^48.5.0", "jest": "29.7.0", "typescript": "^5.5.3", - "vscode-languageserver": "^9.0.1" + "vscode-languageserver": "^10.0.0-next.7" }, "engines": { "node": ">=16.18.0" @@ -6622,32 +6622,35 @@ } }, "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "version": "9.0.0-next.5", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.5.tgz", + "integrity": "sha512-Sl/8RAJtfF/2x/TPBVRuhzRAcqYR/QDjEjNqMcoKFfqsxfVUPzikupRDQYB77Gkbt1RrW43sSuZ5uLtNAcikQQ==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", - "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "version": "10.0.0-next.7", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.7.tgz", + "integrity": "sha512-z7x3alNgWful9KLz/IXBVqv2HwHCE1IQeuUEvmSInJTGH9RxFztxk9WDpY1PRQzGO78NG0qR/yuS/VscIpx39Q==", "dev": true, + "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.5" + "vscode-languageserver-protocol": "3.17.6-next.7" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "version": "3.17.6-next.7", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.7.tgz", + "integrity": "sha512-lx8BQ94x6jl6ZzZOrsN4RxwA1Xh0Ovpus+pWA9TXYF5A9EAAL+pTgYFRra3byucdjw3GDC6zbj7wviyfkMgYuA==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" + "vscode-jsonrpc": "9.0.0-next.5", + "vscode-languageserver-types": "3.17.6-next.4" } }, "node_modules/vscode-languageserver-textdocument": { @@ -6656,9 +6659,10 @@ "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" }, "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + "version": "3.17.6-next.4", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz", + "integrity": "sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q==", + "license": "MIT" }, "node_modules/vscode-uri": { "version": "3.0.8", diff --git a/package.json b/package.json index e31e527cb4f..5bf3a83f7e5 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "eslint-plugin-jsdoc": "^48.5.0", "jest": "29.7.0", "typescript": "^5.5.3", - "vscode-languageserver": "^9.0.1" + "vscode-languageserver": "^10.0.0-next.7" }, "dependencies": { "@chemzqm/neovim": "^6.1.2", @@ -108,9 +108,9 @@ "unidecode": "^1.0.1", "unzip-stream": "^0.3.4", "uuid": "^9.0.1", - "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-protocol": "^3.17.6-next.7", "vscode-languageserver-textdocument": "^1.0.11", - "vscode-languageserver-types": "^3.17.5", + "vscode-languageserver-types": "^3.17.6-next.4", "vscode-uri": "^3.0.8", "which": "^4.0.0" } diff --git a/src/__tests__/client/features.test.ts b/src/__tests__/client/features.test.ts index ebced7de2aa..744b2fb6add 100644 --- a/src/__tests__/client/features.test.ts +++ b/src/__tests__/client/features.test.ts @@ -1,6 +1,6 @@ import * as assert from 'assert' import path from 'path' -import { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, CallHierarchyPrepareRequest, CancellationToken, CancellationTokenSource, CodeAction, CodeActionRequest, CodeLensRequest, Color, ColorInformation, ColorPresentation, CompletionItem, CompletionRequest, CompletionTriggerKind, ConfigurationRequest, DeclarationRequest, DefinitionRequest, DidChangeConfigurationNotification, DidChangeTextDocumentNotification, DidChangeWatchedFilesNotification, DidCloseTextDocumentNotification, DidCreateFilesNotification, DidDeleteFilesNotification, DidOpenTextDocumentNotification, DidRenameFilesNotification, DidSaveTextDocumentNotification, Disposable, DocumentColorRequest, DocumentDiagnosticReport, DocumentDiagnosticReportKind, DocumentDiagnosticRequest, DocumentFormattingRequest, DocumentHighlight, DocumentHighlightKind, DocumentHighlightRequest, DocumentLink, DocumentLinkRequest, DocumentOnTypeFormattingRequest, DocumentRangeFormattingRequest, DocumentSelector, DocumentSymbolRequest, FoldingRange, FoldingRangeRequest, FullDocumentDiagnosticReport, Hover, HoverRequest, ImplementationRequest, InlayHintKind, InlayHintLabelPart, InlayHintRequest, InlineValueEvaluatableExpression, InlineValueRequest, InlineValueText, InlineValueVariableLookup, LinkedEditingRangeRequest, Location, NotificationType0, ParameterInformation, Position, ProgressToken, ProtocolRequestType, Range, ReferencesRequest, RenameRequest, SelectionRange, SelectionRangeRequest, SemanticTokensRegistrationType, SignatureHelpRequest, SignatureHelpTriggerKind, SignatureInformation, TextDocumentEdit, TextDocumentSyncKind, TextEdit, TypeDefinitionRequest, TypeHierarchyPrepareRequest, WillCreateFilesRequest, WillDeleteFilesRequest, WillRenameFilesRequest, WillSaveTextDocumentNotification, WillSaveTextDocumentWaitUntilRequest, WorkDoneProgressBegin, WorkDoneProgressCreateRequest, WorkDoneProgressEnd, WorkDoneProgressReport, WorkspaceEdit, WorkspaceSymbolRequest } from 'vscode-languageserver-protocol' +import { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, CallHierarchyPrepareRequest, CancellationToken, CancellationTokenSource, CodeAction, CodeActionRequest, CodeLensRequest, Color, ColorInformation, ColorPresentation, CompletionItem, CompletionRequest, CompletionTriggerKind, ConfigurationRequest, DeclarationRequest, DefinitionRequest, DidChangeConfigurationNotification, DidChangeTextDocumentNotification, DidChangeWatchedFilesNotification, DidCloseTextDocumentNotification, DidCreateFilesNotification, DidDeleteFilesNotification, DidOpenTextDocumentNotification, DidRenameFilesNotification, DidSaveTextDocumentNotification, Disposable, DocumentColorRequest, DocumentDiagnosticReport, DocumentDiagnosticReportKind, DocumentDiagnosticRequest, DocumentFormattingRequest, DocumentHighlight, DocumentHighlightKind, DocumentHighlightRequest, DocumentLink, DocumentLinkRequest, DocumentOnTypeFormattingRequest, DocumentRangeFormattingRequest, DocumentSelector, DocumentSymbolRequest, FoldingRange, FoldingRangeRequest, FullDocumentDiagnosticReport, Hover, HoverRequest, ImplementationRequest, InlayHintKind, InlayHintLabelPart, InlayHintRequest, InlineCompletionItem, InlineCompletionRequest, InlineValueEvaluatableExpression, InlineValueRequest, InlineValueText, InlineValueVariableLookup, LinkedEditingRangeRequest, Location, NotificationType0, ParameterInformation, Position, ProgressToken, ProtocolRequestType, Range, ReferencesRequest, RenameRequest, SelectionRange, SelectionRangeRequest, SemanticTokensRegistrationType, SignatureHelpRequest, SignatureHelpTriggerKind, SignatureInformation, TextDocumentEdit, TextDocumentSyncKind, TextEdit, TypeDefinitionRequest, TypeHierarchyPrepareRequest, WillCreateFilesRequest, WillDeleteFilesRequest, WillRenameFilesRequest, WillSaveTextDocumentNotification, WillSaveTextDocumentWaitUntilRequest, WorkDoneProgressBegin, WorkDoneProgressCreateRequest, WorkDoneProgressEnd, WorkDoneProgressReport, WorkspaceEdit, WorkspaceSymbolRequest } from 'vscode-languageserver-protocol' import { TextDocument } from 'vscode-languageserver-textdocument' import { URI } from 'vscode-uri' import commands from '../../commands' @@ -154,6 +154,7 @@ describe('Client integration', () => { documentSelector: [{ language: '*' }] }, selectionRangeProvider: true, + inlineCompletionProvider: true, inlineValueProvider: {}, inlayHintProvider: { resolveProvider: true @@ -270,6 +271,7 @@ describe('Client integration', () => { testFeature(SemanticTokensRegistrationType.method, 'document') testFeature(LinkedEditingRangeRequest.method, 'document') testFeature(TypeHierarchyPrepareRequest.method, 'document') + testFeature(InlineCompletionRequest.method, 'document') testFeature(InlineValueRequest.method, 'document') testFeature(InlayHintRequest.method, 'document') testFeature(WorkspaceSymbolRequest.method, 'workspace') @@ -1351,6 +1353,27 @@ describe('Client integration', () => { }, true) }) + test('Inline Completions', async () => { + const provider = client.getFeature(InlineCompletionRequest.method)?.getProvider(document) + isDefined(provider) + const results = (await provider.provideInlineCompletionItems(document, position, { triggerKind: 1, selectedCompletionInfo: { range, text: 'text' } }, tokenSource.token)) as InlineCompletionItem[] + + isArray(results, InlineCompletionItem, 1) + + rangeEqual(results[0].range!, 1, 2, 3, 4) + assert.strictEqual(results[0].filterText!, 'te') + assert.strictEqual(results[0].insertText, 'text inline') + + let middlewareCalled = false + middleware.provideInlineCompletionItems = (d, r, c, t, n) => { + middlewareCalled = true + return n(d, r, c, t) + } + await provider.provideInlineCompletionItems(document, position, { triggerKind: 1, selectedCompletionInfo: undefined }, tokenSource.token) + middleware.provideInlineCompletionItems = undefined + assert.strictEqual(middlewareCalled, true) + }) + test('Workspace symbols', async () => { const providers = client.getFeature(WorkspaceSymbolRequest.method).getProviders() isDefined(providers) diff --git a/src/__tests__/client/server/testServer.js b/src/__tests__/client/server/testServer.js index 3927c3b3f78..b1ebfdc8328 100644 --- a/src/__tests__/client/server/testServer.js +++ b/src/__tests__/client/server/testServer.js @@ -1,16 +1,17 @@ const assert = require('assert') -const {URI} = require('vscode-uri') +const { URI } = require('vscode-uri') const { createConnection, CompletionItemKind, ResourceOperationKind, FailureHandlingKind, DiagnosticTag, CompletionItemTag, TextDocumentSyncKind, MarkupKind, SignatureInformation, ParameterInformation, Location, Range, DocumentHighlight, DocumentHighlightKind, CodeAction, Command, TextEdit, Position, DocumentLink, ColorInformation, Color, ColorPresentation, FoldingRange, SelectionRange, SymbolKind, ProtocolRequestType, WorkDoneProgress, - SignatureHelpRequest, SemanticTokensRefreshRequest, WorkDoneProgressCreateRequest, CodeLensRefreshRequest, InlayHintRefreshRequest, WorkspaceSymbolRequest, DidChangeConfigurationNotification} = require('vscode-languageserver') + SignatureHelpRequest, SemanticTokensRefreshRequest, WorkDoneProgressCreateRequest, CodeLensRefreshRequest, InlayHintRefreshRequest, WorkspaceSymbolRequest, DidChangeConfigurationNotification } = require('vscode-languageserver') const { DidCreateFilesNotification, DidRenameFilesNotification, DidDeleteFilesNotification, + InlineCompletionItem, WillCreateFilesRequest, WillRenameFilesRequest, WillDeleteFilesRequest, InlayHint, InlayHintLabelPart, InlayHintKind, DocumentDiagnosticReportKind, Diagnostic, DiagnosticSeverity, InlineValueText, InlineValueVariableLookup, InlineValueEvaluatableExpression } = require('vscode-languageserver-protocol') @@ -64,7 +65,7 @@ connection.onInitialize(params => { triggerCharacters: [','], retriggerCharacters: [';'] }, - completionProvider: {resolveProvider: true, triggerCharacters: ['"', ':']}, + completionProvider: { resolveProvider: true, triggerCharacters: ['"', ':'] }, referencesProvider: true, documentHighlightProvider: true, codeActionProvider: { @@ -88,16 +89,17 @@ connection.onInitialize(params => { declarationProvider: true, foldingRangeProvider: true, implementationProvider: { - documentSelector: [{language: '*'}] + documentSelector: [{ language: '*' }] }, selectionRangeProvider: true, inlineValueProvider: {}, + inlineCompletionProvider: true, inlayHintProvider: { resolveProvider: true }, typeDefinitionProvider: { id: '82671a9a-2a69-4e9f-a8d7-e1034eaa0d2e', - documentSelector: [{language: '*'}] + documentSelector: [{ language: '*' }] }, callHierarchyProvider: true, semanticTokensProvider: { @@ -114,28 +116,28 @@ connection.onInitialize(params => { fileOperations: { // Static reg is folders + .txt files with operation kind in the path didCreate: { - filters: [{scheme: 'file', pattern: {glob: '**/created-static/**{/,/*.txt}'}}] + filters: [{ scheme: 'file', pattern: { glob: '**/created-static/**{/,/*.txt}' } }] }, didRename: { filters: [ - {scheme: 'file', pattern: {glob: '**/renamed-static/**/', matches: 'folder'}}, - {scheme: 'file', pattern: {glob: '**/renamed-static/**/*.txt', matches: 'file'}} + { scheme: 'file', pattern: { glob: '**/renamed-static/**/', matches: 'folder' } }, + { scheme: 'file', pattern: { glob: '**/renamed-static/**/*.txt', matches: 'file' } } ] }, didDelete: { - filters: [{scheme: 'file', pattern: {glob: '**/deleted-static/**{/,/*.txt}'}}] + filters: [{ scheme: 'file', pattern: { glob: '**/deleted-static/**{/,/*.txt}' } }] }, willCreate: { - filters: [{scheme: 'file', pattern: {glob: '**/created-static/**{/,/*.txt}'}}] + filters: [{ scheme: 'file', pattern: { glob: '**/created-static/**{/,/*.txt}' } }] }, willRename: { filters: [ - {scheme: 'file', pattern: {glob: '**/renamed-static/**/', matches: 'folder'}}, - {scheme: 'file', pattern: {glob: '**/renamed-static/**/*.txt', matches: 'file'}} + { scheme: 'file', pattern: { glob: '**/renamed-static/**/', matches: 'folder' } }, + { scheme: 'file', pattern: { glob: '**/renamed-static/**/*.txt', matches: 'file' } } ] }, willDelete: { - filters: [{scheme: 'file', pattern: {glob: '**/deleted-static/**{/,/*.txt}'}}] + filters: [{ scheme: 'file', pattern: { glob: '**/deleted-static/**{/,/*.txt}' } }] }, }, }, @@ -151,39 +153,39 @@ connection.onInitialize(params => { }, notebookDocumentSync: { notebookSelector: [{ - notebook: {notebookType: 'jupyter-notebook'}, - cells: [{language: 'python'}] + notebook: { notebookType: 'jupyter-notebook' }, + cells: [{ language: 'python' }] }] } } - return {capabilities, customResults: {hello: 'world'}} + return { capabilities, customResults: { hello: 'world' } } }) connection.onInitialized(() => { // Dynamic reg is folders + .js files with operation kind in the path connection.client.register(DidCreateFilesNotification.type, { - filters: [{scheme: 'file', pattern: {glob: '**/created-dynamic/**{/,/*.js}'}}] + filters: [{ scheme: 'file', pattern: { glob: '**/created-dynamic/**{/,/*.js}' } }] }) connection.client.register(DidRenameFilesNotification.type, { filters: [ - {scheme: 'file', pattern: {glob: '**/renamed-dynamic/**/', matches: 'folder'}}, - {scheme: 'file', pattern: {glob: '**/renamed-dynamic/**/*.js', matches: 'file'}} + { scheme: 'file', pattern: { glob: '**/renamed-dynamic/**/', matches: 'folder' } }, + { scheme: 'file', pattern: { glob: '**/renamed-dynamic/**/*.js', matches: 'file' } } ] }) connection.client.register(DidDeleteFilesNotification.type, { - filters: [{scheme: 'file', pattern: {glob: '**/deleted-dynamic/**{/,/*.js}'}}] + filters: [{ scheme: 'file', pattern: { glob: '**/deleted-dynamic/**{/,/*.js}' } }] }) connection.client.register(WillCreateFilesRequest.type, { - filters: [{scheme: 'file', pattern: {glob: '**/created-dynamic/**{/,/*.js}'}}] + filters: [{ scheme: 'file', pattern: { glob: '**/created-dynamic/**{/,/*.js}' } }] }) connection.client.register(WillRenameFilesRequest.type, { filters: [ - {scheme: 'file', pattern: {glob: '**/renamed-dynamic/**/', matches: 'folder'}}, - {scheme: 'file', pattern: {glob: '**/renamed-dynamic/**/*.js', matches: 'file'}} + { scheme: 'file', pattern: { glob: '**/renamed-dynamic/**/', matches: 'folder' } }, + { scheme: 'file', pattern: { glob: '**/renamed-dynamic/**/*.js', matches: 'file' } } ] }) connection.client.register(WillDeleteFilesRequest.type, { - filters: [{scheme: 'file', pattern: {glob: '**/deleted-dynamic/**{/,/*.js}'}}] + filters: [{ scheme: 'file', pattern: { glob: '**/deleted-dynamic/**{/,/*.js}' } }] }) connection.client.register(SignatureHelpRequest.type, { triggerCharacters: [':'], @@ -225,7 +227,7 @@ connection.onNotification('unregister', () => { }) connection.onCodeLens(params => { - return [{range: Range.create(0, 0, 0, 3)}, {range: Range.create(1, 0, 1, 3)}] + return [{ range: Range.create(0, 0, 0, 3) }, { range: Range.create(1, 0, 1, 3) }] }) connection.onNotification('fireCodeLensRefresh', () => { @@ -241,19 +243,19 @@ connection.onNotification('fireInlayHintsRefresh', () => { }) connection.onCodeLensResolve(codelens => { - return {range: codelens.range, command: {title: 'format', command: 'editor.action.format'}} + return { range: codelens.range, command: { title: 'format', command: 'editor.action.format' } } }) connection.onDeclaration(params => { assert.equal(params.position.line, 1) assert.equal(params.position.character, 1) - return {uri: params.textDocument.uri, range: {start: {line: 1, character: 1}, end: {line: 1, character: 2}}} + return { uri: params.textDocument.uri, range: { start: { line: 1, character: 1 }, end: { line: 1, character: 2 } } } }) connection.onDefinition(params => { assert.equal(params.position.line, 1) assert.equal(params.position.character, 1) - return {uri: params.textDocument.uri, range: {start: {line: 0, character: 0}, end: {line: 0, character: 1}}} + return { uri: params.textDocument.uri, range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } } } }) connection.onHover(_params => { @@ -267,7 +269,7 @@ connection.onHover(_params => { connection.onCompletion(_params => { return [ - {label: 'item', insertText: 'text'} + { label: 'item', insertText: 'text' } ] }) @@ -309,7 +311,7 @@ connection.onCodeAction(params => { connection.onExecuteCommand(params => { if (params.command == 'test_command') { - return {success: true} + return { success: true } } }) @@ -341,7 +343,7 @@ connection.onPrepareRename(_params => { }) connection.onRenameRequest(_params => { - return {documentChanges: []} + return { documentChanges: [] } }) connection.onDocumentLinks(_params => { @@ -376,7 +378,7 @@ connection.onFoldingRanges(_params => { connection.onImplementation(params => { assert.equal(params.position.line, 1) assert.equal(params.position.character, 1) - return {uri: params.textDocument.uri, range: {start: {line: 2, character: 2}, end: {line: 3, character: 3}}} + return { uri: params.textDocument.uri, range: { start: { line: 2, character: 2 }, end: { line: 3, character: 3 } } } }) connection.onSelectionRanges(_params => { @@ -386,9 +388,9 @@ connection.onSelectionRanges(_params => { }) let lastFileOperationRequest -connection.workspace.onDidCreateFiles(params => {lastFileOperationRequest = {type: 'create', params}}) -connection.workspace.onDidRenameFiles(params => {lastFileOperationRequest = {type: 'rename', params}}) -connection.workspace.onDidDeleteFiles(params => {lastFileOperationRequest = {type: 'delete', params}}) +connection.workspace.onDidCreateFiles(params => { lastFileOperationRequest = { type: 'create', params } }) +connection.workspace.onDidRenameFiles(params => { lastFileOperationRequest = { type: 'rename', params } }) +connection.workspace.onDidDeleteFiles(params => { lastFileOperationRequest = { type: 'delete', params } }) connection.onRequest( new ProtocolRequestType('testing/lastFileOperationRequest'), @@ -401,7 +403,7 @@ connection.workspace.onWillCreateFiles(params => { const createdFilenames = params.files.map(f => `${f.uri}`).join('\n') return { documentChanges: [{ - textDocument: {uri: '/dummy-edit', version: null}, + textDocument: { uri: '/dummy-edit', version: null }, edits: [ TextEdit.insert(Position.create(0, 0), `WILL CREATE:\n${createdFilenames}`), ] @@ -413,7 +415,7 @@ connection.workspace.onWillRenameFiles(params => { const renamedFilenames = params.files.map(f => `${f.oldUri} -> ${f.newUri}`).join('\n') return { documentChanges: [{ - textDocument: {uri: '/dummy-edit', version: null}, + textDocument: { uri: '/dummy-edit', version: null }, edits: [ TextEdit.insert(Position.create(0, 0), `WILL RENAME:\n${renamedFilenames}`), ] @@ -425,7 +427,7 @@ connection.workspace.onWillDeleteFiles(params => { const deletedFilenames = params.files.map(f => `${f.uri}`).join('\n') return { documentChanges: [{ - textDocument: {uri: '/dummy-edit', version: null}, + textDocument: { uri: '/dummy-edit', version: null }, edits: [ TextEdit.insert(Position.create(0, 0), `WILL DELETE:\n${deletedFilenames}`), ] @@ -436,7 +438,7 @@ connection.workspace.onWillDeleteFiles(params => { connection.onTypeDefinition(params => { assert.equal(params.position.line, 1) assert.equal(params.position.character, 1) - return {uri: params.textDocument.uri, range: {start: {line: 2, character: 2}, end: {line: 3, character: 3}}} + return { uri: params.textDocument.uri, range: { start: { line: 2, character: 2 }, end: { line: 3, character: 3 } } } }) connection.languages.callHierarchy.onPrepare(params => { @@ -524,8 +526,8 @@ connection.languages.typeHierarchy.onPrepare(params => { selectionRange: Range.create(2, 2, 2, 2), uri: params.textDocument.uri } - typeHierarchySample.superTypes = [{...currentItem, name: 'classA', uri: 'uri-for-A'}] - typeHierarchySample.subTypes = [{...currentItem, name: 'classC', uri: 'uri-for-C'}] + typeHierarchySample.superTypes = [{ ...currentItem, name: 'classA', uri: 'uri-for-A' }] + typeHierarchySample.subTypes = [{ ...currentItem, name: 'classC', uri: 'uri-for-C' }] return [currentItem] }) @@ -536,6 +538,11 @@ connection.languages.typeHierarchy.onSupertypes(_params => { connection.languages.typeHierarchy.onSubtypes(_params => { return typeHierarchySample.subTypes }) +connection.languages.inlineCompletion.on(_params => { + return [ + InlineCompletionItem.create('text inline', 'te', Range.create(1, 2, 3, 4)) + ] +}) connection.languages.inlineValue.on(_params => { return [ @@ -572,10 +579,10 @@ connection.onRequest( new ProtocolRequestType('testing/sendSampleProgress'), async (_, __) => { const progressToken = 'TEST-PROGRESS-TOKEN' - await connection.sendRequest(WorkDoneProgressCreateRequest.type, {token: progressToken}) - void connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'begin', title: 'Test Progress'}) - void connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'report', percentage: 50, message: 'Halfway!'}) - void connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'end', message: 'Completed!'}) + await connection.sendRequest(WorkDoneProgressCreateRequest.type, { token: progressToken }) + void connection.sendProgress(WorkDoneProgress.type, progressToken, { kind: 'begin', title: 'Test Progress' }) + void connection.sendProgress(WorkDoneProgress.type, progressToken, { kind: 'report', percentage: 50, message: 'Halfway!' }) + void connection.sendProgress(WorkDoneProgress.type, progressToken, { kind: 'end', message: 'Completed!' }) }, ) @@ -583,13 +590,13 @@ connection.onRequest( new ProtocolRequestType('testing/beginOnlyProgress'), async (_, __) => { const progressToken = 'TEST-PROGRESS-BEGIN' - await connection.sendRequest(WorkDoneProgressCreateRequest.type, {token: progressToken}) + await connection.sendRequest(WorkDoneProgressCreateRequest.type, { token: progressToken }) }, ) connection.onWorkspaceSymbol(() => { return [ - {name: 'name', kind: SymbolKind.Array, location: {uri: 'file:///abc.txt'}} + { name: 'name', kind: SymbolKind.Array, location: { uri: 'file:///abc.txt' } } ] }) diff --git a/src/language-client/client.ts b/src/language-client/client.ts index 1af5fee67af..1b00b4e5e90 100644 --- a/src/language-client/client.ts +++ b/src/language-client/client.ts @@ -1,5 +1,5 @@ 'use strict' -import type { ApplyWorkspaceEditParams, ApplyWorkspaceEditResult, CallHierarchyPrepareRequest, CancellationStrategy, CancellationToken, ClientCapabilities, CodeActionRequest, CodeLensRequest, CompletionRequest, ConfigurationRequest, ConnectionStrategy, DeclarationRequest, DefinitionRequest, DidChangeConfigurationNotification, DidChangeConfigurationRegistrationOptions, DidChangeTextDocumentNotification, DidChangeWatchedFilesNotification, DidChangeWatchedFilesRegistrationOptions, DidChangeWorkspaceFoldersNotification, DidCloseTextDocumentNotification, DidCreateFilesNotification, DidDeleteFilesNotification, DidOpenTextDocumentNotification, DidRenameFilesNotification, DidSaveTextDocumentNotification, Disposable, DocumentColorRequest, DocumentDiagnosticRequest, DocumentFormattingRequest, DocumentHighlightRequest, DocumentLinkRequest, DocumentOnTypeFormattingRequest, DocumentRangeFormattingRequest, DocumentSelector, DocumentSymbolRequest, ExecuteCommandRegistrationOptions, ExecuteCommandRequest, FileOperationRegistrationOptions, FoldingRangeRequest, GenericNotificationHandler, GenericRequestHandler, HoverRequest, ImplementationRequest, InitializeParams, InitializeResult, InlineValueRequest, LinkedEditingRangeRequest, Message, MessageActionItem, MessageSignature, NotificationHandler, NotificationHandler0, NotificationType, NotificationType0, ProgressToken, ProgressType, ProtocolNotificationType, ProtocolNotificationType0, ProtocolRequestType, ProtocolRequestType0, PublishDiagnosticsParams, ReferencesRequest, RegistrationParams, RenameRequest, RequestHandler, RequestHandler0, RequestType, RequestType0, SelectionRangeRequest, SemanticTokensRegistrationType, ServerCapabilities, ShowDocumentParams, ShowDocumentResult, ShowMessageRequestParams, SignatureHelpRequest, TextDocumentRegistrationOptions, TextDocumentSyncOptions, TextEdit, TraceOptions, Tracer, TypeDefinitionRequest, TypeHierarchyPrepareRequest, UnregistrationParams, WillCreateFilesRequest, WillDeleteFilesRequest, WillRenameFilesRequest, WillSaveTextDocumentNotification, WillSaveTextDocumentWaitUntilRequest, WorkDoneProgressBegin, WorkDoneProgressCreateRequest, WorkDoneProgressEnd, WorkDoneProgressReport, WorkspaceEdit, WorkspaceSymbolRequest } from 'vscode-languageserver-protocol' +import type { ApplyWorkspaceEditParams, ApplyWorkspaceEditResult, CallHierarchyPrepareRequest, CancellationStrategy, CancellationToken, ClientCapabilities, CodeActionRequest, CodeLensRequest, CompletionRequest, ConfigurationRequest, ConnectionStrategy, DeclarationRequest, DefinitionRequest, DidChangeConfigurationNotification, DidChangeConfigurationRegistrationOptions, DidChangeTextDocumentNotification, DidChangeWatchedFilesNotification, DidChangeWatchedFilesRegistrationOptions, DidChangeWorkspaceFoldersNotification, DidCloseTextDocumentNotification, DidCreateFilesNotification, DidDeleteFilesNotification, DidOpenTextDocumentNotification, DidRenameFilesNotification, DidSaveTextDocumentNotification, Disposable, DocumentColorRequest, DocumentDiagnosticRequest, DocumentFormattingRequest, DocumentHighlightRequest, DocumentLinkRequest, DocumentOnTypeFormattingRequest, DocumentRangeFormattingRequest, DocumentSelector, DocumentSymbolRequest, ExecuteCommandRegistrationOptions, ExecuteCommandRequest, FileOperationRegistrationOptions, FoldingRangeRequest, GenericNotificationHandler, GenericRequestHandler, HoverRequest, ImplementationRequest, InitializeParams, InitializeResult, InlineCompletionRequest, InlineValueRequest, LinkedEditingRangeRequest, Message, MessageActionItem, MessageSignature, NotificationHandler, NotificationHandler0, NotificationType, NotificationType0, ProgressToken, ProgressType, ProtocolNotificationType, ProtocolNotificationType0, ProtocolRequestType, ProtocolRequestType0, PublishDiagnosticsParams, ReferencesRequest, RegistrationParams, RenameRequest, RequestHandler, RequestHandler0, RequestType, RequestType0, SelectionRangeRequest, SemanticTokensRegistrationType, ServerCapabilities, ShowDocumentParams, ShowDocumentResult, ShowMessageRequestParams, SignatureHelpRequest, TextDocumentRegistrationOptions, TextDocumentSyncOptions, TextEdit, TraceOptions, Tracer, TypeDefinitionRequest, TypeHierarchyPrepareRequest, UnregistrationParams, WillCreateFilesRequest, WillDeleteFilesRequest, WillRenameFilesRequest, WillSaveTextDocumentNotification, WillSaveTextDocumentWaitUntilRequest, WorkDoneProgressBegin, WorkDoneProgressCreateRequest, WorkDoneProgressEnd, WorkDoneProgressReport, WorkspaceEdit, WorkspaceSymbolRequest } from 'vscode-languageserver-protocol' import { TextDocument } from "vscode-languageserver-textdocument" import { Diagnostic, DiagnosticSeverity, DiagnosticTag, MarkupKind, TextDocumentEdit } from 'vscode-languageserver-types' import { URI } from 'vscode-uri' @@ -8,7 +8,7 @@ import DiagnosticCollection from '../diagnostic/collection' import languages from '../languages' import { createLogger } from '../logger' import type { MessageItem } from '../model/notification' -import { CallHierarchyProvider, CodeActionProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentLinkProvider, DocumentRangeFormattingEditProvider, DocumentSymbolProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, LinkedEditingRangeProvider, OnTypeFormattingEditProvider, ProviderResult, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, TypeHierarchyProvider, WorkspaceSymbolProvider } from '../provider' +import { CallHierarchyProvider, CodeActionProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentLinkProvider, DocumentRangeFormattingEditProvider, DocumentSymbolProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlineCompletionItemProvider, LinkedEditingRangeProvider, OnTypeFormattingEditProvider, ProviderResult, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, TypeHierarchyProvider, WorkspaceSymbolProvider } from '../provider' import { OutputChannel, Thenable } from '../types' import { defaultValue } from '../util' import { isFalsyOrEmpty, toArray } from '../util/array' @@ -44,6 +44,7 @@ import { $FormattingOptions, DocumentFormattingFeature, DocumentOnTypeFormatting import { HoverFeature, HoverMiddleware } from './hover' import { ImplementationFeature, ImplementationMiddleware } from './implementation' import { InlayHintsFeature, InlayHintsMiddleware, InlayHintsProviderShape } from './inlayHint' +import { InlineCompletionItemFeature, InlineCompletionMiddleware } from './inlineCompletion' import { InlineValueFeature, InlineValueMiddleware, InlineValueProviderShape } from './inlineValue' import { LinkedEditingFeature, LinkedEditingRangeMiddleware } from './linkedEditingRange' import { ProgressFeature } from './progress' @@ -166,7 +167,7 @@ export type Middleware = _Middleware & TextDocumentSynchronizationMiddleware & S HoverMiddleware & CompletionMiddleware & ExecuteCommandMiddleware & TypeDefinitionMiddleware & ImplementationMiddleware & ColorProviderMiddleware & DeclarationMiddleware & FoldingRangeProviderMiddleware & CallHierarchyMiddleware & SemanticTokensMiddleware & - InlayHintsMiddleware & InlineValueMiddleware & TypeHierarchyMiddleware & + InlayHintsMiddleware & InlineCompletionMiddleware & InlineValueMiddleware & TypeHierarchyMiddleware & WorkspaceSymbolMiddleware & DiagnosticProviderMiddleware & LinkedEditingRangeMiddleware & SelectionRangeProviderMiddleware @@ -1405,6 +1406,7 @@ export abstract class BaseLanguageClient implements FeatureClient & TextDocumentProviderFeature public getFeature(request: typeof LinkedEditingRangeRequest.method): DynamicFeature & TextDocumentProviderFeature public getFeature(request: typeof TypeHierarchyPrepareRequest.method): DynamicFeature & TextDocumentProviderFeature + public getFeature(request: typeof InlineCompletionRequest.method): DynamicFeature & TextDocumentProviderFeature public getFeature(request: typeof InlineValueRequest.method): DynamicFeature & TextDocumentProviderFeature public getFeature(request: typeof InlayHintRequest.method): DynamicFeature & TextDocumentProviderFeature public getFeature(request: typeof WorkspaceSymbolRequest.method): DynamicFeature & WorkspaceProviderFeature @@ -1455,6 +1457,7 @@ export abstract class BaseLanguageClient implements FeatureClient void /** * Called to fill in the client capabilities this feature implements. - * * @param capabilities The client capabilities to fill. */ fillClientCapabilities(capabilities: ClientCapabilities): void @@ -190,7 +188,6 @@ export interface StaticFeature { * A preflight where the server capabilities are shown to all features * before a feature is actually initialized. This allows feature to * capture some state if they are a pre-requisite for other features. - * * @param capabilities the server capabilities * @param documentSelector the document selector pass to the client's constructor. * May be `undefined` if the client was created without a selector. @@ -202,7 +199,6 @@ export interface StaticFeature { * when the client has successfully received the initialize request from * the server and before the client sends the initialized notification * to the server. - * * @param capabilities the server capabilities * @param documentSelector the document selector pass to the client's constructor. * May be `undefined` if the client was created without a selector. @@ -237,14 +233,12 @@ export interface DynamicFeature { /** * Called to fill the initialize params. - * * @params the initialize params. */ fillInitializeParams?: (params: InitializeParams) => void /** * Called to fill in the client capabilities this feature implements. - * * @param capabilities The client capabilities to fill. */ fillClientCapabilities(capabilities: ClientCapabilities): void @@ -253,7 +247,6 @@ export interface DynamicFeature { * A preflight where the server capabilities are shown to all features * before a feature is actually initialized. This allows feature to * capture some state if they are a pre-requisite for other features. - * * @param capabilities the server capabilities * @param documentSelector the document selector pass to the client's constructor. * May be `undefined` if the client was created without a selector. @@ -265,7 +258,6 @@ export interface DynamicFeature { * when the client has successfully received the initialize request from * the server and before the client sends the initialized notification * to the server. - * * @param capabilities the server capabilities. * @param documentSelector the document selector pass to the client's constructor. * May be `undefined` if the client was created without a selector. @@ -284,14 +276,12 @@ export interface DynamicFeature { /** * Is called when the server send a register request for the given message. - * * @param data additional registration data as defined in the protocol. */ register(data: RegistrationData): void /** * Is called when the server wants to unregister a feature. - * * @param id the id used when registering the feature. */ unregister(id: string): void @@ -682,6 +672,7 @@ export interface FeatureClient { getFeature(request: typeof SemanticTokensRegistrationType.method): DynamicFeature & TextDocumentProviderFeature getFeature(request: typeof LinkedEditingRangeRequest.method): DynamicFeature & TextDocumentProviderFeature getFeature(request: typeof TypeHierarchyPrepareRequest.method): DynamicFeature & TextDocumentProviderFeature + getFeature(request: typeof InlineCompletionRequest.method): DynamicFeature & TextDocumentProviderFeature getFeature(request: typeof InlineValueRequest.method): DynamicFeature & TextDocumentProviderFeature getFeature(request: typeof InlayHintRequest.method): DynamicFeature & TextDocumentProviderFeature getFeature(request: typeof WorkspaceSymbolRequest.method): DynamicFeature & WorkspaceProviderFeature diff --git a/src/language-client/inlineCompletion.ts b/src/language-client/inlineCompletion.ts new file mode 100644 index 00000000000..96b006ee0c9 --- /dev/null +++ b/src/language-client/inlineCompletion.ts @@ -0,0 +1,81 @@ +import { + CancellationToken, + ClientCapabilities, + Disposable, + DocumentSelector, + InlineCompletionContext, + InlineCompletionItem, + InlineCompletionList, + InlineCompletionOptions, + InlineCompletionParams, + InlineCompletionRegistrationOptions, + InlineCompletionRequest, + Position, + ServerCapabilities +} from 'vscode-languageserver-protocol' +import { TextDocument } from 'vscode-languageserver-textdocument' +import languages from '../languages' +import { InlineCompletionItemProvider, ProviderResult } from '../provider' +import { + ensure, + FeatureClient, + TextDocumentLanguageFeature +} from './features' +import * as UUID from './utils/uuid' + +export interface ProvideInlineCompletionItemsSignature { + (this: void, document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult +} + +export interface InlineCompletionMiddleware { + provideInlineCompletionItems?: (this: void, document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken, next: ProvideInlineCompletionItemsSignature) => ProviderResult +} + +export interface InlineCompletionProviderShape { + provider: InlineCompletionItemProvider +} + +export class InlineCompletionItemFeature extends TextDocumentLanguageFeature { + + constructor(client: FeatureClient) { + super(client, InlineCompletionRequest.type) + } + + public fillClientCapabilities(capabilities: ClientCapabilities): void { + const inlineCompletion = ensure(ensure(capabilities, 'textDocument')!, 'inlineCompletion')! + inlineCompletion.dynamicRegistration = true + } + + public initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void { + const options = this.getRegistrationOptions(documentSelector, capabilities.inlineCompletionProvider) + if (!options) { + return + } + + this.register({ + id: UUID.generateUuid(), + registerOptions: options + }) + } + + protected registerLanguageProvider(options: InlineCompletionRegistrationOptions): [Disposable, InlineCompletionItemProvider] { + const provider: InlineCompletionItemProvider = { + provideInlineCompletionItems: (document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult => { + const provideInlineCompletionItems: ProvideInlineCompletionItemsSignature = (document, position, context, token) => { + const params: InlineCompletionParams = { + textDocument: { uri: document.uri }, + position, + context + } + return this.sendRequest(InlineCompletionRequest.type, params, token, null) + } + + const middleware = this._client.middleware + return middleware.provideInlineCompletionItems + ? middleware.provideInlineCompletionItems(document, position, context, token, provideInlineCompletionItems) + : provideInlineCompletionItems(document, position, context, token) + } + } + return [languages.registerInlineCompletionItemProvider(options.documentSelector, provider), provider] + } +} diff --git a/src/languages.ts b/src/languages.ts index 614893eea56..f78be461acb 100644 --- a/src/languages.ts +++ b/src/languages.ts @@ -1,11 +1,11 @@ 'use strict' import type { LinkedEditingRanges, SignatureHelpContext } from 'vscode-languageserver-protocol' import { TextDocument } from 'vscode-languageserver-textdocument' -import { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, CodeAction, CodeActionContext, CodeActionKind, CodeLens, ColorInformation, ColorPresentation, DefinitionLink, DocumentHighlight, DocumentLink, DocumentSymbol, FoldingRange, FormattingOptions, Hover, InlineValue, InlineValueContext, Position, Range, SelectionRange, SemanticTokens, SemanticTokensDelta, SemanticTokensLegend, SignatureHelp, SymbolInformation, TextEdit, TypeHierarchyItem, WorkspaceEdit, WorkspaceSymbol } from 'vscode-languageserver-types' +import { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, CodeAction, CodeActionContext, CodeActionKind, CodeLens, ColorInformation, ColorPresentation, DefinitionLink, DocumentHighlight, DocumentLink, DocumentSymbol, FoldingRange, FormattingOptions, Hover, InlineValue, InlineValueContext, Position, Range, SelectionRange, SemanticTokens, SemanticTokensDelta, SemanticTokensLegend, SignatureHelp, TextEdit, TypeHierarchyItem, WorkspaceEdit, WorkspaceSymbol } from 'vscode-languageserver-types' import type { Sources } from './completion/sources' import DiagnosticCollection from './diagnostic/collection' import diagnosticManager from './diagnostic/manager' -import { CallHierarchyProvider, CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentLinkProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSelector, DocumentSemanticTokensProvider, DocumentSymbolProvider, DocumentSymbolProviderMetadata, FoldingContext, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineValuesProvider, LinkedEditingRangeProvider, OnTypeFormattingEditProvider, ReferenceContext, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, TypeHierarchyProvider, WorkspaceSymbolProvider } from './provider' +import { CallHierarchyProvider, CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentLinkProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSelector, DocumentSemanticTokensProvider, DocumentSymbolProvider, DocumentSymbolProviderMetadata, FoldingContext, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionItemProvider, InlineValuesProvider, LinkedEditingRangeProvider, OnTypeFormattingEditProvider, ReferenceContext, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, TypeHierarchyProvider, WorkspaceSymbolProvider } from './provider' import CallHierarchyManager from './provider/callHierarchyManager' import CodeActionManager from './provider/codeActionManager' import CodeLensManager from './provider/codeLensManager' @@ -21,6 +21,7 @@ import FormatRangeManager from './provider/formatRangeManager' import HoverManager from './provider/hoverManager' import ImplementationManager from './provider/implementationManager' import InlayHintManger, { InlayHintWithProvider } from './provider/inlayHintManager' +import InlineCompletionItemManager from './provider/inlineCompletionItemManager' import InlineValueManager from './provider/inlineValueManager' import LinkedEditingRangeManager from './provider/linkedEditingRangeManager' import OnTypeFormatManager from './provider/onTypeFormatManager' @@ -77,6 +78,7 @@ export enum ProviderName { LinkedEditing = 'linkedEditing', InlayHint = 'inlayHint', InlineValue = 'inlineValue', + InlineCompletion = 'inlineCompletion', TypeHierarchy = 'typeHierarchy' } @@ -117,6 +119,7 @@ class Languages { private semanticTokensRangeManager = new SemanticTokensRangeManager() private linkedEditingManager = new LinkedEditingRangeManager() private inlayHintManager = new InlayHintManger() + private inlineCompletionItemManager = new InlineCompletionItemManager() private inlineValueManager = new InlineValueManager() public registerReferenceProvider: (selector: DocumentSelector, provider: ReferenceProvider) => Disposable @@ -158,6 +161,10 @@ class Languages { return sources.createLanguageSource(name, shortcut, selector, provider, triggerCharacters, priority, allCommitCharacters) } + public registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable { + return this.inlineCompletionItemManager.register(selector, provider) + } + public registerCodeActionProvider(selector: DocumentSelector, provider: CodeActionProvider, clientId: string | undefined, codeActionKinds?: CodeActionKind[]): Disposable { return this.codeActionManager.register(selector, provider, clientId, codeActionKinds) } @@ -565,6 +572,8 @@ class Languages { return this.linkedEditingManager.hasProvider(document) case ProviderName.InlayHint: return this.inlayHintManager.hasProvider(document) + case ProviderName.InlineCompletion: + return this.inlineCompletionItemManager.hasProvider(document) case ProviderName.InlineValue: return this.inlineValueManager.hasProvider(document) case ProviderName.TypeHierarchy: diff --git a/src/provider/index.ts b/src/provider/index.ts index 02dff528d71..0b72d0953fc 100644 --- a/src/provider/index.ts +++ b/src/provider/index.ts @@ -1,5 +1,5 @@ 'use strict' -import type { CallHierarchyIncomingCall, DocumentFilter, CallHierarchyItem, CallHierarchyOutgoingCall, CancellationToken, CodeAction, CodeActionContext, CodeActionKind, CodeLens, Color, ColorInformation, ColorPresentation, Command, CompletionContext, CompletionItem, CompletionList, Definition, DefinitionLink, DocumentDiagnosticReport, DocumentHighlight, DocumentLink, DocumentSymbol, Event, FoldingRange, FormattingOptions, Hover, InlayHint, InlineValue, InlineValueContext, LinkedEditingRanges, Location, Position, PreviousResultId, Range, SelectionRange, SemanticTokens, SemanticTokensDelta, SignatureHelp, SignatureHelpContext, SymbolInformation, TextEdit, TypeHierarchyItem, WorkspaceDiagnosticReport, WorkspaceDiagnosticReportPartialResult, WorkspaceEdit, WorkspaceSymbol } from 'vscode-languageserver-protocol' +import type { CallHierarchyIncomingCall, DocumentFilter, CallHierarchyItem, CallHierarchyOutgoingCall, CancellationToken, CodeAction, CodeActionContext, CodeActionKind, CodeLens, Color, ColorInformation, ColorPresentation, Command, CompletionContext, CompletionItem, CompletionList, Definition, DefinitionLink, DocumentDiagnosticReport, DocumentHighlight, DocumentLink, DocumentSymbol, Event, FoldingRange, FormattingOptions, Hover, InlayHint, InlineValue, InlineValueContext, LinkedEditingRanges, Location, Position, PreviousResultId, Range, SelectionRange, SemanticTokens, SemanticTokensDelta, SignatureHelp, SignatureHelpContext, SymbolInformation, TextEdit, TypeHierarchyItem, WorkspaceDiagnosticReport, WorkspaceDiagnosticReportPartialResult, WorkspaceEdit, WorkspaceSymbol, InlineCompletionContext, InlineCompletionItem, InlineCompletionList } from 'vscode-languageserver-protocol' import type { TextDocument } from 'vscode-languageserver-textdocument' import type { URI } from 'vscode-uri' @@ -57,12 +57,10 @@ export type ProviderResult = export interface CompletionItemProvider { /** * Provide completion items for the given position and document. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. * @param context How the completion was triggered. - * * @return An array of completions, a [completion list](#CompletionList), or a thenable that resolves to either. * The lack of a result can be signaled by returning `undefined`, `null`, or an empty array. */ @@ -78,7 +76,6 @@ export interface CompletionItemProvider { * or [details](#CompletionItem.detail). * * The editor will only resolve a completion item once. - * * @param item A completion item currently active in the UI. * @param token A cancellation token. * @return The resolved completion item or a thenable that resolves to of such. It is OK to return the given @@ -90,6 +87,34 @@ export interface CompletionItemProvider { ): ProviderResult } +/** + * The inline completion item provider interface defines the contract between extensions and + * the inline completion feature. + * + * Providers are asked for completions either explicitly by a user gesture or implicitly when typing. + */ +export interface InlineCompletionItemProvider { + + /** + * Provides inline completion items for the given position and document. + * If inline completions are enabled, this method will be called whenever the user stopped typing. + * It will also be called when the user explicitly triggers inline completions or explicitly asks for the next or previous inline completion. + * In that case, all available inline completions should be returned. + * `context.triggerKind` can be used to distinguish between these scenarios. + * @param document The document inline completions are requested for. + * @param position The position inline completions are requested for. + * @param context A context object with additional information. + * @param token A cancellation token. + * @returns An array of completion items or a thenable that resolves to an array of completion items. + */ + provideInlineCompletionItems( + document: TextDocument, + position: Position, + context: InlineCompletionContext, + token: CancellationToken + ): ProviderResult +} + /** * The hover provider interface defines the contract between extensions and * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. @@ -99,7 +124,6 @@ export interface HoverProvider { * Provide a hover for the given position and document. Multiple hovers at the same * position will be merged by the editor. A hover can have a range which defaults * to the word range at the position when omitted. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. @@ -121,7 +145,6 @@ export interface HoverProvider { export interface DefinitionProvider { /** * Provide the definition of the symbol at the given position and document. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. @@ -154,7 +177,6 @@ export interface DeclarationProvider { export interface SignatureHelpProvider { /** * Provide help for the signature at the given position and document. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. @@ -176,7 +198,6 @@ export interface SignatureHelpProvider { export interface TypeDefinitionProvider { /** * Provide the type definition of the symbol at the given position and document. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. @@ -208,7 +229,6 @@ export interface ReferenceContext { export interface ReferenceProvider { /** * Provide a set of project-wide references for the given position and document. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param context @@ -237,7 +257,6 @@ export interface FoldingRangeProvider { /** * Returns a list of folding ranges or null and undefined if the provider * does not want to participate or was cancelled. - * * @param document The document in which the command was invoked. * @param context Additional context information (for future use) * @param token A cancellation token. @@ -266,7 +285,6 @@ export interface DocumentSymbolProvider { /** * Provide symbol information for the given document. - * * @param document The document in which the command was invoked. * @param token A cancellation token. * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be @@ -285,7 +303,6 @@ export interface DocumentSymbolProvider { export interface ImplementationProvider { /** * Provide the implementations of the symbol at the given position and document. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. @@ -314,7 +331,6 @@ export interface WorkspaceSymbolProvider { * and scoring on the results. A good rule of thumb is to match case-insensitive and to simply check that the * characters of *query* appear in their order in a candidate symbol. Don't use prefix, substring, or similar * strict matching. - * * @param query A non-empty query string. * @param token A cancellation token. * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be @@ -330,7 +346,6 @@ export interface WorkspaceSymbolProvider { * is selected in the UI. Providers can implement this method and return incomplete symbols from * [`provideWorkspaceSymbols`](#WorkspaceSymbolProvider.provideWorkspaceSymbols) which often helps to improve * performance. - * * @param symbol The symbol that is to be resolved. Guaranteed to be an instance of an object returned from an * earlier call to `provideWorkspaceSymbols`. * @param token A cancellation token. @@ -351,7 +366,6 @@ export interface RenameProvider { /** * Provide an edit that describes changes that have to be made to one * or many resources to rename a symbol to a different name. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param newName The new name of the symbol. If the given name is not valid, the provider must return a rejected promise. @@ -370,7 +384,6 @@ export interface RenameProvider { * Optional function for resolving and validating a position *before* running rename. The result can * be a range or a range and a placeholder text. The placeholder text should be the identifier of the symbol * which is being renamed - when omitted the text in the returned range is used. - * * @param document The document in which rename will be invoked. * @param position The position at which rename will be invoked. * @param token A cancellation token. @@ -390,7 +403,6 @@ export interface RenameProvider { export interface DocumentFormattingEditProvider { /** * Provide formatting edits for a whole document. - * * @param document The document in which the command was invoked. * @param options Options controlling formatting. * @param token A cancellation token. @@ -415,7 +427,6 @@ export interface DocumentRangeFormattingEditProvider { * The given range is a hint and providers can decide to format a smaller * or larger range. Often this is done by adjusting the start and end * of the range to full syntax nodes. - * * @param document The document in which the command was invoked. * @param range The range which should be formatted. * @param options Options controlling formatting. @@ -440,7 +451,6 @@ export interface DocumentRangeFormattingEditProvider { export interface CodeActionProvider { /** * Provide commands for the given document and range. - * * @param document The document in which the command was invoked. * @param range The selector or range for which the command was invoked. This will always be a selection if * there is a currently active editor. @@ -460,7 +470,6 @@ export interface CodeActionProvider { * Given a code action fill in its [`edit`](#CodeAction.edit)-property. Changes to * all other properties, like title, are ignored. A code action that has an edit * will not be resolved. - * * @param codeAction A code action. * @param token A cancellation token. * @return The resolved code action or a thenable that resolves to such. It is OK to return the given @@ -491,7 +500,6 @@ export interface DocumentHighlightProvider { /** * Provide a set of document highlights, like all occurrences of a variable or * all exit-points of a function. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. @@ -514,7 +522,6 @@ export interface DocumentLinkProvider { /** * Provide links for the given document. Note that the editor ships with a default provider that detects * `http(s)` and `file` links. - * * @param document The document in which the command was invoked. * @param token A cancellation token. * @return An array of [document links](#DocumentLink) or a thenable that resolves to such. The lack of a result @@ -527,7 +534,6 @@ export interface DocumentLinkProvider { * link is selected in the UI. Providers can implement this method and return incomple links * (without target) from the [`provideDocumentLinks`](#DocumentLinkProvider.provideDocumentLinks) method which * often helps to improve performance. - * * @param link The link that is to be resolved. * @param token A cancellation token. */ @@ -549,7 +555,6 @@ export interface CodeLensProvider { * Compute a list of [lenses](#CodeLens). This call should return as fast as possible and if * computing the commands is expensive implementors should only return code lens objects with the * range set and implement [resolve](#CodeLensProvider.resolveCodeLens). - * * @param document The document in which the command was invoked. * @param token A cancellation token. * @return An array of code lenses or a thenable that resolves to such. The lack of a result can be @@ -560,7 +565,6 @@ export interface CodeLensProvider { /** * This function will be called for each visible code lens, usually when scrolling and after * calls to [compute](#CodeLensProvider.provideCodeLenses)-lenses. - * * @param codeLens code lens that must be resolved. * @param token A cancellation token. * @return The given, resolved code lens or thenable that resolves to such. @@ -580,7 +584,6 @@ export interface OnTypeFormattingEditProvider { * The given position and character should hint to the provider * what range the position to expand to, like find the matching `{` * when `}` has been entered. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param ch The character that has been typed. @@ -600,7 +603,6 @@ export interface DocumentColorProvider { /** * Provide colors for the given document. - * * @param document The document in which the command was invoked. * @param token A cancellation token. * @return An array of [color information](#ColorInformation) or a thenable that resolves to such. The lack of a result @@ -610,7 +612,6 @@ export interface DocumentColorProvider { /** * Provide [representations](#ColorPresentation) for a color. - * * @param color The color to show and insert. * @param context A context object with additional information * @param token A cancellation token. @@ -633,7 +634,6 @@ export interface TextDocumentContentProvider { * The editor will use the returned string-content to create a readonly * [document](#TextDocument). Resources allocated should be released when * the corresponding document has been [closed](#workspace.onDidCloseTextDocument). - * * @param uri An uri which scheme matches the scheme this provider was [registered](#workspace.registerTextDocumentContentProvider) for. * @param token A cancellation token. * @return A string or a thenable that resolves to such. @@ -660,7 +660,6 @@ export interface CallHierarchyProvider { * Bootstraps call hierarchy by returning the item that is denoted by the given document * and position. This item will be used as entry into the call graph. Providers should * return `undefined` or `null` when there is no item at the given location. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. @@ -673,7 +672,6 @@ export interface CallHierarchyProvider { * Provide all incoming calls for an item, e.g all callers for a method. In graph terms this describes directed * and annotated edges inside the call graph, e.g the given item is the starting node and the result is the nodes * that can be reached. - * * @param item The hierarchy item for which incoming calls should be computed. * @param token A cancellation token. * @returns A set of incoming calls or a thenable that resolves to such. The lack of a result can be @@ -685,7 +683,6 @@ export interface CallHierarchyProvider { * Provide all outgoing calls for an item, e.g call calls to functions, methods, or constructors from the given item. In * graph terms this describes directed and annotated edges inside the call graph, e.g the given item is the starting * node and the result is the nodes that can be reached. - * * @param item The hierarchy item for which outgoing calls should be computed. * @param token A cancellation token. * @returns A set of outgoing calls or a thenable that resolves to such. The lack of a result can be @@ -759,7 +756,6 @@ export interface DocumentSemanticTokensProvider { * // 1st token, 2nd token, 3rd token * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] * ``` - * * @see [SemanticTokensBuilder](#SemanticTokensBuilder) for a helper to encode tokens as integers. * *NOTE*: When doing edits, it is possible that multiple edits occur until VS Code decides to invoke the semantic tokens provider. * *NOTE*: If the provider cannot temporarily compute semantic tokens, it can indicate this by throwing an error with the message 'Busy'. @@ -815,7 +811,6 @@ export interface LinkedEditingRangeProvider { * that have the same content. A change to one of the ranges can be applied to all other ranges if the new content * is valid. An optional word pattern can be returned with the result to describe valid contents. * If no result-specific word pattern is provided, the word pattern from the language configuration is used. - * * @param document The document in which the provider was invoked. * @param position The position at which the provider was invoked. * @param token A cancellation token. @@ -839,7 +834,6 @@ export interface InlayHintsProvider { * Provide inlay hints for the given range and document. * * *Note* that inlay hints that are not {@link Range.contains contained} by the given range are ignored. - * * @param document The document in which the command was invoked. * @param range The range for which inlay hints should be computed. * @param token A cancellation token. @@ -852,7 +846,6 @@ export interface InlayHintsProvider { * or complete label {@link InlayHintLabelPart parts}. * * *Note* that the editor will resolve an inlay hint at most once. - * * @param hint An inlay hint. * @param token A cancellation token. * @return The resolved inlay hint or a thenable that resolves to such. It is OK to return the given `item`. When no result is returned, the given `item` will be used. @@ -870,7 +863,6 @@ export interface TypeHierarchyProvider { * Bootstraps type hierarchy by returning the item that is denoted by the given document * and position. This item will be used as entry into the type graph. Providers should * return `undefined` or `null` when there is no item at the given location. - * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. @@ -883,7 +875,6 @@ export interface TypeHierarchyProvider { * Provide all supertypes for an item, e.g all types from which a type is derived/inherited. In graph terms this describes directed * and annotated edges inside the type graph, e.g the given item is the starting node and the result is the nodes * that can be reached. - * * @param item The hierarchy item for which super types should be computed. * @param token A cancellation token. * @returns A set of direct supertypes or a thenable that resolves to such. The lack of a result can be @@ -895,7 +886,6 @@ export interface TypeHierarchyProvider { * Provide all subtypes for an item, e.g all types which are derived/inherited from the given item. In * graph terms this describes directed and annotated edges inside the type graph, e.g the given item is the starting * node and the result is the nodes that can be reached. - * * @param item The hierarchy item for which subtypes should be computed. * @param token A cancellation token. * @returns A set of direct subtypes or a thenable that resolves to such. The lack of a result can be @@ -913,7 +903,6 @@ export interface InlineValuesProvider { /** * An optional event to signal that inline values have changed. - * * @see {@link EventEmitter} */ onDidChangeInlineValues?: Event | undefined @@ -922,7 +911,6 @@ export interface InlineValuesProvider { * Provide "inline value" information for a given document and range. * The editor calls this method whenever debugging stops in the given document. * The returned inline values information is rendered in the editor at the end of lines. - * * @param document The document for which the inline values information is needed. * @param viewPort The visible document range for which inline values should be computed. * @param context A bag containing contextual information like the current location. diff --git a/src/provider/inlineCompletionItemManager.ts b/src/provider/inlineCompletionItemManager.ts new file mode 100644 index 00000000000..fa459e6133b --- /dev/null +++ b/src/provider/inlineCompletionItemManager.ts @@ -0,0 +1,36 @@ +'use strict' +import { v4 as uuid } from 'uuid' +import { TextDocument } from 'vscode-languageserver-textdocument' +import { InlineCompletionContext, InlineCompletionItem, InlineCompletionList, Position } from 'vscode-languageserver-types' +import { CancellationToken, Disposable } from '../util/protocol' +import { DocumentSelector, InlineCompletionItemProvider } from './index' +import Manager from './manager' + +export default class InlineCompletionItemManager extends Manager { + public register(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable { + return this.addProvider({ + id: uuid(), + selector, + provider + }) + } + + public async provideInlineCompletionItems( + document: TextDocument, + position: Position, + context: InlineCompletionContext, + token: CancellationToken + ): Promise { + const item = this.getProvider(document) + if (!item) return + + const { provider } = item + let res: InlineCompletionList | InlineCompletionItem[] = null + try { + res = await Promise.resolve(provider.provideInlineCompletionItems(document, position, context, token)) + } catch (e) { + this.handleResults([{ status: 'rejected', reason: e }], 'provideInlineCompletionItems') + } + return res + } +} diff --git a/tsconfig.json b/tsconfig.json index 6f6fb311dac..6a8f46884c2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,8 +13,8 @@ "noUnusedParameters": false, "strictPropertyInitialization": false, "target": "es2017", - "module": "commonjs", - "moduleResolution": "node", + "module": "Node16", + "moduleResolution": "node16", "lib": ["es2017", "es2018", "es2019", "ES2020.Promise"], "declaration": false, "resolveJsonModule": true,