From b6e455fc0a2eedd7c813cd281e062c3f6523c5f4 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sat, 2 Jul 2016 19:31:55 +0200 Subject: [PATCH] Ctags cleanup --- README.md | 2 +- src/CMakeLists.txt | 1 + src/ctags.cc | 84 +++++++++++++++++++++++++++++ src/ctags.h | 23 ++++++++ src/window.cc | 132 +++++++++++++-------------------------------- 5 files changed, 146 insertions(+), 96 deletions(-) create mode 100644 src/ctags.cc create mode 100644 src/ctags.h diff --git a/README.md b/README.md index b482dbb..b4d7516 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ towards libclang with speed and ease of use in mind. * Highlighting of similar types (C++) * Automated documentation search (C++) * Go to declaration, implementation, methods and usages (C++) -* Find symbol through ctags +* Find symbol through Ctags * Spell checking depending on file context * Run shell commands within JuCi++ * Regex search and replace diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fb04df6..78e83a7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -40,6 +40,7 @@ set(global_libraries ${global_libraries} set(project_files config.cc + ctags.cc dialogs.cc dialogs_unix.cc directories.cc diff --git a/src/ctags.cc b/src/ctags.cc new file mode 100644 index 0000000..de5ac9f --- /dev/null +++ b/src/ctags.cc @@ -0,0 +1,84 @@ +#include "ctags.h" +#include "config.h" +#include "terminal.h" + +//Temporary fix for current Arch Linux boost linking problem +#ifdef __GNUC_PREREQ +#if __GNUC_PREREQ(5,1) +#include +#define REGEX_NS std +#endif +#endif +#ifndef REGEX_NS +#include +#define REGEX_NS boost +#endif + +std::unique_ptr Ctags::get_result(const boost::filesystem::path &path, std::vector exclude_paths) { + std::string exclude; + for(auto &path: exclude_paths) { + if(!path.empty()) + exclude+=" --exclude="+path.string(); + } + + 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; +} + +Ctags::Data Ctags::parse_line(const std::string &line) { + Data data; + + const static REGEX_NS::regex regex("^([^\t]+)\t([^\t]+)\t(?:/\\^)?([ \t]*)(.+);\"\tline:([0-9]+)$"); + 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(); + 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; + } + + size_t pos=data.source.find(symbol); + if(pos!=std::string::npos) + data.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(); + } + } + 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)+""; + } + } + else { + Terminal::get().print("Warning (ctags): please report to the juCi++ project that the following line was not parsed:\n"+ + line+'\n', true); + } + + return data; +} diff --git a/src/ctags.h b/src/ctags.h new file mode 100644 index 0000000..936c50a --- /dev/null +++ b/src/ctags.h @@ -0,0 +1,23 @@ +#ifndef JUCI_CTAGS_H_ +#define JUCI_CTAGS_H_ +#include +#include +#include +#include + +class Ctags { +public: + class Data { + public: + std::string path; + unsigned long line; + unsigned long index; + std::string source; + }; + + static std::unique_ptr get_result(const boost::filesystem::path &path, std::vector exclude_paths); + + static Data parse_line(const std::string &line); +}; + +#endif //JUCI_CTAGS_H_ diff --git a/src/window.cc b/src/window.cc index 9a64f49..f5c638a 100644 --- a/src/window.cc +++ b/src/window.cc @@ -8,6 +8,7 @@ #include "project.h" #include "entrybox.h" #include "info.h" +#include "ctags.h" namespace sigc { #ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE @@ -453,14 +454,10 @@ void Window::set_menu_actions() { 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::string exclude; + std::vector exclude_paths; if(!run_path->empty()) { - auto relative_path=filesystem::get_relative_path(build->get_default_path(), build->project_path); - if(!relative_path.empty()) - exclude+=" --exclude="+relative_path.string(); - relative_path=filesystem::get_relative_path(build->get_debug_path(), build->project_path); - if(!relative_path.empty()) - exclude+=" --exclude="+relative_path.string(); + 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()) @@ -468,98 +465,43 @@ void Window::set_menu_actions() { else *run_path=view->file_path.parent_path(); } - std::stringstream stdin_stream, stdout_stream; - auto command=Config::get().project.ctags_command+exclude+" --fields=n --sort=foldcase -f- -R *"; - auto exit_status=Terminal::get().process(stdin_stream, stdout_stream, command, *run_path); - if(exit_status==0) { - auto dialog_iter=view->get_iter_for_dialog(); - view->selection_dialog=std::unique_ptr(new SelectionDialog(*view, view->get_buffer()->create_mark(dialog_iter), true, true)); - auto rows=std::make_shared >(); + auto stream=Ctags::get_result(*run_path, exclude_paths); + stream->seekg(0, std::ios::end); + auto length=stream->tellg(); + if(length==0) + return; + stream->seekg(0, std::ios::beg); + + auto dialog_iter=view->get_iter_for_dialog(); + view->selection_dialog=std::unique_ptr(new SelectionDialog(*view, view->get_buffer()->create_mark(dialog_iter), true, true)); + auto rows=std::make_shared >(); - std::string line; - while(std::getline(stdout_stream, line)) { - std::string symbol, file, source_line; - unsigned long line_nr; - size_t last_pos=-1, pos; - - pos=line.find("\t", last_pos+1); - if(pos==std::string::npos || last_pos+1>=line.size()) { - Terminal::get().print("Warning (ctags): failed to parse symbol\n", true); - continue; - } - symbol=line.substr(last_pos+1, pos-last_pos-1); - last_pos=pos; - - pos=line.find("\t", last_pos+1); - if(pos==std::string::npos || last_pos+1>=line.size()) { - Terminal::get().print("Warning (ctags): failed to parse file\n", true); - continue; - } - file=line.substr(last_pos+1, pos-last_pos-1); - last_pos=pos; - - pos=line.find("/;\"\t", last_pos+1); - if(pos==std::string::npos || last_pos+3>=line.size()) { - //Skipping defines - continue; - } - pos+=3; - source_line=line.substr(last_pos+3, pos-last_pos-7); - size_t line_index=0; - for(;line_index0) - source_line=source_line.substr(line_index); - size_t line_index_add=source_line.find(symbol); - if(line_index_add!=std::string::npos) - line_index+=line_index_add; - - source_line=Glib::Markup::escape_text(source_line); - size_t bold_pos=-1; - while((bold_pos=source_line.find(symbol, bold_pos+1))!=std::string::npos) { - source_line.insert(bold_pos+symbol.size(), ""); - source_line.insert(bold_pos, ""); - bold_pos+=7+symbol.size(); - } - last_pos=pos; - - if(last_pos+6>=line.size()) { - Terminal::get().print("Warning (ctags): failed to parse line number\n", true); - continue; - } - try { - line_nr=std::stoul(line.substr(last_pos+6, line.size()-last_pos-6)); - } - catch(const std::exception &) { - Terminal::get().print("Warning (ctags): failed to parse line number\n", true); - continue; - } - - std::string row=file+":"+std::to_string(line_nr)+": "+source_line; - (*rows)[row]=Source::Offset(line_nr-1, line_index, file); - view->selection_dialog->add_row(row); - } + std::string line; + while(std::getline(*stream, line)) { + auto data=Ctags::parse_line(line); + + std::string row=data.path+":"+std::to_string(data.line+1)+": "+data.source; + (*rows)[row]=Source::Offset(data.line, data.index, data.path); + view->selection_dialog->add_row(row); + } - if(rows->size()==0) + if(rows->size()==0) + return; + view->selection_dialog->on_select=[this, rows, run_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); + if(ec) return; - view->selection_dialog->on_select=[this, rows, run_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); - if(ec) - return; - Notebook::get().open(declaration_file); - auto view=Notebook::get().get_current_view(); - view->place_cursor_at_line_index(offset.line, offset.index); - view->scroll_to_cursor_delayed(view, true, false); - view->hide_tooltips(); - }; + Notebook::get().open(declaration_file); + auto view=Notebook::get().get_current_view(); + view->place_cursor_at_line_index(offset.line, offset.index); + view->scroll_to_cursor_delayed(view, true, false); view->hide_tooltips(); - view->selection_dialog->show(); - } + }; + view->hide_tooltips(); + view->selection_dialog->show(); } });