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