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