1 module dlangide.workspace.workspace; 2 3 import dlangide.workspace.project; 4 import dlangide.ui.frame; 5 import dlangui.core.logger; 6 import std.conv; 7 import std.path; 8 import std.file; 9 import std.json; 10 import std.range; 11 import std.utf; 12 import std.algorithm; 13 14 enum BuildOperation { 15 Build, 16 Clean, 17 Rebuild, 18 Run, 19 Upgrade 20 } 21 22 enum BuildConfiguration { 23 Debug, 24 Release, 25 Unittest 26 } 27 28 29 /** 30 Exception thrown on Workspace errors 31 */ 32 class WorkspaceException : Exception 33 { 34 this(string msg, string file = __FILE__, size_t line = __LINE__) 35 { 36 super(msg, file, line); 37 } 38 } 39 40 immutable string WORKSPACE_EXTENSION = ".dlangidews"; 41 42 /// return true if filename matches rules for workspace file names 43 bool isWorkspaceFile(string filename) { 44 return filename.endsWith(WORKSPACE_EXTENSION); 45 } 46 47 /// DlangIDE workspace 48 class Workspace : WorkspaceItem { 49 protected Project[] _projects; 50 51 protected IDEFrame _frame; 52 protected BuildConfiguration _buildConfiguration; 53 protected ProjectConfiguration _projectConfiguration = ProjectConfiguration.DEFAULT; 54 55 this(IDEFrame frame, string fname = null) { 56 super(fname); 57 _frame = frame; 58 } 59 60 @property Project[] projects() { 61 return _projects; 62 } 63 64 @property BuildConfiguration buildConfiguration() { return _buildConfiguration; } 65 @property void buildConfiguration(BuildConfiguration config) { _buildConfiguration = config; } 66 67 @property ProjectConfiguration projectConfiguration() { return _projectConfiguration; } 68 @property void projectConfiguration(ProjectConfiguration config) { _projectConfiguration = config; } 69 70 protected Project _startupProject; 71 72 @property Project startupProject() { return _startupProject; } 73 @property void startupProject(Project project) { 74 _startupProject = project; 75 _frame.setProjectConfigurations(project.configurations.keys.map!(k => k.to!dstring).array); 76 } 77 78 /// setups currrent project configuration by name 79 void setStartupProjectConfiguration(string conf) 80 { 81 if(_startupProject && conf in _startupProject.configurations) { 82 _projectConfiguration = _startupProject.configurations[conf]; 83 } 84 } 85 86 protected void fillStartupProject() { 87 if (!_startupProject && _projects.length) 88 startupProject = _projects[0]; 89 } 90 91 /// tries to find source file in one of projects, returns found project source file item, or null if not found 92 ProjectSourceFile findSourceFileItem(string filename, bool fullFileName=true) { 93 foreach (Project p; _projects) { 94 ProjectSourceFile res = p.findSourceFileItem(filename, fullFileName); 95 if (res) 96 return res; 97 } 98 return null; 99 } 100 101 /// find project in workspace by filename 102 Project findProject(string filename) { 103 foreach (Project p; _projects) { 104 if (p.filename.equal(filename)) 105 return p; 106 } 107 return null; 108 } 109 110 void addProject(Project p) { 111 _projects ~= p; 112 p.workspace = this; 113 fillStartupProject(); 114 } 115 116 bool addDependencyProject(Project p) { 117 for (int i = 0; i < _projects.length; i++) { 118 if (_projects[i].filename.equal(p.filename)) { 119 _projects[i] = p; 120 return false; 121 } 122 } 123 addProject(p); 124 return true; 125 } 126 127 string absoluteToRelativePath(string path) { 128 return toForwardSlashSeparator(relativePath(path, _dir)); 129 } 130 131 132 override bool save(string fname = null) { 133 if (fname.length > 0) 134 filename = fname; 135 try { 136 JSONValue content; 137 JSONValue[string] json; 138 json["name"] = JSONValue(toUTF8(_name)); 139 json["description"] = JSONValue(toUTF8(_description)); 140 JSONValue[string] projects; 141 foreach (Project p; _projects) { 142 if (p.isDependency) 143 continue; // don't save dependency 144 string pname = toUTF8(p.name); 145 string ppath = absoluteToRelativePath(p.filename); 146 projects[pname] = JSONValue(ppath); 147 } 148 json["projects"] = projects; 149 content = json; 150 string js = content.toPrettyString; 151 write(_filename, js); 152 } catch (JSONException e) { 153 Log.e("Cannot parse json", e); 154 return false; 155 } catch (Exception e) { 156 Log.e("Cannot read workspace file", e); 157 return false; 158 } 159 return true; 160 } 161 162 override bool load(string fname = null) { 163 if (fname.length > 0) 164 filename = fname; 165 if (!exists(_filename) || !isFile(_filename)) { 166 return false; 167 } 168 Log.d("Reading workspace from file ", _filename); 169 170 try { 171 string jsonSource = readText!string(_filename); 172 JSONValue json = parseJSON(jsonSource); 173 _name = toUTF32(json["name"].str); 174 _description = toUTF32(json["description"].str); 175 Log.d("workspace name: ", _name); 176 Log.d("workspace description: ", _description); 177 JSONValue projects = json["projects"]; 178 foreach(string key, ref JSONValue value; projects) { 179 string path = value.str; 180 Log.d("project: ", key, " path:", path); 181 if (!isAbsolute(path)) 182 path = buildNormalizedPath(_dir, path); //, "dub.json" 183 Project project = new Project(this, path); 184 _projects ~= project; 185 project.load(); 186 187 } 188 string js = json.toPrettyString; 189 write(_filename, js); 190 } catch (JSONException e) { 191 Log.e("Cannot parse json", e); 192 return false; 193 } catch (Exception e) { 194 Log.e("Cannot read workspace file", e); 195 return false; 196 } 197 fillStartupProject(); 198 return true; 199 } 200 void close() { 201 } 202 203 void refresh() { 204 foreach (Project p; _projects) { 205 p.refresh(); 206 } 207 } 208 } 209 210 /// global workspace 211 __gshared Workspace currentWorkspace;