1 /** 2 * This file is part of DCD, a development tool for the D programming language. 3 * Copyright (C) 2014 Brian Schott 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 module common.messages; 20 21 import std.socket; 22 import msgpack; 23 import core.time : dur; 24 25 /** 26 * The type of completion list being returned 27 */ 28 enum CompletionType : string 29 { 30 /** 31 * The completion list contains a listing of identifier/kind pairs. 32 */ 33 identifiers = "identifiers", 34 35 /** 36 * The auto-completion list consists of a listing of functions and their 37 * parameters. 38 */ 39 calltips = "calltips", 40 41 /** 42 * The response contains the location of a symbol declaration. 43 */ 44 location = "location", 45 46 /** 47 * The response contains documentation comments for the symbol. 48 */ 49 ddoc = "ddoc", 50 } 51 52 /** 53 * Request kind 54 */ 55 enum RequestKind : ushort 56 { 57 // dfmt off 58 uninitialized = 0b00000000_00000000, 59 /// Autocompletion 60 autocomplete = 0b00000000_00000001, 61 /// Clear the completion cache 62 clearCache = 0b00000000_00000010, 63 /// Add import directory to server 64 addImport = 0b00000000_00000100, 65 /// Shut down the server 66 shutdown = 0b00000000_00001000, 67 /// Get declaration location of given symbol 68 symbolLocation = 0b00000000_00010000, 69 /// Get the doc comments for the symbol 70 doc = 0b00000000_00100000, 71 /// Query server status 72 query = 0b00000000_01000000, 73 /// Search for symbol 74 search = 0b00000000_10000000, 75 /// List import directories 76 listImports = 0b00000001_00000000, 77 /// local symbol usage 78 localUse = 0b00000010_00000000, 79 // dfmt on 80 } 81 82 /** 83 * Autocompletion request message 84 */ 85 struct AutocompleteRequest 86 { 87 /** 88 * File name used for error reporting 89 */ 90 string fileName; 91 92 /** 93 * Command coming from the client 94 */ 95 RequestKind kind; 96 97 /** 98 * Paths to be searched for import files 99 */ 100 string[] importPaths; 101 102 /** 103 * The source code to auto complete 104 */ 105 ubyte[] sourceCode; 106 107 /** 108 * The cursor position 109 */ 110 size_t cursorPosition; 111 112 /** 113 * Name of symbol searched for 114 */ 115 string searchName; 116 } 117 118 /** 119 * Autocompletion response message 120 */ 121 struct AutocompleteResponse 122 { 123 /** 124 * The autocompletion type. (Parameters or identifier) 125 */ 126 string completionType; 127 128 /** 129 * The path to the file that contains the symbol. 130 */ 131 string symbolFilePath; 132 133 /** 134 * The byte offset at which the symbol is located. 135 */ 136 size_t symbolLocation; 137 138 /** 139 * The documentation comment 140 */ 141 string[] docComments; 142 143 /** 144 * The completions 145 */ 146 string[] completions; 147 148 /** 149 * The kinds of the items in the completions array. Will be empty if the 150 * completion type is a function argument list. 151 */ 152 char[] completionKinds; 153 154 /** 155 * Symbol locations for symbol searches. 156 */ 157 size_t[] locations; 158 159 /** 160 * Import paths that are registered by the server. 161 */ 162 string[] importPaths; 163 164 /** 165 * Symbol identifier 166 */ 167 ulong symbolIdentifier; 168 } 169 170 /** 171 * Returns: true on success 172 */ 173 bool sendRequest(Socket socket, AutocompleteRequest request) 174 { 175 ubyte[] message = msgpack.pack(request); 176 ubyte[] messageBuffer = new ubyte[message.length + message.length.sizeof]; 177 auto messageLength = message.length; 178 messageBuffer[0 .. size_t.sizeof] = (cast(ubyte*) &messageLength)[0 .. size_t.sizeof]; 179 messageBuffer[size_t.sizeof .. $] = message[]; 180 return socket.send(messageBuffer) == messageBuffer.length; 181 } 182 183 /** 184 * Gets the response from the server 185 */ 186 AutocompleteResponse getResponse(Socket socket) 187 { 188 ubyte[1024 * 16] buffer; 189 auto bytesReceived = socket.receive(buffer); 190 if (bytesReceived == Socket.ERROR) 191 throw new Exception("Incorrect number of bytes received"); 192 if (bytesReceived == 0) 193 throw new Exception("Server closed the connection, 0 bytes received"); 194 AutocompleteResponse response; 195 msgpack.unpack(buffer[0..bytesReceived], response); 196 return response; 197 } 198 199 /** 200 * Returns: true if a server instance is running 201 * Params: 202 * useTCP = `true` to check a TCP port, `false` for UNIX domain socket 203 * socketFile = the file name for the UNIX domain socket 204 * port = the TCP port 205 */ 206 bool serverIsRunning(bool useTCP, string socketFile, ushort port) 207 { 208 scope (failure) 209 return false; 210 AutocompleteRequest request; 211 request.kind = RequestKind.query; 212 Socket socket; 213 scope (exit) 214 { 215 socket.shutdown(SocketShutdown.BOTH); 216 socket.close(); 217 } 218 version(Windows) useTCP = true; 219 if (useTCP) 220 { 221 socket = new TcpSocket(AddressFamily.INET); 222 socket.connect(new InternetAddress("localhost", port)); 223 } 224 else 225 { 226 version(Windows) {} else 227 { 228 socket = new Socket(AddressFamily.UNIX, SocketType.STREAM); 229 socket.connect(new UnixAddress(socketFile)); 230 } 231 } 232 socket.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"seconds"(5)); 233 socket.blocking = true; 234 if (sendRequest(socket, request)) 235 return getResponse(socket).completionType == "ack"; 236 else 237 return false; 238 }