UNPKG

coc.nvim

Version:

LSP based intellisense engine for neovim & vim8.

329 lines 12.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol"); const events_1 = tslib_1.__importDefault(require("../events")); const manager_1 = tslib_1.__importDefault(require("../snippets/manager")); const util_1 = require("../util"); const object_1 = require("../util/object"); const workspace_1 = tslib_1.__importDefault(require("../workspace")); const floatBuffer_1 = tslib_1.__importDefault(require("./floatBuffer")); const debounce_1 = tslib_1.__importDefault(require("debounce")); const popup_1 = tslib_1.__importDefault(require("./popup")); const logger = require('../util/logger')('model-float'); // factory class for floating window class FloatFactory { constructor(nvim, env, preferTop = false, maxHeight = 999, maxWidth, autoHide = true) { this.nvim = nvim; this.env = env; this.preferTop = preferTop; this.maxHeight = maxHeight; this.maxWidth = maxWidth; this.autoHide = autoHide; this.disposables = []; this.alignTop = false; this.createTs = 0; this.cursor = [0, 0]; if (!env.floating && !env.textprop) return; this.maxWidth = Math.min(maxWidth || 80, this.columns - 10); events_1.default.on('BufEnter', bufnr => { if (this.buffer && bufnr == this.buffer.id) return; if (bufnr == this.targetBufnr) return; this.close(); }, null, this.disposables); events_1.default.on('InsertLeave', bufnr => { if (this.buffer && bufnr == this.buffer.id) return; if (manager_1.default.isActived(bufnr)) return; this.close(); }, null, this.disposables); events_1.default.on('MenuPopupChanged', async (ev, cursorline) => { if (cursorline < ev.row && !this.alignTop) { this.close(); } else if (cursorline > ev.row && this.alignTop) { this.close(); } }, null, this.disposables); events_1.default.on('CursorMoved', debounce_1.default(this.onCursorMoved.bind(this, false), 100), null, this.disposables); events_1.default.on('CursorMovedI', this.onCursorMoved.bind(this, true), null, this.disposables); } onCursorMoved(insertMode, bufnr, cursor) { if (!this.window || this.buffer && bufnr == this.buffer.id) return; if (bufnr == this.targetBufnr && object_1.equals(cursor, this.cursor)) return; if (this.autoHide) { this.close(); return; } if (!workspace_1.default.insertMode || bufnr != this.targetBufnr || (this.cursor && cursor[0] != this.cursor[0])) { this.close(); return; } } async checkFloatBuffer() { let { floatBuffer, nvim, window } = this; if (this.env.textprop) { let valid = await this.activated(); if (!valid) window = null; if (!window) { this.popup = await popup_1.default(nvim, [''], { padding: [0, 1, 0, 1], highlight: 'CocFloating', tab: -1, }); let win = this.window = nvim.createWindow(this.popup.id); nvim.pauseNotification(); win.setVar('float', 1, true); win.setOption('linebreak', true, true); win.setOption('showbreak', '', true); win.setOption('conceallevel', 2, true); await nvim.resumeNotification(); } let buffer = this.nvim.createBuffer(this.popup.bufferId); this.floatBuffer = new floatBuffer_1.default(nvim, buffer, nvim.createWindow(this.popup.id)); } else { if (floatBuffer) { let valid = await floatBuffer.valid; if (valid) return; } let buf = await this.nvim.createNewBuffer(false, true); await buf.setOption('buftype', 'nofile'); await buf.setOption('bufhidden', 'hide'); this.floatBuffer = new floatBuffer_1.default(this.nvim, buf); } } get columns() { return this.env.columns; } get lines() { return this.env.lines - this.env.cmdheight - 1; } async getBoundings(docs, offsetX = 0) { let { nvim, preferTop } = this; let { columns, lines } = this; let alignTop = false; let [row, col] = await nvim.call('coc#util#win_position'); let maxWidth = this.maxWidth; let height = this.floatBuffer.getHeight(docs, maxWidth); height = Math.min(height, this.maxHeight); if (!preferTop) { if (lines - row < height && row > height) { alignTop = true; } } else { if (row >= height || row >= lines - row) { alignTop = true; } } if (alignTop) docs.reverse(); await this.floatBuffer.setDocuments(docs, maxWidth); let { width } = this.floatBuffer; if (offsetX) { offsetX = Math.min(col - 1, offsetX); if (col - offsetX + width > columns) { offsetX = col - offsetX + width - columns; } } this.alignTop = alignTop; return { height: alignTop ? Math.max(1, Math.min(row, height)) : Math.max(1, Math.min(height, (lines - row))), width: Math.min(columns, width), row: alignTop ? -height : 1, col: offsetX == 0 ? 0 : -offsetX, relative: 'cursor' }; } async create(docs, allowSelection = false, offsetX = 0) { if (this.env.floating) { await this.createNvim(docs, allowSelection, offsetX); } else if (this.env.textprop) { await this.createVim(docs, allowSelection, offsetX); } } async createVim(docs, allowSelection = false, offsetX = 0) { if (docs.length == 0) { this.close(); return; } if (this.tokenSource) { this.tokenSource.cancel(); } this.createTs = Date.now(); this.targetBufnr = workspace_1.default.bufnr; let tokenSource = this.tokenSource = new vscode_languageserver_protocol_1.CancellationTokenSource(); let token = tokenSource.token; await this.checkFloatBuffer(); let config = await this.getBoundings(docs, offsetX); let [mode, line, col] = await this.nvim.eval('[mode(),line("."),col(".")]'); this.cursor = [line, col]; if (!config || token.isCancellationRequested) return this.popup.dispose(); allowSelection = mode == 's' && allowSelection; if (['i', 'n', 'ic'].indexOf(mode) !== -1 || allowSelection) { let { nvim, alignTop } = this; let reuse = false; let filetypes = docs.reduce((p, curr) => { p.add(curr.filetype); return p; }, new Set); nvim.pauseNotification(); let { popup, window } = this; this.popup.move({ line: cursorPostion(config.row), col: cursorPostion(config.col), minwidth: config.width - 2, minheight: config.height, maxwidth: config.width - 2, maxheight: config.height }); this.floatBuffer.setLines(); // nvim.call('win_execute', [window.id, `normal! G`], true) if (filetypes.size == 1) { this.popup.setFiletype(filetypes.values().next().value); } let [res, err] = await nvim.resumeNotification(); if (err) { workspace_1.default.showMessage(`Error on ${err[0]}: ${err[1]} - ${err[2]}`, 'error'); return; } if (mode == 's') await manager_1.default.selectCurrentPlaceholder(false); } } async createNvim(docs, allowSelection = false, offsetX = 0) { if (docs.length == 0) { this.close(); return; } if (this.tokenSource) { this.tokenSource.cancel(); } this.createTs = Date.now(); this.targetBufnr = workspace_1.default.bufnr; let tokenSource = this.tokenSource = new vscode_languageserver_protocol_1.CancellationTokenSource(); let token = tokenSource.token; await this.checkFloatBuffer(); let config = await this.getBoundings(docs, offsetX); let [mode, line, col] = await this.nvim.eval('[mode(),line("."),col(".")]'); this.cursor = [line, col]; if (!config || token.isCancellationRequested) return; allowSelection = mode == 's' && allowSelection; if (['i', 'n', 'ic'].indexOf(mode) !== -1 || allowSelection) { let { nvim, alignTop } = this; // change to normal if (mode == 's') await nvim.call('feedkeys', ['\x1b', 'in']); // helps to fix undo issue, don't know why. if (mode.startsWith('i')) await nvim.eval('feedkeys("\\<C-g>u", "n")'); let reuse = this.window && await this.window.valid; if (!reuse) this.window = await nvim.openFloatWindow(this.buffer, false, config); if (token.isCancellationRequested) return; nvim.pauseNotification(); if (!reuse) { nvim.command(`noa call win_gotoid(${this.window.id})`, true); this.window.setVar('float', 1, true); nvim.command(`setl nospell nolist wrap linebreak foldcolumn=1`, true); nvim.command(`setl nonumber norelativenumber nocursorline nocursorcolumn`, true); nvim.command(`setl signcolumn=no conceallevel=2`, true); nvim.command(`setl winhl=Normal:CocFloating,NormalNC:CocFloating,FoldColumn:CocFloating`, true); nvim.command(`silent doautocmd User CocOpenFloat`, true); } else { this.window.setConfig(config, true); nvim.command(`noa call win_gotoid(${this.window.id})`, true); } this.floatBuffer.setLines(); nvim.command(`normal! ${alignTop ? 'G' : 'gg'}0`, true); nvim.command('noa wincmd p', true); let [res, err] = await nvim.resumeNotification(); if (err) { workspace_1.default.showMessage(`Error on ${err[0]}: ${err[1]} - ${err[2]}`, 'error'); return; } if (mode == 's') await manager_1.default.selectCurrentPlaceholder(false); } } /** * Close float window */ close() { if (this.tokenSource) { this.tokenSource.cancel(); this.tokenSource = null; } if (this.popup) { this.popup.dispose(); } else { this.closeWindow(this.window); } } closeWindow(window) { if (!window) return; this.nvim.call('coc#util#close_win', window.id, true); this.window = null; let count = 0; let interval = setInterval(() => { count++; if (count == 5) clearInterval(interval); window.valid.then(valid => { if (valid) { this.nvim.call('coc#util#close_win', window.id, true); } else { clearInterval(interval); } }, _e => { clearInterval(interval); }); }, 200); } dispose() { if (this.tokenSource) { this.tokenSource.cancel(); } util_1.disposeAll(this.disposables); } get buffer() { return this.floatBuffer ? this.floatBuffer.buffer : null; } async activated() { if (this.env.textprop) { if (!this.popup) return false; return await this.popup.visible(); } if (!this.window) return false; let valid = await this.window.valid; return valid; } } exports.default = FloatFactory; function cursorPostion(n) { if (n == 0) return 'cursor'; if (n < 0) return `cursor${n}`; return `cursor+${n}`; } //# sourceMappingURL=floatFactory.js.map