The GDScript LSP Client is works correctly !

This commit is contained in:
Geequlim
2019-06-25 17:16:59 +08:00
parent 1844dd570b
commit 61e1cafdfe
6 changed files with 259 additions and 30 deletions

View File

@@ -1,16 +1,17 @@
import { ExtensionContext } from "vscode";
import GDScriptLanguageClient from "./lsp/GDScriptLanguageClient";
import { GodotTools } from "./godot-tools";
let client: GDScriptLanguageClient = null;
let tools: GodotTools = null;
export function activate(context: ExtensionContext) {
client = new GDScriptLanguageClient();
context.subscriptions.push(client.start());
tools = new GodotTools(context);
tools.activate();
}
export function deactivate(): Thenable<void> {
if (client) {
return client.stop();
}
return new Promise((resolve, reject) => {});
return new Promise((resolve, reject) => {
tools.deactivate();
resolve();
});
}

209
src/godot-tools.ts Normal file
View File

@@ -0,0 +1,209 @@
import * as vscode from "vscode";
import * as path from 'path';
import * as fs from 'fs';
import GDScriptLanguageClient from "./lsp/GDScriptLanguageClient";
import { get_configuration, set_configuration } from "./utils";
import { MessageIO } from "./lsp/MessageIO";
const CONFIG_CONTAINER = "godot_tools";
const TOOL_NAME = "GodotTools";
enum ClientStatus {
PENDING,
DISCONNECTED,
CONNECTED,
}
export class GodotTools {
private context: vscode.ExtensionContext;
private client: GDScriptLanguageClient = null;
private workspace_dir = vscode.workspace.rootPath;
private project_file = "project.godot";
private connection_status: vscode.StatusBarItem = null;
private message_handler: MessageHandler = null;
constructor(p_context: vscode.ExtensionContext) {
this.context = p_context;
this.client = new GDScriptLanguageClient();
this.message_handler = new MessageHandler(this.client.io);
this.client.io.on('disconnected', this.on_client_disconnected.bind(this));
this.client.io.on('connected', this.on_server_connected.bind(this));
this.connection_status = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
}
public activate() {
vscode.commands.registerCommand("godot-tool.open_editor", ()=>{
this.open_workspace_with_editor("-e").catch(err=>vscode.window.showErrorMessage(err));
});
vscode.commands.registerCommand("godot-tool.run_project", ()=>{
this.open_workspace_with_editor().catch(err=>vscode.window.showErrorMessage(err));
});
this.connection_status.text = "starting";
this.connection_status.show();
this.try_connect_to_server();
}
public deactivate() {
this.client.stop();
}
private open_workspace_with_editor(params = "") {
return new Promise((resolve, reject) => {
let valid = false
if (this.workspace_dir) {
let cfg = path.join(this.workspace_dir, this.project_file);
valid = (fs.existsSync(cfg) && fs.statSync(cfg).isFile());
}
if (valid) {
this.run_editor(`--path "${this.workspace_dir}" ${params}`).then(()=>resolve()).catch(err=>{
reject(err);
});
} else {
reject("Current workspace is not a Godot project");
}
});
}
private run_editor(params = "") {
return new Promise((resolve, reject) => {
const run_godot = (path: string, params: string) => {
const escape_command = (cmd: string) => {
let cmdEsc = `"${cmd}"`;
let shell = vscode.workspace.getConfiguration("terminal.integrated.shell").get("windows", "");
if (shell.endsWith("powershell.exe") && process.platform === "win32") {
cmdEsc = `&${cmdEsc}`;
}
return cmdEsc;
};
let existingTerminal = vscode.window.terminals.find(t => t.name === TOOL_NAME)
if (existingTerminal) {
existingTerminal.dispose()
}
let terminal = vscode.window.createTerminal(TOOL_NAME);
let editorPath = escape_command(path);
let cmmand = `${editorPath} ${params}`;
terminal.sendText(cmmand, true);
terminal.show();
resolve();
};
let editorPath = get_configuration("editor_path", "")
editorPath = editorPath.replace("${workspaceRoot}", this.workspace_dir);
if (!fs.existsSync(editorPath) || !fs.statSync(editorPath).isFile()) {
vscode.window.showOpenDialog({
openLabel: "Run",
filters: process.platform === "win32" ? {"Godot Editor Binary": ["exe", "EXE"]} : undefined
}).then((uris: vscode.Uri[])=> {
if (!uris) return;
let path = uris[0].fsPath;
if (!fs.existsSync(path) || !fs.statSync(path).isFile()) {
reject("Invalid editor path to run the project");
} else {
run_godot(path, params);
set_configuration("editor_path", path);
}
});
} else {
run_godot(editorPath, params);
}
});
}
private try_connect_to_server() {
this.client.connect_to_server();
this.update_client_status(ClientStatus.PENDING);
}
private update_client_status(status: ClientStatus) {
this.connection_status.color = vscode.ThemeColor;
switch (status) {
case ClientStatus.PENDING:
this.connection_status.text = `$(sync) Connecting`;
this.connection_status.tooltip = `Connecting to GDScript Language Server`;
break;
case ClientStatus.CONNECTED:
this.connection_status.text = `$(check) Connected`;
this.connection_status.tooltip = `Connected to GDScript Language Server`;
break;
case ClientStatus.DISCONNECTED:
this.connection_status.text = `$(x) Disconnected`;
this.connection_status.tooltip = `Disconnect to GDScript Language Server`;
break;
default:
break;
}
}
private on_server_connected() {
this.context.subscriptions.push(this.client.start());
this.update_client_status(ClientStatus.CONNECTED);
}
private on_client_disconnected() {
this.update_client_status(ClientStatus.DISCONNECTED);
vscode.window.showErrorMessage(`Failed connect to GDScript Language Server`, 'Open Godot Editor', 'Retry', 'Ignore').then(item=>{
if (item == 'Retry') {
this.try_connect_to_server();
} else if (item == 'Open Godot Editor') {
this.update_client_status(ClientStatus.PENDING);
this.open_workspace_with_editor("-e").then(()=>{
setTimeout(()=>{
this.try_connect_to_server();
}, 10 * 1000);
});
}
});
}
};
const CUSTOM_MESSAGE = "gdscrip_client/";
class MessageHandler {
io: MessageIO = null;
constructor(io: MessageIO) {
this.io = io;
this.io.on('message', this.on_server_message.bind(this));
}
changeWorkspace(params: {path: string}) {
vscode.window.showErrorMessage("The GDScript Language Server can't work properly!\nThe opening workspace is diffrent with the editor's.", 'Reload', 'Ignore').then(item=>{
if (item == "Reload") {
let folderUrl = vscode.Uri.file(params.path);
vscode.commands.executeCommand('vscode.openFolder', folderUrl, false);
}
});
}
private on_server_message(message: any) {
if (message && 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]) {
let ret = this[method](message.params);
if (ret) {
ret = this.handle_result(message, ret);
if (ret) {
this.io.send_message(ret);
}
}
}
}
}
private handle_result(request: any, ret: any) {
let data = ret;
if (ret) {
data = JSON.stringify(data);
}
return data
}
}

