Add WIP TCP connection type; move to use async methods; send client successful server start message
This commit is contained in:
54
src/index.js
54
src/index.js
@@ -5,6 +5,7 @@ const {servers} = require("./languageServers");
|
|||||||
const {spawn} = require('child_process');
|
const {spawn} = require('child_process');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
const WebSocket = require('ws');
|
const WebSocket = require('ws');
|
||||||
|
const net = require('net');
|
||||||
const verbose = argv.verbose || false;
|
const verbose = argv.verbose || false;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -27,30 +28,35 @@ console.log("Started websocket server on port: ", _port);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
wss.on('connection', (ws, req) => {
|
wss.on('connection', async (ws, req) => {
|
||||||
// ws://localhost:6005/gdscript?workspace=/yolo
|
|
||||||
const address = url.parse(req.url, true);
|
const address = url.parse(req.url, true);
|
||||||
const pathname = address.pathname.substring(1);
|
const pathname = address.pathname.substring(1);
|
||||||
const qdata = address.query;
|
const qdata = address.query;
|
||||||
const workspace = qdata.workspace;
|
const workspace = qdata.workspace;
|
||||||
|
|
||||||
handleLanguageConnection(ws, pathname, workspace);
|
await handleLanguageConnection(ws, pathname, workspace);
|
||||||
|
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'connected',
|
||||||
|
lang_id: pathname,
|
||||||
|
message: 'LSP Server connection established...',
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function handleLanguageConnection(ws, pathname, workspace) {
|
async function handleLanguageConnection(ws, pathname, workspace) {
|
||||||
const server = servers.find(server => server.endpointName === pathname);
|
const server = servers.find(server => server.endpointName === pathname);
|
||||||
setupLanguageServer(ws, server, workspace);
|
await setupLanguageServer(ws, server, workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupLanguageServer(ws, server, workspace) {
|
async function setupLanguageServer(ws, server, workspace) {
|
||||||
if (!server) return;
|
if (!server) return;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
reader,
|
reader,
|
||||||
writer
|
writer
|
||||||
} = startLanguageServer(server, workspace);
|
} = await startLanguageServer(server, workspace);
|
||||||
|
|
||||||
server.writer = writer;
|
server.writer = writer;
|
||||||
|
|
||||||
@@ -81,13 +87,13 @@ function setupLanguageServer(ws, server, workspace) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function startLanguageServer(languageServer, workspace) {
|
async function startLanguageServer(languageServer, workspace) {
|
||||||
const env = process.env;
|
const env = process.env;
|
||||||
const args = languageServer.args.flat().map(arg =>
|
const args = languageServer.args.flat().map(arg =>
|
||||||
typeof arg === 'string' ? arg.replace('{workspace.path}', workspace || '') : arg
|
typeof arg === 'string' ? arg.replace('{workspace.path}', workspace || '') : arg
|
||||||
).filter(arg => arg !== '');
|
).filter(arg => arg !== '');
|
||||||
|
|
||||||
const serverProcess = spawn(args[0], args.slice(1), { env, shell: true });
|
const serverProcess = spawn(args[0], args.slice(1), { env });
|
||||||
|
|
||||||
serverProcess.stderr.on('data', data => {
|
serverProcess.stderr.on('data', data => {
|
||||||
console.error(`${serverProcess.spawnfile} error: ${data}`);
|
console.error(`${serverProcess.spawnfile} error: ${data}`);
|
||||||
@@ -122,6 +128,17 @@ function startLanguageServer(languageServer, workspace) {
|
|||||||
throw 'The language server process does not have a valid stdin or stdout';
|
throw 'The language server process does not have a valid stdin or stdout';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "tcp":
|
||||||
|
if (!languageServer.port) {
|
||||||
|
throw `Language Server Config for ${languageServer.endpointName} must have 'port' value...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const socket = await connectWithRetry(languageServer.port);
|
||||||
|
|
||||||
|
reader = new StreamMessageReader(socket);
|
||||||
|
writer = new StreamMessageWriter(socket);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw 'Unknown connection type...';
|
throw 'Unknown connection type...';
|
||||||
@@ -133,6 +150,25 @@ function startLanguageServer(languageServer, workspace) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function connectWithRetry(port, host = "localhost", retries = 5, delay = 1000) {
|
||||||
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||||
|
try {
|
||||||
|
const socket = net.connect(port, host);
|
||||||
|
console.log(`Connected on attempt ${attempt}...`);
|
||||||
|
return socket;
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Attempt ${attempt} failed`);
|
||||||
|
|
||||||
|
if (attempt === retries) {
|
||||||
|
throw new Error(`Failed to connect after ${retries} retries...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const wait = ms => new Promise(r => setTimeout(r, ms));
|
||||||
|
await wait(delay * attempt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function processMessage(message, ws, server) {
|
function processMessage(message, ws, server) {
|
||||||
if (message.params) {
|
if (message.params) {
|
||||||
if (message.params.textDocument && message.params.textDocument.uri) {
|
if (message.params.textDocument && message.params.textDocument.uri) {
|
||||||
|
|||||||
@@ -16,11 +16,10 @@ exports.servers = [
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
nameEndsWith: ".gd",
|
nameEndsWith: ".gd",
|
||||||
connectionType: "ipc",
|
connectionType: "tcp",
|
||||||
|
port: 6005,
|
||||||
relativePath: false
|
relativePath: false
|
||||||
}, {
|
}, {
|
||||||
|
|
||||||
|
|
||||||
endpointName: "go",
|
endpointName: "go",
|
||||||
args: [
|
args: [
|
||||||
'gopls', ['-mode=stdio', '-remote=auto']
|
'gopls', ['-mode=stdio', '-remote=auto']
|
||||||
|
|||||||
Reference in New Issue
Block a user