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 dls.util.nullable: 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.tools.tool : Tool;
36     import dls.util.uri : Uri;
37     import dls.util.nullable: nullable;
38 
39     if (!folders.isNull)
40     {
41         ConfigurationItem[] items;
42 
43         foreach (workspaceFolder; folders)
44         {
45             auto uri = new Uri(workspaceFolder.uri);
46             items ~= new ConfigurationItem(uri.toString().nullable);
47             Tool.instance.updateConfig(uri, JSONValue());
48             SymbolTool.instance.importPath(uri);
49             AnalysisTool.instance.addAnalysisConfig(uri);
50         }
51 
52         immutable conf = !initState.capabilities.workspace.isNull
53             && !initState.capabilities.workspace.configuration.isNull
54             && initState.capabilities.workspace.configuration;
55 
56         if (conf)
57         {
58             send(Workspace.configuration, new ConfigurationParams(items));
59         }
60     }
61 }
62 
63 void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params)
64 {
65     import dls.tools.analysis_tool : AnalysisTool;
66     import dls.tools.symbol_tool : SymbolTool;
67     import dls.tools.tool : Tool;
68     import dls.util.uri : Uri;
69     import dls.util.nullable: nullable;
70 
71     workspaceFolders(null, params.event.added.nullable);
72 
73     foreach (folder; params.event.removed)
74     {
75         auto uri = new Uri(folder.uri);
76         Tool.instance.removeConfig(uri);
77         SymbolTool.instance.clearPath(uri);
78         AnalysisTool.instance.removeAnalysisConfig(uri);
79     }
80 }
81 
82 void configuration(string id, JSONValue[] configs)
83 {
84     import dls.protocol.logger : logger;
85     import dls.tools.tool : Tool;
86 
87     auto uris = null ~ Tool.instance.workspacesUris;
88 
89     logger.info("Updating workspace configurations");
90 
91     for (size_t i; i < configs.length && i < uris.length; ++i)
92     {
93         auto config = configs[i];
94 
95         if ("d" in config && "dls" in config["d"])
96         {
97             Tool.instance.updateConfig(uris[i], config["d"]["dls"]);
98         }
99     }
100 }
101 
102 void didChangeConfiguration(DidChangeConfigurationParams params)
103 {
104     import dls.protocol.jsonrpc : send;
105     import dls.protocol.logger : logger;
106     import dls.protocol.messages.methods : Workspace;
107     import dls.protocol.state : initState;
108     import dls.tools.configuration : Configuration;
109     import dls.tools.tool : Tool;
110     import dls.util.nullable: Nullable, nullable;
111 
112     logger.info("Configuration changed");
113 
114     immutable conf = !initState.capabilities.workspace.isNull
115         && !initState.capabilities.workspace.configuration.isNull
116         && initState.capabilities.workspace.configuration;
117 
118     if (conf)
119     {
120         auto items = [new ConfigurationItem(Nullable!string(null))];
121 
122         foreach (uri; Tool.instance.workspacesUris)
123         {
124             items ~= new ConfigurationItem(uri.toString().nullable);
125         }
126 
127         send(Workspace.configuration, new ConfigurationParams(items));
128     }
129     else if ("d" in params.settings && "dls" in params.settings["d"])
130     {
131         logger.info("Updating configuration");
132         Tool.instance.updateConfig(null, params.settings["d"]["dls"]);
133     }
134 }
135 
136 void didChangeWatchedFiles(DidChangeWatchedFilesParams params)
137 {
138     import dls.protocol.interfaces : FileChangeType, PublishDiagnosticsParams;
139     import dls.protocol.jsonrpc : send;
140     import dls.protocol.logger : logger;
141     import dls.protocol.messages.methods : TextDocument;
142     import dls.tools.analysis_tool : AnalysisTool;
143     import dls.tools.symbol_tool : SymbolTool;
144     import dls.util.document : Document;
145     import dls.util.uri : Uri, sameFile;
146     import std.algorithm : canFind, filter, startsWith;
147     import std.file : exists, isFile;
148     import std.path : baseName, dirName, extension, pathSplitter;
149 
150     foreach (event; params.changes.filter!(event => event.type == FileChangeType.deleted))
151     {
152         auto workspaceUri = SymbolTool.instance.getWorkspace(new Uri(event.uri));
153 
154         if (workspaceUri !is null && !exists(workspaceUri.path))
155         {
156             SymbolTool.instance.clearPath(workspaceUri);
157         }
158     }
159 
160     outer: foreach (event; params.changes)
161     {
162         auto uri = new Uri(event.uri);
163         auto dirPath = dirName(uri.path);
164 
165         foreach (part; pathSplitter(dirPath))
166         {
167             if (part.startsWith('.'))
168             {
169                 continue outer;
170             }
171         }
172 
173         auto dirUri = Uri.fromPath(dirPath);
174 
175         logger.info("Resource %s: %s", event.type, uri.path);
176 
177         if (exists(uri.path) && !isFile(uri.path))
178         {
179             continue;
180         }
181 
182         switch (baseName(uri.path))
183         {
184         case "dub.json", "dub.sdl":
185             if (event.type != FileChangeType.deleted)
186             {
187                 SymbolTool.instance.importDubProject(dirUri);
188             }
189 
190             continue;
191 
192         case "dub.selections.json":
193             if (event.type != FileChangeType.deleted)
194             {
195                 SymbolTool.instance.importDubSelections(dirUri);
196             }
197 
198             continue;
199 
200         case ".gitmodules":
201             if (event.type != FileChangeType.deleted)
202             {
203                 SymbolTool.instance.importGitSubmodules(dirUri);
204             }
205 
206             continue;
207 
208         default:
209             break;
210         }
211 
212         switch (extension(uri.path))
213         {
214         case ".d", ".di":
215             Uri[] discarded;
216 
217             if (event.type == FileChangeType.deleted)
218             {
219                 send(TextDocument.publishDiagnostics, new PublishDiagnosticsParams(uri, []));
220             }
221             else if (!Document.uris.canFind!sameFile(uri)
222                     && AnalysisTool.instance.getScannableFilesUris(discarded)
223                     .canFind!sameFile(uri))
224             {
225                 send(TextDocument.publishDiagnostics, new PublishDiagnosticsParams(uri,
226                         AnalysisTool.instance.diagnostics(uri)));
227             }
228 
229             continue;
230 
231         case ".ini":
232             AnalysisTool.instance.updateAnalysisConfig(dirUri);
233             continue;
234 
235         default:
236             break;
237         }
238     }
239 }
240 
241 SymbolInformation[] symbol(WorkspaceSymbolParams params)
242 {
243     import dls.tools.symbol_tool : SymbolTool;
244 
245     return SymbolTool.instance.symbol(params.query);
246 }
247 
248 JSONValue executeCommand(ExecuteCommandParams params)
249 {
250     import dls.tools.command_tool : CommandTool;
251 
252     return CommandTool.instance.executeCommand(params.command,
253             params.arguments.isNull ? [] : params.arguments.get());
254 }
255 
256 void applyEdit(string id, ApplyWorkspaceEditResponse response)
257 {
258 }