Fix #8044 - fixing varous crashes, correct tree drop location and empty nodes after...
[roobuilder] / src / JsRender / JsRender.vala
1 //<Script type="text/javascript">
2
3 namespace JsRender {
4
5
6         public errordomain Error {
7                 INVALID_FORMAT,
8                 RENAME_FILE_EXISTS
9         }
10                 
11         public abstract class JsRender  : Object {
12                 /**
13                  * @cfg {Array} doubleStringProps list of properties that can be double quoted.
14                  */
15                 public Gee.ArrayList<string> doubleStringProps;
16                 
17                 public string id  = "";
18                 public string name { get; set; default = ""; }   // is the JS name of the file.
19                 public string fullname = "";
20                 public string path = "";  // is the full path to the file.
21                 
22                 public  string relpath {
23                         owned get { 
24                                 return  this.project.path  == this.path ? "" : this.path.substring(this.project.path.length+1);
25                         } 
26                         private set {}
27                 }
28                 public  string reldir {
29                         owned get { 
30                                 return  this.project.path == this.dir ? "" : this. dir.substring(this.project.path.length+1);
31                         } 
32                         private set {}
33                 }
34                 
35                 public  string  dir {
36                         owned get { 
37                                 return GLib.Path.get_dirname(this.path);
38                                  
39                         } 
40                         private set {}
41                 }
42                 
43                 public string file_namespace {
44                         public owned get {
45                                 if (!this.name.contains(".")) {
46                                         return "";
47                                 }
48                                 var bits = this.name.split(".");
49                                 return bits[0];
50                         }
51                         private set {}
52                 }
53                 public string file_without_namespace {
54                         public  owned get {
55                                 if (!this.name.contains(".")) {
56                                         return this.name;
57                                 }
58                                 var bits = this.name.split(".");
59                                 return this.name.substring(bits[0].length +1);
60                         }
61                         private set {}
62                 }
63                 
64                 public string file_ext {
65                         public owned get {
66                                 if (!this.path.contains(".")) {
67                                         return "";
68                                 }
69                                 var bits = this.name.split(".");
70                                 return bits[bits.length-1];
71                         }
72                         private set {}
73                 }
74                 public string parent = "";  // JS parent.
75                 public string region = "";  // RooJS - insert region.
76         
77                 public string title = "";  // a title.. ?? nickname.. ??? -
78
79                 private int _version = 1;   // should we increment this based on the node..?
80                 public int version {
81                         get {
82                                 if (this.tree != null) {
83                                         return this.tree.updated_count;
84                                 }
85                                 return ++this._version; // increased on every call? - bit of a kludge until we do real versioning
86                         }
87                         private set {
88                                 
89                                 this._version = value;
90                                 this.updateUndo();
91                         }
92                         
93                 }
94
95
96                 public string permname;
97                 public string language;
98                 public string content_type;
99                 public string modOrder;
100                 public string xtype;
101                 public uint64 webkit_page_id; // set by webkit view - used to extract extension/etc..
102                 public bool gen_extended  = false; // nodetovala??
103
104                 public Project.Project project;
105
106                 // GTK Specifc
107  
108                 public string build_module; // module to build if we compile (or are running tests...)      
109
110                 //Project : false, // link to container project!
111                 
112                 public Node tree; // the tree of nodes.
113                 
114                 //public GLib.List<JsRender> cn; // child files.. (used by project ... should move code here..)
115
116                 public bool hasParent; 
117                 
118                 public bool loaded;
119                 
120                 public Gee.HashMap<string,string> transStrings; // map of md5 -> string.
121                 public  Gee.HashMap<string,string> namedStrings;
122                 
123                 public Gee.HashMap<int,string> undo_json;
124                 
125                 //public        Gee.HashMap<string, GLib.ListStore> errorsByType;
126                 private Gee.ArrayList<Lsp.Diagnostic> errors;
127                 public int error_counter {
128                         get; private set; default = 0;
129                 }
130
131                 //public signal void changed (Node? node, string source);  (not used?)
132                 
133                  
134                 public signal void compile_notice(string type, string file, int line, string message);
135                 
136                 private  GLib.Icon? _icon = null;
137                 
138                 public GLib.Icon? icon { 
139                         private set {}
140                         get {
141                                 if (this._icon !=  null) {
142                                         return this._icon;
143                                 }
144                                 
145                                 if (this.path == "") {
146                                         return null;
147                                 }
148                                 if (!GLib.FileUtils.test(this.path, GLib.FileTest.EXISTS)) {
149                                         return null;
150                                 }
151                                 try {
152                                         this._icon = File.new_for_path(this.path).query_info("standard::icon",GLib.FileQueryInfoFlags.NONE).get_icon();
153                                 } catch(GLib.Error e) {
154                                         return null;
155                                 }
156                                 return this._icon;
157                         }
158                 }
159                 
160                 
161                 /**
162                  * UI componenets
163                  * 
164                  */
165                 //public Xcls_Editor editor;
166                 public GLib.ListStore childfiles; // used by directories..
167                 
168                 
169                 //abstract JsRender(Project.Project project, string path); 
170                 
171                 protected JsRender(Project.Project project, string path)
172                 {
173                     
174                         //this.cn = new GLib.List<JsRender>();
175                         GLib.debug("new jsrender %s", path);
176                         this.path = path;
177                         this.project = project;
178                         this.hasParent = false;
179                         this.parent = "";
180                         this.tree = null; 
181                         this.title = "";
182                         this.region = "";
183                         this.permname = "";
184                         this.modOrder = "";
185                         this.language = "";
186                         this.content_type = "";
187                         this.build_module = "";
188                         //this.loaded = false;
189                         //print("JsRender.cto() - reset transStrings\n");
190                         this.transStrings = new Gee.HashMap<string,string>();
191                         this.namedStrings = new Gee.HashMap<string,string>();
192                         // should use basename reallly...
193                         
194                         var ar = this.path.split("/");
195                         // name is in theory filename without .bjs (or .js eventually...)
196                         try {
197                                 Regex regex = new Regex ("\\.(bjs)$");
198
199                                 this.name = ar.length > 0 ? regex.replace(ar[ar.length-1],ar[ar.length-1].length, 0 , "") : "";
200                         } catch (GLib.Error e) {
201                                 this.name = "???";
202                         }
203                         this.fullname = (this.parent.length > 0 ? (this.parent + ".") : "" ) + this.name;
204
205                         this.doubleStringProps = new Gee.ArrayList<string>();
206                         this.childfiles = new GLib.ListStore(typeof(JsRender));
207                         //this.errorsByType  = new Gee.HashMap<string, GLib.ListStore>();
208                         this.errors = new Gee.ArrayList<Lsp.Diagnostic>((a,b) => { return a.equals(b); }); 
209                         this.undo_json = new Gee.HashMap<int,string>();
210
211
212                 }
213                 
214                 public void renameTo(string name) throws  Error
215                 {
216                         if (this.xtype == "PlainFile") {
217                                 return;
218                         }
219                         var bjs = GLib.Path.get_dirname(this.path) +"/" +  name + ".bjs";
220                         if (FileUtils.test(bjs, FileTest.EXISTS)) {
221                                 throw new Error.RENAME_FILE_EXISTS("File exists %s\n",name);
222                         }
223                         GLib.FileUtils.remove(this.path);
224                         this.removeFiles();
225                         // remove other files?
226                         
227                         this.name = name;
228                         this.path = bjs;
229                         
230                 }
231                 
232
233                 
234                 // not sure why xt is needed... -> project contains xtype..
235                 
236                 public static JsRender factory(string xt, Project.Project project, string path) throws Error
237                 {
238          
239                         switch (xt) {
240                                 case "Gtk":
241                                         return new Gtk(project, path);
242                                         
243                                 case "Roo":
244                                         return new Roo((Project.Roo) project, path);
245 //                      case "Flutter":
246 //                                      return new Flutter(project, path);
247                                 case "PlainFile":
248                                         return new PlainFile(project, path);
249                         }
250                         throw new Error.INVALID_FORMAT("JsRender Factory called with xtype=%s", xt);
251                         //return null;    
252                 }
253                 
254         
255         
256                 public string nickType()
257                 {
258                         var ar = this.name.split(".");
259                         string[] ret = {};
260                         for (var i =0; i < ar.length -1; i++) {
261                                 ret += ar[i];
262                         }
263                         return string.joinv(".", ret);
264                         
265                 }
266                 public string nickName()
267                 {
268                         var ar = this.name.split(".");
269                         return ar[ar.length-1];
270                         
271                 }
272                 
273                 public string nickNameSplit()
274                 {
275                         var n = this.nickName();
276                         var ret = "";
277                         var len = 0;
278                         for (var i = 0; i < n.length; i++) {
279                                 if (i!=0 && n.get(i).isupper() && len > 10) {
280                                         ret +="\n";
281                                         len= 0;
282                                 }
283                                 ret += n.get(i).to_string();
284                                 len++;
285                         }
286                         
287
288                         
289                         return ret;
290                 
291                 }
292                 
293                 Gdk.Pixbuf screenshot = null;
294                 Gdk.Pixbuf screenshot92 = null;
295                 Gdk.Pixbuf screenshot368 = null;
296                 
297                 public Gdk.Pixbuf? getIcon(int size = 0) {
298                     var fname = this.getIconFileName( );                
299                     if (!FileUtils.test(fname, FileTest.EXISTS)) {
300                 GLib.debug("PIXBUF %s:  %s does not exist?", this.name, fname);
301                                 return null;
302                         }
303                         
304                         switch (size) {
305                                 case 0:
306                                         if (this.screenshot == null) {
307                                                 try { 
308                                                         this.screenshot = new Gdk.Pixbuf.from_file(fname);
309                                                 } catch (GLib.Error e) {}
310                                         }
311                                         return this.screenshot;
312                                 
313                                 case 92:
314                                         
315                                         if (this.screenshot == null) {
316                                                 this.getIcon(0);
317                                                 if (this.screenshot == null) {
318                                                         return null;
319                                                 }
320                                         }
321                                         
322                                         this.screenshot92 = this.screenshot.scale_simple(92, (int) (this.screenshot.height * 92.0 /this.screenshot.width * 1.0 )
323                                                 , Gdk.InterpType.NEAREST) ;
324                                     return this.screenshot92;
325                             
326                             case 368:
327                                         if (this.screenshot == null) {
328                                                 this.getIcon(0);
329                                                 if (this.screenshot == null) {
330                                                         return null;
331                                                 }
332                                         }
333                                         
334                                         this.screenshot368 = this.screenshot.scale_simple(368, (int) (this.screenshot.height * 368.0 /this.screenshot.width * 1.0 )
335                                     , Gdk.InterpType.NEAREST) ;
336                                     return this.screenshot368;
337                     }
338                     return null;
339                 }
340                 
341                 public void writeIcon(Gdk.Pixbuf pixbuf) {
342                         
343                         this.screenshot92 = null;
344                         this.screenshot368 = null;
345                         this.screenshot = null;
346                         try {
347                                 GLib.debug("Wirte %s", this.getIconFileName( ));
348                                 pixbuf.save(this.getIconFileName( ),"png");
349                                 this.screenshot = pixbuf;
350                         
351                         } catch (GLib.Error e) {
352                                 GLib.debug("failed to write pixbuf?");
353                         
354                         }
355                                 
356                          
357                         
358                 
359                 }
360                 public void widgetToIcon(global::Gtk.Widget widget) {
361                         
362                         this.screenshot92 = null;
363                         this.screenshot368 = null;
364                         this.screenshot = null;
365                         
366                         try {
367                         
368
369                         var filename = this.getIconFileName();
370                                 
371
372                                  var p = new global::Gtk.WidgetPaintable(widget);
373                                  var s = new global::Gtk.Snapshot();
374                                  GLib.debug("Width %d, Height %d", widget.get_width(), widget.get_height()); 
375                                  p.snapshot(s, widget.get_width(), widget.get_height());
376                                  var n = s.free_to_node();
377                                  if (n == null) {
378                                         return;
379                                 }
380                                  var r = new  Gsk.CairoRenderer();
381                                  r.realize(null);
382                                  var t = r.render_texture(n,null);
383                                  GLib.debug("write to %s", filename);
384                                  t.save_to_png(filename);
385                                  r.unrealize();
386                                          
387                         
388                         } catch (GLib.Error e) {
389                                 GLib.debug("failed to write pixbuf?");
390                         
391                         }
392                                 
393                          
394                         
395                 
396                 }
397
398                 
399                 public string getIconFileName( )
400                 {
401                          
402                         var m5 = GLib.Checksum.compute_for_string(GLib.ChecksumType.MD5,this.path); 
403
404                         var dir = GLib.Environment.get_home_dir() + "/.Builder/icons";
405                         try {
406                                 if (!FileUtils.test(dir, FileTest.IS_DIR)) {
407                                          File.new_for_path(dir).make_directory();
408                                 }
409                         } catch (GLib.Error e) {
410                                 // eakk.. what to do here...
411                         }
412                         var fname = dir + "/" + m5 + ".png";
413                         
414                          
415                         return fname;
416                           
417                 }
418                 
419                 public string toJsonString()
420                 {
421                         if (this.xtype == "PlainFile") {
422                                 return "";
423                         }
424                         var node = new Json.Node(Json.NodeType.OBJECT);
425                         node.set_object(this.toJsonObject());                   
426                         var generator = new JsonGen(node);
427                     generator.indent = 1;
428                     generator.pretty = true;
429                     
430                         
431                         return generator.to_data();
432                 }
433                 
434                 public void saveBJS()
435                 {
436                    // if (!this.loaded) {
437                         ///     GLib.debug("saveBJS - skip - not loaded?");
438                 //          return;
439                     //}
440                     if (this.xtype == "PlainFile") {
441                             return;
442                     }
443                    
444                      
445                     
446                     GLib.debug("WRITE :%s\n " , this.path);// + "\n" + JSON.stringify(write));
447                     try {
448                                 this.writeFile(this.path, this.toJsonString());
449                          
450                     } catch(GLib.Error e) {
451                         print("Save failed");
452                     }
453                 }
454                 
455                 bool in_undo = false;
456                 public void updateUndo()
457                 {
458                         if (this.in_undo) {
459                                 return;
460                         }
461                         if (this.xtype == "PlainFile") {
462                                 // handled by gtk sourceview buffer...
463                                 return;
464                         }
465                         //GLib.debug("UNDO store %d", this.version);
466                         this.undo_json.set(this.version, this.tree.toJsonString());
467                         if (this.undo_json.has_key(this.version+1)) {
468                                 var n = this.version +1;
469                                 while (this.undo_json.has_key(n)) {
470                                         this.undo_json.unset(n++);
471                                 }
472                         
473                         }
474                         
475                 }
476                 
477                 public bool undoStep(int step = -1) // undo back/next
478                 {
479  
480                         if (!this.undo_json.has_key(this.version + step)) {
481                                 //GLib.debug("UNDO step %d failed - no version available", this.version + step);
482                                 return false;
483                         }
484                         var new_version = this.version + step;
485                         var pa = new Json.Parser();
486                         //GLib.debug("UNDO RESTORE : %d",  this.version + step);
487                         try {
488                                 pa.load_from_data(this.undo_json.get(new_version));
489                         } catch (GLib.Error e) {
490                                 return false;
491                         }
492                         var node = pa.get_root();
493                         this.in_undo = true;
494                         this.loadTree(node.get_object(),2); 
495                         this.tree.updated_count = new_version;
496                         this.in_undo = false;
497                         return true;
498                 }
499                   
500                 public string jsonHasOrEmpty(Json.Object obj, string key) {
501                         return obj.has_member(key) ? 
502                                                 obj.get_string_member(key) : "";
503                 }
504
505                 
506                 public Json.Object toJsonObject ()
507                 {
508                     
509                     
510                         var ret = new Json.Object();
511                         if (this.xtype == "PlainFile") {
512                                 return ret;
513                         }
514                         
515                         //ret.set_string_member("id", this.id); // not relivant..
516                         ret.set_string_member("name", this.name);
517                         
518                         if (this.project.xtype == "Roo") {
519                                 ret.set_string_member("parent", this.parent == null ? "" : this.parent);
520                                 ret.set_string_member("title", this.title == null ? "" : this.title);
521                                 //ret.set_string_member("path", this.path);
522                                 //ret.set_string_member("items", this.items);
523                                 ret.set_string_member("permname", this.permname  == null ? "" : this.permname);
524                                 ret.set_string_member("modOrder", this.modOrder  == null ? "" : this.modOrder);
525                         }
526                         if (this.project.xtype == "Gtk") {
527  
528                                 ret.set_string_member("build_module", this.build_module  );
529                         }
530                         ret.set_boolean_member("gen_extended", this.gen_extended);
531                         
532                         if (this.transStrings.size > 0) {
533                                 var tr =  new Json.Object();
534                                 var iter = this.transStrings.map_iterator();
535                                 while (iter.next()) {
536                                         tr.set_string_member(iter.get_value(), iter.get_key());
537                                 }
538                                 ret.set_object_member("strings", tr);
539             }
540
541             
542             
543                         if (this.namedStrings.size > 0) {
544                                 var tr =  new Json.Object();
545                                 var iter = this.namedStrings.map_iterator();
546                                 while (iter.next()) {
547                                         tr.set_string_member(iter.get_key(), iter.get_value());
548                                 }
549                                 ret.set_object_member("named_strings", tr);
550             }
551                         
552                         var ar = new Json.Array();
553                         // empty files do not have a tree.
554                         if (this.tree != null) {
555                                 ar.add_object_element(this.tree.toJsonObject());
556                         }
557                         ret.set_array_member("items", ar);
558                 
559                     return ret;
560                 }
561                 
562                 
563
564                 public string getTitle ()
565                 {
566                     if (this.title == null) { // not sure why this happens..
567                         return "";
568                 }
569                     if (this.title.length > 0) {
570                         return this.title;
571                     }
572                     var a = this.path.split("/");
573                     return a[a.length-1];
574                 }
575                 public string getTitleTip()
576                 {
577                     if (this.title.length > 0) {
578                         return "<b>" + this.title + "</b> " + this.path;
579                     }
580                     return this.path;
581                 }
582                 /*
583                     sortCn: function()
584                     {
585                         this.cn.sort(function(a,b) {
586                             return a.path > b.path;// ? 1 : -1;
587                         });
588                     },
589                 */
590                     // should be in palete provider really..
591
592
593                 public Palete.Palete palete()
594                 {
595                         // error on plainfile?
596                         return this.project.palete;
597
598                 }
599                 
600                 public string guessName(Node ar) // turns the object into full name.
601                 {
602                      // eg. xns: Roo, xtype: XXX -> Roo.xxx
603                     if (!ar.hasXnsType()) {
604                        return "";
605                     }
606                     
607                     return ar.get("xns") + "." + ar.get("xtype");
608                                       
609                                         
610                 }
611                 /**
612                  *  non-atomic write (replacement for put contents, as it creates temporary files.
613                  */
614                 public void writeFile(string path, string contents) throws GLib.IOError, GLib.Error
615                 {
616
617                                  
618                         var f = GLib.File.new_for_path(path);
619                         var data_out = new GLib.DataOutputStream(
620                                           f.replace(null, false, GLib.FileCreateFlags.NONE, null)
621                        );
622                         data_out.put_string(contents, null);
623                         data_out.close(null);
624                 }
625                  
626                 
627                 
628                 public  Node? lineToNode(int line)
629                 {
630                         if (this.tree == null) {
631                                 return null;
632                         }
633                         return this.tree.lineToNode(line);
634                         
635                         
636                 }
637                 
638                 public GLib.ListStore toListStore()
639                 {
640                         var ret = new GLib.ListStore(typeof(Node));
641                         ret.append(this.tree);
642                         return ret;
643                 }
644                  
645                 
646                 // used to handle list of files in project editor (really Gtk only)
647                 public bool compile_group_selected {
648                         get {
649                                 var gproj = (Project.Gtk) this.project;
650                                 
651                                 if (gproj.active_cg == null) {
652                                         return false;
653                                 }
654                                 if (this.xtype == "Dir") {
655                                         // show ticked if all ticked..
656                                         var ticked = true;
657                                         for(var i = 0; i < this.childfiles.n_items; i++ ) {
658                                                 var f = (JsRender) this.childfiles.get_item(i);
659                                                 if (!f.compile_group_selected) {
660                                                         ticked = false;
661                                                         break;
662                                                 }
663                                         }
664                                         return ticked;
665                                 
666                                 
667                                 }
668                                 if (gproj.active_cg.sources == null) {
669                                         GLib.debug("compile_group_selected - sources is null? ");
670                                         return false;
671                                 }
672
673                                 return gproj.active_cg.sources.contains(this.relpath);
674                                 
675                         }
676                         set {
677                                 
678                                 var gproj = (Project.Gtk) this.project;
679                                 
680                                 if (gproj.active_cg == null) {
681                                         return;
682                                 }
683                                 if (gproj.active_cg.loading_ui) {
684                                         return;
685                                 }
686                                 
687                                 if (this.xtype == "Dir") {
688                                         for(var i = 0; i < this.childfiles.n_items; i++ ) {
689                                                 var f = (JsRender) this.childfiles.get_item(i);
690                                                 f.compile_group_selected = value;
691  
692                                         }
693                                         return;
694                                  
695                                 }
696                                 
697                                 
698                                 
699                                 if (value == false) {
700                                         GLib.debug("REMOVE %s", this.relpath);
701                                         
702                                         gproj.active_cg.sources.remove(this.relpath);
703                                         return;
704                                 }
705                                 if (!gproj.active_cg.sources.contains(this.relpath)) { 
706                                         GLib.debug("ADD %s", this.relpath);
707                                         gproj.active_cg.sources.add(this.relpath);
708                                 }
709                         
710                         }
711                 }
712                 /*
713                 public bool compile_group_hidden {
714                         get {
715                                 var gproj = (Project.Gtk) this.project;
716                                 
717                                 
718                                 return gproj.hidden.contains(this.relpath);
719                                 
720                         }
721                         set {
722                                 
723                                 var gproj = (Project.Gtk) this.project;
724                                 
725                                 if (gproj.active_cg == null) {
726                                         return;
727                                 }
728                                 if (gproj.active_cg.loading_ui) {
729                                         return;
730                                 } 
731                                 if (value == false) {
732                                         GLib.debug("REMOVE %s", this.relpath);
733                                         
734                                         gproj.hidden.remove(this.relpath);
735                                         return;
736                                 }
737                                 if (!gproj.hidden.contains(this.relpath)) { 
738                                         gproj.hidden.add(this.relpath);
739                                         // hiding a project will auto clear it.
740                                         for(var i = 0; i < this.childfiles.n_items; i++ ) {
741                                                 var f = (JsRender) this.childfiles.get_item(i);
742                                                 f.compile_group_selected = false;
743                                         }
744                                         return;
745                                         
746                                 }
747                         
748                         }
749                 }
750                 */
751                 public void remove()
752                 {
753                         if (this.xtype == "Dir") {
754                                 return;
755                         }
756                         // cleans up build (should really be in children..
757                         this.removeFile(this.path);
758                         if (this.path.has_suffix(".bjs") && this.project.xtype == "Roo") {
759                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".js");
760                                 return;
761                         }
762                         if (this.path.has_suffix(".bjs") && this.project.xtype == "Gtk") {
763                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".vala");
764                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".c");
765                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".o");                             
766                         }
767                         if (this.path.has_suffix(".vala") && this.project.xtype == "Gtk") {
768                                 this.removeFile(this.path.substring(0, this.path.length-5) + ".c");
769                                 this.removeFile(this.path.substring(0, this.path.length-5) + ".o");                             
770                         }       
771                 
772                 
773                 }
774                 
775                 private void removeFile(string path)
776                 {
777
778                         if (GLib.FileUtils.test(path, GLib.FileTest.EXISTS)) {
779                                 GLib.FileUtils.unlink(path);
780                         }
781
782                 }
783                 public string relTargetName()
784                 {
785                         return this.targetName().substring(this.project.path.length +1);
786                 
787                 }
788                 
789                 public string to_url()
790                 {
791                         return File.new_for_path (this.targetName()).get_uri ();
792                 }
793                 public Palete.LanguageClient? getLanguageServer()
794                 {
795                         
796                         return this.project.getLanguageServer(this.language_id());
797                 
798                 }
799                  
800                 public void updateErrors(Gee.ArrayList<Lsp.Diagnostic> new_errors) 
801                 {
802                         var oc = this.error_counter;
803                         var skip = new Gee.ArrayList<Lsp.Diagnostic>((a,b) => { return a.equals(b); });
804                         var rem = new Gee.ArrayList<Lsp.Diagnostic>((a,b) => { return a.equals(b); });
805                         foreach(var old in this.errors) {
806                                 if (new_errors.contains(old)) {
807                                         skip.add(old);
808                                         continue;
809                                 }
810                                 rem.add(old);
811                                  
812                         }
813                         foreach(var old in  rem) {
814                                 this.removeError(old);
815                         }
816                         foreach(var err in new_errors) {
817                                 if (skip.contains(err)) {
818                                         continue;
819                                 }
820                                 this.addError(err);
821                         }
822                         if (oc != this.error_counter) {
823                                 BuilderApplication.updateCompileResults();
824                         }
825                         
826                 }
827                 
828                 
829                 
830                 public Gee.ArrayList<Lsp.Diagnostic> getErrors()
831                 {
832                         return this.errors;
833                 }
834                 
835                 private void addError(Lsp.Diagnostic diag)
836                 {
837                         
838                         //GLib.debug("ADD Error %s", diag.to_string());
839                         this.errors.add(diag);
840                         this.project.addError(this, diag);
841                         
842                         this.error_counter++;
843                          
844                 }
845                  
846                 public void removeError(Lsp.Diagnostic diag) 
847                 {
848                         //GLib.debug("REMOVE Error %s", diag.to_string());
849                         this.errors.remove(diag);
850                         this.project.removeError(this, diag);
851                         this.error_counter++;
852                 }
853                 public int getErrorsTotal(string category) 
854                 {
855                         var  ret = 0;
856                         foreach(var diag in this.errors) {
857                                 if (diag.category == category) {
858                                         ret++;
859                                 }
860                         }
861                         return ret;
862                         
863                 
864                 } 
865                 
866                 public void loadTree(Json.Object obj, int bjs_version = 2)
867                 {
868                         if (this.xtype == "PlainFile" ){
869                                 return;
870                         }
871                         Node.uid_count = 0;
872                         this.tree = new Node();
873                         this.tree.loadFromJson(obj,bjs_version);
874                         this.tree.file = this;
875                         //this.tree.version_changed.connect(() => {
876                         //      this.updateUndo();
877                         //});
878                 
879                 }
880                 
881                 
882                 
883                 public abstract string language_id();
884                 public abstract void save();
885                 public abstract void saveHTML(string html);
886                 public abstract string toSource() ;
887                 public abstract string toSourceCode() ; // used by commandline tester..
888                 public abstract void setSource(string str);
889                 public abstract string toSourcePreview() ;
890                 public abstract void removeFiles() ;
891                 public abstract void  findTransStrings(Node? node );
892                 public abstract string toGlade();
893                 public abstract string targetName();
894                 public abstract void loadItems() throws GLib.Error;
895
896         } 
897         
898          
899
900 }
901  
902