View File

@@ -1,10 +1,7 @@
import { workspace } from "vscode";
import { LanguageClient, LanguageClientOptions, ServerOptions } from "vscode-languageclient";
import { is_debug_mode, get_configuration } from "../utils";
import logger from "../loggger";
import * as WebSocket from 'ws';
import * as vscode from 'vscode';
import { EventEmitter } from "events";
import { MessageIO, MessageIOReader, MessageIOWriter } from "./MessageIO";
function getClientOptions(): LanguageClientOptions {
@@ -16,7 +13,7 @@ function getClientOptions(): LanguageClientOptions {
],
synchronize: {
// Notify the server about file changes to '.gd files contain in the workspace
fileEvents: workspace.createFileSystemWatcher("**/*.gd"),
// fileEvents: workspace.createFileSystemWatcher("**/*.gd"),
},
};
}
@@ -29,23 +26,19 @@ function get_server_uri() : string {
const io = new MessageIO(get_server_uri());
const serverOptions: ServerOptions = () => {
return new Promise((resolve, reject) => {
io.connect_to_language_server().then(()=>{
resolve({reader: new MessageIOReader(io), writer: new MessageIOWriter(io)});
});
resolve({reader: new MessageIOReader(io), writer: new MessageIOWriter(io)});
});
};
export default class GDScriptLanguageClient extends LanguageClient {
export default class GDScriptLanguageClient extends LanguageClient {
public io: MessageIO = io;
constructor() {
super(`GDScriptLanguageClient`, serverOptions, getClientOptions());
io.on('disconnected', this.on_disconnected.bind(this));
}
private on_disconnected() {
vscode.window.showErrorMessage(`Failed connect to GDScript Language Server`, 'Retry', 'Close').then(item=>{
if (item == 'Retry') {
io.connect_to_language_server();
}
});
connect_to_server() {
io.connect_to_language_server();
}
};

View File

@@ -5,6 +5,7 @@ import MessageBuffer from "./MessageBuffer";
import logger from "../loggger";
import { AbstractMessageWriter, MessageWriter } from "vscode-jsonrpc/lib/messageWriter";
import { Message } from "vscode-jsonrpc";
import { is_debug_mode } from "../utils";
export class MessageIO extends EventEmitter {
@@ -20,13 +21,17 @@ export class MessageIO extends EventEmitter {
if (this.socket) {
this.socket.send(message);
}
logger.log("[client]", message);
if (is_debug_mode) logger.log("[client]", message);
}
protected on_message(chunk: WebSocket.Data) {
let message = chunk.toString();
this.emit('data', message);
logger.log("[server]", message);
if (is_debug_mode) logger.log("[server]", message);
}
on_message_callback(message: Object) {
this.emit("message", message);
}
connect_to_language_server():Promise<void> {
@@ -120,6 +125,7 @@ export class MessageIOReader extends AbstractMessageReader implements MessageRea
this.messageToken++;
var json = JSON.parse(msg);
this.callback(json);
this.io.on_message_callback(json);
}
}

View File

@@ -1,7 +1,13 @@
import { workspace } from "vscode";
import * as vscode from "vscode";
const CONFIG_CONTAINER = "godot_tools";
export function get_configuration(name: string, default_value: any = null) {
return workspace.getConfiguration("godot_tools").get(name, default_value);
return vscode.workspace.getConfiguration(CONFIG_CONTAINER).get(name, default_value);
}
export function set_configuration(name: string, value: any) {
return vscode.workspace.getConfiguration(CONFIG_CONTAINER).update(name, value);
}
export function is_debug_mode(): boolean {