1 module dlangide.workspace.project; 2 3 import dlangide.workspace.workspace; 4 import dlangui.core.logger; 5 import dlangui.core.collections; 6 import std.path; 7 import std.file; 8 import std.json; 9 import std.utf; 10 import std.algorithm; 11 12 /// project item 13 class ProjectItem { 14 protected Project _project; 15 protected ProjectItem _parent; 16 protected string _filename; 17 protected dstring _name; 18 19 this(string filename) { 20 _filename = buildNormalizedPath(filename); 21 _name = toUTF32(baseName(_filename)); 22 } 23 24 this() { 25 } 26 27 @property ProjectItem parent() { 28 return _parent; 29 } 30 31 @property Project project() { 32 return _project; 33 } 34 35 @property void project(Project p) { 36 _project = p; 37 } 38 39 @property string filename() { 40 return _filename; 41 } 42 43 @property dstring name() { 44 return _name; 45 } 46 47 /// returns true if item is folder 48 @property bool isFolder() { 49 return false; 50 } 51 /// returns child object count 52 @property int childCount() { 53 return 0; 54 } 55 /// returns child item by index 56 ProjectItem child(int index) { 57 return null; 58 } 59 } 60 61 /// Project folder 62 class ProjectFolder : ProjectItem { 63 protected ObjectList!ProjectItem _children; 64 65 this(string filename) { 66 super(filename); 67 } 68 69 @property override bool isFolder() { 70 return true; 71 } 72 @property override int childCount() { 73 return _children.count; 74 } 75 /// returns child item by index 76 override ProjectItem child(int index) { 77 return _children[index]; 78 } 79 void addChild(ProjectItem item) { 80 _children.add(item); 81 item._parent = this; 82 item._project = _project; 83 } 84 bool loadDir(string path) { 85 string src = relativeToAbsolutePath(path); 86 if (exists(src) && isDir(src)) { 87 ProjectFolder dir = new ProjectFolder(src); 88 addChild(dir); 89 Log.d(" added project folder ", src); 90 dir.loadItems(); 91 return true; 92 } 93 return false; 94 } 95 bool loadFile(string path) { 96 string src = relativeToAbsolutePath(path); 97 if (exists(src) && isFile(src)) { 98 ProjectSourceFile f = new ProjectSourceFile(src); 99 addChild(f); 100 Log.d(" added project file ", src); 101 return true; 102 } 103 return false; 104 } 105 void loadItems() { 106 foreach(e; dirEntries(_filename, SpanMode.shallow)) { 107 string fn = baseName(e.name); 108 if (e.isDir) { 109 loadDir(fn); 110 } else if (e.isFile) { 111 loadFile(fn); 112 } 113 } 114 } 115 string relativeToAbsolutePath(string path) { 116 if (isAbsolute(path)) 117 return path; 118 return buildNormalizedPath(_filename, path); 119 } 120 } 121 122 /// Project source file 123 class ProjectSourceFile : ProjectItem { 124 this(string filename) { 125 super(filename); 126 } 127 } 128 129 class WorkspaceItem { 130 protected string _filename; 131 protected string _dir; 132 protected dstring _name; 133 protected dstring _description; 134 135 this(string fname = null) { 136 filename = fname; 137 } 138 139 /// file name of workspace item 140 @property string filename() { 141 return _filename; 142 } 143 144 /// file name of workspace item 145 @property void filename(string fname) { 146 if (fname.length > 0) { 147 _filename = buildNormalizedPath(fname); 148 _dir = dirName(filename); 149 } else { 150 _filename = null; 151 _dir = null; 152 } 153 } 154 155 /// name 156 @property dstring name() { 157 return _name; 158 } 159 160 /// name 161 @property void name(dstring s) { 162 _name = s; 163 } 164 165 /// name 166 @property dstring description() { 167 return _description; 168 } 169 170 /// name 171 @property void description(dstring s) { 172 _description = s; 173 } 174 175 /// load 176 bool load(string fname) { 177 // override it 178 return false; 179 } 180 181 bool save() { 182 return false; 183 } 184 } 185 186 /// DLANGIDE D project 187 class Project : WorkspaceItem { 188 protected Workspace _workspace; 189 protected bool _opened; 190 protected ProjectFolder _items; 191 this(string fname = null) { 192 super(fname); 193 _items = new ProjectFolder(fname); 194 } 195 196 string relativeToAbsolutePath(string path) { 197 if (isAbsolute(path)) 198 return path; 199 return buildNormalizedPath(_dir, path); 200 } 201 202 @property ProjectFolder items() { 203 return _items; 204 } 205 206 @property Workspace workspace() { 207 return _workspace; 208 } 209 210 @property void workspace(Workspace p) { 211 _workspace = p; 212 } 213 214 ProjectFolder findItems() { 215 ProjectFolder folder = new ProjectFolder(_filename); 216 folder.project = this; 217 folder.loadDir(relativeToAbsolutePath("src")); 218 folder.loadDir(relativeToAbsolutePath("source")); 219 return folder; 220 } 221 222 /// tries to find source file in project, returns found project source file item, or null if not found 223 ProjectSourceFile findSourceFileItem(ProjectItem dir, string filename) { 224 for (int i = 0; i < dir.childCount; i++) { 225 ProjectItem item = dir.child(i); 226 if (item.isFolder) { 227 ProjectSourceFile res = findSourceFileItem(item, filename); 228 if (res) 229 return res; 230 } else { 231 ProjectSourceFile res = cast(ProjectSourceFile)item; 232 if (res && res.filename.equal(filename)) 233 return res; 234 } 235 } 236 return null; 237 } 238 239 ProjectSourceFile findSourceFileItem(string filename) { 240 return findSourceFileItem(_items, filename); 241 } 242 243 override bool load(string fname = null) { 244 if (fname.length > 0) 245 filename = fname; 246 if (!exists(filename) || !isFile(filename)) { 247 return false; 248 } 249 Log.d("Reading project from file ", _filename); 250 251 try { 252 string jsonSource = readText!string(_filename); 253 JSONValue json = parseJSON(jsonSource); 254 _name = toUTF32(json["name"].str); 255 _description = toUTF32(json["description"].str); 256 Log.d(" project name: ", _name); 257 Log.d(" project description: ", _description); 258 259 _items = findItems(); 260 } catch (JSONException e) { 261 Log.e("Cannot parse json", e); 262 return false; 263 } catch (Exception e) { 264 Log.e("Cannot read project file", e); 265 return false; 266 } 267 return true; 268 } 269 } 270