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.general;
22 
23 import dls.protocol.interfaces.general;
24 import std.json : JSONValue;
25 
26 @("")
27 InitializeResult initialize(InitializeParams params)
28 {
29     import dls.server : Server;
30     import dls.protocol.state : initOptions, initState;
31     import dls.tools.symbol_tool : useCompatCompletionItemKinds,
32         useCompatSymbolKinds;
33     import dls.tools.analysis_tool : AnalysisTool;
34     import dls.tools.format_tool : FormatTool;
35     import dls.tools.symbol_tool : SymbolTool;
36     import dls.util.logger : logger;
37     import dls.util.uri : Uri;
38     import std.algorithm : map, sort, uniq;
39     import std.array : array;
40     import std.typecons : Nullable, nullable;
41 
42     logger.info("Initializing server");
43     initState = params;
44     Server.initialized = true;
45     AnalysisTool.initialize();
46     FormatTool.initialize();
47     SymbolTool.initialize();
48 
49     debug
50     {
51     }
52     else
53     {
54         import dls.updater : cleanup;
55         import std.concurrency : spawn;
56 
57         spawn(&cleanup);
58     }
59 
60     if (params.capabilities.textDocument.completion.isNull
61             || params.capabilities.textDocument.completion.completionItemKind.isNull
62             || params.capabilities.textDocument.completion.completionItemKind.valueSet.isNull)
63     {
64         useCompatCompletionItemKinds();
65     }
66     else
67     {
68         useCompatCompletionItemKinds(
69                 params.capabilities.textDocument.completion.completionItemKind.valueSet);
70     }
71 
72     if (params.capabilities.workspace.symbol.isNull || params.capabilities.workspace.symbol.symbolKind.isNull
73             || params.capabilities.workspace.symbol.symbolKind.valueSet.isNull)
74     {
75         useCompatSymbolKinds();
76     }
77     else
78     {
79         useCompatSymbolKinds(params.capabilities.workspace.symbol.symbolKind.valueSet);
80     }
81 
82     if (params.capabilities.textDocument.documentSymbol.isNull
83             || params.capabilities.textDocument.documentSymbol.symbolKind.isNull
84             || params.capabilities.textDocument.documentSymbol.symbolKind.valueSet.isNull)
85     {
86         useCompatSymbolKinds();
87     }
88     else
89     {
90         useCompatSymbolKinds(params.capabilities.textDocument.documentSymbol.symbolKind.valueSet);
91     }
92 
93     Uri[] uris;
94 
95     if (!params.rootUri.isNull)
96     {
97         uris ~= new Uri(params.rootUri);
98     }
99     else if (!params.rootPath.isNull)
100     {
101         uris ~= Uri.fromPath(params.rootPath);
102     }
103 
104     if (!params.workspaceFolders.isNull)
105     {
106         uris ~= params.workspaceFolders.map!(wf => new Uri(wf.uri)).array;
107     }
108 
109     foreach (uri; uris.sort!q{a.path < b.path}
110             .uniq!q{a.path == b.path})
111     {
112         SymbolTool.instance.importPath(uri);
113         AnalysisTool.instance.addAnalysisConfigPath(uri);
114     }
115 
116     auto result = new InitializeResult();
117 
118     with (result.capabilities)
119     {
120         textDocumentSync = new TextDocumentSyncOptions(true.nullable,
121                 TextDocumentSyncKind.incremental.nullable);
122         textDocumentSync.save = new SaveOptions(false.nullable);
123         hoverProvider = initOptions.capabilities.hover;
124         completionProvider = initOptions.capabilities.completion ? new CompletionOptions(true.nullable,
125                 ["."].nullable).nullable : Nullable!CompletionOptions();
126         definitionProvider = initOptions.capabilities.definition;
127         typeDefinitionProvider = initOptions.capabilities.definition;
128         referencesProvider = initOptions.capabilities.references;
129         documentHighlightProvider = initOptions.capabilities.documentHighlight;
130         documentSymbolProvider = initOptions.capabilities.documentSymbol;
131         workspaceSymbolProvider = initOptions.capabilities.workspaceSymbol;
132         documentFormattingProvider = initOptions.capabilities.documentFormatting;
133         renameProvider = initOptions.capabilities.rename
134             ? new RenameOptions(true.nullable) : Nullable!RenameOptions();
135         workspace = new ServerCapabilities.Workspace(new ServerCapabilities.Workspace.WorkspaceFolders(true.nullable,
136                 JSONValue(true).nullable).nullable);
137     }
138 
139     return result;
140 }
141 
142 @("")
143 void initialized(JSONValue nothing)
144 {
145     import dls.protocol.interfaces : DidChangeWatchedFilesRegistrationOptions,
146         FileSystemWatcher, Registration, RegistrationParams;
147     import dls.protocol.jsonrpc : send;
148     import dls.protocol.messages.methods : Client;
149     import dls.protocol.state : initOptions, initState;
150     import dls.server : Server;
151     import dls.util.logger : logger;
152     import std.typecons : nullable;
153 
154     debug
155     {
156     }
157     else
158     {
159         import dls.updater : update;
160         import std.concurrency : spawn;
161 
162         spawn(&update, initOptions.autoUpdate);
163     }
164 
165     const didChangeWatchedFiles = initState.capabilities.workspace.didChangeWatchedFiles;
166 
167     if (!didChangeWatchedFiles.isNull && didChangeWatchedFiles.dynamicRegistration)
168     {
169         logger.info("Registering watchers");
170         auto watchers = [
171             new FileSystemWatcher("**/dub.selections.json"),
172             new FileSystemWatcher("**/dub.{json,sdl}"), new FileSystemWatcher("**/*.ini")
173         ];
174         auto registrationOptions = new DidChangeWatchedFilesRegistrationOptions(watchers);
175         auto registration = new Registration!DidChangeWatchedFilesRegistrationOptions(
176                 "dls-registration-watch-dub-files",
177                 "workspace/didChangeWatchedFiles", registrationOptions.nullable);
178         send(Client.registerCapability,
179                 new RegistrationParams!DidChangeWatchedFilesRegistrationOptions([registration]));
180     }
181 }
182 
183 @("")
184 JSONValue shutdown(JSONValue nothing)
185 {
186     import dls.server : Server;
187     import dls.tools.analysis_tool : AnalysisTool;
188     import dls.tools.format_tool : FormatTool;
189     import dls.tools.symbol_tool : SymbolTool;
190     import dls.util.logger : logger;
191 
192     logger.info("Shutting down server");
193     Server.shutdown = true;
194     AnalysisTool.shutdown();
195     FormatTool.shutdown();
196     SymbolTool.shutdown();
197     return JSONValue(null);
198 }
199 
200 @("")
201 void exit(JSONValue nothing)
202 {
203     import dls.server : Server;
204     import dls.util.logger : logger;
205 
206     logger.info("Exiting server");
207     Server.exit = true;
208 }
209 
210 @("$")
211 void cancelRequest(JSONValue id)
212 {
213 }