1 //<Script type="text/javascript">
6 * Projects can only contain one directory... - it can import other projects..(later)
8 * we need to sort out that - paths is currently a key/value array..
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..
20 should really store project data in the directory of the project?
24 // List of projects - just an array of paths in .Builder/Projects.json
26 // .roobuilder.jcfg << hidden file with project details?
35 public errordomain Error {
45 // static array of all projects.
46 private Gee.ArrayList<Project> projects;
50 public bool projects_loaded = false;
53 // used to pass around callbacks with project as a return
54 public class Callback : Object {
55 public signal void call(Project project);
58 public abstract class Project : Object {
60 public signal void on_changed ();
63 //public string fn = ""; // just a md5...
67 return GLib.Path.get_basename(path);
73 public string path = "";
74 private Gee.ArrayList<JsRender.JsRender> sub_paths;
76 private Gee.HashMap<string,JsRender.JsRender> files ; // contains full list of files.
80 //public Json.Object json_project_data;
82 public Palete.Palete palete;
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;
91 protected Gee.HashMap<string,Palete.LanguageClient> language_servers;
93 protected Project (string path) {
96 //this.json_project_data = new Json.Object();
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);
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>();
113 public static void loadAll(bool force = false)
115 if (projects_loaded && !force) {
119 var dirname = GLib.Environment.get_home_dir() + "/.Builder";
120 var dir = File.new_for_path(dirname);
121 if (!dir.query_exists()) {
123 dir.make_directory();
124 } catch(GLib.Error e) {
125 GLib.error("could not make builder directory %s", e.message);
129 projects = new Gee.ArrayList<Project>();
132 if (FileUtils.test(dirname + "/Projects.list", GLib.FileTest.IS_REGULAR)) {
134 projects_loaded = true;
137 convertOldProjects(); // this saves..
138 foreach(var p in projects) {
141 projects_loaded = true;
145 public static void remove(Project p) {
151 public static void saveProjectList()
153 var f = new Json.Object();
154 foreach(var p in projects) {
155 f.set_string_member(p.path, p.xtype);
158 var generator = new Json.Generator ();
159 var root = new Json.Node(Json.NodeType.OBJECT);
161 generator.set_root (root);
162 generator.pretty = true;
163 generator.indent = 4;
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);
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);
181 public static void convertOldProjects()
184 var dirname = GLib.Environment.get_home_dir() + "/.Builder";
185 var dir = File.new_for_path(dirname);
187 var file_enum = dir.enumerate_children(
188 GLib.FileAttribute.STANDARD_DISPLAY_NAME,
189 GLib.FileQueryInfoFlags.NONE,
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)) {
199 Project.factoryFromFileOld(dirname + "/" + fn);
201 } catch(GLib.Error e) {
202 GLib.warning("oops - something went wrong scanning the projects\n");
204 GLib.debug("Loaded all old Projects - saving");
205 Project.saveProjectList();
209 public static Gee.ArrayList<Project> allProjectsByName()
216 public static Project? getProjectByName(string name)
219 foreach (var p in projects) {
220 if (p.name == name) {
228 public static Project? getProjectByPath(string path)
231 foreach (var p in projects) {
232 if (p.path == path) {
240 public static string listAllToString()
246 all.sort((fa,fb) => {
247 return ((Project)fa).name.collate(((Project)fb).name);
251 var iter = all.list_iterator();
252 var ret = "ID\tName\tDirectory\n";
253 while (iter.next()) {
254 ret += "%s\t%s\n".printf(
268 public static void loadIntoStore(GLib.ListStore st)
271 foreach (var p in projects) {
280 static void loadProjectList()
283 var dirname = GLib.Environment.get_home_dir() + "/.Builder";
285 projects = new Gee.ArrayList<Project>();
287 var pa = new Json.Parser();
289 pa.load_from_file(dirname + "/Projects.list");
290 } catch (GLib.Error e) {
291 GLib.error("could not load json file %s", e.message);
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?");
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!
305 Project.factory(val.get_string(), key );
306 } catch (GLib.Error e ) {
307 GLib.debug("error createing project %s", e.message);
315 // load project data from project file.
316 public static void factoryFromFileOld(string jsonfile)
319 GLib.debug("parse %s", jsonfile);
321 var pa = new Json.Parser();
323 pa.load_from_file(jsonfile);
324 } catch (GLib.Error e) {
325 GLib.error("could not load json file %s", e.message);
327 var node = pa.get_root();
330 if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
331 GLib.debug("SKIP " + jsonfile + " - invalid format?");
335 var obj = node.get_object ();
336 var xtype = obj.get_string_member("xtype");
339 var paths = obj.get_object_member("paths");
342 paths.foreach_member((sobj, key, val) => {
349 if (fpath.length < 0 || !FileUtils.test(fpath,FileTest.IS_DIR)) {
355 proj = factory(xtype, fpath);
357 GLib.debug("Skip file - invalid file type");
361 //proj.json_project_data = obj; // store the original object...
363 //proj.fn = Path.get_basename(jsonfile).split(".")[0];
368 proj.name = obj.get_string_member("name");
370 // used to load paths..
371 //proj.initSubDirectories();
374 //proj.initDatabase();
376 GLib.debug("Add Project %s", proj.name);
384 public static Project factory(string xtype, string path) throws Error
387 // check to see if it's already loaded..
389 foreach(var p in projects) {
390 if (p.path == path) {
398 var ret = new Gtk(path);
403 var ret = new Roo(path);
408 // return new Flutter(path);
410 throw new Error.INVALID_TYPE("invalid project type");
420 var s = this.toJSON();
421 GLib.debug("Save Project %s\n%s", this.name, s);
423 //FileUtils.set_contents(dirname + "/" + this.fn + ".json", s, s.length);
424 FileUtils.set_contents(this.path + "/.roobuilder.jcfg", s, s.length);
425 } catch (GLib.Error e) {
426 GLib.error("failed to save file %s", e.message);
435 public string toJSON( )
438 var obj = new Json.Object();
439 obj.set_string_member("xtype", this.xtype);
444 var generator = new Json.Generator ();
445 var root = new Json.Node(Json.NodeType.OBJECT);
446 root.init_object(obj);
447 generator.set_root (root);
449 generator.pretty = true;
450 generator.indent = 4;
453 return generator.to_data (null);
458 // used to check what type a project might be..
460 public static string peekProjectType(string fn)
462 var pa = new Json.Parser();
464 pa.load_from_file(fn);
465 } catch (GLib.Error e) {
466 GLib.debug("could not load json file %s", e.message);
470 var node = pa.get_root();
472 if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
473 GLib.debug("SKIP %s/.roobuilder.jcfg - invalid format?",fn);
477 var obj = node.get_object ();
479 var xtype = obj.get_string_member("xtype");
480 return xtype == null ? "" : xtype;
485 // this will do a full scan - should only be done on viewing project..
486 // not initial load.. - may take time.
490 if (this.is_scanned) {
493 GLib.debug("load is_scanned = false");
496 // used to load paths..
497 this.sub_paths = new Gee.ArrayList<JsRender.JsRender>();
498 this.files = new Gee.HashMap<string,JsRender.JsRender>();
499 this.loadSubDirectories("", 0); // must happend before loadJson! (as compile groups checks if file exists.
503 if (FileUtils.test(this.path + "/.roobuilder.jcfg", FileTest.EXISTS)) {
505 var pa = new Json.Parser();
507 pa.load_from_file(this.path + "/.roobuilder.jcfg");
508 } catch (GLib.Error e) {
509 GLib.error("could not load json file %s", e.message);
511 var node = pa.get_root();
514 if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
515 GLib.debug("SKIP %s/.roobuilder.jcfg - invalid format?",this.path);
519 var obj = node.get_object ();
524 this.is_scanned = true; // loaded.. dont need to do it again..
525 GLib.debug("load is_scanned = true");
532 public string firstPath()
534 var iter = this.paths.map_iterator();
535 while (iter.next()) {
536 return iter.get_key();
542 public bool hasPath(string path)
544 var iter = this.paths.map_iterator();
545 while (iter.next()) {
546 if (iter.get_key() == path) {
555 // returns the first path
556 public string getName()
558 var iter = this.paths.map_iterator();
559 while (iter.next()) {
560 return GLib.Path.get_basename(iter.get_key());
567 public Gee.ArrayList<JsRender.JsRender> sortedFiles()
569 var files = new Gee.ArrayList<JsRender.JsRender>();
571 var fiter = this.files.map_iterator();
572 while(fiter.next()) {
573 files.add(fiter.get_value());
575 files.sort((fa,fb) => {
576 return ((JsRender.JsRender)fa).name.collate(((JsRender.JsRender)fb).name);
585 public string listAllFilesToString()
588 var iter = this.sortedFiles().list_iterator();
589 var ret = "ID\tName\tDirectory\n";
590 while (iter.next()) {
591 ret += "%s\n".printf(
607 public JsRender.JsRender? getByRelPath(string relpath)
609 foreach(var f in this.files.values) {
610 if (f.relpath == relpath || f.relTargetName() == relpath) {
616 // this get's a file using the full path ( replaces vala->bjs if they exist);
618 public JsRender.JsRender? getByPath(string path)
621 // keys are not paths...
622 foreach(var f in this.files.values) {
623 //GLib.debug("check %s = %s ? %s", path, f.path, f.targetName());
624 if (f.path == path || f.targetName() == path) {
631 public JsRender.JsRender? getById(string id)
633 foreach(var f in this.files.values) {
641 // name should include extension.
643 public JsRender.JsRender? newFile (string xtype, string sub_dir, string name)
646 var fp = this.path + (sub_dir.length > 0 ? "/" : "") + sub_dir;
647 if (this.files.has_key(fp + "/" + name)) {
652 var ret = JsRender.JsRender.factory(xtype,
656 this.files.set(fp + "/" + name , ret);
658 } catch (JsRender.Error e) {
659 GLib.error("failed to create file %s", e.message);
664 public JsRender.JsRender loadFileOnly (string path)
668 return JsRender.JsRender.factory(xt, this, path);
669 } catch (JsRender.Error e) {
670 GLib.error("failed to create file %s", e.message);
676 public JsRender.JsRender create(string filename)
678 var ret = this.loadFileOnly(filename);
685 private void loadSubDirectories(string subdir, int dp)
688 //print("Project.Base: Running scandir on " + dir +"\n");
689 if (dp > 5) { // no more than 5 deep?
692 if (subdir == "build") { // cmake!
696 if (subdir == "autom4te.cache") { // automake?
699 //if (subdir == "debian") { // debian!?
704 var dir = this.path + (subdir.length > 0 ? "/" : "") + subdir;
707 GLib.debug("Project %s Scan Dir: %s", this.name, dir);
708 var jsDir = new JsRender.Dir(this, dir);
709 this.sub_paths.add(jsDir); // might be ''...
712 // this should be done async -- but since we are getting the proto up ...
713 var other_files = new Gee.ArrayList<string>();
714 var bjs_files = new Gee.ArrayList<string>();
715 var vala_files = new Gee.ArrayList<string>();
716 var subs = new Gee.ArrayList<string>();
719 var f = File.new_for_path(dir);
721 var file_enum = f.enumerate_children(GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null);
725 while ((next_file = file_enum.next_file(null)) != null) {
726 var fn = next_file.get_display_name();
729 //print("trying" + dir + "/" + fn +"\n");
731 if (fn[0] == '.') { // skip hidden
735 if (FileUtils.test(dir + "/" + fn, GLib.FileTest.IS_DIR)) {
736 subs.add(dir + "/" + fn);
739 if (Regex.match_simple("\\.(o|cache|out|stamp|~)$", fn)) { // object..
742 if (Regex.match_simple("^(config1.builder|a.out|stamp-h1|depcomp|config.log|config.status|obj-x86_64-linux-gnu)$", fn)) { // object..
747 if (Regex.match_simple("\\.vala$", fn)) {
750 //print("no a bjs\n");
753 if (!Regex.match_simple("\\.bjs$", fn)) {
755 //print("no a bjs\n");
758 bjs_files.add(fn.substring(0, fn.length-4));
761 var el = JsRender.JsRender.factory(xt,this, dir + "/" + fn);
762 this.files.set( dir + "/" + fn, el);
763 jsDir.childfiles.append(el);
770 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
771 } catch (GLib.Error e) {
772 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
775 foreach(var fn in other_files) {
776 var dpos = fn.last_index_of(".");
777 var without_ext = fn.substring(0, dpos);
778 if (bjs_files.contains(without_ext)) { // will remove vala and c.
781 // c with a vala - skip
782 if (Regex.match_simple("\\.c$", fn) && vala_files.contains(without_ext + ".vala")) {
785 // Makefile (only allow am files at present.
786 if (without_ext == "Makefile") {
787 if (!Regex.match_simple("\\.am$", fn)) {
791 if (without_ext == "configure") {
792 if (!Regex.match_simple("\\.ac$", fn)) {
801 //GLib.debug("Could have added %s/%s", dir, fn);
803 var el = JsRender.JsRender.factory("PlainFile",this, dir + "/" + fn);
804 this.files.set( dir + "/" + fn, el);
805 jsDir.childfiles.append(el);
806 } catch (JsRender.Error e) {
807 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
811 foreach (var sd in subs) {
812 this.loadSubDirectories(sd.substring(this.path.length+1), dp+1);
818 // calle dfrom new file dialog
819 // add files to dires (and main file list)
823 public void addFile(JsRender.JsRender pfile)
824 { // add a single file, and trigger changed.
826 if (pfile.xtype == "Gtk" || pfile.xtype == "Roo" ) {
827 this.files.set(pfile.path, pfile); // duplicate check
829 if (pfile.xtype == "Gtk" && pfile.build_module != "") {
831 var gfile = (JsRender.Gtk) pfile;
832 gfile.updateCompileGroup("", pfile.build_module);
836 var sp = this.findDir(pfile.dir);
837 sp.childfiles.append(pfile);
838 this.files.set(pfile.path, pfile);
845 public void deleteFile(JsRender.JsRender file)
847 if (file.xtype =="Dir") {
850 var sp = this.findDir(file.dir);
851 for(var i =0;i < sp.childfiles.n_items; i++) {
852 var jf = (JsRender.JsRender) sp.childfiles.get_item(i);
853 if (jf.path == file.path) {
854 sp.childfiles.remove(i);
858 if (this.files.has_key(file.path)) {
859 this.files.unset(file.path);
871 // but do not add it to our list.!!!
872 public void makeProjectSubdir(string name)
874 var dir = File.new_for_path(this.path + "/" + name);
875 if (FileUtils.test(this.path + "/" + name, FileTest.EXISTS)) {
880 dir.make_directory();
881 } catch (GLib.Error e) {
882 GLib.error("Failed to make directory %s", this.path + "/" + name);
886 public void createDir(string subdir) // add a single dir, and trigger changed.
888 if (subdir.strip() == "" || this.subpathsContains(subdir)) {
891 var dir= File.new_for_path(this.path + "/" + subdir);
893 if (!dir.query_exists()) {
897 dir.make_directory();
898 } catch (GLib.Error e) {
899 GLib.error("Failed to make directory %s", this.path + "/" + name);
903 this.sub_paths.add(new JsRender.Dir(this,this.path + "/" + subdir));
904 this.on_changed(); // not sure if it's needed - adding a dir doesnt really change much.
907 // this store is used in the icon view ?? do we need to store and update it?
908 public void loadFilesIntoStore(GLib.ListStore ls)
911 //GLib.debug("Load files (into grid) %s", this.name);
912 foreach(var f in this.files.values) {
913 // GLib.debug("Add file %s", f.name);
914 if (f.xtype == "PlainFile") {
921 public void loadDirsIntoStore(GLib.ListStore ls)
924 foreach(var f in this.sub_paths) {
925 //GLib.debug("Add %s", f.name);
931 public bool subpathsContains(string subpath)
933 foreach(var sp in this.sub_paths) {
935 if (sp.path == this.path + "/" + subpath) {
943 public void loadDirsToStringList( global::Gtk.StringList sl, string prefix)
946 while (sl.get_n_items() > 0) {
950 foreach(var sp in this.sub_paths) {
951 var add = sp.path == this.path ? "/" : sp.path.substring(this.path.length);
952 if (prefix.length > 0 && !add.has_prefix(prefix)) {
960 public JsRender.Dir? findDir(string path) {
962 foreach(var jdir in this.sub_paths) {
963 if (path == jdir.path) {
964 return (JsRender.Dir)jdir;
970 public string[] pathsMatching(string name, bool full_path)
974 foreach(var jdir in this.sub_paths) {
978 if (Path.get_basename (jdir.path) == name) {
979 GLib.debug("pathsMatching %s\n", jdir.path);
980 ret += full_path ? jdir.path : jdir.relpath;
987 public Gee.ArrayList<string> readArray(Json.Array ar)
989 var ret = new Gee.ArrayList<string>();
990 for(var i =0; i< ar.get_length(); i++) {
991 var add = ar.get_string_element(i);
992 if (ret.contains(add)) {
1001 // called from file..
1002 public void addError(JsRender.JsRender f, Lsp.Diagnostic diag)
1004 var new_ce = new Palete.CompileError.new_from_diagnostic(f, diag);
1005 var ls = this.getErrors(new_ce.category); // will create if necessary..
1006 // find the file in the list store.
1008 for(var i =0; i < ls.get_n_items(); i++) {
1009 var ce = ls.get_item(i) as Palete.CompileError;
1010 if (ce.file.path == f.path) {
1011 ce.lines.append(new_ce);
1015 // we did not have the file..
1016 var add = new Palete.CompileError.new_from_file(f, diag.category);
1018 add.lines.append(new_ce);
1021 public void removeError(JsRender.JsRender f, Lsp.Diagnostic diag)
1023 var ls = this.getErrors(diag.category);
1024 for(var i =0; i < ls.get_n_items(); i++) {
1025 var ce = ls.get_item(i) as Palete.CompileError;
1026 if (ce.file.path != f.path) {
1029 for(var j =0; j < ce.lines.get_n_items(); j++) {
1030 var lce = ce.lines.get_item(j) as Palete.CompileError;
1032 if (!diag.equals( lce.diag)) {
1036 if (ce.lines.get_n_items() < 1) {
1044 public GLib.ListStore getErrors(string n)
1046 var ls = this.errorsByType.get(n);
1048 ls = new GLib.ListStore(typeof(Palete.CompileError));
1049 this.errorsByType.set(n, ls );
1055 public abstract Palete.LanguageClient getLanguageServer(string lang);
1058 public abstract void onSave(); // write meson?
1059 public abstract void initDatabase();
1060 public abstract void initialize(); // for new projects (make dirs?);
1061 public abstract void loadJson(Json.Object obj);
1062 public abstract void saveJson(Json.Object obj);