Browse Source

Added Ctags fallback to Go to Implementation, and can now set breakpoints when compile is in progress

merge-requests/365/head
eidheim 10 years ago
parent
commit
311f459650
  1. 2
      src/CMakeLists.txt
  2. 153
      src/ctags.cc
  3. 11
      src/ctags.h
  4. 54
      src/project.cc
  5. 18
      src/source_clang.cc
  6. 30
      src/window.cc

2
src/CMakeLists.txt

@ -40,7 +40,6 @@ set(global_libraries ${global_libraries}
set(project_files set(project_files
config.cc config.cc
ctags.cc
dialogs.cc dialogs.cc
dialogs_unix.cc dialogs_unix.cc
directories.cc directories.cc
@ -59,6 +58,7 @@ set(project_files
#Files used both in ../src and ../tests #Files used both in ../src and ../tests
set(project_shared_files set(project_shared_files
cmake.cc cmake.cc
ctags.cc
dispatcher.cc dispatcher.cc
filesystem.cc filesystem.cc
git.cc git.cc

153
src/ctags.cc

@ -1,6 +1,11 @@
#include "ctags.h" #include "ctags.h"
#include "config.h" #include "config.h"
#include "terminal.h" #include "terminal.h"
#include "project_build.h"
#include "filesystem.h"
#include "directories.h"
#include <iostream>
#include <vector>
//Temporary fix for current Arch Linux boost linking problem //Temporary fix for current Arch Linux boost linking problem
#ifdef __GNUC_PREREQ #ifdef __GNUC_PREREQ
@ -14,71 +19,149 @@
#define REGEX_NS boost #define REGEX_NS boost
#endif #endif
std::unique_ptr<std::stringstream> Ctags::get_result(const boost::filesystem::path &path, std::vector<boost::filesystem::path> exclude_paths) { std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream> > Ctags::get_result(const boost::filesystem::path &path) {
auto build=Project::Build::create(path);
auto run_path=build->project_path;
std::string exclude; 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()) if(!path.empty())
exclude+=" --exclude="+path.string(); 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; std::stringstream stdin_stream;
//TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below //TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below
std::unique_ptr<std::stringstream> stdout_stream(new std::stringstream()); std::unique_ptr<std::stringstream> stdout_stream(new std::stringstream());
auto command=Config::get().project.ctags_command+exclude+" --fields=n --sort=foldcase -f- -R *"; 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, path); Terminal::get().process(stdin_stream, *stdout_stream, command, run_path);
return stdout_stream; return {run_path, std::move(stdout_stream)};
} }
Ctags::Data Ctags::parse_line(const std::string &line) { Ctags::Location Ctags::parse_line(const std::string &line, bool markup) {
Data data; 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; REGEX_NS::smatch sm;
if(REGEX_NS::regex_match(line, sm, regex)) { if(REGEX_NS::regex_match(line, sm, regex)) {
data.source=sm[4].str(); location.source=sm[4].str();
if(data.source.size()>1 && data.source[data.source.size()-1]=='/' && data.source[data.source.size()-2]!='\\') { size_t pos=location.source.find(";\"\tline:");
data.source.pop_back(); if(pos==std::string::npos)
if(data.source.size()>1 && data.source[data.source.size()-1]=='$' && data.source[data.source.size()-2]!='\\') return location;
data.source.pop_back();
auto symbol=sm[1].str();
data.path=sm[2].str();
data.index=sm[3].str().size();
try { try {
data.line=std::stoul(sm[5].str())-1; location.line=std::stoul(location.source.substr(pos+8))-1;
} }
catch(const std::exception&) { catch(const std::exception&) {
data.line=0; 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();
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) if(pos!=std::string::npos)
data.index+=pos; location.index+=pos;
data.source=Glib::Markup::escape_text(data.source); if(markup) {
location.source=Glib::Markup::escape_text(location.source);
pos=-1; pos=-1;
while((pos=data.source.find(symbol, pos+1))!=std::string::npos) { while((pos=location.source.find(symbol, pos+1))!=std::string::npos) {
data.source.insert(pos+symbol.size(), "</b>"); location.source.insert(pos+symbol.size(), "</b>");
data.source.insert(pos, "<b>"); location.source.insert(pos, "<b>");
pos+=7+symbol.size(); pos+=7+symbol.size();
} }
} }
}
else { else {
data.path=sm[2].str(); location.file_path=sm[2].str();
data.index=0; location.index=0;
data.source=sm[1].str(); location.source=sm[1].str();
try { if(markup)
data.line=std::stoul(sm[3].str())-1; location.source="<b>"+Glib::Markup::escape_text(location.source)+"</b>";
} }
catch(const std::exception&) {
data.line=0;
} }
data.source="<b>"+Glib::Markup::escape_text(data.source)+"</b>"; 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<type.size();++c) {
if(type[c]=='<')
++bracket_count;
else if(type[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<std::string> parts;
size_t text_start=-1;
for(size_t c=0;c<full_type.size();++c) {
auto &chr=full_type[c];
if((chr>='0' && chr<='9') || (chr>='a' && chr<='z') || (chr>='A' && chr<='Z') || chr=='_' || chr=='~') {
if(text_start==static_cast<size_t>(-1))
text_start=c;
} }
else { else {
Terminal::get().print("Warning (ctags): please report to the juCi++ project that the following line was not parsed:\n"+ if(text_start!=static_cast<size_t>(-1))
line+'\n', true); 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;
} }

11
src/ctags.h

@ -7,17 +7,20 @@
class Ctags { class Ctags {
public: public:
class Data { class Location {
public: public:
std::string path; boost::filesystem::path file_path;
unsigned long line; unsigned long line;
unsigned long index; unsigned long index;
std::string source; std::string source;
operator bool() const { return !file_path.empty(); }
}; };
static std::unique_ptr<std::stringstream> get_result(const boost::filesystem::path &path, std::vector<boost::filesystem::path> exclude_paths); static std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream> > 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_ #endif //JUCI_CTAGS_H_

54
src/project.cc

@ -307,56 +307,57 @@ void Project::Clang::debug_start() {
auto debug_build_path=build->get_debug_path(); auto debug_build_path=build->get_debug_path();
if(debug_build_path.empty() || !build->update_debug()) if(debug_build_path.empty() || !build->update_debug())
return; return;
auto project_path=build->project_path; auto project_path=std::make_shared<boost::filesystem::path>(build->project_path);
auto run_arguments_it=debug_run_arguments.find(project_path.string()); auto run_arguments_it=debug_run_arguments.find(project_path->string());
std::string run_arguments; auto run_arguments=std::make_shared<std::string>();
if(run_arguments_it!=debug_run_arguments.end()) 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(); auto view=Notebook::get().get_current_view();
run_arguments=build->get_executable(view?view->file_path:"").string(); *run_arguments=build->get_executable(view?view->file_path:"").string();
if(run_arguments.empty()) { if(run_arguments->empty()) {
Terminal::get().print("Warning: could not find executable.\n"); 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); 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; return;
} }
size_t pos=run_arguments.find(project_path.string()); size_t pos=run_arguments->find(project_path->string());
if(pos!=std::string::npos) if(pos!=std::string::npos)
run_arguments.replace(pos, project_path.string().size(), debug_build_path.string()); run_arguments->replace(pos, project_path->string().size(), debug_build_path.string());
run_arguments=filesystem::escape_argument(run_arguments); *run_arguments=filesystem::escape_argument(*run_arguments);
} }
auto breakpoints=std::make_shared<std::vector<std::pair<boost::filesystem::path, int> > >(); 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, run_arguments, project_path](int exit_status){
if(exit_status!=EXIT_SUCCESS)
debugging=false;
else {
dispatcher.post([this, run_arguments, project_path] {
std::vector<std::pair<boost::filesystem::path, int> > breakpoints;
for(size_t c=0;c<Notebook::get().size();c++) { for(size_t c=0;c<Notebook::get().size();c++) {
auto view=Notebook::get().get_view(c); auto view=Notebook::get().get_view(c);
if(filesystem::file_in_path(view->file_path, project_path)) { if(filesystem::file_in_path(view->file_path, *project_path)) {
auto iter=view->get_buffer()->begin(); auto iter=view->get_buffer()->begin();
if(view->get_source_buffer()->get_source_marks_at_iter(iter, "debug_breakpoint").size()>0) 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); breakpoints.emplace_back(view->file_path, iter.get_line()+1);
while(view->get_source_buffer()->forward_iter_to_source_mark(iter, "debug_breakpoint")) while(view->get_source_buffer()->forward_iter_to_source_mark(iter, "debug_breakpoint"))
breakpoints->emplace_back(view->file_path, iter.get_line()+1); breakpoints.emplace_back(view->file_path, iter.get_line()+1);
} }
} }
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){
if(exit_status!=EXIT_SUCCESS)
debugging=false;
else {
std::string remote_host; std::string remote_host;
auto options_it=debug_options.find(project_path.string()); auto options_it=debug_options.find(project_path->string());
if(options_it!=debug_options.end() && options_it->second.remote_enabled.get_active()) if(options_it!=debug_options.end() && options_it->second.remote_enabled.get_active())
remote_host=options_it->second.remote_host.get_text(); remote_host=options_it->second.remote_host.get_text();
std::unique_lock<std::mutex> lock(debug_start_mutex); std::unique_lock<std::mutex> lock(debug_start_mutex);
Debug::LLDB::get().start(run_arguments, project_path, *breakpoints, [this, run_arguments](int exit_status){ Debug::LLDB::get().start(*run_arguments, *project_path, breakpoints, [this, run_arguments](int exit_status){
debugging=false; debugging=false;
Terminal::get().async_print(run_arguments+" returned: "+std::to_string(exit_status)+'\n'); Terminal::get().async_print(*run_arguments+" returned: "+std::to_string(exit_status)+'\n');
}, [this](const std::string &status) { }, [this](const std::string &status) {
dispatcher.post([this, status] { dispatcher.post([this, status] {
debug_update_status(status); debug_update_status(status);
@ -372,6 +373,7 @@ void Project::Clang::debug_start() {
view->get_buffer()->place_cursor(view->get_buffer()->get_insert()->get_iter()); view->get_buffer()->place_cursor(view->get_buffer()->get_insert()->get_iter());
}); });
}, remote_host); }, remote_host);
});
} }
}); });
} }

18
src/source_clang.cc

@ -7,6 +7,7 @@
#endif #endif
#include "info.h" #include "info.h"
#include "dialogs.h" #include "dialogs.h"
#include "ctags.h"
namespace sigc { namespace sigc {
#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE #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"); Info::get().print("Could not find implementation");
} }
return location; return location;

30
src/window.cc

@ -452,23 +452,11 @@ void Window::set_menu_actions() {
menu.add_action("source_find_symbol_ctags", [this]() { menu.add_action("source_find_symbol_ctags", [this]() {
if(auto view=Notebook::get().get_current_view()) { if(auto view=Notebook::get().get_current_view()) {
auto build=Project::Build::create(view->file_path); auto pair=Ctags::get_result(view->file_path.parent_path());
auto run_path=std::make_shared<boost::filesystem::path>(build->project_path); auto path=std::move(pair.first);
std::vector<boost::filesystem::path> exclude_paths; auto stream=std::move(pair.second);
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);
stream->seekg(0, std::ios::end); stream->seekg(0, std::ios::end);
auto length=stream->tellg(); if(stream->tellg()==0)
if(length==0)
return; return;
stream->seekg(0, std::ios::beg); stream->seekg(0, std::ios::beg);
@ -478,20 +466,20 @@ void Window::set_menu_actions() {
std::string line; std::string line;
while(std::getline(*stream, 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; std::string row=location.file_path.string()+":"+std::to_string(location.line+1)+": "+location.source;
(*rows)[row]=Source::Offset(data.line, data.index, data.path); (*rows)[row]=Source::Offset(location.line, location.index, location.file_path);
view->selection_dialog->add_row(row); view->selection_dialog->add_row(row);
} }
if(rows->size()==0) if(rows->size()==0)
return; 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); auto offset=rows->at(selected);
boost::filesystem::path declaration_file; boost::filesystem::path declaration_file;
boost::system::error_code ec; 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) if(ec)
return; return;
Notebook::get().open(declaration_file); Notebook::get().open(declaration_file);

Loading…
Cancel
Save