UNPKG

coc.nvim

Version:

LSP based intellisense engine for neovim & vim8.

226 lines 9.64 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const fs_1 = tslib_1.__importDefault(require("fs")); const path_1 = tslib_1.__importDefault(require("path")); const vscode_languageserver_types_1 = require("vscode-languageserver-types"); const vscode_uri_1 = require("vscode-uri"); const languages_1 = tslib_1.__importDefault(require("../languages")); const highligher_1 = tslib_1.__importDefault(require("../model/highligher")); const fs_2 = require("../util/fs"); const workspace_1 = tslib_1.__importDefault(require("../workspace")); const logger = require('../util/logger')('refactor'); const name = '__coc_refactor__'; class Refactor { constructor() { this.id = 0; this.nvim = workspace_1.default.nvim; let config = workspace_1.default.getConfiguration('refactor'); this.config = { afterContext: config.get('afterContext', 3), beforeContext: config.get('beforeContext', 3), openCommand: config.get('openCommand', 'edit') }; } async start() { let [bufnr, cursor, winid] = await this.nvim.eval('[bufnr("%"),coc#util#cursor(),win_getid()]'); let doc = workspace_1.default.getDocument(bufnr); if (!doc) return; let position = { line: cursor[0], character: cursor[1] }; let res = await languages_1.default.prepareRename(doc.textDocument, position); if (res === false) { workspace_1.default.showMessage('Invalid position for rename', 'error'); return; } let curname; if (res == null) { let range = doc.getWordRangeAtPosition(position); if (range) curname = doc.textDocument.getText(range); } else { if (vscode_languageserver_types_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; } let edit = await languages_1.default.provideRenameEdits(doc.textDocument, position, 'newname'); if (!edit) { workspace_1.default.showMessage('Server return empty response', 'warning'); return; } let items = this.getFileItems(edit); await this.createRefactorWindow(items, curname, winid); } async createRefactorWindow(items, curname, winid) { let { nvim } = this; let highligher = new highligher_1.default(); highligher.addLine('Save current buffer to make changes', 'Comment'); highligher.addLine('—'); for (let item of items) { for (let range of item.ranges) { highligher.addLine('—'); highligher.addText(`${item.filepath}`, 'Label'); highligher.addText(':'); highligher.addText(String(range.start + 1), 'LineNr'); highligher.addText(':'); highligher.addText(String(range.end + 1), 'LineNr'); let start = highligher.length; let lines = await this.getLines(item.filepath, range.start, range.end); highligher.addLines(lines); highligher.addLine('—'); } } let { openCommand } = this.config; nvim.pauseNotification(); nvim.command(`${openCommand} ${name}${this.id++}`, true); nvim.command(`setl buftype=acwrite nobuflisted bufhidden=hide nofen wrap conceallevel=3 concealcursor=n`, true); nvim.call('bufnr', ['%'], true); nvim.call('matchadd', ['Conceal', '^—'], true); nvim.call('coc#util#do_autocmd', ['CocRefactorOpen'], true); let [res, err] = await nvim.resumeNotification(); if (err) { logger.error(err); workspace_1.default.showMessage(`Error on open refactor window: ${err}`, 'error'); return; } let buffer = nvim.createBuffer(res[2]); nvim.pauseNotification(); highligher.render(buffer); nvim.command('exe 1', true); nvim.command('setl nomod', true); nvim.command(`execute 'normal! /\\<'.escape('${curname.replace(/'/g, "''")}', '\\\\/.*$^~[]')."\\\\>\\<cr>"`, true); workspace_1.default.registerLocalKeymap('n', '<CR>', async () => { let currwin = await nvim.call('win_getid'); let lines = await nvim.eval('getline(3,line("."))'); let len = lines.length; for (let i = 0; i < len; i++) { let line = lines[len - i - 1]; let ms = line.match(/^—(.*?):(\d+):(\d+)/); if (ms) { let filepath = ms[1]; let start = parseInt(ms[2], 10); let lnum = i == 0 ? start : start + i - 1; let bufname = filepath.startsWith(workspace_1.default.cwd) ? path_1.default.relative(workspace_1.default.cwd, filepath) : filepath; nvim.pauseNotification(); nvim.call('win_gotoid', [winid], true); this.nvim.call('coc#util#jump', ['edit', bufname, [lnum, 1]], true); nvim.command('normal! zz', true); let [, err] = await nvim.resumeNotification(); if (err) workspace_1.default.showMessage(`Error on open ${filepath}: ${err}`, 'error'); break; } } }, true); await nvim.resumeNotification(); } getFileItems(edit) { let res = []; let { beforeContext, afterContext } = this.config; let { changes, documentChanges } = edit; changes = changes || {}; for (let change of documentChanges || []) { if (vscode_languageserver_types_1.TextDocumentEdit.is(change)) { let { textDocument, edits } = change; changes[textDocument.uri] = edits; } } for (let key of Object.keys(changes)) { let max = this.getLineCount(key); let edits = changes[key]; let ranges = []; // start end highlights let start = null; let end = null; let highlights = []; edits.sort((a, b) => a.range.start.line - b.range.start.line); for (let edit of edits) { let { line } = edit.range.start; let s = Math.max(0, line - beforeContext); if (start != null && s <= end) { end = Math.min(max, line + afterContext); highlights.push(adjustRange(edit.range, start)); } else { if (start != null) ranges.push({ start, end, highlights }); start = s; end = Math.min(max, line + afterContext); highlights = [adjustRange(edit.range, start)]; } } if (start != null) ranges.push({ start, end, highlights }); res.push({ ranges, filepath: vscode_uri_1.URI.parse(key).fsPath }); } return res; } getLineCount(uri) { let doc = workspace_1.default.getDocument(uri); if (doc) return doc.lineCount; let content = fs_1.default.readFileSync(vscode_uri_1.URI.parse(uri).fsPath, 'utf8'); return content.split(/\r?\n/).length; } async getLines(fsPath, start, end) { let uri = vscode_uri_1.URI.file(fsPath).toString(); let doc = workspace_1.default.getDocument(uri); if (doc) return doc.getLines(start, end + 1); return await fs_2.readFileLines(fsPath, start, end); } async saveRefactor(bufnr) { let { nvim } = this; let buffer = nvim.createBuffer(bufnr); let lines = await buffer.lines; let changes = {}; let arr = []; let uri; let start; let end; for (let line of lines.slice(2)) { if (line.startsWith('—') && line.length == 1 && uri) { let edits = changes[uri] || []; let r = vscode_languageserver_types_1.Range.create(start - 1, 0, end, 0); edits.push(vscode_languageserver_types_1.TextEdit.replace(r, arr.join('\n') + '\n')); changes[uri] = edits; arr = []; } else if (line.startsWith('—')) { let ms = line.match(/^—(.*?):(\d+):(\d+)/); if (ms) { uri = vscode_uri_1.URI.file(ms[1]).toString(); start = parseInt(ms[2], 10); end = parseInt(ms[3], 10); } else { arr.push(line); } } else { arr.push(line); } } await workspace_1.default.applyEdit({ changes }); nvim.command('setl nomod', true); nvim.command('noa wa', true); } } exports.default = Refactor; function adjustRange(range, offset) { let { start, end } = range; return vscode_languageserver_types_1.Range.create(start.line - offset, start.character, end.line - offset, end.character); } //# sourceMappingURL=refactor.js.map