Browse Source

Autocomplete cleanup. Should now be easier to implement autocomplete for other languages

merge-requests/365/head
eidheim 9 years ago
parent
commit
430925ada0
  1. 214
      src/autocomplete.h
  2. 2
      src/selection_dialog.cc
  3. 1
      src/selection_dialog.h
  4. 405
      src/source_clang.cc
  5. 20
      src/source_clang.h

214
src/autocomplete.h

@ -0,0 +1,214 @@
#ifndef JUCI_AUTOCOMPLETE_H_
#define JUCI_AUTOCOMPLETE_H_
#include "dispatcher.h"
#include "selection_dialog.h"
#include "tooltips.h"
#include <thread>
template <class Suggestion>
class Autocomplete {
Gtk::TextView *view;
/// Some libraries/utilities, like libclang, require that autocomplete is started at the beginning of a word
bool strip_word;
Dispatcher dispatcher;
public:
enum class State { IDLE, STARTING, RESTARTING, CANCELED };
std::string prefix;
std::mutex prefix_mutex;
std::unordered_map<std::string, std::pair<std::string, std::string>> rows;
Tooltips tooltips;
std::atomic<State> state;
std::thread thread;
std::function<bool()> is_processing = [] { return true; };
std::function<void()> reparse = [] {};
std::function<void()> cancel_reparse = [] {};
std::function<std::unique_ptr<std::lock_guard<std::mutex>>()> get_parse_lock = [] { return nullptr; };
std::function<void()> stop_parse = [] {};
std::function<bool(guint last_keyval)> is_continue_key = [](guint) { return false; };
std::function<bool(guint last_keyval)> is_restart_key = [](guint) { return false; };
std::function<bool()> run_check = [] { return false; };
std::function<void()> before_get_suggestions = [] {};
std::function<void()> after_get_suggestions = [] {};
std::function<void()> on_get_suggestions_error = [] {};
std::function<std::shared_ptr<std::vector<Suggestion>>(std::string &buffer, int line_number, int column)> get_suggestions = [](std::string &, int, int) { return std::make_shared<std::vector<Suggestion>>(); };
std::function<void(Suggestion &)> foreach_suggestion = [](Suggestion &) {};
std::function<void()> setup_dialog = [] {};
Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word): view(view), state(State::IDLE), strip_word(strip_word) {
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<Gtk::TextBuffer::Mark> &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);
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<Glib::ustring>(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<std::string &>(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();
}
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<std::string>(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);
}
};
}
};
#endif // JUCI_AUTOCOMPLETE_H_

2
src/selection_dialog.cc

