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