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