Browse Source

Improvement of primary-right click: now goes to declaration if no implementation was found (related to #317)

merge-requests/365/head
eidheim 9 years ago
parent
commit
30e0eace01
  1. 9
      src/source.cc
  2. 2
      src/source.h
  3. 238
      src/source_clang.cc
  4. 108
      src/window.cc

9
src/source.cc

@ -2246,13 +2246,8 @@ bool Source::View::on_button_press_event(GdkEventButton *event) {
if(iter) if(iter)
get_buffer()->place_cursor(iter); get_buffer()->place_cursor(iter);
if(is_implementation_location) { Menu::get().actions["source_goto_declaration_or_implementation"]->activate();
if(is_implementation_location()) return true;
Menu::get().actions["source_goto_declaration"]->activate();
else
Menu::get().actions["source_goto_implementation"]->activate();
return true;
}
} }
} }

2
src/source.h

@ -68,8 +68,8 @@ namespace Source {
std::function<void()> non_interactive_completion; std::function<void()> non_interactive_completion;
std::function<void()> format_style; std::function<void()> format_style;
std::function<Offset()> get_declaration_location; std::function<Offset()> get_declaration_location;
std::function<bool()> is_implementation_location;
std::function<std::vector<Offset>(const std::vector<Source::View*> &views)> get_implementation_locations; std::function<std::vector<Offset>(const std::vector<Source::View*> &views)> get_implementation_locations;
std::function<std::vector<Offset>(const std::vector<Source::View*> &views)> get_declaration_or_implementation_locations;
std::function<std::vector<std::pair<Offset, std::string> >(const std::vector<Source::View*> &views)> get_usages; std::function<std::vector<std::pair<Offset, std::string> >(const std::vector<Source::View*> &views)> get_usages;
std::function<std::string()> get_method; std::function<std::string()> get_method;
std::function<std::vector<std::pair<Offset, std::string> >()> get_methods; std::function<std::vector<std::pair<Offset, std::string> >()> get_methods;

238
src/source_clang.cc

@ -1012,64 +1012,7 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
} }
}); });
auto get_header_location=[this]() { auto declaration_location=[this]() {
// If cursor is at an include line, return offset to included file
const static std::regex include_regex("^[ \t]*#[ \t]*include[ \t]*[<\"]([^<>\"]+)[>\"].*$");
std::smatch sm;
auto line=get_line();
if(std::regex_match(line, sm, include_regex)) {
struct ClientData {
boost::filesystem::path &file_path;
std::string found_include;
int line_nr;
std::string sm_str;
};
ClientData client_data{this->file_path, std::string(), get_buffer()->get_insert()->get_iter().get_line(), sm[1].str()};
// Attempt to find the 100% correct include file first
clang_getInclusions(clang_tu->cx_tu, [](CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data_) {
auto client_data=static_cast<ClientData*>(client_data_);
if(client_data->found_include.empty() && include_len>0) {
auto source_location=clang::SourceLocation(inclusion_stack[0]);
if(static_cast<int>(source_location.get_offset().line)-1==client_data->line_nr &&
filesystem::get_normal_path(source_location.get_path())==client_data->file_path)
client_data->found_include=clang::to_string(clang_getFileName(included_file));
}
}, &client_data);
if(!client_data.found_include.empty())
return Offset(0, 0, client_data.found_include);
// Find a matching include file if no include was found previously
clang_getInclusions(clang_tu->cx_tu, [](CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data_) {
auto client_data=static_cast<ClientData*>(client_data_);
if(client_data->found_include.empty()) {
for(unsigned c=1;c<include_len;++c) {
auto source_location=clang::SourceLocation(inclusion_stack[c]);
if(static_cast<int>(source_location.get_offset().line)-1<=client_data->line_nr &&
filesystem::get_normal_path(source_location.get_path())==client_data->file_path) {
auto included_file_str=clang::to_string(clang_getFileName(included_file));
if(included_file_str.size()>=client_data->sm_str.size() &&
included_file_str.compare(included_file_str.size()-client_data->sm_str.size(), client_data->sm_str.size(), client_data->sm_str)==0) {
client_data->found_include=included_file_str;
break;
}
}
}
}
}, &client_data);
if(!client_data.found_include.empty())
return Offset(0, 0, client_data.found_include);
}
return Offset();
};
get_declaration_location=[this, get_header_location](){
if(!parsed) {
Info::get().print("Buffer is parsing");
return Offset();
}
auto identifier=get_identifier(); auto identifier=get_identifier();
if(identifier) { if(identifier) {
auto source_location=identifier.cursor.get_canonical().get_source_location(); auto source_location=identifier.cursor.get_canonical().get_source_location();
@ -1077,35 +1020,72 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
return Offset(offset.line-1, offset.index-1, source_location.get_path()); return Offset(offset.line-1, offset.index-1, source_location.get_path());
} }
else { else {
auto location=get_header_location(); // If cursor is at an include line, return offset to included file
if(location) const static std::regex include_regex("^[ \t]*#[ \t]*include[ \t]*[<\"]([^<>\"]+)[>\"].*$");
return location; std::smatch sm;
} auto line=get_line();
Info::get().print("No declaration found"); if(std::regex_match(line, sm, include_regex)) {
return Offset(); struct ClientData {
}; boost::filesystem::path &file_path;
std::string found_include;
int line_nr;
std::string sm_str;
};
ClientData client_data{this->file_path, std::string(), get_buffer()->get_insert()->get_iter().get_line(), sm[1].str()};
// Attempt to find the 100% correct include file first
clang_getInclusions(clang_tu->cx_tu, [](CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data_) {
auto client_data=static_cast<ClientData*>(client_data_);
if(client_data->found_include.empty() && include_len>0) {
auto source_location=clang::SourceLocation(inclusion_stack[0]);
if(static_cast<int>(source_location.get_offset().line)-1==client_data->line_nr &&
filesystem::get_normal_path(source_location.get_path())==client_data->file_path)
client_data->found_include=clang::to_string(clang_getFileName(included_file));
}
}, &client_data);
if(!client_data.found_include.empty())
return Offset(0, 0, client_data.found_include);
// Find a matching include file if no include was found previously
clang_getInclusions(clang_tu->cx_tu, [](CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data_) {
auto client_data=static_cast<ClientData*>(client_data_);
if(client_data->found_include.empty()) {
for(unsigned c=1;c<include_len;++c) {
auto source_location=clang::SourceLocation(inclusion_stack[c]);
if(static_cast<int>(source_location.get_offset().line)-1<=client_data->line_nr &&
filesystem::get_normal_path(source_location.get_path())==client_data->file_path) {
auto included_file_str=clang::to_string(clang_getFileName(included_file));
if(included_file_str.size()>=client_data->sm_str.size() &&
included_file_str.compare(included_file_str.size()-client_data->sm_str.size(), client_data->sm_str.size(), client_data->sm_str)==0) {
client_data->found_include=included_file_str;
break;
}
}
}
}
}, &client_data);
is_implementation_location=[this]() { if(!client_data.found_include.empty())
if(!parsed) return Offset(0, 0, client_data.found_include);
return false;
auto iter=get_buffer()->get_insert()->get_iter();
auto line=static_cast<unsigned>(iter.get_line());
auto index=static_cast<unsigned>(iter.get_line_index());
for(auto &token: *clang_tokens) {
if(token.is_identifier()) {
if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1)
return clang_isCursorDefinition(token.get_cursor().cx_cursor)>0;
} }
} }
return false; return Offset();
}; };
get_implementation_locations=[this, get_header_location](const std::vector<Source::View*> &views){ get_declaration_location=[this, declaration_location](){
std::vector<Offset> locations;
if(!parsed) { if(!parsed) {
Info::get().print("Buffer is parsing"); Info::get().print("Buffer is parsing");
return locations; return Offset();
} }
auto offset=declaration_location();
if(!offset)
Info::get().print("No declaration found");
return offset;
};
auto implementation_locations=[this](const std::vector<Source::View*> &views) {
std::vector<Offset> offsets;
auto identifier=get_identifier(); auto identifier=get_identifier();
if(identifier) { if(identifier) {
wait_parsing(views); wait_parsing(views);
@ -1122,32 +1102,32 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
if(referenced && identifier.kind==referenced.get_kind() && if(referenced && identifier.kind==referenced.get_kind() &&
identifier.spelling==token.get_spelling() && identifier.usr==referenced.get_usr()) { identifier.spelling==token.get_spelling() && identifier.usr==referenced.get_usr()) {
if(clang_isCursorDefinition(referenced.cx_cursor)) { if(clang_isCursorDefinition(referenced.cx_cursor)) {
Offset location; Offset offset;
location.file_path=cursor.get_source_location().get_path(); offset.file_path=cursor.get_source_location().get_path();
auto clang_offset=cursor.get_source_location().get_offset(); auto clang_offset=cursor.get_source_location().get_offset();
location.line=clang_offset.line-1; offset.line=clang_offset.line-1;
location.index=clang_offset.index-1; offset.index=clang_offset.index-1;
locations.emplace_back(location); offsets.emplace_back(offset);
} }
} }
} }
} }
} }
} }
if(!locations.empty()) if(!offsets.empty())
return locations; return offsets;
//If no implementation was found, try using clang_getCursorDefinition //If no implementation was found, try using clang_getCursorDefinition
auto definition=identifier.cursor.get_definition(); auto definition=identifier.cursor.get_definition();
if(definition) { if(definition) {
auto definition_location=definition.get_source_location(); auto definition_location=definition.get_source_location();
Offset location; Offset offset;
location.file_path=definition_location.get_path(); offset.file_path=definition_location.get_path();
auto offset=definition_location.get_offset(); auto definition_offset=definition_location.get_offset();
location.line=offset.line-1; offset.line=definition_offset.line-1;
location.index=offset.index-1; offset.index=definition_offset.index-1;
locations.emplace_back(location); offsets.emplace_back(offset);
return locations; return offsets;
} }
//If no implementation was found, try using Ctags //If no implementation was found, try using Ctags
@ -1161,24 +1141,72 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
auto ctags_locations=Ctags::get_locations(this->file_path, name, identifier.cursor.get_type_description()); auto ctags_locations=Ctags::get_locations(this->file_path, name, identifier.cursor.get_type_description());
if(!ctags_locations.empty()) { if(!ctags_locations.empty()) {
for(auto &ctags_location: ctags_locations) { for(auto &ctags_location: ctags_locations) {
Offset location; Offset offset;
location.file_path=ctags_location.file_path; offset.file_path=ctags_location.file_path;
location.line=ctags_location.line; offset.line=ctags_location.line;
location.index=ctags_location.index; offset.index=ctags_location.index;
locations.emplace_back(location); offsets.emplace_back(offset);
} }
return locations; return offsets;
} }
} }
return offsets;
};
get_implementation_locations=[this, implementation_locations](const std::vector<Source::View*> &views){
if(!parsed) {
Info::get().print("Buffer is parsing");
return std::vector<Offset>();
}
auto offsets=implementation_locations(views);
if(offsets.empty())
Info::get().print("No implementation found");
return offsets;
};
get_declaration_or_implementation_locations=[this, declaration_location, implementation_locations](const std::vector<Source::View*> &views) {
if(!parsed) {
Info::get().print("Buffer is parsing");
return std::vector<Offset>();
}
std::vector<Offset> offsets;
bool is_implementation=false;
auto iter=get_buffer()->get_insert()->get_iter();
auto line=static_cast<unsigned>(iter.get_line());
auto index=static_cast<unsigned>(iter.get_line_index());
for(auto &token: *clang_tokens) {
if(token.is_identifier()) {
if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index<=token.offsets.second.index-1) {
if(clang_isCursorDefinition(token.get_cursor().cx_cursor)>0)
is_implementation=true;
break;
}
}
}
// If cursor is at implementation, return declaration_location
if(is_implementation) {
auto offset=declaration_location();
if(offset)
offsets.emplace_back(offset);
}
else { else {
auto location=get_header_location(); auto implementation_offsets=implementation_locations(views);
if(location) { if(!implementation_offsets.empty()) {
locations.emplace_back(location); offsets=std::move(implementation_offsets);
return locations; }
else {
auto offset=declaration_location();
if(offset)
offsets.emplace_back(offset);
} }
} }
Info::get().print("No implementation found");
return locations; if(offsets.empty())
Info::get().print("No declaration or implementation found");
return offsets;
}; };
get_usages=[this](const std::vector<Source::View*> &views) { get_usages=[this](const std::vector<Source::View*> &views) {

108
src/window.cc

@ -741,58 +741,65 @@ void Window::set_menu_actions() {
} }
} }
}); });
menu.add_action("source_goto_implementation", [this]() { auto goto_selected_location=[](Source::View *view, const std::vector<Source::Offset> &locations) {
if(auto view=Notebook::get().get_current_view()) { if(!locations.empty()) {
if(view->get_implementation_locations) { auto dialog_iter=view->get_iter_for_dialog();
auto locations=view->get_implementation_locations(Notebook::get().get_views()); SelectionDialog::create(view, view->get_buffer()->create_mark(dialog_iter), true, true);
if(!locations.empty()) { auto rows=std::make_shared<std::unordered_map<std::string, Source::Offset> >();
auto dialog_iter=view->get_iter_for_dialog(); auto project_path=Project::Build::create(view->file_path)->project_path;
SelectionDialog::create(view, view->get_buffer()->create_mark(dialog_iter), true, true); if(project_path.empty()) {
auto rows=std::make_shared<std::unordered_map<std::string, Source::Offset> >(); if(!Directories::get().path.empty())
auto project_path=Project::Build::create(view->file_path)->project_path; project_path=Directories::get().path;
if(project_path.empty()) { else
if(!Directories::get().path.empty()) project_path=view->file_path.parent_path();
project_path=Directories::get().path; }
else for(auto &location: locations) {
project_path=view->file_path.parent_path(); auto path=filesystem::get_relative_path(filesystem::get_normal_path(location.file_path), project_path);
} if(path.empty())
for(auto &location: locations) { path=location.file_path.filename();
auto path=filesystem::get_relative_path(filesystem::get_normal_path(location.file_path), project_path); auto row=path.string()+":"+std::to_string(location.line+1);
if(path.empty()) (*rows)[row]=location;
path=location.file_path.filename(); SelectionDialog::get()->add_row(row);
auto row=path.string()+":"+std::to_string(location.line+1); }
(*rows)[row]=location;
SelectionDialog::get()->add_row(row);
}
if(rows->size()==0) if(rows->size()==0)
return; return;
else if(rows->size()==1) { else if(rows->size()==1) {
auto location=*rows->begin(); auto location=*rows->begin();
if(!boost::filesystem::is_regular_file(location.second.file_path)) if(!boost::filesystem::is_regular_file(location.second.file_path))
return; return;
Notebook::get().open(location.second.file_path); Notebook::get().open(location.second.file_path);
auto view=Notebook::get().get_current_view(); auto view=Notebook::get().get_current_view();
auto line=static_cast<int>(location.second.line); auto line=static_cast<int>(location.second.line);
auto index=static_cast<int>(location.second.index); auto index=static_cast<int>(location.second.index);
view->place_cursor_at_line_index(line, index); view->place_cursor_at_line_index(line, index);
view->scroll_to_cursor_delayed(view, true, false); view->scroll_to_cursor_delayed(view, true, false);
return; return;
}
SelectionDialog::get()->on_select=[this, rows](const std::string &selected, bool hide_window) {
auto location=rows->at(selected);
if(!boost::filesystem::is_regular_file(location.file_path))
return;
Notebook::get().open(location.file_path);
auto view=Notebook::get().get_current_view();
view->place_cursor_at_line_index(location.line, location.index);
view->scroll_to_cursor_delayed(view, true, false);
view->hide_tooltips();
};
view->hide_tooltips();
SelectionDialog::get()->show();
}
} }
SelectionDialog::get()->on_select=[rows](const std::string &selected, bool hide_window) {
auto location=rows->at(selected);
if(!boost::filesystem::is_regular_file(location.file_path))
return;
Notebook::get().open(location.file_path);
auto view=Notebook::get().get_current_view();
view->place_cursor_at_line_index(location.line, location.index);
view->scroll_to_cursor_delayed(view, true, false);
view->hide_tooltips();
};
view->hide_tooltips();
SelectionDialog::get()->show();
}
};
menu.add_action("source_goto_implementation", [this, goto_selected_location]() {
if(auto view=Notebook::get().get_current_view()) {
if(view->get_implementation_locations)
goto_selected_location(view, view->get_implementation_locations(Notebook::get().get_views()));
}
});
menu.add_action("source_goto_declaration_or_implementation", [this, goto_selected_location]() {
if(auto view=Notebook::get().get_current_view()) {
if(view->get_declaration_or_implementation_locations)
goto_selected_location(view, view->get_declaration_or_implementation_locations(Notebook::get().get_views()));
} }
}); });
@ -1202,6 +1209,7 @@ void Window::activate_menu_items() {
menu.actions["source_find_documentation"]->set_enabled(view && view->get_token_data); menu.actions["source_find_documentation"]->set_enabled(view && view->get_token_data);
menu.actions["source_goto_declaration"]->set_enabled(view && view->get_declaration_location); menu.actions["source_goto_declaration"]->set_enabled(view && view->get_declaration_location);
menu.actions["source_goto_implementation"]->set_enabled(view && view->get_implementation_locations); menu.actions["source_goto_implementation"]->set_enabled(view && view->get_implementation_locations);
menu.actions["source_goto_declaration_or_implementation"]->set_enabled(view && view->get_declaration_or_implementation_locations);
menu.actions["source_goto_usage"]->set_enabled(view && view->get_usages); menu.actions["source_goto_usage"]->set_enabled(view && view->get_usages);
menu.actions["source_goto_method"]->set_enabled(view && view->get_methods); menu.actions["source_goto_method"]->set_enabled(view && view->get_methods);
menu.actions["source_rename"]->set_enabled(view && view->rename_similar_tokens); menu.actions["source_rename"]->set_enabled(view && view->rename_similar_tokens);

Loading…
Cancel
Save