From ae1b183b876d27efa661d7bb1cce97cd2cc99fba Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 21 Sep 2017 20:01:19 +0200 Subject: [PATCH] Added current parameter completion that shows possible arguments. Also fixes some issues with non-interactive completion, optimizations to selection/completion dialogs, and some cleanup." --- libclangmm | 2 +- src/CMakeLists.txt | 1 + src/autocomplete.cc | 165 +++++++++++++++++++++++++++ src/autocomplete.h | 182 ++---------------------------- src/debug_lldb.h | 2 + src/directories.cc | 50 ++++----- src/project.cc | 28 +++-- src/selection_dialog.cc | 35 +++--- src/selection_dialog.h | 11 +- src/source.cc | 35 +++--- src/source_clang.cc | 237 +++++++++++++++++++++++++++++---------- src/source_clang.h | 13 +-- src/source_diff.cc | 5 +- src/source_spellcheck.cc | 4 +- src/window.cc | 108 +++++++++--------- 15 files changed, 507 insertions(+), 371 deletions(-) create mode 100644 src/autocomplete.cc diff --git a/libclangmm b/libclangmm index d978517..0ab1692 160000 --- a/libclangmm +++ b/libclangmm @@ -1 +1 @@ -Subproject commit d978517ec201cb2421168417124de12ff7ef8457 +Subproject commit 0ab1692d125b1e3b742a4b43abe37fc2e84e5f2c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1c54ffe..ff9f3df 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,7 @@ set(project_files #Files used both in ../src and ../tests set(project_shared_files + autocomplete.cc cmake.cc compile_commands.cc ctags.cc diff --git a/src/autocomplete.cc b/src/autocomplete.cc new file mode 100644 index 0000000..34ef1a6 --- /dev/null +++ b/src/autocomplete.cc @@ -0,0 +1,165 @@ +#include "autocomplete.h" +#include "selection_dialog.h" + +Autocomplete::Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word) + : view(view), interactive_completion(interactive_completion), strip_word(strip_word), state(State::IDLE) { + view->get_buffer()->signal_changed().connect([this, &last_keyval] { + if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) { + cancel_reparse(); + return; + } + if(!this->view->has_focus()) + return; + if(is_continue_key(last_keyval) && (this->interactive_completion || state != State::IDLE)) + run(); + else { + stop(); + + if(is_restart_key(last_keyval) && this->interactive_completion) + run(); + } + }); + + view->get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr &mark) { + if(mark->get_name() == "insert") + stop(); + }); + + view->signal_key_release_event().connect([this](GdkEventKey *key) { + if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) { + if(CompletionDialog::get()->on_key_release(key)) + return true; + } + return false; + }, false); + + view->signal_focus_out_event().connect([this](GdkEventFocus *event) { + stop(); + return false; + }); +} + +void Autocomplete::run() { + if(run_check()) { + if(!is_processing()) + return; + + if(state == State::CANCELED) + state = State::RESTARTING; + + if(state != State::IDLE) + return; + + state = State::STARTING; + + before_add_rows(); + + if(thread.joinable()) + thread.join(); + auto buffer = view->get_buffer()->get_text(); + auto iter = view->get_buffer()->get_insert()->get_iter(); + auto line_nr = iter.get_line() + 1; + auto column_nr = iter.get_line_index() + 1; + if(strip_word) { + auto pos = iter.get_offset() - 1; + while(pos >= 0 && ((buffer[pos] >= 'a' && buffer[pos] <= 'z') || (buffer[pos] >= 'A' && buffer[pos] <= 'Z') || + (buffer[pos] >= '0' && buffer[pos] <= '9') || buffer[pos] == '_')) { + buffer.replace(pos, 1, " "); + column_nr--; + pos--; + } + } + thread = std::thread([this, line_nr, column_nr, buffer = std::move(buffer)] { + auto lock = get_parse_lock(); + if(!is_processing()) + return; + stop_parse(); + + auto &buffer_raw = const_cast(buffer.raw()); + rows.clear(); + add_rows(buffer_raw, line_nr, column_nr); + + if(is_processing()) { + dispatcher.post([this]() { + after_add_rows(); + if(state == State::RESTARTING) { + state = State::IDLE; + reparse(); + run(); + } + else if(state == State::CANCELED || rows.empty()) { + state = State::IDLE; + reparse(); + } + else { + auto start_iter = view->get_buffer()->get_insert()->get_iter(); + if(prefix.size() > 0 && !start_iter.backward_chars(prefix.size())) { + state = State::IDLE; + reparse(); + return; + } + CompletionDialog::create(view, view->get_buffer()->create_mark(start_iter)); + setup_initial_dialog(); + setup_dialog(); + for(auto &row : rows) { + CompletionDialog::get()->add_row(row.first); + row.first.clear(); + } + state = State::IDLE; + + view->get_buffer()->begin_user_action(); + CompletionDialog::get()->show(); + } + }); + } + else { + dispatcher.post([this] { + state = State::CANCELED; + on_add_rows_error(); + }); + } + }); + } + + if(state != State::IDLE) + cancel_reparse(); +} + +void Autocomplete::stop() { + if(state == State::STARTING || state == State::RESTARTING) + state = State::CANCELED; +} + +void Autocomplete::setup_initial_dialog() { + CompletionDialog::get()->on_hide = [this]() { + view->get_buffer()->end_user_action(); + tooltips.hide(); + tooltips.clear(); + reparse(); + }; + + CompletionDialog::get()->on_changed = [this](unsigned int index, const std::string &text) { + if(index >= rows.size()) { + tooltips.hide(); + return; + } + auto tooltip = rows[index].second; + if(tooltip.empty()) + tooltips.hide(); + else { + tooltips.clear(); + auto create_tooltip_buffer = [ this, tooltip = std::move(tooltip) ]() { + auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()); + + tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), tooltip, "def:note"); + + return tooltip_buffer; + }; + + auto iter = CompletionDialog::get()->start_mark->get_iter(); + tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter)); + + tooltips.show(true); + } + }; +} diff --git a/src/autocomplete.h b/src/autocomplete.h index 457f335..562e417 100644 --- a/src/autocomplete.h +++ b/src/autocomplete.h @@ -1,13 +1,13 @@ #ifndef JUCI_AUTOCOMPLETE_H_ #define JUCI_AUTOCOMPLETE_H_ #include "dispatcher.h" -#include "selection_dialog.h" #include "tooltips.h" +#include #include -template class Autocomplete { Gtk::TextView *view; + bool &interactive_completion; /// Some libraries/utilities, like libclang, require that autocomplete is started at the beginning of a word bool strip_word; @@ -18,7 +18,7 @@ public: std::string prefix; std::mutex prefix_mutex; - std::unordered_map> rows; + std::vector> rows; Tooltips tooltips; std::atomic state; @@ -35,180 +35,22 @@ public: std::function is_restart_key = [](guint) { return false; }; std::function run_check = [] { return false; }; - std::function before_get_suggestions = [] {}; - std::function after_get_suggestions = [] {}; - std::function on_get_suggestions_error = [] {}; + std::function before_add_rows = [] {}; + std::function after_add_rows = [] {}; + std::function on_add_rows_error = [] {}; - std::function>(std::string &buffer, int line_number, int column)> get_suggestions = [](std::string &, int, int) { return std::make_shared>(); }; - - std::function foreach_suggestion = [](Suggestion &) {}; + /// The handler is not run in the main loop. + std::function add_rows = [](std::string &, int, int) {}; std::function setup_dialog = [] {}; - Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word): view(view), strip_word(strip_word), state(State::IDLE) { - view->get_buffer()->signal_changed().connect([this, &interactive_completion, &last_keyval] { - if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) { - cancel_reparse(); - return; - } - if(!this->view->has_focus()) - return; - if(is_continue_key(last_keyval) && (interactive_completion || state != State::IDLE)) - run(); - else { - if(state == State::STARTING || state == State::RESTARTING) - state = State::CANCELED; - - if(is_restart_key(last_keyval) && interactive_completion) - run(); - } - }); - - view->get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr &mark) { - if(mark->get_name() == "insert") { - if(state == State::STARTING || state == State::RESTARTING) - state = State::CANCELED; - } - }); - - view->signal_key_release_event().connect([this](GdkEventKey *key) { - if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) { - if(CompletionDialog::get()->on_key_release(key)) - return true; - } - return false; - }, false); + Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word); - view->signal_focus_out_event().connect([this](GdkEventFocus *event) { - if(state == State::STARTING || state == State::RESTARTING) - state = State::CANCELED; - return false; - }); - } - - void run() { - if(run_check()) { - if(!is_processing()) - return; - - if(state == State::CANCELED) - state = State::RESTARTING; - - if(state != State::IDLE) - return; - - state = State::STARTING; - - before_get_suggestions(); - - if(thread.joinable()) - thread.join(); - auto buffer = std::make_shared(view->get_buffer()->get_text()); - auto iter = view->get_buffer()->get_insert()->get_iter(); - auto line_nr = iter.get_line() + 1; - auto column_nr = iter.get_line_index() + 1; - if(strip_word) { - auto pos = iter.get_offset() - 1; - while(pos >= 0 && (((*buffer)[pos] >= 'a' && (*buffer)[pos] <= 'z') || ((*buffer)[pos] >= 'A' && (*buffer)[pos] <= 'Z') || - ((*buffer)[pos] >= '0' && (*buffer)[pos] <= '9') || (*buffer)[pos] == '_')) { - buffer->replace(pos, 1, " "); - column_nr--; - pos--; - } - } - thread = std::thread([this, line_nr, column_nr, buffer]() { - auto lock = get_parse_lock(); - if(!is_processing()) - return; - stop_parse(); - - auto &buffer_raw = const_cast(buffer->raw()); - auto suggestions = get_suggestions(buffer_raw, line_nr, column_nr); - - if(is_processing()) { - dispatcher.post([this, suggestions] { - if(state == State::CANCELED) { - after_get_suggestions(); - reparse(); - state = State::IDLE; - } - else if(state == State::RESTARTING) { - after_get_suggestions(); - reparse(); - state = State::IDLE; - run(); - } - else { - auto start_iter = view->get_buffer()->get_insert()->get_iter(); - if(prefix.size() > 0 && !start_iter.backward_chars(prefix.size())) - return; - CompletionDialog::create(view, view->get_buffer()->create_mark(start_iter)); - rows.clear(); - setup_initial_dialog(); - setup_dialog(); - - for(auto &suggestion : *suggestions) - foreach_suggestion(suggestion); - suggestions->clear(); - after_get_suggestions(); - state = State::IDLE; - - if(!rows.empty()) { - view->get_buffer()->begin_user_action(); - CompletionDialog::get()->show(); - } - else - reparse(); - } - }); - } - else { - dispatcher.post([this] { - state = State::CANCELED; - on_get_suggestions_error(); - }); - } - }); - } - - if(state != State::IDLE) - cancel_reparse(); - } + void run(); + void stop(); private: - void setup_initial_dialog() { - CompletionDialog::get()->on_hide = [this]() { - view->get_buffer()->end_user_action(); - tooltips.hide(); - tooltips.clear(); - reparse(); - }; - - CompletionDialog::get()->on_changed = [this](const std::string &selected) { - if(selected.empty()) { - tooltips.hide(); - return; - } - auto tooltip = std::make_shared(rows.at(selected).second); - if(tooltip->empty()) - tooltips.hide(); - else { - tooltips.clear(); - auto create_tooltip_buffer = [this, tooltip]() { - auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()); - - tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), *tooltip, "def:note"); - - return tooltip_buffer; - }; - - auto iter = CompletionDialog::get()->start_mark->get_iter(); - tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter)); - - tooltips.show(true); - } - }; - } + void setup_initial_dialog(); }; #endif // JUCI_AUTOCOMPLETE_H_ diff --git a/src/debug_lldb.h b/src/debug_lldb.h index ebf07a2..b36f4a5 100644 --- a/src/debug_lldb.h +++ b/src/debug_lldb.h @@ -40,7 +40,9 @@ namespace Debug { } std::unordered_map> on_start; + /// The handlers are not run in the main loop. std::unordered_map> on_exit; + /// The handlers are not run in the main loop. std::unordered_map> on_event; std::mutex mutex; diff --git a/src/directories.cc b/src/directories.cc index 7c426fc..2276c07 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -163,10 +163,9 @@ Directories::Directories() : Gtk::ListViewText(1) { if(menu_popup_row_path.empty()) return; EntryBox::get().clear(); - auto source_path=std::make_shared(menu_popup_row_path); - EntryBox::get().entries.emplace_back("", [this, source_path](const std::string &content) { - bool is_directory=boost::filesystem::is_directory(*source_path); - auto target_path = (is_directory ? *source_path : source_path->parent_path())/content; + EntryBox::get().entries.emplace_back("", [this, source_path=menu_popup_row_path](const std::string &content) { + bool is_directory=boost::filesystem::is_directory(source_path); + auto target_path = (is_directory ? source_path : source_path.parent_path())/content; if(!boost::filesystem::exists(target_path)) { if(filesystem::write(target_path, "")) { update(); @@ -206,10 +205,9 @@ Directories::Directories() : Gtk::ListViewText(1) { if(menu_popup_row_path.empty()) return; EntryBox::get().clear(); - auto source_path=std::make_shared(menu_popup_row_path); - EntryBox::get().entries.emplace_back("", [this, source_path](const std::string &content) { - bool is_directory=boost::filesystem::is_directory(*source_path); - auto target_path = (is_directory ? *source_path : source_path->parent_path())/content; + EntryBox::get().entries.emplace_back("", [this, source_path=menu_popup_row_path](const std::string &content) { + bool is_directory=boost::filesystem::is_directory(source_path); + auto target_path = (is_directory ? source_path : source_path.parent_path())/content; if(!boost::filesystem::exists(target_path)) { boost::system::error_code ec; boost::filesystem::create_directory(target_path, ec); @@ -252,11 +250,10 @@ Directories::Directories() : Gtk::ListViewText(1) { if(menu_popup_row_path.empty()) return; EntryBox::get().clear(); - auto source_path=std::make_shared(menu_popup_row_path); - EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), [this, source_path](const std::string &content){ - bool is_directory=boost::filesystem::is_directory(*source_path); + EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), [this, source_path=menu_popup_row_path](const std::string &content){ + bool is_directory=boost::filesystem::is_directory(source_path); - auto target_path=source_path->parent_path()/content; + auto target_path=source_path.parent_path()/content; if(boost::filesystem::exists(target_path)) { Terminal::get().print("Error: could not rename to "+target_path.string()+": already exists\n", true); @@ -264,12 +261,12 @@ Directories::Directories() : Gtk::ListViewText(1) { } if(is_directory) - this->remove_path(*source_path); + this->remove_path(source_path); boost::system::error_code ec; - boost::filesystem::rename(*source_path, target_path, ec); + boost::filesystem::rename(source_path, target_path, ec); if(ec) { - Terminal::get().print("Error: could not rename "+source_path->string()+": "+ec.message()+'\n', true); + Terminal::get().print("Error: could not rename "+source_path.string()+": "+ec.message()+'\n', true); return; } update(); @@ -279,9 +276,9 @@ Directories::Directories() : Gtk::ListViewText(1) { for(size_t c=0;cfile_path, *source_path)) { + if(filesystem::file_in_path(view->file_path, source_path)) { auto file_it=view->file_path.begin(); - for(auto source_it=source_path->begin();source_it!=source_path->end();source_it++) + for(auto source_it=source_path.begin();source_it!=source_path.end();source_it++) file_it++; auto new_file_path=target_path; for(;file_it!=view->file_path.end();file_it++) @@ -289,7 +286,7 @@ Directories::Directories() : Gtk::ListViewText(1) { view->rename(new_file_path); } } - else if(view->file_path==*source_path) { + else if(view->file_path==source_path) { view->rename(target_path); std::string old_language_id; @@ -651,25 +648,24 @@ void Directories::remove_path(const boost::filesystem::path &dir_path) { } } -void Directories::colorize_path(const boost::filesystem::path &dir_path_, bool include_parent_paths) { - auto it=directories.find(dir_path_.string()); +void Directories::colorize_path(const boost::filesystem::path &dir_path, bool include_parent_paths) { + auto it=directories.find(dir_path.string()); if(it==directories.end()) return; if(it!=directories.end() && it->second.repository) { - auto dir_path=std::make_shared(dir_path_); auto repository=it->second.repository; std::thread git_status_thread([this, dir_path, repository, include_parent_paths] { - auto status=std::make_shared(); + Git::Repository::Status status; try { - *status=repository->get_status(); + status=repository->get_status(); } catch(const std::exception &e) { Terminal::get().async_print(std::string("Error (git): ")+e.what()+'\n', true); } - dispatcher.post([this, dir_path, include_parent_paths, status] { - auto it=directories.find(dir_path->string()); + dispatcher.post([this, dir_path=std::move(dir_path), include_parent_paths, status=std::move(status)] { + auto it=directories.find(dir_path.string()); if(it==directories.end()) return; @@ -698,9 +694,9 @@ void Directories::colorize_path(const boost::filesystem::path &dir_path_, bool i auto name=Glib::Markup::escape_text(child.get_value(column_record.name)); auto path=child.get_value(column_record.path); Gdk::RGBA *color; - if(status->modified.find(path.generic_string())!=status->modified.end()) + if(status.modified.find(path.generic_string())!=status.modified.end()) color=&yellow; - else if(status->added.find(path.generic_string())!=status->added.end()) + else if(status.added.find(path.generic_string())!=status.added.end()) color=&green; else color=&normal_color; diff --git a/src/project.cc b/src/project.cc index 66697b6..63118a1 100644 --- a/src/project.cc +++ b/src/project.cc @@ -549,7 +549,7 @@ void Project::Clang::debug_backtrace() { } else SelectionDialog::create(true, true); - auto rows=std::make_shared >(); + std::vector rows; if(backtrace.size()==0) { Info::get().print("No backtrace found"); return; @@ -569,7 +569,7 @@ void Project::Clang::debug_backtrace() { auto file_path=frame.file_path.filename().string(); row+=":"+Glib::Markup::escape_text(file_path)+":"+std::to_string(frame.line_nr)+" - "+Glib::Markup::escape_text(frame.function_name); } - (*rows)[row]=frame; + rows.emplace_back(frame); SelectionDialog::get()->add_row(row); if(!cursor_set && view && frame.file_path==view->file_path) { SelectionDialog::get()->set_cursor_at_last_row(); @@ -577,8 +577,10 @@ void Project::Clang::debug_backtrace() { } } - SelectionDialog::get()->on_select=[this, rows](const std::string& selected, bool hide_window) { - auto frame=rows->at(selected); + SelectionDialog::get()->on_select=[this, rows=std::move(rows)](unsigned int index, const std::string &text, bool hide_window) { + if(index>=rows.size()) + return; + auto frame=rows[index]; if(!frame.file_path.empty()) { Notebook::get().open(frame.file_path); if(auto view=Notebook::get().get_current_view()) { @@ -607,7 +609,7 @@ void Project::Clang::debug_show_variables() { } else SelectionDialog::create(true, true); - auto rows=std::make_shared >(); + auto rows=std::make_shared>(); if(variables.size()==0) { Info::get().print("No variables found"); return; @@ -616,12 +618,14 @@ void Project::Clang::debug_show_variables() { for(auto &variable: variables) { std::string row="#"+std::to_string(variable.thread_index_id)+":#"+std::to_string(variable.frame_index)+":"+variable.file_path.filename().string()+":"+std::to_string(variable.line_nr)+" - "+Glib::Markup::escape_text(variable.name)+""; - (*rows)[row]=variable; + rows->emplace_back(variable); SelectionDialog::get()->add_row(row); } - SelectionDialog::get()->on_select=[this, rows](const std::string& selected, bool hide_window) { - auto variable=rows->at(selected); + SelectionDialog::get()->on_select=[this, rows](unsigned int index, const std::string &text, bool hide_window) { + if(index>=rows->size()) + return; + auto variable=(*rows)[index]; Debug::LLDB::get().select_frame(variable.frame_index, variable.thread_index_id); if(!variable.file_path.empty()) { Notebook::get().open(variable.file_path); @@ -639,14 +643,14 @@ void Project::Clang::debug_show_variables() { debug_variable_tooltips.clear(); }; - SelectionDialog::get()->on_changed=[this, rows, view, iter](const std::string &selected) { - if(selected.empty()) { + SelectionDialog::get()->on_changed=[this, rows, view, iter](unsigned int index, const std::string &text) { + if(index>=rows->size()) { debug_variable_tooltips.hide(); return; } debug_variable_tooltips.clear(); - auto create_tooltip_buffer=[this, rows, view, selected]() { - auto variable=rows->at(selected); + auto create_tooltip_buffer=[this, rows, view, index]() { + auto variable=(*rows)[index]; auto tooltip_buffer=view?Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()):Gtk::TextBuffer::create(); Glib::ustring value=variable.value; diff --git a/src/selection_dialog.cc b/src/selection_dialog.cc index 7a2435f..5a75afc 100644 --- a/src/selection_dialog.cc +++ b/src/selection_dialog.cc @@ -22,11 +22,13 @@ SelectionDialogBase::ListViewText::ListViewText(bool use_markup) : Gtk::TreeView void SelectionDialogBase::ListViewText::append(const std::string& value) { auto new_row=list_store->append(); new_row->set_value(column_record.text, value); + new_row->set_value(column_record.index, size++); } void SelectionDialogBase::ListViewText::clear() { unset_model(); list_store.reset(); + size=0; } SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, Glib::RefPtr start_mark, bool show_search_entry, bool use_markup): @@ -116,14 +118,18 @@ void SelectionDialogBase::cursor_changed() { if(!is_visible()) return; auto it=list_view_text.get_selection()->get_selected(); - std::string row; + unsigned int index=static_cast(-1); if(it) - it->get_value(0, row); - if(last_row==row) + index=it->get_value(list_view_text.column_record.index); + if(last_index==index) return; - if(on_changed) - on_changed(row); - last_row=row; + if(on_changed) { + std::string text; + if(it) + text=it->get_value(list_view_text.column_record.text); + on_changed(index, text); + } + last_index=index; } void SelectionDialogBase::add_row(const std::string& row) { list_view_text.append(row); @@ -165,12 +171,13 @@ void SelectionDialogBase::hide() { if(on_hide) on_hide(); list_view_text.clear(); + last_index=static_cast(-1); } std::unique_ptr SelectionDialog::instance; SelectionDialog::SelectionDialog(Gtk::TextView *text_view, Glib::RefPtr start_mark, bool show_search_entry, bool use_markup) : SelectionDialogBase(text_view, start_mark, show_search_entry, use_markup) { - std::shared_ptr search_key(new std::string()); + auto search_key=std::make_shared(); auto filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model()); filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator& iter){ @@ -211,10 +218,10 @@ SelectionDialog::SelectionDialog(Gtk::TextView *text_view, Glib::RefPtrget_selected(); if(on_select && it) { - std::string row; - it->get_value(0, row); + auto index=it->get_value(list_view_text.column_record.index); + auto text=it->get_value(list_view_text.column_record.text); hide(); - on_select(row, true); + on_select(index, text, true); } else hide(); @@ -301,7 +308,7 @@ std::unique_ptr CompletionDialog::instance; CompletionDialog::CompletionDialog(Gtk::TextView *text_view, Glib::RefPtr start_mark) : SelectionDialogBase(text_view, start_mark, false, false) { show_offset=text_view->get_buffer()->get_insert()->get_iter().get_offset(); - std::shared_ptr search_key(new std::string()); + auto search_key=std::make_shared(); auto filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model()); if(show_offset==start_mark->get_iter().get_offset()) { filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator& iter){ @@ -347,9 +354,9 @@ void CompletionDialog::select(bool hide_window) { auto it=list_view_text.get_selection()->get_selected(); if(on_select && it) { - std::string row; - it->get_value(0, row); - on_select(row, hide_window); + auto index=it->get_value(list_view_text.column_record.index); + auto text=it->get_value(list_view_text.column_record.text); + on_select(index, text, hide_window); } if(hide_window) hide(); diff --git a/src/selection_dialog.h b/src/selection_dialog.h index a782946..e2343fa 100644 --- a/src/selection_dialog.h +++ b/src/selection_dialog.h @@ -10,18 +10,21 @@ class SelectionDialogBase { public: ColumnRecord() { add(text); + add(index); } Gtk::TreeModelColumn text; + Gtk::TreeModelColumn index; }; public: bool use_markup; + ColumnRecord column_record; ListViewText(bool use_markup); void append(const std::string& value); void clear(); private: Glib::RefPtr list_store; - ColumnRecord column_record; Gtk::CellRendererText cell_renderer; + unsigned int size=0; }; class SearchEntry : public Gtk::Entry { @@ -43,8 +46,8 @@ public: std::function on_show; std::function on_hide; - std::function on_select; - std::function on_changed; + std::function on_select; + std::function on_changed; Glib::RefPtr start_mark; protected: @@ -58,7 +61,7 @@ protected: SearchEntry search_entry; bool show_search_entry; - std::string last_row; + unsigned int last_index=static_cast(-1); }; class SelectionDialog : public SelectionDialogBase { diff --git a/src/source.cc b/src/source.cc index 9d1a131..80483f8 100644 --- a/src/source.cc +++ b/src/source.cc @@ -422,27 +422,27 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr comment_characters; + std::string comment_characters; if(is_bracket_language) - comment_characters=std::make_shared("//"); + comment_characters="//"; else if(language) { if(language->get_id()=="cmake" || language->get_id()=="makefile" || language->get_id()=="python" || language->get_id()=="python3" || language->get_id()=="sh" || language->get_id()=="perl" || language->get_id()=="ruby" || language->get_id()=="r" || language->get_id()=="asm" || language->get_id()=="automake") - comment_characters=std::make_shared("#"); + comment_characters="#"; else if(language->get_id()=="latex" || language->get_id()=="matlab" || language->get_id()=="octave" || language->get_id()=="bibtex") - comment_characters=std::make_shared("%"); + comment_characters="%"; else if(language->get_id()=="fortran") - comment_characters=std::make_shared("!"); + comment_characters="!"; else if(language->get_id()=="pascal") - comment_characters=std::make_shared("//"); + comment_characters="//"; else if(language->get_id()=="lua") - comment_characters=std::make_shared("--"); + comment_characters="--"; } - if(comment_characters) { - toggle_comments=[this, comment_characters] { + if(!comment_characters.empty()) { + toggle_comments=[this, comment_characters=std::move(comment_characters)] { std::vector lines; Gtk::TextIter selection_start, selection_end; get_buffer()->get_selection_bounds(selection_start, selection_end); @@ -470,12 +470,12 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtrsize();++c) { + for(size_t c=0;c((*comment_characters)[c])) { - if(csize()-1) { + else if(*iter==static_cast(comment_characters[c])) { + if(cbegin_user_action(); for(auto &line: lines) { auto iter=get_buffer()->get_iter_at_line(line); iter.forward_chars(min_indentation); if(lines_commented) { auto end_iter=iter; - end_iter.forward_chars(comment_characters->size()+static_cast(extra_spaces)); + end_iter.forward_chars(comment_characters.size()+static_cast(extra_spaces)); while(*iter==' ' || *iter=='\t') { iter.forward_char(); end_iter.forward_char(); @@ -1772,6 +1772,8 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) { break; } } + if(get_buffer()->size()==1) // special case due to backward_char(s) returning false when moving to start of buffer + do_smart_backspace=false; if(do_smart_backspace) { auto previous_line_end_iter=iter; if(previous_line_end_iter.backward_chars(line.size()+1)) { @@ -2367,10 +2369,11 @@ bool Source::View::on_key_press_event_smart_inserts(GdkEventKey *key) { // Insert () if(key->keyval==GDK_KEY_parenleft && allow_insertion(iter)) { if(symbol_count(iter, '(', ')')==0) { - get_buffer()->insert_at_cursor("()"); - auto iter=get_buffer()->get_insert()->get_iter(); + get_buffer()->insert_at_cursor(")"); + iter=get_buffer()->get_insert()->get_iter(); iter.backward_char(); get_buffer()->place_cursor(iter); + get_buffer()->insert_at_cursor("("); scroll_to(get_buffer()->get_insert()); return true; } diff --git a/src/source_clang.cc b/src/source_clang.cc index 1502697..763d641 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -465,9 +465,50 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa parse_process_state=ParseProcessState::IDLE; }; - autocomplete.is_continue_key=[this](guint keyval) { + // Activate argument completions + get_buffer()->signal_changed().connect([this] { + if(!interactive_completion) + return; + if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) + return; + if(!has_focus()) + return; + if(show_arguments) + autocomplete.stop(); + show_arguments=false; + delayed_show_arguments_connection.disconnect(); + delayed_show_arguments_connection=Glib::signal_timeout().connect([this]() { + if(get_buffer()->get_has_selection()) + return false; + if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) + return false; + if(!has_focus()) + return false; + if(is_possible_parameter()) { + autocomplete.stop(); + autocomplete.run(); + } + return false; + }, 500); + }, false); + + // Remove argument completions + signal_key_press_event().connect([this](GdkEventKey *key) { + if(show_arguments && CompletionDialog::get() && CompletionDialog::get()->is_visible() && + key->keyval != GDK_KEY_Down && key->keyval != GDK_KEY_Up && + key->keyval != GDK_KEY_Return && key->keyval != GDK_KEY_KP_Enter && + key->keyval != GDK_KEY_ISO_Left_Tab && key->keyval != GDK_KEY_Tab && + (key->keyval < GDK_KEY_Shift_L || key->keyval > GDK_KEY_Hyper_R)) { + get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter()); + CompletionDialog::get()->hide(); + } + return false; + }, false); + + autocomplete.is_continue_key=[](guint keyval) { if((keyval>='0' && keyval<='9') || (keyval>='a' && keyval<='z') || (keyval>='A' && keyval<='Z') || keyval=='_') return true; + return false; }; @@ -481,8 +522,12 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa autocomplete.run_check=[this]() { auto iter=get_buffer()->get_insert()->get_iter(); - if(iter.backward_char() && iter.backward_char() && !is_code_iter(iter)) + iter.backward_char(); + if(!is_code_iter(iter)) return false; + + show_arguments=false; + std::string line=" "+get_line_before(); const static std::regex in_specified_namespace("^.*[a-zA-Z0-9_\\)\\]\\>](->|\\.|::)([a-zA-Z0-9_]*)$"); const static std::regex within_namespace("^.*[^a-zA-Z0-9_]+([a-zA-Z0-9_]{3,})$"); @@ -503,83 +548,117 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa if(autocomplete.prefix.size()==0 || autocomplete.prefix[0]<'0' || autocomplete.prefix[0]>'9') return true; } + else if(is_possible_parameter()) { + show_arguments=true; + std::unique_lock lock(autocomplete.prefix_mutex); + autocomplete.prefix=""; + return true; + } + else if(!interactive_completion) { + auto end_iter=get_buffer()->get_insert()->get_iter(); + auto iter=end_iter; + while(iter.backward_char() && autocomplete.is_continue_key(*iter)) {} + if(iter!=end_iter) + iter.forward_char(); + std::unique_lock lock(autocomplete.prefix_mutex); + autocomplete.prefix=get_buffer()->get_text(iter, end_iter); + return true; + } + return false; }; - autocomplete.before_get_suggestions=[this] { + autocomplete.before_add_rows=[this] { status_state="autocomplete..."; if(update_status_state) update_status_state(this); }; - autocomplete.after_get_suggestions=[this] { + autocomplete.after_add_rows=[this] { status_state=""; if(update_status_state) update_status_state(this); }; - autocomplete.on_get_suggestions_error=[this] { + autocomplete.on_add_rows_error=[this] { Terminal::get().print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true); full_reparse(); }; - autocomplete.get_suggestions=[this](std::string &buffer, int line_number, int column) { - auto suggestions=std::make_shared>(); + autocomplete.add_rows=[this](std::string &buffer, int line_number, int column) { if(this->language && (this->language->get_id()=="chdr" || this->language->get_id()=="cpphdr")) clangmm::remove_include_guard(buffer); auto results=clang_tu->get_code_completions(buffer, line_number, column); if(results.cx_results==nullptr) { auto expected=ParseState::PROCESSING; parse_state.compare_exchange_strong(expected, ParseState::RESTARTING); - return suggestions; + return; } - if(autocomplete.state==Autocomplete::State::STARTING) { + if(autocomplete.state==Autocomplete::State::STARTING) { std::string prefix_copy; { std::lock_guard lock(autocomplete.prefix_mutex); prefix_copy=autocomplete.prefix; } - + for (unsigned i = 0; i < results.size(); ++i) { auto result=results.get(i); if(result.available()) { - auto chunks=result.get_chunks(); - bool match=false; - for(auto &chunk: chunks) { - if(chunk.kind!=clangmm::CompletionChunk_ResultType && chunk.kind!=clangmm::CompletionChunk_Informative) { - if(chunk.chunk.size()>=prefix_copy.size() && chunk.chunk.compare(0, prefix_copy.size(), prefix_copy)==0) - match=true; - break; + std::string text; + if(show_arguments) { + class Recursive { + public: + static void f(const clangmm::CompletionString &completion_string, std::string &text) { + for(unsigned i = 0; i < completion_string.get_num_chunks(); ++i) { + auto kind=static_cast(clang_getCompletionChunkKind(completion_string.cx_completion_sting, i)); + if(kind==clangmm::CompletionChunk_Optional) + f(clangmm::CompletionString(clang_getCompletionChunkCompletionString(completion_string.cx_completion_sting, i)), text); + else if(kind==clangmm::CompletionChunk_CurrentParameter) { + auto chunk_cstr=clangmm::String(clang_getCompletionChunkText(completion_string.cx_completion_sting, i)); + text+=chunk_cstr.c_str; + } + } + } + }; + Recursive::f(result, text); + if(!text.empty()) { + bool already_added=false; + for(auto &pair: autocomplete.rows) { + if(pair.first==text) { + already_added=true; + break; + } + } + if(!already_added) + autocomplete.rows.emplace_back(std::move(text), result.get_brief_comment()); } } - if(match) { - suggestions->emplace_back(std::move(chunks)); - suggestions->back().brief_comments=result.get_brief_comments(); + else { + std::string return_text; + bool match=false; + for(unsigned i = 0; i < result.get_num_chunks(); ++i) { + auto kind=static_cast(clang_getCompletionChunkKind(result.cx_completion_sting, i)); + if(kind!=clangmm::CompletionChunk_Informative) { + auto chunk_cstr=clangmm::String(clang_getCompletionChunkText(result.cx_completion_sting, i)); + if(kind==clangmm::CompletionChunk_TypedText) { + if(strlen(chunk_cstr.c_str)>=prefix_copy.size() && prefix_copy.compare(0, prefix_copy.size(), chunk_cstr.c_str, prefix_copy.size())==0) + match = true; + else + break; + } + if(kind==clangmm::CompletionChunk_ResultType) + return_text=std::string(" → ")+chunk_cstr.c_str; + else + text+=chunk_cstr.c_str; + } + } + if(match && !text.empty()) + autocomplete.rows.emplace_back(std::move(text), result.get_brief_comment()); } } } } - return suggestions; - }; - - autocomplete.foreach_suggestion=[this](Suggestion &suggestion) { - std::string row; - std::string return_value; - for(auto &chunk : suggestion.chunks) { - if(chunk.kind == clangmm::CompletionChunk_ResultType) - return_value = chunk.chunk; - else if(chunk.kind != clangmm::CompletionChunk_Informative) - row += chunk.chunk; - } - suggestion.chunks.clear(); - if(!row.empty()) { - auto row_insert_on_selection = row; - if(!return_value.empty()) - row += " → " + return_value; - autocomplete.rows[row] = std::pair(std::move(row_insert_on_selection), std::move(suggestion.brief_comments)); - CompletionDialog::get()->add_row(row); - } }; autocomplete.setup_dialog=[this] { @@ -587,8 +666,15 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa hide_tooltips(); }; - CompletionDialog::get()->on_select=[this](const std::string &selected, bool hide_window) { - auto row = autocomplete.rows.at(selected).first; + CompletionDialog::get()->on_select=[this](unsigned int index, const std::string &text, bool hide_window) { + if(index>=autocomplete.rows.size()) + return; + std::string row; + auto pos=text.find(" → "); + if(pos!=std::string::npos) + row=text.substr(0, pos); + else + row=text; //erase existing variable or function before insert iter get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter()); //do not insert template argument or function parameters if they already exist @@ -607,47 +693,73 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa get_buffer()->insert(CompletionDialog::get()->start_mark->get_iter(), row); //if selection is finalized, select text inside template arguments or function parameters if(hide_window) { - auto para_pos=row.find('('); - auto angle_pos=row.find('<'); size_t start_pos=std::string::npos; size_t end_pos=std::string::npos; - if(angle_pos'); - } - else if(para_pos!=std::string::npos) { - start_pos=para_pos; - end_pos=row.size()-1; + if(show_arguments) { + start_pos=0; + end_pos=row.size(); } - if(start_pos==std::string::npos || end_pos==std::string::npos) { - if((start_pos=row.find('\"'))!=std::string::npos) - end_pos=row.find('\"', start_pos+1); + else { + auto para_pos=row.find('('); + auto angle_pos=row.find('<'); + if(angle_pos'); + } + else if(para_pos!=std::string::npos) { + start_pos=para_pos+1; + end_pos=row.size()-1; + } + if(start_pos==std::string::npos || end_pos==std::string::npos) { + if((start_pos=row.find('\"'))!=std::string::npos) { + end_pos=row.find('\"', start_pos+1); + ++start_pos; + } + } } if(start_pos==std::string::npos || end_pos==std::string::npos) { if((start_pos=row.find(' '))!=std::string::npos) { - if((start_pos=row.find("expression", start_pos+1))!=std::string::npos) { - end_pos=start_pos+10; - start_pos--; + std::vector parameters={"expression", "arguments", "identifier", "type name", "qualifier::name", "macro", "condition"}; + for(auto ¶meter: parameters) { + if((start_pos=row.find(parameter, start_pos+1))!=std::string::npos) { + end_pos=start_pos+parameter.size(); + break; + } } } } + if(start_pos!=std::string::npos && end_pos!=std::string::npos) { - auto start_offset=CompletionDialog::get()->start_mark->get_iter().get_offset()+start_pos+1; - auto end_offset=CompletionDialog::get()->start_mark->get_iter().get_offset()+end_pos; - if(start_offset!=end_offset) + int start_offset=CompletionDialog::get()->start_mark->get_iter().get_offset()+start_pos; + int end_offset=CompletionDialog::get()->start_mark->get_iter().get_offset()+end_pos; + auto size=get_buffer()->size(); + if(start_offset!=end_offset && start_offsetselect_range(get_buffer()->get_iter_at_offset(start_offset), get_buffer()->get_iter_at_offset(end_offset)); } else { //new autocomplete after for instance when selecting "std::" auto iter=get_buffer()->get_insert()->get_iter(); - if(iter.backward_char() && *iter==':') + if(iter.backward_char() && *iter==':') { autocomplete.run(); + return; + } } } }; }; } +bool Source::ClangViewAutocomplete::is_possible_parameter() { + auto iter=get_buffer()->get_insert()->get_iter(); + if(iter.backward_char() && (!interactive_completion || last_keyval=='(' || last_keyval==',' || last_keyval==' ' || + last_keyval==GDK_KEY_Return || last_keyval==GDK_KEY_KP_Enter)) { + while((*iter==' ' || *iter=='\t' || *iter=='\n' || *iter=='\r') && iter.backward_char()) {} + if(*iter=='(' || *iter==',') + return true; + } + return false; +} + const std::unordered_map &Source::ClangViewAutocomplete::autocomplete_manipulators_map() { //TODO: feel free to add more static std::unordered_map map={ @@ -1552,7 +1664,7 @@ void Source::ClangView::full_reparse() { return; } } - autocomplete.state=Autocomplete::State::IDLE; + autocomplete.state=Autocomplete::State::IDLE; soft_reparse_needed=false; full_reparse_running=true; if(full_reparse_thread.joinable()) @@ -1571,6 +1683,7 @@ void Source::ClangView::full_reparse() { } void Source::ClangView::async_delete() { + delayed_show_arguments_connection.disconnect(); delayed_tag_similar_identifiers_connection.disconnect(); parsing_in_progress->cancel("canceled, freeing resources in the background"); diff --git a/src/source_clang.h b/src/source_clang.h index fbaa832..933eaad 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -58,18 +58,13 @@ namespace Source { class ClangViewAutocomplete : public virtual ClangViewParse { public: - class Suggestion { - public: - explicit Suggestion(std::vector &&chunks) : - chunks(chunks) { } - std::vector chunks; - std::string brief_comments; - }; - ClangViewAutocomplete(const boost::filesystem::path &file_path, Glib::RefPtr language); protected: - Autocomplete autocomplete; + Autocomplete autocomplete; + sigc::connection delayed_show_arguments_connection; private: + bool is_possible_parameter(); + bool show_arguments; const std::unordered_map &autocomplete_manipulators_map(); }; diff --git a/src/source_diff.cc b/src/source_diff.cc index 1b46118..5e397ac 100644 --- a/src/source_diff.cc +++ b/src/source_diff.cc @@ -259,15 +259,14 @@ void Source::DiffView::configure() { } } catch(const std::exception &e) { - auto e_what=std::make_shared(e.what()); - dispatcher.post([this, e_what] { + dispatcher.post([this, e_what=e.what()] { get_buffer()->remove_tag(renderer->tag_added, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_modified, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed_below, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed_above, get_buffer()->begin(), get_buffer()->end()); renderer->queue_draw(); - Terminal::get().print("Error (git): "+*e_what+'\n', true); + Terminal::get().print(std::string("Error (git): ")+e_what+'\n', true); }); } }); diff --git a/src/source_spellcheck.cc b/src/source_spellcheck.cc index b21e4d5..895b80d 100644 --- a/src/source_spellcheck.cc +++ b/src/source_spellcheck.cc @@ -168,10 +168,10 @@ Source::SpellCheckView::SpellCheckView() : Gsv::View() { return false; for(auto &suggestion: suggestions) SelectionDialog::get()->add_row(suggestion); - SelectionDialog::get()->on_select=[this, word](const std::string& selected, bool hide_window) { + SelectionDialog::get()->on_select=[this, word](unsigned int index, const std::string &text, bool hide_window) { get_buffer()->begin_user_action(); get_buffer()->erase(word.first, word.second); - get_buffer()->insert(get_buffer()->get_insert()->get_iter(), selected); + get_buffer()->insert(get_buffer()->get_insert()->get_iter(), text); get_buffer()->end_user_action(); }; hide_tooltips(); diff --git a/src/window.cc b/src/window.cc index 90a8160..8a2bee8 100644 --- a/src/window.cc +++ b/src/window.cc @@ -628,21 +628,23 @@ void Window::set_menu_actions() { else SelectionDialog::create(true, true); - auto rows=std::make_shared >(); + std::vector rows; std::string line; while(std::getline(*stream, line)) { auto location=Ctags::get_location(line, true); std::string row=location.file_path.string()+":"+std::to_string(location.line+1)+": "+location.source; - (*rows)[row]=Source::Offset(location.line, location.index, location.file_path); + rows.emplace_back(Source::Offset(location.line, location.index, location.file_path)); SelectionDialog::get()->add_row(row); } - if(rows->size()==0) + if(rows.size()==0) return; - SelectionDialog::get()->on_select=[this, rows, path](const std::string &selected, bool hide_window) { - auto offset=rows->at(selected); + SelectionDialog::get()->on_select=[this, rows=std::move(rows), path=std::move(path)](unsigned int index, const std::string &text, bool hide_window) { + if(index>=rows.size()) + return; + auto offset=rows[index]; auto full_path=path/offset.file_path; if(!boost::filesystem::is_regular_file(full_path)) return; @@ -693,7 +695,7 @@ void Window::set_menu_actions() { for(auto view: Notebook::get().get_views()) buffer_paths.emplace(view->file_path.string()); - auto paths=std::make_shared>(); + std::vector paths; // populate with all files in search_path for (boost::filesystem::recursive_directory_iterator iter(search_path), end; iter != end; iter++) { auto path = iter->path(); @@ -708,22 +710,21 @@ void Window::set_menu_actions() { auto row_str = filesystem::get_relative_path(path, search_path).string(); if(buffer_paths.count(path.string())) row_str=""+row_str+""; - paths->emplace(row_str, path); + paths.emplace_back(path); SelectionDialog::get()->add_row(row_str); } - if(paths->empty()) { + if(paths.empty()) { Info::get().print("No files found in current project"); return; } - SelectionDialog::get()->on_select=[this, paths](const std::string &selected, bool hide_window) { - auto it=paths->find(selected); - if(it!=paths->end()) { - Notebook::get().open(it->second); - if (auto view=Notebook::get().get_current_view()) - view->hide_tooltips(); - } + SelectionDialog::get()->on_select=[this, paths=std::move(paths)](unsigned int index, const std::string &text, bool hide_window) { + if(index>=paths.size()) + return; + Notebook::get().open(paths[index]); + if (auto view=Notebook::get().get_current_view()) + view->hide_tooltips(); }; if(view) @@ -825,7 +826,7 @@ void Window::set_menu_actions() { if(!locations.empty()) { auto dialog_iter=view->get_iter_for_dialog(); SelectionDialog::create(view, view->get_buffer()->create_mark(dialog_iter), true, true); - auto rows=std::make_shared >(); + std::vector rows; auto project_path=Project::Build::create(view->file_path)->project_path; if(project_path.empty()) { if(!Directories::get().path.empty()) @@ -838,26 +839,28 @@ void Window::set_menu_actions() { if(path.empty()) path=location.file_path.filename(); auto row=path.string()+":"+std::to_string(location.line+1); - (*rows)[row]=location; + rows.emplace_back(location); SelectionDialog::get()->add_row(row); } - if(rows->size()==0) + if(rows.size()==0) return; - else if(rows->size()==1) { - auto location=*rows->begin(); - if(!boost::filesystem::is_regular_file(location.second.file_path)) + else if(rows.size()==1) { + auto location=*rows.begin(); + if(!boost::filesystem::is_regular_file(location.file_path)) return; - Notebook::get().open(location.second.file_path); + Notebook::get().open(location.file_path); auto view=Notebook::get().get_current_view(); - auto line=static_cast(location.second.line); - auto index=static_cast(location.second.index); + auto line=static_cast(location.line); + auto index=static_cast(location.index); view->place_cursor_at_line_index(line, index); view->scroll_to_cursor_delayed(view, true, false); return; } - SelectionDialog::get()->on_select=[rows](const std::string &selected, bool hide_window) { - auto location=rows->at(selected); + SelectionDialog::get()->on_select=[rows=std::move(rows)](unsigned int index, const std::string &text, bool hide_window) { + if(index>=rows.size()) + return; + auto location=rows[index]; if(!boost::filesystem::is_regular_file(location.file_path)) return; Notebook::get().open(location.file_path); @@ -890,7 +893,7 @@ void Window::set_menu_actions() { if(!usages.empty()) { auto dialog_iter=view->get_iter_for_dialog(); SelectionDialog::create(view, view->get_buffer()->create_mark(dialog_iter), true, true); - auto rows=std::make_shared >(); + std::vector rows; auto iter=view->get_buffer()->get_insert()->get_iter(); for(auto &usage: usages) { @@ -902,7 +905,7 @@ void Window::set_menu_actions() { current_page=false; } row+=std::to_string(usage.first.line+1)+": "+usage.second; - (*rows)[row]=usage.first; + rows.emplace_back(usage.first); SelectionDialog::get()->add_row(row); //Set dialog cursor to the last row if the textview cursor is at the same line @@ -912,10 +915,12 @@ void Window::set_menu_actions() { } } - if(rows->size()==0) + if(rows.size()==0) return; - SelectionDialog::get()->on_select=[this, rows](const std::string &selected, bool hide_window) { - auto offset=rows->at(selected); + SelectionDialog::get()->on_select=[this, rows=std::move(rows)](unsigned int index, const std::string &text, bool hide_window) { + if(index>=rows.size()) + return; + auto offset=rows[index]; if(!boost::filesystem::is_regular_file(offset.file_path)) return; Notebook::get().open(offset.file_path); @@ -937,16 +942,18 @@ void Window::set_menu_actions() { if(!methods.empty()) { auto dialog_iter=view->get_iter_for_dialog(); SelectionDialog::create(view, view->get_buffer()->create_mark(dialog_iter), true, true); - auto rows=std::make_shared >(); + std::vector rows; auto iter=view->get_buffer()->get_insert()->get_iter(); for(auto &method: methods) { - (*rows)[method.second]=method.first; + rows.emplace_back(method.first); SelectionDialog::get()->add_row(method.second); if(iter.get_line()>=static_cast(method.first.line)) SelectionDialog::get()->set_cursor_at_last_row(); } - SelectionDialog::get()->on_select=[view, rows](const std::string& selected, bool hide_window) { - auto offset=rows->at(selected); + SelectionDialog::get()->on_select=[view, rows=std::move(rows)](unsigned int index, const std::string &text, bool hide_window) { + if(index>=rows.size()) + return; + auto offset=rows[index]; view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(offset.line, offset.index)); view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); view->hide_tooltips(); @@ -971,16 +978,16 @@ void Window::set_menu_actions() { EntryBox::get().buttons.back().activate(); return; } - auto method=std::make_shared(view->get_method()); - if(method->empty()) + auto method=view->get_method(); + if(method.empty()) return; EntryBox::get().clear(); EntryBox::get().labels.emplace_back(); - EntryBox::get().labels.back().set_text(*method); - EntryBox::get().buttons.emplace_back(button_text, [this, method](){ + EntryBox::get().labels.back().set_text(method); + EntryBox::get().buttons.emplace_back(button_text, [this, method=std::move(method)](){ if(auto view=Notebook::get().get_current_view()) { - view->get_buffer()->insert_at_cursor(*method); + view->get_buffer()->insert_at_cursor(method); EntryBox::get().clear(); } }); @@ -1033,8 +1040,8 @@ void Window::set_menu_actions() { menu.add_action("project_set_run_arguments", [this]() { auto project=Project::create(); - auto run_arguments=std::make_shared >(project->get_run_arguments()); - if(run_arguments->second.empty()) + auto run_arguments=project->get_run_arguments(); + if(run_arguments.second.empty()) return; EntryBox::get().clear(); @@ -1044,8 +1051,8 @@ void Window::set_menu_actions() { label_it->set_text("Synopsis: [environment_variable=value]... executable [argument]...\nSet empty to let juCi++ deduce executable."); }; label_it->update(0, ""); - EntryBox::get().entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ - Project::run_arguments[run_arguments->first]=content; + EntryBox::get().entries.emplace_back(run_arguments.second, [this, run_arguments_first=std::move(run_arguments.first)](const std::string &content){ + Project::run_arguments[run_arguments_first]=content; EntryBox::get().hide(); }, 50); auto entry_it=EntryBox::get().entries.begin(); @@ -1130,8 +1137,8 @@ void Window::set_menu_actions() { #ifdef JUCI_ENABLE_DEBUG menu.add_action("debug_set_run_arguments", [this]() { auto project=Project::create(); - auto run_arguments=std::make_shared >(project->debug_get_run_arguments()); - if(run_arguments->second.empty()) + auto run_arguments=project->debug_get_run_arguments(); + if(run_arguments.second.empty()) return; EntryBox::get().clear(); @@ -1141,8 +1148,8 @@ void Window::set_menu_actions() { label_it->set_text("Synopsis: [environment_variable=value]... executable [argument]...\nSet empty to let juCi++ deduce executable."); }; label_it->update(0, ""); - EntryBox::get().entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ - Project::debug_run_arguments[run_arguments->first]=content; + EntryBox::get().entries.emplace_back(run_arguments.second, [this, run_arguments_first=std::move(run_arguments.first)](const std::string& content){ + Project::debug_run_arguments[run_arguments_first]=content; EntryBox::get().hide(); }, 50); auto entry_it=EntryBox::get().entries.begin(); @@ -1559,11 +1566,10 @@ void Window::rename_token_entry() { if(view->get_token_spelling && view->rename_similar_tokens) { auto spelling=std::make_shared(view->get_token_spelling()); if(!spelling->empty()) { - auto iter=std::make_shared(view->get_buffer()->get_insert()->get_iter()); - EntryBox::get().entries.emplace_back(*spelling, [this, view, spelling, iter](const std::string& content){ + EntryBox::get().entries.emplace_back(*spelling, [this, view, spelling, iter=view->get_buffer()->get_insert()->get_iter()](const std::string& content){ //TODO: gtk needs a way to check if iter is valid without dumping g_error message //iter->get_buffer() will print such a message, but no segfault will occur - if(Notebook::get().get_current_view()==view && content!=*spelling && iter->get_buffer() && view->get_buffer()->get_insert()->get_iter()==*iter) + if(Notebook::get().get_current_view()==view && content!=*spelling && iter.get_buffer() && view->get_buffer()->get_insert()->get_iter()==iter) view->rename_similar_tokens(content); else Info::get().print("Operation canceled");