From 07570f990a4b9e1d27f27a8c4996e5ba1f7679c2 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Fri, 19 May 2017 16:57:47 -0400 Subject: [PATCH 18/31] FIXME: LSP parsing --- gcc/lsp-main.c | 21 ++++--- gcc/lsp.c | 122 +++++++++++++++++++++++++++++++++------ gcc/lsp.h | 8 ++- gcc/testsuite/gcc.dg/lsp/test.py | 45 ++++----------- 4 files changed, 133 insertions(+), 63 deletions(-) diff --git a/gcc/lsp-main.c b/gcc/lsp-main.c index a0d358f..7866307 100644 --- a/gcc/lsp-main.c +++ b/gcc/lsp-main.c @@ -27,21 +27,24 @@ along with GCC; see the file COPYING3. If not see using namespace lsp; -/* FIXME. */ +/* Implementation of lsp::server. + Ideally we'd wire this up to GCC's IR. */ -class test_lsp_server : public lsp::noop_server +class gcc_lsp_server : public lsp::noop_server { + /* Implementation of "textDocument/definition": + find the definition of the symbol at a given source location. */ + void do_text_document_definition (const TextDocumentPositionParams &p, vec &out) OVERRIDE { - // FIXME: currently a hack + /* TODO. */ + /* FIXME: currently, all we do is echo back the input location. */ Location result; - result.uri = "test.c"; - result.range.start.line = 20; - result.range.start.character = 10; - result.range.end.line = 20; - result.range.end.character = 15; + result.uri = p.textDocument.uri; + result.range.start = p.position; + result.range.end = p.position; out.safe_push (result); } }; @@ -52,7 +55,7 @@ void serve_lsp (int port) { // FIXME - test_lsp_server lsp_server; + gcc_lsp_server lsp_server; lsp::jsonrpc_server json_lsp_server (true, lsp_server); jsonrpc::http_server http_server (json_lsp_server); diff --git a/gcc/lsp.c b/gcc/lsp.c index ff25d00..f64643e 100644 --- a/gcc/lsp.c +++ b/gcc/lsp.c @@ -32,22 +32,105 @@ using namespace lsp; // TODO: autogenerate the interface binding/marshalling/demarshalling code // from an interface description. -TextDocumentItem -TextDocumentItem::from_json (const json::value *params, - json::value *&out_err) +#define GET_AS_OBJECT_OBJ(PARAMS) \ + const json::object *obj = (PARAMS)->as_object (); \ + if (!obj) \ + { \ + out_err = make_invalid_params (NULL, "not an object"); \ + return result; \ + } + +static bool +extract_int (const json::object *obj, const char *name, + int &out, json::value *&out_err) { - TextDocumentItem result; - const json::object *obj = params->as_object (); - if (!obj) + const json::value *v = obj->get (name); + if (!v) + { + char *msg = xasprintf ("missing int attribute: \"%s\"", name); + out_err = make_invalid_params (NULL, msg); + free (msg); + return false; + } + const json::number *n = v->as_number (); + if (!n) + { + char *msg = xasprintf ("not a number: \"%s\"", name); + out_err = make_invalid_params (NULL, msg); + free (msg); + return false; + } + out = n->get (); + return true; +} + +static bool +extract_string (const json::object *obj, const char *name, + const char *&out, json::value *&out_err) +{ + const json::value *v = obj->get (name); + if (!v) { - out_err = make_invalid_params (NULL, NULL); - return result; + char *msg = xasprintf ("missing string attribute: \"%s\"", name); + out_err = make_invalid_params (NULL, msg); + free (msg); + return false; } + const json::string *s = v->as_string (); + if (!s) + { + char *msg = xasprintf ("not a string: \"%s\"", name); + out_err = make_invalid_params (NULL, msg); + free (msg); + return false; + } + out = s->get_string (); + return true; +} + +#define SET_NUMBER(LHS, NAME) \ + do { \ + if (!extract_int (obj, NAME, LHS, out_err)) \ + return result; \ + } while (0) + +#define SET_STRING(LHS, NAME) \ + do { \ + if (!extract_string (obj, NAME, LHS, out_err)) \ + return result; \ + } while (0) + +Position +Position::from_json (const json::value *params, + json::value *&out_err) +{ + Position result; + GET_AS_OBJECT_OBJ (params); + SET_NUMBER (result.line, "line"); + SET_NUMBER (result.character, "character"); + return result; +} + +TextDocumentIdentifier +TextDocumentIdentifier::from_json (const json::value *params, + json::value *&out_err) +{ + TextDocumentIdentifier result; + GET_AS_OBJECT_OBJ (params); + SET_STRING (result.uri, "uri"); + return result; +} - result.uri = obj->get ("uri")->as_string ()->get_string (); - result.languageId = obj->get ("languageId")->as_string ()->get_string (); - result.version = obj->get ("version")->as_number ()->get (); - result.text = obj->get ("text")->as_string ()->get_string (); +TextDocumentItem +TextDocumentItem::from_json (const json::value *params, + json::value *&out_err) +{ + TextDocumentItem result; + GET_AS_OBJECT_OBJ (params); + SET_STRING (result.uri, "uri"); + SET_STRING (result.languageId, "languageId"); + SET_NUMBER (result.version, "version"); + SET_STRING (result.text, "text"); return result; } @@ -56,12 +139,7 @@ DidOpenTextDocumentParams::from_json (const json::value *params, json::value *&out_err) { DidOpenTextDocumentParams result; - const json::object *obj = params->as_object (); - if (!obj) - { - out_err = make_invalid_params (NULL, NULL); - return result; - } + GET_AS_OBJECT_OBJ (params); // FIXME: error-handling result.textDocument = TextDocumentItem::from_json (obj->get ("textDocument"), out_err); @@ -73,6 +151,8 @@ DidChangeTextDocumentParams::from_json (const json::value *params, json::value *&out_err) { DidChangeTextDocumentParams result; + GET_AS_OBJECT_OBJ (params); + // FIXME return result; } @@ -82,7 +162,11 @@ TextDocumentPositionParams::from_json (const json::value *params, json::value *&out_err) { TextDocumentPositionParams result; - // FIXME + GET_AS_OBJECT_OBJ (params); + result.textDocument + = TextDocumentIdentifier::from_json (obj->get ("textDocument"), out_err); + result.position + = Position::from_json (obj->get ("position"), out_err); return result; } diff --git a/gcc/lsp.h b/gcc/lsp.h index f44e278..b9c99c8 100644 --- a/gcc/lsp.h +++ b/gcc/lsp.h @@ -28,6 +28,8 @@ typedef const char *DocumentUri; struct Position { + static Position from_json (const json::value *params, + json::value *&out_err); json::value *to_json () const; int line; @@ -57,6 +59,9 @@ struct Location struct TextDocumentIdentifier { + static TextDocumentIdentifier from_json (const json::value *params, + json::value *&out_err); + DocumentUri uri; }; @@ -67,7 +72,7 @@ struct TextDocumentItem DocumentUri uri; const char *languageId; - double version; + int version; const char *text; }; @@ -178,4 +183,3 @@ class jsonrpc_server : public ::jsonrpc::server } // namespace lsp #endif /* GCC_LSP_H */ - diff --git a/gcc/testsuite/gcc.dg/lsp/test.py b/gcc/testsuite/gcc.dg/lsp/test.py index a793745..b08d119 100644 --- a/gcc/testsuite/gcc.dg/lsp/test.py +++ b/gcc/testsuite/gcc.dg/lsp/test.py @@ -1,18 +1,17 @@ -import jsonrpc # pip install json-rpc - -def test(): - print('test') +import json +import jsonrpc # pip install json-rpc import requests -import json + +# Various types to model the LSP interface class Position: - def __init__(self, line, column): + def __init__(self, line, character): self.line = line - self.column = column + self.character = character def to_json(self): - return {'line': self.line, 'column': self.column} + return {'line': self.line, 'character': self.character} class TextDocumentIdentifier: def __init__(self, uri): @@ -26,6 +25,7 @@ class TextDocumentPositionParams: self.textDocument = textDocument self.position = position +# A wrapper for making LSP calls against a server class LspProxy: def __init__(self, url): @@ -43,8 +43,8 @@ class LspProxy: def post_request(self, method, params): payload = self.make_request(method, params) headers = {'content-type': 'application/json'} - response = requests.post( - self.url, data=json.dumps(payload), headers=headers) + response = requests.post(self.url, data=json.dumps(payload), + headers=headers) print('response: %r' % response) print('response.json(): %r' % response.json()) return response.json() @@ -54,33 +54,12 @@ class LspProxy: {"textDocument" : textDocument.to_json(), "position" : position.to_json()}) print(json_resp) - def main(): url = "http://localhost:4000/jsonrpc" - headers = {'content-type': 'application/json'} - - """ - # Example echo method - payload = { - "method": "echo", - "params": ["echome!"], - "jsonrpc": "2.0", - "id": 0, - } - response = requests.post( - url, data=json.dumps(payload), headers=headers) - print('response: %r' % response) - response = response.json() - print('response.json(): %r' % response) - - assert response["result"] == "echome!" - assert response["jsonrpc"] - assert response["id"] == 0 - """ - proxy = LspProxy(url) - print(proxy.goto_definition(TextDocumentIdentifier('test.c'), Position(9, 17))) + print(proxy.goto_definition(TextDocumentIdentifier('test.c'), + Position(9, 17))) if __name__ == "__main__": main() -- 1.8.5.3