249 lines
5.5 KiB
D
249 lines
5.5 KiB
D
/**
|
|
* This file is part of DCD, a development tool for the D programming language.
|
|
* Copyright (C) 2014 Brian Schott
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
module common.messages;
|
|
|
|
import std.socket;
|
|
import msgpack;
|
|
import core.time : dur;
|
|
|
|
/**
|
|
* The type of completion list being returned
|
|
*/
|
|
enum CompletionType : string
|
|
{
|
|
/**
|
|
* The completion list contains a listing of identifier/kind pairs.
|
|
*/
|
|
identifiers = "identifiers",
|
|
|
|
/**
|
|
* The auto-completion list consists of a listing of functions and their
|
|
* parameters.
|
|
*/
|
|
calltips = "calltips",
|
|
|
|
/**
|
|
* The response contains the location of a symbol declaration.
|
|
*/
|
|
location = "location",
|
|
|
|
/**
|
|
* The response contains documentation comments for the symbol.
|
|
*/
|
|
ddoc = "ddoc",
|
|
}
|
|
|
|
/**
|
|
* Request kind
|
|
*/
|
|
enum RequestKind : ushort
|
|
{
|
|
// dfmt off
|
|
uninitialized = 0b00000000_00000000,
|
|
/// Autocompletion
|
|
autocomplete = 0b00000000_00000001,
|
|
/// Clear the completion cache
|
|
clearCache = 0b00000000_00000010,
|
|
/// Add import directory to server
|
|
addImport = 0b00000000_00000100,
|
|
/// Shut down the server
|
|
shutdown = 0b00000000_00001000,
|
|
/// Get declaration location of given symbol
|
|
symbolLocation = 0b00000000_00010000,
|
|
/// Get the doc comments for the symbol
|
|
doc = 0b00000000_00100000,
|
|
/// Query server status
|
|
query = 0b00000000_01000000,
|
|
/// Search for symbol
|
|
search = 0b00000000_10000000,
|
|
/// List import directories
|
|
listImports = 0b00000001_00000000,
|
|
/// local symbol usage
|
|
localUse = 0b00000010_00000000,
|
|
// dfmt on
|
|
}
|
|
|
|
/**
|
|
* Autocompletion request message
|
|
*/
|
|
struct AutocompleteRequest
|
|
{
|
|
/**
|
|
* File name used for error reporting
|
|
*/
|
|
string fileName;
|
|
|
|
/**
|
|
* Command coming from the client
|
|
*/
|
|
RequestKind kind;
|
|
|
|
/**
|
|
* Paths to be searched for import files
|
|
*/
|
|
string[] importPaths;
|
|
|
|
/**
|
|
* The source code to auto complete
|
|
*/
|
|
ubyte[] sourceCode;
|
|
|
|
/**
|
|
* The cursor position
|
|
*/
|
|
size_t cursorPosition;
|
|
|
|
/**
|
|
* Name of symbol searched for
|
|
*/
|
|
string searchName;
|
|
}
|
|
|
|
/**
|
|
* Autocompletion response message
|
|
*/
|
|
struct AutocompleteResponse
|
|
{
|
|
/**
|
|
* The autocompletion type. (Parameters or identifier)
|
|
*/
|
|
string completionType;
|
|
|
|
/**
|
|
* The path to the file that contains the symbol.
|
|
*/
|
|
string symbolFilePath;
|
|
|
|
/**
|
|
* The byte offset at which the symbol is located.
|
|
*/
|
|
size_t symbolLocation;
|
|
|
|
/**
|
|
* The documentation comment
|
|
*/
|
|
string[] docComments;
|
|
|
|
/**
|
|
* The completions
|
|
*/
|
|
string[] completions;
|
|
|
|
/**
|
|
* The kinds of the items in the completions array. Will be empty if the
|
|
* completion type is a function argument list.
|
|
*/
|
|
char[] completionKinds;
|
|
|
|
/**
|
|
* Symbol locations for symbol searches.
|
|
*/
|
|
size_t[] locations;
|
|
|
|
/**
|
|
* Import paths that are registered by the server.
|
|
*/
|
|
string[] importPaths;
|
|
|
|
/**
|
|
* Symbol identifier
|
|
*/
|
|
ulong symbolIdentifier;
|
|
|
|
/**
|
|
* Creates an empty acknoledgement response
|
|
*/
|
|
static AutocompleteResponse ack()
|
|
{
|
|
AutocompleteResponse response;
|
|
response.completionType = "ack";
|
|
return response;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns: true on success
|
|
*/
|
|
bool sendRequest(Socket socket, AutocompleteRequest request)
|
|
{
|
|
ubyte[] message = msgpack.pack(request);
|
|
ubyte[] messageBuffer = new ubyte[message.length + message.length.sizeof];
|
|
auto messageLength = message.length;
|
|
messageBuffer[0 .. size_t.sizeof] = (cast(ubyte*) &messageLength)[0 .. size_t.sizeof];
|
|
messageBuffer[size_t.sizeof .. $] = message[];
|
|
return socket.send(messageBuffer) == messageBuffer.length;
|
|
}
|
|
|
|
/**
|
|
* Gets the response from the server
|
|
*/
|
|
AutocompleteResponse getResponse(Socket socket)
|
|
{
|
|
ubyte[1024 * 16] buffer;
|
|
auto bytesReceived = socket.receive(buffer);
|
|
if (bytesReceived == Socket.ERROR)
|
|
throw new Exception("Incorrect number of bytes received");
|
|
if (bytesReceived == 0)
|
|
throw new Exception("Server closed the connection, 0 bytes received");
|
|
AutocompleteResponse response;
|
|
msgpack.unpack(buffer[0..bytesReceived], response);
|
|
return response;
|
|
}
|
|
|
|
/**
|
|
* Returns: true if a server instance is running
|
|
* Params:
|
|
* useTCP = `true` to check a TCP port, `false` for UNIX domain socket
|
|
* socketFile = the file name for the UNIX domain socket
|
|
* port = the TCP port
|
|
*/
|
|
bool serverIsRunning(bool useTCP, string socketFile, ushort port)
|
|
{
|
|
scope (failure)
|
|
return false;
|
|
AutocompleteRequest request;
|
|
request.kind = RequestKind.query;
|
|
Socket socket;
|
|
scope (exit)
|
|
{
|
|
socket.shutdown(SocketShutdown.BOTH);
|
|
socket.close();
|
|
}
|
|
version(Windows) useTCP = true;
|
|
if (useTCP)
|
|
{
|
|
socket = new TcpSocket(AddressFamily.INET);
|
|
socket.connect(new InternetAddress("localhost", port));
|
|
}
|
|
else
|
|
{
|
|
version(Windows) {} else
|
|
{
|
|
socket = new Socket(AddressFamily.UNIX, SocketType.STREAM);
|
|
socket.connect(new UnixAddress(socketFile));
|
|
}
|
|
}
|
|
socket.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"seconds"(5));
|
|
socket.blocking = true;
|
|
if (sendRequest(socket, request))
|
|
return getResponse(socket).completionType == "ack";
|
|
else
|
|
return false;
|
|
}
|