diff --git a/juci/selectiondialog.cc b/juci/selectiondialog.cc index 02a2c4c..8edb13d 100644 --- a/juci/selectiondialog.cc +++ b/juci/selectiondialog.cc @@ -18,26 +18,22 @@ void SelectionDialog::show() { list_view_text->set_headers_visible(false); list_view_text->set_hscroll_policy(Gtk::ScrollablePolicy::SCROLL_NATURAL); list_view_text->set_activate_on_single_click(true); - list_view_text->set_search_entry(search_entry); list_view_text->set_hover_selection(false); list_view_text->set_rules_hint(true); //list_view_text->set_fixed_height_mode(true); //TODO: This is buggy on OS X, remember to post an issue on GTK+ 3 + last_selected=-1; + list_view_text->signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { if(shown) { select(); } }); - list_view_text->signal_cursor_changed().connect([this](){ - cursor_changed(); - }); + list_view_text->signal_cursor_changed().connect(sigc::mem_fun(*this, &SelectionDialog::cursor_changed), true); list_view_text->signal_realize().connect([this](){ resize(); }); - if(start_mark) - text_view.get_buffer()->delete_mark(start_mark); - start_mark=text_view.get_buffer()->create_mark(text_view.get_buffer()->get_insert()->get_iter()); start_offset=start_mark->get_iter().get_offset(); list_view_text->clear_items(); for (auto &i : rows) { @@ -56,16 +52,23 @@ void SelectionDialog::show() { window->show_all(); shown=true; - selected=false; + row_in_entry=false; + auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); + if(text.size()>0) { + search_entry.set_text(text); + list_view_text->set_search_entry(search_entry); + } } void SelectionDialog::hide() { - window->hide(); + window->hide(); shown=false; + if(tooltips) + tooltips->hide(); } void SelectionDialog::select(bool hide_window) { - selected=true; + row_in_entry=true; auto selected=list_view_text->get_selected(); std::pair select; if(selected.size()>0) { @@ -74,8 +77,6 @@ void SelectionDialog::select(bool hide_window) { text_view.get_buffer()->insert(start_mark->get_iter(), select.first); } if(hide_window) { - if(tooltips) - tooltips->hide(); hide(); char find_char=select.first.back(); if(find_char==')' || find_char=='>') { @@ -105,9 +106,10 @@ bool SelectionDialog::on_key_release(GdkEventKey* key) { auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); if(text.size()>0) { search_entry.set_text(text); + list_view_text->set_search_entry(search_entry); } + cursor_changed(); } - return false; } @@ -116,11 +118,12 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) { (key->keyval>=GDK_KEY_A && key->keyval<=GDK_KEY_Z) || (key->keyval>=GDK_KEY_a && key->keyval<=GDK_KEY_z) || key->keyval==GDK_KEY_underscore || key->keyval==GDK_KEY_BackSpace) { - if(selected) { + if(row_in_entry) { text_view.get_buffer()->erase(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); - selected=false; - if(key->keyval==GDK_KEY_BackSpace) + row_in_entry=false; + if(key->keyval==GDK_KEY_BackSpace) { return true; + } } return false; } @@ -161,24 +164,32 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) { } void SelectionDialog::cursor_changed() { - if(tooltips) - tooltips->hide(); auto selected=list_view_text->get_selected(); if(selected.size()>0) { - auto select = rows.at(list_view_text->get_text(selected[0])); - if(select.second.size()>0) { - tooltips=std::unique_ptr(new Tooltips()); - auto tooltip_text=select.second; - auto get_tooltip_buffer=[this, tooltip_text]() { - auto tooltip_buffer=Gtk::TextBuffer::create(text_view.get_buffer()->get_tag_table()); - //TODO: Insert newlines to tooltip_text (use 80 chars, then newline?) - tooltip_buffer->insert_at_cursor(tooltip_text); - return tooltip_buffer; - }; - tooltips->emplace_back(get_tooltip_buffer, text_view, text_view.get_buffer()->create_mark(start_mark->get_iter()), text_view.get_buffer()->create_mark(text_view.get_buffer()->get_insert()->get_iter())); - tooltips->show(true); + if(selected[0]!=last_selected || last_selected==-1) { + if(tooltips) + tooltips->hide(); + auto row = rows.at(list_view_text->get_text(selected[0])); + if(row.second.size()>0) { + tooltips=std::unique_ptr(new Tooltips()); + auto tooltip_text=row.second; + auto get_tooltip_buffer=[this, tooltip_text]() { + auto tooltip_buffer=Gtk::TextBuffer::create(text_view.get_buffer()->get_tag_table()); + //TODO: Insert newlines to tooltip_text (use 80 chars, then newline?) + tooltip_buffer->insert_at_cursor(tooltip_text); + return tooltip_buffer; + }; + tooltips->emplace_back(get_tooltip_buffer, text_view, text_view.get_buffer()->create_mark(start_mark->get_iter()), text_view.get_buffer()->create_mark(text_view.get_buffer()->get_insert()->get_iter())); + tooltips->show(true); + } } } + else if(tooltips) + tooltips->hide(); + if(selected.size()>0) + last_selected=selected[0]; + else + last_selected=-1; } void SelectionDialog::move() { diff --git a/juci/selectiondialog.h b/juci/selectiondialog.h index ec27d9b..32d5e27 100644 --- a/juci/selectiondialog.h +++ b/juci/selectiondialog.h @@ -16,23 +16,23 @@ public: bool on_key_press(GdkEventKey* key); std::map > rows; - bool shown=false; + Glib::RefPtr start_mark; private: void resize(); void select(bool hide_window=true); void cursor_changed(); Gtk::Entry search_entry; - Glib::RefPtr start_mark; int start_offset; - bool selected; + bool row_in_entry; Gtk::TextView& text_view; std::unique_ptr window; std::unique_ptr scrolled_window; std::unique_ptr list_view_text; std::unique_ptr tooltips; + int last_selected; }; #endif // JUCI_SELECTIONDIALOG_H_ \ No newline at end of file diff --git a/juci/source.cc b/juci/source.cc index 4de235f..cc3cf0a 100644 --- a/juci/source.cc +++ b/juci/source.cc @@ -63,7 +63,7 @@ string Source::View::get_line_before_insert() { } //Basic indentation -bool Source::View::on_key_press(GdkEventKey* key) { +bool Source::View::on_key_press_event(GdkEventKey* key) { auto config=Singletons::Config::source(); const std::regex spaces_regex(std::string("^(")+config->tab_char+"*).*$"); //Indent as in next or previous line @@ -139,7 +139,7 @@ bool Source::View::on_key_press(GdkEventKey* key) { } } } - return false; + return Gsv::View::on_key_press_event(key); } ////////////////// @@ -148,7 +148,7 @@ bool Source::View::on_key_press(GdkEventKey* key) { clang::Index Source::ClangView::clang_index(0, 0); Source::ClangView::ClangView(const std::string& file_path, const std::string& project_path, Terminal::Controller& terminal): -Source::View(file_path, project_path), terminal(terminal), selection_dialog(*this), +Source::View(file_path, project_path), terminal(terminal), parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { int start_offset = get_source_buffer()->begin().get_offset(); int end_offset = get_source_buffer()->end().get_offset(); @@ -223,22 +223,17 @@ parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { }); get_source_buffer()->signal_changed().connect([this]() { - cancel_show_autocomplete=true; parse_thread_mapped=false; delayed_reparse_connection.disconnect(); - if(!selection_dialog.shown) { - delayed_reparse_connection=Glib::signal_timeout().connect([this]() { - clang_readable=false; - parse_thread_go=true; - return false; - }, 1000); - } + delayed_reparse_connection=Glib::signal_timeout().connect([this]() { + clang_readable=false; + parse_thread_go=true; + return false; + }, 1000); type_tooltips.hide(); diagnostic_tooltips.hide(); }); - signal_key_press_event().connect(sigc::mem_fun(*this, &Source::ClangView::on_key_press), false); - signal_key_release_event().connect(sigc::mem_fun(*this, &Source::ClangView::on_key_release), false); signal_motion_notify_event().connect(sigc::mem_fun(*this, &Source::ClangView::clangview_on_motion_notify_event), false); signal_focus_out_event().connect(sigc::mem_fun(*this, &Source::ClangView::clangview_on_focus_out_event), false); signal_scroll_event().connect(sigc::mem_fun(*this, &Source::ClangView::clangview_on_scroll_event), false); @@ -289,25 +284,6 @@ reparse(const std::map &buffer) { return status; } -std::vector Source::ClangView:: -get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map) { - INFO("Getting auto complete suggestions"); - std::vector suggestions; - clang::CodeCompleteResults results(clang_tu.get(), - file_path, - buffer_map, - line_number, - column-1); - for (int i = 0; i < results.size(); i++) { - auto result=results.get(i); - suggestions.emplace_back(result.get_chunks()); - suggestions.back().brief_comments=result.get_brief_comments(); - } - DEBUG("Number of suggestions"); - DEBUG_VAR(suggestions.size()); - return suggestions; -} - std::vector Source::ClangView:: get_compilation_commands() { clang::CompilationDatabase db(project_path); @@ -451,10 +427,6 @@ void Source::ClangView::clangview_on_mark_set(const Gtk::TextBuffer::iterator& i delayed_tooltips_connection.disconnect(); if(mark->get_name()=="insert") { - cancel_show_autocomplete=true; - if(selection_dialog.shown) { - selection_dialog.hide(); - } delayed_tooltips_connection.disconnect(); delayed_tooltips_connection=Glib::signal_timeout().connect([this]() { if(clang_readable) { @@ -484,9 +456,6 @@ bool Source::ClangView::clangview_on_focus_out_event(GdkEventFocus* event) { } bool Source::ClangView::clangview_on_scroll_event(GdkEventScroll* event) { - if(selection_dialog.shown) - selection_dialog.move(); - delayed_tooltips_connection.disconnect(); type_tooltips.hide(); diagnostic_tooltips.hide(); @@ -525,114 +494,9 @@ highlight_token(clang::Token *token, end_offset), token_kind); } -bool Source::ClangView::on_key_release(GdkEventKey* key) { - if(selection_dialog.shown) { - if(selection_dialog.on_key_release(key)) - return true; - } - - INFO("Source::ClangView::on_key_release getting iters"); - // Get function to fill popup with suggests item vector under is for testing - Gtk::TextIter beg = get_source_buffer()->get_insert()->get_iter(); - Gtk::TextIter end = get_source_buffer()->get_insert()->get_iter(); - Gtk::TextIter tmp = get_source_buffer()->get_insert()->get_iter(); - Gtk::TextIter tmp1 = get_source_buffer()->get_insert()->get_iter(); - Gtk::TextIter line = get_source_buffer()->get_iter_at_line(tmp.get_line()); - if (end.backward_char() && end.backward_char()) { - bool illegal_chars = - end.backward_search("\"", Gtk::TEXT_SEARCH_VISIBLE_ONLY, tmp, tmp1, line) - || - end.backward_search("//", Gtk::TEXT_SEARCH_VISIBLE_ONLY, tmp, tmp1, line); - INFO("Source::ClangView::on_key_release checking key->keyval"); - if (illegal_chars) { - return false; - } - std::string c = get_source_buffer()->get_text(end, beg); - switch (key->keyval) { - case 46: - break; - case 58: - if (c != "::") return false; - break; - case 60: - if (c != "->") return false; - break; - case 62: - if (c != "->") return false; - break; - default: - return false; - } - } else { - return false; - } - delayed_reparse_connection.disconnect(); - if(!autocomplete_running) { - autocomplete_running=true; - cancel_show_autocomplete=false; - INFO("Source::ClangView::on_key_release getting autocompletions"); - std::shared_ptr > ac_data=std::make_shared >(); - autocomplete_done_connection.disconnect(); - autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){ - if(!cancel_show_autocomplete) { - std::map > rows; - for (auto &data : *ac_data) { - std::stringstream ss; - std::string return_value; - for (auto &chunk : data.chunks) { - switch (chunk.kind) { - case clang::CompletionChunk_ResultType: - return_value = chunk.chunk; - break; - case clang::CompletionChunk_Informative: break; - default: ss << chunk.chunk; break; - } - } - if (ss.str().length() > 0) { // if length is 0 the result is empty - auto pair=std::pair(ss.str(), data.brief_comments); - rows[ss.str() + " --> " + return_value] = pair; - } - } - if (rows.empty()) { - rows["No suggestions found..."] = std::pair(); - } - selection_dialog.rows=std::move(rows); - selection_dialog.show(); - } - autocomplete_running=false; - }); - - std::shared_ptr > buffer_map=std::make_shared >(); - (*buffer_map)[file_path]=get_buffer()->get_text(get_buffer()->begin(), get_buffer()->get_insert()->get_iter()); - (*buffer_map)[file_path]+="\n"; - auto line_nr=beg.get_line()+1; - auto column_nr=beg.get_line_offset()+2; - std::thread autocomplete_thread([this, ac_data, line_nr, column_nr, buffer_map](){ - parsing_mutex.lock(); - *ac_data=move(get_autocomplete_suggestions(line_nr, column_nr, *buffer_map)); - autocomplete_done(); - parsing_mutex.unlock(); - }); - - autocomplete_thread.detach(); - } - else { - std::map > rows; - rows["Autocomplete already running, try again."] = std::pair("", ""); - selection_dialog.rows=std::move(rows); - selection_dialog.show(); - } - - return false; -} - //Clang indentation //TODO: replace indentation methods with a better implementation or maybe use libclang -bool Source::ClangView::on_key_press(GdkEventKey* key) { - if(selection_dialog.shown) { - if(selection_dialog.on_key_press(key)) - return true; - } +bool Source::ClangView::on_key_press_event(GdkEventKey* key) { auto config=Singletons::Config::source(); const std::regex bracket_regex(std::string("^(")+config->tab_char+"*).*\\{ *$"); const std::regex no_bracket_statement_regex(std::string("^(")+config->tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$"); @@ -713,7 +577,143 @@ bool Source::ClangView::on_key_press(GdkEventKey* key) { return false; } - return Source::View::on_key_press(key); + return Source::View::on_key_press_event(key); +} + +////////////////////////////// +//// ClangViewAutocomplete /// +////////////////////////////// + +Source::ClangViewAutocomplete::ClangViewAutocomplete(const std::string& file_path, const std::string& project_path, Terminal::Controller& terminal): +Source::ClangView(file_path, project_path, terminal), selection_dialog(*this) { + get_buffer()->signal_changed().connect([this](){ + if(!selection_dialog.shown) { + auto insert=get_buffer()->get_insert(); + auto iter=insert->get_iter(); + int line_nr=iter.get_line(); + auto line_iter=get_buffer()->get_iter_at_line(line_nr); + std::string line=get_buffer()->get_text(line_iter, iter); + const std::regex method("^(.*)(->|\\.|::)([a-zA-Z0-9_]*)$"); + std::smatch sm; + if(std::regex_match(line, sm, method)) { + if(sm[1].str().find("//")==std::string::npos) { + if(last_keyval=='.' || last_keyval=='>' || last_keyval==':') { + if(sm[3]=="" && !autocomplete_running) { + start_autocomplete(); + } + } + } + } + else if(autocomplete_running) + cancel_show_autocomplete=true; + } + }); + get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr& mark){ + if(mark->get_name()=="insert") { + cancel_show_autocomplete=true; + if(selection_dialog.shown) { + selection_dialog.hide(); + } + } + }); + signal_scroll_event().connect([this](GdkEventScroll* event){ + if(selection_dialog.shown) + selection_dialog.move(); + return false; + }, false); + signal_key_release_event().connect([this](GdkEventKey* key){ + if(selection_dialog.shown) { + if(selection_dialog.on_key_release(key)) + return true; + } + + return false; + }, false); +} + +bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) { + if(selection_dialog.shown) { + delayed_reparse_connection.disconnect(); + if(selection_dialog.on_key_press(key)) + return true; + } + last_keyval=key->keyval; + return ClangView::on_key_press_event(key); +} +void Source::ClangViewAutocomplete::start_autocomplete() { + delayed_reparse_connection.disconnect(); + if(!autocomplete_running) { + autocomplete_running=true; + cancel_show_autocomplete=false; + INFO("Source::ClangView::on_key_release getting autocompletions"); + std::shared_ptr > ac_data=std::make_shared >(); + if(selection_dialog.start_mark) + get_buffer()->delete_mark(selection_dialog.start_mark); + selection_dialog.start_mark=get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()); + autocomplete_done_connection.disconnect(); + autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){ + if(!cancel_show_autocomplete) { + std::map > rows; + for (auto &data : *ac_data) { + std::stringstream ss; + std::string return_value; + for (auto &chunk : data.chunks) { + switch (chunk.kind) { + case clang::CompletionChunk_ResultType: + return_value = chunk.chunk; + break; + case clang::CompletionChunk_Informative: break; + default: ss << chunk.chunk; break; + } + } + if (ss.str().length() > 0) { // if length is 0 the result is empty + auto pair=std::pair(ss.str(), data.brief_comments); + rows[ss.str() + " --> " + return_value] = pair; + } + } + if (rows.empty()) { + rows["No suggestions found..."] = std::pair(); + } + selection_dialog.rows=std::move(rows); + selection_dialog.show(); + } + autocomplete_running=false; + }); + + std::shared_ptr > buffer_map=std::make_shared >(); + (*buffer_map)[this->file_path]=get_buffer()->get_text(get_buffer()->begin(), get_buffer()->get_insert()->get_iter()); + (*buffer_map)[this->file_path]+="\n"; + auto iter = get_source_buffer()->get_insert()->get_iter(); + auto line_nr=iter.get_line()+1; + auto column_nr=iter.get_line_offset()+2; + std::thread autocomplete_thread([this, ac_data, line_nr, column_nr, buffer_map](){ + parsing_mutex.lock(); + *ac_data=move(get_autocomplete_suggestions(line_nr, column_nr, *buffer_map)); + autocomplete_done(); + parsing_mutex.unlock(); + }); + + autocomplete_thread.detach(); + } +} + +std::vector Source::ClangViewAutocomplete:: +get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map) { + INFO("Getting auto complete suggestions"); + std::vector suggestions; + clang::CodeCompleteResults results(clang_tu.get(), + file_path, + buffer_map, + line_number, + column-1); + for (int i = 0; i < results.size(); i++) { + auto result=results.get(i); + suggestions.emplace_back(result.get_chunks()); + suggestions.back().brief_comments=result.get_brief_comments(); + } + DEBUG("Number of suggestions"); + DEBUG_VAR(suggestions.size()); + return suggestions; } //////////////////// @@ -727,7 +727,7 @@ Source::Controller::Controller(const std::string& file_path, std::string project project_path=boost::filesystem::path(file_path).parent_path().string(); } if (Singletons::Config::source()->legal_extension(file_path.substr(file_path.find_last_of(".") + 1))) - view=std::unique_ptr(new ClangView(file_path, project_path, terminal)); + view=std::unique_ptr(new ClangViewAutocomplete(file_path, project_path, terminal)); else view=std::unique_ptr(new GenericView(file_path, project_path)); INFO("Source Controller with childs constructed"); diff --git a/juci/source.h b/juci/source.h index 2f9182b..6c87d4e 100644 --- a/juci/source.h +++ b/juci/source.h @@ -60,18 +60,16 @@ namespace Source { std::string project_path; Gtk::TextIter search_start, search_end; protected: - bool on_key_press(GdkEventKey* key); + bool on_key_press_event(GdkEventKey* key); }; // class View class GenericView : public View { public: GenericView(const std::string& file_path, const std::string& project_path): - View(file_path, project_path) { - signal_key_press_event().connect(sigc::mem_fun(*this, &Source::GenericView::on_key_press), false); - } - private: - bool on_key_press(GdkEventKey* key) { - return Source::View::on_key_press(key); + View(file_path, project_path) {} + protected: + bool on_key_press_event(GdkEventKey* key) { + return Source::View::on_key_press_event(key); } }; @@ -79,6 +77,12 @@ namespace Source { public: ClangView(const std::string& file_path, const std::string& project_path, Terminal::Controller& terminal); ~ClangView(); + protected: + std::unique_ptr clang_tu; + std::map get_buffer_map() const; + std::mutex parsing_mutex; + sigc::connection delayed_reparse_connection; + bool on_key_press_event(GdkEventKey* key); private: // inits the syntax highligthing on file open void init_syntax_highlighting(const std::map @@ -86,9 +90,6 @@ namespace Source { int start_offset, int end_offset, clang::Index *index); - std::vector get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map); - SelectionDialog selection_dialog; - int reparse(const std::map &buffers); void update_syntax(); void update_diagnostics(); @@ -100,12 +101,7 @@ namespace Source { sigc::connection delayed_tooltips_connection; bool clangview_on_focus_out_event(GdkEventFocus* event); bool clangview_on_scroll_event(GdkEventScroll* event); - static clang::Index clang_index; - std::map get_buffer_map() const; - std::mutex parsing_mutex; - - std::unique_ptr clang_tu; std::unique_ptr clang_tokens; bool clang_readable=false; void highlight_token(clang::Token *token, @@ -114,17 +110,9 @@ namespace Source { void highlight_cursor(clang::Token *token, std::vector *source_ranges); std::vector get_compilation_commands(); - bool on_key_press(GdkEventKey* key); - bool on_key_release(GdkEventKey* key); Terminal::Controller& terminal; - - Glib::Dispatcher autocomplete_done; - sigc::connection autocomplete_done_connection; - - sigc::connection delayed_reparse_connection; + std::shared_ptr parsing_in_progress; - bool autocomplete_running=false; - bool cancel_show_autocomplete=false; Glib::Dispatcher parse_done; Glib::Dispatcher parse_start; std::thread parse_thread; @@ -134,6 +122,22 @@ namespace Source { std::atomic parse_thread_mapped; std::atomic parse_thread_stop; }; + + class ClangViewAutocomplete : public ClangView { + public: + ClangViewAutocomplete(const std::string& file_path, const std::string& project_path, Terminal::Controller& terminal); + protected: + bool on_key_press_event(GdkEventKey* key); + private: + void start_autocomplete(); + SelectionDialog selection_dialog; + std::vector get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map); + Glib::Dispatcher autocomplete_done; + sigc::connection autocomplete_done_connection; + bool autocomplete_running=false; + bool cancel_show_autocomplete=false; + char last_keyval=0; + }; class Controller { public: