@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
JavaScript
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