UNPKG

@chemzqm/neovim

Version:

NodeJS client API for vim9 and neovim

415 lines (411 loc) 15.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Buffer = void 0; const constants_1 = require("../utils/constants"); const Base_1 = require("./Base"); class Buffer extends Base_1.BaseApi { constructor() { super(...arguments); this.prefix = 'nvim_buf_'; } /** * Attach to buffer to listen to buffer events * @param sendBuffer Set to true if the initial notification should contain * the whole buffer. If so, the first notification will be a * `nvim_buf_lines_event`. Otherwise, the first notification will be * a `nvim_buf_changedtick_event` */ async attach(sendBuffer = false, options = {}) { return await this.request(`${this.prefix}attach`, [sendBuffer, options]); } /** * Detach from buffer to stop listening to buffer events */ async detach() { return await this.request(`${this.prefix}detach`, []); } /** Retrieves a scoped option depending on type of `this` */ getOption(name) { if (constants_1.isCocNvim) return this.request(`nvim_get_option_value`, [name, { buf: this.id }], true); return super.getOption(name); } setOption(name, value, isNotify) { if (constants_1.isCocNvim) return this[isNotify ? 'notify' : 'request'](`nvim_set_option_value`, [name, value, { buf: this.id }], true); return this[isNotify ? 'notify' : 'request'](`${this.prefix}set_option`, [name, value]); } /** * Get the bufnr of Buffer */ get id() { return this.data; } /** Total number of lines in buffer */ get length() { return this.request(`${this.prefix}line_count`, []); } /** Get lines in buffer */ get lines() { return this.getLines(); } /** Gets a changed tick of a buffer */ get changedtick() { return this.request(`${this.prefix}get_changedtick`, []); } get commands() { return this.getCommands(); } getCommands(options = {}) { return this.request(`${this.prefix}get_commands`, [options]); } /** Get specific lines of buffer */ getLines({ start, end, strictIndexing } = { start: 0, end: -1, strictIndexing: true }) { const indexing = typeof strictIndexing === 'undefined' ? true : strictIndexing; return this.request(`${this.prefix}get_lines`, [ start, end, indexing, ]); } setLines(lines, opts, notify = false) { let { start, end, strictIndexing } = opts !== null && opts !== void 0 ? opts : {}; start = start !== null && start !== void 0 ? start : 0; end = end !== null && end !== void 0 ? end : start + 1; const indexing = strictIndexing !== null && strictIndexing !== void 0 ? strictIndexing : true; const method = notify ? 'notify' : 'request'; return this[method](`${this.prefix}set_lines`, [ start, end, indexing, typeof lines === 'string' ? [lines] : lines ]); } /** * Set virtual text for a line, works on nvim >= 0.5.0 and vim9 * * @public * @param {number} src_id - Source group to use or 0 to use a new group, or -1 * @param {number} line - Line to annotate with virtual text (zero-indexed) * @param {Chunk[]} chunks - List with [text, hl_group] * @param {{[index} opts * @returns {Promise<number>} */ setVirtualText(src_id, line, chunks, opts = {}) { this.client.call('coc#vtext#add', [this.id, src_id, line, chunks, opts], true); return Promise.resolve(src_id); } /** * Removes an ext mark by notification. * * @public * @param {number} ns_id - Namespace id * @param {number} id - Extmark id */ deleteExtMark(ns_id, id) { this.notify(`${this.prefix}del_extmark`, [ ns_id, id, ]); } /** * Gets the position (0-indexed) of an extmark. * * @param {number} ns_id - Namespace id * @param {number} id - Extmark id * @param {Object} opts - Optional parameters. * @returns {Promise<[] | [number, number] | [number, number, ExtmarkDetails]>} */ async getExtMarkById(ns_id, id, opts = {}) { return this.request(`${this.prefix}get_extmark_by_id`, [ns_id, id, opts]); } /** * Gets extmarks in "traversal order" from a |charwise| region defined by * buffer positions (inclusive, 0-indexed |api-indexing|). * * Region can be given as (row,col) tuples, or valid extmark ids (whose * positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1) * respectively, thus the following are equivalent: * * nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) * nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {}) * * @param {number} ns_id - Namespace id * @param {[number, number] | number} start * @param {[number, number] | number} end * @param {Object} opts * @returns {Promise<[number, number, number, ExtmarkDetails?][]>} */ async getExtMarks(ns_id, start, end, opts = {}) { return this.request(`${this.prefix}get_extmarks`, [ns_id, start, end, opts]); } /** * Creates or updates an extmark by notification, `:h nvim_buf_set_extmark`. * * @param {number} ns_id * @param {number} line * @param {number} col * @param {ExtmarkOptions} opts * @returns {void} */ setExtMark(ns_id, line, col, opts = {}) { this.notify(`${this.prefix}set_extmark`, [ ns_id, line, col, opts ]); } /** Insert lines at `start` index */ insert(lines, start) { return this.setLines(lines, { start, end: start, strictIndexing: true, }); } /** Replace lines starting at `start` index */ replace(_lines, start) { const lines = typeof _lines === 'string' ? [_lines] : _lines; return this.setLines(lines, { start, end: start + lines.length, strictIndexing: false, }); } /** Remove lines at index */ remove(start, end, strictIndexing = false) { return this.setLines([], { start, end, strictIndexing }); } /** Append a string or list of lines to end of buffer */ append(lines) { return this.setLines(lines, { start: -1, end: -1, strictIndexing: false, }); } /** Get buffer name */ get name() { return this.request(`${this.prefix}get_name`, []); } /** Set current buffer name */ setName(value) { return this.request(`${this.prefix}set_name`, [value]); } /** Is current buffer valid */ get valid() { return this.request(`${this.prefix}is_valid`, []); } /** Get mark position given mark name */ mark(name) { return this.request(`${this.prefix}get_mark`, [name]); } // range(start, end) { // """Return a `Range` object, which represents part of the Buffer.""" // return Range(this, start, end) // } /** Gets keymap */ getKeymap(mode) { return this.request(`${this.prefix}get_keymap`, [mode]); } /** * Add buffer keymap by notification, replace keycodes for expr keymap enabled by default. */ setKeymap(mode, lhs, rhs, opts = {}) { let option = opts.expr ? Object.assign({ replace_keycodes: true }, opts) : opts; this.notify(`${this.prefix}set_keymap`, [mode, lhs, rhs, option]); } deleteKeymap(mode, lhs) { this.notify(`${this.prefix}del_keymap`, [mode, lhs]); } /** * Checks if a buffer is valid and loaded. See |api-buffer| for * more info about unloaded buffers. */ get loaded() { return this.request(`${this.prefix}is_loaded`, []); } /** * Returns the byte offset for a line. * * Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is * one byte. 'fileformat' and 'fileencoding' are ignored. The * line index just after the last line gives the total byte-count * of the buffer. A final EOL byte is counted if it would be * written, see 'eol'. * * Unlike |line2byte()|, throws error for out-of-bounds indexing. * Returns -1 for unloaded buffer. * * @return {Number} Integer byte offset, or -1 for unloaded buffer. */ getOffset(index) { return this.request(`${this.prefix}get_offset`, [index]); } /** Adds a highlight to buffer. This can be used for plugins which dynamically generate highlights to a buffer (like a semantic highlighter or linter). The function adds a single highlight to a buffer. Unlike matchaddpos() highlights follow changes to line numbering (as lines are inserted/removed above the highlighted line), like signs and marks do. "src_id" is useful for batch deletion/updating of a set of highlights. When called with src_id = 0, an unique source id is generated and returned. Succesive calls can pass in it as "src_id" to add new highlights to the same source group. All highlights in the same group can then be cleared with nvim_buf_clear_namespace. If the highlight never will be manually deleted pass in -1 for "src_id". If "hl_group" is the empty string no highlight is added, but a new src_id is still returned. This is useful for an external plugin to synchrounously request an unique src_id at initialization, and later asynchronously add and clear highlights in response to buffer changes. */ addHighlight({ hlGroup, line, colStart: _start, colEnd: _end, srcId: _srcId, }) { if (!hlGroup) throw new Error('hlGroup should not empty'); const colEnd = typeof _end !== 'undefined' ? _end : -1; const colStart = typeof _start !== 'undefined' ? _start : -0; const srcId = typeof _srcId !== 'undefined' ? _srcId : -1; const method = srcId == 0 ? 'request' : 'notify'; let res = this[method](`${this.prefix}add_highlight`, [ srcId, hlGroup, line, colStart, colEnd, ]); return method === 'request' ? res : Promise.resolve(null); } /** * Clear highlights of specified lins. * * @deprecated use clearNamespace() instead. */ clearHighlight(args = {}) { const defaults = { srcId: -1, lineStart: 0, lineEnd: -1, }; const { srcId, lineStart, lineEnd } = Object.assign({}, defaults, args); return this.notify(`${this.prefix}clear_highlight`, [ srcId, lineStart, lineEnd, ]); } /** * Add highlight to ranges by notification. * * @param {string | number} srcId Unique key or namespace number. * @param {string} hlGroup Highlight group. * @param {Range[]} ranges List of highlight ranges */ highlightRanges(srcId, hlGroup, ranges, option) { this.client.call('coc#highlight#ranges', [this.id, srcId, hlGroup, ranges, option !== null && option !== void 0 ? option : {}], true); } /** * Clear namespace by id or name. * * @param key Unique key or namespace number, use -1 for all namespaces * @param lineStart Start of line, 0 based, default to 0. * @param lineEnd End of line, 0 based, default to -1. */ clearNamespace(key, lineStart = 0, lineEnd = -1) { this.client.call('coc#highlight#clear_highlight', [this.id, key, lineStart, lineEnd], true); } /** * Add sign to buffer by notification. * * @param {SignPlaceOption} sign * @returns {void} */ placeSign(sign) { let opts = { lnum: sign.lnum }; if (typeof sign.priority === 'number') opts.priority = sign.priority; this.client.call('sign_place', [sign.id || 0, sign.group || '', sign.name, this.id, opts], true); } /** * Unplace signs by notification */ unplaceSign(opts) { let details = { buffer: this.id }; if (opts.id != null) details.id = opts.id; this.client.call('sign_unplace', [opts.group || '', details], true); } /** * Get signs by group name or id and lnum. * * @param {SignPlacedOption} opts * @returns {Promise<SignItem[]>} */ async getSigns(opts) { let res = await this.client.call('sign_getplaced', [this.id, opts || {}]); return res[0].signs; } /** * Get highlight items by name space (end inclusive). * * @param {string} ns Namespace key. * @param {number} start 0 based line number. * @param {number} end 0 based line number. * @returns {Promise<HighlightItem[]>} */ async getHighlights(ns, start = 0, end = -1) { let res = []; let arr = await this.client.call('coc#highlight#get_highlights', [this.id, ns, start, end]); for (let item of arr) { res.push({ hlGroup: item[0], lnum: item[1], colStart: item[2], colEnd: item[3], id: item[4] }); } return res; } /** * Update highlight items by notification. * * @param {string | number} ns Namespace key or id. * @param {HighlightItem[]} highlights Highlight items. * @param {HighlightOption} opts Optional options. * @returns {void} */ updateHighlights(ns, highlights, opts = {}) { if (typeof opts === 'number') { this.client.logError('Bad option for buffer.updateHighlights()', new Error()); return; } let start = typeof opts.start === 'number' ? opts.start : 0; let end = typeof opts.end === 'number' ? opts.end : -1; let changedtick = typeof opts.changedtick === 'number' ? opts.changedtick : null; let priority = typeof opts.priority === 'number' ? opts.priority : null; let arr = highlights.map(o => [o.hlGroup, o.lnum, o.colStart, o.colEnd, o.combine === false ? 0 : 1, o.start_incl ? 1 : 0, o.end_incl ? 1 : 0]); if (start == 0 && end == -1) { this.client.call('coc#highlight#buffer_update', [this.id, ns, arr, priority, changedtick], true); return; } this.client.call('coc#highlight#update_highlights', [this.id, ns, arr, start, end, priority, changedtick], true); } /** * Listens to buffer for events */ listen(eventName, cb, disposables) { this.client.attachBufferEvent(this.id, eventName, cb); if (disposables) { disposables.push({ dispose: () => { this.client.detachBufferEvent(this.id, eventName, cb); } }); } } } exports.Buffer = Buffer;