Skip to content

Commit

Permalink
Enable Document Symbol Lookup Feature for LSP (lcompilers#760)
Browse files Browse the repository at this point in the history
* enable symbol lookup feature for lsp
* fix symbol location indexing
  • Loading branch information
ankitaS11 committed Jul 19, 2022
1 parent 8c90296 commit 05a15e6
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 36 deletions.
1 change: 1 addition & 0 deletions src/libasr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ if (WITH_LSP)
set (SRC ${SRC}
lsp/JSONRPC2Connection.cpp
lsp/LPythonServer.cpp
lsp/MessageHandler.cpp
)
endif()

Expand Down
5 changes: 0 additions & 5 deletions src/libasr/lsp/JSONRPC2Connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,6 @@ void JSONRPC2Connection::send_notification(std::string method, rapidjson::Docume
body.AddMember("jsonrpc", rapidjson::Value().SetString("2.0", allocator), allocator);
body.AddMember("method", rapidjson::Value().SetString(method.c_str(), allocator), allocator);
body.AddMember("params", params, allocator);
rapidjson::StringBuffer buffer;
buffer.Clear();
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
body.Accept(writer);
std::string str_body(buffer.GetString());
this->_send(body);
}

Expand Down
100 changes: 71 additions & 29 deletions src/libasr/lsp/LPythonServer.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#include <map>
#include <string>

#include <rapidjson/document.h>
#include <rapidjson/writer.h>

#include "LPythonServer.hpp"
#include "JSONRPC2Connection.hpp"
#include "MessageHandler.hpp"

struct handle_functions
{
JSONRPC2Connection* conn;
Expand All @@ -14,6 +15,37 @@ struct handle_functions
this->conn = new JSONRPC2Connection();
}

std::string getPath(std::string uri) {
// Converts URI to path
if (uri.compare(0, 7, "file://"))
return uri;
std::string ret;
#ifdef _WIN32
// Skipping the initial "/" on Windows
size_t i = 8;
#else
size_t i = 7;
#endif
auto from_hex = [](unsigned char c) {
return c - '0' < 10 ? c - '0' : (c | 32) - 'a' + 10;
};
for (; i < uri.size(); i++) {
if (i + 3 <= uri.size() && uri[i] == '%') {
ret.push_back(from_hex(uri[i + 1]) * 16 + from_hex(uri[i + 2]));
i += 2;
} else
ret.push_back(uri[i]);
}
#ifdef _WIN32
std::replace(ret.begin(), ret.end(), '\\', '/');
if (ret.size() > 1 && ret[0] >= 'a' && ret[0] <= 'z' && ret[1] == ':') {
ret[0] = toupper(ret[0]);
}
#endif

return ret;
}

rapidjson::Document serve_initialize(rapidjson::Document &/*request*/) {
rapidjson::Document capabilities(rapidjson::kObjectType);
rapidjson::Document::AllocatorType &allocator = capabilities.GetAllocator();
Expand Down Expand Up @@ -73,43 +105,53 @@ struct handle_functions

void serve_document_symbol(rapidjson::Document &request, JSONRPC2Connection& obj, int rid) {
std::string uri = request["params"]["textDocument"]["uri"].GetString();
std::string path = getPath(uri);
using LFortran::CompilerOptions;
CompilerOptions compiler_options;
std::string runtime_library_dir = LFortran::get_runtime_library_dir();

int start_character = 0;
int start_line = 1;
int end_character = 10;
int end_line = 10;
std::vector<LFortran::LPython::lsp_locations>
symbol_lists = LFortran::LPython::get_SymbolLists(path, runtime_library_dir, compiler_options);

rapidjson::Document test_output(rapidjson::kArrayType);
rapidjson::Document range_object(rapidjson::kObjectType);
rapidjson::Document::AllocatorType &allocator = range_object.GetAllocator();
range_object.SetObject();

rapidjson::Document start_detail(rapidjson::kObjectType);
start_detail.SetObject();
start_detail.AddMember("character", rapidjson::Value().SetInt(start_character), allocator);
start_detail.AddMember("line", rapidjson::Value().SetInt(start_line), allocator);
range_object.AddMember("start", start_detail, allocator);

rapidjson::Document end_detail(rapidjson::kObjectType);
end_detail.SetObject();
end_detail.AddMember("character", rapidjson::Value().SetInt(end_character), allocator);
end_detail.AddMember("line", rapidjson::Value().SetInt(end_line), allocator);
range_object.AddMember("end", end_detail, allocator);

rapidjson::Document location_object(rapidjson::kObjectType);
location_object.SetObject();
location_object.AddMember("range", range_object, allocator);
location_object.AddMember("uri", rapidjson::Value().SetString(uri.c_str(), allocator), allocator);
rapidjson::Document test_capture(rapidjson::kObjectType);

rapidjson::Document test_output(rapidjson::kArrayType);
test_output.SetArray();

rapidjson::Document test_capture(rapidjson::kObjectType);
test_capture.SetObject();
test_capture.AddMember("kind", rapidjson::Value().SetInt(12), allocator);
test_capture.AddMember("location", location_object, allocator);
test_capture.AddMember("name", rapidjson::Value().SetString("lsp_symbols", allocator), allocator);
test_output.PushBack(test_capture, test_output.GetAllocator());

for (auto symbol : symbol_lists) {
uint32_t start_character = symbol.first_column;
uint32_t start_line = symbol.first_line;
uint32_t end_character = symbol.last_column;
uint32_t end_line = symbol.last_line;
std::string name = symbol.symbol_name;

range_object.SetObject();
rapidjson::Document::AllocatorType &allocator = range_object.GetAllocator();

start_detail.SetObject();
start_detail.AddMember("character", rapidjson::Value().SetInt(start_character), allocator);
start_detail.AddMember("line", rapidjson::Value().SetInt(start_line), allocator);
range_object.AddMember("start", start_detail, allocator);

end_detail.SetObject();
end_detail.AddMember("character", rapidjson::Value().SetInt(end_character), allocator);
end_detail.AddMember("line", rapidjson::Value().SetInt(end_line), allocator);
range_object.AddMember("end", end_detail, allocator);

location_object.SetObject();
location_object.AddMember("range", range_object, allocator);
location_object.AddMember("uri", rapidjson::Value().SetString(uri.c_str(), allocator), allocator);

test_capture.SetObject();
test_capture.AddMember("kind", rapidjson::Value().SetInt(12), allocator);
test_capture.AddMember("location", location_object, allocator);
test_capture.AddMember("name", rapidjson::Value().SetString(name.c_str(), allocator), allocator);
test_output.PushBack(test_capture, test_output.GetAllocator());
}
obj.write_message(rid, test_output);
}
};
Expand Down
4 changes: 4 additions & 0 deletions src/libasr/lsp/LPythonServer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
#include <string>
#include <rapidjson/document.h>

#include <libasr/utils.h>
#include <lpython/semantics/python_ast_to_asr.h>

#include "JSONRPC2Connection.hpp"
#include "MessageHandler.hpp"

class LPythonServer {
public:
Expand Down
40 changes: 40 additions & 0 deletions src/libasr/lsp/MessageHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "MessageHandler.hpp"

namespace LFortran::LPython {

std::vector<lsp_locations> get_SymbolLists(const std::string &infile,
const std::string &runtime_library_dir,
LFortran::CompilerOptions &compiler_options) {
Allocator al(4*1024);
LFortran::diag::Diagnostics diagnostics;
LFortran::LocationManager lm;
lm.in_filename = infile;
std::string input = LFortran::read_file(infile);
lm.init_simple(input);
LFortran::Result<LFortran::LPython::AST::ast_t*> r1 = LFortran::parse_python_file(
al, runtime_library_dir, infile, diagnostics, compiler_options.new_parser);
LFortran::LPython::AST::ast_t* ast = r1.result;
LFortran::Result<LFortran::ASR::TranslationUnit_t*> x = LFortran::LPython::python_ast_to_asr(al, *ast, diagnostics, true,
compiler_options.disable_main, compiler_options.symtab_only, infile);
std::vector<lsp_locations> symbol_lists;
lsp_locations loc;
for (auto &a : x.result->m_global_scope->get_scope()) {
std::string symbol_name = a.first;
uint32_t first_line;
uint32_t last_line;
uint32_t first_column;
uint32_t last_column;
lm.pos_to_linecol(a.second->base.loc.first, first_line, first_column);
lm.pos_to_linecol(a.second->base.loc.last, last_line, last_column);
loc.first_column = first_column;
loc.last_column = last_column;
loc.first_line = first_line-1;
loc.last_line = last_line-1;
loc.symbol_name = symbol_name;
symbol_lists.push_back(loc);
}
return symbol_lists;
}

}

23 changes: 23 additions & 0 deletions src/libasr/lsp/MessageHandler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef MESSAGE_HANDLER_HPP
#define MESSAGE_HANDLER_HPP

#include <lpython/utils.h>
#include <lpython/parser/parser.h>
#include <libasr/string_utils.h>
#include <lpython/semantics/python_ast_to_asr.h>

namespace LFortran::LPython {

struct lsp_locations {
std::string symbol_name;
uint32_t first_line;
uint32_t first_column;
uint32_t last_line;
uint32_t last_column;
};
std::vector<lsp_locations> get_SymbolLists(const std::string &infile,
const std::string &runtime_library_dir,
LFortran::CompilerOptions &compiler_options);

}
#endif
2 changes: 1 addition & 1 deletion src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@


namespace LFortran::LPython {

// Does a CPython style lookup for a module:
// * First the current directory (this is incorrect, we need to do it relative to the current file)
// * Then the LPython runtime directory
Expand Down
2 changes: 1 addition & 1 deletion src/lpython/semantics/python_ast_to_asr.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <libasr/asr.h>

namespace LFortran::LPython {

std::string pickle_python(AST::ast_t &ast, bool colors=false, bool indent=false);
std::string pickle_tree_python(AST::ast_t &ast, bool colors=true);
Result<ASR::TranslationUnit_t*> python_ast_to_asr(Allocator &al,
Expand Down

0 comments on commit 05a15e6

Please sign in to comment.