Эх сурвалжийг харах

Suppress "workspace/symbol" not found error (#723)

* Discard outgoing "workspace/symbol" LSP messages
David Kincaid 11 сар өмнө
parent
commit
170d3d4819

+ 21 - 19
src/lsp/ClientConnectionManager.ts

@@ -17,13 +17,13 @@ import { subProcess, killSubProcesses } from "../utils/subspawn";
 const log = createLogger("lsp.manager", { output: "Godot LSP" });
 
 enum ManagerStatus {
-	INITIALIZING,
-	INITIALIZING_LSP,
-	PENDING,
-	PENDING_LSP,
-	DISCONNECTED,
-	CONNECTED,
-	RETRYING,
+	INITIALIZING = 0,
+	INITIALIZING_LSP = 1,
+	PENDING = 2,
+	PENDING_LSP = 3,
+	DISCONNECTED = 4,
+	CONNECTED = 5,
+	RETRYING = 6,
 }
 
 export class ClientConnectionManager {
@@ -126,16 +126,18 @@ export class ClientConnectionManager {
 
 		if (result.version[2] < minimumVersion) {
 			const message = `Cannot launch headless LSP: Headless LSP mode is only available on v${targetVersion} or newer, but the specified Godot executable is v${result.version}.`;
-			vscode.window.showErrorMessage(message, "Select Godot executable", "Open Settings", "Disable Headless LSP", "Ignore").then(item => {
-				if (item === "Select Godot executable") {
-					select_godot_executable(settingName);
-				} else if (item === "Open Settings") {
-					vscode.commands.executeCommand("workbench.action.openSettings", settingName);
-				} else if (item === "Disable Headless LSP") {
-					set_configuration("lsp.headless", false);
-					prompt_for_reload();
-				}
-			});
+			vscode.window
+				.showErrorMessage(message, "Select Godot executable", "Open Settings", "Disable Headless LSP", "Ignore")
+				.then((item) => {
+					if (item === "Select Godot executable") {
+						select_godot_executable(settingName);
+					} else if (item === "Open Settings") {
+						vscode.commands.executeCommand("workbench.action.openSettings", settingName);
+					} else if (item === "Disable Headless LSP") {
+						set_configuration("lsp.headless", false);
+						prompt_for_reload();
+					}
+				});
 			return;
 		}
 
@@ -197,7 +199,7 @@ export class ClientConnectionManager {
 				if (this.target === TargetLSP.HEADLESS) {
 					options = ["Restart LSP", ...options];
 				}
-				vscode.window.showInformationMessage(message, ...options).then(item => {
+				vscode.window.showInformationMessage(message, ...options).then((item) => {
 					if (item === "Restart LSP") {
 						this.connect_to_language_server();
 					}
@@ -324,7 +326,7 @@ export class ClientConnectionManager {
 			options = ["Open workspace with Godot Editor", ...options];
 		}
 
-		vscode.window.showErrorMessage(message, ...options).then(item => {
+		vscode.window.showErrorMessage(message, ...options).then((item) => {
 			if (item === "Retry") {
 				this.connect_to_language_server();
 			}

+ 33 - 16
src/lsp/GDScriptLanguageClient.ts

@@ -1,27 +1,40 @@
 import * as vscode from "vscode";
-import { LanguageClient, NotificationMessage, RequestMessage, ResponseMessage } from "vscode-languageclient/node";
-import { EventEmitter } from "events";
+import {
+	LanguageClient,
+	type NotificationMessage,
+	type RequestMessage,
+	type ResponseMessage,
+} from "vscode-languageclient/node";
+import { EventEmitter } from "node:events";
 import { get_configuration, createLogger } from "../utils";
-import { Message, MessageIO, MessageIOReader, MessageIOWriter, TCPMessageIO, WebSocketMessageIO } from "./MessageIO";
+import {
+	type Message,
+	type MessageIO,
+	MessageIOReader,
+	MessageIOWriter,
+	TCPMessageIO,
+	WebSocketMessageIO,
+} from "./MessageIO";
 import { globals } from "../extension";
 
 const log = createLogger("lsp.client", { output: "Godot LSP" });
 
 export enum ClientStatus {
-	PENDING,
-	DISCONNECTED,
-	CONNECTED,
+	PENDING = 0,
+	DISCONNECTED = 1,
+	CONNECTED = 2,
 }
 
 export enum TargetLSP {
-	HEADLESS,
-	EDITOR,
+	HEADLESS = 0,
+	EDITOR = 1,
 }
 
 const CUSTOM_MESSAGE = "gdscript_client/";
 
 export default class GDScriptLanguageClient extends LanguageClient {
-	public readonly io: MessageIO = (get_configuration("lsp.serverProtocol") === "ws") ? new WebSocketMessageIO() : new TCPMessageIO();
+	public readonly io: MessageIO =
+		get_configuration("lsp.serverProtocol") === "ws" ? new WebSocketMessageIO() : new TCPMessageIO();
 
 	private _status_changed_callbacks: ((v: ClientStatus) => void)[] = [];
 	private _initialize_request: Message = null;
@@ -35,10 +48,14 @@ export default class GDScriptLanguageClient extends LanguageClient {
 	public lastSymbolHovered = "";
 
 	private _started = false;
-	public get started(): boolean { return this._started; }
+	public get started(): boolean {
+		return this._started;
+	}
 
 	private _status: ClientStatus;
-	public get status(): ClientStatus { return this._status; }
+	public get status(): ClientStatus {
+		return this._status;
+	}
 	public set status(v: ClientStatus) {
 		if (this._status !== v) {
 			this._status = v;
@@ -72,7 +89,7 @@ export default class GDScriptLanguageClient extends LanguageClient {
 					// Notify the server about file changes to '.gd files contain in the workspace
 					fileEvents: vscode.workspace.createFileSystemWatcher("**/*.gd"),
 				},
-			}
+			},
 		);
 		this.status = ClientStatus.PENDING;
 		this.io.on("disconnected", this.on_disconnected.bind(this));
@@ -149,7 +166,7 @@ export default class GDScriptLanguageClient extends LanguageClient {
 				let value: string = message.result["contents"]?.value;
 				if (value) {
 					// this is a dirty hack to fix language server sending us prerendered
-					// markdown but not correctly stripping leading #'s, leading to 
+					// markdown but not correctly stripping leading #'s, leading to
 					// docstrings being displayed as titles
 					value = value.replace(/\n[#]+/g, "\n");
 
@@ -216,7 +233,7 @@ export default class GDScriptLanguageClient extends LanguageClient {
 			this.io.writer.write(this._initialize_request);
 		}
 		this.status = ClientStatus.CONNECTED;
-	
+
 		const host = get_configuration("lsp.serverHost");
 		log.info(`connected to LSP at ${host}:${this.lastPortTried}`);
 	}
@@ -260,12 +277,12 @@ class MessageHandler extends EventEmitter {
 
 	on_message(message: any) {
 		// FIXME: Hot fix VSCode 1.42 hover position
-		if (message && message.result && message.result.range && message.result.contents) {
+		if (message?.result?.range && message.result.contents) {
 			message.result.range = undefined;
 		}
 
 		// What does this do?
-		if (message && message.method && (message.method as string).startsWith(CUSTOM_MESSAGE)) {
+		if (message?.method && (message.method as string).startsWith(CUSTOM_MESSAGE)) {
 			const method = (message.method as string).substring(CUSTOM_MESSAGE.length, message.method.length);
 			if (this[method]) {
 				const ret = this[method](message.params);

+ 29 - 24
src/lsp/MessageBuffer.ts

@@ -4,34 +4,33 @@
  * ------------------------------------------------------------------------------------------ */
 
 const DefaultSize: number = 8192;
-const CR: number = Buffer.from('\r', 'ascii')[0];
-const LF: number = Buffer.from('\n', 'ascii')[0];
-const CRLF: string = '\r\n';
+const CR: number = Buffer.from("\r", "ascii")[0];
+const LF: number = Buffer.from("\n", "ascii")[0];
+const CRLF: string = "\r\n";
 
 export default class MessageBuffer {
-
 	private encoding: BufferEncoding;
 	private index: number;
 	private buffer: Buffer;
 
-	constructor(encoding: string = 'utf8') {
+	constructor(encoding = "utf8") {
 		this.encoding = encoding as BufferEncoding;
 		this.index = 0;
 		this.buffer = Buffer.allocUnsafe(DefaultSize);
 	}
 
-	public append(chunk: Buffer | String): void {
-		var toAppend: Buffer = <Buffer>chunk;
-		if (typeof (chunk) === 'string') {
-			var str = <string>chunk;
-			var bufferLen = Buffer.byteLength(str, this.encoding);
+	public append(chunk: Buffer | string): void {
+		let toAppend: Buffer = <Buffer>chunk;
+		if (typeof chunk === "string") {
+			const str = <string>chunk;
+			const bufferLen = Buffer.byteLength(str, this.encoding);
 			toAppend = Buffer.allocUnsafe(bufferLen);
 			toAppend.write(str, 0, bufferLen, this.encoding);
 		}
 		if (this.buffer.length - this.index >= toAppend.length) {
 			toAppend.copy(this.buffer, this.index, 0, toAppend.length);
 		} else {
-			var newSize = (Math.ceil((this.index + toAppend.length) / DefaultSize) + 1) * DefaultSize;
+			const newSize = (Math.ceil((this.index + toAppend.length) / DefaultSize) + 1) * DefaultSize;
 			if (this.index === 0) {
 				this.buffer = Buffer.allocUnsafe(newSize);
 				toAppend.copy(this.buffer, 0, 0, toAppend.length);
@@ -42,10 +41,16 @@ export default class MessageBuffer {
 		this.index += toAppend.length;
 	}
 
-	public tryReadHeaders(): { [key: string]: string; } | undefined {
-		let result: { [key: string]: string; } | undefined = undefined;
+	public tryReadHeaders(): { [key: string]: string } | undefined {
+		let result: { [key: string]: string } | undefined = undefined;
 		let current = 0;
-		while (current + 3 < this.index && (this.buffer[current] !== CR || this.buffer[current + 1] !== LF || this.buffer[current + 2] !== CR || this.buffer[current + 3] !== LF)) {
+		while (
+			current + 3 < this.index &&
+			(this.buffer[current] !== CR ||
+				this.buffer[current + 1] !== LF ||
+				this.buffer[current + 2] !== CR ||
+				this.buffer[current + 3] !== LF)
+		) {
 			current++;
 		}
 		// No header / body separator found (e.g CRLFCRLF)
@@ -53,18 +58,18 @@ export default class MessageBuffer {
 			return result;
 		}
 		result = Object.create(null);
-		let headers = this.buffer.toString('ascii', 0, current).split(CRLF);
-		headers.forEach((header) => {
-			let index: number = header.indexOf(':');
+		const headers = this.buffer.toString("ascii", 0, current).split(CRLF);
+        for (const header of headers) {
+			const index: number = header.indexOf(":");
 			if (index === -1) {
-				throw new Error('Message header must separate key and value using :');
+				throw new Error("Message header must separate key and value using :");
 			}
-			let key = header.substr(0, index);
-			let value = header.substr(index + 1).trim();
+			const key = header.substr(0, index);
+			const value = header.substr(index + 1).trim();
 			result![key] = value;
-		});
+        }
 
-		let nextStart = current + 4;
+		const nextStart = current + 4;
 		this.buffer = this.buffer.slice(nextStart);
 		this.index = this.index - nextStart;
 		return result;
@@ -74,8 +79,8 @@ export default class MessageBuffer {
 		if (this.index < length) {
 			return null;
 		}
-		let result = this.buffer.toString(this.encoding, 0, length);
-		let nextStart = length;
+		const result = this.buffer.toString(this.encoding, 0, length);
+		const nextStart = length;
 		this.buffer.copy(this.buffer, 0, nextStart);
 		this.index = this.index - nextStart;
 		return result;

+ 39 - 30
src/lsp/MessageIO.ts

@@ -1,16 +1,16 @@
 import {
 	AbstractMessageReader,
-	MessageReader,
-	DataCallback,
-	Disposable,
-	RequestMessage,
-	ResponseMessage,
-	NotificationMessage,
+	type MessageReader,
+	type DataCallback,
+	type Disposable,
+	type RequestMessage,
+	type ResponseMessage,
+	type NotificationMessage,
 	AbstractMessageWriter,
-	MessageWriter
+	type MessageWriter,
 } from "vscode-jsonrpc";
-import { EventEmitter } from "events";
-import { WebSocket, Data } from "ws";
+import { EventEmitter } from "node:events";
+import { WebSocket, type Data } from "ws";
 import { Socket } from "net";
 import MessageBuffer from "./MessageBuffer";
 import { createLogger } from "../utils";
@@ -45,7 +45,6 @@ export class MessageIO extends EventEmitter {
 	}
 }
 
-
 export class WebSocketMessageIO extends MessageIO {
 	private socket: WebSocket = null;
 
@@ -59,7 +58,10 @@ export class WebSocketMessageIO extends MessageIO {
 		return new Promise((resolve, reject) => {
 			this.socket = null;
 			const ws = new WebSocket(`ws://${host}:${port}`);
-			ws.on("open", () => { this.on_connected(ws); resolve(); });
+			ws.on("open", () => {
+				this.on_connected(ws);
+				resolve();
+			});
 			ws.on("message", this.on_message.bind(this));
 			ws.on("error", this.on_disconnected.bind(this));
 			ws.on("close", this.on_disconnected.bind(this));
@@ -91,7 +93,10 @@ export class TCPMessageIO extends MessageIO {
 			this.socket = null;
 			const socket = new Socket();
 			socket.connect(port, host);
-			socket.on("connect", () => { this.on_connected(socket); resolve(); });
+			socket.on("connect", () => {
+				this.on_connected(socket);
+				resolve();
+			});
 			socket.on("data", this.on_message.bind(this));
 			socket.on("end", this.on_disconnected.bind(this));
 			socket.on("close", this.on_disconnected.bind(this));
@@ -162,15 +167,15 @@ export class MessageIOReader extends AbstractMessageReader implements MessageRea
 				if (!contentLength) {
 					throw new Error("Header must provide a Content-Length property.");
 				}
-				const length = parseInt(contentLength);
-				if (isNaN(length)) {
+				const length = Number.parseInt(contentLength);
+				if (Number.isNaN(length)) {
 					throw new Error("Content-Length value must be a number.");
 				}
 				this.nextMessageLength = length;
 				// Take the encoding form the header. For compatibility
 				// treat both utf-8 and utf8 as node utf8
 			}
-			var msg = this.buffer.tryReadContent(this.nextMessageLength);
+			const msg = this.buffer.tryReadContent(this.nextMessageLength);
 			if (msg === null) {
 				/** We haven't received the full message yet. */
 				this.setPartialMessageTimer();
@@ -179,7 +184,7 @@ export class MessageIOReader extends AbstractMessageReader implements MessageRea
 			this.clearPartialMessageTimer();
 			this.nextMessageLength = -1;
 			this.messageToken++;
-			var json = JSON.parse(msg);
+			const json = JSON.parse(msg);
 
 			log.debug("rx:", json);
 			this.callback(json);
@@ -200,13 +205,18 @@ export class MessageIOReader extends AbstractMessageReader implements MessageRea
 		if (this._partialMessageTimeout <= 0) {
 			return;
 		}
-		this.partialMessageTimer = setTimeout((token, timeout) => {
-			this.partialMessageTimer = undefined;
-			if (token === this.messageToken) {
-				this.firePartialMessage({ messageToken: token, waitingTime: timeout });
-				this.setPartialMessageTimer();
-			}
-		}, this._partialMessageTimeout, this.messageToken, this._partialMessageTimeout);
+		this.partialMessageTimer = setTimeout(
+			(token, timeout) => {
+				this.partialMessageTimer = undefined;
+				if (token === this.messageToken) {
+					this.firePartialMessage({ messageToken: token, waitingTime: timeout });
+					this.setPartialMessageTimer();
+				}
+			},
+			this._partialMessageTimeout,
+			this.messageToken,
+			this._partialMessageTimeout,
+		);
 	}
 }
 
@@ -227,21 +237,20 @@ export class MessageIOWriter extends AbstractMessageWriter implements MessageWri
 		this.io.on("close", () => this.fireClose());
 	}
 
-	public end(): void {
-
-	}
+	public end(): void {}
 
 	public write(msg: Message): Promise<void> {
+        // discard outgoing messages that we know aren't supported
 		if ((msg as RequestMessage).method === "didChangeWatchedFiles") {
 			return;
 		}
+		if ((msg as RequestMessage).method === "workspace/symbol") {
+			return;
+		}
 		const json = JSON.stringify(msg);
 		const contentLength = Buffer.byteLength(json, this.encoding);
 
-		const headers: string[] = [
-			ContentLength, contentLength.toString(), CRLF,
-			CRLF
-		];
+		const headers: string[] = [ContentLength, contentLength.toString(), CRLF, CRLF];
 		try {
 			// callback
 			this.io.on_send_message(msg);

+ 1 - 1
src/lsp/gdscript.capabilities.ts

@@ -1,4 +1,4 @@
-import { DocumentSymbol, Range, SymbolKind } from "vscode-languageclient";
+import type { DocumentSymbol, Range, SymbolKind } from "vscode-languageclient";
 
 export interface NativeSymbolInspectParams {
 	native_class: string;