Browse Source

Added debug support for Rust

merge-requests/365/head
eidheim 8 years ago
parent
commit
d600375970
  1. 7
      README.md
  2. 32
      src/debug_lldb.cc
  3. 2
      src/notebook.cc
  4. 4
      src/project.h
  5. 15
      src/project_build.h
  6. 92
      src/source_language_protocol.cc
  7. 4
      src/source_language_protocol.h

7
README.md

@ -23,15 +23,16 @@ towards libclang with speed, stability, and ease of use in mind.
* Meson * Meson
* Git support through libgit2 * Git support through libgit2
* Fast C++ autocompletion * 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++) * Tooltips showing type information and doxygen documentation (C++)
* Rename refactoring across files (C++) * Rename refactoring across files (C++)
* Highlighting of similar types (C++) * Highlighting of similar types (C++)
* Automated documentation search (C++) * Automated documentation search (C++)
* Go to declaration, implementation, methods and usages (C++) * Go to declaration, implementation, methods and usages (C++)
* CUDA files are supported and parsed as 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 * Find symbol through Ctags
* Spell checking depending on file context * Spell checking depending on file context
* Run shell commands within juCi++ * Run shell commands within juCi++

32
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); auto values=frame.GetVariables(true, true, true, false);
//First try to find variable based on name, file and line number //First try to find variable based on name, file and line number
for(uint32_t value_index=0;value_index<values.GetSize();value_index++) { if(!file_path.empty()) {
lldb::SBStream stream; for(uint32_t value_index=0;value_index<values.GetSize();value_index++) {
auto value=values.GetValueAtIndex(value_index); lldb::SBStream stream;
auto value=values.GetValueAtIndex(value_index);
if(value.GetName()!=nullptr && value.GetName()==variable) {
auto declaration=value.GetDeclaration(); if(value.GetName()!=nullptr && value.GetName()==variable) {
if(declaration.IsValid()) { auto declaration=value.GetDeclaration();
if(declaration.GetLine()==line_nr && (declaration.GetColumn()==0 || declaration.GetColumn()==line_index)) { if(declaration.IsValid()) {
auto file_spec=declaration.GetFileSpec(); if(declaration.GetLine()==line_nr && (declaration.GetColumn()==0 || declaration.GetColumn()==line_index)) {
auto value_decl_path=filesystem::get_normal_path(file_spec.GetDirectory()); auto file_spec=declaration.GetFileSpec();
value_decl_path/=file_spec.GetFilename(); auto value_decl_path=filesystem::get_normal_path(file_spec.GetDirectory());
if(value_decl_path==file_path) { value_decl_path/=file_spec.GetFilename();
value.GetDescription(stream); if(value_decl_path==file_path) {
variable_value=stream.GetData(); value.GetDescription(stream);
break; variable_value=stream.GetData();
break;
}
} }
} }
} }

2
src/notebook.cc

@ -366,7 +366,7 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
}); });
#ifdef JUCI_ENABLE_DEBUG #ifdef JUCI_ENABLE_DEBUG
if(dynamic_cast<Source::ClangView*>(source_view)) { if(dynamic_cast<Source::ClangView*>(source_view) || (source_view->language && source_view->language->get_id()=="rust")) {
source_view->toggle_breakpoint=[source_view](int line_nr) { 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) { 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); auto start_iter=source_view->get_buffer()->get_iter_at_line(line_nr);

4
src/project.h

@ -143,9 +143,9 @@ namespace Project {
void compile_and_run() override; void compile_and_run() override;
}; };
class Rust : public Base { class Rust : public LLDB {
public: public:
Rust(std::unique_ptr<Build> &&build) : Base(std::move(build)) {} Rust(std::unique_ptr<Build> &&build) : LLDB(std::move(build)) {}
void compile_and_run() override; void compile_and_run() override;
}; };

15
src/project_build.h

@ -11,9 +11,9 @@ namespace Project {
boost::filesystem::path project_path; 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;} 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 bool update_debug(bool force=false) {return false;}
virtual std::string get_compile_command() { return std::string(); } 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; 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 {}; class NpmBuild : public Build {};
} }

92
src/source_language_protocol.cc

@ -4,6 +4,7 @@
#include "terminal.h" #include "terminal.h"
#include "project.h" #include "project.h"
#include "filesystem.h" #include "filesystem.h"
#include "debug_lldb.h"
#include <regex> #include <regex>
#include <future> #include <future>
@ -15,8 +16,7 @@ LanguageProtocol::Client::Client(std::string root_uri_, std::string language_id_
server_message_stream.write(bytes, n); server_message_stream.write(bytes, n);
parse_server_message(); parse_server_message();
}, [](const char *bytes, size_t n) { }, [](const char *bytes, size_t n) {
if(output_messages_and_errors) std::cerr.write(bytes, n);
Terminal::get().async_print("Error (language server): "+std::string(bytes, n)+'\n', true);
}, true); }, true);
} }
@ -162,6 +162,8 @@ void LanguageProtocol::Client::parse_server_message() {
} }
} }
else if(error_it!=pt.not_found()) { else if(error_it!=pt.not_found()) {
if(!output_messages_and_errors)
boost::property_tree::write_json(std::cerr, pt);
if(message_id) { if(message_id) {
auto id_it=handlers.find(message_id); auto id_it=handlers.find(message_id);
if(id_it!=handlers.end()) { if(id_it!=handlers.end()) {
@ -399,31 +401,10 @@ void Source::LanguageProtocolView::setup_navigation_and_refactoring() {
}; };
get_declaration_location=[this]() { get_declaration_location=[this]() {
auto iter=get_buffer()->get_insert()->get_iter(); auto offset=get_declaration(get_buffer()->get_insert()->get_iter());
auto offset=std::make_shared<Offset>(); if(!offset)
std::promise<void> 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<std::string>("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<unsigned>("line", 0), start->second.get<unsigned>("character", 0), uri);
}
break; // TODO: can a language server return several definitions?
}
}
result_processed.set_value();
});
result_processed.get_future().get();
if(!*offset)
Info::get().print("No declaration found"); Info::get().print("No declaration found");
return *offset; return offset;
}; };
get_usages=[this] { get_usages=[this] {
@ -816,10 +797,43 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect
if(offset>=get_buffer()->get_char_count()) if(offset>=get_buffer()->get_char_count())
return; return;
type_tooltips.clear(); 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()); 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"); 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; 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<Offset>();
std::promise<void> 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<std::string>("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<unsigned>("line", 0), start->second.get<unsigned>("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() { void Source::LanguageProtocolView::setup_autocomplete() {
non_interactive_completion=[this] { non_interactive_completion=[this] {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) if(CompletionDialog::get() && CompletionDialog::get()->is_visible())

4
src/source_language_protocol.h

@ -94,7 +94,7 @@ namespace Source {
size_t document_version = 1; size_t document_version = 1;
Dispatcher dispatcher; Dispatcher dispatcher;
void setup_navigation_and_refactoring(); void setup_navigation_and_refactoring();
void escape_text(std::string &text); void escape_text(std::string &text);
@ -105,6 +105,8 @@ namespace Source {
Glib::RefPtr<Gtk::TextTag> similar_symbol_tag; Glib::RefPtr<Gtk::TextTag> similar_symbol_tag;
sigc::connection delayed_tag_similar_symbols_connection; sigc::connection delayed_tag_similar_symbols_connection;
void tag_similar_symbols(); void tag_similar_symbols();
Offset get_declaration(const Gtk::TextIter &iter);
Autocomplete autocomplete; Autocomplete autocomplete;
void setup_autocomplete(); void setup_autocomplete();

Loading…
Cancel
Save