UNPKG

coc.nvim

Version:

LSP based intellisense engine for neovim & vim8.

435 lines 16.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol"); const vscode_uri_1 = require("vscode-uri"); const ansiparse_1 = require("../util/ansiparse"); const diff_1 = require("../util/diff"); const fzy_1 = require("../util/fzy"); const score_1 = require("../util/score"); const string_1 = require("../util/string"); const workspace_1 = tslib_1.__importDefault(require("../workspace")); const uuidv1 = require("uuid/v1"); const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; const logger = require('../util/logger')('list-worker'); const controlCode = '\x1b'; // perform loading task class Worker { constructor(nvim, manager) { this.nvim = nvim; this.manager = manager; this.recentFiles = []; this._loading = false; this.task = null; this.totalItems = []; this._onDidChangeItems = new vscode_languageserver_protocol_1.Emitter(); this.onDidChangeItems = this._onDidChangeItems.event; let { prompt } = manager; prompt.onDidChangeInput(async () => { let { listOptions } = manager; let { interactive } = listOptions; if (this.timer) clearTimeout(this.timer); // reload or filter items if (interactive) { this.stop(); this.timer = setTimeout(async () => { await this.loadItems(); }, 100); } else if (this.length) { let wait = Math.max(Math.min(Math.floor(this.length / 200), 300), 50); this.timer = setTimeout(async () => { await this.drawItems(); }, wait); } }); } loadMru() { let mru = workspace_1.default.createMru('mru'); mru.load().then(files => { this.recentFiles = files; }, logError); } set loading(loading) { if (this._loading == loading) return; this._loading = loading; let { nvim } = this; if (loading) { this.interval = setInterval(async () => { let idx = Math.floor((new Date()).getMilliseconds() / 100); nvim.pauseNotification(); nvim.setVar('coc_list_loading_status', frames[idx], true); nvim.command('redraws', true); nvim.resumeNotification(false, true).logError(); }, 100); } else { if (this.interval) { clearInterval(this.interval); nvim.pauseNotification(); nvim.setVar('coc_list_loading_status', '', true); nvim.command('redraws', true); nvim.resumeNotification(false, true).logError(); } } } get isLoading() { return this._loading; } async loadItems(reload = false) { let { context, list, listOptions } = this.manager; if (!list) return; this.loadMru(); if (this.timer) clearTimeout(this.timer); let id = this.taskId = uuidv1(); this.loading = true; let { interactive } = listOptions; let source = this.tokenSource = new vscode_languageserver_protocol_1.CancellationTokenSource(); let token = source.token; let items = await list.loadItems(context, token); if (token.isCancellationRequested) return; if (!items || Array.isArray(items)) { items = (items || []); this.totalItems = items.map(item => { item.label = this.fixLabel(item.label); this.parseListItemAnsi(item); return item; }); this.loading = false; let highlights = []; if (!interactive) { let res = this.filterItems(items); items = res.items; highlights = res.highlights; } else { highlights = this.getItemsHighlight(items); } this._onDidChangeItems.fire({ items, highlights, reload }); } else { let task = this.task = items; let totalItems = this.totalItems = []; let count = 0; let currInput = context.input; let timer; let lastTs; let _onData = () => { lastTs = Date.now(); if (this.taskId != id || !this.manager.isActivated) return; if (count >= totalItems.length) return; let inputChanged = this.input != currInput; if (interactive && inputChanged) return; if (count == 0 || inputChanged) { currInput = this.input; count = totalItems.length; let items; let highlights = []; if (interactive) { items = totalItems.slice(); highlights = this.getItemsHighlight(items); } else { let res = this.filterItems(totalItems); items = res.items; highlights = res.highlights; } this._onDidChangeItems.fire({ items, highlights, reload, append: false }); } else { let remain = totalItems.slice(count); count = totalItems.length; let items; let highlights = []; if (!interactive) { let res = this.filterItems(remain); items = res.items; highlights = res.highlights; } else { items = remain; highlights = this.getItemsHighlight(remain); } this._onDidChangeItems.fire({ items, highlights, append: true }); } }; task.on('data', async (item) => { if (timer) clearTimeout(timer); if (this.taskId != id || !this._loading) return; if (interactive && this.input != currInput) return; item.label = this.fixLabel(item.label); this.parseListItemAnsi(item); totalItems.push(item); if (this.input != currInput) return; if ((!lastTs && totalItems.length == 500) || Date.now() - lastTs > 200) { _onData(); } else { timer = setTimeout(_onData, 50); } }); let disposable = token.onCancellationRequested(() => { this.loading = false; disposable.dispose(); if (timer) clearTimeout(timer); if (task == this.task) { task.dispose(); this.task = null; this.taskId = null; } }); task.on('error', async (error) => { this.loading = false; disposable.dispose(); if (timer) clearTimeout(timer); await this.manager.cancel(); workspace_1.default.showMessage(`Task error: ${error.toString()}`, 'error'); logger.error(error); }); task.on('end', async () => { this.loading = false; disposable.dispose(); if (timer) clearTimeout(timer); if (totalItems.length == 0) { this._onDidChangeItems.fire({ items: [], highlights: [] }); } else { _onData(); } }); } } // draw all items with filter if necessary async drawItems() { let { totalItems } = this; let { listOptions, isActivated } = this.manager; if (!isActivated) return; let { interactive } = listOptions; let items = totalItems; let highlights = []; if (!interactive) { let res = this.filterItems(totalItems); items = res.items; highlights = res.highlights; } else { highlights = this.getItemsHighlight(items); } this._onDidChangeItems.fire({ items, highlights }); } stop() { if (this.tokenSource) { this.tokenSource.cancel(); this.tokenSource = null; } this.loading = false; if (this.timer) { clearTimeout(this.timer); } if (this.task) { this.task.dispose(); this.task = null; this.taskId = null; } } get length() { return this.totalItems.length; } get input() { return this.manager.prompt.input; } getItemsHighlight(items) { let { input } = this; if (!input) return []; return items.map(item => { let filterLabel = getFilterLabel(item); if (filterLabel == '') return null; let res = score_1.getMatchResult(filterLabel, input); if (!res || !res.score) return null; return this.getHighlights(filterLabel, res.matches); }); } filterItems(items) { let { input } = this.manager.prompt; let highlights = []; let { sort, matcher, ignorecase } = this.manager.listOptions; if (input.length == 0) { let filtered = items.slice(); let sort = filtered.length && typeof filtered[0].recentScore == 'number'; return { items: sort ? filtered.sort((a, b) => b.recentScore - a.recentScore) : filtered, highlights }; } let extended = this.manager.getConfig('extendedSearchMode', true); let filtered; if (input.length > 0) { let inputs = extended ? input.split(/\s+/) : [input]; if (matcher == 'strict') { filtered = items.filter(item => { let spans = []; let filterLabel = getFilterLabel(item); for (let input of inputs) { let idx = ignorecase ? filterLabel.toLowerCase().indexOf(input.toLowerCase()) : filterLabel.indexOf(input); if (idx == -1) return false; spans.push([string_1.byteIndex(filterLabel, idx), string_1.byteIndex(filterLabel, idx + string_1.byteLength(input))]); } highlights.push({ spans }); return true; }); } else if (matcher == 'regex') { let flags = ignorecase ? 'iu' : 'u'; let regexes = inputs.reduce((p, c) => { try { let regex = new RegExp(c, flags); p.push(regex); // tslint:disable-next-line: no-empty } catch (e) { } return p; }, []); filtered = items.filter(item => { let spans = []; let filterLabel = getFilterLabel(item); for (let regex of regexes) { let ms = filterLabel.match(regex); if (ms == null) return false; spans.push([string_1.byteIndex(filterLabel, ms.index), string_1.byteIndex(filterLabel, ms.index + string_1.byteLength(ms[0]))]); } highlights.push({ spans }); return true; }); } else { filtered = items.filter(item => { let filterText = item.filterText || item.label; return inputs.every(s => fzy_1.hasMatch(s, filterText)); }); filtered = filtered.map(item => { let filterLabel = getFilterLabel(item); let matchScore = 0; let matches = []; for (let input of inputs) { matches.push(...fzy_1.positions(input, filterLabel)); matchScore += fzy_1.score(input, filterLabel); } let { recentScore } = item; if (!recentScore && item.location) { let uri = getItemUri(item); if (uri.startsWith('file')) { let fsPath = vscode_uri_1.URI.parse(uri).fsPath; recentScore = -this.recentFiles.indexOf(fsPath); } } return Object.assign({}, item, { filterLabel, score: matchScore, recentScore, matches }); }); if (sort && items.length) { filtered.sort((a, b) => { if (a.score != b.score) return b.score - a.score; if (input.length && a.recentScore != b.recentScore) { return (a.recentScore || -Infinity) - (b.recentScore || -Infinity); } if (a.location && b.location) { let au = getItemUri(a); let bu = getItemUri(b); return au > bu ? 1 : -1; } return a.label > b.label ? 1 : -1; }); } for (let item of filtered) { if (!item.matches) continue; let hi = this.getHighlights(item.filterLabel, item.matches); highlights.push(hi); } } } return { items: filtered, highlights }; } getHighlights(text, matches) { let spans = []; if (matches.length) { let start = matches.shift(); let next = matches.shift(); let curr = start; while (next) { if (next == curr + 1) { curr = next; next = matches.shift(); continue; } spans.push([string_1.byteIndex(text, start), string_1.byteIndex(text, curr) + 1]); start = next; curr = start; next = matches.shift(); } spans.push([string_1.byteIndex(text, start), string_1.byteIndex(text, curr) + 1]); } return { spans }; } // set correct label, add ansi highlights parseListItemAnsi(item) { let { label } = item; if (item.ansiHighlights || label.indexOf(controlCode) == -1) return; let { line, highlights } = ansiparse_1.parseAnsiHighlights(label); item.label = line; item.ansiHighlights = highlights; } fixLabel(label) { let { columns } = workspace_1.default.env; label = label.split('\n').join(' '); return label.slice(0, columns * 2); } } exports.default = Worker; function getFilterLabel(item) { return item.filterText != null ? diff_1.patchLine(item.filterText, item.label) : item.label; } function getItemUri(item) { let { location } = item; if (typeof location == 'string') return location; return location.uri; } function logError(e) { logger.error(e); } //# sourceMappingURL=worker.js.map