Browse Source

Cleanup and improvement to Source->Find Pattern: now also moves cursor offset to first match when line is selected. Also, when showing match results, line in current view is highlighted.

pipelines/143601543
eidheim 6 years ago
parent
commit
e849fc0f45
  1. 1
      src/CMakeLists.txt
  2. 8
      src/ctags.cc
  3. 2
      src/ctags.h
  4. 137
      src/grep.cc
  5. 18
      src/grep.h
  6. 3
      src/project.cc
  7. 110
      src/window.cc

1
src/CMakeLists.txt

@ -8,6 +8,7 @@ set(JUCI_SHARED_FILES
documentation_cppreference.cc documentation_cppreference.cc
filesystem.cc filesystem.cc
git.cc git.cc
grep.cc
menu.cc menu.cc
meson.cc meson.cc
project_build.cc project_build.cc

8
src/ctags.cc

@ -17,8 +17,8 @@ std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream>> Ctags::ge
std::string exclude = " --exclude=node_modules"; std::string exclude = " --exclude=node_modules";
if(!build->project_path.empty()) { if(!build->project_path.empty()) {
run_path = build->project_path; run_path = build->project_path;
exclude += " --exclude=" + filesystem::get_relative_path(build->get_default_path(), run_path).string(); exclude += " --exclude=" + filesystem::escape_argument(filesystem::get_relative_path(build->get_default_path(), run_path).string());
exclude += " --exclude=" + filesystem::get_relative_path(build->get_debug_path(), run_path).string(); exclude += " --exclude=" + filesystem::escape_argument(filesystem::get_relative_path(build->get_debug_path(), run_path).string());
} }
else else
run_path = path; run_path = path;
@ -36,7 +36,7 @@ std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream>> Ctags::ge
return {run_path, std::move(stdout_stream)}; return {run_path, std::move(stdout_stream)};
} }
Ctags::Location Ctags::get_location(const std::string &line_, bool markup) { Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup) {
Location location; Location location;
#ifdef _WIN32 #ifdef _WIN32
@ -118,7 +118,7 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool markup) {
if(pos != std::string::npos) if(pos != std::string::npos)
location.index += pos; location.index += pos;
if(markup) { if(add_markup) {
location.source = Glib::Markup::escape_text(location.source); location.source = Glib::Markup::escape_text(location.source);
std::string symbol = Glib::Markup::escape_text(location.symbol); std::string symbol = Glib::Markup::escape_text(location.symbol);
pos = -1; pos = -1;

2
src/ctags.h

@ -20,7 +20,7 @@ public:
static std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream>> get_result(const boost::filesystem::path &path); static std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream>> get_result(const boost::filesystem::path &path);
static Location get_location(const std::string &line, bool markup); static Location get_location(const std::string &line, bool add_markup);
static std::vector<Location> get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type); static std::vector<Location> get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type);

137
src/grep.cc

@ -0,0 +1,137 @@
#include "grep.h"
#include "config.h"
#include "filesystem.h"
#include "project_build.h"
#include "terminal.h"
std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream>> Grep::get_result(const boost::filesystem::path &path, const std::string &pattern, bool case_sensitive, bool extended_regex) {
boost::filesystem::path run_path;
auto build = Project::Build::create(path);
std::string exclude = "--exclude-dir=node_modules";
if(!build->project_path.empty()) {
run_path = build->project_path;
exclude += " --exclude-dir=" + filesystem::escape_argument(filesystem::get_relative_path(build->get_default_path(), build->project_path).string());
exclude += " --exclude-dir=" + filesystem::escape_argument(filesystem::get_relative_path(build->get_debug_path(), build->project_path).string());
}
else
run_path = path;
std::string flags;
if(!case_sensitive)
flags += " -i";
if(extended_regex)
flags += " -E";
auto escaped_pattern = '\'' + pattern + '\'';
for(size_t i = 1; i < escaped_pattern.size() - 1; ++i) {
if(escaped_pattern[i] == '\'') {
escaped_pattern.insert(i, "\\");
++i;
}
}
std::string command = Config::get().project.grep_command + " -R " + flags + " --color=always --binary-files=without-match " + exclude + " -n " + escaped_pattern + " *";
std::stringstream stdin_stream;
//TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below
auto stdout_stream = std::make_unique<std::stringstream>();
Terminal::get().process(stdin_stream, *stdout_stream, command, run_path);
return {run_path, std::move(stdout_stream)};
}
Grep::Location Grep::get_location(std::string line, bool color_codes_to_markup, bool include_offset, const std::string &only_for_file) {
std::vector<std::pair<size_t, size_t>> positions;
size_t file_end = std::string::npos, line_end = std::string::npos;
if(color_codes_to_markup) {
std::string escaped = Glib::Markup::escape_text(line);
auto decode_escape_sequence = [](const std::string &line, size_t &i) -> bool {
if(line.compare(i, 7, "&#x1b;[") != 0)
return false;
i += 7;
for(; i < line.size(); ++i) {
if((line[i] >= '0' && line[i] <= '9') || line[i] == ';')
continue;
return true;
}
return false;
};
bool open = false;
size_t start;
line.clear();
line.reserve(escaped.size());
for(size_t i = 0; i < escaped.size(); ++i) {
if(decode_escape_sequence(escaped, i)) {
if(escaped[i] == 'm') {
if(!open)
start = line.size();
else
positions.emplace_back(start, line.size());
open = !open;
}
continue;
}
if(escaped[i] == ':') {
if(file_end == std::string::npos)
file_end = line.size();
else if(line_end == std::string::npos)
line_end = line.size();
}
line += escaped[i];
}
if(file_end == std::string::npos || line_end == std::string::npos)
return {};
for(auto it = positions.rbegin(); it != positions.rend(); ++it) {
if(it->first > line_end) {
line.insert(it->second, "</b>");
line.insert(it->first, "<b>");
}
}
}
else {
file_end = line.find(':');
if(file_end == std::string::npos)
return {};
line_end = line.find(':', file_end + 1);
if(file_end == std::string::npos)
return {};
}
Location location;
location.markup = std::move(line);
auto file = location.markup.substr(0, file_end);
if(!only_for_file.empty() && file != only_for_file)
return location;
location.file_path = std::move(file);
try {
location.line = std::stoul(location.markup.substr(file_end + 1, line_end - file_end)) - 1;
if(!include_offset) {
location.offset = 0;
return location;
}
// Find line offset by searching for first match marked with <b></b>
Glib::ustring ustr = location.markup.substr(line_end + 1);
size_t offset = 0;
bool escaped = false;
for(auto chr : ustr) {
if(chr == '<')
break;
else if(chr == '&')
escaped = true;
else if(chr == ';') {
escaped = false;
continue;
}
else if(escaped)
continue;
offset++;
}
location.offset = offset;
return location;
}
catch(...) {
return {};
}
}

18
src/grep.h

@ -0,0 +1,18 @@
#pragma once
#include <boost/filesystem.hpp>
class Grep {
public:
class Location {
public:
std::string file_path;
unsigned long line;
unsigned long offset;
std::string markup;
operator bool() const { return !file_path.empty(); }
};
static std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream>> get_result(const boost::filesystem::path &path, const std::string &pattern, bool case_sensitive, bool extended_regex);
static Location get_location(std::string line, bool color_codes_to_markup, bool include_offset, const std::string &only_for_file = {});
};

3
src/project.cc

@ -219,8 +219,7 @@ void Project::Base::recreate_build() {
} }
void Project::Base::show_symbols() { void Project::Base::show_symbols() {
auto view_folder = get_preferably_view_folder(); auto pair = Ctags::get_result(get_preferably_view_folder());
auto pair = Ctags::get_result(view_folder);
auto path = std::move(pair.first); auto path = std::move(pair.first);
auto stream = std::move(pair.second); auto stream = std::move(pair.second);

110
src/window.cc

@ -7,6 +7,7 @@
#include "directories.h" #include "directories.h"
#include "entrybox.h" #include "entrybox.h"
#include "filesystem.h" #include "filesystem.h"
#include "grep.h"
#include "info.h" #include "info.h"
#include "menu.h" #include "menu.h"
#include "notebook.h" #include "notebook.h"
@ -786,105 +787,60 @@ void Window::set_menu_actions() {
menu.add_action("source_find_pattern", [this]() { menu.add_action("source_find_pattern", [this]() {
std::string excludes = "--exclude-dir=node_modules"; std::string excludes = "--exclude-dir=node_modules";
auto view_folder = Project::get_preferably_view_folder();
auto build = Project::Build::create(view_folder);
boost::filesystem::path default_path, debug_path;
if(!build->project_path.empty()) {
view_folder = build->project_path;
excludes += " --exclude-dir=" + filesystem::escape_argument(filesystem::get_relative_path(build->get_default_path(), build->project_path).string());
excludes += " --exclude-dir=" + filesystem::escape_argument(filesystem::get_relative_path(build->get_debug_path(), build->project_path).string());
}
EntryBox::get().clear(); EntryBox::get().clear();
EntryBox::get().entries.emplace_back(last_find_pattern, [this, view_folder = std::move(view_folder), excludes = std::move(excludes)](const std::string &pattern) { EntryBox::get().entries.emplace_back(last_find_pattern, [this](const std::string &pattern_) {
auto pattern = pattern_; // Store pattern to safely hide entrybox
EntryBox::get().hide();
if(!pattern.empty()) { if(!pattern.empty()) {
std::stringstream stdin_stream; auto pair = Grep::get_result(Project::get_preferably_view_folder(), pattern, find_pattern_case_sensitive, find_pattern_extended_regex);
std::string flags; auto path = std::move(pair.first);
if(!find_pattern_case_sensitive) auto stream = std::move(pair.second);
flags += " -i"; stream->seekg(0, std::ios::end);
if(find_pattern_extended_regex) if(stream->tellg() == 0) {
flags += " -E";
auto escaped_pattern = '\'' + pattern + '\'';
for(size_t i = 1; i < escaped_pattern.size() - 1; ++i) {
if(escaped_pattern[i] == '\'') {
escaped_pattern.insert(i, "\\");
++i;
}
}
std::string command = Config::get().project.grep_command + " -R " + flags + " --color=always --binary-files=without-match " + excludes + " -n " + escaped_pattern + " *";
//TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below
auto stdout_stream = std::make_unique<std::stringstream>();
Terminal::get().process(stdin_stream, *stdout_stream, command, view_folder);
stdout_stream->seekg(0, std::ios::end);
if(stdout_stream->tellg() == 0) {
Info::get().print("Pattern not found"); Info::get().print("Pattern not found");
EntryBox::get().hide(); EntryBox::get().hide();
return; return;
} }
stdout_stream->seekg(0, std::ios::beg); stream->seekg(0, std::ios::beg);
if(auto view = Notebook::get().get_current_view()) if(auto view = Notebook::get().get_current_view())
SelectionDialog::create(view, true, true); SelectionDialog::create(view, true, true);
else else
SelectionDialog::create(true, true); SelectionDialog::create(true, true);
std::string line; std::string line;
while(std::getline(*stdout_stream, line)) {
auto line_markup = Glib::Markup::escape_text(line); std::string current_path;
size_t start = 0; unsigned int current_line = 0;
while((start = line_markup.find("&#x1b;[", start)) != std::string::npos) { auto view = Notebook::get().get_current_view();
auto start_end = line_markup.find("&#x1b;[K", start + 7); if(view) {
if(start_end == std::string::npos) current_path = filesystem::get_relative_path(view->file_path, path).string();
break; current_line = view->get_buffer()->get_insert()->get_iter().get_line();
auto start_size = start_end - start + 8; }
auto end = line_markup.find("&#x1b;[m&#x1b;[K", start + start_size); bool set_cursor_at_path = true;
if(end == std::string::npos) while(std::getline(*stream, line)) {
break; auto location = Grep::get_location(std::move(line), true, false, current_path);
line_markup.replace(end, 16, "</b>"); SelectionDialog::get()->add_row(location.markup);
line_markup.replace(start, start_size, "<b>"); if(view && location) {
start = end - start_size + 3 + 4; if(set_cursor_at_path) {
} SelectionDialog::get()->set_cursor_at_last_row();
SelectionDialog::get()->add_row(line_markup); set_cursor_at_path = false;
} }
SelectionDialog::get()->on_select = [view_folder = std::move(view_folder)](unsigned int index, const std::string &text, bool hide_window) { else {
auto remove_markup = [](std::string &markup) { if(current_line >= location.line)
auto start = markup.end(); SelectionDialog::get()->set_cursor_at_last_row();
for(auto it = markup.begin(); it != markup.end();) {
if(*it == '<') {
start = it;
it++;
} }
else if(*it == '>' && start != markup.end()) {
it = markup.erase(start, ++it);
start = markup.end();
} }
else
it++;
} }
}; SelectionDialog::get()->on_select = [path = std::move(path)](unsigned int index, const std::string &text, bool hide_window) {
auto file_end = text.find(':'); auto location = Grep::get_location(text, false, true);
if(file_end != std::string::npos) { Notebook::get().open(path / location.file_path);
auto file = text.substr(0, file_end);
remove_markup(file);
auto line_end = text.find(':', file_end + 1);
if(line_end != std::string::npos) {
try {
auto line_str = text.substr(file_end + 1, line_end - file_end);
remove_markup(line_str);
auto line = std::stoi(line_str);
Notebook::get().open(view_folder / file);
auto view = Notebook::get().get_current_view(); auto view = Notebook::get().get_current_view();
view->place_cursor_at_line_pos(line - 1, 0); view->place_cursor_at_line_offset(location.line, location.offset);
view->scroll_to_cursor_delayed(true, false); view->scroll_to_cursor_delayed(true, false);
}
catch(...) {
}
}
}
}; };
SelectionDialog::get()->show(); SelectionDialog::get()->show();
} }
EntryBox::get().hide();
}); });
auto entry_it = EntryBox::get().entries.begin(); auto entry_it = EntryBox::get().entries.begin();
entry_it->set_placeholder_text("Pattern"); entry_it->set_placeholder_text("Pattern");

Loading…
Cancel
Save