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 24 /** 25 * The type of completion list being returned 26 */ 27 enum CompletionType : string 28 { 29 /** 30 * The completion list contains a listing of identifier/kind pairs. 31 */ 32 identifiers = "identifiers", 33 34 /** 35 * The auto-completion list consists of a listing of functions and their 36 * parameters. 37 */ 38 calltips = "calltips", 39 40 /** 41 * The response contains the location of a symbol declaration. 42 */ 43 location = "location", 44 45 /** 46 * The response contains documentation comments for the symbol. 47 */ 48 ddoc = "ddoc", 49 } 50 51 /** 52 * Request kind 53 */ 54 enum RequestKind : ushort 55 { 56 // dfmt off 57 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 78 // dfmt on 79 } 80 81 /** 82 * Autocompletion request message 83 */ 84 struct AutocompleteRequest 85 { 86 /** 87 * File name used for error reporting 88 */ 89 string fileName; 90 91 /** 92 * Command coming from the client 93 */ 94 RequestKind kind; 95 96 /** 97 * Paths to be searched for import files 98 */ 99 string[] importPaths; 100 101 /** 102 * The source code to auto complete 103 */ 104 ubyte[] sourceCode; 105 106 /** 107 * The cursor position 108 */ 109 size_t cursorPosition; 110 111 /** 112 * Name of symbol searched for 113 */ 114 string searchName; 115 } 116 117 /** 118 * Autocompletion response message 119 */ 120 struct AutocompleteResponse 121 { 122 /** 123 * The autocompletion type. (Parameters or identifier) 124 */ 125 string completionType; 126 127 /** 128 * The path to the file that contains the symbol. 129 */ 130 string symbolFilePath; 131 132 /** 133 * The byte offset at which the symbol is located. 134 */ 135 size_t symbolLocation; 136 137 /** 138 * The documentation comment 139 */ 140 string[] docComments; 141 142 /** 143 * The completions 144 */ 145 string[] completions; 146 147 /** 148 * The kinds of the items in the completions array. Will be empty if the 149 * completion type is a function argument list. 150 */ 151 char[] completionKinds; 152 153 /** 154 * Symbol locations for symbol searches. 155 */ 156 size_t[] locations; 157 158 /** 159 * Import paths that are registered by the server. 160 */ 161 string[] importPaths; 162 } 163 164 /** 165 * Returns: true on success 166 */ 167 bool sendRequest(Socket socket, AutocompleteRequest request) 168 { 169 ubyte[] message = msgpack.pack(request); 170 ubyte[] messageBuffer = new ubyte[message.length + message.length.sizeof]; 171 auto messageLength = message.length; 172 messageBuffer[0 .. size_t.sizeof] = (cast(ubyte*) &messageLength)[0 .. size_t.sizeof]; 173 messageBuffer[size_t.sizeof .. $] = message[]; 174 return socket.send(messageBuffer) == messageBuffer.length; 175 } 176 177 /** 178 * Gets the response from the server 179 */ 180 AutocompleteResponse getResponse(Socket socket) 181 { 182 ubyte[1024 * 16] buffer; 183 auto bytesReceived = socket.receive(buffer); 184 if (bytesReceived == Socket.ERROR) 185 throw new Exception("Incorrect number of bytes received"); 186 if (bytesReceived == 0) 187 throw new Exception("Server closed the connection, 0 bytes received"); 188 AutocompleteResponse response; 189 msgpack.unpack(buffer[0..bytesReceived], response); 190 return response; 191 } 192 193 /** 194 * Returns: true if a server instance is running 195 * Params: 196 * useTCP = `true` to check a TCP port, `false` for UNIX domain socket 197 * socketFile = the file name for the UNIX domain socket 198 * port = the TCP port 199 */ 200 bool serverIsRunning(bool useTCP, string socketFile, ushort port) 201 { 202 scope (failure) 203 return false; 204 AutocompleteRequest request; 205 request.kind = RequestKind.query; 206 Socket socket; 207 scope (exit) 208 { 209 socket.shutdown(SocketShutdown.BOTH); 210 socket.close(); 211 } 212 if (useTCP) 213 { 214 socket = new TcpSocket(AddressFamily.INET); 215 socket.connect(new InternetAddress("localhost", port)); 216 } 217 else 218 { 219 socket = new Socket(AddressFamily.UNIX, SocketType.STREAM); 220 socket.connect(new UnixAddress(socketFile)); 221 } 222 socket.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"seconds"(5)); 223 socket.blocking = true; 224 if (sendRequest(socket, request)) 225 return getResponse(socket).completionType == "ack"; 226 else 227 return false; 228 }