Fix #8048 - context provider start work - fix meson build flag - project settings...
[roobuilder] / src / Project / Project.vala
1 //<Script type="text/javascript">
2
3 /**
4  * Project Object
5  * 
6  * Projects can only contain one directory... - it can import other projects..(later)
7  * 
8  * we need to sort out that - paths is currently a key/value array..
9  * 
10  * currently we store projects in ~/.Builder/{md5}.json
11    - then for Gtk projects we have a file config1.builder - which contains dependancies, and a list of files for each target
12      (for builder it's ended up in src/Builder/config1.builder? by accident.
13    - should really support something like 
14        roobuilder --build {path_to_cfg} {target} - or local directory if not set..
15        roobuilder --build-errors {path_to_cfg} {target} - or local directory if not set..
16        
17        
18    
19    
20    should really store project data in the directory of the project?
21    
22    
23    // steps:
24    // List of projects - just an array of paths in .Builder/Projects.json
25    
26    // .roobuilder.jcfg  << hidden file with project details?
27    
28    
29    
30  
31  * 
32  * 
33  */
34 namespace Project {
35          public errordomain Error {
36                 INVALID_TYPE,
37                 NEED_IMPLEMENTING,
38                 MISSING_FILE,
39                 INVALID_VALUE,
40                 INVALID_FORMAT
41         }
42
43
44         
45         // static array of all projects.
46         private Gee.ArrayList<Project>  projects;
47         
48         
49         
50         public bool  projects_loaded = false;
51         
52         
53         // used to pass around callbacks with project as a return
54         public class Callback : Object {
55                 public signal void call(Project project);
56         }
57         
58         public abstract class Project : Object {
59                 
60                 public signal void on_changed (); 
61         
62                 //public string id;
63                 //public string fn = ""; // just a md5...
64                 public string name  { 
65                         private set {} 
66                         owned get {
67                                 return GLib.Path.get_basename(path);
68                         }
69                          
70                 }
71                 public string version = "1.0";
72                 public string licence = "LGPL";         
73                 public string path = "";
74                 private Gee.ArrayList<JsRender.JsRender> sub_paths;
75                 
76                 private Gee.HashMap<string,JsRender.JsRender> files ;  // contains full list of files.
77                 //tree : false,
78                 public  string xtype;
79                 
80                 //public Json.Object json_project_data;
81
82                 public Palete.Palete palete;
83                  
84                 private bool is_scanned = false; 
85                 public  Gee.HashMap<string,Palete.GirObject> gir_cache = null; // used by Gir ??? is this used by Roo?
86                 //public Palete.ValaCompileRequest last_request = null; // depricated?
87                 public Gee.HashMap<string,GLib.ListStore>? errorsByType = null;
88                 public bool loading = false; // flag this to block saving (normally when loading ui that might trigger save..
89                 
90                 
91                 protected Gee.HashMap<string,Palete.LanguageClient> language_servers;
92                 
93                 protected Project (string path) {
94                         
95                          
96                         //this.json_project_data = new Json.Object();
97                         
98                         this.is_scanned = false;
99                         this.sub_paths = new Gee.ArrayList<JsRender.JsRender>();
100                         this.files = new Gee.HashMap<string,JsRender.JsRender>();
101                         //XObject.extend(this, cfg);
102                         //this.files = { }; 
103                         this.path = path;
104                         this.language_servers = new Gee.HashMap<string,Palete.LanguageClient>();
105                         this.language_servers.set("dummy", new Palete.LanguageClientDummy(this));
106                         this.errorsByType = new  Gee.HashMap<string,GLib.ListStore>();
107                         
108                 }
109                  
110                 
111                 
112                 
113                 public static void loadAll(bool force = false)
114                 {
115                         if (projects_loaded && !force) {
116                                 return;
117                         }
118
119                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
120                         var dir = File.new_for_path(dirname);
121                         if (!dir.query_exists()) {
122                                 try {
123                                         dir.make_directory();
124                                 } catch(GLib.Error e) {
125                                         GLib.error("could not make builder directory %s", e.message);
126                                 }
127                                 return;
128                         }
129                         projects = new  Gee.ArrayList<Project>();
130                           
131                     
132                     if (FileUtils.test(dirname + "/Projects.list", GLib.FileTest.IS_REGULAR)) {
133                         loadProjectList();
134                         projects_loaded = true;
135                         return;
136                 }
137                 convertOldProjects(); // this saves..
138                 foreach(var p in projects) {
139                         p.save();
140                 }
141                         projects_loaded = true;
142  
143         }
144          
145         public static void remove(Project p) {
146                 projects.remove(p);
147                 saveProjectList();
148         
149         }
150         
151         public static void saveProjectList()
152         {
153                         var f = new Json.Object();
154                         foreach(var p in projects) {
155                                 f.set_string_member(p.path, p.xtype);
156                         }
157                         
158                         var  generator = new Json.Generator ();
159                         var  root = new Json.Node(Json.NodeType.OBJECT);
160                         root.init_object(f);
161                         generator.set_root (root);
162                         generator.pretty = true;
163                         generator.indent = 4;
164
165                         var data = generator.to_data (null);
166                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
167                 GLib.debug("Write new Project list\n %s", data);
168                 //Posix.exit(0);
169                 
170                 try {
171                                 //FileUtils.set_contents(dirname + "/" + this.fn + ".json", s, s.length);  
172                                 FileUtils.set_contents(dirname + "/Projects.list", data, data.length);  
173                         } catch (GLib.Error e) {
174                                 GLib.error("failed  to save file %s", e.message);
175                         }
176                 
177         }
178         
179         
180         
181         public static void convertOldProjects()
182         {
183         
184                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
185                         var  dir = File.new_for_path(dirname);
186                         try {
187                                 var file_enum = dir.enumerate_children(
188                                                                 GLib.FileAttribute.STANDARD_DISPLAY_NAME, 
189                                         GLib.FileQueryInfoFlags.NONE, 
190                                         null
191                                 );
192                                  
193                                 FileInfo next_file; 
194                                 while ((next_file = file_enum.next_file(null)) != null) {
195                                         var fn = next_file.get_display_name();
196                                         if (!Regex.match_simple("\\.json$", fn)) {
197                                                 continue;
198                                         }
199                                         Project.factoryFromFileOld(dirname + "/" + fn);
200                                 }       
201                         } catch(GLib.Error e) {
202                                 GLib.warning("oops - something went wrong scanning the projects\n");
203                         }
204                         GLib.debug("Loaded all old Projects - saving");
205                         Project.saveProjectList();
206
207                 }
208
209                 public static Gee.ArrayList<Project> allProjectsByName()
210                 {
211                         
212                         return projects;
213                 
214                 }
215                 
216                 public static Project? getProjectByName(string name)
217                 {
218                         
219                         foreach (var p in projects) {
220                                 if (p.name == name) {
221                                         return p;
222                                 }
223                         }
224                         
225                         return null;
226                 
227                 }
228                 public static Project? getProjectByPath(string path)
229                 {
230                         
231                         foreach (var p in projects) {
232                                 if (p.path == path) {
233                                         return p;
234                                 }
235                         }
236                         
237                         return null;
238                 
239                 }
240                 public static string listAllToString()
241                 {
242                         var all = projects;
243
244                          
245                         
246                         all.sort((fa,fb) => {
247                                 return ((Project)fa).name.collate(((Project)fb).name);
248
249                         });
250
251                         var iter = all.list_iterator();
252                         var ret = "ID\tName\tDirectory\n";
253                         while (iter.next()) {
254                                 ret += "%s\t%s\n".printf(
255                                                  
256                                                 iter.get().name,
257                                                 iter.get().path
258                                                 );
259                          
260                                 
261                         }
262                         
263                         return ret;
264                 
265                 }
266                 
267                 
268                 public static void loadIntoStore(GLib.ListStore st)
269                 {
270                         st.remove_all();
271                         foreach (var p in projects) {
272                                 st.append(p);
273                         }
274                         
275                 }
276                         
277                 
278  
279                 
280                 static void loadProjectList()
281                 {
282                 
283                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
284                          
285                         projects = new  Gee.ArrayList<Project>();
286                           
287                     var pa = new Json.Parser();
288                         try { 
289                                 pa.load_from_file(dirname + "/Projects.list");  
290                         } catch (GLib.Error e) {
291                                 GLib.error("could not load json file %s", e.message);
292                         }
293                         var node = pa.get_root();
294                         if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
295                                 GLib.error( dirname + "/Projects.list - invalid format?");
296                                  
297                         }
298
299                         
300                         var obj = node.get_object ();
301                         obj.foreach_member((sobj, key, val) => {
302                                 GLib.debug("read ProjectList %s: %s", key, val.get_string());
303                                 // facotry adds project!
304                                 try {
305                                         Project.factory(val.get_string(), key );
306                                 } catch (GLib.Error e ) {
307                                         GLib.debug("error createing project %s", e.message);
308                                 }
309                                  
310                         });
311                         
312                 
313                 }
314                 
315                 // load project data from project file.
316                 public static void   factoryFromFileOld(string jsonfile)
317                 {
318                          
319                         GLib.debug("parse %s", jsonfile);
320
321                         var pa = new Json.Parser();
322                         try { 
323                                 pa.load_from_file(jsonfile);
324                         } catch (GLib.Error e) {
325                                 GLib.error("could not load json file %s", e.message);
326                         }
327                         var node = pa.get_root();
328
329                         
330                         if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
331                                 GLib.debug("SKIP " + jsonfile + " - invalid format?");
332                                 return;
333                         }
334                         
335                         var obj = node.get_object ();
336                         var xtype =  obj.get_string_member("xtype");
337
338
339                         var paths = obj.get_object_member("paths");
340                         var i = 0;
341                         var fpath = "";
342                         paths.foreach_member((sobj, key, val) => {
343                                 if (i ==0 ) {
344                                         fpath = key;
345                                 }
346                                         
347                         });
348                         
349                         if (fpath.length < 0 || !FileUtils.test(fpath,FileTest.IS_DIR)) {
350                                 return;
351                         }
352                         
353                         Project proj;
354                         try {
355                                 proj = factory(xtype, fpath);
356                         } catch (Error e)  {
357                                 GLib.debug("Skip file - invalid file type");
358                                 return;
359                         }
360
361                         //proj.json_project_data  = obj; // store the original object...
362                         
363                         //proj.fn =  Path.get_basename(jsonfile).split(".")[0];
364
365                         proj.loadJson(obj);
366                         // might not exist?
367  
368                         proj.name = obj.get_string_member("name");
369
370                         // used to load paths..
371                         //proj.initSubDirectories();
372                         
373                          
374                         //proj.initDatabase();
375                         
376                         GLib.debug("Add Project %s", proj.name);
377                         
378                         projects.add(proj);
379                          
380                         
381                 }
382                 
383                 
384                 public static Project factory(string xtype, string path) throws Error
385                 {
386
387                         // check to see if it's already loaded..
388
389                          foreach(var p in projects) {
390                                   if (p.path == path) {
391                                         return p;
392                                  }
393                         }
394
395                         
396                         switch(xtype) {
397                                 case "Gtk":
398                                         var ret =  new Gtk(path);
399                                         projects.add(ret);
400                                         
401                                         return ret;
402                                 case "Roo":
403                                         var ret = new Roo(path);
404                                         projects.add(ret);
405                                  
406                                         return ret;
407                                 //case "Flutter":
408                                 //      return new Flutter(path);
409                         }
410                         throw new Error.INVALID_TYPE("invalid project type");
411                                 
412                 }
413                 
414                 
415          
416
417                 public void save()
418                 {
419                         
420                         if (this.loading) {
421                                 return;
422                         }
423                         
424                         var  s =  this.toJSON();
425                         GLib.debug("Save Project %s\n%s", this.name, s);
426                         try {
427                                 //FileUtils.set_contents(dirname + "/" + this.fn + ".json", s, s.length);  
428                                 FileUtils.set_contents(this.path + "/.roobuilder.jcfg", s, s.length);  
429                         } catch (GLib.Error e) {
430                                 GLib.error("failed  to save file %s", e.message);
431                         }
432                         this.onSave();
433                         
434                 }
435
436         
437                 
438                 
439                 public string toJSON( )
440                 {
441                         
442                         var obj = new Json.Object();
443                         obj.set_string_member("xtype", this.xtype);
444                                                 
445                         
446                         this.saveJson(obj);
447                 
448                         var  generator = new Json.Generator ();
449                         var  root = new Json.Node(Json.NodeType.OBJECT);
450                         root.init_object(obj);
451                         generator.set_root (root);
452                         //if (show_all) {
453                                 generator.pretty = true;
454                                 generator.indent = 4;
455                         //}
456
457                         return  generator.to_data (null);
458                           
459                           
460                 }
461                 
462                 // used to check what type a project might be..
463                 // for 'new'
464                 public static string peekProjectType(string fn) 
465                 {
466                         var pa = new Json.Parser();
467                         try { 
468                                 pa.load_from_file(fn);
469                         } catch (GLib.Error e) {
470                                 GLib.debug("could not load json file %s", e.message);
471                                 return "";
472                                 
473                         }
474                         var node = pa.get_root();
475
476                         if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
477                                 GLib.debug("SKIP %s/.roobuilder.jcfg  - invalid format?",fn);
478                                 return "";
479                         }
480                         
481                         var obj = node.get_object ();
482
483                         var xtype =  obj.get_string_member("xtype");
484                         return xtype == null ? "" : xtype;
485                 
486                 }
487                 
488                 
489                 // this will do a full scan - should only be done on viewing project..
490                 // not initial load.. - may take time.
491                 
492                 public   void   load()
493                 {
494                         if (this.is_scanned) {
495                                 return;
496                         }
497                         GLib.debug("load is_scanned = false");
498                         
499                         
500                         // used to load paths..
501                         this.sub_paths = new Gee.ArrayList<JsRender.JsRender>();
502                         this.files = new Gee.HashMap<string,JsRender.JsRender>();
503                         this.loadSubDirectories("", 0); // must happend before loadJson! (as compile groups checks if file exists.
504                          
505
506                         
507                         if (FileUtils.test(this.path + "/.roobuilder.jcfg", FileTest.EXISTS)) {
508                                   
509                                 var pa = new Json.Parser();
510                                 try { 
511                                         pa.load_from_file(this.path + "/.roobuilder.jcfg");
512                                 } catch (GLib.Error e) {
513                                         GLib.error("could not load json file %s", e.message);
514                                 }
515                                 var node = pa.get_root();
516
517                                 
518                                 if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
519                                         GLib.debug("SKIP %s/.roobuilder.jcfg  - invalid format?",this.path);
520                                         return;
521                                 }
522                                 
523                                 var obj = node.get_object ();
524                                  
525                                 this.loadJson(obj);
526                         } 
527                         this.initDatabase();    
528                         this.is_scanned = true; // loaded.. dont need to do it again..
529                          GLib.debug("load is_scanned = true");
530                         
531                 }
532                 
533                 
534                 /*
535                 
536                 public string firstPath()
537                 {
538                         var iter = this.paths.map_iterator();
539                         while (iter.next()) {
540                                 return iter.get_key();
541                         }
542                   
543                         return "";
544                 }
545
546                 public bool hasPath(string path)
547                 {
548                         var iter = this.paths.map_iterator();
549                         while (iter.next()) {
550                                 if (iter.get_key() == path) {
551                                 return true;
552                         }
553                         }
554                   
555                         return false;
556                 }
557
558                 
559                 // returns the first path
560                 public string getName()
561                 {
562                         var iter = this.paths.map_iterator();
563                         while (iter.next()) {
564                                 return GLib.Path.get_basename(iter.get_key());
565                         }
566                   
567                         return "";
568                 }
569                 */
570
571                 public Gee.ArrayList<JsRender.JsRender> sortedFiles()
572                 {
573                         var files = new Gee.ArrayList<JsRender.JsRender>();
574
575                         var fiter = this.files.map_iterator();
576                         while(fiter.next()) {
577                                 files.add(fiter.get_value());
578                         }
579                         files.sort((fa,fb) => {
580                                 return ((JsRender.JsRender)fa).name.collate(((JsRender.JsRender)fb).name);
581
582                         });
583                         return files;
584
585                 }
586                 
587          
588          
589                 public string listAllFilesToString()
590                 {
591                  
592                         var iter = this.sortedFiles().list_iterator();
593                         var ret = "ID\tName\tDirectory\n";
594                         while (iter.next()) {
595                                 ret += "%s\n".printf(
596                                                  
597                                                 iter.get().name
598                                                  
599                                                 );
600                          
601                                 
602                         }
603                         
604                         return ret;
605                 
606                 }
607                 
608          
609          
610          
611                 public JsRender.JsRender? getByRelPath(string relpath)
612                 {
613                         foreach(var f in this.files.values) {
614                                 if (f.relpath == relpath || f.relTargetName() == relpath) {
615                                         return f;
616                                 }
617                         };
618                         return null;
619                 }
620                 // this get's a file using the full path ( replaces vala->bjs if they exist);
621                 
622                 public JsRender.JsRender? getByPath(string path)
623                 {
624                  
625                         // keys are not paths...
626                         foreach(var f in this.files.values) {
627                                 //GLib.debug("check %s = %s ? %s", path, f.path, f.targetName());
628                                 if (f.path == path || f.targetName() == path) {
629                                         return f;
630                                 }
631                         };
632                         return null;                    
633                 }
634                 
635                 public JsRender.JsRender? getById(string id)
636                 {
637                         foreach(var f in this.files.values) {
638                                 if (f.id == id) {
639                                         return f;
640                                 }
641                         };
642                         return null;
643                 }
644  
645                 // name should include extension.       
646                 /*
647                 public JsRender.JsRender? newFile (string xtype, string sub_dir, string name)
648                 {
649                         try {
650                                 var fp = this.path + (sub_dir.length > 0  ? "/" : "") + sub_dir;
651                                 if (this.files.has_key(fp + "/" +  name)) {
652                                         return null;
653                                 }
654                                  
655                                 
656                                 var ret =  JsRender.JsRender.factory(xtype, 
657                                                                                          this, 
658                                                                                          fp + "/" +  name
659                                                                                          );
660                                 this.files.set(fp + "/" +  name , ret);
661                                 return ret;
662                         } catch (JsRender.Error e) {
663                                 GLib.error("failed to create file %s", e.message);
664                         }
665                 }
666                 */
667          
668                 public JsRender.JsRender loadFileOnly (string path)
669                 {
670                         var xt = this.xtype;
671                         try {
672                                 return JsRender.JsRender.factory(xt, this, path);
673                         } catch (JsRender.Error e) {
674                                 GLib.error("failed to create file %s", e.message);
675                         } 
676                         
677                 } 
678                 
679                 /* 
680                 public JsRender.JsRender create(string filename)
681                 {
682                         var ret = this.loadFileOnly(filename);
683                         ret.save();
684                         this.addFile(ret);
685                         return ret;
686                         
687                 }
688                 */
689                 private void loadSubDirectories(string subdir, int dp) 
690                 {
691                         //dp = dp || 0;
692                         //print("Project.Base: Running scandir on " + dir +"\n");
693                         if (dp > 5) { // no more than 5 deep?
694                                 return;
695                         }
696                         if (subdir == "build") { // cmake!
697                                 return;
698                         }
699                         if (subdir == "build-dir") { // flatpack
700                                 return;
701                         }
702                         if (subdir == "repo") { // flatpack ?? wrong command?
703                                 return;
704                         }
705                         if (subdir == "autom4te.cache") { // automake?
706                                 return;
707                         }
708                         if (subdir == "obj-x86_64-linux-gnu") { // meson?
709                                 return;
710                         }
711                         //if (subdir == "debian") { // debian!?
712                         //      return;
713                         //}
714
715                         
716                         var dir = this.path + (subdir.length > 0 ? "/" : "") + subdir;
717                         
718                         
719                         GLib.debug("Project %s Scan Dir: %s", this.name, dir);
720                         var jsDir = new JsRender.Dir(this, dir);
721                         this.sub_paths.add(jsDir); // might be ''...
722                         
723                         
724                         // this should be done async -- but since we are getting the proto up ...
725                         var other_files = new Gee.ArrayList<string>();
726                         var bjs_files = new Gee.ArrayList<string>();
727                         var vala_files = new Gee.ArrayList<string>();
728                         var subs = new Gee.ArrayList<string>();
729                         
730                         
731                         var f = File.new_for_path(dir);
732                         try {
733                                 var file_enum = f.enumerate_children(GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null);
734                                 
735                                  
736                                 FileInfo next_file; 
737                                 while ((next_file = file_enum.next_file(null)) != null) {
738                                         var fn = next_file.get_display_name();
739                         
740                                          
741                                         //print("trying"  + dir + "/" + fn +"\n");
742                                         
743                                         if (fn[0] == '.') { // skip hidden
744                                                 continue;
745                                         }
746                                         
747                                         if (FileUtils.test(dir  + "/" + fn, GLib.FileTest.IS_DIR)) {
748                                                 if (subdir == "debian") { // dont bother with subdirs  of debian.
749                                         continue;
750                                 }
751
752                                                 
753                                                 subs.add(dir  + "/" + fn);
754                                                 continue;
755                                         }
756                                         if (Regex.match_simple("\\.(o|cache|out|stamp|~)$", fn)) { // object..
757                                                 continue;
758                                         }
759                                         if (Regex.match_simple("^(config1.builder|a.out|stamp-h1|depcomp|config.log|config.status)$", fn)) { // object..
760                                                 continue;
761                                         }
762                                         
763                                         
764                                         if (Regex.match_simple("\\.vala$", fn)) {
765                                                 vala_files.add(fn);
766                                                 other_files.add(fn);
767                                                 //print("no a bjs\n");
768                                                 continue;
769                                         }
770                                         if (!Regex.match_simple("\\.bjs$", fn)) {
771                                                 other_files.add(fn);
772                                                 //print("no a bjs\n");
773                                                 continue;
774                                         }
775                                         bjs_files.add(fn.substring(0, fn.length-4));
776                                         
777                                         var xt = this.xtype;
778                                         var el = JsRender.JsRender.factory(xt,this, dir + "/" + fn);
779                                         this.files.set( dir + "/" + fn, el);
780                                         jsDir.childfiles.append(el);
781                                         
782                                         // parent ?? 
783                                         
784                                          
785                                 }
786                         } catch (Error e) {
787                                 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
788                         } catch (GLib.Error e) {
789                                 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
790                         } 
791
792                         foreach(var fn in other_files) {
793                                 var dpos = fn.last_index_of(".");
794                                 var without_ext = fn.substring(0, dpos);
795                                 if (bjs_files.contains(without_ext)) {  // will remove vala and c.
796                                         continue;
797                                 }
798                                 // c with a vala - skip
799                                 if (Regex.match_simple("\\.c$", fn) && vala_files.contains(without_ext + ".vala")) {
800                                         continue;
801                                 }
802                                 // Makefile (only allow am files at present.
803                                 if (without_ext == "Makefile") {
804                                         if (!Regex.match_simple("\\.am$", fn)) {
805                                                 continue;
806                                         }
807                                 }
808                                 if (without_ext == "configure") {
809                                         if (!Regex.match_simple("\\.ac$", fn)) {
810                                                 continue;
811                                         }
812                                 }
813                                 
814                                 
815                                 
816                                 
817                                 
818                                 //GLib.debug("Could have added %s/%s", dir, fn);
819                                 try {
820                                          var el = JsRender.JsRender.factory("PlainFile",this, dir + "/" + fn);
821                                          this.files.set( dir + "/" + fn, el);
822                                         jsDir.childfiles.append(el);
823                                 } catch (JsRender.Error e) {
824                                         GLib.warning("Project::scanDirs failed : " + e.message + "\n");
825                                 }
826                         }
827                         
828                         foreach (var sd in subs) {
829                                  this.loadSubDirectories(sd.substring(this.path.length+1), dp+1);
830                         }
831                         
832                 
833                 }
834                 
835                 // calle dfrom new file dialog
836                 // add files to dires (and main file list)
837                 // update 
838                         
839                  
840                 public void addFile(JsRender.JsRender pfile)
841                 { // add a single file, and trigger changed.
842                 
843                         if (pfile.xtype == "Gtk" || pfile.xtype == "Roo" ) {
844                                 this.files.set(pfile.path, pfile); // duplicate check
845                                 
846                                 if (pfile.xtype == "Gtk" && pfile.build_module != "") {
847                                 
848                                         var gfile = (JsRender.Gtk) pfile;
849                                         gfile.updateCompileGroup("", pfile.build_module);
850                                          
851                                 }
852                         }
853                         var sp = this.findDir(pfile.dir);
854                         sp.childfiles.append(pfile);    
855                         this.files.set(pfile.path, pfile);
856                         
857                         this.on_changed();
858                 }
859                 
860                 
861                 
862                 public void deleteFile(JsRender.JsRender file) 
863                 {
864                         if (file.xtype =="Dir") {
865                                 return;
866                         }
867                         var sp = this.findDir(file.dir);
868                         for(var i =0;i < sp.childfiles.n_items; i++) {
869                                 var jf = (JsRender.JsRender) sp.childfiles.get_item(i);
870                                 if (jf.path == file.path) {
871                                         sp.childfiles.remove(i);
872                                         break;
873                                 }
874                         }
875                         if (this.files.has_key(file.path)) {
876                                 this.files.unset(file.path);
877                         }
878
879                         
880                         file.remove();
881                         this.save();
882                         
883                         // remove it from 
884                         
885                         
886                 }
887                         
888                 // but do not add it to our list.!!!
889                 public void makeProjectSubdir(string name)
890                 {
891                         var dir = File.new_for_path(this.path + "/" + name);
892                         if (FileUtils.test(this.path + "/" + name, FileTest.EXISTS)) {
893                                 return;
894                         }
895                         try {
896                                  
897                                 dir.make_directory();   
898                         } catch (GLib.Error e) {
899                                 GLib.error("Failed to make directory %s", this.path + "/" + name);
900                         } 
901                 }
902                 
903                 public void createDir(string subdir)   // add a single dir, and trigger changed.
904                 {
905                         if (subdir.strip() == "" || this.subpathsContains(subdir)) {
906                                 return;
907                         }
908                         var dir= File.new_for_path(this.path + "/" + subdir);
909
910                         if (!dir.query_exists()) {
911                         
912                                 try {
913                                          
914                                         dir.make_directory();   
915                                 } catch (GLib.Error e) {
916                                         GLib.error("Failed to make directory %s", this.path + "/" + name);
917                                 }
918
919                         }
920                         this.sub_paths.add(new JsRender.Dir(this,this.path + "/" + subdir));
921                         this.on_changed();  // not sure if it's needed - adding a dir doesnt really change much.
922                 }
923                 
924                 // this store is used in the icon view ?? do we need to store and update it?
925                 public void loadFilesIntoStore(GLib.ListStore ls) 
926                 {
927                         ls.remove_all();
928                         //GLib.debug("Load files (into grid) %s", this.name);                   
929                         foreach(var f in this.files.values) {
930                         //      GLib.debug("Add file %s", f.name);
931                                 if (f.xtype == "PlainFile") {
932                                         continue;
933                                 }
934                                 ls.append(f);
935                         }
936                         
937                 }
938                 public void loadDirsIntoStore(GLib.ListStore  ls) 
939                 {
940                         ls.remove_all();
941                         foreach(var f in this.sub_paths) {
942                                 //GLib.debug("Add %s", f.name);
943                                 ls.append(f);
944                         }
945                          ;
946                 }
947                 
948                 public bool subpathsContains(string subpath) 
949                 {
950                         foreach(var sp in this.sub_paths) {
951
952                                 if (sp.path == this.path + "/" + subpath) {
953                                         return true;
954                                 }
955                         }
956                         return false;
957                         
958                 }
959                 
960                 public void loadDirsToStringList( global::Gtk.StringList sl, string prefix) 
961                 {
962                          
963                         while (sl.get_n_items() > 0) {
964                                 sl.remove(0);
965                         }
966                         
967                         foreach(var sp in this.sub_paths) {
968                                  var add = sp.path == this.path ? "/" : sp.path.substring(this.path.length);
969                                 if (prefix.length > 0 && !add.has_prefix(prefix)) {
970                                         continue;
971                                 }
972                                 sl.append(add);
973                         }
974                 
975                 }
976                 
977                 public JsRender.Dir? findDir(string path) {
978                         
979                         foreach(var jdir in this.sub_paths) { 
980                                 if (path == jdir.path) {
981                                         return (JsRender.Dir)jdir;
982                                 }
983                         }
984                         return null;
985                 }
986                 
987                 public string[] pathsMatching(string name, bool full_path)
988                 {
989                         string[] ret = {};
990                          
991                         foreach(var jdir in this.sub_paths) { 
992                                 if (Path.get_basename (jdir.path) == name) {
993                                         GLib.debug("pathsMatching %s\n", jdir.path);
994                                         ret += full_path ? jdir.path : jdir.relpath;
995                                 }
996                                 
997                         }
998                         return ret;
999                         
1000                 }
1001                 
1002                 public Gee.ArrayList<JsRender.Dir> pathsUnder(string name)
1003                 {
1004                         var ret = new Gee.ArrayList<JsRender.Dir>();
1005                          
1006                         foreach(var jdir in this.sub_paths) { 
1007                                 if (jdir.relpath.has_prefix(name + "/")) {
1008                                         ret.add(jdir as JsRender.Dir);
1009                                 }
1010                                 
1011                         }
1012                         return ret;
1013                         
1014                 }
1015                 
1016                 public Gee.ArrayList<string> readArray(Json.Array ar) 
1017                 {
1018                         var ret = new Gee.ArrayList<string>();
1019                         for(var i =0; i< ar.get_length(); i++) {
1020                                 var add = ar.get_string_element(i);
1021                                 if (ret.contains(add)) {
1022                                         continue;
1023                                 }
1024                         
1025                                 ret.add(add);
1026                         }
1027                         return ret;
1028                 }
1029                 
1030                 // called from file..
1031                 public void addError(JsRender.JsRender f, Lsp.Diagnostic diag)
1032                 {
1033                         var new_ce = new Palete.CompileError.new_from_diagnostic(f, diag);
1034                         var ls = this.getErrors(new_ce.category); // will create if necessary..
1035                         // find the file in the list store.
1036
1037                         for(var i =0; i < ls.get_n_items(); i++) {
1038                                 var ce = ls.get_item(i) as Palete.CompileError;
1039                                 if (ce.file.path == f.path) {
1040                                         ce.lines.append(new_ce);
1041                                         return;
1042                                 } 
1043                         }
1044                         // we did not have the file..
1045                         var add = new Palete.CompileError.new_from_file(f, diag.category);
1046                         ls.append(add);
1047                         add.lines.append(new_ce);
1048                           
1049                 }
1050                 public void removeError(JsRender.JsRender f, Lsp.Diagnostic diag)
1051                 {
1052                         var ls = this.getErrors(diag.category);
1053                         for(var i =0; i < ls.get_n_items(); i++) {
1054                                 var ce = ls.get_item(i) as Palete.CompileError;
1055                                 if (ce.file.path != f.path) {
1056                                         continue;
1057                                 }
1058                                 for(var j =0; j < ce.lines.get_n_items(); j++) {
1059                                         var  lce = ce.lines.get_item(j) as Palete.CompileError;
1060                                         
1061                                         if (!diag.equals( lce.diag)) {
1062                                                 continue;
1063                                         }
1064                                         ce.lines.remove(j);
1065                                         if (ce.lines.get_n_items() < 1) {
1066                                                 ls.remove(i);
1067                                                 return;
1068                                         }
1069                                 }
1070                         }
1071
1072                 }
1073                 public GLib.ListStore getErrors(string n)
1074                 {
1075                         var ls = this.errorsByType.get(n);
1076                         if (ls == null) {
1077                                 ls = new GLib.ListStore(typeof(Palete.CompileError));
1078                                 this.errorsByType.set(n, ls );
1079                         }
1080                         return ls;
1081                 }
1082                 
1083                 
1084                 public abstract Palete.LanguageClient  getLanguageServer(string lang);
1085                 
1086                 
1087                 public abstract void onSave(); // write meson?
1088                 public abstract void initDatabase();
1089                 public abstract void initialize(); // for new projects (make dirs?);
1090                 public abstract void loadJson(Json.Object obj); 
1091                 public abstract void saveJson(Json.Object obj);
1092                   
1093         }
1094 }
1095