Browse Source

Now uses ctags to find methods in C/C++ source files

pipelines/235045657
eidheim 6 years ago
parent
commit
4b56c48e3e
  1. 69
      src/source.cc
  2. 101
      src/source_clang.cc
  3. 67
      src/source_generic.cc
  4. 6
      tests/source_clang_test.cc

69
src/source.cc

@ -1,5 +1,6 @@
#include "source.h"
#include "config.h"
#include "ctags.h"
#include "directories.h"
#include "filesystem.h"
#include "git.h"
@ -20,6 +21,18 @@
#include <regex>
#include <set>
#ifdef _WIN32
#include <windows.h>
inline DWORD get_current_process_id() {
return GetCurrentProcessId();
}
#else
#include <unistd.h>
inline pid_t get_current_process_id() {
return getpid();
}
#endif
Glib::RefPtr<Gsv::LanguageManager> Source::LanguageManager::get_default() {
static auto instance = Gsv::LanguageManager::create();
return instance;
@ -297,6 +310,62 @@ Source::View::View(const boost::filesystem::path &file_path, const Glib::RefPtr<
}
};
}
get_methods = [this]() {
std::vector<std::pair<Offset, std::string>> methods;
boost::filesystem::path file_path;
boost::system::error_code ec;
bool use_tmp_file = false;
if(this->get_buffer()->get_modified()) {
use_tmp_file = true;
file_path = boost::filesystem::temp_directory_path(ec);
if(ec) {
Terminal::get().print("Error: could not get temporary directory folder\n", true);
return methods;
}
file_path /= "jucipp_get_methods" + std::to_string(get_current_process_id());
boost::filesystem::create_directory(file_path, ec);
if(ec) {
Terminal::get().print("Error: could not create temporary folder\n", true);
return methods;
}
file_path /= this->file_path.filename();
filesystem::write(file_path, this->get_buffer()->get_text().raw());
}
else
file_path = this->file_path;
Ctags ctags(file_path, false, true);
if(use_tmp_file)
boost::filesystem::remove_all(file_path.parent_path(), ec);
if(!ctags) {
Info::get().print("No methods found in current buffer");
return methods;
}
std::string line;
while(std::getline(ctags.output, line)) {
auto location = ctags.get_location(line, true);
std::transform(location.kind.begin(), location.kind.end(), location.kind.begin(),
[](char c) { return std::tolower(c); });
std::vector<std::string> ignore_kinds = {"variable", "local", "constant", "global", "property", "member", "enum",
"class", "struct", "namespace",
"macro", "param", "header",
"typedef", "using", "alias",
"project", "option"};
if(std::none_of(ignore_kinds.begin(), ignore_kinds.end(), [&location](const std::string &e) { return location.kind.find(e) != std::string::npos; }) &&
location.source.find("<b>") != std::string::npos)
methods.emplace_back(Offset(location.line, location.index), std::to_string(location.line + 1) + ": " + location.source);
}
std::sort(methods.begin(), methods.end(), [](const std::pair<Offset, std::string> &e1, const std::pair<Offset, std::string> &e2) {
return e1.first < e2.first;
});
if(methods.empty())
Info::get().print("No methods found in current buffer");
return methods;
};
}
Gsv::DrawSpacesFlags Source::View::parse_show_whitespace_characters(const std::string &text) {

101
src/source_clang.cc

@ -1577,61 +1577,62 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
return std::string();
};
get_methods = [this]() {
std::vector<std::pair<Offset, std::string>> methods;
if(!parsed) {
Info::get().print("Buffer is parsing");
return methods;
}
clangmm::Offset last_offset{static_cast<unsigned>(-1), static_cast<unsigned>(-1)};
for(auto &token : *clang_tokens) {
if(token.is_identifier()) {
auto cursor = token.get_cursor();
auto kind = cursor.get_kind();
if(kind == clangmm::Cursor::Kind::FunctionDecl || kind == clangmm::Cursor::Kind::CXXMethod ||
kind == clangmm::Cursor::Kind::Constructor || kind == clangmm::Cursor::Kind::Destructor ||
kind == clangmm::Cursor::Kind::FunctionTemplate || kind == clangmm::Cursor::Kind::ConversionFunction) {
auto offset = cursor.get_source_location().get_offset();
if(offset == last_offset)
continue;
last_offset = offset;
std::string prefix;
auto parent = cursor.get_semantic_parent();
while(parent && parent.get_kind() != clangmm::Cursor::Kind::TranslationUnit) {
prefix.insert(0, parent.get_display_name() + (prefix.empty() ? "" : "::"));
parent = parent.get_semantic_parent();
}
if(!prefix.empty())
prefix += ':';
prefix += std::to_string(offset.line) + ": ";
prefix = Glib::Markup::escape_text(prefix);
std::string ret;
if(kind != clangmm::Cursor::Kind::Constructor && kind != clangmm::Cursor::Kind::Destructor) {
ret += cursor.get_type().get_result().get_spelling();
if(!ret.empty() && ret.back() != '&' && ret.back() != '*')
ret += ' ';
}
ret = Glib::Markup::escape_text(ret);
if(language && (language->get_id() == "chdr" || language->get_id() == "cpphdr")) {
get_methods = [this]() {
std::vector<std::pair<Offset, std::string>> methods;
if(!parsed) {
Info::get().print("Buffer is parsing");
return methods;
}
clangmm::Offset last_offset{static_cast<unsigned>(-1), static_cast<unsigned>(-1)};
for(auto &token : *clang_tokens) {
if(token.is_identifier()) {
auto cursor = token.get_cursor();
auto kind = cursor.get_kind();
if(kind == clangmm::Cursor::Kind::FunctionDecl || kind == clangmm::Cursor::Kind::CXXMethod ||
kind == clangmm::Cursor::Kind::Constructor || kind == clangmm::Cursor::Kind::Destructor ||
kind == clangmm::Cursor::Kind::FunctionTemplate || kind == clangmm::Cursor::Kind::ConversionFunction) {
auto offset = cursor.get_source_location().get_offset();
if(offset == last_offset)
continue;
last_offset = offset;
std::string scope;
auto parent = cursor.get_semantic_parent();
while(parent && parent.get_kind() != clangmm::Cursor::Kind::TranslationUnit) {
scope.insert(0, parent.get_display_name() + (scope.empty() ? "" : "::"));
parent = parent.get_semantic_parent();
}
if(!scope.empty())
scope += "::";
scope = Glib::Markup::escape_text(scope);
std::string ret;
if(kind != clangmm::Cursor::Kind::Constructor && kind != clangmm::Cursor::Kind::Destructor) {
ret += cursor.get_type().get_result().get_spelling();
if(!ret.empty() && ret.back() != '&' && ret.back() != '*')
ret += ' ';
}
ret = Glib::Markup::escape_text(ret);
auto method = Glib::Markup::escape_text(cursor.get_display_name());
// Add bold method token
auto end = method.find('(');
if(end == std::string::npos)
continue;
method.insert(end, "</b>");
method.insert(0, "<b>");
auto method = Glib::Markup::escape_text(cursor.get_display_name());
// Add bold method token
auto end = method.find('(');
if(end == std::string::npos)
continue;
method.insert(end, "</b>");
method.insert(0, "<b>");
methods.emplace_back(Offset(offset.line - 1, offset.index - 1), prefix + ret + method);
methods.emplace_back(Offset(offset.line - 1, offset.index - 1), std::to_string(offset.line) + ": " + ret + scope + method);
}
}
}
}
if(methods.empty())
Info::get().print("No methods found in current buffer");
if(methods.empty())
Info::get().print("No methods found in current buffer");
return methods;
};
return methods;
};
}
get_token_data = [this]() -> std::vector<std::string> {
clangmm::Cursor cursor;

67
src/source_generic.cc

@ -1,5 +1,4 @@
#include "source_generic.h"
#include "ctags.h"
#include "filesystem.h"
#include "info.h"
#include "selection_dialog.h"
@ -7,18 +6,6 @@
#include "terminal.h"
#include <algorithm>
#ifdef _WIN32
#include <windows.h>
inline DWORD get_current_process_id() {
return GetCurrentProcessId();
}
#else
#include <unistd.h>
inline pid_t get_current_process_id() {
return getpid();
}
#endif
Source::GenericView::GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language) : BaseView(file_path, language), View(file_path, language, true), autocomplete(this, interactive_completion, last_keyval, false) {
spellcheck_all = true;
@ -55,60 +42,6 @@ Source::GenericView::GenericView(const boost::filesystem::path &file_path, const
setup_buffer_words();
setup_autocomplete();
get_methods = [this]() {
std::vector<std::pair<Offset, std::string>> methods;
boost::filesystem::path file_path;
boost::system::error_code ec;
bool use_tmp_file = false;
if(this->get_buffer()->get_modified()) {
use_tmp_file = true;
file_path = boost::filesystem::temp_directory_path(ec);
if(ec) {
Terminal::get().print("Error: could not get temporary directory folder\n", true);
return methods;
}
file_path /= "jucipp_get_methods" + std::to_string(get_current_process_id());
boost::filesystem::create_directory(file_path, ec);
if(ec) {
Terminal::get().print("Error: could not create temporary folder\n", true);
return methods;
}
file_path /= this->file_path.filename();
filesystem::write(file_path, this->get_buffer()->get_text().raw());
}
else
file_path = this->file_path;
Ctags ctags(file_path, false, true);
if(use_tmp_file)
boost::filesystem::remove_all(file_path.parent_path(), ec);
if(!ctags) {
Info::get().print("No methods found in current buffer");
return methods;
}
std::string line;
while(std::getline(ctags.output, line)) {
auto location = ctags.get_location(line, true);
std::transform(location.kind.begin(), location.kind.end(), location.kind.begin(),
[](char c) { return std::tolower(c); });
std::vector<std::string> ignore_kinds = {"variable", "local", "constant", "global", "property", "member", "enum",
"macro", "param", "header",
"typedef", "using", "alias",
"project", "option"};
if(std::none_of(ignore_kinds.begin(), ignore_kinds.end(), [&location](const std::string &e) { return location.kind.find(e) != std::string::npos; }))
methods.emplace_back(Offset(location.line, location.index), location.source);
}
std::sort(methods.begin(), methods.end(), [](const std::pair<Offset, std::string> &e1, const std::pair<Offset, std::string> &e2) {
return e1.first < e2.first;
});
if(methods.empty())
Info::get().print("No methods found in current buffer");
return methods;
};
}
Source::GenericView::~GenericView() {

6
tests/source_clang_test.cc

@ -23,6 +23,12 @@ int main() {
auto app = Gtk::Application::create();
Gsv::init();
#ifdef JUCI_USE_UCTAGS
Config::get().project.ctags_command = "uctags";
#else
Config::get().project.ctags_command = "ctags";
#endif
#ifdef _WIN32
g_assert_cmpstr(std::getenv("MSYSTEM_PREFIX"), !=, nullptr);
#endif

Loading…
Cancel
Save