From 311f4596502e0cfcec9a6f1a574f00eff9b8e5ab Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 3 Jul 2016 09:18:47 +0200 Subject: [PATCH] Added Ctags fallback to Go to Implementation, and can now set breakpoints when compile is in progress --- src/CMakeLists.txt | 2 +- src/ctags.cc | 169 +++++++++++++++++++++++++++++++++----------- src/ctags.h | 11 +-- src/project.cc | 96 +++++++++++++------------ src/source_clang.cc | 18 +++++ src/window.cc | 30 +++----- 6 files changed, 210 insertions(+), 116 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 78e83a7..b41f0aa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -40,7 +40,6 @@ set(global_libraries ${global_libraries} set(project_files config.cc - ctags.cc dialogs.cc dialogs_unix.cc directories.cc @@ -59,6 +58,7 @@ set(project_files #Files used both in ../src and ../tests set(project_shared_files cmake.cc + ctags.cc dispatcher.cc filesystem.cc git.cc diff --git a/src/ctags.cc b/src/ctags.cc index de5ac9f..b9e1d6e 100644 --- a/src/ctags.cc +++ b/src/ctags.cc @@ -1,6 +1,11 @@ #include "ctags.h" #include "config.h" #include "terminal.h" +#include "project_build.h" +#include "filesystem.h" +#include "directories.h" +#include +#include //Temporary fix for current Arch Linux boost linking problem #ifdef __GNUC_PREREQ @@ -14,71 +19,149 @@ #define REGEX_NS boost #endif -std::unique_ptr Ctags::get_result(const boost::filesystem::path &path, std::vector exclude_paths) { +std::pair > Ctags::get_result(const boost::filesystem::path &path) { + auto build=Project::Build::create(path); + auto run_path=build->project_path; std::string exclude; - for(auto &path: exclude_paths) { + if(!run_path.empty()) { + auto path=filesystem::get_relative_path(build->get_default_path(), build->project_path); if(!path.empty()) exclude+=" --exclude="+path.string(); + path=filesystem::get_relative_path(build->get_debug_path(), build->project_path); + if(!path.empty()) + exclude+=" --exclude="+path.string(); + } + else { + if(!Directories::get().path.empty()) + run_path=Directories::get().path; + else + run_path=path; } std::stringstream stdin_stream; //TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below std::unique_ptr stdout_stream(new std::stringstream()); - auto command=Config::get().project.ctags_command+exclude+" --fields=n --sort=foldcase -f- -R *"; - Terminal::get().process(stdin_stream, *stdout_stream, command, path); - return stdout_stream; + auto command=Config::get().project.ctags_command+exclude+" --fields=n --sort=foldcase -I \"override noexcept\" -f- -R *"; + Terminal::get().process(stdin_stream, *stdout_stream, command, run_path); + return {run_path, std::move(stdout_stream)}; } -Ctags::Data Ctags::parse_line(const std::string &line) { - Data data; +Ctags::Location Ctags::parse_line(const std::string &line, bool markup) { + Location location; - const static REGEX_NS::regex regex("^([^\t]+)\t([^\t]+)\t(?:/\\^)?([ \t]*)(.+);\"\tline:([0-9]+)$"); + const static REGEX_NS::regex regex("^([^\t]+)\t([^\t]+)\t(?:/\\^)?([ \t]*)(.+)$"); REGEX_NS::smatch sm; if(REGEX_NS::regex_match(line, sm, regex)) { - data.source=sm[4].str(); - if(data.source.size()>1 && data.source[data.source.size()-1]=='/' && data.source[data.source.size()-2]!='\\') { - data.source.pop_back(); - if(data.source.size()>1 && data.source[data.source.size()-1]=='$' && data.source[data.source.size()-2]!='\\') - data.source.pop_back(); + location.source=sm[4].str(); + size_t pos=location.source.find(";\"\tline:"); + if(pos==std::string::npos) + return location; + try { + location.line=std::stoul(location.source.substr(pos+8))-1; + } + catch(const std::exception&) { + location.line=0; + } + location.source.erase(pos); + if(location.source.size()>1 && location.source[location.source.size()-1]=='/' && location.source[location.source.size()-2]!='\\') { + location.source.pop_back(); + if(location.source.size()>1 && location.source[location.source.size()-1]=='$' && location.source[location.source.size()-2]!='\\') + location.source.pop_back(); auto symbol=sm[1].str(); - data.path=sm[2].str(); - data.index=sm[3].str().size(); - try { - data.line=std::stoul(sm[5].str())-1; - } - catch(const std::exception&) { - data.line=0; - } + location.file_path=sm[2].str(); + location.index=sm[3].str().size(); - size_t pos=data.source.find(symbol); + size_t pos=location.source.find(symbol); if(pos!=std::string::npos) - data.index+=pos; + location.index+=pos; - data.source=Glib::Markup::escape_text(data.source); - pos=-1; - while((pos=data.source.find(symbol, pos+1))!=std::string::npos) { - data.source.insert(pos+symbol.size(), ""); - data.source.insert(pos, ""); - pos+=7+symbol.size(); + if(markup) { + location.source=Glib::Markup::escape_text(location.source); + pos=-1; + while((pos=location.source.find(symbol, pos+1))!=std::string::npos) { + location.source.insert(pos+symbol.size(), ""); + location.source.insert(pos, ""); + pos+=7+symbol.size(); + } } } else { - data.path=sm[2].str(); - data.index=0; - data.source=sm[1].str(); - try { - data.line=std::stoul(sm[3].str())-1; - } - catch(const std::exception&) { - data.line=0; - } - data.source=""+Glib::Markup::escape_text(data.source)+""; + location.file_path=sm[2].str(); + location.index=0; + location.source=sm[1].str(); + if(markup) + location.source=""+Glib::Markup::escape_text(location.source)+""; } } - else { - Terminal::get().print("Warning (ctags): please report to the juCi++ project that the following line was not parsed:\n"+ - line+'\n', true); + else + std::cerr << "Warning (ctags): please report to the juCi++ project that the following line was not parsed:\n" << line << std::endl; + + return location; +} + +Ctags::Location Ctags::get_location(const boost::filesystem::path &path, const std::string &name, const std::string &type) { + //insert name into type + size_t c=0; + size_t bracket_count=0; + for(;c') + --bracket_count; + else if(bracket_count==0 && type[c]=='(') + break; + } + auto full_type=type; + full_type.insert(c, name); + + //Split up full_type + std::vector parts; + size_t text_start=-1; + for(size_t c=0;c='0' && chr<='9') || (chr>='a' && chr<='z') || (chr>='A' && chr<='Z') || chr=='_' || chr=='~') { + if(text_start==static_cast(-1)) + text_start=c; + } + else { + if(text_start!=static_cast(-1)) + parts.emplace_back(full_type.substr(text_start, c-text_start)); + text_start=-1; + if(chr=='*' || chr=='&') + parts.emplace_back(std::string()+chr); + } + } + + auto result=get_result(path); + result.second->seekg(0, std::ios::end); + if(result.second->tellg()==0) + return Location(); + result.second->seekg(0, std::ios::beg); + + std::string line; + size_t best_score=0; + Location best_location; + while(std::getline(*result.second, line)) { + if(line.find(name)==std::string::npos) + continue; + auto location=Ctags::parse_line(line, false); + location.file_path=result.first/location.file_path; + + //Find match score + size_t score=0; + size_t pos=0; + for(auto &part: parts) { + auto find_pos=location.source.find(part, pos); + if(find_pos!=std::string::npos) { + pos=find_pos+1; + ++score; + } + } + if(score>best_score) { + best_score=score; + best_location=location; + } } - return data; + return best_location; } diff --git a/src/ctags.h b/src/ctags.h index 936c50a..062d9c7 100644 --- a/src/ctags.h +++ b/src/ctags.h @@ -7,17 +7,20 @@ class Ctags { public: - class Data { + class Location { public: - std::string path; + boost::filesystem::path file_path; unsigned long line; unsigned long index; std::string source; + operator bool() const { return !file_path.empty(); } }; - static std::unique_ptr get_result(const boost::filesystem::path &path, std::vector exclude_paths); + static std::pair > get_result(const boost::filesystem::path &path); - static Data parse_line(const std::string &line); + static Location parse_line(const std::string &line, bool markup); + + static Location get_location(const boost::filesystem::path &path, const std::string &name, const std::string &type); }; #endif //JUCI_CTAGS_H_ diff --git a/src/project.cc b/src/project.cc index a2c21b2..5d74dcf 100644 --- a/src/project.cc +++ b/src/project.cc @@ -307,71 +307,73 @@ void Project::Clang::debug_start() { auto debug_build_path=build->get_debug_path(); if(debug_build_path.empty() || !build->update_debug()) return; - auto project_path=build->project_path; + auto project_path=std::make_shared(build->project_path); - auto run_arguments_it=debug_run_arguments.find(project_path.string()); - std::string run_arguments; + auto run_arguments_it=debug_run_arguments.find(project_path->string()); + auto run_arguments=std::make_shared(); if(run_arguments_it!=debug_run_arguments.end()) - run_arguments=run_arguments_it->second; + *run_arguments=run_arguments_it->second; - if(run_arguments.empty()) { + if(run_arguments->empty()) { auto view=Notebook::get().get_current_view(); - run_arguments=build->get_executable(view?view->file_path:"").string(); - if(run_arguments.empty()) { + *run_arguments=build->get_executable(view?view->file_path:"").string(); + if(run_arguments->empty()) { Terminal::get().print("Warning: could not find executable.\n"); Terminal::get().print("Solution: either use Debug Set Run Arguments, or open a source file within a directory where add_executable is set.\n", true); return; } - size_t pos=run_arguments.find(project_path.string()); + size_t pos=run_arguments->find(project_path->string()); if(pos!=std::string::npos) - run_arguments.replace(pos, project_path.string().size(), debug_build_path.string()); - run_arguments=filesystem::escape_argument(run_arguments); - } - - auto breakpoints=std::make_shared > >(); - for(size_t c=0;cfile_path, project_path)) { - auto iter=view->get_buffer()->begin(); - if(view->get_source_buffer()->get_source_marks_at_iter(iter, "debug_breakpoint").size()>0) - breakpoints->emplace_back(view->file_path, iter.get_line()+1); - while(view->get_source_buffer()->forward_iter_to_source_mark(iter, "debug_breakpoint")) - breakpoints->emplace_back(view->file_path, iter.get_line()+1); - } + run_arguments->replace(pos, project_path->string().size(), debug_build_path.string()); + *run_arguments=filesystem::escape_argument(*run_arguments); } if(Config::get().project.clear_terminal_on_compile) Terminal::get().clear(); debugging=true; - Terminal::get().print("Compiling and debugging "+run_arguments+"\n"); - Terminal::get().async_process(Config::get().project.make_command, debug_build_path, [this, breakpoints, run_arguments, project_path](int exit_status){ + Terminal::get().print("Compiling and debugging "+*run_arguments+"\n"); + Terminal::get().async_process(Config::get().project.make_command, debug_build_path, [this, run_arguments, project_path](int exit_status){ if(exit_status!=EXIT_SUCCESS) debugging=false; else { - std::string remote_host; - auto options_it=debug_options.find(project_path.string()); - if(options_it!=debug_options.end() && options_it->second.remote_enabled.get_active()) - remote_host=options_it->second.remote_host.get_text(); - std::unique_lock lock(debug_start_mutex); - Debug::LLDB::get().start(run_arguments, project_path, *breakpoints, [this, run_arguments](int exit_status){ - debugging=false; - Terminal::get().async_print(run_arguments+" returned: "+std::to_string(exit_status)+'\n'); - }, [this](const std::string &status) { - dispatcher.post([this, status] { - debug_update_status(status); - }); - }, [this](const boost::filesystem::path &file_path, int line_nr, int line_index) { - dispatcher.post([this, file_path, line_nr, line_index] { - Project::debug_stop.first=file_path; - Project::debug_stop.second.first=line_nr-1; - Project::debug_stop.second.second=line_index-1; - - debug_update_stop(); - if(auto view=Notebook::get().get_current_view()) - view->get_buffer()->place_cursor(view->get_buffer()->get_insert()->get_iter()); - }); - }, remote_host); + dispatcher.post([this, run_arguments, project_path] { + std::vector > breakpoints; + for(size_t c=0;cfile_path, *project_path)) { + auto iter=view->get_buffer()->begin(); + if(view->get_source_buffer()->get_source_marks_at_iter(iter, "debug_breakpoint").size()>0) + breakpoints.emplace_back(view->file_path, iter.get_line()+1); + while(view->get_source_buffer()->forward_iter_to_source_mark(iter, "debug_breakpoint")) + breakpoints.emplace_back(view->file_path, iter.get_line()+1); + } + } + + std::string remote_host; + auto options_it=debug_options.find(project_path->string()); + if(options_it!=debug_options.end() && options_it->second.remote_enabled.get_active()) + remote_host=options_it->second.remote_host.get_text(); + std::unique_lock lock(debug_start_mutex); + Debug::LLDB::get().start(*run_arguments, *project_path, breakpoints, [this, run_arguments](int exit_status){ + debugging=false; + Terminal::get().async_print(*run_arguments+" returned: "+std::to_string(exit_status)+'\n'); + }, [this](const std::string &status) { + dispatcher.post([this, status] { + debug_update_status(status); + }); + }, [this](const boost::filesystem::path &file_path, int line_nr, int line_index) { + dispatcher.post([this, file_path, line_nr, line_index] { + Project::debug_stop.first=file_path; + Project::debug_stop.second.first=line_nr-1; + Project::debug_stop.second.second=line_index-1; + + debug_update_stop(); + if(auto view=Notebook::get().get_current_view()) + view->get_buffer()->place_cursor(view->get_buffer()->get_insert()->get_iter()); + }); + }, remote_host); + }); } }); } diff --git a/src/source_clang.cc b/src/source_clang.cc index 6afe67a..920630e 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -7,6 +7,7 @@ #endif #include "info.h" #include "dialogs.h" +#include "ctags.h" namespace sigc { #ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE @@ -992,6 +993,23 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file } } } + + //If no implementation was found, try using Ctags + auto name=identifier.cursor.get_spelling(); + auto parent=identifier.cursor.get_semantic_parent(); + while(parent && parent.get_kind()!=clang::CursorKind::TranslationUnit) { + auto spelling=parent.get_spelling()+"::"; + name.insert(0, spelling); + parent=parent.get_semantic_parent(); + } + auto ctags_location=Ctags::get_location(this->file_path, name, identifier.cursor.get_type()); + if(ctags_location) { + location.file_path=ctags_location.file_path; + location.line=ctags_location.line; + location.index=ctags_location.index; + return location; + } + Info::get().print("Could not find implementation"); } return location; diff --git a/src/window.cc b/src/window.cc index f5c638a..f900b2d 100644 --- a/src/window.cc +++ b/src/window.cc @@ -452,23 +452,11 @@ void Window::set_menu_actions() { menu.add_action("source_find_symbol_ctags", [this]() { if(auto view=Notebook::get().get_current_view()) { - auto build=Project::Build::create(view->file_path); - auto run_path=std::make_shared(build->project_path); - std::vector exclude_paths; - if(!run_path->empty()) { - exclude_paths.emplace_back(filesystem::get_relative_path(build->get_default_path(), build->project_path)); - exclude_paths.emplace_back(filesystem::get_relative_path(build->get_debug_path(), build->project_path)); - } - else { - if(!Directories::get().path.empty()) - *run_path=Directories::get().path; - else - *run_path=view->file_path.parent_path(); - } - auto stream=Ctags::get_result(*run_path, exclude_paths); + auto pair=Ctags::get_result(view->file_path.parent_path()); + auto path=std::move(pair.first); + auto stream=std::move(pair.second); stream->seekg(0, std::ios::end); - auto length=stream->tellg(); - if(length==0) + if(stream->tellg()==0) return; stream->seekg(0, std::ios::beg); @@ -478,20 +466,20 @@ void Window::set_menu_actions() { std::string line; while(std::getline(*stream, line)) { - auto data=Ctags::parse_line(line); + auto location=Ctags::parse_line(line, true); - std::string row=data.path+":"+std::to_string(data.line+1)+": "+data.source; - (*rows)[row]=Source::Offset(data.line, data.index, data.path); + std::string row=location.file_path.string()+":"+std::to_string(location.line+1)+": "+location.source; + (*rows)[row]=Source::Offset(location.line, location.index, location.file_path); view->selection_dialog->add_row(row); } if(rows->size()==0) return; - view->selection_dialog->on_select=[this, rows, run_path](const std::string &selected, bool hide_window) { + view->selection_dialog->on_select=[this, rows, path](const std::string &selected, bool hide_window) { auto offset=rows->at(selected); boost::filesystem::path declaration_file; boost::system::error_code ec; - declaration_file=boost::filesystem::canonical(*run_path/offset.file_path, ec); + declaration_file=boost::filesystem::canonical(path/offset.file_path, ec); if(ec) return; Notebook::get().open(declaration_file);