2 generic interface to language server
3 ?? first of will be for vala... but later?
4 based on gvls-client-jsonrpc (loosly)
5 and vala-language-server
14 public abstract class LanguageClient : Jsonrpc.Server {
16 public Project.Project project;
17 private GLib.SubprocessLauncher launcher;
18 private GLib.Subprocess subprocess;
19 private IOStream subprocess_stream;
20 public Jsonrpc.Client? jsonrpc_client = null;
22 protected LanguageClient(Project.Project project)
24 // extend versions will proably call initialize to start and connect to server.
25 this.project = project;
30 public bool initProcess(string process_path)
32 this.launcher = new GLib.SubprocessLauncher (SubprocessFlags.STDIN_PIPE | SubprocessFlags.STDOUT_PIPE);
33 this.launcher.set_environ(GLib.Environ.get());
35 this.subprocess = launcher.spawnv ({ process_path });
36 var input_stream = this.subprocess.get_stdout_pipe ();
37 var output_stream = this.subprocess.get_stdin_pipe ();
39 if (input_stream is GLib.UnixInputStream && output_stream is GLib.UnixOutputStream) {
41 if (!GLib.Unix.set_fd_nonblocking(((GLib.UnixInputStream)input_stream).fd, true)
42 || !GLib.Unix.set_fd_nonblocking (((GLib.UnixOutputStream)output_stream).fd, true))
44 GLib.debug("could not set pipes to nonblocking");
48 this.subprocess_stream = new SimpleIOStream (input_stream, output_stream);
49 this.accept_io_stream ( this.subprocess_stream);
50 } catch (GLib.Error e) {
51 GLib.debug("subprocess startup error %s", e.message);
58 utility method to build variant based queries
60 public Variant buildDict (...) {
61 var builder = new GLib.VariantBuilder (new GLib.VariantType ("a{sv}"));
64 string? key = l.arg ();
68 Variant val = l.arg ();
69 builder.add ("{sv}", key, val);
71 return builder.end ();
74 public override void client_accepted (Jsonrpc.Client client)
76 if (this.jsonrpc_client == null) {
77 this.jsonrpc_client = client;
79 GLib.debug("client accepted connection - calling init server");
82 this.jsonrpc_client.notification.connect((method, paramz) => {
83 this.onNotification(method, paramz);
86 this.jsonrpc_client.failed.connect(() => {
87 GLib.debug("language server server has failed");
90 this.initialize_server ();
97 if (!this.initialized) {
98 GLib.debug("Server has not been initialized");
101 if (this.sent_shutdown) {
102 GLib.debug("Server has been started its shutting down process");
109 public abstract void initialize_server();
111 //public abstract void initialize_server() ;
113 protected bool initialized = false;
114 bool sent_shutdown = false;
117 public void onNotification(string method, Variant? return_value)
120 case "textDocument/publishDiagnostics":
121 this.onDiagnostic(return_value);
127 GLib.debug("got notification %s : %s", method , Json.to_string (Json.gvariant_serialize (return_value), true));
134 public void onDiagnostic(Variant? return_value)
136 var dg = Json.gobject_deserialize (typeof (Lsp.Diagnostics), Json.gvariant_serialize (return_value)) as Lsp.Diagnostics;
137 var f = this.project.getByPath(dg.filename);
139 GLib.debug("no file %s", dg.uri);
142 foreach(var v in f.errorsByType.values) {
145 foreach(var diag in dg.diagnostics) {
146 var ce = new CompileError.new_from_diagnostic(f, diag);
147 if (!f.errorsByType.has_key(ce.category)) {
148 f.errorsByType.set(ce.category, new GLib.ListStore(typeof(CompileError)));
150 f.errorsByType.get(ce.category).append(ce);
152 f.project.updateErrorsforFile(f);
156 public void document_open (JsRender.JsRender file)
158 if (!this.isReady()) {
161 GLib.debug ("LS sent open");
163 this.jsonrpc_client.send_notification (
164 "textDocument/didOpen",
166 textDocument : this.buildDict (
167 uri: new Variant.string (file.to_url()),
168 languageId : new Variant.string (file.language_id()),
169 version : new GLib.Variant.uint64 ( (uint64) file.version),
170 text : new Variant.string (file.toSource())
175 } catch( GLib.Error e) {
176 GLib.debug ("LS sent open err %s", e.message);
181 public void document_save (JsRender.JsRender file)
183 if (!this.isReady()) {
186 GLib.debug ("LS send save");
188 this.jsonrpc_client.send_notification (
189 "textDocument/didChange",
191 textDocument : this.buildDict ( ///TextDocumentItem;
192 uri: new GLib.Variant.string (file.to_url())
198 } catch( GLib.Error e) {
199 GLib.debug ("LS sent save err %s", e.message);
204 public void document_close (JsRender.JsRender file)
206 if (!this.isReady()) {
209 GLib.debug ("LS send close");
211 this.jsonrpc_client.send_notification (
212 "textDocument/didChange",
214 textDocument : this.buildDict ( ///TextDocumentItem;
215 uri: new GLib.Variant.string (file.to_url())
221 } catch( GLib.Error e) {
222 GLib.debug ("LS sent close err %s", e.message);
227 public void document_change (JsRender.JsRender file)
229 if (!this.isReady()) {
232 GLib.debug ("LS send change");
233 var ar = new Json.Array();
234 var obj = new Json.Object();
235 obj.set_string_member("text", file.toSource());
236 ar.add_object_element(obj);
237 var node = new Json.Node(Json.NodeType.ARRAY);
240 this.jsonrpc_client.send_notification (
241 "textDocument/didChange",
243 textDocument : this.buildDict ( ///TextDocumentItem;
244 uri: new GLib.Variant.string (file.to_url()),
245 version : new GLib.Variant.uint64 ( (uint64) file.version)
247 contentChanges : Json.gvariant_deserialize (node, null)
252 } catch( GLib.Error e) {
253 GLib.debug ("LS sent close err %s", e.message);
258 public void exit () throws GLib.Error
260 if (!this.isReady()) {
263 this.sent_shutdown = true;
265 this.jsonrpc_client.send_notification_async (
272 public async void shutdown () throws GLib.Error
274 if (!this.isReady()) {
277 this.sent_shutdown = true;
278 Variant? return_value;
279 yield this.jsonrpc_client.call_async (
285 GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true));
287 //public async ??/symbol (string symbol) throws GLib.Error {
289 // and now for the important styff..
293 @triggerType 1 = typing or ctl-spac, 2 = tiggercharactres? 3= inside completion?
295 public async void completion (JsRender.JsRender file, int line, int offset , int triggerType = 1, out Lsp.CompletionList? ret) throws GLib.Error
297 /* partial_result_token , work_done_token context = null) */
298 GLib.debug("get completion %s @ %d:%d", file.relpath, line, offset);
301 if (!this.isReady()) {
304 Variant? return_value;
305 yield this.jsonrpc_client.call_async (
306 "textDocument/completion",
308 context : this.buildDict ( ///CompletionContext;
309 triggerKind: new GLib.Variant.int32 (triggerType)
310 // triggerCharacter : new GLib.Variant.string ("")
312 textDocument : this.buildDict ( ///TextDocumentItem;
313 uri: new GLib.Variant.string (file.to_url()),
314 version : new GLib.Variant.uint64 ( (uint64) file.version)
316 position : this.buildDict (
317 line : new GLib.Variant.uint64 ( (uint64) line) ,
318 character : new GLib.Variant.uint64 ( (uint64) offset)
326 //GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true));
327 var json = Json.gvariant_serialize (return_value);
328 var ar = json.get_array();
331 ret = Json.gobject_deserialize (typeof (Lsp.CompletionList), json) as Lsp.CompletionList;
334 ret = new Lsp.CompletionList();
335 for(var i = 0; i < ar.get_length(); i++ ) {
336 var add= Json.gobject_deserialize ( typeof (Lsp.CompletionItem), ar.get_element(i)) as Lsp.CompletionItem;
345 //CompletionListInfo.itmems.parse_varient or CompletionListInfo.parsevarient