From 7a456b8ddbf0d518ed6ae4a70f228f98938c11c9 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 14 Nov 2018 16:21:31 +0100 Subject: [PATCH] Added Edit.Show/Hide that can be used to hide text in a buffer by making it smaller --- CMakeLists.txt | 2 +- src/files.h | 1 + src/menu.cc | 6 ++ src/notebook.cc | 2 +- src/source.cc | 151 +++++++++++++++++++++++++++++++++++++++++---- src/source.h | 6 +- src/source_base.cc | 10 ++- src/source_base.h | 1 + src/window.cc | 5 ++ 9 files changed, 168 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c0589d4..11f7ca8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required (VERSION 2.8.8) project(juci) -set(JUCI_VERSION "1.4.6.3") +set(JUCI_VERSION "1.4.6.4") set(CPACK_PACKAGE_NAME "jucipp") set(CPACK_PACKAGE_CONTACT "Ole Christian Eidheim ") diff --git a/src/files.h b/src/files.h index 044cb52..e76cc60 100644 --- a/src/files.h +++ b/src/files.h @@ -89,6 +89,7 @@ const std::string default_config_file = R"RAW({ "edit_cut": "x", "edit_copy": "c", "edit_paste": "v", + "edit_show_or_hide": "", "edit_find": "f", "source_spellcheck": "", "source_spellcheck_clear": "", diff --git a/src/menu.cc b/src/menu.cc index b5c19de..8ef6832 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -197,6 +197,12 @@ const Glib::ustring menu_xml = R"RAW( app.edit_paste +
+ + _Show/_Hide + app.edit_show_or_hide + +
_Find diff --git a/src/notebook.cc b/src/notebook.cc index a89f001..a1d0ae2 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -435,7 +435,7 @@ void Notebook::open_uri(const std::string &uri) { void Notebook::configure(size_t index) { auto source_font_description = Pango::FontDescription(Config::get().source.font); - auto source_map_font_desc = Pango::FontDescription(static_cast(source_font_description.get_family()) + " " + Config::get().source.map_font_size); + auto source_map_font_desc = Pango::FontDescription(source_font_description.get_family() + " " + Config::get().source.map_font_size); source_maps.at(index)->override_font(source_map_font_desc); if(Config::get().source.show_map) { if(hboxes.at(index)->get_children().size() == 1) diff --git a/src/source.cc b/src/source.cc index dbcb9df..23dd48d 100644 --- a/src/source.cc +++ b/src/source.cc @@ -8,6 +8,7 @@ #include "selection_dialog.h" #include "terminal.h" #include "utility.h" +#include #include #include #include @@ -152,6 +153,9 @@ Source::View::View(const boost::filesystem::path &file_path, const Glib::RefPtr< link_tag = get_buffer()->create_tag("link"); + hide_tag = get_buffer()->create_tag(); + hide_tag->property_scale() = 0.25; + if(language) { auto language_id = language->get_id(); if(language_id == "chdr" || language_id == "cpphdr" || language_id == "c" || language_id == "cpp") { @@ -373,7 +377,7 @@ void Source::View::configure() { else set_wrap_mode(Gtk::WrapMode::WRAP_NONE); property_highlight_current_line() = Config::get().source.highlight_current_line; - property_show_line_numbers() = Config::get().source.show_line_numbers; + line_renderer->set_visible(Config::get().source.show_line_numbers); if(Config::get().source.font.size() > 0) override_font(Pango::FontDescription(Config::get().source.font)); @@ -459,17 +463,43 @@ void Source::View::setup_signals() { } }); - signal_realize().connect([this] { - auto gutter = get_gutter(Gtk::TextWindowType::TEXT_WINDOW_LEFT); - auto renderer = gutter->get_renderer_at_pos(15, 0); - if(renderer) { - renderer_activate_connection.disconnect(); - renderer_activate_connection = renderer->signal_activate().connect([this](const Gtk::TextIter &iter, const Gdk::Rectangle &, GdkEvent *) { - if(toggle_breakpoint) - toggle_breakpoint(iter.get_line()); - }); + + // Line numbers + line_renderer = Gtk::manage(new Gsv::GutterRendererText()); + auto gutter = get_gutter(Gtk::TextWindowType::TEXT_WINDOW_LEFT); + + line_renderer->set_alignment_mode(Gsv::GutterRendererAlignmentMode::GUTTER_RENDERER_ALIGNMENT_MODE_FIRST); + line_renderer->set_alignment(1.0, -1); + line_renderer->set_padding(3, -1); + gutter->insert(line_renderer, GTK_SOURCE_VIEW_GUTTER_POSITION_LINES); + + auto set_line_renderer_width = [this] { + int width, height; + line_renderer->measure(std::to_string(get_buffer()->get_line_count()), width, height); + line_renderer->set_size(width); + }; + set_line_renderer_width(); + get_buffer()->signal_changed().connect([set_line_renderer_width] { + set_line_renderer_width(); + }); + signal_style_updated().connect([set_line_renderer_width] { + set_line_renderer_width(); + }); + line_renderer->signal_query_data().connect([this](const Gtk::TextIter &start, const Gtk::TextIter &end, Gsv::GutterRendererState state) { + if(!start.begins_tag(hide_tag) && !start.has_tag(hide_tag)) { + if(start.get_line() == get_buffer()->get_insert()->get_iter().get_line()) + line_renderer->set_text(Gsv::Markup("" + std::to_string(start.get_line() + 1) + "")); + else + line_renderer->set_text(Gsv::Markup(std::to_string(start.get_line() + 1))); } }); + line_renderer->signal_query_activatable().connect([](const Gtk::TextIter &, const Gdk::Rectangle &, GdkEvent *) { + return true; + }); + line_renderer->signal_activate().connect([this](const Gtk::TextIter &iter, const Gdk::Rectangle &, GdkEvent *) { + if(toggle_breakpoint) + toggle_breakpoint(iter.get_line()); + }); type_tooltips.on_motion = [this] { delayed_tooltips_connection.disconnect(); @@ -478,6 +508,7 @@ void Source::View::setup_signals() { delayed_tooltips_connection.disconnect(); }; + signal_motion_notify_event().connect([this](GdkEventMotion *event) { if(on_motion_last_x != event->x || on_motion_last_y != event->y) { delayed_tooltips_connection.disconnect(); @@ -957,7 +988,6 @@ Source::View::~View() { delayed_tooltips_connection.disconnect(); delayed_tag_similar_symbols_connection.disconnect(); delayed_tag_clickable_connection.disconnect(); - renderer_activate_connection.disconnect(); non_deleted_views.erase(this); views.erase(this); @@ -977,6 +1007,105 @@ void Source::View::hide_dialogs() { CompletionDialog::get()->hide(); } +void Source::View::show_or_hide() { + Gtk::TextIter start, end; + get_buffer()->get_selection_bounds(start, end); + + if(start == end && !(start.starts_line() && start.ends_line())) { // Select code block instead if no current selection + start = get_buffer()->get_iter_at_line(start.get_line()); + auto tabs_end = get_tabs_end_iter(start); + auto start_tabs = tabs_end.get_line_offset() - start.get_line_offset(); + + if(!end.ends_line()) + end.forward_to_line_end(); + + auto last_empty = get_buffer()->end(); + auto last_tabs_end = get_buffer()->end(); + while(true) { + if(end.ends_line()) { + auto line_start = get_buffer()->get_iter_at_line(end.get_line()); + auto tabs_end = get_tabs_end_iter(line_start); + if(end.starts_line() || tabs_end.ends_line()) { // Empty line + if(!last_empty) + last_empty = end; + } + else { + auto tabs = tabs_end.get_line_offset() - line_start.get_line_offset(); + if(is_cpp && tabs == 0 && *line_start == '#') { // C/C++ defines can be at the first line + if(end.get_line() == start.get_line()) // Do not try to find define blocks since these rarely are indented + break; + } + else if(tabs < start_tabs) { + end = get_buffer()->get_iter_at_line(end.get_line()); + break; + } + else if(tabs == start_tabs) { + // Check for block continuation keywords + std::string text = get_buffer()->get_text(tabs_end, end); + if(end.get_line() != start.get_line()) { + if(text.empty()) { + end = get_buffer()->get_iter_at_line(end.get_line()); + break; + } + static std::vector exact = {"}", ")", "]", ">", " followed_by_non_token_char = {"elseif", "elif", "case", "default", "private", "public", "protected"}; + if(text == "{") { // C/C++ sometimes starts a block with a standalone { + if(!is_token_char(*last_tabs_end)) { + end = get_buffer()->get_iter_at_line(end.get_line()); + break; + } + else { // Check for ; at the end of last line + auto iter = tabs_end; + while(iter.backward_char() && iter > last_tabs_end && (*iter == ' ' || *iter == '\t' || iter.ends_line() || !is_code_iter(iter))) { + } + if(*iter == ';') { + end = get_buffer()->get_iter_at_line(end.get_line()); + break; + } + } + } + else if(std::none_of(exact.begin(), exact.end(), [&text](const std::string &e) { + return text.compare(0, e.size(), e) == 0; + }) && + std::none_of(followed_by_non_token_char.begin(), followed_by_non_token_char.end(), [this, &text](const std::string &e) { + return text.compare(0, e.size(), e) == 0 && text.size() > e.size() && !is_token_char(text[e.size()]); + })) { + end = get_buffer()->get_iter_at_line(end.get_line()); + break; + } + } + last_tabs_end = tabs_end; + } + last_empty = get_buffer()->end(); + } + } + if(end.is_end()) + break; + end.forward_char(); + } + if(last_empty) + end = get_buffer()->get_iter_at_line(last_empty.get_line()); + } + if(start == end) + end.forward_char(); // Select empty line + + if(!start.starts_line()) + start = get_buffer()->get_iter_at_line(start.get_line()); + if(!end.ends_line() && !end.starts_line()) + end.forward_to_line_end(); + + if((start.begins_tag(hide_tag) || start.has_tag(hide_tag)) && (end.ends_tag(hide_tag) || end.has_tag(hide_tag))) { + get_buffer()->remove_tag(hide_tag, start, end); + return; + } + auto iter = start; + if(iter.forward_to_tag_toggle(hide_tag) && iter < end) { + get_buffer()->remove_tag(hide_tag, start, end); + return; + } + get_buffer()->apply_tag(hide_tag, start, end); +} + void Source::View::insert_with_links_tagged(const Glib::RefPtr &buffer, const std::string &text) { static std::regex http_regex("(https?://[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=]+[a-zA-Z0-9\\-_~/@$*+;=])"); std::smatch sm; diff --git a/src/source.h b/src/source.h index 3f8dc0a..05f9a66 100644 --- a/src/source.h +++ b/src/source.h @@ -81,6 +81,8 @@ namespace Source { void hide_tooltips() override; void hide_dialogs() override; + void show_or_hide(); /// Show or hide text selection + bool soft_reparse_needed = false; bool full_reparse_needed = false; virtual void soft_reparse(bool delayed = false) { soft_reparse_needed = false; } @@ -104,6 +106,8 @@ namespace Source { Glib::RefPtr link_tag; /// Used in tooltips void insert_with_links_tagged(const Glib::RefPtr &buffer, const std::string &text); + Glib::RefPtr hide_tag; + virtual void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) { diagnostic_tooltips.show(rectangle); } void add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, bool error, std::function &)> &&set_buffer); void clear_diagnostic_tooltips(); @@ -141,7 +145,7 @@ namespace Source { Gsv::DrawSpacesFlags parse_show_whitespace_characters(const std::string &text); - sigc::connection renderer_activate_connection; + Gsv::GutterRendererText *line_renderer; bool use_fixed_continuation_indenting = true; bool is_cpp = false; diff --git a/src/source_base.cc b/src/source_base.cc index ba5091d..f4b113e 100644 --- a/src/source_base.cc +++ b/src/source_base.cc @@ -605,16 +605,22 @@ Gtk::TextIter Source::BaseView::get_tabs_end_iter() { return get_tabs_end_iter(get_buffer()->get_insert()); } +bool Source::BaseView::is_token_char(gunichar chr) { + if((chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z') || (chr >= '0' && chr <= '9') || chr == '_') + return true; + return false; +} + std::pair Source::BaseView::get_token_iters(Gtk::TextIter iter) { auto start = iter; auto end = iter; - while((*iter >= 'A' && *iter <= 'Z') || (*iter >= 'a' && *iter <= 'z') || (*iter >= '0' && *iter <= '9') || *iter == '_') { + while(is_token_char(*iter)) { start = iter; if(!iter.backward_char()) break; } - while((*end >= 'A' && *end <= 'Z') || (*end >= 'a' && *end <= 'z') || (*end >= '0' && *end <= '9') || *end == '_') { + while(is_token_char(*end)) { if(!end.forward_char()) break; } diff --git a/src/source_base.h b/src/source_base.h index 2e5f87b..225169f 100644 --- a/src/source_base.h +++ b/src/source_base.h @@ -121,6 +121,7 @@ namespace Source { Gtk::TextIter get_tabs_end_iter(int line_nr); Gtk::TextIter get_tabs_end_iter(); + bool is_token_char(gunichar chr); std::pair get_token_iters(Gtk::TextIter iter); std::string get_token(const Gtk::TextIter &iter); void cleanup_whitespace_characters(); diff --git a/src/window.cc b/src/window.cc index d3cf905..22914bb 100644 --- a/src/window.cc +++ b/src/window.cc @@ -522,6 +522,11 @@ void Window::set_menu_actions() { } }); + menu.add_action("edit_show_or_hide", []() { + if(auto view = Notebook::get().get_current_view()) + view->show_or_hide(); + }); + menu.add_action("edit_find", [this]() { search_and_replace_entry(); });