-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LSP: Add support for goto def in compiler for VSCode extension #982
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -449,6 +449,151 @@ int get_errors (const std::string &infile, | |||||||||||
return 0; | ||||||||||||
} | ||||||||||||
|
||||||||||||
bool input_in_scope_identifier(int input_location, | ||||||||||||
std::pair<int, int> token_location) { | ||||||||||||
return token_location.first <= input_location && | ||||||||||||
input_location <= token_location.second; | ||||||||||||
} | ||||||||||||
|
||||||||||||
bool find_tok_in_scopes(const std::string &infile, | ||||||||||||
const std::string &runtime_library_dir, | ||||||||||||
CompilerOptions &compiler_options, | ||||||||||||
const std::string tok_name) { | ||||||||||||
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); | ||||||||||||
if (r1.ok) { | ||||||||||||
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); | ||||||||||||
if (!x.ok) { | ||||||||||||
std::cout << "{}\n"; | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
std::vector<LFortran::LPython::document_symbols> symbol_lists; | ||||||||||||
LFortran::LPython::document_symbols 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; | ||||||||||||
if (loc.symbol_name == tok_name) { | ||||||||||||
rapidjson::Document test_output(rapidjson::kArrayType); | ||||||||||||
rapidjson::Document range_object(rapidjson::kObjectType); | ||||||||||||
rapidjson::Document start_detail(rapidjson::kObjectType); | ||||||||||||
rapidjson::Document end_detail(rapidjson::kObjectType); | ||||||||||||
rapidjson::Document location_object(rapidjson::kObjectType); | ||||||||||||
rapidjson::Document test_capture(rapidjson::kObjectType); | ||||||||||||
|
||||||||||||
test_output.SetArray(); | ||||||||||||
|
||||||||||||
uint32_t start_character = loc.first_column; | ||||||||||||
uint32_t start_line = loc.first_line; | ||||||||||||
uint32_t end_character = loc.last_column; | ||||||||||||
uint32_t end_line = loc.last_line; | ||||||||||||
std::string name = loc.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", allocator), allocator); | ||||||||||||
|
||||||||||||
test_capture.SetObject(); | ||||||||||||
test_capture.AddMember("kind", rapidjson::Value().SetInt(1), 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()); | ||||||||||||
rapidjson::StringBuffer buffer; | ||||||||||||
buffer.Clear(); | ||||||||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); | ||||||||||||
test_output.Accept(writer); | ||||||||||||
std::string resp_str(buffer.GetString()); | ||||||||||||
|
||||||||||||
std::cout << resp_str; | ||||||||||||
return true; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
std::cout << "{}\n"; | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
std::cout << "{}\n"; | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
|
||||||||||||
std::pair<std::pair<int, int>, std::pair<int, int>> | ||||||||||||
return_goto_def(const std::string &infile, | ||||||||||||
const std::string &runtime_library_dir, | ||||||||||||
CompilerOptions compiler_options, int index) { | ||||||||||||
std::string input = LFortran::read_file(infile); | ||||||||||||
// Src -> Tokens | ||||||||||||
Allocator al(64 * 1024 * 1024); | ||||||||||||
std::vector<int> toks; | ||||||||||||
std::vector<LFortran::YYSTYPE> stypes; | ||||||||||||
std::vector<LFortran::Location> locations; | ||||||||||||
LFortran::diag::Diagnostics diagnostics; | ||||||||||||
auto res = LFortran::tokens(al, input, diagnostics, &stypes, &locations); | ||||||||||||
LFortran::LocationManager lm; | ||||||||||||
lm.in_filename = infile; | ||||||||||||
lm.init_simple(input); | ||||||||||||
// std::cerr << diagnostics.render(input, lm, compiler_options); | ||||||||||||
if (res.ok) { | ||||||||||||
toks = res.result; | ||||||||||||
} else { | ||||||||||||
std::cout << "{}\n"; | ||||||||||||
return {}; | ||||||||||||
} | ||||||||||||
|
||||||||||||
for (size_t i = 0; i < toks.size(); ++i) { | ||||||||||||
if (input_in_scope_identifier( | ||||||||||||
index, | ||||||||||||
std::make_pair<int, int>(locations[i].first, locations[i].last))) { | ||||||||||||
Comment on lines
+583
to
+585
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just do:
Suggested change
And remove |
||||||||||||
auto token_name = LFortran::get_token_name(toks[i], stypes[i]); | ||||||||||||
if (find_tok_in_scopes(infile, runtime_library_dir, compiler_options, | ||||||||||||
token_name)) { | ||||||||||||
return {}; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
} | ||||||||||||
std::cout << "{}\n"; | ||||||||||||
return {}; | ||||||||||||
} | ||||||||||||
|
||||||||||||
#endif | ||||||||||||
|
||||||||||||
#ifdef HAVE_LFORTRAN_LLVM | ||||||||||||
|
@@ -779,6 +924,7 @@ int main(int argc, char *argv[]) | |||||||||||
bool show_cpp = false; | ||||||||||||
bool show_c = false; | ||||||||||||
bool show_document_symbols = false; | ||||||||||||
int goto_def_index = -1; | ||||||||||||
bool show_errors = false; | ||||||||||||
bool with_intrinsic_modules = false; | ||||||||||||
std::string arg_pass; | ||||||||||||
|
@@ -859,6 +1005,7 @@ int main(int argc, char *argv[]) | |||||||||||
// LSP specific options | ||||||||||||
app.add_flag("--show-errors", show_errors, "Show errors when LSP is running in the background"); | ||||||||||||
app.add_flag("--show-document-symbols", show_document_symbols, "Show symbols in lpython file"); | ||||||||||||
app.add_option("--goto-def", goto_def_index, "Pass index for GoTo Definition"); | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. VSCode calls it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it seems the input almost always is a line and column number? If so, that should be the input here as well. So the description would be "Return a definition for the symbol at the given line and column", and if this is too long, we can shorten it. |
||||||||||||
|
||||||||||||
if( compiler_options.fast ) { | ||||||||||||
lpython_pass_manager.use_optimization_passes(); | ||||||||||||
|
@@ -1028,7 +1175,17 @@ int main(int argc, char *argv[]) | |||||||||||
return 1; | ||||||||||||
#endif | ||||||||||||
} | ||||||||||||
|
||||||||||||
if (goto_def_index != -1) { | ||||||||||||
#ifdef HAVE_LFORTRAN_RAPIDJSON | ||||||||||||
return_goto_def(arg_file, runtime_library_dir, compiler_options, goto_def_index); | ||||||||||||
return 1; | ||||||||||||
#else | ||||||||||||
std::cerr << "Compiler was not built with LSP support (-DWITH_LSP), " | ||||||||||||
"please build it again." | ||||||||||||
<< std::endl; | ||||||||||||
return 1; | ||||||||||||
#endif | ||||||||||||
} | ||||||||||||
if (show_errors) { | ||||||||||||
#ifdef HAVE_LFORTRAN_RAPIDJSON | ||||||||||||
return get_errors(arg_file, runtime_library_dir, compiler_options); | ||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -696,5 +696,24 @@ std::string pickle_token(int token, const LFortran::YYSTYPE &yystype) | |
return t; | ||
} | ||
|
||
std::string get_token_name(int token, const LFortran::YYSTYPE &yystype) { | ||
std::string token_name; | ||
if (token == yytokentype::TK_NAME) { | ||
token_name += yystype.string.str(); | ||
} else if (token == yytokentype::TK_INTEGER) { | ||
token_name += BigInt::int_to_str(yystype.n); | ||
} else if (token == yytokentype::TK_REAL) { | ||
token_name += std::to_string(yystype.f); | ||
} else if (token == yytokentype::TK_IMAG_NUM) { | ||
token_name += std::to_string(yystype.f) + "j"; | ||
} else if (token == yytokentype::TK_STRING) { | ||
token_name = "\"" + yystype.string.str() + "\""; | ||
} else if (token == yytokentype::TK_TYPE_COMMENT) { | ||
token_name = "\"" + yystype.string.str() + "\""; | ||
} | ||
return token_name; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we call this function in |
||
|
||
|
||
|
||
} // namespace LFortran |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will only be able to lookup symbols at global scope. Rather, we need to use
resolve_symbol
to lookup the symbol in the scoped symbol table. For that, we need to know which ASR node we are in.To do that, we need to implement "index" -> ASR node calculation.
This will have to be one by the "overlapping intervals" problem, as we discussed some time ago.