X-Git-Url: http://git.roojs.org/?a=blobdiff_plain;f=src%2FPalete%2FLanguageClientVala.vala;h=f49bbd8b28fbd4803741f348844417eb5f176b5a;hb=HEAD;hp=d535822b40c8072f9209c478e2a7d616a9519f2b;hpb=042e9d4a0fbdacac933b2a2df0625b7ba4bd7674;p=roobuilder diff --git a/src/Palete/LanguageClientVala.vala b/src/Palete/LanguageClientVala.vala index d535822b4..f49bbd8b2 100644 --- a/src/Palete/LanguageClientVala.vala +++ b/src/Palete/LanguageClientVala.vala @@ -1,7 +1,6 @@ namespace Palete { public class LanguageClientVala : LanguageClient { - int countdown = 0; protected bool initialized = false; bool sent_shutdown = false; uint change_queue_id = 0; @@ -20,9 +19,13 @@ namespace Palete { private IOStream? subprocess_stream = null; public Jsonrpc.Client? jsonrpc_client = null; + int countdown = 0; Gee.ArrayList open_files; private JsRender.JsRender? _change_queue_file = null; + int doc_countdown = 0; private string change_queue_file_source = ""; + private JsRender.JsRender? doc_queue_file = null; + JsRender.JsRender? change_queue_file { set { @@ -33,9 +36,18 @@ namespace Palete { return this._change_queue_file; } } + + + void startServer() { - this.initProcess("/usr/bin/vala-language-server"); + var exe = GLib.Environment.find_program_in_path( "vala-language-server"); + if (exe == null) { + GLib.warning("could not find vala-language-server"); + + return; + } + this.initProcess(exe); } @@ -43,37 +55,87 @@ namespace Palete { { // extend versions will proably call initialize to start and connect to server. base(project); - this.open_files = new Gee.ArrayList(); - this.change_queue_id = GLib.Timeout.add_seconds(1, () => { - if (this.change_queue_file == null) { - return true; - } - this.countdown--; - if (this.countdown < 0){ - this.document_change_force.begin(this.change_queue_file, this.change_queue_file_source, (o, res) => { - this.document_change_force.end(res); - }); - this.change_queue_file = null; - - } - return true; - }); + + if (this.change_queue_id == 0 ) { + this.change_queue_id = GLib.Timeout.add(500, () => { + this.run_change_queue(); + return true; + }); + } + this.startServer(); } + void run_change_queue() + { + + if (this.change_queue_file == null) { + return ; + } + if (this.countdown < -1) { + return; + } + if (this.getting_diagnostics) { + return; + } + this.countdown--; + + + if (this.countdown < 0){ + this.document_change_force.begin(this.change_queue_file, this.change_queue_file_source, (o, res) => { + this.document_change_force.end(res); + }); + this.change_queue_file = null; + + } + return ; + } + async int queuer(int cnt) + { + SourceFunc cb = this.queuer.callback; + + GLib.Timeout.add(500, () => { + GLib.Idle.add((owned) cb); + return false; + }); + + yield; + return cnt; + } + static int doc_queue_id = 0; + + + + public bool initProcess(string process_path) { this.onClose(); this.log(LanguageClientAction.LAUNCH, process_path); GLib.debug("Launching %s", process_path); this.launcher = new GLib.SubprocessLauncher (SubprocessFlags.STDIN_PIPE | SubprocessFlags.STDOUT_PIPE); - this.launcher.set_environ(GLib.Environ.get()); + var env = GLib.Environ.get(); + env += "G_MESSAGES_DEBUG=all"; + + this.launcher.set_environ(env); + var logpath = GLib.Environment.get_home_dir() + "/.cache/vala-language-server"; + + if (!GLib.FileUtils.test(logpath, GLib.FileTest.IS_DIR)) { + Posix.mkdir(logpath, 0700); + } + // not very reliable.. + //this.launcher.set_stderr_file_path( + // logpath + "/" + + // (new GLib.DateTime.now_local()).format("%Y-%m-%d") + ".log" + //); + //GLib.debug("log lang server to %s", logpath + "/" + + // (new GLib.DateTime.now_local()).format("%Y-%m-%d") + ".log"); + try { - this.subprocess = launcher.spawnv ({ process_path }); + this.subprocess = launcher.spawnv ({ process_path , "2>" , "/tmp/vala-language-server.log" }); this.subprocess.wait_async.begin( null, ( obj,res ) => { try { @@ -125,14 +187,14 @@ namespace Palete { this.jsonrpc_client.failed.connect(() => { this.log(LanguageClientAction.ERROR_RPC, "client failed"); + GLib.debug("language server server has failed"); this.onClose(); - GLib.debug("language server server has failed"); + }); this.initialize_server (); } - } @@ -145,13 +207,24 @@ namespace Palete { this.buildDict ( processId: new Variant.int32 ((int32) Posix.getpid ()), rootPath: new Variant.string (this.project.path), - rootUri: new Variant.string (File.new_for_path (this.project.path).get_uri ()) + rootUri: new Variant.string (File.new_for_path (this.project.path).get_uri ()), + capabilities : this.buildDict ( + textDocument: this.buildDict ( + documentSymbol : this.buildDict ( + hierarchicalDocumentSymbolSupport : new Variant.boolean (true) + ) + ) + ) ), null, out return_value ); GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true)); + this.open_files = new Gee.ArrayList((a,b) => { + return a.path == b.path; + }); this.initialized = true; + this.getting_diagnostics = false; return; } catch (GLib.Error e) { GLib.debug ("LS replied with error %s", e.message); @@ -167,6 +240,7 @@ namespace Palete { if (this.launcher == null) { return; } + this.getting_diagnostics = false; this.in_close = true; GLib.debug("onClose called"); @@ -201,12 +275,10 @@ namespace Palete { public async void restartServer() { this.startServer(); - foreach(var f in this.open_files) { - this.document_open(f); - } + } - public bool isReady() + public override bool isReady() { if (this.closed) { this.log(LanguageClientAction.RESTART,"closed is set - restarting"); @@ -241,8 +313,12 @@ namespace Palete { { switch (method) { case "textDocument/publishDiagnostics": - GLib.debug("got notification %s : %s", method , Json.to_string (Json.gvariant_serialize (return_value), true)); - this.onDiagnostic(return_value); + //GLib.debug("got notification %s : %s", method , Json.to_string (Json.gvariant_serialize (return_value), true)); + + GLib.Idle.add(() => { + this.onDiagnostic(return_value); + return false; + }); return; default: break; @@ -252,20 +328,30 @@ namespace Palete { } + bool getting_diagnostics = false; /*** */ public void onDiagnostic(Variant? return_value) { - GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true)); + //GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true)); var dg = Json.gobject_deserialize (typeof (Lsp.Diagnostics), Json.gvariant_serialize (return_value)) as Lsp.Diagnostics; + GLib.debug("got diag for %s", dg.filename); this.log(LanguageClientAction.DIAG, dg.filename); + if (this.project.path == dg.filename) { + this.getting_diagnostics = false; + this.log(LanguageClientAction.DIAG_END, "diagnostics done"); + return; + + } + this.getting_diagnostics =true; var f = this.project.getByPath(dg.filename); if (f == null) { //GLib.debug("no file %s", dg.uri); //this.project.updateErrorsforFile(null); return; } + //GLib.debug("got Diagnostics for %s", f.path); f.updateErrors( dg.diagnostics ); @@ -276,9 +362,11 @@ namespace Palete { if (!this.isReady()) { return; } - if (!this.open_files.contains(file)) { - this.open_files.add(file); + if (this.open_files.contains(file)) { + return; } + this.open_files.add(file); + GLib.debug ("LS sent open"); try { @@ -382,7 +470,7 @@ namespace Palete { }); } - this.countdown = 3; + this.countdown = 2; this.change_queue_file = file; @@ -392,12 +480,19 @@ namespace Palete { public override async void document_change_force (JsRender.JsRender file, string contents) { + + if (!this.isReady()) { return; } - + this.countdown = -2; // not really relivant.. + this.change_queue_file = null; // this is more important.. + + if (!this.open_files.contains(file)) { + this.document_open(file); + } - GLib.debug ("LS send change"); + GLib.debug ("LS send change %s rev %d", file.path, file.version); var ar = new Json.Array(); var obj = new Json.Object(); obj.set_string_member("text", contents); @@ -543,25 +638,182 @@ namespace Palete { } + + + + static int hover_call_count = 1; + bool getting_hover = false; + //CompletionListInfo.itmems.parse_varient or CompletionListInfo.parsevarient - public override async Gee.ArrayList syntax (JsRender.JsRender file) throws GLib.Error + public override async Lsp.Hover hover (JsRender.JsRender file, int line, int offset) throws GLib.Error { /* partial_result_token , work_done_token context = null) */ - GLib.debug("get syntax %s", file.relpath); + //GLib.debug("get hover %s %d %d", file.relpath, (int)line, (int)offset); + var ret = new Lsp.Hover(); + //ret = null; + if (!this.isReady()) { + return ret; + } + if (this.getting_hover) { + return ret; + } + + hover_call_count++; + var call_id = yield this.queuer(hover_call_count); + + //GLib.debug("end hover call=%d count=%d", call_id, hover_call_count); + if (call_id != hover_call_count) { + //GLib.debug("get hover CANCELLED %s %d %d", file.relpath, (int)line, (int)offset); + return ret; + } + + //GLib.debug("get hover RUN %s %d %d", file.relpath, (int)line, (int)offset); + + this.getting_hover = true; + + Variant? return_value; + try { + yield this.jsonrpc_client.call_async ( + "textDocument/hover", + this.buildDict ( + + textDocument : this.buildDict ( ///TextDocumentItem; + uri: new GLib.Variant.string (file.to_url()), + version : new GLib.Variant.uint64 ( (uint64) file.version) + ), + position : this.buildDict ( + line : new GLib.Variant.uint64 ( (uint) line) , + character : new GLib.Variant.uint64 ( uint.max(0, (offset -1))) + ) + + ), + null, + out return_value + ); + } catch(GLib.Error e) { + this.getting_hover = false; + throw e; + } + this.getting_hover = false; + GLib.debug ("LS hover replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true)); + if (return_value == null) { + return ret; + } + + var json = Json.gvariant_serialize (return_value); + if (json.get_node_type() != Json.NodeType.OBJECT) { + return ret; + } + + + ret = Json.gobject_deserialize ( typeof (Lsp.Hover), json) as Lsp.Hover; + + return ret; + + + + } + + + static int doc_symbol_queue_call_count = 1; + + + + public override void queueDocumentSymbols (JsRender.JsRender file) + { + + this.documentSymbols.begin(file, (o, res) => { + var ret = documentSymbols.end(res); + file.navigation_tree_updated(ret); + }); + + + } + + bool getting_symbols = false; + + public override async Gee.ArrayList documentSymbols (JsRender.JsRender file) throws GLib.Error + { + /* partial_result_token , work_done_token context = null) */ + GLib.debug("get documentSymbols %s", file.relpath); var ret = new Gee.ArrayList(); //ret = null; if (!this.isReady()) { + GLib.debug("docsymbols not ready"); + return ret; + } + if (this.getting_symbols) { + GLib.debug("docsymbols currently getting symbols"); + return ret; + } + + + doc_symbol_queue_call_count++; + var call_id = yield this.queuer(doc_symbol_queue_call_count); + if (call_id != doc_symbol_queue_call_count) { + GLib.debug("docsymbols call id does not match %d %d" ,call_id , doc_symbol_queue_call_count); return ret; } + this.getting_symbols = true; + Variant? return_value; - yield this.jsonrpc_client.call_async ( - "textDocument/documentSymbol", + try { + yield this.jsonrpc_client.call_async ( + "textDocument/documentSymbol", + this.buildDict ( + + textDocument : this.buildDict ( ///TextDocumentItem; + uri: new GLib.Variant.string (file.to_url()), + version : new GLib.Variant.uint64 ( (uint64) file.version) + ) + + ), + null, + out return_value + ); + } catch(Error e) { + this.getting_symbols = false; + throw e; + } + this.getting_symbols = false; + + GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true)); + var json = Json.gvariant_serialize (return_value); + + + + var ar = json.get_array(); + GLib.debug ("LS replied with %D items", ar.get_length()); + for(var i = 0; i < ar.get_length(); i++ ) { + var add= Json.gobject_deserialize ( typeof (Lsp.DocumentSymbol), ar.get_element(i)) as Lsp.DocumentSymbol; + ret.add( add); + + } + return ret ; + + + } + // cant seem to get this to show anything!! + public override async Gee.ArrayList signatureHelp (JsRender.JsRender file, int line, int offset) throws GLib.Error { + /* partial_result_token , work_done_token context = null) */ + GLib.debug("get signatureHelp %s, %d, %d", file.relpath, line, offset); + var ret = new Gee.ArrayList(); + //ret = null; + if (!this.isReady()) { + return ret; + } + Variant? return_value; + yield this.jsonrpc_client.call_async ( + "textDocument/signatureHelp", this.buildDict ( textDocument : this.buildDict ( ///TextDocumentItem; - uri: new GLib.Variant.string (file.to_url()), - version : new GLib.Variant.uint64 ( (uint64) file.version) - ) + uri: new GLib.Variant.string (file.to_url()) + ), + position : this.buildDict ( + line : new GLib.Variant.uint64 ( (uint) line) , + character : new GLib.Variant.uint64 ( uint.max(0, (offset -1))) + ) ), null, @@ -571,21 +823,54 @@ namespace Palete { GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true)); var json = Json.gvariant_serialize (return_value); - + if (json.get_node_type() != Json.NodeType.ARRAY) { + return ret; + } + var ar = json.get_array(); + GLib.debug ("LS replied with %D items", ar.get_length()); for(var i = 0; i < ar.get_length(); i++ ) { - var add= Json.gobject_deserialize ( typeof (Lsp.DocumentSymbol), ar.get_element(i)) as Lsp.DocumentSymbol; + var add= Json.gobject_deserialize ( typeof (Lsp.SignatureInformation), ar.get_element(i)) as Lsp.SignatureInformation; ret.add( add); } - return ret ; + return ret ; - + } + // ok for general symbol search, not much details though. + public override async Gee.ArrayList symbol (string sym) throws GLib.Error + { + /* partial_result_token , work_done_token context = null) */ + GLib.debug("get symbol %s,", sym); + var ret = new Gee.ArrayList(); + //ret = null; + if (!this.isReady()) { + return ret; + } + Variant? return_value; + yield this.jsonrpc_client.call_async ( + "workspace/symbol", + this.buildDict ( + query : new GLib.Variant.string (sym) + ), + null, + out return_value + ); + +GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true)); + return ret; } } + + + + + + + } \ No newline at end of file