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