feat: connect NWScript language server to Monaco via WebSocket

Add the forked nwscript-ee-language-server as a git submodule and wire
it up to the editor through a WebSocket-based LSP bridge:

- Backend: lsp.service.ts spawns the language server in --stdio mode
  and bridges JSON-RPC messages between WebSocket and stdin/stdout
- Backend: /ws/lsp upgrade handler in index.ts
- Frontend: LspClient class using vscode-ws-jsonrpc for JSON-RPC over
  WebSocket, with Monaco providers for completions, hover, and
  diagnostics
- Frontend: useLspClient/useLspDocument hooks integrated into
  MonacoEditor component
This commit is contained in:
plenarius
2026-04-20 19:41:05 -04:00
parent 64908098cd
commit b7177a8fd7
9 changed files with 589 additions and 3 deletions
+10
View File
@@ -10,6 +10,7 @@ import dockerRouter from "./routes/docker.js";
import editorRouter from "./routes/editor.js";
import terminalRouter from "./routes/terminal.js";
import { attachWebSocket, createTerminalSession } from "./services/terminal.service.js";
import { attachLspWebSocket } from "./services/lsp.service.js";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const app = express();
@@ -41,6 +42,15 @@ app.get("*path", (_req, res) => {
server.on("upgrade", (request, socket, head) => {
const url = new URL(request.url || "", `http://${request.headers.host}`);
if (url.pathname === "/ws/lsp") {
const lspWss = new WebSocketServer({ noServer: true });
lspWss.handleUpgrade(request, socket, head, (ws) => {
attachLspWebSocket(ws);
});
return;
}
const termMatch = url.pathname.match(/^\/ws\/terminal\/(.+)$/);
if (termMatch) {
const sessionId = termMatch[1];