UNPKG

coc.nvim

Version:

LSP based intellisense engine for neovim & vim8.

322 lines 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const fs_1 = tslib_1.__importDefault(require("fs")); const readline_1 = tslib_1.__importDefault(require("readline")); const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol"); const vscode_uri_1 = require("vscode-uri"); const util_1 = require("../util"); const position_1 = require("../util/position"); const string_1 = require("../util/string"); const workspace_1 = tslib_1.__importDefault(require("../workspace")); const configuration_1 = tslib_1.__importDefault(require("./configuration")); const logger = require('../util/logger')('list-basic'); class BasicList { constructor(nvim) { this.nvim = nvim; this.defaultAction = 'open'; this.actions = []; this.options = []; this.disposables = []; this.config = new configuration_1.default(); } get hlGroup() { return this.config.get('previewHighlightGroup', 'Search'); } get previewHeight() { return this.config.get('maxPreviewHeight', 12); } get splitRight() { return this.config.get('previewSplitRight', false); } parseArguments(args) { if (!this.optionMap) { this.optionMap = new Map(); for (let opt of this.options) { let parts = opt.name.split(/,\s*/g).map(s => s.replace(/\s+.*/g, '')); let name = opt.key ? opt.key : parts[parts.length - 1].replace(/^-/, ''); for (let p of parts) { this.optionMap.set(p, { name, hasValue: opt.hasValue }); } } } let res = {}; for (let i = 0; i < args.length; i++) { let arg = args[i]; let def = this.optionMap.get(arg); if (!def) { logger.error(`Option "${arg}" of "${this.name}" not found`); continue; } let value = true; if (def.hasValue) { value = args[i + 1] || ''; i = i + 1; } res[def.name] = value; } return res; } getConfig() { return workspace_1.default.getConfiguration(`list.source.${this.name}`); } addAction(name, fn, options) { this.createAction(Object.assign({ name, execute: fn }, options || {})); } addMultipleAction(name, fn, options) { this.createAction(Object.assign({ name, multiple: true, execute: fn }, options || {})); } addLocationActions() { this.createAction({ name: 'preview', execute: async (item, context) => { let loc = await this.convertLocation(item.location); await this.previewLocation(loc, context); } }); let { nvim } = this; this.createAction({ name: 'quickfix', multiple: true, execute: async (items) => { let quickfixItems = await Promise.all(items.map(item => { return this.convertLocation(item.location).then(loc => { return workspace_1.default.getQuickfixItem(loc); }); })); await nvim.call('setqflist', [quickfixItems]); let openCommand = await nvim.getVar('coc_quickfix_open_command'); nvim.command(typeof openCommand === 'string' ? openCommand : 'copen', true); } }); for (let name of ['open', 'tabe', 'drop', 'vsplit', 'split']) { this.createAction({ name, execute: async (item) => { await this.jumpTo(item.location, name == 'open' ? null : name); } }); } } async convertLocation(location) { if (typeof location == 'string') return vscode_languageserver_protocol_1.Location.create(location, vscode_languageserver_protocol_1.Range.create(0, 0, 0, 0)); if (vscode_languageserver_protocol_1.Location.is(location)) return location; let u = vscode_uri_1.URI.parse(location.uri); if (u.scheme != 'file') return vscode_languageserver_protocol_1.Location.create(location.uri, vscode_languageserver_protocol_1.Range.create(0, 0, 0, 0)); const rl = readline_1.default.createInterface({ input: fs_1.default.createReadStream(u.fsPath, { encoding: 'utf8' }), }); let match = location.line; let n = 0; let resolved = false; let line = await new Promise(resolve => { rl.on('line', line => { if (resolved) return; if (line.indexOf(match) !== -1) { rl.removeAllListeners(); rl.close(); resolved = true; resolve(line); return; } n = n + 1; }); rl.on('error', e => { this.nvim.errWriteLine(`Read ${u.fsPath} error: ${e.message}`); resolve(null); }); }); if (line != null) { let character = location.text ? line.indexOf(location.text) : 0; if (character == 0) character = line.match(/^\s*/)[0].length; let end = vscode_languageserver_protocol_1.Position.create(n, character + (location.text ? location.text.length : 0)); return vscode_languageserver_protocol_1.Location.create(location.uri, vscode_languageserver_protocol_1.Range.create(vscode_languageserver_protocol_1.Position.create(n, character), end)); } return vscode_languageserver_protocol_1.Location.create(location.uri, vscode_languageserver_protocol_1.Range.create(0, 0, 0, 0)); } async jumpTo(location, command) { if (typeof location == 'string') { await workspace_1.default.jumpTo(location, null, command); return; } let { range, uri } = await this.convertLocation(location); let position = range.start; if (position.line == 0 && position.character == 0 && position_1.comparePosition(position, range.end) == 0) { // allow plugin that remember position. position = null; } await workspace_1.default.jumpTo(uri, position, command); } createAction(action) { let { name } = action; let idx = this.actions.findIndex(o => o.name == name); // allow override if (idx !== -1) this.actions.splice(idx, 1); this.actions.push(action); } async previewLocation(location, context) { let { nvim } = this; let { uri, range } = location; let lineCount = Infinity; let doc = workspace_1.default.getDocument(location.uri); if (doc) lineCount = doc.lineCount; let height = Math.min(this.previewHeight, lineCount); let u = vscode_uri_1.URI.parse(uri); // handle different scheme if (u.scheme == 'untitled' || u.scheme == 'unknown') { let bufnr = parseInt(u.path, 10); let valid = await nvim.call('bufloaded', [bufnr]); let lnum = location.range.start.line + 1; if (valid) { let name = await nvim.call('bufname', [bufnr]); name = name || '[No Name]'; let filetype = await nvim.call('getbufvar', [bufnr, '&filetype']); let lines = await nvim.call('getbufline', [bufnr, 1, '$']); await this.preview({ bufname: name, sketch: true, filetype: filetype || 'txt', lnum, lines }, context); } else { await this.preview({ sketch: true, filetype: 'txt', lines: [] }, context); } return; } // check let filepath = u.scheme == 'file' ? u.fsPath : u.toString(); nvim.pauseNotification(); nvim.call('fnameescape', filepath, true); nvim.call('bufloaded', filepath, true); nvim.call('eval', `!empty(getwininfo(${context.window.id}))`, true); let [res, error] = await nvim.resumeNotification(); if (error) { logger.error(error); return; } // open previewwindow let { position } = context.options; let [escaped, exists, valid] = res; let lnum = range.start.line + 1; let winid = context.listWindow.id; nvim.pauseNotification(); nvim.command('pclose', true); if (this.splitRight || position == 'tab') { if (valid && this.splitRight) nvim.call('win_gotoid', [context.window.id], true); nvim.command(`silent belowright vs +setl\\ previewwindow ${escaped}`, true); } else { let mod = context.options.position == 'top' ? 'below' : 'above'; nvim.command(`silent ${mod} ${height}sp +setl\\ previewwindow ${escaped}`, true); } nvim.command(`exe ${lnum}`, true); nvim.command('setl winfixheight nofoldenable', true); // highlight range if (position_1.comparePosition(range.start, range.end) !== 0) { let arr = []; for (let i = range.start.line; i <= range.end.line; i++) { let curr = await workspace_1.default.getLine(uri, range.start.line); let sc = i == range.start.line ? range.start.character : 0; let ec = i == range.end.line ? range.end.character : curr.length; if (sc == ec) continue; arr.push(vscode_languageserver_protocol_1.Range.create(i, sc, i, ec)); } for (let r of arr) { let line = await workspace_1.default.getLine(uri, r.start.line); let start = string_1.byteIndex(line, r.start.character) + 1; let end = string_1.byteIndex(line, r.end.character) + 1; nvim.call('matchaddpos', [this.hlGroup, [[lnum, start, end - start]]], true); } } if (!exists) nvim.command('setl nobuflisted bufhidden=wipe', true); nvim.command('normal! zz', true); nvim.call('win_gotoid', [winid], true); if (workspace_1.default.isVim) nvim.command('redraw', true); let [, err] = await nvim.resumeNotification(); // tslint:disable-next-line: no-console if (err) console.error(`Error on ${err[0]}: ${err[1]} - ${err[2]}`); } async preview(options, context) { let { nvim } = this; let { bufname, filetype, sketch, lines, lnum } = options; if (!bufname) sketch = true; let mod = context.options.position == 'top' ? 'below' : 'above'; let height = Math.min(this.previewHeight, lines ? Math.max(lines.length, 1) : Infinity); let winid = context.listWindow.id; let valid = await context.window.valid; nvim.pauseNotification(); nvim.command('pclose', true); if (this.splitRight || context.options.position == 'tab') { if (valid && this.splitRight) nvim.call('win_gotoid', [context.window.id], true); if (bufname) { nvim.command(`silent belowright vs +setl\\ previewwindow ${bufname}`, true); } else { nvim.command(`silent belowright vnew +setl\\ previewwindow`, true); } } else { if (bufname) { nvim.command(`silent ${mod} ${height}sp +setl\\ previewwindow ${bufname}`, true); } else { nvim.command(`silent ${mod} ${height}new +setl\\ previewwindow`, true); } } if (lines && lines.length) { nvim.call('append', [0, lines], true); nvim.command('normal! Gdd', true); } nvim.command(`exe ${lnum || 1}`, true); nvim.command('setl winfixheight nomodifiable', true); if (sketch) nvim.command('setl buftype=nofile bufhidden=wipe nobuflisted', true); if (filetype == 'detect') { nvim.command('filetype detect', true); } else if (filetype) { nvim.command(`setf ${filetype}`, true); } if (lnum && lnum != 1) nvim.command('normal! zz', true); nvim.call('win_gotoid', [winid], true); if (workspace_1.default.isVim) nvim.command('redraw', true); let [, err] = await nvim.resumeNotification(); // tslint:disable-next-line: no-console if (err) console.error(`Error on ${err[0]}: ${err[1]} - ${err[2]}`); } getPreviewCommand(context) { let { position } = context.options; if (position == 'tab') return `belowright vs`; let mod = position == 'top' ? 'below' : 'above'; return `${mod} ${this.previewHeight}sp`; } doHighlight() { // noop } dispose() { util_1.disposeAll(this.disposables); } } exports.default = BasicList; //# sourceMappingURL=basic.js.map