UNPKG

@chemzqm/neovim

Version:

NodeJS client API for vim9 and neovim

193 lines (192 loc) 6.67 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.VimTransport = void 0; const constants_1 = require("../utils/constants"); const base_1 = __importDefault(require("./base")); const connection_1 = __importDefault(require("./connection")); const request_1 = __importDefault(require("./request")); const notifyMethod = constants_1.isCocNvim ? 'coc#api#Notify' : 'nvim#api#Notify'; class VimTransport extends base_1.default { constructor(logger) { super(logger, true); this.pending = new Map(); this.nextRequestId = -1; this.attached = false; /** * Cached error message */ this.errText = ''; /** * Cached out message */ this.outText = ''; } attach(writer, reader, client) { let connection = this.connection = new connection_1.default(reader, writer); this.attached = true; this.client = client; connection.on('request', (id, obj) => { let [method, args] = obj; this.emit('request', method, args, this.createResponse(method, id)); }); connection.on('notification', (obj) => { let [event, args] = obj; this.emit('notification', event.toString(), args); }); connection.on('response', (id, obj) => { let req = this.pending.get(id); if (req) { this.pending.delete(id); let err = null; let result = null; if (req.isDirect) { result = obj; } else { if (!Array.isArray(obj)) { err = obj; } else { err = obj[0]; result = obj[1]; } } req.callback(this.client, err, result); } }); } send(arr) { this.connection.send(arr); } detach() { if (!this.attached) return; this.attached = false; this.connection.dispose(); for (let req of this.pending.values()) { req.callback(this.client, 'connection disconnected', null); } this.pending.clear(); } vimCommand(command, ...args) { switch (command) { case 'expr': this.connection.expr(args[0]); break; case 'call': this.connection.call(args[0], args[1]); break; case 'ex': this.connection.ex(args[0]); break; case 'redraw': this.connection.redraw(args[0]); break; default: throw new Error(`command "${command}" not exists`); } } vimRequest(command, args) { if (!this.attached) return Promise.reject(new Error('transport disconnected')); Error.captureStackTrace(args); let id = this.nextRequestId; this.nextRequestId = this.nextRequestId - 1; return new Promise((resolve, reject) => { let req = new request_1.default(this.connection, (err, res) => { if (!err && res === 'ERROR') { if (command === 'eval') { err = new Error(`Invalid expression "${args[0]}", checkout v:errmsg`); } else { err = new Error(`Error on function "${args[0]}", checkout v:errmsg"`); } } if (err) { err.stack = `Error: vim "${command}" error - ${err}\n` + args['stack'].split(/\r?\n/).slice(3).join('\n'); this.client.logError(`Error on vim command "${command}"`, args, err); reject(err); return; } resolve(res); }, id); this.pending.set(id, req); if (command === 'call') { req.call(args[0], args[1]); } else { req.expr(args[0]); } }); } /** * Send request to vim */ request(method, args, cb) { if (!this.attached) return cb([0, 'transport disconnected']); let id = this.nextRequestId; this.nextRequestId = this.nextRequestId - 1; let req = new request_1.default(this.connection, (err, res) => { cb(err, res); }, id); this.pending.set(id, req); req.request(method, args); } notify(method, args) { if (!this.attached) return; if (this.pauseLevel != 0) { let arr = this.paused.get(this.pauseLevel); if (arr) { arr.push([method, args]); return; } } let fname = method.slice(5); if (fname == 'err_write') { this.errText = this.errText + args[0].toString(); return; } if (fname == 'out_write') { let msg = args[0].toString() || ''; if (!msg.includes('\n')) { this.outText = this.outText + msg; } else { let text = this.outText + args[0].toString(); this.outText = ''; this.connection.call(notifyMethod, [fname, [text]]); } return; } if (fname == 'err_writeln') { let text = this.errText + args[0].toString(); this.errText = ''; this.connection.call(notifyMethod, [fname, [text]]); return; } this.connection.call(notifyMethod, [fname, args]); } createResponse(_method, requestId) { let called = false; let { connection } = this; // let startTs = Date.now() return { send: (resp, isError) => { if (called || !this.attached) return; called = true; let err = null; if (isError) err = typeof resp === 'string' ? resp : resp.toString(); // if (debug) this.debug(`Send "${method}" (${requestId}) response to vim ${Date.now() - startTs}ms`) connection.response(requestId, [err, isError ? null : resp]); } }; } } exports.VimTransport = VimTransport;