coc.nvim
Version:
LSP based intellisense engine for neovim & vim8.
226 lines • 9.64 kB
JavaScript
;
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