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.protocol.interfaces : CodeActionKind; 30 import dls.protocol.logger : logger; 31 import dls.protocol.state : initOptions, initState; 32 import dls.server : Server; 33 import dls.tools.analysis_tool : AnalysisTool; 34 import dls.tools.command_tool : CommandTool; 35 import dls.tools.format_tool : FormatTool; 36 import dls.tools.symbol_tool : SymbolTool; 37 import dls.tools.tool : Tool; 38 import dls.util.json : convertToJSON; 39 import dls.util.uri : Uri, filenameCmp, sameFile; 40 import std.algorithm : map, sort, uniq; 41 import std.array : array; 42 import std.typecons : Nullable, nullable; 43 44 initState = params; 45 logger.info("Initializing server"); 46 AnalysisTool.initialize(); 47 CommandTool.initialize(); 48 FormatTool.initialize(); 49 SymbolTool.initialize(); 50 51 debug 52 { 53 } 54 else 55 { 56 import dls.updater : cleanup; 57 import std.concurrency : spawn; 58 59 spawn(&cleanup); 60 } 61 62 Uri[] uris; 63 64 if (!params.rootUri.isNull) 65 { 66 uris ~= new Uri(params.rootUri); 67 } 68 else if (!params.rootPath.isNull) 69 { 70 uris ~= Uri.fromPath(params.rootPath); 71 } 72 73 if (!params.workspaceFolders.isNull) 74 { 75 uris ~= params.workspaceFolders.map!(wf => new Uri(wf.uri)).array; 76 } 77 78 foreach (uri; uris.sort!((a, b) => filenameCmp(a, b) < 0) 79 .uniq!sameFile) 80 { 81 Tool.updateConfig(uri, JSONValue()); 82 SymbolTool.instance.importPath(uri); 83 AnalysisTool.instance.addAnalysisConfig(uri); 84 } 85 86 auto result = new InitializeResult(); 87 88 with (result.capabilities) 89 { 90 import std.json : JSONValue; 91 import std.typecons : Nullable; 92 93 textDocumentSync = new TextDocumentSyncOptions(true.nullable, 94 TextDocumentSyncKind.incremental.nullable); 95 textDocumentSync.save = new SaveOptions(false.nullable); 96 97 if (!params.capabilities.textDocument.isNull) 98 { 99 const textDocCaps = params.capabilities.textDocument.get(); 100 101 if (!textDocCaps.hover.isNull) 102 hoverProvider = initOptions.capabilities.hover; 103 104 if (!textDocCaps.completion.isNull && initOptions.capabilities.completion) 105 completionProvider = new CompletionOptions(true.nullable, ["."].nullable); 106 107 if (!textDocCaps.definition.isNull) 108 definitionProvider = initOptions.capabilities.definition; 109 110 if (!textDocCaps.typeDefinition.isNull) 111 typeDefinitionProvider = JSONValue(initOptions.capabilities.definition); 112 113 if (!textDocCaps.references.isNull) 114 referencesProvider = initOptions.capabilities.references; 115 116 if (!textDocCaps.documentHighlight.isNull) 117 documentHighlightProvider = initOptions.capabilities.documentHighlight; 118 119 if (!textDocCaps.documentSymbol.isNull) 120 documentSymbolProvider = initOptions.capabilities.documentSymbol; 121 122 if (!textDocCaps.codeAction.isNull) 123 codeActionProvider = JSONValue(initOptions.capabilities.codeAction); 124 125 if (!textDocCaps.formatting.isNull) 126 documentFormattingProvider = initOptions.capabilities.documentFormatting; 127 128 if (!textDocCaps.rangeFormatting.isNull) 129 documentRangeFormattingProvider = initOptions.capabilities.documentRangeFormatting; 130 131 if (!textDocCaps.onTypeFormatting.isNull 132 && initOptions.capabilities.documentOnTypeFormatting) 133 documentOnTypeFormattingProvider = new DocumentOnTypeFormattingOptions(";"); 134 135 if (!textDocCaps.rename.isNull) 136 { 137 immutable prepareSupport = !textDocCaps.rename.prepareSupport.isNull 138 && textDocCaps.rename.prepareSupport.get(); 139 140 renameProvider = initOptions.capabilities.rename ? prepareSupport 141 ? convertToJSON(new RenameOptions(true.nullable)) 142 : JSONValue(true) : JSONValue(false); 143 } 144 } 145 146 if (!params.capabilities.workspace.isNull) 147 { 148 const workspaceCaps = params.capabilities.workspace.get(); 149 150 if (!workspaceCaps.symbol.isNull) 151 workspaceSymbolProvider = initOptions.capabilities.workspaceSymbol; 152 153 if (!workspaceCaps.executeCommand.isNull && initOptions.capabilities.codeAction) 154 executeCommandProvider = new ExecuteCommandOptions(CommandTool.instance.commands); 155 156 if (!workspaceCaps.workspaceFolders.isNull && workspaceCaps.workspaceFolders.get()) 157 workspace = new ServerCapabilities.Workspace( 158 new ServerCapabilities.Workspace.WorkspaceFolders(true.nullable, 159 JSONValue(true).nullable).nullable); 160 } 161 } 162 163 Server.initialized = true; 164 return result; 165 } 166 167 @("") 168 void initialized(JSONValue nothing) 169 { 170 import dls.protocol.interfaces : ConfigurationItem, ConfigurationParams, 171 DidChangeWatchedFilesRegistrationOptions, FileSystemWatcher, 172 Registration, RegistrationParams, WatchKind; 173 import dls.protocol.jsonrpc : send; 174 import dls.protocol.logger : logger; 175 import dls.protocol.messages.methods : Client, Workspace; 176 import dls.protocol.state : initOptions, initState; 177 import dls.server : Server; 178 import dls.tools.analysis_tool : AnalysisTool; 179 import dls.tools.tool : Tool; 180 import std.typecons : Nullable, nullable; 181 182 debug 183 { 184 } 185 else 186 { 187 import dls.updater : update; 188 import std.concurrency : spawn; 189 190 spawn(&update, initOptions.autoUpdate); 191 } 192 193 if (!initState.capabilities.workspace.isNull) 194 { 195 immutable didChangeWatchedFiles = !initState.capabilities.workspace.didChangeWatchedFiles.isNull 196 && initState.capabilities.workspace.didChangeWatchedFiles.dynamicRegistration; 197 ubyte watchAllEvents = WatchKind.create + WatchKind.change + WatchKind.delete_; 198 199 if (didChangeWatchedFiles) 200 { 201 logger.info("Registering file watchers"); 202 //dfmt off 203 auto watchers = [ 204 new FileSystemWatcher("**/dub.{json,sdl}", watchAllEvents.nullable), 205 new FileSystemWatcher("**/dub.selections.json", watchAllEvents.nullable), 206 new FileSystemWatcher("**/.gitmodules", watchAllEvents.nullable), 207 new FileSystemWatcher("**/*.ini", watchAllEvents.nullable), 208 new FileSystemWatcher("**/*.{d,di}", watchAllEvents.nullable) 209 ]; 210 //dfmt on 211 auto registrationOptions = new DidChangeWatchedFilesRegistrationOptions(watchers); 212 auto registration = new Registration!DidChangeWatchedFilesRegistrationOptions("dls-file-watchers", 213 "workspace/didChangeWatchedFiles", registrationOptions.nullable); 214 send(Client.registerCapability, 215 new RegistrationParams!DidChangeWatchedFilesRegistrationOptions([registration])); 216 } 217 218 immutable configuration = !initState.capabilities.workspace.configuration.isNull 219 && initState.capabilities.workspace.configuration; 220 221 if (configuration) 222 { 223 auto items = [new ConfigurationItem(Nullable!string(null))]; 224 225 foreach (uri; Tool.workspacesUris) 226 { 227 items ~= new ConfigurationItem(uri.toString().nullable); 228 } 229 230 send(Workspace.configuration, new ConfigurationParams(items)); 231 } 232 } 233 234 AnalysisTool.instance.scanAllWorkspaces(); 235 } 236 237 @("") 238 JSONValue shutdown(JSONValue nothing) 239 { 240 import dls.protocol.definitions : TextDocumentIdentifier; 241 import dls.protocol.logger : logger; 242 import dls.server : Server; 243 import dls.tools.analysis_tool : AnalysisTool; 244 import dls.tools.command_tool : CommandTool; 245 import dls.tools.format_tool : FormatTool; 246 import dls.tools.symbol_tool : SymbolTool; 247 import dls.util.document : Document; 248 249 logger.info("Shutting down server"); 250 Server.initialized = false; 251 AnalysisTool.shutdown(); 252 CommandTool.shutdown(); 253 FormatTool.shutdown(); 254 SymbolTool.shutdown(); 255 256 foreach (uri; Document.uris) 257 { 258 Document.close(new TextDocumentIdentifier(uri)); 259 } 260 261 return JSONValue(null); 262 } 263 264 @("") 265 void exit(JSONValue nothing) 266 { 267 import dls.protocol.logger : logger; 268 import dls.server : Server; 269 270 if (Server.initialized) 271 { 272 logger.warning("Shutdown not requested prior to exit"); 273 shutdown(JSONValue()); 274 Server.initialized = true; 275 } 276 277 logger.info("Exiting server"); 278 Server.exit = true; 279 } 280 281 @("$") 282 void cancelRequest(CancelParams params) 283 { 284 import dls.server : Server; 285 286 Server.cancel(params.id); 287 }