@chemzqm/neovim
Version:
NodeJS client API for vim9 and neovim
193 lines (192 loc) • 6.67 kB
JavaScript
;
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;