diff --git a/client/package.json b/client/package.json index f7de88804..7572eeda2 100644 --- a/client/package.json +++ b/client/package.json @@ -5,7 +5,7 @@ "author": "Microsoft Corporation", "license": "MIT", "engines": { - "vscode": "^1.22" + "vscode": "^1.23" }, "repository": { "type": "git", diff --git a/client/src/client.ts b/client/src/client.ts index f2b040e36..dba61aa7a 100644 --- a/client/src/client.ts +++ b/client/src/client.ts @@ -10,9 +10,9 @@ import { FileSystemWatcher as VFileSystemWatcher, DiagnosticCollection, Diagnostic as VDiagnostic, Uri, ProviderResult, CancellationToken, Position as VPosition, Location as VLocation, Range as VRange, CompletionItem as VCompletionItem, CompletionList as VCompletionList, SignatureHelp as VSignatureHelp, Definition as VDefinition, DocumentHighlight as VDocumentHighlight, - SymbolInformation as VSymbolInformation, CodeActionContext as VCodeActionContext, Command as VCommand, CodeLens as VCodeLens, + SymbolInformation as VSymbolInformation, CodeActionContext as VCodeActionContext, Command as VCommand, CodeLens as VCodeLens, CodeActionKind as VCodeActionKind, FormattingOptions as VFormattingOptions, TextEdit as VTextEdit, WorkspaceEdit as VWorkspaceEdit, MessageItem, - Hover as VHover, + Hover as VHover, CodeAction as VCodeAction, DocumentLink as VDocumentLink, TextDocumentWillSaveEvent, WorkspaceFolder as VWorkspaceFolder, CompletionContext as VCompletionContext } from 'vscode'; @@ -53,7 +53,7 @@ import { DocumentLinkRequest, DocumentLinkResolveRequest, DocumentLinkRegistrationOptions, ExecuteCommandRequest, ExecuteCommandParams, ExecuteCommandRegistrationOptions, ApplyWorkspaceEditRequest, ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse, - MarkupKind, SymbolKind, CompletionItemKind + MarkupKind, SymbolKind, CompletionItemKind, Command } from 'vscode-languageserver-protocol'; import { ColorProviderMiddleware } from './colorProvider'; @@ -359,7 +359,7 @@ export interface ProvideWorkspaceSymbolsSignature { } export interface ProvideCodeActionsSignature { - (document: TextDocument, range: VRange, context: VCodeActionContext, token: CancellationToken): ProviderResult; + (document: TextDocument, range: VRange, context: VCodeActionContext, token: CancellationToken): ProviderResult<(VCommand | VCodeAction)[]>; } export interface ProvideCodeLensesSignature { @@ -430,7 +430,7 @@ export interface _Middleware { provideDocumentHighlights?: (this: void, document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideDocumentHighlightsSignature) => ProviderResult; provideDocumentSymbols?: (this: void, document: TextDocument, token: CancellationToken, next: ProvideDocumentSymbolsSignature) => ProviderResult; provideWorkspaceSymbols?: (this: void, query: string, token: CancellationToken, next: ProvideWorkspaceSymbolsSignature) => ProviderResult; - provideCodeActions?: (this: void, document: TextDocument, range: VRange, context: VCodeActionContext, token: CancellationToken, next: ProvideCodeActionsSignature) => ProviderResult; + provideCodeActions?: (this: void, document: TextDocument, range: VRange, context: VCodeActionContext, token: CancellationToken, next: ProvideCodeActionsSignature) => ProviderResult<(VCommand | VCodeAction)[]>; provideCodeLenses?: (this: void, document: TextDocument, token: CancellationToken, next: ProvideCodeLensesSignature) => ProviderResult; resolveCodeLens?: (this: void, codeLens: VCodeLens, token: CancellationToken, next: ResolveCodeLensSignature) => ProviderResult; provideDocumentFormattingEdits?: (this: void, document: TextDocument, options: VFormattingOptions, token: CancellationToken, next: ProvideDocumentFormattingEditsSignature) => ProviderResult; @@ -1671,7 +1671,22 @@ class CodeActionFeature extends TextDocumentFeature { + if (values === null) { + return undefined; + } + let result: (VCommand | VCodeAction)[] = []; + for (let item of values) { + if (Command.is(item)) { + result.push(client.protocol2CodeConverter.asCommand(item)) + } else { + result.push(client.protocol2CodeConverter.asCodeAction(item)); + }; + } + return result; + }, (error) => { client.logFailedRequest(CodeActionRequest.type, error); return Promise.resolve([]); @@ -1702,7 +1729,7 @@ class CodeActionFeature extends TextDocumentFeature => { + provideCodeActions: (document: TextDocument, range: VRange, context: VCodeActionContext, token: CancellationToken): ProviderResult<(VCommand | VCodeAction)[]> => { return middleware.provideCodeActions ? middleware.provideCodeActions(document, range, context, token, provideCodeActions) : provideCodeActions(document, range, context, token); diff --git a/client/src/protocolConverter.ts b/client/src/protocolConverter.ts index e2d5e4c3b..5c8efb692 100644 --- a/client/src/protocolConverter.ts +++ b/client/src/protocolConverter.ts @@ -92,6 +92,10 @@ export interface Converter { asCommands(items: undefined | null): undefined asCommands(items: ls.Command[] | undefined | null): code.Command[] | undefined; + asCodeAction(item: ls.CodeAction): code.CodeAction; + asCodeAction(item: undefined | null): undefined; + asCodeAction(item: ls.CodeAction | undefined | null): code.CodeAction | undefined; + asCodeLens(item: ls.CodeLens): code.CodeLens; asCodeLens(item: undefined | null): undefined; asCodeLens(item: ls.CodeLens | undefined | null): code.CodeLens | undefined; @@ -494,6 +498,50 @@ export function createConverter(uriConverter?: URIConverter): Converter { return items.map(asCommand); } + const kindMapping: Map = new Map(); + kindMapping.set('', code.CodeActionKind.Empty); + kindMapping.set(ls.CodeActionKind.QuickFix, code.CodeActionKind.QuickFix); + kindMapping.set(ls.CodeActionKind.Refactor, code.CodeActionKind.Refactor); + kindMapping.set(ls.CodeActionKind.RefactorExtract, code.CodeActionKind.RefactorExtract); + kindMapping.set(ls.CodeActionKind.RefactorInline, code.CodeActionKind.RefactorInline); + kindMapping.set(ls.CodeActionKind.RefactorRewrite, code.CodeActionKind.RefactorRewrite); + kindMapping.set(ls.CodeActionKind.Source, code.CodeActionKind.Source); + kindMapping.set(ls.CodeActionKind.SourceOrganizeImports, code.CodeActionKind.SourceOrganizeImports); + + function asCodeActionKind(item: null | undefined): undefined; + function asCodeActionKind(item: ls.CodeActionKind): code.CodeActionKind; + function asCodeActionKind(item: ls.CodeActionKind | null | undefined): code.CodeActionKind | undefined; + function asCodeActionKind(item: ls.CodeActionKind | null | undefined): code.CodeActionKind | undefined { + if (item === void 0 || item === null) { + return undefined + } + let result: code.CodeActionKind | undefined = kindMapping.get(item); + if (result) { + return result; + } + let parts = item.split('.'); + result = code.CodeActionKind.Empty; + for (let part of parts) { + result = result.append(part); + } + return result; + } + + function asCodeAction(item: ls.CodeAction): code.CodeAction; + function asCodeAction(item: undefined | null): undefined; + function asCodeAction(item: ls.CodeAction | undefined | null): code.CodeAction | undefined; + function asCodeAction(item: ls.CodeAction | undefined | null): code.CodeAction | undefined { + if (item === void 0 || item === null) { + return undefined; + } + let result = new code.CodeAction(item.title); + if (item.kind !== void 0) { result.kind = asCodeActionKind(item.kind); } + if (item.diagnostics) { result.diagnostics = asDiagnostics(item.diagnostics); } + if (item.edit) { result.edit = asWorkspaceEdit(item.edit); } + if (item.command) { result.command = asCommand(item.command); } + return result; + } + function asCodeLens(item: ls.CodeLens): code.CodeLens; function asCodeLens(item: undefined | null): undefined; function asCodeLens(item: ls.CodeLens | undefined | null): code.CodeLens | undefined; @@ -583,6 +631,7 @@ export function createConverter(uriConverter?: URIConverter): Converter { asSymbolInformation, asCommand, asCommands, + asCodeAction, asCodeLens, asCodeLenses, asWorkspaceEdit, diff --git a/protocol/src/protocol.ts b/protocol/src/protocol.ts index 8f30c3cdf..4075edf4a 100644 --- a/protocol/src/protocol.ts +++ b/protocol/src/protocol.ts @@ -15,7 +15,7 @@ import { CompletionItem, CompletionList, Hover, SignatureHelp, Definition, ReferenceContext, DocumentHighlight, DocumentSymbolParams, SymbolInformation, CodeLens, CodeActionContext, FormattingOptions, DocumentLink, MarkupKind, - SymbolKind, CompletionItemKind + SymbolKind, CompletionItemKind, CodeAction, CodeActionKind } from 'vscode-languageserver-types'; import { ImplementationRequest, ImplementationClientCapabilities, ImplementationServerCapabilities } from './protocol.implementation'; @@ -460,6 +460,27 @@ export interface TextDocumentClientCapabilities { * Whether code action supports dynamic registration. */ dynamicRegistration?: boolean; + + /** + * The client support code action literals as a valid + * response of the `textDocument/codeAction` request. + */ + codeActionLiteralSupport?: { + /** + * The code action kind is support with the following value + * set. + */ + codeActionKind: { + + /** + * The code action kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + */ + valueSet: CodeActionKind[]; + }; + }; }; /** @@ -1522,7 +1543,7 @@ export interface CodeActionParams { * A request to provide commands for the given text document and range. */ export namespace CodeActionRequest { - export const type = new RequestType('textDocument/codeAction'); + export const type = new RequestType('textDocument/codeAction'); } //---- Code Lens Provider ------------------------------------------- diff --git a/types/src/main.ts b/types/src/main.ts index 73c338a0f..e8799e3ad 100644 --- a/types/src/main.ts +++ b/types/src/main.ts @@ -446,6 +446,15 @@ export interface WorkspaceEdit { documentChanges?: TextDocumentEdit[]; } +export namespace WorkspaceEdit { + export function is(value: any): value is WorkspaceEdit { + let candidate: WorkspaceEdit = value; + return candidate && + (candidate.changes !== void 0 || candidate.documentChanges !== void 0) && + (candidate.documentChanges === void 0 || Is.typedArray(candidate.documentChanges, TextDocumentEdit.is)); + } +} + /** * A change to capture text edits for existing resources. */ @@ -1467,7 +1476,7 @@ export namespace CodeActionContext { * * A CodeAction must set either `edit` and/or a `command`. If both are supplied, the `edit` is applied first, then the `command` is executed. */ -export interface CodeAction { +export type CodeAction = { /** * A short, human-readable, title for this code action. @@ -1481,6 +1490,11 @@ export interface CodeAction { */ kind?: CodeActionKind; + /** + * The diagnostics that this code action resolves. + */ + diagnostics?: Diagnostic[]; + /** * The workspace edit this code action performs. */ @@ -1492,11 +1506,18 @@ export interface CodeAction { * executed and then the command. */ command?: Command; +} - /** - * The diagnostics that this code action resolves. - */ - diagnostics?: Diagnostic[]; +export namespace CodeAction { + export function is(value: any): value is CodeAction { + let candidate: CodeAction = value; + return candidate && Is.string(candidate.title) && + (candidate.diagnostics === void 0 || Is.typedArray(candidate.diagnostics, Diagnostic.is)) && + (candidate.kind === void 0 || Is.string(candidate.kind)) && + (candidate.edit !== void 0 || candidate.command !== void 0) && + (candidate.command === void 0 || Command.is(candidate.command)) && + (candidate.edit === void 0 || WorkspaceEdit.is(candidate.edit)); + } } /**