UNPKG

@gdquest/codemirror-lsp

Version:

Enables Codemirror to interact with a local lsp, ie. a LSP that isn't socket-based, but rather work by function calls.

506 lines (499 loc) 15.4 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // ../../node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.js var require_eventemitter3 = __commonJS({ "../../node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.js"(exports, module) { "use strict"; var has = Object.prototype.hasOwnProperty; var prefix = "~"; function Events() { } if (Object.create) { Events.prototype = /* @__PURE__ */ Object.create(null); if (!new Events().__proto__) prefix = false; } function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } function addListener(emitter, event, fn, context, once) { if (typeof fn !== "function") { throw new TypeError("The listener must be a function"); } var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events(); else delete emitter._events[evt]; } function EventEmitter2() { this._events = new Events(); this._eventsCount = 0; } EventEmitter2.prototype.eventNames = function eventNames() { var names = [], events, name; if (this._eventsCount === 0) return names; for (name in events = this._events) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; EventEmitter2.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event, handlers = this._events[evt]; if (!handlers) return []; if (handlers.fn) return [handlers.fn]; for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { ee[i] = handlers[i].fn; } return ee; }; EventEmitter2.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event, listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; EventEmitter2.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt], len = arguments.length, args, i; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, void 0, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len - 1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length, j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, void 0, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; default: if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; EventEmitter2.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; EventEmitter2.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; EventEmitter2.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) { clearEvent(this, evt); } } else { for (var i = 0, events = [], length = listeners.length; i < length; i++) { if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) { events.push(listeners[i]); } } if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; else clearEvent(this, evt); } return this; }; EventEmitter2.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events(); this._eventsCount = 0; } return this; }; EventEmitter2.prototype.off = EventEmitter2.prototype.removeListener; EventEmitter2.prototype.addListener = EventEmitter2.prototype.on; EventEmitter2.prefixed = prefix; EventEmitter2.EventEmitter = EventEmitter2; if ("undefined" !== typeof module) { module.exports = EventEmitter2; } } }); // src/index.mts import { linter, setDiagnostics } from "@codemirror/lint"; import { ViewPlugin } from "@codemirror/view"; // ../../node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.mjs var import_index = __toESM(require_eventemitter3(), 1); var eventemitter3_default = import_index.default; // src/jsonRpc/index.mts var JsonRpcError = class extends Error { constructor(message, options) { const { cause, code, response } = options ?? {}; super(message, { cause }); this.code = 0; this.response = null; if (code != null) { this.code = code; } if (response != null) { this.response = response; } } }; var JsonRpc = class extends eventemitter3_default { constructor() { super(...arguments); this._requests = /* @__PURE__ */ new Map(); this._lastId = 0; } async send(request) { return await new Promise( (resolve, reject) => { if (Array.isArray(request)) { this._processBatch(request, resolve); } else { this._process(request, resolve, reject); } } ); } notify(request) { if (Array.isArray(request)) { new Promise((resolve) => { this._processBatch(request, resolve); }).catch((err) => { console.error("Error while processing batch:", err); }); return; } new Promise((resolve, reject) => { this._process(request, resolve, reject); }).catch((err) => { console.error("Error while running process:", err); }); } _process(request, resolve, reject) { const id = this._lastId; this._lastId += 1; const formattedRequest = { jsonrpc: "2.0", id, ...request }; const removeListeners = () => { this.off("response", onResponse); this.off("error", onError); }; const onResponse = (response) => { if (response.id === id) { resolve(response); removeListeners(); } }; const onError = (response) => { if (response.id === id) { const error = new JsonRpcError(response.error.message, { code: response.error.code, response }); reject(error); removeListeners(); } }; this.on("response", onResponse); this.on("error", onError); this.emit("request", JSON.stringify(formattedRequest)); } _processBatch(request, resolve) { const requests = request.map((request2) => { const formattedRequest = { jsonrpc: "2.0", id: this._lastId, ...request2 }; this._lastId += 1; return formattedRequest; }); const onBatch = (responses) => { const receivedResponses = []; for (const response of responses) { for (const request2 of requests) { if (request2.id === response.id) { receivedResponses.push(response); } } } if (receivedResponses.length > 0) { resolve(receivedResponses); this.off("batch", onBatch); } }; this.on("batch", onBatch); this.emit("request", requests); } }; // src/lsp/conversion.mts function getCharacterFromPosition(text, position) { let currentLine = 0; let currentColumn = 0; for (let ci = 0; ci < text.length; ci++) { const char = text[ci]; if (char === "\n") { currentLine += 1; currentColumn = 0; } else { currentColumn += 1; } if (position.line < currentLine) { return 0; } if (currentLine === position.line && currentColumn === position.character) { return ci + 1; } } return text.length; } function getPositionFromCharacter(text, character) { let currentLine = 0; let currentColumn = 0; for (let i = 1; i < text.length; i++) { const char = text[i - 1]; if (char === "\n") { currentLine += 1; currentColumn = 0; } else { currentColumn += 1; } if (i === character) { return { line: currentLine, character: currentColumn }; } } return { line: currentLine, character: currentColumn }; } function getDiagnosticSeverity(severity) { switch (severity) { case 1: return "error"; case 2: return "warning"; case 3: case 4: default: return "info"; } } // src/index.mts function createLsp(config) { const lspBus = new import_index.default(); let lspView = null; let nextVersionId = 0; const { autocompletion = true, onClientCommand, getData } = config; const jsonRpcClient = new JsonRpc(); class LspPlugin { constructor(view) { this.view = view; } update(update) { } } ViewPlugin.fromClass(LspPlugin); const lspLinter = linter(async (view) => { lspView = view; lspBus.emit("view", view); const diagnostics = []; const localVersion = nextVersionId; nextVersionId += 1; jsonRpcClient.notify({ method: "textDocument/didChange", params: { textDocument: { uri: "file:///whatever", version: localVersion }, contentChanges: [ { text: view.state.doc.toString() } ] } }); return diagnostics; }, {}); jsonRpcClient.on("request", (request) => { onClientCommand(request); }); const onNotification = (notification) => { switch (notification.method) { case "textDocument/publishDiagnostics": onPublishDiagnostics(notification); break; } }; jsonRpcClient.on("notification", onNotification); const onPublishDiagnostics = (notification) => { console.log("onPublishDiagnostics", notification, lspView); if (lspView == null) { return; } const diagnostics = notification.params.diagnostics.map( (diagnostic) => { return { from: getCharacterFromPosition( lspView?.state.doc.toString() ?? "", diagnostic.range.start ), to: getCharacterFromPosition( lspView?.state.doc.toString() ?? "", diagnostic.range.end ), severity: getDiagnosticSeverity(diagnostic.severity), source: diagnostic.source, message: diagnostic.message }; } ); console.log("diagnostics", diagnostics); const transitionSpec = setDiagnostics(lspView.state, diagnostics); const transaction = lspView.state.update(transitionSpec); lspView.dispatch(transaction); }; const emitServerCommand = (jsonRpc) => { const json = JSON.parse(jsonRpc); if (typeof json === "string") { throw new Error("JSON is string"); } if (Array.isArray(json)) { const elements = []; for (const el of json) { if ("method" in el) { jsonRpcClient.emit("notification", el); } else { elements.push(el); } } jsonRpcClient.emit("batch", elements); return; } if ("error" in json) { jsonRpcClient.emit("error", json); return; } if ("result" in json) { jsonRpcClient.emit("response", json); return; } if ("method" in json) { jsonRpcClient.emit("notification", json); return; } throw new Error("unknown error"); }; getData({ emitServerCommand }); return [ lspLinter, ...autocompletion ? [ // generateLspAutocomplete({ // client: jsonRpcClient, // bus: lspBus, // }), ] : [] ]; } export { createLsp, getCharacterFromPosition, getDiagnosticSeverity, getPositionFromCharacter }; //# sourceMappingURL=index.mjs.map