mirror of
1
0
Fork 0
ultimate-vim/sources_non_forked/coc.nvim/src/__tests__/handler/codeActions.test.ts

415 lines
15 KiB
TypeScript

import { Neovim } from '@chemzqm/neovim'
import { CancellationToken, CodeAction, Command, CodeActionContext, CodeActionKind, TextEdit, Disposable, Range, Position } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
import commands from '../../commands'
import ActionsHandler from '../../handler/codeActions'
import languages from '../../languages'
import { ProviderResult } from '../../provider'
import { disposeAll } from '../../util'
import { rangeInRange } from '../../util/position'
import helper from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
let codeActions: ActionsHandler
let currActions: CodeAction[]
let resolvedAction: CodeAction
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
codeActions = helper.plugin.getHandler().codeActions
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
_range: Range,
_context: CodeActionContext,
_token: CancellationToken
) => currActions,
resolveCodeAction: (
_action: CodeAction,
_token: CancellationToken
): ProviderResult<CodeAction> => resolvedAction
}, undefined))
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('handler codeActions', () => {
describe('organizeImport', () => {
it('should throw error when organize import action not found', async () => {
currActions = []
await helper.createDocument()
let err
try {
await codeActions.organizeImport()
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should perform organize import action', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
let edits: TextEdit[] = []
edits.push(TextEdit.replace(Range.create(0, 0, 0, 3), 'bar'))
edits.push(TextEdit.replace(Range.create(1, 0, 1, 3), 'foo'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('organize import', edit, CodeActionKind.SourceOrganizeImports)
currActions = [action, CodeAction.create('another action')]
await codeActions.organizeImport()
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar', 'foo'])
})
it('should register editor.action.organizeImport command', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
let edits: TextEdit[] = []
edits.push(TextEdit.replace(Range.create(0, 0, 0, 3), 'bar'))
edits.push(TextEdit.replace(Range.create(1, 0, 1, 3), 'foo'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('organize import', edit, CodeActionKind.SourceOrganizeImports)
currActions = [action, CodeAction.create('another action')]
await commands.executeCommand('editor.action.organizeImport')
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar', 'foo'])
})
})
describe('codeActionRange', () => {
it('should show warning when no action available', async () => {
await helper.createDocument()
currActions = []
await codeActions.codeActionRange(1, 2, CodeActionKind.QuickFix)
let line = await helper.getCmdline()
expect(line).toMatch(/No quickfix code action/)
})
it('should apply chosen action', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [action]
let p = codeActions.codeActionRange(1, 2, CodeActionKind.QuickFix)
await helper.wait(100)
await nvim.input('<CR>')
await p
let buf = nvim.createBuffer(doc.bufnr)
let lines = await buf.lines
expect(lines[0]).toBe('bar')
})
})
describe('getCodeActions', () => {
it('should get empty actions', async () => {
currActions = []
let doc = await helper.createDocument()
let res = await codeActions.getCodeActions(doc)
expect(res.length).toBe(0)
})
it('should not filter disabled actions', async () => {
currActions = []
let action = CodeAction.create('foo', CodeActionKind.QuickFix)
action.disabled = { reason: 'disabled' }
currActions.push(action)
action = CodeAction.create('foo', CodeActionKind.QuickFix)
action.disabled = { reason: 'disabled' }
currActions.push(action)
let doc = await helper.createDocument()
let res = await codeActions.getCodeActions(doc)
expect(res.length).toBe(1)
})
it('should get all actions', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['', '', ''], { start: 0, end: -1, strictIndexing: false })
let action = CodeAction.create('curr action', CodeActionKind.Empty)
currActions = [action]
let range: Range
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
r: Range,
_context: CodeActionContext, _token: CancellationToken
) => {
range = r
return [CodeAction.create('a'), CodeAction.create('b'), CodeAction.create('c')]
},
}, undefined))
let res = await codeActions.getCodeActions(doc)
expect(range).toEqual(Range.create(0, 0, 3, 0))
expect(res.length).toBe(4)
})
it('should filter actions by range', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['', '', ''], { start: 0, end: -1, strictIndexing: false })
currActions = []
let range: Range
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
r: Range,
_context: CodeActionContext, _token: CancellationToken
) => {
range = r
if (rangeInRange(r, Range.create(0, 0, 1, 0))) return [CodeAction.create('a')]
return [CodeAction.create('a'), CodeAction.create('b'), CodeAction.create('c')]
},
}, undefined))
let res = await codeActions.getCodeActions(doc, Range.create(0, 0, 0, 0))
expect(range).toEqual(Range.create(0, 0, 0, 0))
expect(res.length).toBe(1)
})
it('should filter actions by kind prefix', async () => {
let doc = await helper.createDocument()
let action = CodeAction.create('my action', CodeActionKind.SourceFixAll)
currActions = [action]
let res = await codeActions.getCodeActions(doc, undefined, [CodeActionKind.Source])
expect(res.length).toBe(1)
expect(res[0].kind).toBe(CodeActionKind.SourceFixAll)
})
})
describe('getCurrentCodeActions', () => {
let range: Range
beforeEach(() => {
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
r: Range,
_context: CodeActionContext, _token: CancellationToken
) => {
range = r
return [CodeAction.create('a'), CodeAction.create('b'), CodeAction.create('c')]
},
}, undefined))
})
it('should get codeActions by line', async () => {
currActions = []
await helper.createDocument()
let res = await codeActions.getCurrentCodeActions('line')
expect(range).toEqual(Range.create(0, 0, 1, 0))
expect(res.length).toBe(3)
})
it('should get codeActions by cursor', async () => {
currActions = []
await helper.createDocument()
let res = await codeActions.getCurrentCodeActions('cursor')
expect(range).toEqual(Range.create(0, 0, 0, 0))
expect(res.length).toBe(3)
})
it('should get codeActions by visual mode', async () => {
currActions = []
await helper.createDocument()
await nvim.setLine('foo')
await nvim.command('normal! 0v$')
await nvim.input('<esc>')
let res = await codeActions.getCurrentCodeActions('v')
expect(range).toEqual(Range.create(0, 0, 0, 3))
expect(res.length).toBe(3)
})
})
describe('doCodeAction', () => {
it('should not throw when no action exists', async () => {
currActions = []
await helper.createDocument()
let err
try {
await codeActions.doCodeAction(undefined)
} catch (e) {
err = e
}
expect(err).toBeUndefined()
})
it('should apply single code action when only is title', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [action]
await codeActions.doCodeAction(undefined, 'code fix')
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
it('should apply single code action when only is codeAction array', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [action]
await codeActions.doCodeAction(undefined, [CodeActionKind.QuickFix])
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
it('should show disabled code action', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let refactorAction = CodeAction.create('code refactor', edit, CodeActionKind.Refactor)
refactorAction.disabled = { reason: 'invalid position' }
let fixAction = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [refactorAction, fixAction]
let p = codeActions.doCodeAction(undefined)
let winid = await helper.waitFloat()
let win = nvim.createWindow(winid)
let buf = await win.buffer
let lines = await buf.lines
expect(lines.length).toBe(2)
expect(lines[1]).toMatch(/code refactor/)
await nvim.input('2')
await helper.wait(50)
await nvim.input('j')
await nvim.input('<cr>')
await helper.wait(50)
let valid = await win.valid
expect(valid).toBe(true)
let cmdline = await helper.getCmdline()
expect(cmdline).toMatch(/invalid position/)
await nvim.input('<esc>')
})
it('should action dialog to choose action', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [action, CodeAction.create('foo')]
let promise = codeActions.doCodeAction(null)
await helper.wait(50)
let ids = await nvim.call('coc#float#get_float_win_list') as number[]
expect(ids.length).toBeGreaterThan(0)
await nvim.input('<CR>')
await promise
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
it('should choose code actions by range', async () => {
let range: Range
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
r: Range,
_context: CodeActionContext, _token: CancellationToken
) => {
range = r
return [CodeAction.create('my title'), CodeAction.create('b'), CodeAction.create('c')]
},
}, undefined))
await helper.createDocument()
await nvim.setLine('abc')
await nvim.command('normal! 0v$')
await nvim.input('<esc>')
await codeActions.doCodeAction('v', 'my title')
expect(range).toEqual({ start: { line: 0, character: 0 }, end: { line: 0, character: 3 } })
})
})
describe('doQuickfix', () => {
it('should throw when quickfix action does not exist', async () => {
let err
currActions = []
await helper.createDocument()
try {
await codeActions.doQuickfix()
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should do preferred quickfix action', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
action.isPreferred = true
currActions = [CodeAction.create('foo', CodeActionKind.QuickFix), action, CodeAction.create('bar')]
await codeActions.doQuickfix()
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
})
describe('applyCodeAction', () => {
it('should resolve codeAction', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', CodeActionKind.QuickFix)
action.isPreferred = true
currActions = [action]
resolvedAction = Object.assign({ edit }, action)
let arr = await codeActions.getCurrentCodeActions('line', [CodeActionKind.QuickFix])
await codeActions.applyCodeAction(arr[0])
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
it('should throw for disabled action', async () => {
let action: any = CodeAction.create('my action', CodeActionKind.Empty)
action.disabled = { reason: 'disabled', providerId: 'x' }
let err
try {
await codeActions.applyCodeAction(action)
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should invoke registered command after apply edit', async () => {
let called
disposables.push(commands.registerCommand('test.execute', async (s: string) => {
called = s
await nvim.command(s)
}))
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', CodeActionKind.QuickFix)
action.isPreferred = true
currActions = [action]
resolvedAction = Object.assign({
edit,
command: Command.create('run vim command', 'test.execute', 'normal! $')
}, action)
let arr = await codeActions.getCurrentCodeActions('line', [CodeActionKind.QuickFix])
await codeActions.applyCodeAction(arr[0])
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
expect(called).toBe('normal! $')
})
})
})