1 /*
2  *Copyright (C) 2018 Laurent Tréguier
3  *
4  *This file is part of DLS.
5  *
6  *DLS is free software: you can redistribute it and/or modify
7  *it under the terms of the GNU General Public License as published by
8  *the Free Software Foundation, either version 3 of the License, or
9  *(at your option) any later version.
10  *
11  *DLS is distributed in the hope that it will be useful,
12  *but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *GNU General Public License for more details.
15  *
16  *You should have received a copy of the GNU General Public License
17  *along with DLS.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 module dls.protocol.messages.workspace;
22 
23 import dls.protocol.interfaces : SymbolInformation;
24 import dls.protocol.interfaces.workspace;
25 import std.json : JSONValue;
26 import std.typecons : Nullable;
27 
28 void workspaceFolders(string id, Nullable!(WorkspaceFolder[]) folders)
29 {
30     import dls.protocol.jsonrpc : send;
31     import dls.protocol.messages.methods : Workspace;
32     import dls.protocol.state : initState;
33     import dls.tools.analysis_tool : AnalysisTool;
34     import dls.tools.symbol_tool : SymbolTool;
35     import dls.util.uri : Uri;
36     import std.typecons : nullable;
37 
38     if (!folders.isNull)
39     {
40         ConfigurationItem[] items;
41 
42         foreach (workspaceFolder; folders)
43         {
44             auto uri = new Uri(workspaceFolder.uri);
45             items ~= new ConfigurationItem(uri.toString().nullable);
46             SymbolTool.instance.importPath(uri);
47             AnalysisTool.instance.addAnalysisConfig(uri);
48         }
49 
50         immutable conf = !initState.capabilities.workspace.isNull
51             && !initState.capabilities.workspace.configuration.isNull
52             && initState.capabilities.workspace.configuration;
53 
54         if (conf)
55         {
56             send(Workspace.configuration, new ConfigurationParams(items));
57         }
58     }
59 }
60 
61 void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params)
62 {
63     import dls.tools.analysis_tool : AnalysisTool;
64     import dls.tools.symbol_tool : SymbolTool;
65     import dls.tools.tool : Tool;
66     import dls.util.uri : Uri;
67     import std.typecons : nullable;
68 
69     workspaceFolders(null, params.event.added.nullable);
70 
71     foreach (folder; params.event.removed)
72     {
73         auto uri = new Uri(folder.uri);
74         SymbolTool.instance.clearPath(uri);
75         AnalysisTool.instance.removeAnalysisConfig(uri);
76         Tool.removeConfig(uri);
77     }
78 }
79 
80 void configuration(string id, JSONValue[] configs)
81 {
82     import dls.protocol.logger : logger;
83     import dls.tools.tool : Tool;
84 
85     auto uris = null ~ Tool.workspacesUris;
86 
87     logger.info("Updating workspace configurations");
88 
89     for (size_t i; i < configs.length && i < uris.length; ++i)
90     {
91         auto config = configs[i];
92 
93         if ("d" in config && "dls" in config["d"])
94         {
95             Tool.updateConfig(uris[i], config["d"]["dls"]);
96         }
97     }
98 }
99 
100 void didChangeConfiguration(DidChangeConfigurationParams params)
101 {
102     import dls.protocol.jsonrpc : send;
103     import dls.protocol.logger : logger;
104     import dls.protocol.messages.methods : Workspace;
105     import dls.protocol.state : initState;
106     import dls.tools.configuration : Configuration;
107     import dls.tools.tool : Tool;
108     import std.typecons : Nullable, nullable;
109 
110     logger.info("Configuration changed");
111 
112     immutable conf = !initState.capabilities.workspace.isNull
113         && !initState.capabilities.workspace.configuration.isNull
114         && initState.capabilities.workspace.configuration;
115 
116     if (conf)
117     {
118         auto items = [new ConfigurationItem(Nullable!string(null))];
119 
120         foreach (uri; Tool.workspacesUris)
121         {
122             items ~= new ConfigurationItem(uri.toString().nullable);
123         }
124 
125         send(Workspace.configuration, new ConfigurationParams(items));
126     }
127     else if ("d" in params.settings && "dls" in params.settings["d"])
128     {
129         logger.info("Updating configuration");
130         Tool.updateConfig(null, params.settings["d"]["dls"]);
131     }
132 }
133 
134 void didChangeWatchedFiles(DidChangeWatchedFilesParams params)
135 {
136     import dls.protocol.interfaces : FileChangeType, PublishDiagnosticsParams;
137     import dls.protocol.jsonrpc : send;
138     import dls.protocol.logger : logger;
139     import dls.protocol.messages.methods : TextDocument;
140     import dls.tools.analysis_tool : AnalysisTool;
141     import dls.tools.symbol_tool : SymbolTool;
142     import dls.util.document : Document;
143     import dls.util.uri : Uri, sameFile;
144     import std.algorithm : canFind, filter, startsWith;
145     import std.file : exists, isFile;
146     import std.path : baseName, dirName, extension, pathSplitter;
147 
148     foreach (event; params.changes.filter!(event => event.type == FileChangeType.deleted))
149     {
150         auto workspaceUri = SymbolTool.instance.getWorkspace(new Uri(event.uri));
151 
152         if (workspaceUri !is null && !exists(workspaceUri.path))
153         {
154             SymbolTool.instance.clearPath(workspaceUri);
155         }
156     }
157 
158     outer: foreach (event; params.changes)
159     {
160         auto uri = new Uri(event.uri);
161         auto dirPath = dirName(uri.path);
162 
163         foreach (part; pathSplitter(dirPath))
164         {
165             if (part.startsWith('.'))
166             {
167                 continue outer;
168             }
169         }
170 
171         auto dirUri = Uri.fromPath(dirPath);
172 
173         logger.info("Resource %s: %s", event.type, uri.path);
174 
175         if (exists(uri.path) && !isFile(uri.path))
176         {
177             continue;
178         }
179 
180         switch (baseName(uri.path))
181         {
182         case "dub.json", "dub.sdl":
183             if (event.type != FileChangeType.deleted)
184             {
185                 SymbolTool.instance.importDubProject(dirUri);
186             }
187 
188             continue;
189 
190         case "dub.selections.json":
191             if (event.type != FileChangeType.deleted)
192             {
193                 SymbolTool.instance.importDubSelections(dirUri);
194             }
195 
196             continue;
197 
198         case ".gitmodules":
199             if (event.type != FileChangeType.deleted)
200             {
201                 SymbolTool.instance.importGitSubmodules(dirUri);
202             }
203 
204             continue;
205 
206         default:
207             break;
208         }
209 
210         switch (extension(uri.path))
211         {
212         case ".d", ".di":
213             if (event.type != FileChangeType.deleted
214                     && !Document.uris.canFind!sameFile(uri)
215                     && SymbolTool.instance.workspacesFilesUris.canFind!sameFile(uri))
216             {
217                 send(TextDocument.publishDiagnostics, new PublishDiagnosticsParams(uri,
218                         AnalysisTool.instance.diagnostics(uri)));
219             }
220 
221             continue;
222 
223         case ".ini":
224             AnalysisTool.instance.updateAnalysisConfig(dirUri);
225             continue;
226 
227         default:
228             break;
229         }
230     }
231 }
232 
233 SymbolInformation[] symbol(WorkspaceSymbolParams params)
234 {
235     import dls.tools.symbol_tool : SymbolTool;
236 
237     return SymbolTool.instance.symbol(params.query);
238 }
239 
240 JSONValue executeCommand(ExecuteCommandParams params)
241 {
242     import dls.tools.command_tool : CommandTool;
243 
244     return CommandTool.instance.executeCommand(params.command,
245             params.arguments.isNull ? [] : params.arguments.get());
246 }
247 
248 void applyEdit(string id, ApplyWorkspaceEditResponse response)
249 {
250 }