UNPKG

coc.nvim

Version:

LSP based intellisense engine for neovim & vim8.

1,172 lines 50.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol"); const commands_1 = tslib_1.__importDefault(require("../commands")); const manager_1 = tslib_1.__importDefault(require("../diagnostic/manager")); const events_1 = tslib_1.__importDefault(require("../events")); const languages_1 = tslib_1.__importDefault(require("../languages")); const manager_2 = tslib_1.__importDefault(require("../list/manager")); const floatFactory_1 = tslib_1.__importDefault(require("../model/floatFactory")); const services_1 = tslib_1.__importDefault(require("../services")); const manager_3 = tslib_1.__importDefault(require("../snippets/manager")); const util_1 = require("../util"); const convert_1 = require("../util/convert"); const object_1 = require("../util/object"); const position_1 = require("../util/position"); const string_1 = require("../util/string"); const workspace_1 = tslib_1.__importDefault(require("../workspace")); const codelens_1 = tslib_1.__importDefault(require("./codelens")); const colors_1 = tslib_1.__importDefault(require("./colors")); const refactor_1 = tslib_1.__importDefault(require("./refactor")); const documentHighlight_1 = tslib_1.__importDefault(require("./documentHighlight")); const debounce = require("debounce"); const logger = require('../util/logger')('Handler'); const pairs = new Map([ ['<', '>'], ['>', '<'], ['{', '}'], ['[', ']'], ['(', ')'], ]); class Handler { constructor(nvim) { this.nvim = nvim; this.documentLines = []; this.disposables = []; this.labels = {}; this.selectionRange = null; this.getPreferences(); workspace_1.default.onDidChangeConfiguration(e => { if (e.affectsConfiguration('coc.preferences')) { this.getPreferences(); } }); this.refactor = new refactor_1.default(); this.hoverFactory = new floatFactory_1.default(nvim, workspace_1.default.env); this.disposables.push(this.hoverFactory); let { signaturePreferAbove, signatureFloatMaxWidth, signatureMaxHeight } = this.preferences; this.signatureFactory = new floatFactory_1.default(nvim, workspace_1.default.env, signaturePreferAbove, signatureMaxHeight, signatureFloatMaxWidth, false); this.disposables.push(this.signatureFactory); events_1.default.on('CursorMovedI', async (bufnr, cursor) => { if (!this.signaturePosition) return; let doc = workspace_1.default.getDocument(bufnr); if (!doc) return; let { line, character } = this.signaturePosition; if (cursor[0] - 1 == line) { let currline = doc.getline(cursor[0] - 1); let col = string_1.byteLength(currline.slice(0, character)) + 1; if (cursor[1] > col) return; } this.signatureFactory.close(); }, null, this.disposables); events_1.default.on('InsertLeave', bufnr => { this.signatureFactory.close(); }, null, this.disposables); events_1.default.on(['TextChangedI', 'TextChangedP'], async () => { if (this.preferences.signatureHideOnChange) { this.signatureFactory.close(); } this.hoverFactory.close(); }, null, this.disposables); let lastInsert; events_1.default.on('InsertCharPre', async (character) => { lastInsert = Date.now(); if (character == ')') this.signatureFactory.close(); }, null, this.disposables); events_1.default.on('Enter', async (bufnr) => { let { bracketEnterImprove } = this.preferences; await this.onCharacterType('\n', bufnr); if (bracketEnterImprove) { let line = await nvim.call('line', '.') - 1; let doc = workspace_1.default.getDocument(bufnr); if (!doc) return; let pre = doc.getline(line - 1); let curr = doc.getline(line); let prevChar = pre[pre.length - 1]; if (prevChar && pairs.has(prevChar)) { let nextChar = curr.trim()[0]; if (nextChar && pairs.get(prevChar) == nextChar) { let edits = []; let opts = await workspace_1.default.getFormatOptions(doc.uri); let space = opts.insertSpaces ? ' '.repeat(opts.tabSize) : '\t'; let preIndent = pre.match(/^\s*/)[0]; let currIndent = curr.match(/^\s*/)[0]; let newText = '\n' + preIndent + space; let pos = vscode_languageserver_protocol_1.Position.create(line - 1, pre.length); // make sure indent of current line if (preIndent != currIndent) { let newText = doc.filetype == 'vim' ? ' \\ ' + preIndent : preIndent; edits.push({ range: vscode_languageserver_protocol_1.Range.create(vscode_languageserver_protocol_1.Position.create(line, 0), vscode_languageserver_protocol_1.Position.create(line, currIndent.length)), newText }); } else if (doc.filetype == 'vim') { edits.push({ range: vscode_languageserver_protocol_1.Range.create(line, currIndent.length, line, currIndent.length), newText: ' \\ ' }); } if (doc.filetype == 'vim') { newText = newText + '\\ '; } edits.push({ range: vscode_languageserver_protocol_1.Range.create(pos, pos), newText }); await doc.applyEdits(nvim, edits); await workspace_1.default.moveTo(vscode_languageserver_protocol_1.Position.create(line, newText.length - 1)); } } } }, null, this.disposables); events_1.default.on('TextChangedI', async (bufnr) => { let curr = Date.now(); if (!lastInsert || curr - lastInsert > 500) return; let doc = workspace_1.default.getDocument(bufnr); if (!doc) return; let { triggerSignatureHelp, triggerSignatureWait, formatOnType } = this.preferences; if (!triggerSignatureHelp && !formatOnType) return; let [pos, line] = await nvim.eval('[coc#util#cursor(), getline(".")]'); let pre = pos[1] == 0 ? '' : line.slice(pos[1] - 1, pos[1]); if (!pre || string_1.isWord(pre)) return; if (!doc.paused) await this.onCharacterType(pre, bufnr); if (triggerSignatureHelp && languages_1.default.shouldTriggerSignatureHelp(doc.textDocument, pre)) { doc.forceSync(); await util_1.wait(Math.min(Math.max(triggerSignatureWait, 50), 300)); if (!workspace_1.default.insertMode) return; try { let cursor = await nvim.call('coc#util#cursor'); await this.triggerSignatureHelp(doc, { line: cursor[0], character: cursor[1] }); } catch (e) { logger.error(`Error on signature help:`, e); } } }, null, this.disposables); events_1.default.on('InsertLeave', async (bufnr) => { await util_1.wait(30); if (workspace_1.default.insertMode) return; await this.onCharacterType('\n', bufnr, true); }, null, this.disposables); events_1.default.on('CursorMoved', debounce((bufnr, cursor) => { if (!this.preferences.previewAutoClose || !this.hoverPosition) return; if (this.preferences.hoverTarget == 'float') return; let arr = [bufnr, cursor[0], cursor[1]]; if (object_1.equals(arr, this.hoverPosition)) return; let doc = workspace_1.default.documents.find(doc => doc.uri.startsWith('coc://')); if (doc && doc.bufnr != bufnr) { nvim.command('pclose', true); } }, 100), null, this.disposables); if (this.preferences.currentFunctionSymbolAutoUpdate) { events_1.default.on('CursorHold', async () => { try { await this.getCurrentFunctionSymbol(); } catch (e) { logger.error(e); } }, null, this.disposables); } let provider = { onDidChange: null, provideTextDocumentContent: async () => { nvim.pauseNotification(); nvim.command('setlocal conceallevel=2 nospell nofoldenable wrap', true); nvim.command('setlocal bufhidden=wipe nobuflisted', true); nvim.command('setfiletype markdown', true); nvim.command(`exe "normal! z${this.documentLines.length}\\<cr>"`, true); await nvim.resumeNotification(); return this.documentLines.join('\n'); } }; this.disposables.push(workspace_1.default.registerTextDocumentContentProvider('coc', provider)); this.codeLensManager = new codelens_1.default(nvim); this.colors = new colors_1.default(nvim); this.documentHighlighter = new documentHighlight_1.default(nvim, this.colors); this.disposables.push(commands_1.default.registerCommand('editor.action.organizeImport', async (bufnr) => { if (!bufnr) bufnr = await nvim.call('bufnr', '%'); let doc = workspace_1.default.getDocument(bufnr); if (!doc) return false; let range = vscode_languageserver_protocol_1.Range.create(0, 0, doc.lineCount, 0); let actions = await this.getCodeActions(bufnr, range, [vscode_languageserver_protocol_1.CodeActionKind.SourceOrganizeImports]); if (actions && actions.length) { await this.applyCodeAction(actions[0]); return true; } workspace_1.default.showMessage(`Orgnize import action not found.`, 'warning'); return false; })); commands_1.default.titles.set('editor.action.organizeImport', 'run organize import code action.'); } async getCurrentFunctionSymbol() { let position = await workspace_1.default.getCursorPosition(); let buffer = await this.nvim.buffer; let document = workspace_1.default.getDocument(buffer.id); if (!document) return; let symbols = await this.getDocumentSymbols(document); if (!symbols || symbols.length === 0) { buffer.setVar('coc_current_function', '', true); this.nvim.call('coc#util#do_autocmd', ['CocStatusChange'], true); return ''; } symbols = symbols.filter(s => [ 'Class', 'Method', 'Function', ].includes(s.kind)); let filetype = document.filetype; let functionName = ''; for (let sym of symbols.reverse()) { if (sym.range && position_1.positionInRange(position, sym.range) == 0 && !sym.text.endsWith(') callback')) { functionName = sym.text; let kind = sym.kind.toLowerCase(); let label = this.labels[sym.kind.toLowerCase()]; if (label) functionName = `${label} ${functionName}`; break; } } buffer.setVar('coc_current_function', functionName, true); this.nvim.call('coc#util#do_autocmd', ['CocStatusChange'], true); return functionName; } async onHover() { let { document, position } = await workspace_1.default.getCurrentState(); let hovers = await languages_1.default.getHover(document, position); if (hovers && hovers.length) { await this.previewHover(hovers); return true; } let target = this.preferences.hoverTarget; if (target == 'float') { this.hoverFactory.close(); } else if (target == 'preview') { this.nvim.command('pclose', true); } return false; } async gotoDefinition(openCommand) { let { document, position } = await workspace_1.default.getCurrentState(); let definition = await languages_1.default.getDefinition(document, position); if (isEmpty(definition)) { this.onEmptyLocation('Definition', definition); return false; } await this.handleLocations(definition, openCommand); return true; } async gotoDeclaration(openCommand) { let { document, position } = await workspace_1.default.getCurrentState(); let definition = await languages_1.default.getDeclaration(document, position); if (isEmpty(definition)) { this.onEmptyLocation('Declaration', definition); return false; } await this.handleLocations(definition, openCommand); return true; } async gotoTypeDefinition(openCommand) { let { document, position } = await workspace_1.default.getCurrentState(); let definition = await languages_1.default.getTypeDefinition(document, position); if (isEmpty(definition)) { this.onEmptyLocation('Type definition', definition); return false; } await this.handleLocations(definition, openCommand); return true; } async gotoImplementation(openCommand) { let { document, position } = await workspace_1.default.getCurrentState(); let definition = await languages_1.default.getImplementation(document, position); if (isEmpty(definition)) { this.onEmptyLocation('Implementation', definition); return false; } await this.handleLocations(definition, openCommand); return true; } async gotoReferences(openCommand) { let { document, position } = await workspace_1.default.getCurrentState(); let locs = await languages_1.default.getReferences(document, { includeDeclaration: false }, position); if (isEmpty(locs)) { this.onEmptyLocation('References', locs); return false; } await this.handleLocations(locs, openCommand); return true; } async getDocumentSymbols(document) { document = document || workspace_1.default.getDocument(workspace_1.default.bufnr); if (!document) return []; let symbols = await languages_1.default.getDocumentSymbol(document.textDocument); if (!symbols) return null; if (symbols.length == 0) return []; let level = 0; let res = []; let pre = null; if (isDocumentSymbols(symbols)) { symbols.sort(sortDocumentSymbols); symbols.forEach(s => addDoucmentSymbol(res, s, level)); } else { symbols.sort(sortSymbolInformations); for (let sym of symbols) { let { name, kind, location, containerName } = sym; if (!containerName || !pre) { level = 0; } else { if (pre.containerName == containerName) { level = pre.level || 0; } else { let container = getPreviousContainer(containerName, res); level = container ? container.level + 1 : 0; } } let { start } = location.range; let o = { col: start.character + 1, lnum: start.line + 1, text: name, level, kind: convert_1.getSymbolKind(kind), range: location.range, containerName }; res.push(o); pre = o; } } return res; } async rename(newName) { let { nvim } = this; let buf = await nvim.buffer; let doc = workspace_1.default.getDocument(buf.id); let position = await workspace_1.default.getCursorPosition(); if (!doc) return false; let res = await languages_1.default.prepareRename(doc.textDocument, position); if (res === false) { workspace_1.default.showMessage('Invalid position for rename', 'error'); return false; } doc.forceSync(); let curname; if (res == null) { let range = doc.getWordRangeAtPosition(position); if (range) curname = doc.textDocument.getText(range); } else { if (vscode_languageserver_protocol_1.Range.is(res)) { let line = doc.getline(res.start.line); curname = line.slice(res.start.character, res.end.character); } else { curname = res.placeholder; } } if (!curname) { workspace_1.default.showMessage('Invalid position', 'warning'); return false; } if (!newName) { newName = await workspace_1.default.callAsync('input', ['new name:', curname]); nvim.command('normal! :<C-u>', true); if (!newName) { workspace_1.default.showMessage('Empty word, canceled', 'warning'); return false; } } let edit = await languages_1.default.provideRenameEdits(doc.textDocument, position, newName); if (!edit) { workspace_1.default.showMessage('Server return empty response for rename', 'warning'); return false; } await workspace_1.default.applyEdit(edit); return true; } async documentFormatting() { let document = await workspace_1.default.document; if (!document) return false; let options = await workspace_1.default.getFormatOptions(document.uri); let textEdits = await languages_1.default.provideDocumentFormattingEdits(document.textDocument, options); if (!textEdits || textEdits.length == 0) return false; await document.applyEdits(this.nvim, textEdits); return true; } async documentRangeFormatting(mode) { let document = await workspace_1.default.document; if (!document) return -1; let range; if (mode) { range = await workspace_1.default.getSelectedRange(mode, document); if (!range) return -1; } else { let lnum = await this.nvim.getVvar('lnum'); let count = await this.nvim.getVvar('count'); let mode = await this.nvim.call('mode'); // we can't handle if (count == 0 || mode == 'i' || mode == 'R') return -1; range = vscode_languageserver_protocol_1.Range.create(lnum - 1, 0, lnum - 1 + count, 0); } let options = await workspace_1.default.getFormatOptions(document.uri); let textEdits = await languages_1.default.provideDocumentRangeFormattingEdits(document.textDocument, range, options); if (!textEdits) return -1; await document.applyEdits(this.nvim, textEdits); return 0; } async runCommand(id, ...args) { if (id) { await events_1.default.fire('Command', [id]); let res = await commands_1.default.executeCommand(id, ...args); await this.nvim.command(`silent! call repeat#set("\\<Plug>(coc-command-repeat)", -1)`); return res; } else { await manager_2.default.start(['commands']); } } async getCodeActions(bufnr, range, only) { let document = workspace_1.default.getDocument(bufnr); if (!document) return []; if (!range) { let lnum = await this.nvim.call('line', ['.']); range = { start: { line: lnum - 1, character: 0 }, end: { line: lnum, character: 0 } }; } let diagnostics = manager_1.default.getDiagnosticsInRange(document.textDocument, range); let context = { diagnostics }; if (only && Array.isArray(only)) context.only = only; let codeActionsMap = await languages_1.default.getCodeActions(document.textDocument, range, context); if (!codeActionsMap) return []; let codeActions = []; for (let clientId of codeActionsMap.keys()) { let actions = codeActionsMap.get(clientId); for (let action of actions) { codeActions.push(Object.assign({ clientId }, action)); } } codeActions.sort((a, b) => { if (a.isPrefered && !b.isPrefered) { return -1; } if (b.isPrefered && !a.isPrefered) { return 1; } return 0; }); return codeActions; } async doCodeAction(mode, only) { let bufnr = await this.nvim.call('bufnr', '%'); let range; if (mode) range = await workspace_1.default.getSelectedRange(mode, workspace_1.default.getDocument(bufnr)); let codeActions = await this.getCodeActions(bufnr, range, Array.isArray(only) ? only : null); if (!codeActions || codeActions.length == 0) { workspace_1.default.showMessage('No action available', 'warning'); return; } if (only && typeof only == 'string') { let action = codeActions.find(o => o.title == only || (o.command && o.command.title == only)); if (!action) return workspace_1.default.showMessage(`action "${only}" not found.`, 'warning'); await this.applyCodeAction(action); } else { let idx = await workspace_1.default.showQuickpick(codeActions.map(o => o.title)); if (idx == -1) return; let action = codeActions[idx]; if (action) await this.applyCodeAction(action); } } /** * Get current codeActions * * @public * @returns {Promise<CodeAction[]>} */ async getCurrentCodeActions(mode, only) { let bufnr = await this.nvim.call('bufnr', '%'); let document = workspace_1.default.getDocument(bufnr); if (!document) return []; let range; if (mode) range = await workspace_1.default.getSelectedRange(mode, workspace_1.default.getDocument(bufnr)); return await this.getCodeActions(bufnr, range, only); } async doQuickfix() { let actions = await this.getCurrentCodeActions(null, [vscode_languageserver_protocol_1.CodeActionKind.QuickFix]); if (!actions || actions.length == 0) { workspace_1.default.showMessage('No quickfix action available', 'warning'); return false; } await this.applyCodeAction(actions[0]); await this.nvim.command(`silent! call repeat#set("\\<Plug>(coc-fix-current)", -1)`); return true; } async applyCodeAction(action) { let { command, edit } = action; if (edit) await workspace_1.default.applyEdit(edit); if (command) { if (commands_1.default.has(command.command)) { commands_1.default.execute(command); } else { let clientId = action.clientId; let service = services_1.default.getService(clientId); let params = { command: command.command, arguments: command.arguments }; if (service.client) { let { client } = service; client .sendRequest(vscode_languageserver_protocol_1.ExecuteCommandRequest.type, params) .then(undefined, error => { workspace_1.default.showMessage(`Execute '${command.command} error: ${error}'`, 'error'); }); } } } } async doCodeLensAction() { await this.codeLensManager.doAction(); } async fold(kind) { let document = await workspace_1.default.document; let win = await this.nvim.window; let foldmethod = await win.getOption('foldmethod'); if (foldmethod != 'manual') { workspace_1.default.showMessage('foldmethod option should be manual!', 'warning'); return false; } let ranges = await languages_1.default.provideFoldingRanges(document.textDocument, {}); if (ranges == null) { workspace_1.default.showMessage('no range provider found', 'warning'); return false; } if (!ranges || ranges.length == 0) { workspace_1.default.showMessage('no range found', 'warning'); return false; } if (kind) { ranges = ranges.filter(o => o.kind == kind); } if (ranges && ranges.length) { await win.setOption('foldenable', true); for (let range of ranges.reverse()) { let { startLine, endLine } = range; let cmd = `${startLine + 1}, ${endLine + 1}fold`; this.nvim.command(cmd, true); } return true; } return false; } async pickColor() { await this.colors.pickColor(); } async pickPresentation() { await this.colors.pickPresentation(); } async highlight() { let bufnr = await this.nvim.call('bufnr', '%'); await this.documentHighlighter.highlight(bufnr); } async links() { let doc = await workspace_1.default.document; let links = await languages_1.default.getDocumentLinks(doc.textDocument); links = links || []; let res = []; for (let link of links) { if (link.target) { res.push(link); } else { link = await languages_1.default.resolveDocumentLink(link); res.push(link); } } return links; } async openLink() { let { document, position } = await workspace_1.default.getCurrentState(); let links = await languages_1.default.getDocumentLinks(document); if (!links || links.length == 0) return false; for (let link of links) { if (position_1.positionInRange(position, link.range)) { let { target } = link; if (!target) { link = await languages_1.default.resolveDocumentLink(link); target = link.target; } if (target) { await workspace_1.default.openResource(target); return true; } return false; } } return false; } async getCommands() { let list = commands_1.default.commandList; let res = []; let { titles } = commands_1.default; for (let item of list) { res.push({ id: item.id, title: titles.get(item.id) || '' }); } return res; } async selectFunction(inner, visualmode) { let { nvim } = this; let [bufnr, mode] = await nvim.eval(`[bufnr('%'), mode()]`); let doc = workspace_1.default.getDocument(bufnr); if (!doc) return; let range; if (visualmode) { range = await workspace_1.default.getSelectedRange(visualmode, doc); } else { let pos = await workspace_1.default.getCursorPosition(); range = vscode_languageserver_protocol_1.Range.create(pos, pos); } let symbols = await this.getDocumentSymbols(doc); if (!symbols || symbols.length === 0) { workspace_1.default.showMessage('No symbols found', 'warning'); return; } let properties = symbols.filter(s => s.kind == 'Property'); symbols = symbols.filter(s => [ 'Method', 'Function', ].includes(s.kind)); let selectRange; for (let sym of symbols.reverse()) { if (sym.range && !object_1.equals(sym.range, range) && position_1.rangeInRange(range, sym.range)) { selectRange = sym.range; break; } } if (!selectRange) { for (let sym of properties) { if (sym.range && !object_1.equals(sym.range, range) && position_1.rangeInRange(range, sym.range)) { selectRange = sym.range; break; } } } if (inner && selectRange) { let { start, end } = selectRange; let line = doc.getline(start.line + 1); let endLine = doc.getline(end.line - 1); selectRange = vscode_languageserver_protocol_1.Range.create(start.line + 1, line.match(/^\s*/)[0].length, end.line - 1, endLine.length); } if (selectRange) await workspace_1.default.selectRange(selectRange); } async onCharacterType(ch, bufnr, insertLeave = false) { if (!ch || string_1.isWord(ch) || !this.preferences.formatOnType) return; if (manager_3.default.getSession(bufnr) != null) return; let doc = workspace_1.default.getDocument(bufnr); if (!doc || doc.paused) return; if (!languages_1.default.hasOnTypeProvider(ch, doc.textDocument)) return; let position = await workspace_1.default.getCursorPosition(); let origLine = doc.getline(position.line); let { changedtick, dirty } = doc; if (dirty) { doc.forceSync(); await util_1.wait(50); } let pos = insertLeave ? { line: position.line + 1, character: 0 } : position; try { let edits = await languages_1.default.provideDocumentOntTypeEdits(ch, doc.textDocument, pos); // changed by other process if (doc.changedtick != changedtick) return; if (insertLeave) { edits = edits.filter(edit => { return edit.range.start.line < position.line + 1; }); } if (edits && edits.length) { await doc.applyEdits(this.nvim, edits); let newLine = doc.getline(position.line); if (newLine.length > origLine.length) { let character = position.character + (newLine.length - origLine.length); await workspace_1.default.moveTo(vscode_languageserver_protocol_1.Position.create(position.line, character)); } } } catch (e) { if (!/timeout\s/.test(e.message)) { console.error(`Error on formatOnType: ${e.message}`); // tslint:disable-line } } } async triggerSignatureHelp(document, position) { if (this.signatureTokenSource) { this.signatureTokenSource.cancel(); this.signatureTokenSource = null; } let part = document.getline(position.line).slice(0, position.character); if (part.endsWith(')')) { this.signatureFactory.close(); return; } let idx = Math.max(part.lastIndexOf(','), part.lastIndexOf('(')); if (idx != -1) position.character = idx + 1; let tokenSource = this.signatureTokenSource = new vscode_languageserver_protocol_1.CancellationTokenSource(); let token = tokenSource.token; let timer = setTimeout(() => { if (!token.isCancellationRequested) { tokenSource.cancel(); } }, 3000); let signatureHelp = await languages_1.default.getSignatureHelp(document.textDocument, position, token); clearTimeout(timer); if (token.isCancellationRequested || !signatureHelp || signatureHelp.signatures.length == 0) { this.signatureFactory.close(); return false; } let { activeParameter, activeSignature, signatures } = signatureHelp; if (activeSignature) { // make active first let [active] = signatures.splice(activeSignature, 1); if (active) signatures.unshift(active); } if (this.preferences.signatureHelpTarget == 'float') { let paramDoc = null; let docs = signatures.reduce((p, c, idx) => { let activeIndexes = null; let nameIndex = c.label.indexOf('('); if (idx == 0 && activeParameter != null) { let active = c.parameters[activeParameter]; if (active) { let after = c.label.slice(nameIndex == -1 ? 0 : nameIndex); paramDoc = active.documentation; if (typeof active.label === 'string') { let str = after.slice(0); let ms = str.match(new RegExp('\\b' + active.label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\b')); let index = ms ? ms.index : str.indexOf(active.label); if (index != -1) { activeIndexes = [ index + nameIndex, index + active.label.length + nameIndex ]; } } else { activeIndexes = active.label; } } } if (activeIndexes == null) { activeIndexes = [nameIndex + 1, nameIndex + 1]; } p.push({ content: c.label, filetype: document.filetype, active: activeIndexes }); if (paramDoc) { let content = typeof paramDoc === 'string' ? paramDoc : paramDoc.value; if (content.trim().length) { p.push({ content, filetype: vscode_languageserver_protocol_1.MarkupContent.is(c.documentation) ? 'markdown' : 'txt' }); } } if (idx == 0 && c.documentation) { let { documentation } = c; let content = typeof documentation === 'string' ? documentation : documentation.value; if (content.trim().length) { p.push({ content, filetype: vscode_languageserver_protocol_1.MarkupContent.is(c.documentation) ? 'markdown' : 'txt' }); } } return p; }, []); let offset = 0; let session = manager_3.default.getSession(document.bufnr); if (session && session.isActive) { let { value } = session.placeholder; if (value.indexOf('\n') == -1) offset = value.length; } this.signaturePosition = position; await this.signatureFactory.create(docs, true, offset); // show float } else { let columns = workspace_1.default.env.columns; signatures = signatures.slice(0, workspace_1.default.env.cmdheight); let signatureList = []; for (let signature of signatures) { let parts = []; let { label } = signature; label = label.replace(/\n/g, ' '); if (label.length >= columns - 16) { label = label.slice(0, columns - 16) + '...'; } let nameIndex = label.indexOf('('); if (nameIndex == -1) { parts = [{ text: label, type: 'Normal' }]; } else { parts.push({ text: label.slice(0, nameIndex), type: 'Label' }); let after = label.slice(nameIndex); if (signatureList.length == 0 && activeParameter != null) { let active = signature.parameters[activeParameter]; if (active) { let start; let end; if (typeof active.label === 'string') { let str = after.slice(0); let ms = str.match(new RegExp('\\b' + active.label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\b')); let idx = ms ? ms.index : str.indexOf(active.label); if (idx == -1) { parts.push({ text: after, type: 'Normal' }); } else { start = idx; end = idx + active.label.length; } } else { [start, end] = active.label; start = start - nameIndex; end = end - nameIndex; } if (start != null && end != null) { parts.push({ text: after.slice(0, start), type: 'Normal' }); parts.push({ text: after.slice(start, end), type: 'MoreMsg' }); parts.push({ text: after.slice(end), type: 'Normal' }); } } } else { parts.push({ text: after, type: 'Normal' }); } } signatureList.push(parts); } this.nvim.callTimer('coc#util#echo_signatures', [signatureList], true); } return true; } async showSignatureHelp() { let buffer = await this.nvim.buffer; let document = workspace_1.default.getDocument(buffer.id); if (!document) return false; let position = await workspace_1.default.getCursorPosition(); return await this.triggerSignatureHelp(document, position); } async handleLocations(definition, openCommand) { if (!definition) return; let locations = Array.isArray(definition) ? definition : [definition]; let len = locations.length; if (len == 0) return; if (len == 1 && openCommand !== false) { let location = definition[0]; if (vscode_languageserver_protocol_1.LocationLink.is(definition[0])) { let link = definition[0]; location = vscode_languageserver_protocol_1.Location.create(link.targetUri, link.targetRange); } let { uri, range } = location; await workspace_1.default.jumpTo(uri, range.start, openCommand); } else { await workspace_1.default.showLocations(definition); } } async getSelectionRanges() { let { document, position } = await workspace_1.default.getCurrentState(); let selectionRanges = await languages_1.default.getSelectionRanges(document, [position]); if (selectionRanges && selectionRanges.length) return selectionRanges; return null; } async selectRange(visualmode, forward) { let { nvim } = this; let positions = []; let bufnr = await nvim.call('bufnr', '%'); let doc = workspace_1.default.getDocument(bufnr); if (!doc) return; if (!forward && (!this.selectionRange || !visualmode)) return; if (visualmode) { let range = await workspace_1.default.getSelectedRange(visualmode, doc); positions.push(range.start, range.end); } else { let position = await workspace_1.default.getCursorPosition(); positions.push(position); } if (!forward) { let curr = vscode_languageserver_protocol_1.Range.create(positions[0], positions[1]); let { selectionRange } = this; while (selectionRange && selectionRange.parent) { if (object_1.equals(selectionRange.parent.range, curr)) { break; } selectionRange = selectionRange.parent; } if (selectionRange && selectionRange.parent) { await workspace_1.default.selectRange(selectionRange.range); } return; } let selectionRanges = await languages_1.default.getSelectionRanges(doc.textDocument, positions); if (selectionRanges == null) { workspace_1.default.showMessage('Selection range provider not found for current document', 'warning'); return; } if (!selectionRanges || selectionRanges.length == 0) { workspace_1.default.showMessage('No selection range found', 'warning'); return; } let mode = await nvim.eval('mode()'); if (mode != 'n') await nvim.eval(`feedkeys("\\<Esc>", 'in')`); let selectionRange; if (selectionRanges.length == 1) { selectionRange = selectionRanges[0]; } else if (positions.length > 1) { let r = vscode_languageserver_protocol_1.Range.create(positions[0], positions[1]); selectionRange = selectionRanges[0]; while (selectionRange) { if (object_1.equals(r, selectionRange.range)) { selectionRange = selectionRange.parent; continue; } if (position_1.positionInRange(positions[1], selectionRange.range) == 0) { break; } selectionRange = selectionRange.parent; } } if (!selectionRange) return; this.selectionRange = selectionRanges[0]; await workspace_1.default.selectRange(selectionRange.range); } async codeActionRange(start, end, only) { let listArgs = ['--normal', '--number-select', 'actions', `-start`, start + '', `-end`, end + '']; if (only == 'quickfix') { listArgs.push('-quickfix'); } else if (only == 'source') { listArgs.push('-source'); } await manager_2.default.start(listArgs); } /** * Refactor of current symbol */ async doRefactor() { await this.refactor.start(); } async saveRefactor(bufnr) { await this.refactor.saveRefactor(bufnr); } async previewHover(hovers) { let lines = []; let target = this.preferences.hoverTarget; let i = 0; let docs = []; for (let hover of hovers) { let { contents } = hover; if (i > 0) lines.push('---'); if (Array.isArray(contents)) { for (let item of contents) { if (typeof item === 'string') { if (item.trim().length) { lines.push(...item.split('\n')); docs.push({ content: item, filetype: 'markdown' }); } } else { let content = item.value.trim(); if (target == 'preview') { content = '``` ' + item.language + '\n' + content + '\n```'; } lines.push(...content.trim().split('\n')); docs.push({ filetype: item.language, content: item.value }); } } } else if (typeof contents == 'string') { lines.push(...contents.split('\n')); docs.push({ content: contents, filetype: 'markdown' }); } else if (vscode_languageserver_protocol_1.MarkedString.is(contents)) { // tslint:disable-line let content = contents.value.trim(); if (target == 'preview') { content = '``` ' + contents.language + '\n' + content + '\n```'; } lines.push(...content.split('\n')); docs.push({ filetype: contents.language, content: contents.value }); } else if (vscode_languageserver_protocol_1.MarkupContent.is(contents)) { lines.push(...contents.value.split('\n')); docs.push({ filetype: contents.kind == 'markdown' ? 'markdown' : 'txt', content: contents.value }); } i++; } if (target == 'echo') { const msg = lines.join('\n').trim(); if (msg.length) { await this.nvim.call('coc#util#echo_hover', msg); } } else if (target == 'float') { manager_1.default.hideFloat(); await this.hoverFactory.create(docs); } else { this.documentLines = lines; let arr = await this.nvim.call('getcurpos'); this.hoverPosition = [workspace_1.default.bufnr, arr[1], arr[2]]; await this.nvim.command(`pedit coc://document`); } } getPreferences() { let config = workspace_1.default.getConfiguration('coc.preferences'); let signatureConfig = workspace_1.default.getConfiguration('signature'); let hoverTarget = config.get('hoverTarget', 'float'); if (hoverTarget == 'float' && !workspace_1.default.env.floating && !workspace_1.default.env.textprop) { hoverTarget = 'preview'; } let signatureHelpTarget = signatureConfig.get('target', 'float'); if (signatureHelpTarget == 'float' && !workspace_1.default.env.floating && !workspace_1.default.env.textprop) { signatureHelpTarget = 'echo'; } this.labels = workspace_1.default.getConfiguration('suggest').get('completionItemKindLabels', {}); this.preferences = { hoverTarget, signatureHelpTarget, signatureMaxHeight: signatureConfig.get('maxWindowHeight', 8), triggerSignatureHelp: signatureConfig.get('enable', true), triggerSignatureWait: signatureConfig.get('triggerSignatureWait', 50), signaturePreferAbove: signatureConfig.get('preferShownAbove', true), signatureFloatMaxWidth: signatureConfig.get('floatMaxWidth', 80), signatureHideOnChange: signatureConfig.get('hideOnTextChange', false), formatOnType: config.get('formatOnType', false), bracketEnterImprove: config.get('bracketEnterImprove', true), previewAutoClose: config.get('previewAutoClose', false), currentFunctionSymbolAutoUpdate: config.get('currentFunctionSymbolAutoUpdate', false), }; } onEmptyLocation(name, location) { if (location == null) { workspace_1.default.showMessage(`${name} provider not found for current document`, 'warning'); } else if (location.length == 0) { workspace_1.default.showMessage(`${name} not found`, 'warning'); } } dispose() { this.colors.dispose(); util_1.disposeAll(this.disposables); } } exports.default = Handler; function getPreviousContainer(containerName, symbols) { if (!symbols.length) return null; let i = symbols.length - 1; let last = symbols[i]; if (last.text == containerName) { return last; } while (i >= 0) { let sym = symbols[i]; if (sym.text == containerName) { return sym; } i--; } return null; } function sortDocumentSymbols(a, b) { let ra = a.selectionRange; let rb = b.selectionRange; if (ra.start.line < rb.start.line) { return -1; } if (ra.start.line > rb.start.line) { return 1; } return ra.start.character - rb.start.character; } function addDoucmentSymbol(res, sym, level) { let { name, selectionRange, kind, children, range } = sym; let { start } = selectionRange; res.push({ col: start.character + 1, lnum: start