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.text_document;
22 
23 import dls.protocol.definitions;
24 import dls.protocol.interfaces.text_document;
25 import std.json : JSONValue;
26 import dls.util.nullable: Nullable;
27 
28 void didOpen(DidOpenTextDocumentParams params)
29 {
30     import dls.protocol.interfaces : PublishDiagnosticsParams;
31     import dls.protocol.jsonrpc : send;
32     import dls.protocol.logger : logger;
33     import dls.protocol.messages.methods : TextDocument;
34     import dls.tools.analysis_tool : AnalysisTool;
35     import dls.tools.symbol_tool : SymbolTool;
36     import dls.util.document : Document;
37     import dls.util.uri : Uri, sameFile;
38     import std.algorithm : canFind;
39     import std.uni : toLower;
40 
41     auto uri = new Uri(params.textDocument.uri);
42     logger.info("Document opened: %s", uri.path);
43 
44     immutable loneFile = !SymbolTool.instance.workspacesFilesUris.canFind!sameFile(uri);
45 
46     if (!Document.open(params.textDocument))
47     {
48         logger.warning("Document %s is already open", uri.path);
49     }
50 
51     if (loneFile)
52     {
53         send(TextDocument.publishDiagnostics, new PublishDiagnosticsParams(uri,
54                 AnalysisTool.instance.diagnostics(uri)));
55     }
56 }
57 
58 void didChange(DidChangeTextDocumentParams params)
59 {
60     import dls.protocol.logger : logger;
61     import dls.util.document : Document;
62     import dls.util.uri : Uri;
63 
64     auto uri = new Uri(params.textDocument.uri);
65     logger.info("Document changed: %s", uri.path);
66 
67     if (!Document.change(params.textDocument, params.contentChanges))
68     {
69         logger.warning("Document %s is not open", uri.path);
70     }
71 }
72 
73 void willSave(WillSaveTextDocumentParams params)
74 {
75     if (scanOnWillSave(true))
76     {
77         scanDocument(params.textDocument);
78     }
79 }
80 
81 TextEdit[] willSaveWaitUntil(WillSaveTextDocumentParams params)
82 {
83     return [];
84 }
85 
86 void didSave(DidSaveTextDocumentParams params)
87 {
88     if (!scanOnWillSave(false))
89     {
90         scanDocument(params.textDocument);
91     }
92 }
93 
94 void didClose(DidCloseTextDocumentParams params)
95 {
96     import dls.protocol.interfaces : PublishDiagnosticsParams;
97     import dls.protocol.jsonrpc : send;
98     import dls.protocol.logger : logger;
99     import dls.protocol.messages.methods : TextDocument;
100     import dls.tools.analysis_tool : AnalysisTool;
101     import dls.util.document : Document;
102     import dls.util.uri : Uri, sameFile;
103     import std.algorithm : canFind;
104 
105     auto uri = new Uri(params.textDocument.uri);
106     logger.info("Document closed: %s", uri.path);
107 
108     if (!Document.close(params.textDocument))
109     {
110         logger.warning("Document %s is not open", uri.path);
111     }
112 
113     Uri[] discaredFiles;
114 
115     if (!AnalysisTool.instance.getScannableFilesUris(discaredFiles).canFind!sameFile(uri))
116     {
117         send(TextDocument.publishDiagnostics, new PublishDiagnosticsParams(uri, []));
118     }
119 }
120 
121 CompletionItem[] completion(CompletionParams params)
122 {
123     import dls.tools.symbol_tool : SymbolTool;
124     import dls.util.uri : Uri;
125 
126     return SymbolTool.instance.completion(new Uri(params.textDocument.uri), params.position);
127 }
128 
129 @("completionItem", "resolve")
130 CompletionItem completionItem_resolve(CompletionItem item)
131 {
132     import dls.tools.symbol_tool : SymbolTool;
133 
134     return SymbolTool.instance.completionResolve(item);
135 }
136 
137 Hover hover(TextDocumentPositionParams params)
138 {
139     import dls.tools.symbol_tool : SymbolTool;
140     import dls.util.uri : Uri;
141 
142     return SymbolTool.instance.hover(new Uri(params.textDocument.uri), params.position);
143 }
144 
145 SignatureHelp signatureHelp(TextDocumentPositionParams params)
146 {
147     return null;
148 }
149 
150 Location[] declaration(TextDocumentPositionParams params)
151 {
152     return definition(params);
153 }
154 
155 Location[] definition(TextDocumentPositionParams params)
156 {
157     import dls.tools.symbol_tool : SymbolTool;
158     import dls.util.uri : Uri;
159 
160     return SymbolTool.instance.definition(new Uri(params.textDocument.uri), params.position);
161 }
162 
163 Location[] typeDefinition(TextDocumentPositionParams params)
164 {
165     import dls.tools.symbol_tool : SymbolTool;
166     import dls.util.uri : Uri;
167 
168     return SymbolTool.instance.typeDefinition(new Uri(params.textDocument.uri), params.position);
169 }
170 
171 Location implementation(TextDocumentPositionParams params)
172 {
173     return null;
174 }
175 
176 Location[] references(ReferenceParams params)
177 {
178     import dls.tools.symbol_tool : SymbolTool;
179     import dls.util.uri : Uri;
180 
181     return SymbolTool.instance.references(new Uri(params.textDocument.uri),
182             params.position, params.context.includeDeclaration);
183 }
184 
185 DocumentHighlight[] documentHighlight(TextDocumentPositionParams params)
186 {
187     import dls.tools.symbol_tool : SymbolTool;
188     import dls.util.uri : Uri;
189 
190     return SymbolTool.instance.highlight(new Uri(params.textDocument.uri), params.position);
191 }
192 
193 JSONValue documentSymbol(DocumentSymbolParams params)
194 {
195     import dls.protocol.state : initState;
196     import dls.tools.symbol_tool : SymbolTool;
197     import dls.util.json : convertToJSON;
198     import dls.util.uri : Uri;
199 
200     auto uri = new Uri(params.textDocument.uri);
201 
202     if (!initState.capabilities.textDocument.isNull && !initState.capabilities.textDocument.documentSymbol.isNull
203             && !initState.capabilities.textDocument.documentSymbol.hierarchicalDocumentSymbolSupport.isNull
204             && initState.capabilities.textDocument.documentSymbol.hierarchicalDocumentSymbolSupport)
205     {
206         return convertToJSON(SymbolTool.instance.symbol!DocumentSymbol(uri, null));
207     }
208     else
209     {
210         return convertToJSON(SymbolTool.instance.symbol!SymbolInformation(uri, null));
211     }
212 }
213 
214 JSONValue codeAction(CodeActionParams params)
215 {
216     import dls.protocol.state : initState;
217     import dls.tools.analysis_tool : AnalysisTool;
218     import dls.util.json : convertToJSON;
219     import dls.util.uri : Uri;
220 
221     if (initState.capabilities.textDocument.isNull || initState.capabilities.textDocument.codeAction.isNull
222             || initState.capabilities.textDocument.codeAction.codeActionLiteralSupport.isNull)
223     {
224         return convertToJSON(AnalysisTool.instance.codeAction(new Uri(params.textDocument.uri),
225                 params.range, params.context.diagnostics, true));
226     }
227     else
228     {
229         return convertToJSON(AnalysisTool.instance.codeAction(new Uri(params.textDocument.uri),
230                 params.range, params.context.diagnostics,
231                 params.context.only.isNull ? [] : params.context.only.get()));
232     }
233 }
234 
235 CodeLens[] codeLens(CodeLensParams params)
236 {
237     return [];
238 }
239 
240 @("codeLens", "resolve")
241 CodeLens codeLens_resolve(CodeLens codeLens)
242 {
243     return codeLens;
244 }
245 
246 DocumentLink[] documentLink(DocumentLinkParams params)
247 {
248     return [];
249 }
250 
251 @("documentLink", "resolve")
252 DocumentLink documentLink_resolve(DocumentLink link)
253 {
254     return link;
255 }
256 
257 ColorInformation[] documentColor(DocumentColorParams params)
258 {
259     return [];
260 }
261 
262 ColorPresentation[] colorPresentation(ColorPresentationParams params)
263 {
264     return [];
265 }
266 
267 TextEdit[] formatting(DocumentFormattingParams params)
268 {
269     import dls.tools.format_tool : FormatTool;
270     import dls.util.uri : Uri;
271 
272     return FormatTool.instance.formatting(new Uri(params.textDocument.uri), params.options);
273 }
274 
275 TextEdit[] rangeFormatting(DocumentRangeFormattingParams params)
276 {
277     import dls.tools.format_tool : FormatTool;
278     import dls.util.uri : Uri;
279 
280     return FormatTool.instance.rangeFormatting(new Uri(params.textDocument.uri),
281             params.range, params.options);
282 }
283 
284 TextEdit[] onTypeFormatting(DocumentOnTypeFormattingParams params)
285 {
286     import dls.tools.format_tool : FormatTool;
287     import dls.util.uri : Uri;
288 
289     return FormatTool.instance.onTypeFormatting(new Uri(params.textDocument.uri),
290             params.position, params.options);
291 }
292 
293 WorkspaceEdit rename(RenameParams params)
294 {
295     import dls.tools.symbol_tool : SymbolTool;
296     import dls.util.uri : Uri;
297 
298     return SymbolTool.instance.rename(new Uri(params.textDocument.uri),
299             params.position, params.newName);
300 }
301 
302 Range prepareRename(TextDocumentPositionParams params)
303 {
304     import dls.tools.symbol_tool : SymbolTool;
305     import dls.util.uri : Uri;
306 
307     return SymbolTool.instance.prepareRename(new Uri(params.textDocument.uri), params.position);
308 }
309 
310 FoldingRange[] foldingRange(FoldingRangeParams params)
311 {
312     return [];
313 }
314 
315 private bool scanOnWillSave(bool will)
316 {
317     import std.functional : memoize;
318 
319     static bool result;
320 
321     static bool impl()
322     {
323         return result;
324     }
325 
326     result = will;
327     return memoize!impl();
328 }
329 
330 private void scanDocument(const TextDocumentIdentifier textDocument)
331 {
332     import dls.protocol.interfaces : PublishDiagnosticsParams;
333     import dls.protocol.jsonrpc : send;
334     import dls.protocol.logger : logger;
335     import dls.protocol.messages.methods : TextDocument;
336     import dls.tools.analysis_tool : AnalysisTool;
337     import dls.util.uri : Uri;
338 
339     auto uri = new Uri(textDocument.uri);
340     logger.info("Document saved: %s", uri.path);
341     send(TextDocument.publishDiagnostics, new PublishDiagnosticsParams(uri,
342             AnalysisTool.instance.diagnostics(uri)));
343 }