namespace Palete {
- public enum LanguageClientAction {
- INIT,
- LAUNCH,
- ACCEPT,
-
- DIAG,
- OPEN,
- SAVE,
- CLOSE,
- CHANGE,
- TERM,
- COMPLETE,
- COMPLETE_REPLY,
-
- RESTART,
- ERROR,
- ERROR_START,
- ERROR_RPC,
- ERROR_REPLY,
-
- EXIT,
- }
+
public abstract class LanguageClient : Jsonrpc.Server {
public Project.Project project;
- private GLib.SubprocessLauncher launcher = null;
- private GLib.Subprocess? subprocess = null;
- private IOStream? subprocess_stream = null;
+ private GLib.SubprocessLauncher launcher;
+ private GLib.Subprocess subprocess;
+ private IOStream subprocess_stream;
public Jsonrpc.Client? jsonrpc_client = null;
- Gee.ArrayList<JsRender.JsRender> open_files;
- private JsRender.JsRender? _change_queue_file = null;
- private string change_queue_file_source = "";
-
- JsRender.JsRender? change_queue_file {
- set {
- this.change_queue_file_source = value == null ? "" : value.toSource();
- this._change_queue_file = value;
- }
- get {
- return this._change_queue_file;
- }
- }
-
- uint change_queue_id = 0;
- int countdown = 0;
- protected bool initialized = false;
- bool sent_shutdown = false;
- private bool _closed = false;
- private bool closed {
- get { return this._closed ; }
- set {
- GLib.debug("closed has been set? to %s" , value ? "TRUE" : "FALSE" );
- this._closed = value;
- }
- }
-
- public signal void log(LanguageClientAction action, string message);
-
-
protected LanguageClient(Project.Project project)
{
// extend versions will proably call initialize to start and connect to server.
this.project = project;
- this.open_files = new Gee.ArrayList<JsRender.JsRender>();
- 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_real(this.change_queue_file, this.change_queue_file_source);
- this.change_queue_file = null;
-
- }
- return true;
- });
+
}
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());
try {
-
-
this.subprocess = launcher.spawnv ({ process_path });
-
- this.subprocess.wait_async.begin( null, ( obj,res ) => {
- try {
- this.subprocess.wait_async.end(res);
- } catch (GLib.Error e) {
- this.log(LanguageClientAction.ERROR_START, e.message);
- GLib.debug("subprocess startup error %s", e.message);
- }
- this.log(LanguageClientAction.EXIT, "process ended");
- GLib.debug("Subprocess ended %s", process_path);
- this.onClose();
-
- });
var input_stream = this.subprocess.get_stdout_pipe ();
var output_stream = this.subprocess.get_stdin_pipe ();
if (!GLib.Unix.set_fd_nonblocking(((GLib.UnixInputStream)input_stream).fd, true)
|| !GLib.Unix.set_fd_nonblocking (((GLib.UnixOutputStream)output_stream).fd, true))
{
- GLib.debug("could not set pipes to nonblocking");
- this.onClose();
+ GLib.debug("could not set pipes to nonblocking");
return false;
}
}
- this.subprocess_stream = new GLib.SimpleIOStream (input_stream, output_stream);
+ this.subprocess_stream = new SimpleIOStream (input_stream, output_stream);
this.accept_io_stream ( this.subprocess_stream);
} catch (GLib.Error e) {
- this.log(LanguageClientAction.ERROR_START, e.message);
- GLib.debug("subprocess startup error %s", e.message);
- this.onClose();
+ GLib.debug("subprocess startup error %s", e.message);
return false;
}
return true;
}
- bool in_close = false;
- protected void onClose()
- {
- if (this.in_close) {
- return;
- }
- if (this.launcher == null) {
- return;
- }
- this.in_close = true;
- GLib.debug("onClose called");
-
- if (this.jsonrpc_client != null) {
- try {
- this.jsonrpc_client.close();
- } catch (GLib.Error e) {
- GLib.debug("rpc Error close error %s", e.message);
- }
- }
- if (this.subprocess_stream != null) {
- try {
- this.subprocess_stream.close();
- } catch (GLib.Error e) {
- GLib.debug("stream Error close %s", e.message);
- }
- }
- if (this.subprocess != null) {
- this.subprocess.force_exit();
- }
- if (this.launcher != null) {
- this.launcher.close();
- }
-
- this.launcher = null;
- this.subprocess = null;
- this.jsonrpc_client = null;
- this.closed = true;
- this.in_close = false;
- }
+
/**
utility method to build variant based queries
*/
this.jsonrpc_client = client;
GLib.debug("client accepted connection - calling init server");
- this.log(LanguageClientAction.ACCEPT, "client accepted");
+
this.jsonrpc_client.notification.connect((method, paramz) => {
this.onNotification(method, paramz);
});
this.jsonrpc_client.failed.connect(() => {
- this.log(LanguageClientAction.ERROR_RPC, "client failed");
- this.onClose();
-
GLib.debug("language server server has failed");
});
}
public bool isReady()
{
- if (this.closed) {
- this.log(LanguageClientAction.RESTART,"closed is set - restarting");
- GLib.debug("server stopped = restarting");
- this.initialized = false;
- this.closed = false;
- this.startServer();
- foreach(var f in this.open_files) {
- this.document_open(f);
- }
- return false; // can't do an operation yet?
-
- }
-
if (!this.initialized) {
GLib.debug("Server has not been initialized");
return false;
GLib.debug("Server has been started its shutting down process");
return false;
}
- // restart server..
-
-
-
return true;
}
public abstract void initialize_server();
- public abstract void startServer();
+
//public abstract void initialize_server() ;
-
+ protected bool initialized = false;
+ bool sent_shutdown = false;
public void onNotification(string method, Variant? return_value)
*/
public void onDiagnostic(Variant? return_value)
{
-
var dg = Json.gobject_deserialize (typeof (Lsp.Diagnostics), Json.gvariant_serialize (return_value)) as Lsp.Diagnostics;
- this.log(LanguageClientAction.DIAG, dg.filename);
var f = this.project.getByPath(dg.filename);
if (f == null) {
- //GLib.debug("no file %s", dg.uri);
- this.project.updateErrorsforFile(null);
+ GLib.debug("no file %s", dg.uri);
return;
}
foreach(var v in f.errorsByType.values) {
if (!this.isReady()) {
return;
}
- if (!this.open_files.contains(file)) {
- this.open_files.add(file);
- }
-
GLib.debug ("LS sent open");
try {
this.jsonrpc_client.send_notification (
),
null
);
- this.log(LanguageClientAction.OPEN, file.path);
} catch( GLib.Error e) {
- this.log(LanguageClientAction.ERROR_RPC, e.message);
- this.onClose();
GLib.debug ("LS sent open err %s", e.message);
}
if (!this.isReady()) {
return;
}
- this.change_queue_file = null;
- GLib.debug ("LS send save");
+ GLib.debug ("LS send save");
try {
this.jsonrpc_client.send_notification (
"textDocument/didChange",
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())
+
)
),
null
);
- this.log(LanguageClientAction.SAVE, file.path);
} catch( GLib.Error e) {
- this.log(LanguageClientAction.ERROR_RPC, e.message);
- GLib.debug ("LS save err %s", e.message);
- this.onClose();
+ GLib.debug ("LS sent save err %s", e.message);
}
if (!this.isReady()) {
return;
}
- this.change_queue_file = null;
-
- if (this.open_files.contains(file)) {
- this.open_files.remove(file);
- }
- this.log(LanguageClientAction.CLOSE, file.path);
- GLib.debug ("LS send close");
+ GLib.debug ("LS send close");
try {
this.jsonrpc_client.send_notification (
"textDocument/didChange",
null
);
} catch( GLib.Error e) {
- this.log(LanguageClientAction.ERROR_RPC, e.message);
- GLib.debug ("LS close err %s", e.message);
- this.onClose();
+ GLib.debug ("LS sent close err %s", e.message);
}
}
-
-
- public void document_change (JsRender.JsRender file )
- {
- if (this.change_queue_file != null && this.change_queue_file.path != file.path) {
- this.document_change_real(this.change_queue_file, this.change_queue_file_source);
- }
-
- this.countdown = 3;
- this.change_queue_file = file;
-
-
-
- }
-
-
- public void document_change_real (JsRender.JsRender file, string contents)
+ public void document_change (JsRender.JsRender file)
{
if (!this.isReady()) {
return;
}
-
-
GLib.debug ("LS send change");
var ar = new Json.Array();
var obj = new Json.Object();
- obj.set_string_member("text", contents);
+ obj.set_string_member("text", file.toSource());
ar.add_object_element(obj);
var node = new Json.Node(Json.NodeType.ARRAY);
node.set_array(ar);
- this.log(LanguageClientAction.CHANGE, file.path);
try {
this.jsonrpc_client.send_notification (
"textDocument/didChange",
null
);
} catch( GLib.Error e) {
- this.log(LanguageClientAction.ERROR_RPC, e.message);
- GLib.debug ("LS change err %s", e.message);
- this.onClose();
+ GLib.debug ("LS sent close err %s", e.message);
}
}
- // called by close window (on last window)...
public void exit () throws GLib.Error
{
if (!this.isReady()) {
-
return;
}
- this.log(LanguageClientAction.TERM, "SEND exit");
+ this.sent_shutdown = true;
- this.jsonrpc_client.send_notification (
+ this.jsonrpc_client.send_notification_async (
"exit",
null,
null
);
- this.onClose();
-
- }
- // not used currently..
+
+ }
public async void shutdown () throws GLib.Error
{
if (!this.isReady()) {
return;
}
- this.log(LanguageClientAction.TERM, "SEND shutodwn");
this.sent_shutdown = true;
Variant? return_value;
yield this.jsonrpc_client.call_async (
@triggerType 1 = typing or ctl-spac, 2 = tiggercharactres? 3= inside completion?
*/
- public async Lsp.CompletionList? completion(JsRender.JsRender file, int line, int offset , int triggerType = 1) throws GLib.Error
+ public async void completion (JsRender.JsRender file, int line, int offset , int triggerType = 1, out Lsp.CompletionList? ret) throws GLib.Error
{
/* partial_result_token , work_done_token context = null) */
GLib.debug("get completion %s @ %d:%d", file.relpath, line, offset);
- var ret = new Lsp.CompletionList();
-
+ ret = null;
if (!this.isReady()) {
- GLib.debug("completion - language server not ready");
- return ret;
+ return;
}
- // make sure completion has the latest info..
- //if (this.change_queue_file != null && this.change_queue_file.path != file.path) {
- // this.document_change_real(this.change_queue_file, this.change_queue_file_source);
- // this.change_queue_file != null;
- //}
- this.log(LanguageClientAction.COMPLETE, "SEND complete %s @ %d:%d".printf(file.relpath, line, offset) );
-
Variant? return_value;
-
- var args = this.buildDict (
+ yield this.jsonrpc_client.call_async (
+ "textDocument/completion",
+ this.buildDict (
context : this.buildDict ( ///CompletionContext;
triggerKind: new GLib.Variant.int32 (triggerType)
// triggerCharacter : new GLib.Variant.string ("")
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)))
+ line : new GLib.Variant.uint64 ( (uint64) line) ,
+ character : new GLib.Variant.uint64 ( (uint64) offset)
)
- );
-
- GLib.debug ("textDocument/completion send with %s", Json.to_string (Json.gvariant_serialize (args), true));
-
- yield this.jsonrpc_client.call_async (
- "textDocument/completion",
- args,
+ ),
null,
out return_value
);
//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();
-
- if (json.get_node_type() == Json.NodeType.OBJECT) {
+ if (ar == null) {
ret = Json.gobject_deserialize (typeof (Lsp.CompletionList), json) as Lsp.CompletionList;
- this.log(LanguageClientAction.COMPLETE_REPLY, "GOT complete %d items".printf(ret.items.size) );
- GLib.debug ("LS replied with Object");
- return ret;
+ return;
}
-
- if (json.get_node_type() != Json.NodeType.ARRAY) {
- GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true));
- this.log(LanguageClientAction.ERROR_REPLY, "GOT something else??");
- return ret;
-
- }
- var ar = json.get_array();
-
+ ret = new Lsp.CompletionList();
for(var i = 0; i < ar.get_length(); i++ ) {
var add= Json.gobject_deserialize ( typeof (Lsp.CompletionItem), ar.get_element(i)) as Lsp.CompletionItem;
ret.items.add( add);
}
- this.log(LanguageClientAction.COMPLETE_REPLY, "GOT array %d items".printf(ret.items.size) );
- GLib.debug ("LS replied with Array");
- return ret;
+
+
}
//CompletionListInfo.itmems.parse_varient or CompletionListInfo.parsevarient
- public async Gee.ArrayList<Lsp.DocumentSymbol> syntax (JsRender.JsRender file) throws GLib.Error
- {
- /* partial_result_token , work_done_token context = null) */
- GLib.debug("get syntax %s", file.relpath);
- var ret = new Gee.ArrayList<Lsp.DocumentSymbol>();
- //ret = null;
- if (!this.isReady()) {
- return ret;
- }
- Variant? return_value;
- 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
- );
-
-
- 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();
- 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 ;
-
-
- }
}
}