Fix #8103 - updating of tree
[roobuilder] / src / Palete / LanguageClientVala.vala
index f35f74f..1be2b05 100644 (file)
@@ -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<JsRender.JsRender> 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,35 +55,94 @@ namespace Palete {
                {
                        // extend versions will proably call initialize to start and connect to server.
                        base(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_force(this.change_queue_file,  this.change_queue_file_source);
-                                       this.change_queue_file = null;
-                                          
-                               }
-                               return true;
-                       });
+
+                       if (this.change_queue_id == 0 ) {
+                               this.change_queue_id = GLib.Timeout.add_seconds(1, () => {
+                                       this.run_change_queue();
+                                       this.run_doc_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 ;
+               }
+                
                 
+               void run_doc_queue()
+               {
+               
+                       if (this.doc_queue_file == null) {
+                               return ;
+                       }
+                       if (this.doc_countdown < -1) {
+                               return;
+                       }
+                       this.doc_countdown--;
+
+                       if (this.doc_countdown < 0){
+                               var sendfile = this.doc_queue_file;
+                               this.documentSymbols.begin(this.doc_queue_file, (o, res) => {
+                                       var ret = this.documentSymbols.end(res);
+                                       sendfile.navigation_tree_updated(ret);
+                               });
+                               this.doc_queue_file = null;
+                                  
+                       }
+                       return ;
+               }
                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 {
@@ -123,9 +194,10 @@ 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 ();
@@ -143,13 +215,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<JsRender.JsRender>((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);
@@ -165,6 +248,7 @@ namespace Palete {
                        if (this.launcher == null) {
                                return;
                        }
+                       this.getting_diagnostics = false;
                        this.in_close = true;
                        GLib.debug("onClose called");
                        
@@ -196,6 +280,11 @@ namespace Palete {
                        this.in_close = false;
                }
        
+               public async void restartServer()
+               {
+                       this.startServer();
+                        
+               }
        
                public bool isReady()
                {
@@ -204,10 +293,11 @@ namespace Palete {
                                GLib.debug("server stopped = restarting");
                                this.initialized = false;
                                this.closed = false;
-                               this.startServer();
-                               foreach(var f in this.open_files) {
-                                       this.document_open(f);
-                               }
+                               GLib.MainLoop loop = new GLib.MainLoop ();
+                               this.restartServer.begin ((obj, async_res) => {
+                                       this.restartServer.end(async_res);
+                                       loop.quit ();
+                               });
                                return false; // can't do an operation yet?
                                 
                        }
@@ -221,7 +311,7 @@ namespace Palete {
                                return false;
                        }
                        // restart server..
-
+               
                        
                        
                        return true;
@@ -231,7 +321,12 @@ namespace Palete {
                {
                        switch (method) {
                                case "textDocument/publishDiagnostics":
-                                       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;
@@ -241,31 +336,32 @@ 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));                                     
                        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);
+                               //this.project.updateErrorsforFile(null);
                                return;
                        }
-                       foreach(var v in f.errorsByType.values) {
-                               v.remove_all();
-                       }
-                       foreach(var diag in dg.diagnostics) {
-                               var ce = new CompileError.new_from_diagnostic(f, diag);
-                               if (!f.errorsByType.has_key(ce.category)) {
-                                       f.errorsByType.set(ce.category, new  GLib.ListStore(typeof(CompileError)));
-                               }
-                               f.errorsByType.get(ce.category).append(ce);
-                       }
-                       f.project.updateErrorsforFile(f);
+                       //GLib.debug("got Diagnostics for %s", f.path);
+                       f.updateErrors( dg.diagnostics );
+                        
                        
                }
                
@@ -274,9 +370,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 {
@@ -301,22 +399,33 @@ namespace Palete {
 
                }
                
-               public override  void document_save (JsRender.JsRender file)  
+               public override  async void document_save (JsRender.JsRender file)  
        {
                        if (!this.isReady()) {
                                return;
                        }
+                       // save only really flags the file on the server - to actually force a change update - we need to 
+                       // flag it as changed.
+                       yield this.document_change_force(file, file.toSource());
+                       
                        this.change_queue_file = null;
                        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)
-                                               )
-                                       ),
+                        
+                               var args = this.buildDict (  
+                                       textDocument : this.buildDict (    ///TextDocumentItem;
+                                               uri: new GLib.Variant.string (file.to_url()),
+                                               version :  new GLib.Variant.uint64 ( (uint64) file.version)
+                                       )
+                               );
+                        
+                               //GLib.debug ("textDocument/save send with %s", Json.to_string (Json.gvariant_serialize (args), true));                                 
+                       
+                        
+                        
+                                 yield this.jsonrpc_client.send_notification_async  (
+                                       "textDocument/didSave",
+                                       args,
                                        null 
                                );
                                this.log(LanguageClientAction.SAVE, file.path);
@@ -364,7 +473,9 @@ namespace Palete {
                public override void document_change (JsRender.JsRender file )    
                {
                        if (this.change_queue_file != null && this.change_queue_file.path != file.path) {
-                               this.document_change_force(this.change_queue_file, this.change_queue_file_source);
+                               this.document_change_force.begin(this.change_queue_file, this.change_queue_file_source, (o, res) => {
+                                       this.document_change_force.end(res);
+                               });
                        }
                        
                        this.countdown = 3;
@@ -375,14 +486,21 @@ namespace Palete {
                }
        
 
-               public override void document_change_force (JsRender.JsRender file, string contents)  
+               public override async void document_change_force (JsRender.JsRender file, string contents)  
        {
+                       
+                       
                        if (!this.isReady()) {
                                return;
                        }
-                            
+                       this.countdown = 9; // 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);
@@ -391,7 +509,7 @@ namespace Palete {
                        node.set_array(ar);
                        this.log(LanguageClientAction.CHANGE, file.path);
                         try {
-                               this.jsonrpc_client.send_notification (
+                               yield this.jsonrpc_client.send_notification_async (
                                        "textDocument/didChange",
                                        this.buildDict (  
                                                textDocument : this.buildDict (    ///TextDocumentItem;
@@ -528,11 +646,66 @@ namespace Palete {
                
 
                }
+               
+               
                //CompletionListInfo.itmems.parse_varient  or CompletionListInfo.parsevarient
-               public override async Gee.ArrayList<Lsp.DocumentSymbol> 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);
+                       var ret = new Lsp.Hover();      
+                       //ret = null;
+                   if (!this.isReady()) {
+                               return ret;
+                       }
+                       Variant? return_value;
+                       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
+                       );
+                       
+                       
+                       GLib.debug ("LS replied with %s", Json.to_string (Json.gvariant_serialize (return_value), true));                                       
+                       var json = Json.gvariant_serialize (return_value);
+                       ret =  Json.gobject_deserialize ( typeof (Lsp.Hover),  json) as Lsp.Hover; 
+                       
+                       return ret;
+                       
+               
+
+               }
+               
+               public override void queueDocumentSymbols (JsRender.JsRender file) 
+               {
+                       if (this.doc_queue_file != null && this.doc_queue_file.path != file.path) {
+                               var sendfile = this.doc_queue_file;
+                               this.documentSymbols.begin(this.doc_queue_file, (o, res) => {
+                                       var ret = documentSymbols.end(res);
+                                       sendfile.navigation_tree_updated(ret);
+                               });
+                       }
+                       
+                       this.doc_countdown = 3;
+                       this.doc_queue_file = file;
+               }
+               
+        
+               public override async Gee.ArrayList<Lsp.DocumentSymbol> 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<Lsp.DocumentSymbol>();      
                        //ret = null;
                    if (!this.isReady()) {
@@ -560,17 +733,18 @@ namespace Palete {
                         
 
                        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 ;
+                       return ret ;
                        
                
-
                }
                
+               
        }
        
 }
\ No newline at end of file