'use strict' import { CancellationToken, ClientCapabilities, CodeAction, CodeActionContext, CodeActionKind, CodeActionOptions, CodeActionParams, CodeActionRegistrationOptions, CodeActionRequest, CodeActionResolveRequest, Command, Disposable, DocumentSelector, ExecuteCommandParams, ExecuteCommandRequest, Range, ServerCapabilities } from 'vscode-languageserver-protocol' import { TextDocument } from "vscode-languageserver-textdocument" import commands from '../commands' import languages from '../languages' import { CodeActionProvider, ProviderResult } from '../provider' import { ExecuteCommandMiddleware, ExecuteCommandSignature } from './executeCommand' import { ensure, FeatureClient, TextDocumentLanguageFeature } from './features' import * as UUID from './utils/uuid' export interface ProvideCodeActionsSignature { ( this: void, document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken ): ProviderResult<(Command | CodeAction)[]> } export interface ResolveCodeActionSignature { (this: void, item: CodeAction, token: CancellationToken): ProviderResult } export interface CodeActionMiddleware { provideCodeActions?: ( this: void, document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken, next: ProvideCodeActionsSignature ) => ProviderResult<(Command | CodeAction)[]> resolveCodeAction?: ( this: void, item: CodeAction, token: CancellationToken, next: ResolveCodeActionSignature ) => ProviderResult } export class CodeActionFeature extends TextDocumentLanguageFeature { private disposables: Disposable[] = [] constructor(client: FeatureClient) { super(client, CodeActionRequest.type) } public fillClientCapabilities(capabilities: ClientCapabilities): void { const cap = ensure(ensure(capabilities, 'textDocument')!, 'codeAction')! cap.dynamicRegistration = true cap.isPreferredSupport = true cap.disabledSupport = true cap.dataSupport = true cap.honorsChangeAnnotations = false cap.resolveSupport = { properties: ['edit'] } cap.codeActionLiteralSupport = { codeActionKind: { valueSet: [ CodeActionKind.Empty, CodeActionKind.QuickFix, CodeActionKind.Refactor, CodeActionKind.RefactorExtract, CodeActionKind.RefactorInline, CodeActionKind.RefactorRewrite, CodeActionKind.Source, CodeActionKind.SourceOrganizeImports ] } } } public initialize( capabilities: ServerCapabilities, documentSelector: DocumentSelector ): void { const options = this.getRegistrationOptions(documentSelector, capabilities.codeActionProvider) if (!options) { return } this.register({ id: UUID.generateUuid(), registerOptions: options }) } protected registerLanguageProvider( options: CodeActionRegistrationOptions ): [Disposable, CodeActionProvider] { const registerCommand = (id: string) => { const client = this._client const executeCommand: ExecuteCommandSignature = (command: string, args: any[]): any => { const params: ExecuteCommandParams = { command, arguments: args } return client.sendRequest(ExecuteCommandRequest.type, params) } const middleware = client.middleware! this.disposables.push(commands.registerCommand(id, (...args: any[]) => { return middleware.executeCommand ? middleware.executeCommand(id, args, executeCommand) : executeCommand(id, args) }, null, true)) } const provider: CodeActionProvider = { provideCodeActions: (document, range, context, token) => { const client = this._client const _provideCodeActions: ProvideCodeActionsSignature = (document, range, context, token) => { const params: CodeActionParams = { textDocument: { uri: document.uri }, range, context, } return this.sendRequest(CodeActionRequest.type, params, token).then( values => { if (!values) return undefined // some server may not registered commands to client. values.forEach(val => { let cmd = Command.is(val) ? val.command : val.command?.command if (cmd && !commands.has(cmd)) registerCommand(cmd) }) return values } ) } const middleware = client.middleware! return middleware.provideCodeActions ? middleware.provideCodeActions(document, range, context, token, _provideCodeActions) : _provideCodeActions(document, range, context, token) }, resolveCodeAction: options.resolveProvider ? (item: CodeAction, token: CancellationToken) => { const middleware = this._client.middleware! const resolveCodeAction: ResolveCodeActionSignature = (item, token) => { return this.sendRequest(CodeActionResolveRequest.type, item, token, item) } return middleware.resolveCodeAction ? middleware.resolveCodeAction(item, token, resolveCodeAction) : resolveCodeAction(item, token) } : undefined } return [languages.registerCodeActionProvider(options.documentSelector, provider, this._client.id, options.codeActionKinds), provider] } public dispose(): void { this.disposables.forEach(o => { o.dispose() }) this.disposables = [] super.dispose() } }