1
0
Fork 0
mirror of synced 2024-06-25 18:21:11 -04:00
ultimate-vim/sources_non_forked/coc.nvim/src/handler/codeActions.ts
2022-07-20 13:20:15 +08:00

150 lines
5.9 KiB
TypeScript

'use strict'
import { NeovimClient as Neovim } from '@chemzqm/neovim'
import { CodeActionContext, CodeActionKind, Range } from 'vscode-languageserver-protocol'
import commandManager from '../commands'
import diagnosticManager from '../diagnostic/manager'
import languages from '../languages'
import Document from '../model/document'
import { ExtendedCodeAction, HandlerDelegate } from '../types'
import window from '../window'
import workspace from '../workspace'
const logger = require('../util/logger')('handler-codeActions')
/**
* Handle codeActions related methods.
*/
export default class CodeActions {
constructor(
private nvim: Neovim,
private handler: HandlerDelegate
) {
handler.addDisposable(commandManager.registerCommand('editor.action.organizeImport', async (bufnr?: number) => {
await this.organizeImport(bufnr)
}))
commandManager.titles.set('editor.action.organizeImport', 'run organize import code action.')
}
public async codeActionRange(start: number, end: number, only?: string): Promise<void> {
let { doc } = await this.handler.getCurrentState()
await doc.synchronize()
let line = doc.getline(end - 1)
let range = Range.create(start - 1, 0, end - 1, line.length)
let codeActions = await this.getCodeActions(doc, range, only ? [only] : null)
codeActions = codeActions.filter(o => !o.disabled)
if (!codeActions || codeActions.length == 0) {
window.showMessage(`No${only ? ' ' + only : ''} code action available`, 'warning')
return
}
let idx = await window.showMenuPicker(codeActions.map(o => o.title), 'Choose action')
let action = codeActions[idx]
if (action) await this.applyCodeAction(action)
}
public async organizeImport(bufnr?: number): Promise<void> {
let { doc } = await this.handler.getCurrentState()
if (bufnr && doc.bufnr != bufnr) return
await doc.synchronize()
let actions = await this.getCodeActions(doc, undefined, [CodeActionKind.SourceOrganizeImports])
if (actions && actions.length) {
await this.applyCodeAction(actions[0])
return
}
throw new Error('Organize import action not found.')
}
public async getCodeActions(doc: Document, range?: Range, only?: CodeActionKind[]): Promise<ExtendedCodeAction[]> {
range = range || Range.create(0, 0, doc.lineCount, 0)
let diagnostics = diagnosticManager.getDiagnosticsInRange(doc.textDocument, range)
let context: CodeActionContext = { diagnostics }
if (only && Array.isArray(only)) context.only = only
let codeActions = await this.handler.withRequestToken('code action', token => {
return languages.getCodeActions(doc.textDocument, range, context, token)
})
if (!codeActions || codeActions.length == 0) return []
codeActions.sort((a, b) => {
if (a.isPreferred && !b.isPreferred) return -1
if (b.isPreferred && !a.isPreferred) return 1
if (a.disabled && !b.disabled) return 1
if (b.disabled && !a.disabled) return -1
return 0
})
return codeActions
}
private get floatActions(): boolean {
if (!workspace.floatSupported) return false
let config = workspace.getConfiguration('coc.preferences')
return config.get<boolean>('floatActions', true)
}
public async doCodeAction(mode: string | null, only?: CodeActionKind[] | string): Promise<void> {
let { doc } = await this.handler.getCurrentState()
let range: Range
if (mode) range = await window.getSelectedRange(mode)
await doc.synchronize()
let codeActions = await this.getCodeActions(doc, range, Array.isArray(only) ? only : null)
if (typeof only == 'string') {
codeActions = codeActions.filter(o => o.title == only || (o.command && o.command.title == only))
} else if (Array.isArray(only)) {
codeActions = codeActions.filter(o => only.some(k => o.kind && o.kind.startsWith(k)))
}
if (!codeActions || codeActions.length == 0) {
window.showMessage(`No${only ? ' ' + only : ''} code action available`, 'warning')
return
}
if (only && codeActions.length == 1 && !codeActions[0].disabled) {
await this.applyCodeAction(codeActions[0])
return
}
if (!this.floatActions) codeActions = codeActions.filter(o => !o.disabled)
let idx = this.floatActions
? await window.showMenuPicker(
codeActions.map(o => {
return { text: o.title, disabled: o.disabled }
}),
'Choose action'
)
: await window.showQuickpick(codeActions.map(o => o.title))
let action = codeActions[idx]
if (action) await this.applyCodeAction(action)
}
/**
* Get current codeActions
*/
public async getCurrentCodeActions(mode?: string, only?: CodeActionKind[]): Promise<ExtendedCodeAction[]> {
let { doc } = await this.handler.getCurrentState()
let range: Range
if (mode) range = await window.getSelectedRange(mode)
let codeActions = await this.getCodeActions(doc, range, only)
return codeActions.filter(o => !o.disabled)
}
/**
* Invoke preferred quickfix at current position
*/
public async doQuickfix(): Promise<void> {
let actions = await this.getCurrentCodeActions('line', [CodeActionKind.QuickFix])
if (!actions || actions.length == 0) {
throw new Error('No quickfix action available')
}
await this.applyCodeAction(actions[0])
this.nvim.command(`silent! call repeat#set("\\<Plug>(coc-fix-current)", -1)`, true)
}
public async applyCodeAction(action: ExtendedCodeAction): Promise<void> {
if (action.disabled) {
throw new Error(`Action "${action.title}" is disabled: ${action.disabled.reason}`)
}
if (!action.providerId) {
throw new Error('providerId not found with codeAction')
}
let resolved = await this.handler.withRequestToken('resolve codeAction', token => {
return languages.resolveCodeAction(action, token)
})
let { edit, command } = resolved
if (edit) await workspace.applyEdit(edit)
if (command) await commandManager.execute(command)
}
}