diff --git a/src/autocomplete.cpp b/src/autocomplete.cpp index d776b39..c405e9e 100644 --- a/src/autocomplete.cpp +++ b/src/autocomplete.cpp @@ -1,7 +1,7 @@ #include "autocomplete.hpp" #include "selection_dialog.hpp" -Autocomplete::Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool pass_buffer_and_strip_word) +Autocomplete::Autocomplete(Gsv::View *view, bool &interactive_completion, guint &last_keyval, bool pass_buffer_and_strip_word) : view(view), interactive_completion(interactive_completion), pass_buffer_and_strip_word(pass_buffer_and_strip_word) { view->get_buffer()->signal_changed().connect([this, &last_keyval] { if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) { diff --git a/src/autocomplete.hpp b/src/autocomplete.hpp index b55bde0..dd22091 100644 --- a/src/autocomplete.hpp +++ b/src/autocomplete.hpp @@ -7,9 +7,9 @@ #include class Autocomplete { - Gtk::TextView *view; + Gsv::View *view; bool &interactive_completion; - /// If text_view buffer should be passed to add_rows. Empty buffer is passed if not. + /// If view buffer should be passed to add_rows. Empty buffer is passed if not. /// Also, some utilities, like libclang, require that autocomplete is started at the beginning of a word. bool pass_buffer_and_strip_word; @@ -52,7 +52,7 @@ public: std::function(unsigned int)> set_tooltip_buffer = [](unsigned int index) { return nullptr; }; - Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool pass_buffer_and_strip_word); + Autocomplete(Gsv::View *view, bool &interactive_completion, guint &last_keyval, bool pass_buffer_and_strip_word); void run(); void stop(); diff --git a/src/compile_commands.cpp b/src/compile_commands.cpp index b610208..c666043 100644 --- a/src/compile_commands.cpp +++ b/src/compile_commands.cpp @@ -180,7 +180,7 @@ std::vector CompileCommands::get_arguments(const boost::filesystem: if(std::regex_match(clang_version_string, sm, clang_version_regex)) { auto clang_version = sm[1].str(); auto env_msystem_prefix = std::getenv("MSYSTEM_PREFIX"); - if(env_msystem_prefix != nullptr) + if(env_msystem_prefix) arguments.emplace_back("-I" + (boost::filesystem::path(env_msystem_prefix) / "lib/clang" / clang_version / "include").string()); } #endif @@ -202,7 +202,7 @@ std::vector CompileCommands::get_arguments(const boost::filesystem: #endif #ifdef _WIN32 auto env_msystem_prefix = std::getenv("MSYSTEM_PREFIX"); - if(env_msystem_prefix != nullptr) + if(env_msystem_prefix) arguments.emplace_back("-I" + (boost::filesystem::path(env_msystem_prefix) / "lib/clang" / clang_version / "include").string()); #endif } diff --git a/src/debug_lldb.cpp b/src/debug_lldb.cpp index 7501fb5..e9dd9d8 100644 --- a/src/debug_lldb.cpp +++ b/src/debug_lldb.cpp @@ -187,7 +187,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat for(auto &e : environment_from_arguments) environment.emplace_back(e.c_str()); size_t environ_size = 0; - while(environ[environ_size] != nullptr) + while(environ[environ_size]) ++environ_size; for(size_t c = 0; c < environ_size; ++c) environment.emplace_back(environ[c]); @@ -340,13 +340,11 @@ std::vector Debug::LLDB::get_backtrace() { backtrace_frame.index = c_f; - if(frame.GetFunctionName() != nullptr) - backtrace_frame.function_name = frame.GetFunctionName(); + if(auto function_name = frame.GetFunctionName()) + backtrace_frame.function_name = function_name; - auto module_filename = frame.GetModule().GetFileSpec().GetFilename(); - if(module_filename != nullptr) { + if(auto module_filename = frame.GetModule().GetFileSpec().GetFilename()) backtrace_frame.module_filename = module_filename; - } auto line_entry = frame.GetLineEntry(); if(line_entry.IsValid()) { @@ -381,8 +379,8 @@ std::vector Debug::LLDB::get_variables() { Debug::LLDB::Variable variable; variable.thread_index_id = thread.GetIndexID(); variable.frame_index = c_f; - if(value.GetName() != nullptr) - variable.name = value.GetName(); + if(auto name = value.GetName()) + variable.name = name; value.GetDescription(stream); variable.value = stream.GetData(); @@ -442,19 +440,21 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil lldb::SBStream stream; auto value = values.GetValueAtIndex(value_index); - if(value.GetName() != nullptr && value.GetName() == variable) { - auto declaration = value.GetDeclaration(); - if(declaration.IsValid()) { - if(declaration.GetLine() == line_nr && (declaration.GetColumn() == 0 || declaration.GetColumn() == line_index)) { - auto file_spec = declaration.GetFileSpec(); - auto value_decl_path = filesystem::get_normal_path(file_spec.GetDirectory()); - value_decl_path /= file_spec.GetFilename(); - if(value_decl_path == file_path) { - value.GetDescription(stream); - std::string variable_value = stream.GetData(); - if(ends_with(variable_value, "\n\n")) - variable_value.pop_back(); // Remove newline at end of string - return variable_value; + if(auto name = value.GetName()) { + if(name == variable) { + auto declaration = value.GetDeclaration(); + if(declaration.IsValid()) { + if(declaration.GetLine() == line_nr && (declaration.GetColumn() == 0 || declaration.GetColumn() == line_index)) { + auto file_spec = declaration.GetFileSpec(); + auto value_decl_path = filesystem::get_normal_path(file_spec.GetDirectory()); + value_decl_path /= file_spec.GetFilename(); + if(value_decl_path == file_path) { + value.GetDescription(stream); + std::string variable_value = stream.GetData(); + if(ends_with(variable_value, "\n\n")) + variable_value.pop_back(); // Remove newline at end of string + return variable_value; + } } } } diff --git a/src/dialogs_win.cpp b/src/dialogs_win.cpp index f026825..dc33d79 100644 --- a/src/dialogs_win.cpp +++ b/src/dialogs_win.cpp @@ -19,7 +19,7 @@ public: Win32Dialog(){}; ~Win32Dialog() { - if(dialog != nullptr) + if(dialog) dialog->Release(); } diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 58628c9..9a6f07d 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -86,10 +86,8 @@ boost::filesystem::path filesystem::get_home_path() noexcept { if(!home_path.empty()) return home_path; std::vector environment_variables = {"HOME", "AppData"}; - char *ptr = nullptr; for(auto &variable : environment_variables) { - ptr = std::getenv(variable.c_str()); - if(ptr != nullptr) { + if(auto ptr = std::getenv(variable.c_str())) { boost::system::error_code ec; boost::filesystem::path path(ptr); if(boost::filesystem::exists(path, ec)) { diff --git a/src/git.cpp b/src/git.cpp index 7476def..c1cd09d 100644 --- a/src/git.cpp +++ b/src/git.cpp @@ -8,11 +8,11 @@ Git::Error Git::error; std::string Git::Error::message() noexcept { #if LIBGIT2_VER_MAJOR > 0 || (LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR >= 28) - const git_error *last_error = git_error_last(); + auto last_error = git_error_last(); #else - const git_error *last_error = giterr_last(); + auto last_error = giterr_last(); #endif - if(last_error == nullptr) + if(!last_error) return std::string(); else return last_error->message; @@ -253,7 +253,7 @@ std::shared_ptr Git::get_repository(const boost::filesystem::pa } boost::filesystem::path Git::path(const char *cpath, boost::optional cpath_length_) noexcept { - if(cpath == nullptr) + if(!cpath) return boost::filesystem::path(); auto cpath_length = cpath_length_.value_or(strlen(cpath)); if(cpath_length > 0 && (cpath[cpath_length - 1] == '/' || cpath[cpath_length - 1] == '\\')) diff --git a/src/notebook.hpp b/src/notebook.hpp index 733c26d..feec9f0 100644 --- a/src/notebook.hpp +++ b/src/notebook.hpp @@ -18,12 +18,9 @@ class Notebook : public Gtk::Paned { class CursorLocation { public: - CursorLocation(Source::View *view, const Gtk::TextIter &iter) : view(view), mark(iter.get_buffer()->create_mark(iter, false)) {} - ~CursorLocation() { - mark->get_buffer()->delete_mark(mark); - } + CursorLocation(Source::View *view, const Gtk::TextIter &iter) : view(view), mark(iter, false) {} Source::View *view; - Glib::RefPtr mark; + Source::Mark mark; }; private: diff --git a/src/project.cpp b/src/project.cpp index db7d7b1..51fcfce 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -629,7 +629,7 @@ void Project::LLDB::debug_show_variables() { next_char_iter++; value.replace(iter, next_char_iter, "?"); } - tooltip.buffer->insert(tooltip.buffer->get_insert()->get_iter(), value.substr(0, value.size() - 1)); + tooltip.insert_code(value.substr(0, value.size() - 1), {}); } }; if(view) { diff --git a/src/selection_dialog.cpp b/src/selection_dialog.cpp index d9e7728..8670e5e 100644 --- a/src/selection_dialog.cpp +++ b/src/selection_dialog.cpp @@ -36,7 +36,7 @@ void SelectionDialogBase::ListViewText::clear() { } SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const boost::optional &start_iter, bool show_search_entry, bool use_markup) - : start_mark(start_iter ? start_iter->get_buffer()->create_mark(*start_iter) : Glib::RefPtr()), text_view(text_view), window(Gtk::WindowType::WINDOW_POPUP), vbox(Gtk::Orientation::ORIENTATION_VERTICAL), list_view_text(use_markup), show_search_entry(show_search_entry) { + : start_mark(start_iter ? Source::Mark(*start_iter) : Source::Mark()), text_view(text_view), window(Gtk::WindowType::WINDOW_POPUP), vbox(Gtk::Orientation::ORIENTATION_VERTICAL), list_view_text(use_markup), show_search_entry(show_search_entry) { auto g_application = g_application_get_default(); auto gio_application = Glib::wrap(g_application, true); auto application = Glib::RefPtr::cast_static(gio_application); @@ -141,11 +141,6 @@ SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const boost:: }); } -SelectionDialogBase::~SelectionDialogBase() { - if(text_view) - text_view->get_buffer()->delete_mark(start_mark); -} - void SelectionDialogBase::cursor_changed() { if(!is_visible()) return; diff --git a/src/selection_dialog.hpp b/src/selection_dialog.hpp index 4f58471..07f3eba 100644 --- a/src/selection_dialog.hpp +++ b/src/selection_dialog.hpp @@ -1,4 +1,5 @@ #pragma once +#include "source_base.hpp" #include #include #include @@ -38,7 +39,7 @@ class SelectionDialogBase { public: SelectionDialogBase(Gtk::TextView *text_view, const boost::optional &start_iter, bool show_search_entry, bool use_markup); - virtual ~SelectionDialogBase(); + virtual ~SelectionDialogBase() {} void add_row(const std::string &row); void erase_rows(); void set_cursor_at_last_row(); @@ -53,7 +54,7 @@ public: std::function index, const std::string &text)> on_change; std::function on_select; std::function on_search_entry_changed; - Glib::RefPtr start_mark; + Source::Mark start_mark; protected: void cursor_changed(); diff --git a/src/source.cpp b/src/source.cpp index 48cb6d3..1f2ccb6 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -133,9 +133,7 @@ Source::View::View(const boost::filesystem::path &file_path, const Glib::RefPtr< clickable_tag->property_underline() = Pango::Underline::UNDERLINE_SINGLE; clickable_tag->property_underline_set() = true; - get_buffer()->create_tag("def:warning"); get_buffer()->create_tag("def:warning_underline"); - get_buffer()->create_tag("def:error"); get_buffer()->create_tag("def:error_underline"); auto mark_attr_debug_breakpoint = Gsv::MarkAttributes::create(); @@ -464,33 +462,26 @@ void Source::View::configure() { auto scheme = get_source_buffer()->get_style_scheme(); auto tag_table = get_buffer()->get_tag_table(); auto style = scheme->get_style("def:warning"); - auto diagnostic_tag = get_buffer()->get_tag_table()->lookup("def:warning"); auto diagnostic_tag_underline = get_buffer()->get_tag_table()->lookup("def:warning_underline"); if(style && (style->property_foreground_set() || style->property_background_set())) { Glib::ustring warning_property; - if(style->property_foreground_set()) { + if(style->property_foreground_set()) warning_property = style->property_foreground().get_value(); - diagnostic_tag->property_foreground() = warning_property; - } else if(style->property_background_set()) warning_property = style->property_background().get_value(); diagnostic_tag_underline->property_underline() = Pango::Underline::UNDERLINE_ERROR; auto tag_class = G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions: auto param_spec = g_object_class_find_property(tag_class, "underline-rgba"); - if(param_spec != nullptr) { + if(param_spec) diagnostic_tag_underline->set_property("underline-rgba", Gdk::RGBA(warning_property)); - } } style = scheme->get_style("def:error"); - diagnostic_tag = get_buffer()->get_tag_table()->lookup("def:error"); diagnostic_tag_underline = get_buffer()->get_tag_table()->lookup("def:error_underline"); if(style && (style->property_foreground_set() || style->property_background_set())) { Glib::ustring error_property; - if(style->property_foreground_set()) { + if(style->property_foreground_set()) error_property = style->property_foreground().get_value(); - diagnostic_tag->property_foreground() = error_property; - } else if(style->property_background_set()) error_property = style->property_background().get_value(); @@ -630,11 +621,17 @@ void Source::View::setup_signals() { get_buffer()->signal_mark_set().connect([this](const Gtk::TextIter &iterator, const Glib::RefPtr &mark) { auto mark_name = mark->get_name(); + if(mark_name == "selection_bound") { + if(get_buffer()->get_has_selection()) + delayed_tooltips_connection.disconnect(); - if(get_buffer()->get_has_selection() && mark_name == "selection_bound") - delayed_tooltips_connection.disconnect(); + if(update_status_location) + update_status_location(this); - if(mark_name == "insert") { + if(!keep_previous_extended_selections) + previous_extended_selections.clear(); + } + else if(mark_name == "insert") { hide_tooltips(); delayed_tooltips_connection.disconnect(); @@ -672,10 +669,6 @@ void Source::View::setup_signals() { if(!keep_previous_extended_selections) previous_extended_selections.clear(); } - - if(!keep_previous_extended_selections && (mark_name == "insert" || mark_name == "selection_bound")) - if(!keep_previous_extended_selections) - previous_extended_selections.clear(); }); signal_key_release_event().connect([this](GdkEventKey *event) { @@ -929,10 +922,9 @@ void Source::View::setup_format_style(bool is_generic_view) { } } if(left_gravity_insert) { - auto mark = get_buffer()->create_mark(start); + Mark mark(start); get_buffer()->insert(start, replacement_str); get_buffer()->place_cursor(mark->get_iter()); - get_buffer()->delete_mark(mark); } else get_buffer()->insert(start, replacement_str); @@ -2145,7 +2137,7 @@ bool Source::View::on_key_press_event_basic(GdkEventKey *key) { // Indent right when clicking tab, no matter where in the line the cursor is. Also works on selected text. Gtk::TextIter selection_start, selection_end; get_buffer()->get_selection_bounds(selection_start, selection_end); - auto selection_end_mark = get_buffer()->create_mark(selection_end); + Mark selection_end_mark(selection_end); int line_start = selection_start.get_line(); int line_end = selection_end.get_line(); for(int line = line_start; line <= line_end; line++) { @@ -2153,7 +2145,6 @@ bool Source::View::on_key_press_event_basic(GdkEventKey *key) { if(!get_buffer()->get_has_selection() || line_it != selection_end_mark->get_iter()) get_buffer()->insert(line_it, tab); } - get_buffer()->delete_mark(selection_end_mark); return true; } // Indent left when clicking shift-tab, no matter where in the line the cursor is. Also works on selected text. @@ -2472,12 +2463,11 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey *key) { if(!iter.ends_line() && *iter != ')' && *iter != ']' && *iter != '}') { get_buffer()->insert_at_cursor('\n' + tabs + tab); auto iter = get_buffer()->get_insert()->get_iter(); - auto mark = get_buffer()->create_mark(iter); + Mark mark(iter); iter.forward_to_line_end(); get_buffer()->insert(iter, '\n' + tabs + static_cast(close_symbol)); scroll_to(get_buffer()->get_insert()); get_buffer()->place_cursor(mark->get_iter()); - get_buffer()->delete_mark(mark); return true; } else { @@ -2603,12 +2593,11 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey *key) { if(!iter.ends_line() && *iter != ')' && *iter != ']') { get_buffer()->insert_at_cursor('\n' + tabs + tab); auto iter = get_buffer()->get_insert()->get_iter(); - auto mark = get_buffer()->create_mark(iter); + Mark mark(iter); iter.forward_to_line_end(); get_buffer()->insert(iter, '\n' + tabs + '}'); scroll_to(get_buffer()->get_insert()); get_buffer()->place_cursor(mark->get_iter()); - get_buffer()->delete_mark(mark); return true; } else { @@ -2886,16 +2875,14 @@ bool Source::View::on_key_press_event_smart_inserts(GdkEventKey *key) { auto after_end = end; if(before_start.backward_char() && *before_start == '*' && before_start.backward_char() && *before_start == '/' && *after_end == '*' && after_end.forward_char() && *after_end == '/') { - auto start_mark = get_buffer()->create_mark(start); - auto end_mark = get_buffer()->create_mark(end); + Mark start_mark(start); + Mark end_mark(end); get_buffer()->erase(before_start, start); after_end = end_mark->get_iter(); after_end.forward_chars(2); get_buffer()->erase(end_mark->get_iter(), after_end); get_buffer()->select_range(start_mark->get_iter(), end_mark->get_iter()); - get_buffer()->delete_mark(start_mark); - get_buffer()->delete_mark(end_mark); return true; } } @@ -2960,16 +2947,14 @@ bool Source::View::on_key_press_event_smart_inserts(GdkEventKey *key) { if(!left.empty() && !right.empty()) { Gtk::TextIter start, end; get_buffer()->get_selection_bounds(start, end); - auto start_mark = get_buffer()->create_mark(start); - auto end_mark = get_buffer()->create_mark(end); + Mark start_mark(start); + Mark end_mark(end); get_buffer()->insert(start, left); get_buffer()->insert(end_mark->get_iter(), right); auto start_mark_next_iter = start_mark->get_iter(); start_mark_next_iter.forward_chars(left.size()); get_buffer()->select_range(start_mark_next_iter, end_mark->get_iter()); - get_buffer()->delete_mark(start_mark); - get_buffer()->delete_mark(end_mark); return true; } return false; diff --git a/src/source_base.cpp b/src/source_base.cpp index 4836820..675c01e 100644 --- a/src/source_base.cpp +++ b/src/source_base.cpp @@ -1238,15 +1238,15 @@ void Source::BaseView::setup_extra_cursor_signals() { extra_cursor_selection->set_priority(get_buffer()->get_tag_table()->get_size() - 1); - auto last_insert = get_buffer()->create_mark(get_buffer()->get_insert()->get_iter(), false); - auto last_selection_bound = get_buffer()->create_mark(get_buffer()->get_selection_bound()->get_iter(), false); + auto last_insert = std::make_shared(get_buffer()->get_insert()->get_iter(), false); + auto last_selection_bound = std::make_shared(get_buffer()->get_selection_bound()->get_iter(), false); get_buffer()->signal_mark_set().connect([this, last_insert, last_selection_bound](const Gtk::TextIter &iter, const Glib::RefPtr &mark) mutable { if(mark->get_name() == "insert") { if(enable_multiple_cursors || enable_multiple_cursors_placements) { auto set_enable_multiple_cursors = enable_multiple_cursors; if(set_enable_multiple_cursors) enable_multiple_cursors = false; - auto offset_diff = mark->get_iter().get_offset() - last_insert->get_iter().get_offset(); + auto offset_diff = mark->get_iter().get_offset() - (*last_insert)->get_iter().get_offset(); if(offset_diff != 0) { for(auto &extra_cursor : extra_cursors) { auto iter = extra_cursor.insert->get_iter(); @@ -1258,7 +1258,7 @@ void Source::BaseView::setup_extra_cursor_signals() { if(set_enable_multiple_cursors) enable_multiple_cursors = true; } - get_buffer()->move_mark(last_insert, mark->get_iter()); + get_buffer()->move_mark(*last_insert, mark->get_iter()); } if(mark->get_name() == "selection_bound") { @@ -1266,7 +1266,7 @@ void Source::BaseView::setup_extra_cursor_signals() { auto set_enable_multiple_cursors = enable_multiple_cursors; if(set_enable_multiple_cursors) enable_multiple_cursors = false; - auto offset_diff = mark->get_iter().get_offset() - last_selection_bound->get_iter().get_offset(); + auto offset_diff = mark->get_iter().get_offset() - (*last_selection_bound)->get_iter().get_offset(); if(offset_diff != 0) { for(auto &extra_cursor : extra_cursors) { auto iter = extra_cursor.selection_bound->get_iter(); @@ -1278,7 +1278,7 @@ void Source::BaseView::setup_extra_cursor_signals() { if(set_enable_multiple_cursors) enable_multiple_cursors = true; } - get_buffer()->move_mark(last_selection_bound, mark->get_iter()); + get_buffer()->move_mark(*last_selection_bound, mark->get_iter()); } }); @@ -1540,7 +1540,7 @@ void Source::BaseView::insert_snippet(Gtk::TextIter iter, const std::string &sni parameter_offsets_and_sizes_map.erase(it); } - auto mark = get_buffer()->create_mark(iter); + Mark mark(iter); get_source_buffer()->begin_user_action(); Gtk::TextIter start, end; @@ -1563,7 +1563,6 @@ void Source::BaseView::insert_snippet(Gtk::TextIter iter, const std::string &sni get_source_buffer()->end_user_action(); iter = mark->get_iter(); - get_buffer()->delete_mark(mark); for(auto ¶meter_offsets_and_sized : parameter_offsets_and_sizes_map) { snippet_parameters_list.emplace_back(); for(auto &offsets : parameter_offsets_and_sized.second) { @@ -1660,8 +1659,8 @@ bool Source::BaseView::clear_snippet_marks() { Source::BaseView::ExtraCursor::ExtraCursor(const Glib::RefPtr &extra_cursor_selection, const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter, bool snippet, int line_offset) : extra_cursor_selection(extra_cursor_selection), - insert(start_iter.get_buffer()->create_mark(start_iter, false)), - selection_bound(end_iter.get_buffer()->create_mark(end_iter, false)), + insert(start_iter, false), + selection_bound(end_iter, false), line_offset(line_offset), snippet(snippet) { insert->set_visible(true); if(start_iter != end_iter) @@ -1671,8 +1670,6 @@ Source::BaseView::ExtraCursor::ExtraCursor(const Glib::RefPtr &ext Source::BaseView::ExtraCursor::~ExtraCursor() { insert->get_buffer()->remove_tag(extra_cursor_selection, insert->get_iter(), selection_bound->get_iter()); insert->set_visible(false); - insert->get_buffer()->delete_mark(insert); - selection_bound->get_buffer()->delete_mark(selection_bound); } void Source::BaseView::ExtraCursor::move(const Gtk::TextIter &iter, bool selection_activated) { diff --git a/src/source_base.hpp b/src/source_base.hpp index 5490910..f268ff6 100644 --- a/src/source_base.hpp +++ b/src/source_base.hpp @@ -11,6 +11,22 @@ #include namespace Source { + /// RAII-style text mark. Use instead of Gtk::TextBuffer::create_mark and Gtk::TextBuffer::delete_mark, + /// since Gtk::TextBuffer::delete_mark is not called upon Glib::RefPtr deletion + class Mark : public Glib::RefPtr { + public: + Mark(const Gtk::TextIter &iter, bool left_gravity = true) : Glib::RefPtr(iter.get_buffer()->create_mark(iter, left_gravity)) {} + Mark() = default; + Mark(Mark &&) = default; + Mark &operator=(Mark &&) = default; + Mark(const Mark &) = delete; + Mark &operator=(const Mark &) = delete; + ~Mark() { + if(*this) + (*this)->get_buffer()->delete_mark(*this); + } + }; + class SearchView : public Gsv::View { public: SearchView(); @@ -162,8 +178,8 @@ namespace Source { void move(const Gtk::TextIter &iter, bool selection_activated); void move_selection_bound(const Gtk::TextIter &iter); - Glib::RefPtr insert; - Glib::RefPtr selection_bound; + Mark insert; + Mark selection_bound; /// Used when moving cursor up/down lines int line_offset; /// Set to true when the extra cursor corresponds to a snippet parameter @@ -181,12 +197,8 @@ namespace Source { class SnippetParameter { public: SnippetParameter(const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter) - : start(start_iter.get_buffer()->create_mark(start_iter, false)), end(end_iter.get_buffer()->create_mark(end_iter, false)), size(end_iter.get_offset() - start_iter.get_offset()) {} - ~SnippetParameter() { - start->get_buffer()->delete_mark(start); - end->get_buffer()->delete_mark(end); - } - Glib::RefPtr start, end; + : start(start_iter, false), end(end_iter, false), size(end_iter.get_offset() - start_iter.get_offset()) {} + Mark start, end; /// Used to check if the parameter has been deleted, and should be passed on next tab int size; }; diff --git a/src/source_clang.cpp b/src/source_clang.cpp index ff6d7a4..323866f 100644 --- a/src/source_clang.cpp +++ b/src/source_clang.cpp @@ -511,7 +511,11 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) type_tooltips.emplace_back(this, start, end, [this, token](Tooltip &tooltip) { auto cursor = token.get_cursor(); - tooltip.buffer->insert(tooltip.buffer->get_insert()->get_iter(), "Type: " + cursor.get_type_description()); + auto type_description = cursor.get_type_description(); + size_t pos = 0; + while((pos = type_description.find("::__1", pos)) != std::string::npos) + type_description.erase(pos, 5); + tooltip.insert_code(type_description, std::string{}); auto brief_comment = cursor.get_brief_comments(); if(brief_comment != "") tooltip.insert_with_links_tagged("\n\n" + brief_comment); @@ -647,7 +651,12 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) next_char_iter++; debug_value.replace(iter, next_char_iter, "?"); } - tooltip.buffer->insert(tooltip.buffer->get_insert()->get_iter(), (tooltip.buffer->size() > 0 ? "\n\n" : "") + value_type + ": " + debug_value.substr(pos + 3, debug_value.size() - (pos + 3) - 1)); + tooltip.buffer->insert(tooltip.buffer->get_insert()->get_iter(), (tooltip.buffer->size() > 0 ? "\n\n" : "") + value_type + ":\n"); + auto value = debug_value.substr(pos + 3, debug_value.size() - (pos + 3) - 1); + size_t pos = 0; + while((pos = value.find("::__1", pos)) != std::string::npos) + value.erase(pos, 5); + tooltip.insert_code(value, {}); } } } @@ -820,7 +829,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa if(this->language && (this->language->get_id() == "chdr" || this->language->get_id() == "cpphdr")) clangmm::remove_include_guard(buffer); code_complete_results = std::make_unique(clang_tu->get_code_completions(buffer, line_number, column)); - if(code_complete_results->cx_results == nullptr) { + if(!code_complete_results->cx_results) { auto expected = ParseState::processing; parse_state.compare_exchange_strong(expected, ParseState::restarting); return; diff --git a/src/source_language_protocol.cpp b/src/source_language_protocol.cpp index 6ce99e8..a5fae17 100644 --- a/src/source_language_protocol.cpp +++ b/src/source_language_protocol.cpp @@ -1092,7 +1092,7 @@ void Source::LanguageProtocolView::update_diagnostics(std::vectorget_iter(), mark.end->get_iter(), false, [](Tooltip &tooltip) { + add_diagnostic_tooltip(mark.first->get_iter(), mark.second->get_iter(), false, [](Tooltip &tooltip) { tooltip.buffer->insert_at_cursor(type_coverage_message); }); num_warnings++; @@ -1131,7 +1131,7 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect // hover result structure vary significantly from the different language servers struct Content { std::string value; - bool markdown; + std::string kind; }; std::vector contents; auto contents_pt = result.get_child_optional("contents"); @@ -1139,20 +1139,28 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect return; auto value = contents_pt->get_value(""); if(!value.empty()) - contents.emplace_back(Content{value, true}); + contents.emplace_back(Content{value, "markdown"}); else { auto value_pt = contents_pt->get_optional("value"); - if(value_pt) - contents.emplace_back(Content{*value_pt, contents_pt->get("kind", "") == "markdown"}); + if(value_pt) { + auto kind = contents_pt->get("kind", ""); + if(kind.empty()) + kind = contents_pt->get("language", ""); + contents.emplace_back(Content{*value_pt, kind}); + } else { for(auto it = contents_pt->begin(); it != contents_pt->end(); ++it) { auto value = it->second.get("value", ""); - if(!value.empty()) - contents.emplace_back(Content{value, contents_pt->get("kind", "") == "markdown"}); + if(!value.empty()) { + auto kind = it->second.get("kind", ""); + if(kind.empty()) + kind = it->second.get("language", ""); + contents.emplace_back(Content{value, kind}); + } else { value = it->second.get_value(""); if(!value.empty()) - contents.emplace_back(Content{value, true}); + contents.emplace_back(Content{value, "markdown"}); } } } @@ -1172,12 +1180,13 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect for(size_t i = 0; i < contents.size(); i++) { if(i > 0) tooltip.buffer->insert_at_cursor("\n\n"); - if(contents[i].markdown && language_id != "python") // TODO: python-language-server might support markdown in the future - tooltip.insert_markdown(contents[i].value); - else { + if(contents[i].kind == "plaintext" || contents[i].kind.empty()) tooltip.insert_with_links_tagged(contents[i].value); - tooltip.remove_trailing_newlines(); - } + else if(contents[i].kind == "markdown") + tooltip.insert_markdown(contents[i].value); + else + tooltip.insert_code(contents[i].value, contents[i].kind); + tooltip.remove_trailing_newlines(); } #ifdef JUCI_ENABLE_DEBUG @@ -1224,7 +1233,8 @@ void Source::LanguageProtocolView::show_type_tooltips(const Gdk::Rectangle &rect next_char_iter++; debug_value.replace(iter, next_char_iter, "?"); } - tooltip.buffer->insert_at_cursor("\n\n" + value_type + ": " + debug_value.substr(pos + 3, debug_value.size() - (pos + 3) - 1)); + tooltip.buffer->insert_at_cursor("\n\n" + value_type + ":\n"); + tooltip.insert_code(debug_value.substr(pos + 3, debug_value.size() - (pos + 3) - 1), {}); } } } @@ -1514,20 +1524,17 @@ void Source::LanguageProtocolView::setup_autocomplete() { for(auto parameter_it = parameters.begin(); parameter_it != parameters.end(); ++parameter_it) { auto label = parameter_it->second.get("label", ""); auto insert = label; - auto plaintext = parameter_it->second.get("documentation", ""); - std::string markdown; - if(plaintext.empty()) { - auto documentation = parameter_it->second.get_child_optional("documentation"); - if(documentation) { - auto kind = documentation->get("kind", ""); - if(kind == "markdown") - markdown = documentation->get("value", ""); - else - plaintext = documentation->get("value", ""); + auto documentation = parameter_it->second.get("documentation", ""); + std::string kind; + if(documentation.empty()) { + auto documentation_pt = parameter_it->second.get_child_optional("documentation"); + if(documentation_pt) { + documentation = documentation_pt->get("value", ""); + kind = documentation_pt->get("kind", ""); } } autocomplete->rows.emplace_back(std::move(label)); - autocomplete_rows.emplace_back(AutocompleteRow{std::move(insert), std::move(plaintext), std::move(markdown)}); + autocomplete_rows.emplace_back(AutocompleteRow{std::move(insert), {}, std::move(documentation), std::move(kind)}); } } } @@ -1550,16 +1557,13 @@ void Source::LanguageProtocolView::setup_autocomplete() { for(auto it = begin; it != end; ++it) { auto label = it->second.get("label", ""); auto detail = it->second.get("detail", ""); - auto plaintext = it->second.get("documentation", ""); - std::string markdown; - if(plaintext.empty()) { - auto documentation = it->second.get_child_optional("documentation"); - if(documentation) { - auto kind = documentation->get("kind", ""); - if(kind == "markdown") - markdown = documentation->get("value", ""); - else - plaintext = documentation->get("value", ""); + auto documentation = it->second.get("documentation", ""); + std::string kind; + if(documentation.empty()) { + auto documentation_pt = it->second.get_child_optional("documentation"); + if(documentation_pt) { + documentation = documentation_pt->get("value", ""); + kind = documentation_pt->get("kind", ""); } } auto insert = it->second.get("insertText", ""); @@ -1600,12 +1604,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { } if(starts_with(label, prefix)) { autocomplete->rows.emplace_back(std::move(label)); - if(!plaintext.empty() && detail != plaintext) { - if(!detail.empty()) - detail += "\n\n"; - detail += plaintext; - } - autocomplete_rows.emplace_back(AutocompleteRow{std::move(insert), std::move(detail), std::move(markdown)}); + autocomplete_rows.emplace_back(AutocompleteRow{std::move(insert), std::move(detail), std::move(documentation), std::move(kind)}); } } } @@ -1621,7 +1620,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { for(auto &snippet : *snippets) { if(starts_with(snippet.prefix, prefix)) { autocomplete->rows.emplace_back(snippet.prefix); - autocomplete_rows.emplace_back(AutocompleteRow{snippet.body, snippet.description, {}}); + autocomplete_rows.emplace_back(AutocompleteRow{snippet.body, {}, snippet.description, {}}); } } } @@ -1691,17 +1690,23 @@ void Source::LanguageProtocolView::setup_autocomplete() { }; autocomplete->set_tooltip_buffer = [this](unsigned int index) -> std::function { - auto plaintext = autocomplete_rows[index].plaintext; - auto markdown = autocomplete_rows[index].markdown; - if(plaintext.empty() && markdown.empty()) + auto autocomplete = autocomplete_rows[index]; + if(autocomplete.detail.empty() && autocomplete.documentation.empty()) return nullptr; - return [plaintext = std::move(plaintext), markdown = std::move(markdown)](Tooltip &tooltip) { - if(!plaintext.empty()) - tooltip.insert_with_links_tagged(plaintext); - if(!markdown.empty()) { - if(!plaintext.empty()) + return [this, autocomplete = std::move(autocomplete)](Tooltip &tooltip) { + if(!autocomplete.detail.empty()) { + tooltip.insert_code(autocomplete.detail, language ? language->get_id().raw() : std::string{}); + tooltip.remove_trailing_newlines(); + } + if(!autocomplete.documentation.empty()) { + if(tooltip.buffer->size() > 0) tooltip.buffer->insert_at_cursor("\n\n"); - tooltip.insert_markdown(markdown); + if(autocomplete.kind == "plaintext" || autocomplete.kind.empty()) + tooltip.insert_with_links_tagged(autocomplete.documentation); + else if(autocomplete.kind == "markdown") + tooltip.insert_markdown(autocomplete.documentation); + else + tooltip.insert_code(autocomplete.documentation, autocomplete.kind); } }; }; diff --git a/src/source_language_protocol.hpp b/src/source_language_protocol.hpp index 4b6cc1b..bd76f3c 100644 --- a/src/source_language_protocol.hpp +++ b/src/source_language_protocol.hpp @@ -200,8 +200,9 @@ namespace Source { struct AutocompleteRow { std::string insert; - std::string plaintext; - std::string markdown; + std::string detail; + std::string documentation; + std::string kind; }; std::vector autocomplete_rows; @@ -214,17 +215,7 @@ namespace Source { std::vector last_diagnostics; sigc::connection update_type_coverage_connection; - class TypeCoverageMark { - public: - TypeCoverageMark(const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter) - : start(start_iter.get_buffer()->create_mark(start_iter)), end(end_iter.get_buffer()->create_mark(end_iter)) {} - ~TypeCoverageMark() { - start->get_buffer()->delete_mark(start); - end->get_buffer()->delete_mark(end); - } - Glib::RefPtr start, end; - }; - std::list type_coverage_marks; + std::list> type_coverage_marks; size_t num_warnings = 0, num_errors = 0, num_fix_its = 0; void update_type_coverage(); std::atomic update_type_coverage_retries = {60}; diff --git a/src/source_spellcheck.cpp b/src/source_spellcheck.cpp index 3846ece..7525d94 100644 --- a/src/source_spellcheck.cpp +++ b/src/source_spellcheck.cpp @@ -7,7 +7,7 @@ AspellConfig *Source::SpellCheckView::spellcheck_config = nullptr; Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path, const Glib::RefPtr &language) : BaseView(file_path, language) { - if(spellcheck_config == nullptr) + if(!spellcheck_config) spellcheck_config = new_aspell_config(); spellcheck_checker = nullptr; spellcheck_error_tag = get_buffer()->create_tag("spellcheck_error"); @@ -29,7 +29,7 @@ Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path, }); get_buffer()->signal_changed().connect([this]() { - if(spellcheck_checker == nullptr) + if(!spellcheck_checker) return; delayed_spellcheck_suggestions_connection.disconnect(); @@ -129,29 +129,29 @@ Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path, // In case of for instance text paste or undo/redo get_buffer()->signal_insert().connect([this](const Gtk::TextIter &start_iter, const Glib::ustring &inserted_string, int) { - if(spellcheck_checker == nullptr) + if(!spellcheck_checker) return; - if(!disable_spellcheck) - return; - - auto iter = start_iter; - if(!is_word_iter(iter) && !iter.starts_line()) - iter.backward_char(); - if(is_word_iter(iter)) { - auto word = get_word(iter); - get_buffer()->remove_tag(spellcheck_error_tag, word.first, word.second); + if(disable_spellcheck) { + auto iter = start_iter; + if(!is_word_iter(iter) && !iter.starts_line()) + iter.backward_char(); + if(is_word_iter(iter)) { + auto word = get_word(iter); + get_buffer()->remove_tag(spellcheck_error_tag, word.first, word.second); + } } }, false); get_buffer()->signal_mark_set().connect([this](const Gtk::TextIter &iter, const Glib::RefPtr &mark) { - if(spellcheck_checker == nullptr) - return; - if(mark->get_name() == "insert") { if(SelectionDialog::get()) SelectionDialog::get()->hide(); + if(!spellcheck_checker) + return; delayed_spellcheck_suggestions_connection.disconnect(); + if(get_buffer()->get_has_selection()) + return; delayed_spellcheck_suggestions_connection = Glib::signal_timeout().connect([this]() { if(get_buffer()->get_insert()->get_iter().has_tag(spellcheck_error_tag)) { SelectionDialog::create(this, false); @@ -182,13 +182,6 @@ Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path, } }); - get_buffer()->signal_mark_set().connect([](const Gtk::TextIter &iterator, const Glib::RefPtr &mark) { - if(mark->get_name() == "insert") { - if(SelectionDialog::get()) - SelectionDialog::get()->hide(); - } - }); - signal_focus_out_event().connect([this](GdkEventFocus *event) { delayed_spellcheck_suggestions_connection.disconnect(); return false; @@ -221,7 +214,7 @@ Source::SpellCheckView::~SpellCheckView() { delayed_spellcheck_suggestions_connection.disconnect(); delayed_spellcheck_error_clear.disconnect(); - if(spellcheck_checker != nullptr) + if(spellcheck_checker) delete_aspell_speller(spellcheck_checker); signal_tag_added_connection.disconnect(); @@ -234,7 +227,7 @@ void Source::SpellCheckView::configure() { aspell_config_replace(spellcheck_config, "encoding", "utf-8"); } spellcheck_possible_err = new_aspell_speller(spellcheck_config); - if(spellcheck_checker != nullptr) + if(spellcheck_checker) delete_aspell_speller(spellcheck_checker); spellcheck_checker = nullptr; if(aspell_error_number(spellcheck_possible_err) != 0) @@ -251,7 +244,7 @@ void Source::SpellCheckView::hide_dialogs() { } void Source::SpellCheckView::spellcheck(const Gtk::TextIter &start, const Gtk::TextIter &end) { - if(spellcheck_checker == nullptr) + if(!spellcheck_checker) return; auto iter = start; while(iter && iter < end) { @@ -550,9 +543,8 @@ std::vector Source::SpellCheckView::get_spellcheck_suggestions(cons std::vector words; const char *word; - while((word = aspell_string_enumeration_next(elements)) != nullptr) { + while((word = aspell_string_enumeration_next(elements))) words.emplace_back(word); - } delete_aspell_string_enumeration(elements); return words; diff --git a/src/terminal.cpp b/src/terminal.cpp index a09719a..8d17097 100644 --- a/src/terminal.cpp +++ b/src/terminal.cpp @@ -277,13 +277,12 @@ size_t Terminal::print(const std::string &message, bool bold) { umessage.replace(iter, next_char_iter, "?"); } - auto start_mark = get_buffer()->create_mark(get_buffer()->get_iter_at_line(get_buffer()->end().get_line())); + Source::Mark start_mark(get_buffer()->get_iter_at_line(get_buffer()->end().get_line())); if(bold) get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag); else get_buffer()->insert(get_buffer()->end(), umessage); auto start_iter = start_mark->get_iter(); - get_buffer()->delete_mark(start_mark); auto end_iter = get_buffer()->get_insert()->get_iter(); apply_link_tags(start_iter, end_iter); diff --git a/src/tooltips.cpp b/src/tooltips.cpp index e2b868f..321fbb4 100644 --- a/src/tooltips.cpp +++ b/src/tooltips.cpp @@ -11,34 +11,30 @@ std::set Tooltips::shown_tooltips; Gdk::Rectangle Tooltips::drawn_tooltips_rectangle = Gdk::Rectangle(); -Tooltip::Tooltip(Gtk::TextView *text_view, const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter, std::function set_buffer_) - : start_mark(start_iter.get_buffer()->create_mark(start_iter)), end_mark(end_iter.get_buffer()->create_mark(end_iter)), text_view(text_view), set_buffer(std::move(set_buffer_)) {} +Tooltip::Tooltip(Gsv::View *view, const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter, std::function set_buffer_) + : start_mark(start_iter), end_mark(end_iter), view(view), set_buffer(std::move(set_buffer_)) {} Tooltip::Tooltip(std::function set_buffer_) - : text_view(nullptr), set_buffer(std::move(set_buffer_)) {} + : view(nullptr), set_buffer(std::move(set_buffer_)) {} Tooltip::~Tooltip() { Tooltips::shown_tooltips.erase(this); - if(text_view) { - text_view->get_buffer()->delete_mark(start_mark); - text_view->get_buffer()->delete_mark(end_mark); - } } void Tooltip::update() { - if(text_view) { + if(view) { auto iter = start_mark->get_iter(); auto end_iter = end_mark->get_iter(); - text_view->get_iter_location(iter, activation_rectangle); + view->get_iter_location(iter, activation_rectangle); if(iter.get_offset() < end_iter.get_offset()) { while(iter.forward_char() && iter != end_iter) { Gdk::Rectangle rectangle; - text_view->get_iter_location(iter, rectangle); + view->get_iter_location(iter, rectangle); activation_rectangle.join(rectangle); } } int location_window_x, location_window_y; - text_view->buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, activation_rectangle.get_x(), activation_rectangle.get_y(), location_window_x, location_window_y); + view->buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, activation_rectangle.get_x(), activation_rectangle.get_y(), location_window_x, location_window_y); activation_rectangle.set_x(location_window_x); activation_rectangle.set_y(location_window_y); } @@ -82,20 +78,18 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) buffer = Gtk::TextBuffer::create(); - if(text_view) { - auto tag = text_view->get_buffer()->get_tag_table()->lookup("def:warning"); - auto new_tag = buffer->create_tag("def:warning"); - if(tag->property_foreground_set()) - new_tag->property_foreground_rgba() = tag->property_foreground_rgba().get_value(); - if(tag->property_background_set()) - new_tag->property_background_rgba() = tag->property_background_rgba().get_value(); - - tag = text_view->get_buffer()->get_tag_table()->lookup("def:error"); - new_tag = buffer->create_tag("def:error"); - if(tag->property_foreground_set()) - new_tag->property_foreground_rgba() = tag->property_foreground_rgba().get_value(); - if(tag->property_background_set()) - new_tag->property_background_rgba() = tag->property_background_rgba().get_value(); + if(view) { + auto create_tag_from_style = [this](const std::string &style_name) { + if(auto style = view->get_source_buffer()->get_style_scheme()->get_style(style_name)) { + auto tag = buffer->create_tag(style_name); + if(style->property_foreground_set()) + tag->property_foreground() = style->property_foreground(); + if(style->property_background_set()) + tag->property_background() = style->property_background(); + } + }; + create_tag_from_style("def:warning"); + create_tag_from_style("def:error"); } link_tag = buffer->create_tag("link"); link_tag->property_underline() = Pango::Underline::UNDERLINE_SINGLE; @@ -164,8 +158,8 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) std::smatch sm; if(std::regex_match(link, sm, regex)) { auto path = boost::filesystem::path(sm[1].str()); - if(auto view = dynamic_cast(text_view)) - path = filesystem::get_normal_path(view->file_path.parent_path() / path); + if(auto source_view = dynamic_cast(view)) + path = filesystem::get_normal_path(source_view->file_path.parent_path() / path); boost::system::error_code ec; if(boost::filesystem::is_regular_file(path, ec)) { @@ -185,8 +179,8 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) } auto path = boost::filesystem::path(link); - if(auto view = dynamic_cast(text_view)) - path = filesystem::get_normal_path(view->file_path.parent_path() / path); + if(auto source_view = dynamic_cast(view)) + path = filesystem::get_normal_path(source_view->file_path.parent_path() / path); boost::system::error_code ec; if(boost::filesystem::is_regular_file(path, ec) && Notebook::get().open(path)) return true; @@ -206,13 +200,14 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) // Add ScrolledWindow if needed Gtk::Widget *widget = tooltip_text_view; + auto screen_width = Gdk::Screen::get_default()->get_width(); auto screen_height = Gdk::Screen::get_default()->get_height(); - if(size.second > screen_height - 6 /* 2xpadding */) { + if(size.first > screen_width - 6 /* 2xpadding */ || size.second > screen_height - 6 /* 2xpadding */) { auto scrolled_window = Gtk::manage(new Gtk::ScrolledWindow()); - scrolled_window->property_hscrollbar_policy() = Gtk::PolicyType::POLICY_NEVER; - scrolled_window->property_vscrollbar_policy() = Gtk::PolicyType::POLICY_AUTOMATIC; + scrolled_window->property_hscrollbar_policy() = size.first > screen_width - 6 ? Gtk::PolicyType::POLICY_AUTOMATIC : Gtk::PolicyType::POLICY_NEVER; + scrolled_window->property_vscrollbar_policy() = size.second > screen_height - 6 ? Gtk::PolicyType::POLICY_AUTOMATIC : Gtk::PolicyType::POLICY_NEVER; scrolled_window->add(*tooltip_text_view); - scrolled_window->set_size_request(-1, screen_height - 6 /* 2xpadding */); + scrolled_window->set_size_request(size.first > screen_width - 6 ? screen_width - 6 : -1, size.second > screen_height - 6 ? screen_height - 6 : -1); widget = scrolled_window; } @@ -225,7 +220,7 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) #endif window->signal_realize().connect([this] { - if(!text_view) { + if(!view) { auto &dialog = SelectionDialog::get(); if(dialog && dialog->is_visible()) { int root_x, root_y; @@ -245,15 +240,15 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) return; // Do not show empty tooltips int root_x = 0, root_y = 0; - if(text_view) { + if(view) { Gdk::Rectangle visible_rect; - text_view->get_visible_rect(visible_rect); + view->get_visible_rect(visible_rect); int visible_window_x, visible_window_y; - text_view->buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, visible_rect.get_x(), visible_rect.get_y(), visible_window_x, visible_window_y); + view->buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, visible_rect.get_x(), visible_rect.get_y(), visible_window_x, visible_window_y); - auto window_x = std::max(activation_rectangle.get_x(), visible_window_x); // Adjust tooltip right if it is left of text_view + auto window_x = std::max(activation_rectangle.get_x(), visible_window_x); // Adjust tooltip right if it is left of view auto window_y = activation_rectangle.get_y(); - text_view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(window_x, window_y, root_x, root_y); + view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(window_x, window_y, root_x, root_y); root_x -= 3; // -1xpadding if(root_y < size.second) @@ -262,7 +257,7 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) // Move tooltip left if it is right of screen auto screen_width = Gdk::Screen::get_default()->get_width(); - rectangle.set_x(root_x + size.first > screen_width ? screen_width - size.first : root_x); + rectangle.set_x(root_x + size.first > screen_width ? std::max(0, screen_width - size.first) /* Move tooptip right if it is left of screen */ : root_x); } rectangle.set_width(size.first); @@ -292,9 +287,9 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) void Tooltip::hide(const boost::optional> &last_mouse_pos, const boost::optional> &mouse_pos) { // Keep tooltip if mouse is moving towards it // Calculated using dot product between the mouse_pos vector and the corners of the tooltip window - if(text_view && window && shown && last_mouse_pos && mouse_pos) { + if(view && window && shown && last_mouse_pos && mouse_pos) { static int root_x, root_y; - text_view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(last_mouse_pos->first, last_mouse_pos->second, root_x, root_y); + view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(last_mouse_pos->first, last_mouse_pos->second, root_x, root_y); int diff_x = mouse_pos->first - last_mouse_pos->first; int diff_y = mouse_pos->second - last_mouse_pos->second; class Corner { @@ -326,7 +321,7 @@ void Tooltip::wrap_lines() { while(true) { auto last_space = buffer->end(); bool long_line = true; - for(unsigned c = 0; c <= 80; c++) { + for(int c = 0; c <= max_columns; c++) { if(*iter == ' ') last_space = iter; if(iter.ends_line()) { @@ -337,7 +332,7 @@ void Tooltip::wrap_lines() { return; } if(long_line) { - if(!last_space) { // If word is longer than 80 + if(!last_space) { // If word is longer than max_columns while(true) { if(iter.ends_line()) break; @@ -366,21 +361,8 @@ void Tooltip::wrap_lines() { } } -void Tooltip::insert_with_links_tagged(const std::string &text) { - static std::regex http_regex("(https?://[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=]+[a-zA-Z0-9\\-_~/@$*+;=])"); - std::smatch sm; - std::sregex_iterator it(text.begin(), text.end(), http_regex); - std::sregex_iterator end; - size_t start_pos = 0; - for(; it != end; ++it) { - buffer->insert(buffer->get_insert()->get_iter(), &text[start_pos], &text[it->position()]); - buffer->insert_with_tag(buffer->get_insert()->get_iter(), &text[it->position()], &text[it->position() + it->length()], link_tag); - start_pos = it->position() + it->length(); - } - buffer->insert(buffer->get_insert()->get_iter(), &text[start_pos], &text[text.size()]); -} -void Tooltip::insert_markdown(const std::string &input) { +void Tooltip::create_tags() { if(!h1_tag) { h1_tag = buffer->create_tag(); h1_tag->property_weight() = Pango::WEIGHT_ULTRAHEAVY; @@ -419,6 +401,24 @@ void Tooltip::insert_markdown(const std::string &input) { strikethrough_tag = buffer->create_tag(); strikethrough_tag->property_strikethrough() = true; } +} + +void Tooltip::insert_with_links_tagged(const std::string &text) { + static std::regex http_regex("(https?://[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=]+[a-zA-Z0-9\\-_~/@$*+;=])"); + std::smatch sm; + std::sregex_iterator it(text.begin(), text.end(), http_regex); + std::sregex_iterator end; + size_t start_pos = 0; + for(; it != end; ++it) { + buffer->insert(buffer->get_insert()->get_iter(), &text[start_pos], &text[it->position()]); + buffer->insert_with_tag(buffer->get_insert()->get_iter(), &text[it->position()], &text[it->position() + it->length()], link_tag); + start_pos = it->position() + it->length(); + } + buffer->insert(buffer->get_insert()->get_iter(), &text[start_pos], &text[text.size()]); +} + +void Tooltip::insert_markdown(const std::string &input) { + create_tags(); size_t i = 0; @@ -705,6 +705,7 @@ void Tooltip::insert_markdown(const std::string &input) { if(starts_with(input, i, "```")) { auto i_saved = i; if(forward_to({'\n'})) { + auto language = input.substr(i_saved + 3, i - (i_saved + 3)); i++; if(i < input.size()) { auto start = i; @@ -717,7 +718,7 @@ void Tooltip::insert_markdown(const std::string &input) { auto end = buffer->end(); if(end.backward_char() && !end.starts_line()) buffer->insert_at_cursor("\n"); - buffer->insert_with_tag(buffer->get_insert()->get_iter(), input.substr(start, i - start), code_block_tag); + insert_code(input.substr(start, i - start), language, true); buffer->insert_at_cursor("\n"); i += 3; return true; @@ -781,6 +782,106 @@ void Tooltip::insert_markdown(const std::string &input) { remove_trailing_newlines(); } +void Tooltip::insert_code(const std::string &code, const boost::optional &language_identifier, bool block) { + create_tags(); + + auto insert_iter = buffer->get_insert()->get_iter(); + Source::Mark start_mark(insert_iter); + + bool style_format_type_description = false; + if(!block) { + auto pos = code.find("\n"); + if(pos != std::string::npos) + block = true; + else if(insert_iter == buffer->begin() && !block && utf8_character_count(code) > static_cast(max_columns)) { + block = true; + style_format_type_description = true; + } + } + + buffer->insert_with_tag(insert_iter, code, block ? code_block_tag : code_tag); + + if(view && language_identifier) { + auto tmp_view = Gsv::View(); + tmp_view.get_buffer()->set_text(code); + auto scheme = view->get_source_buffer()->get_style_scheme(); + tmp_view.get_source_buffer()->set_style_scheme(scheme); + Glib::RefPtr language; + if(!language_identifier->empty()) + language = Source::LanguageManager::get_default()->get_language(*language_identifier); + if(!language) { + if(auto source_view = dynamic_cast(view)) + language = source_view->language; + } + if(language) { + tmp_view.get_source_buffer()->set_language(language); + tmp_view.get_source_buffer()->set_highlight_syntax(true); + tmp_view.get_source_buffer()->ensure_highlight(tmp_view.get_buffer()->begin(), tmp_view.get_buffer()->end()); + + auto start_iter = start_mark->get_iter(); + tmp_view.get_buffer()->get_tag_table()->foreach([this, &tmp_view, &start_iter](const Glib::RefPtr &tmp_tag) { + if(tmp_tag->property_foreground_set()) { + auto tag = buffer->create_tag(); + tag->property_foreground_rgba() = tmp_tag->property_foreground_rgba().get_value(); + + auto tmp_iter = tmp_view.get_source_buffer()->begin(); + Gtk::TextIter tmp_start; + if(tmp_iter.starts_tag(tmp_tag)) + tmp_start = tmp_iter; + while(tmp_iter.forward_to_tag_toggle(tmp_tag)) { + if(tmp_iter.ends_tag(tmp_tag)) { + auto start = start_iter; + start.forward_chars(tmp_start.get_offset()); + auto end = start_iter; + end.forward_chars(tmp_iter.get_offset()); + buffer->apply_tag(tag, start, end); + } + else + tmp_start = tmp_iter; + } + } + }); + } + } + + // Make long type descriptions readable + if(style_format_type_description) { + int initial_max_columns = max_columns; + std::list open_brackets; + for(auto iter = start_mark->get_iter(); iter; iter.forward_char()) { + if(*iter == '(' || *iter == '[' || *iter == '{' || *iter == '<') + open_brackets.emplace_back(iter); + else if(*iter == ')' || *iter == ']' || *iter == '}' || *iter == '>') { + if(!open_brackets.empty()) + open_brackets.pop_back(); + } + else if(*iter == ',' && !open_brackets.empty()) { + auto line_offset = open_brackets.back()->get_iter().get_line_offset(); + if(iter.forward_char()) { + Source::Mark mark(iter); + auto previous = iter; + if(*iter == ' ' && iter.forward_char()) { + buffer->erase(previous, iter); + iter = mark->get_iter(); + } + buffer->insert(mark->get_iter(), '\n' + std::string(line_offset + 1, ' ')); + iter = mark->get_iter(); + } + } + else if(*iter == ' ' && open_brackets.empty() && iter.get_line_offset() + (buffer->end().get_offset() - iter.get_offset()) > initial_max_columns) { // Add new line between return value, parameters and specifiers + auto previous = iter; + if(iter.forward_char()) { + Source::Mark mark(iter); + buffer->erase(previous, iter); + buffer->insert(mark->get_iter(), "\n"); + iter = mark->get_iter(); + } + } + max_columns = std::max(max_columns, iter.get_line_offset() + 1); + } + } +} + void Tooltip::remove_trailing_newlines() { auto end = buffer->end(); while(end.starts_line() && end.backward_char()) { diff --git a/src/tooltips.hpp b/src/tooltips.hpp index fe6202f..b110b70 100644 --- a/src/tooltips.hpp +++ b/src/tooltips.hpp @@ -1,7 +1,8 @@ #pragma once +#include "source_base.hpp" #include #include -#include +#include #include #include #include @@ -9,7 +10,7 @@ class Tooltip { public: - Tooltip(Gtk::TextView *text_view, const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter, std::function set_buffer_); + Tooltip(Gsv::View *view, const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter, std::function set_buffer_); Tooltip(std::function set_buffer_); ~Tooltip(); @@ -18,22 +19,23 @@ public: void hide(const boost::optional> &last_mouse_pos, const boost::optional> &mouse_pos); Gdk::Rectangle activation_rectangle; - Glib::RefPtr start_mark; - Glib::RefPtr end_mark; + Source::Mark start_mark, end_mark; Glib::RefPtr buffer; void insert_with_links_tagged(const std::string &text); void insert_markdown(const std::string &text); + void insert_code(const std::string &code, const boost::optional &language_identifier, bool block = false); // Remove empty lines at end of buffer void remove_trailing_newlines(); private: std::unique_ptr window; - void wrap_lines(); - Gtk::TextView *text_view; + Gsv::View *view; std::function set_buffer; + /// Initial maximum number of characters on each line + int max_columns = 80; std::pair size; Gdk::Rectangle rectangle; @@ -52,6 +54,9 @@ private: std::map, std::string> links; std::map, std::string> reference_links; std::unordered_map references; + + void wrap_lines(); + void create_tags(); }; class Tooltips { diff --git a/src/window.cpp b/src/window.cpp index 17b1600..20b6fc7 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1266,7 +1266,7 @@ void Window::set_menu_actions() { std::set> buffers; std::set header_views; - std::vector, Glib::RefPtr>> fix_it_marks; + std::list> fix_it_marks; for(auto &fix_it : fix_its) { auto view = current_view; if(fix_it.path != current_view->file_path) { @@ -1278,27 +1278,24 @@ void Window::set_menu_actions() { } auto start_iter = view->get_iter_at_line_pos(fix_it.offsets.first.line, fix_it.offsets.first.index); auto end_iter = view->get_iter_at_line_pos(fix_it.offsets.second.line, fix_it.offsets.second.index); - fix_it_marks.emplace_back(view->get_buffer()->create_mark(start_iter), view->get_buffer()->create_mark(end_iter)); + fix_it_marks.emplace_back(start_iter, end_iter); buffers.emplace(view->get_buffer()); } for(auto &buffer : buffers) buffer->begin_user_action(); - for(size_t i = 0; i < fix_its.size(); ++i) { - auto buffer = fix_it_marks[i].first->get_buffer(); - if(fix_its[i].type == Source::FixIt::Type::insert) - buffer->insert(fix_it_marks[i].first->get_iter(), fix_its[i].source); - else if(fix_its[i].type == Source::FixIt::Type::replace) { - buffer->erase(fix_it_marks[i].first->get_iter(), fix_it_marks[i].second->get_iter()); - buffer->insert(fix_it_marks[i].first->get_iter(), fix_its[i].source); + auto fix_it_mark = fix_it_marks.begin(); + for(auto &fix_it : fix_its) { + auto buffer = fix_it_mark->first->get_buffer(); + if(fix_it.type == Source::FixIt::Type::insert) + buffer->insert(fix_it_mark->first->get_iter(), fix_it.source); + else if(fix_it.type == Source::FixIt::Type::replace) { + buffer->erase(fix_it_mark->first->get_iter(), fix_it_mark->second->get_iter()); + buffer->insert(fix_it_mark->first->get_iter(), fix_it.source); } - else if(fix_its[i].type == Source::FixIt::Type::erase) - buffer->erase(fix_it_marks[i].first->get_iter(), fix_it_marks[i].second->get_iter()); - } - for(auto &mark_pair : fix_it_marks) { - auto buffer = mark_pair.first->get_buffer(); - buffer->delete_mark(mark_pair.first); - buffer->delete_mark(mark_pair.second); + else if(fix_it.type == Source::FixIt::Type::erase) + buffer->erase(fix_it_mark->first->get_iter(), fix_it_mark->second->get_iter()); + ++fix_it_mark; } for(auto &buffer : buffers) buffer->end_user_action(); diff --git a/tests/stubs/selection_dialog.cpp b/tests/stubs/selection_dialog.cpp index 9d659a8..be32351 100644 --- a/tests/stubs/selection_dialog.cpp +++ b/tests/stubs/selection_dialog.cpp @@ -16,8 +16,6 @@ std::unique_ptr SelectionDialog::instance; SelectionDialog::SelectionDialog(Gtk::TextView *text_view, const boost::optional &start_iter, bool show_search_entry, bool use_markup) : SelectionDialogBase(text_view, start_iter, show_search_entry, use_markup) {} -SelectionDialogBase::~SelectionDialogBase() {} - bool SelectionDialog::on_key_press(GdkEventKey *key) { return true; } std::unique_ptr CompletionDialog::instance; diff --git a/tests/tooltips_test.cpp b/tests/tooltips_test.cpp index 6b1ad95..d89f73d 100644 --- a/tests/tooltips_test.cpp +++ b/tests/tooltips_test.cpp @@ -4,6 +4,7 @@ int main() { auto app = Gtk::Application::create(); + Gsv::init(); auto get_markdown_tooltip = [](const std::string &input) { auto tooltip = std::make_unique([&](Tooltip &tooltip) {