diff --git a/README.md b/README.md index d021c4d..d0d59fd 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,16 @@ towards libclang with speed, stability, and ease of use in mind. * Meson * Git support through libgit2 * Fast C++ autocompletion -* Keyword and buffer autocompletion for other file types -* Language server protocol support that is enabled if `[language identifier]-language-server` executable is found. This executable can be a symbolic link to one of your installed language server binaries. -See [language-server-protocol/specification.md](https://github.com/Microsoft/language-server-protocol/blob/gh-pages/specification.md) for the currently defined language identifiers. * Tooltips showing type information and doxygen documentation (C++) * Rename refactoring across files (C++) * Highlighting of similar types (C++) * Automated documentation search (C++) * Go to declaration, implementation, methods and usages (C++) * CUDA files are supported and parsed as C++ +* Other file types: + * Language server protocol support is enabled if `[language identifier]-language-server` executable is found. This executable can be a symbolic link to one of your installed language server binaries. +See [language-server-protocol/specification.md](https://github.com/Microsoft/language-server-protocol/blob/gh-pages/specification.md) for the currently defined language identifiers. + * otherwise, only keyword and buffer completion supported * Find symbol through Ctags * Spell checking depending on file context * Run shell commands within juCi++ diff --git a/src/debug_lldb.cc b/src/debug_lldb.cc index f6af9ea..70bbe0a 100644 --- a/src/debug_lldb.cc +++ b/src/debug_lldb.cc @@ -411,21 +411,23 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil auto values=frame.GetVariables(true, true, true, false); //First try to find variable based on name, file and line number - for(uint32_t value_index=0;value_index(source_view)) { + if(dynamic_cast(source_view) || (source_view->language && source_view->language->get_id()=="rust")) { source_view->toggle_breakpoint=[source_view](int line_nr) { if(source_view->get_source_buffer()->get_source_marks_at_line(line_nr, "debug_breakpoint").size()>0) { auto start_iter=source_view->get_buffer()->get_iter_at_line(line_nr); diff --git a/src/project.h b/src/project.h index 6ded3c3..674f6d7 100644 --- a/src/project.h +++ b/src/project.h @@ -143,9 +143,9 @@ namespace Project { void compile_and_run() override; }; - class Rust : public Base { + class Rust : public LLDB { public: - Rust(std::unique_ptr &&build) : Base(std::move(build)) {} + Rust(std::unique_ptr &&build) : LLDB(std::move(build)) {} void compile_and_run() override; }; diff --git a/src/project_build.h b/src/project_build.h index cad9376..d2128db 100644 --- a/src/project_build.h +++ b/src/project_build.h @@ -11,9 +11,9 @@ namespace Project { boost::filesystem::path project_path; - boost::filesystem::path get_default_path(); + virtual boost::filesystem::path get_default_path(); virtual bool update_default(bool force=false) {return false;} - boost::filesystem::path get_debug_path(); + virtual boost::filesystem::path get_debug_path(); virtual bool update_debug(bool force=false) {return false;} virtual std::string get_compile_command() { return std::string(); } @@ -46,7 +46,16 @@ namespace Project { boost::filesystem::path get_executable(const boost::filesystem::path &path) override; }; - class CargoBuild : public Build {}; + class CargoBuild : public Build { + public: + boost::filesystem::path get_default_path() override { return project_path/"target"/"debug"; } + bool update_default(bool force=false) override { return true; } + boost::filesystem::path get_debug_path() override { return get_default_path(); } + bool update_debug(bool force=false) override { return true; } + + std::string get_compile_command() override { return "cargo build"; } + boost::filesystem::path get_executable(const boost::filesystem::path &path) override { return get_debug_path()/project_path.filename(); } + }; class NpmBuild : public Build {}; } diff --git a/src/source_language_protocol.cc b/src/source_language_protocol.cc index 1b4e13c..74f9cb0 100644 --- a/src/source_language_protocol.cc +++ b/src/source_language_protocol.cc @@ -4,6 +4,7 @@ #include "terminal.h" #include "project.h" #include "filesystem.h" +#include "debug_lldb.h" #include #include @@ -15,8 +16,7 @@ LanguageProtocol::Client::Client(std::string root_uri_, std::string language_id_ server_message_stream.write(bytes, n); parse_server_message(); }, [](const char *bytes, size_t n) { - if(output_messages_and_errors) - Terminal::get().async_print("Error (language server): "+std::string(bytes, n)+'\n', true); + std::cerr.write(bytes, n); }, true); } @@ -162,6 +162,8 @@ void LanguageProtocol::Client::parse_server_message() { } } else if(error_it!=pt.not_found()) { + if(!output_messages_and_errors) + boost::property_tree::write_json(std::cerr, pt); if(message_id) { auto id_it=handlers.find(message_id); if(id_it!=handlers.end()) { @@ -399,31 +401,10 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() { }; get_declaration_location=[this]() { - auto iter=get_buffer()->get_insert()->get_iter(); - auto offset=std::make_shared(); - std::promise result_processed; - client->write_request("textDocument/definition", "\"textDocument\":{\"uri\":\""+uri+"\"}, \"position\": {\"line\": "+std::to_string(iter.get_line())+", \"character\": "+std::to_string(iter.get_line_offset())+"}", [offset, &result_processed](const boost::property_tree::ptree &result, bool error) { - if(!error) { - for(auto it=result.begin();it!=result.end();++it) { - auto uri=it->second.get("uri", ""); - if(uri.compare(0, 7, "file://")==0) - uri.erase(0, 7); - auto range=it->second.find("range"); - if(range!=it->second.not_found()) { - auto start=range->second.find("start"); - if(start!=range->second.not_found()) - *offset=Offset(start->second.get("line", 0), start->second.get("character", 0), uri); - } - break; // TODO: can a language server return several definitions? - } - } - result_processed.set_value(); - }); - result_processed.get_future().get(); - - if(!*offset) + auto offset=get_declaration(get_buffer()->get_insert()->get_iter()); + if(!offset) Info::get().print("No declaration found"); - return *offset; + return offset; }; get_usages=[this] { @@ -816,10 +797,43 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect if(offset>=get_buffer()->get_char_count()) return; type_tooltips.clear(); - auto create_tooltip_buffer=[this, value=std::move(value)]() { + auto create_tooltip_buffer=[this, offset, value=std::move(value)]() { auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), "Type: "+value, "def:note"); +#ifdef JUCI_ENABLE_DEBUG + if(language_id=="rust") { + if(Debug::LLDB::get().is_stopped()) { + Glib::ustring value_type="Value"; + + auto start=get_buffer()->get_iter_at_offset(offset); + auto end=start; + auto previous=start; + while(previous.backward_char() && ((*previous>='A' && *previous<='Z') || (*previous>='a' && *previous<='z') || (*previous>='0' && *previous<='9') || *previous=='_') && start.backward_char()) {} + while(((*end>='A' && *end<='Z') || (*end>='a' && *end<='z') || (*end>='0' && *end<='9') || *end=='_') && end.forward_char()) {} + + auto offset=get_declaration(start); + Glib::ustring debug_value=Debug::LLDB::get().get_value(get_buffer()->get_text(start, end), offset.file_path, offset.line+1, offset.index+1); + if(debug_value.empty()) { + value_type="Return value"; + debug_value=Debug::LLDB::get().get_return_value(file_path, start.get_line()+1, start.get_line_index()+1); + } + if(!debug_value.empty()) { + size_t pos=debug_value.find(" = "); + if(pos!=Glib::ustring::npos) { + Glib::ustring::iterator iter; + while(!debug_value.validate(iter)) { + auto next_char_iter=iter; + next_char_iter++; + debug_value.replace(iter, next_char_iter, "?"); + } + tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), "\n\n"+value_type+": "+debug_value.substr(pos+3, debug_value.size()-(pos+3)-1), "def:note"); + } + } + } + } +#endif + return tooltip_buffer; }; @@ -872,6 +886,30 @@ void Source::LanguageProtocolView::tag_similar_symbols() { } } +Source::Offset Source::LanguageProtocolView::get_declaration(const Gtk::TextIter &iter) { + auto offset=std::make_shared(); + std::promise result_processed; + client->write_request("textDocument/definition", "\"textDocument\":{\"uri\":\""+uri+"\"}, \"position\": {\"line\": "+std::to_string(iter.get_line())+", \"character\": "+std::to_string(iter.get_line_offset())+"}", [offset, &result_processed](const boost::property_tree::ptree &result, bool error) { + if(!error) { + for(auto it=result.begin();it!=result.end();++it) { + auto uri=it->second.get("uri", ""); + if(uri.compare(0, 7, "file://")==0) + uri.erase(0, 7); + auto range=it->second.find("range"); + if(range!=it->second.not_found()) { + auto start=range->second.find("start"); + if(start!=range->second.not_found()) + *offset=Offset(start->second.get("line", 0), start->second.get("character", 0), uri); + } + break; // TODO: can a language server return several definitions? + } + } + result_processed.set_value(); + }); + result_processed.get_future().get(); + return *offset; +} + void Source::LanguageProtocolView::setup_autocomplete() { non_interactive_completion=[this] { if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) diff --git a/src/source_language_protocol.h b/src/source_language_protocol.h index 063bb78..768d88d 100644 --- a/src/source_language_protocol.h +++ b/src/source_language_protocol.h @@ -94,7 +94,7 @@ namespace Source { size_t document_version = 1; Dispatcher dispatcher; - + void setup_navigation_and_refactoring(); void escape_text(std::string &text); @@ -105,6 +105,8 @@ namespace Source { Glib::RefPtr similar_symbol_tag; sigc::connection delayed_tag_similar_symbols_connection; void tag_similar_symbols(); + + Offset get_declaration(const Gtk::TextIter &iter); Autocomplete autocomplete; void setup_autocomplete();