@ -160,6 +160,8 @@ void SelectionDialogBase::show() {
list_view_text.scroll_to_row(list_view_text.get_model()->get_path(list_view_text.get_selection()->get_selected()), 0.5); list_view_text.scroll_to_row(list_view_text.get_model()->get_path(list_view_text.get_selection()->get_selected()), 0.5);
} }
} }
if(on_show)
on_show();
} }
void SelectionDialogBase::set_cursor_at_last_row() { void SelectionDialogBase::set_cursor_at_last_row() {

1
src/selection_dialog.h

@ -41,6 +41,7 @@ public:
bool is_visible() {return window.is_visible();} bool is_visible() {return window.is_visible();}
void get_position(int &root_x, int &root_y) {window.get_position(root_x, root_y);} void get_position(int &root_x, int &root_y) {window.get_position(root_x, root_y);}
std::function<void()> on_show;
std::function<void()> on_hide; std::function<void()> on_hide;
std::function<void(const std::string& selected, bool hide_window)> on_select; std::function<void(const std::string& selected, bool hide_window)> on_select;
std::function<void(const std::string &selected)> on_changed; std::function<void(const std::string &selected)> on_changed;

405
src/source_clang.cc

@ -568,74 +568,156 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle)
Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language): Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language):
Source::ClangViewParse(file_path, language), autocomplete_state(AutocompleteState::IDLE) { Source::ClangViewParse(file_path, language), autocomplete(this, interactive_completion, last_keyval, true) {
non_interactive_completion=[this] { non_interactive_completion=[this] {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) if(CompletionDialog::get() && CompletionDialog::get()->is_visible())
return; return;
autocomplete_check(); autocomplete.run();
}; };
get_buffer()->signal_changed().connect([this](){ autocomplete.is_processing=[this] {
if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) return parse_state==ParseState::PROCESSING;
};
autocomplete.reparse=[this] {
soft_reparse();
};
autocomplete.cancel_reparse=[this] {
delayed_reparse_connection.disconnect(); delayed_reparse_connection.disconnect();
else { };
if(!has_focus())
return; autocomplete.get_parse_lock=[this]() {
if((last_keyval>='0' && last_keyval<='9') || return std::make_unique<std::lock_guard<std::mutex>>(parse_mutex);
(last_keyval>='a' && last_keyval<='z') || (last_keyval>='A' && last_keyval<='Z') || };
last_keyval=='_') {
if(interactive_completion || autocomplete_state!=AutocompleteState::IDLE) autocomplete.stop_parse=[this]() {
autocomplete_check(); parse_process_state=ParseProcessState::IDLE;
} };
else {
if(autocomplete_state==AutocompleteState::STARTING || autocomplete_state==AutocompleteState::RESTARTING) autocomplete.is_continue_key=[this](guint keyval) {
autocomplete_state=AutocompleteState::CANCELED; if((keyval>='0' && keyval<='9') || (keyval>='a' && keyval<='z') || (keyval>='A' && keyval<='Z') || keyval=='_')
return true;
return false;
};
autocomplete.is_restart_key=[this](guint keyval) {
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
iter.backward_chars(2); iter.backward_chars(2);
if(last_keyval=='.' || (last_keyval==':' && *iter==':') || (last_keyval=='>' && *iter=='-')) { if(keyval=='.' || (keyval==':' && *iter==':') || (keyval=='>' && *iter=='-'))
if(interactive_completion) return true;
autocomplete_check(); return false;
} };
autocomplete.run_check=[this]() {
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char() && iter.backward_char() && !is_code_iter(iter))
return 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,})$");
std::smatch sm;
if(std::regex_match(line, sm, in_specified_namespace)) {
{
std::unique_lock<std::mutex> lock(autocomplete.prefix_mutex);
autocomplete.prefix=sm[2].str();
} }
if(autocomplete.prefix.size()==0 || autocomplete.prefix[0]<'0' || autocomplete.prefix[0]>'9')
return true;
} }
}); else if(std::regex_match(line, sm, within_namespace)) {
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){ {
if(mark->get_name()=="insert") { std::unique_lock<std::mutex> lock(autocomplete.prefix_mutex);
if(autocomplete_state==AutocompleteState::STARTING || autocomplete_state==AutocompleteState::RESTARTING) autocomplete.prefix=sm[1].str();
autocomplete_state=AutocompleteState::CANCELED;
} }
}); if(autocomplete.prefix.size()==0 || autocomplete.prefix[0]<'0' || autocomplete.prefix[0]>'9')
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 true;
} }
return false; return false;
}, false); };
signal_focus_out_event().connect([this](GdkEventFocus* event) { autocomplete.before_get_suggestions=[this] {
if(autocomplete_state==AutocompleteState::STARTING || autocomplete_state==AutocompleteState::RESTARTING) status_state="autocomplete...";
autocomplete_state=AutocompleteState::CANCELED; if(update_status_state)
return false; update_status_state(this);
}); };
}
void Source::ClangViewAutocomplete::autocomplete_dialog_setup() { autocomplete.after_get_suggestions=[this] {
auto start_iter=get_buffer()->get_insert()->get_iter(); status_state="";
if(prefix.size()>0 && !start_iter.backward_chars(prefix.size())) if(update_status_state)
return; update_status_state(this);
CompletionDialog::create(this, get_buffer()->create_mark(start_iter));
completion_dialog_rows.clear();
CompletionDialog::get()->on_hide=[this](){
get_buffer()->end_user_action();
autocomplete_tooltips.hide();
autocomplete_tooltips.clear();
parsed=false;
soft_reparse();
}; };
CompletionDialog::get()->on_select=[this](const std::string& selected, bool hide_window) {
auto row = completion_dialog_rows.at(selected).first; autocomplete.on_get_suggestions_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<std::vector<Suggestion>>();
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;
}
if(autocomplete.state==Autocomplete<Suggestion>::State::STARTING) {
std::string prefix_copy;
{
std::lock_guard<std::mutex> 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;
}
}
if(match) {
suggestions->emplace_back(std::move(chunks));
suggestions->back().brief_comments=result.get_brief_comments();
}
}
}
}
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::string, std::string>(std::move(row_insert_on_selection), std::move(suggestion.brief_comments));
CompletionDialog::get()->add_row(row);
}
};
autocomplete.setup_dialog=[this] {
CompletionDialog::get()->on_show=[this] {
hide_tooltips();
};
CompletionDialog::get()->on_select=[this](const std::string &selected, bool hide_window) {
auto row = autocomplete.rows.at(selected).first;
//erase existing variable or function before insert iter //erase existing variable or function before insert iter
get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_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 //do not insert template argument or function parameters if they already exist
@ -647,7 +729,7 @@ void Source::ClangViewAutocomplete::autocomplete_dialog_setup() {
} }
} }
//Fixes for the most commonly used stream manipulators //Fixes for the most commonly used stream manipulators
static auto manipulators_map=autocomplete_manipulators_map(); auto manipulators_map=autocomplete_manipulators_map();
auto it=manipulators_map.find(row); auto it=manipulators_map.find(row);
if(it!=manipulators_map.end()) if(it!=manipulators_map.end())
row=it->second; row=it->second;
@ -688,212 +770,23 @@ void Source::ClangViewAutocomplete::autocomplete_dialog_setup() {
//new autocomplete after for instance when selecting "std::" //new autocomplete after for instance when selecting "std::"
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char() && *iter==':') if(iter.backward_char() && *iter==':')
autocomplete_check(); autocomplete.run();
} }
} }
}; };
CompletionDialog::get()->on_changed=[this](const std::string &selected) {
if(selected.empty()) {
autocomplete_tooltips.hide();
return;
}
auto tooltip=std::make_shared<std::string>(completion_dialog_rows.at(selected).second);
if(tooltip->empty()) {
autocomplete_tooltips.hide();
}
else {
autocomplete_tooltips.clear();
auto create_tooltip_buffer=[this, tooltip]() {
auto tooltip_buffer=Gtk::TextBuffer::create(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();
autocomplete_tooltips.emplace_back(create_tooltip_buffer, this, get_buffer()->create_mark(iter), get_buffer()->create_mark(iter));
autocomplete_tooltips.show(true);
}
}; };
} }
void Source::ClangViewAutocomplete::autocomplete_check() { const std::unordered_map<std::string, std::string> &Source::ClangViewAutocomplete::autocomplete_manipulators_map() {
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char() && iter.backward_char() && !is_code_iter(iter))
return;
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,})$");
std::smatch sm;
if(std::regex_match(line, sm, in_specified_namespace)) {
{
std::unique_lock<std::mutex> lock(prefix_mutex);
prefix=sm[3].str();
}
if(prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9')
autocomplete();
}
else if(std::regex_match(line, sm, within_namespace)) {
{
std::unique_lock<std::mutex> lock(prefix_mutex);
prefix=sm[3].str();
}
if(prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9')
autocomplete();
}
if(autocomplete_state!=AutocompleteState::IDLE)
delayed_reparse_connection.disconnect();
}
void Source::ClangViewAutocomplete::autocomplete() {
if(parse_state!=ParseState::PROCESSING)
return;
if(autocomplete_state==AutocompleteState::CANCELED)
autocomplete_state=AutocompleteState::RESTARTING;
if(autocomplete_state!=AutocompleteState::IDLE)
return;
autocomplete_state=AutocompleteState::STARTING;
status_state="autocomplete...";
if(update_status_state)
update_status_state(this);
if(autocomplete_thread.joinable())
autocomplete_thread.join();
auto buffer=std::make_shared<Glib::ustring>(get_buffer()->get_text());
auto iter=get_buffer()->get_insert()->get_iter();
auto line_nr=iter.get_line()+1;
auto column_nr=iter.get_line_index()+1;
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--;
}
autocomplete_thread=std::thread([this, line_nr, column_nr, buffer](){
std::unique_lock<std::mutex> lock(parse_mutex);
if(parse_state==ParseState::PROCESSING) {
parse_process_state=ParseProcessState::IDLE;
auto &buffer_raw=const_cast<std::string&>(buffer->raw());
remove_include_guard(buffer_raw);
auto autocomplete_data=std::make_shared<std::vector<AutoCompleteData> >(autocomplete_get_suggestions(buffer_raw, line_nr, column_nr));
if(parse_state==ParseState::PROCESSING) {
dispatcher.post([this, autocomplete_data] {
if(autocomplete_state==AutocompleteState::CANCELED) {
status_state="";
if(update_status_state)
update_status_state(this);
soft_reparse();
autocomplete_state=AutocompleteState::IDLE;
}
else if(autocomplete_state==AutocompleteState::RESTARTING) {
status_state="";
if(update_status_state)
update_status_state(this);
soft_reparse();
autocomplete_state=AutocompleteState::IDLE;
autocomplete_check();
}
else {
autocomplete_dialog_setup();
for (auto &data : *autocomplete_data) {
std::string row;
std::string return_value;
for (auto &chunk : data.chunks) {
if(chunk.kind==clangmm::CompletionChunk_ResultType)
return_value=chunk.chunk;
else if(chunk.kind!=clangmm::CompletionChunk_Informative)
row+=chunk.chunk;
}
data.chunks.clear();
if (!row.empty()) {
auto row_insert_on_selection=row;
if(!return_value.empty())
row+=" --> " + return_value;
completion_dialog_rows[row] = std::pair<std::string, std::string>(std::move(row_insert_on_selection), std::move(data.brief_comments));
CompletionDialog::get()->add_row(row);
}
}
autocomplete_data->clear();
status_state="";
if(update_status_state)
update_status_state(this);
autocomplete_state=AutocompleteState::IDLE;
if (!completion_dialog_rows.empty()) {
get_buffer()->begin_user_action();
hide_tooltips();
CompletionDialog::get()->show();
}
else
soft_reparse();
}
});
}
else {
dispatcher.post([this] {
Terminal::get().print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true);
autocomplete_state=AutocompleteState::CANCELED;
full_reparse();
});
}
}
});
}
std::vector<Source::ClangViewAutocomplete::AutoCompleteData> Source::ClangViewAutocomplete::autocomplete_get_suggestions(const std::string &buffer, int line_number, int column) {
std::vector<AutoCompleteData> suggestions;
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;
}
if(autocomplete_state==AutocompleteState::STARTING) {
std::unique_lock<std::mutex> lock(prefix_mutex);
auto prefix_copy=prefix;
lock.unlock();
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;
}
}
if(match) {
suggestions.emplace_back(std::move(chunks));
suggestions.back().brief_comments=result.get_brief_comments();
}
}
}
}
return suggestions;
}
std::unordered_map<std::string, std::string> Source::ClangViewAutocomplete::autocomplete_manipulators_map() {
std::unordered_map<std::string, std::string> map;
//TODO: feel free to add more //TODO: feel free to add more
map["endl(basic_ostream<_CharT, _Traits> &__os)"]="endl"; static std::unordered_map<std::string, std::string> map={
map["flush(basic_ostream<_CharT, _Traits> &__os)"]="flush"; {"endl(basic_ostream<_CharT, _Traits> &__os)", "endl"},
map["hex(std::ios_base &__str)"]="hex"; //clang++ headers {"flush(basic_ostream<_CharT, _Traits> &__os)", "flush"},
map["hex(std::ios_base &__base)"]="hex"; //g++ headers {"hex(std::ios_base &__str)", "hex"}, //clang++ headers
map["dec(std::ios_base &__str)"]="dec"; {"hex(std::ios_base &__base)", "hex"}, //g++ headers
map["dec(std::ios_base &__base)"]="dec"; {"dec(std::ios_base &__str)", "dec"},
{"dec(std::ios_base &__base)", "dec"}
};
return map; return map;
} }
@ -1696,7 +1589,7 @@ void Source::ClangView::full_reparse() {
return; return;
} }
} }
autocomplete_state=AutocompleteState::IDLE; autocomplete.state=Autocomplete<ClangViewAutocomplete::Suggestion>::State::IDLE;
soft_reparse_needed=false; soft_reparse_needed=false;
full_reparse_running=true; full_reparse_running=true;
if(full_reparse_thread.joinable()) if(full_reparse_thread.joinable())
@ -1704,8 +1597,8 @@ void Source::ClangView::full_reparse() {
full_reparse_thread=std::thread([this](){ full_reparse_thread=std::thread([this](){
if(parse_thread.joinable()) if(parse_thread.joinable())
parse_thread.join(); parse_thread.join();
if(autocomplete_thread.joinable()) if(autocomplete.thread.joinable())
autocomplete_thread.join(); autocomplete.thread.join();
dispatcher.post([this] { dispatcher.post([this] {
parse_initialize(); parse_initialize();
full_reparse_running=false; full_reparse_running=false;
@ -1726,8 +1619,8 @@ void Source::ClangView::async_delete() {
full_reparse_thread.join(); full_reparse_thread.join();
if(parse_thread.joinable()) if(parse_thread.joinable())
parse_thread.join(); parse_thread.join();
if(autocomplete_thread.joinable()) if(autocomplete.thread.joinable())
autocomplete_thread.join(); autocomplete.thread.join();
do_delete_object(); do_delete_object();
}); });
} }

20
src/source_clang.h

@ -9,6 +9,7 @@
#include "source.h" #include "source.h"
#include "terminal.h" #include "terminal.h"
#include "dispatcher.h" #include "dispatcher.h"
#include "autocomplete.h"
namespace Source { namespace Source {
class ClangViewParse : public View { class ClangViewParse : public View {
@ -58,12 +59,10 @@ namespace Source {
}; };
class ClangViewAutocomplete : public virtual ClangViewParse { class ClangViewAutocomplete : public virtual ClangViewParse {
protected:
enum class AutocompleteState {IDLE, STARTING, RESTARTING, CANCELED};
public: public:
class AutoCompleteData { class Suggestion {
public: public:
explicit AutoCompleteData(const std::vector<clangmm::CompletionChunk> &chunks) : explicit Suggestion(std::vector<clangmm::CompletionChunk> &&chunks) :
chunks(chunks) { } chunks(chunks) { }
std::vector<clangmm::CompletionChunk> chunks; std::vector<clangmm::CompletionChunk> chunks;
std::string brief_comments; std::string brief_comments;
@ -71,18 +70,9 @@ namespace Source {
ClangViewAutocomplete(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language); ClangViewAutocomplete(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language);
protected: protected:
std::atomic<AutocompleteState> autocomplete_state; Autocomplete<Suggestion> autocomplete;
std::thread autocomplete_thread;
private: private:
void autocomplete_dialog_setup(); const std::unordered_map<std::string, std::string> &autocomplete_manipulators_map();
void autocomplete_check();
void autocomplete();
std::unordered_map<std::string, std::pair<std::string, std::string> > completion_dialog_rows;
std::vector<AutoCompleteData> autocomplete_get_suggestions(const std::string &buffer, int line_number, int column);
Tooltips autocomplete_tooltips;
std::string prefix;
std::mutex prefix_mutex;
static std::unordered_map<std::string, std::string> autocomplete_manipulators_map();
}; };
class ClangViewRefactor : public virtual ClangViewParse { class ClangViewRefactor : public virtual ClangViewParse {

Loading…
Cancel
Save