|
|
|
|
@ -21,7 +21,7 @@ namespace sigc {
|
|
|
|
|
clang::Index Source::ClangViewParse::clang_index(0, 0); |
|
|
|
|
|
|
|
|
|
Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language): |
|
|
|
|
Source::View(file_path, project_path, language), parse_error(false) { |
|
|
|
|
Source::View(file_path, project_path, language) { |
|
|
|
|
JDEBUG("start"); |
|
|
|
|
|
|
|
|
|
auto tag_table=get_buffer()->get_tag_table(); |
|
|
|
|
@ -35,36 +35,35 @@ Source::View(file_path, project_path, language), parse_error(false) {
|
|
|
|
|
parsing_in_progress=Singleton::terminal->print_in_progress("Parsing "+file_path.string()); |
|
|
|
|
//GTK-calls must happen in main thread, so the parse_thread
|
|
|
|
|
//sends signals to the main thread that it is to call the following functions:
|
|
|
|
|
parse_start_connection=parse_start.connect([this]{ |
|
|
|
|
if(parse_thread_buffer_map_mutex.try_lock()) { |
|
|
|
|
parse_thread_buffer_map=get_buffer_map(); |
|
|
|
|
parse_thread_mapped=true; |
|
|
|
|
parse_thread_buffer_map_mutex.unlock(); |
|
|
|
|
parse_preprocess_connection=parse_preprocess.connect([this]{ |
|
|
|
|
auto expected=ParseProcessState::PREPROCESSING; |
|
|
|
|
if(parse_mutex.try_lock()) { |
|
|
|
|
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING)) |
|
|
|
|
parse_thread_buffer=get_buffer()->get_text(); |
|
|
|
|
parse_mutex.unlock(); |
|
|
|
|
} |
|
|
|
|
parse_thread_go=true; |
|
|
|
|
else |
|
|
|
|
parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING); |
|
|
|
|
}); |
|
|
|
|
parse_done_connection=parse_done.connect([this](){ |
|
|
|
|
if(parse_thread_mapped) { |
|
|
|
|
if(parsing_mutex.try_lock()) { |
|
|
|
|
parse_postprocess_connection=parse_postprocess.connect([this](){ |
|
|
|
|
if(parse_mutex.try_lock()) { |
|
|
|
|
auto expected=ParseProcessState::POSTPROCESSING; |
|
|
|
|
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) { |
|
|
|
|
update_syntax(); |
|
|
|
|
update_diagnostics(); |
|
|
|
|
parsed=true; |
|
|
|
|
set_status(""); |
|
|
|
|
parsing_mutex.unlock(); |
|
|
|
|
} |
|
|
|
|
parsing_in_progress->done("done"); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
parse_thread_go=true; |
|
|
|
|
parse_mutex.unlock(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
parse_fail_connection=parse_fail.connect([this](){ |
|
|
|
|
parse_error_connection=parse_error.connect([this](){ |
|
|
|
|
Singleton::terminal->print("Error: failed to reparse "+this->file_path.string()+".\n", true); |
|
|
|
|
set_status(""); |
|
|
|
|
set_info(""); |
|
|
|
|
parsing_in_progress->cancel("failed"); |
|
|
|
|
}); |
|
|
|
|
init_parse(); |
|
|
|
|
parse_initialize(); |
|
|
|
|
|
|
|
|
|
get_buffer()->signal_changed().connect([this]() { |
|
|
|
|
soft_reparse(); |
|
|
|
|
@ -105,78 +104,76 @@ void Source::ClangViewParse::configure() {
|
|
|
|
|
no_bracket_no_para_statement_regex=boost::regex("^([ \\t]*)(else|try|do) *$"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Source::ClangViewParse::init_parse() { |
|
|
|
|
void Source::ClangViewParse::parse_initialize() { |
|
|
|
|
type_tooltips.hide(); |
|
|
|
|
diagnostic_tooltips.hide(); |
|
|
|
|
parsed=false; |
|
|
|
|
parse_thread_go=true; |
|
|
|
|
parse_thread_mapped=false; |
|
|
|
|
parse_thread_stop=false; |
|
|
|
|
if(parse_thread.joinable()) |
|
|
|
|
parse_thread.join(); |
|
|
|
|
parse_state=ParseState::PROCESSING; |
|
|
|
|
parse_process_state=ParseProcessState::STARTING; |
|
|
|
|
|
|
|
|
|
auto buffer_map=get_buffer_map(); |
|
|
|
|
auto buffer=get_buffer()->get_text(); |
|
|
|
|
//Remove includes for first parse for initial syntax highlighting
|
|
|
|
|
auto& str=buffer_map[file_path.string()]; |
|
|
|
|
std::size_t pos=0; |
|
|
|
|
while((pos=str.find("#include", pos))!=std::string::npos) { |
|
|
|
|
while((pos=buffer.find("#include", pos))!=std::string::npos) { |
|
|
|
|
auto start_pos=pos; |
|
|
|
|
pos=str.find('\n', pos+8); |
|
|
|
|
pos=buffer.find('\n', pos+8); |
|
|
|
|
if(pos==std::string::npos) |
|
|
|
|
break; |
|
|
|
|
if(start_pos==0 || str[start_pos-1]=='\n') { |
|
|
|
|
str.replace(start_pos, pos-start_pos, pos-start_pos, ' '); |
|
|
|
|
if(start_pos==0 || buffer[start_pos-1]=='\n') { |
|
|
|
|
buffer.replace(start_pos, pos-start_pos, pos-start_pos, ' '); |
|
|
|
|
} |
|
|
|
|
pos++; |
|
|
|
|
} |
|
|
|
|
clang_tu = std::unique_ptr<clang::TranslationUnit>(new clang::TranslationUnit(clang_index, file_path.string(), get_compilation_commands(), buffer_map)); |
|
|
|
|
clang_tokens=clang_tu->get_tokens(0, buffer_map.find(file_path.string())->second.size()-1); |
|
|
|
|
clang_tu = std::unique_ptr<clang::TranslationUnit>(new clang::TranslationUnit(clang_index, file_path.string(), get_compilation_commands(), buffer.raw())); |
|
|
|
|
clang_tokens=clang_tu->get_tokens(0, buffer.bytes()-1); |
|
|
|
|
update_syntax(); |
|
|
|
|
|
|
|
|
|
set_status("parsing..."); |
|
|
|
|
if(parse_thread.joinable()) |
|
|
|
|
parse_thread.join(); |
|
|
|
|
parse_thread=std::thread([this]() { |
|
|
|
|
while(true) { |
|
|
|
|
while(!parse_thread_go && !parse_thread_stop) |
|
|
|
|
while(parse_state==ParseState::PROCESSING && parse_process_state!=ParseProcessState::STARTING && parse_process_state!=ParseProcessState::PROCESSING) |
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
|
|
|
|
if(parse_thread_stop) |
|
|
|
|
if(parse_state!=ParseState::PROCESSING) |
|
|
|
|
break; |
|
|
|
|
if(!parse_thread_mapped) { |
|
|
|
|
parse_thread_go=false; |
|
|
|
|
parse_start(); |
|
|
|
|
} |
|
|
|
|
else if (parse_thread_mapped && parsing_mutex.try_lock() && parse_thread_buffer_map_mutex.try_lock()) { |
|
|
|
|
auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer_map); |
|
|
|
|
if(status==0) |
|
|
|
|
clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer_map.find(file_path.string())->second.size()-1); |
|
|
|
|
else |
|
|
|
|
parse_error=true; |
|
|
|
|
parse_thread_go=false; |
|
|
|
|
parsing_mutex.unlock(); |
|
|
|
|
parse_thread_buffer_map_mutex.unlock(); |
|
|
|
|
if(status!=0) { |
|
|
|
|
parse_fail(); |
|
|
|
|
parse_thread_stop=true; |
|
|
|
|
auto expected=ParseProcessState::STARTING; |
|
|
|
|
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) |
|
|
|
|
parse_preprocess(); |
|
|
|
|
else if (parse_process_state==ParseProcessState::PROCESSING && parse_mutex.try_lock()) { |
|
|
|
|
auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer.raw()); |
|
|
|
|
parsing_in_progress->done("done"); |
|
|
|
|
if(status==0) { |
|
|
|
|
auto expected=ParseProcessState::PROCESSING; |
|
|
|
|
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::POSTPROCESSING)) { |
|
|
|
|
clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer.bytes()-1); |
|
|
|
|
parse_mutex.unlock(); |
|
|
|
|
parse_postprocess(); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
parse_done(); |
|
|
|
|
parse_mutex.unlock(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
parse_state=ParseState::STOP; |
|
|
|
|
parse_mutex.unlock(); |
|
|
|
|
parse_error(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::map<std::string, std::string> Source::ClangViewParse::get_buffer_map() const { |
|
|
|
|
std::map<std::string, std::string> buffer_map; |
|
|
|
|
buffer_map[file_path.string()]=get_source_buffer()->get_text(); |
|
|
|
|
return buffer_map; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Source::ClangViewParse::soft_reparse() { |
|
|
|
|
parse_thread_mapped=false; |
|
|
|
|
soft_reparse_needed=false; |
|
|
|
|
parsed=false; |
|
|
|
|
if(parse_state!=ParseState::PROCESSING) |
|
|
|
|
return; |
|
|
|
|
parse_process_state=ParseProcessState::IDLE; |
|
|
|
|
delayed_reparse_connection.disconnect(); |
|
|
|
|
delayed_reparse_connection=Glib::signal_timeout().connect([this]() { |
|
|
|
|
parsed=false; |
|
|
|
|
parse_thread_go=true; |
|
|
|
|
auto expected=ParseProcessState::IDLE; |
|
|
|
|
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING)) |
|
|
|
|
set_status("parsing..."); |
|
|
|
|
return false; |
|
|
|
|
}, 1000); |
|
|
|
|
@ -613,27 +610,45 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
|
|
|
|
|
//// ClangViewAutocomplete ///
|
|
|
|
|
//////////////////////////////
|
|
|
|
|
Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language): |
|
|
|
|
Source::ClangViewParse(file_path, project_path, language), autocomplete_cancel_starting(false) { |
|
|
|
|
Source::ClangViewParse(file_path, project_path, language), autocomplete_state(AutocompleteState::IDLE) { |
|
|
|
|
get_buffer()->signal_changed().connect([this](){ |
|
|
|
|
if(completion_dialog_shown) |
|
|
|
|
if(autocomplete_state==AutocompleteState::SHOWN) |
|
|
|
|
delayed_reparse_connection.disconnect(); |
|
|
|
|
start_autocomplete(); |
|
|
|
|
else { |
|
|
|
|
if(!has_focus()) |
|
|
|
|
return; |
|
|
|
|
if((last_keyval>='0' && last_keyval<='9') || |
|
|
|
|
(last_keyval>='a' && last_keyval<='z') || (last_keyval>='A' && last_keyval<='Z') || |
|
|
|
|
last_keyval=='_') { |
|
|
|
|
autocomplete_check(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
if(autocomplete_state==AutocompleteState::STARTING) |
|
|
|
|
autocomplete_state=AutocompleteState::CANCELED; |
|
|
|
|
else { |
|
|
|
|
auto iter=get_buffer()->get_insert()->get_iter(); |
|
|
|
|
if(last_keyval=='.' || last_keyval==':' || (last_keyval=='>' && iter.backward_char() && iter.backward_char() && *iter=='-')) |
|
|
|
|
autocomplete_check(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){ |
|
|
|
|
if(mark->get_name()=="insert") { |
|
|
|
|
autocomplete_cancel_starting=true; |
|
|
|
|
if(completion_dialog_shown) |
|
|
|
|
completion_dialog->hide(); |
|
|
|
|
if(autocomplete_state==AutocompleteState::SHOWN) |
|
|
|
|
autocomplete_dialog->hide(); |
|
|
|
|
if(autocomplete_state==AutocompleteState::STARTING) |
|
|
|
|
autocomplete_state=AutocompleteState::CANCELED; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
signal_scroll_event().connect([this](GdkEventScroll* event){ |
|
|
|
|
if(completion_dialog_shown) |
|
|
|
|
completion_dialog->hide(); |
|
|
|
|
if(autocomplete_state==AutocompleteState::SHOWN) |
|
|
|
|
autocomplete_dialog->hide(); |
|
|
|
|
return false; |
|
|
|
|
}, false); |
|
|
|
|
signal_key_release_event().connect([this](GdkEventKey* key){ |
|
|
|
|
if(completion_dialog_shown) { |
|
|
|
|
if(completion_dialog->on_key_release(key)) |
|
|
|
|
if(autocomplete_state==AutocompleteState::SHOWN) { |
|
|
|
|
if(autocomplete_dialog->on_key_release(key)) |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -641,18 +656,64 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_cancel_s
|
|
|
|
|
}, false); |
|
|
|
|
|
|
|
|
|
signal_focus_out_event().connect([this](GdkEventFocus* event) { |
|
|
|
|
autocomplete_cancel_starting=true; |
|
|
|
|
if(completion_dialog_shown) |
|
|
|
|
completion_dialog->hide(); |
|
|
|
|
|
|
|
|
|
if(autocomplete_state==AutocompleteState::SHOWN) |
|
|
|
|
autocomplete_dialog->hide(); |
|
|
|
|
if(autocomplete_state==AutocompleteState::STARTING) |
|
|
|
|
autocomplete_state=AutocompleteState::CANCELED; |
|
|
|
|
return false; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
autocomplete_fail_connection=autocomplete_fail.connect([this]() { |
|
|
|
|
autocomplete_error_connection=autocomplete_done.connect([this](){ |
|
|
|
|
if(autocomplete_state==AutocompleteState::CANCELED) { |
|
|
|
|
set_status(""); |
|
|
|
|
soft_reparse(); |
|
|
|
|
autocomplete_state=AutocompleteState::IDLE; |
|
|
|
|
autocomplete_restart(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
autocomplete_dialog_setup(); |
|
|
|
|
|
|
|
|
|
for (auto &data : autocomplete_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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
auto row=ss.str(); |
|
|
|
|
auto row_insert_on_selection=row; |
|
|
|
|
if (!row.empty()) { |
|
|
|
|
if(!return_value.empty()) |
|
|
|
|
row+=" --> " + return_value; |
|
|
|
|
autocomplete_dialog_rows[row] = row_insert_on_selection; |
|
|
|
|
autocomplete_dialog->add_row(row, data.brief_comments); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
set_status(""); |
|
|
|
|
if (!autocomplete_dialog_rows.empty()) { |
|
|
|
|
autocomplete_state=AutocompleteState::SHOWN; |
|
|
|
|
get_source_buffer()->begin_user_action(); |
|
|
|
|
autocomplete_dialog->show(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
autocomplete_state=AutocompleteState::IDLE; |
|
|
|
|
soft_reparse(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
autocomplete_restart_connection=autocomplete_restart.connect([this]() { |
|
|
|
|
autocomplete_check(); |
|
|
|
|
}); |
|
|
|
|
autocomplete_error_connection=autocomplete_error.connect([this]() { |
|
|
|
|
Singleton::terminal->print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true); |
|
|
|
|
autocomplete_state=AutocompleteState::CANCELED; |
|
|
|
|
full_reparse(); |
|
|
|
|
autocomplete_starting=false; |
|
|
|
|
autocomplete_cancel_starting=false; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
do_delete_object_connection=do_delete_object.connect([this](){ |
|
|
|
|
@ -662,88 +723,35 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_cancel_s
|
|
|
|
|
delete this; |
|
|
|
|
}); |
|
|
|
|
do_full_reparse.connect([this](){ |
|
|
|
|
init_parse(); |
|
|
|
|
parse_initialize(); |
|
|
|
|
full_reparse_running=false; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) { |
|
|
|
|
last_keyval=key->keyval; |
|
|
|
|
if(completion_dialog_shown) { |
|
|
|
|
if(completion_dialog->on_key_press(key)) |
|
|
|
|
if(autocomplete_state==AutocompleteState::SHOWN) { |
|
|
|
|
if(autocomplete_dialog->on_key_press(key)) |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return ClangViewParse::on_key_press_event(key); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Source::ClangViewAutocomplete::start_autocomplete() { |
|
|
|
|
if(!has_focus()) |
|
|
|
|
return; |
|
|
|
|
auto iter=get_buffer()->get_insert()->get_iter(); |
|
|
|
|
if(!((last_keyval>='0' && last_keyval<='9') ||
|
|
|
|
|
(last_keyval>='a' && last_keyval<='z') || (last_keyval>='A' && last_keyval<='Z') || |
|
|
|
|
last_keyval=='_' || last_keyval=='.' || last_keyval==':' ||
|
|
|
|
|
(last_keyval=='>' && iter.backward_char() && iter.backward_char() && *iter=='-'))) { |
|
|
|
|
autocomplete_cancel_starting=true; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
std::string line=" "+get_line_before(); |
|
|
|
|
if((std::count(line.begin(), line.end(), '\"')%2)!=1 && line.find("//")==std::string::npos) { |
|
|
|
|
const boost::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$"); |
|
|
|
|
const boost::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$"); |
|
|
|
|
boost::smatch sm; |
|
|
|
|
if(boost::regex_match(line, sm, in_specified_namespace)) { |
|
|
|
|
prefix_mutex.lock(); |
|
|
|
|
prefix=sm[3].str(); |
|
|
|
|
prefix_mutex.unlock(); |
|
|
|
|
if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !completion_dialog_shown) { |
|
|
|
|
autocomplete(); |
|
|
|
|
} |
|
|
|
|
else if(last_keyval=='.' && autocomplete_starting) |
|
|
|
|
autocomplete_cancel_starting=true; |
|
|
|
|
} |
|
|
|
|
else if(boost::regex_match(line, sm, within_namespace)) { |
|
|
|
|
prefix_mutex.lock(); |
|
|
|
|
prefix=sm[3].str(); |
|
|
|
|
prefix_mutex.unlock(); |
|
|
|
|
if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !completion_dialog_shown) { |
|
|
|
|
autocomplete(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
autocomplete_cancel_starting=true; |
|
|
|
|
if(autocomplete_starting || completion_dialog_shown) |
|
|
|
|
delayed_reparse_connection.disconnect(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Source::ClangViewAutocomplete::autocomplete() { |
|
|
|
|
if(parse_thread_stop) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(!autocomplete_starting) { |
|
|
|
|
autocomplete_starting=true; |
|
|
|
|
autocomplete_cancel_starting=false; |
|
|
|
|
std::shared_ptr<std::vector<AutoCompleteData> > ac_data=std::make_shared<std::vector<AutoCompleteData> >(); |
|
|
|
|
autocomplete_done_connection.disconnect(); |
|
|
|
|
autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){ |
|
|
|
|
autocomplete_starting=false; |
|
|
|
|
if(!autocomplete_cancel_starting) { |
|
|
|
|
void Source::ClangViewAutocomplete::autocomplete_dialog_setup() { |
|
|
|
|
auto start_iter=get_buffer()->get_insert()->get_iter(); |
|
|
|
|
if(prefix.size()>0 && !start_iter.backward_chars(prefix.size())) |
|
|
|
|
return; |
|
|
|
|
completion_dialog=std::unique_ptr<CompletionDialog>(new CompletionDialog(*this, get_buffer()->create_mark(start_iter))); |
|
|
|
|
auto rows=std::make_shared<std::unordered_map<std::string, std::string> >(); |
|
|
|
|
completion_dialog->on_hide=[this](){ |
|
|
|
|
autocomplete_dialog=std::unique_ptr<CompletionDialog>(new CompletionDialog(*this, get_buffer()->create_mark(start_iter))); |
|
|
|
|
autocomplete_dialog_rows.clear(); |
|
|
|
|
autocomplete_dialog->on_hide=[this](){ |
|
|
|
|
get_source_buffer()->end_user_action(); |
|
|
|
|
completion_dialog_shown=false; |
|
|
|
|
autocomplete_state=AutocompleteState::IDLE; |
|
|
|
|
parsed=false; |
|
|
|
|
soft_reparse(); |
|
|
|
|
}; |
|
|
|
|
completion_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { |
|
|
|
|
auto row = rows->at(selected); |
|
|
|
|
get_buffer()->erase(completion_dialog->start_mark->get_iter(), get_buffer()->get_insert()->get_iter()); |
|
|
|
|
autocomplete_dialog->on_select=[this](const std::string& selected, bool hide_window) { |
|
|
|
|
auto row = autocomplete_dialog_rows.at(selected); |
|
|
|
|
get_buffer()->erase(autocomplete_dialog->start_mark->get_iter(), get_buffer()->get_insert()->get_iter()); |
|
|
|
|
auto iter=get_buffer()->get_insert()->get_iter(); |
|
|
|
|
if(*iter=='<' || *iter=='(') { |
|
|
|
|
auto bracket_pos=row.find(*iter); |
|
|
|
|
@ -751,8 +759,9 @@ void Source::ClangViewAutocomplete::autocomplete() {
|
|
|
|
|
row=row.substr(0, bracket_pos); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
get_buffer()->insert(completion_dialog->start_mark->get_iter(), row); |
|
|
|
|
get_buffer()->insert(autocomplete_dialog->start_mark->get_iter(), row); |
|
|
|
|
if(hide_window) { |
|
|
|
|
autocomplete_state=AutocompleteState::IDLE; |
|
|
|
|
auto para_pos=row.find('('); |
|
|
|
|
auto angle_pos=row.find('<'); |
|
|
|
|
size_t start_pos=std::string::npos; |
|
|
|
|
@ -765,85 +774,114 @@ void Source::ClangViewAutocomplete::autocomplete() {
|
|
|
|
|
start_pos=para_pos; |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
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--; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if(start_pos!=std::string::npos && end_pos!=std::string::npos) { |
|
|
|
|
auto start_offset=completion_dialog->start_mark->get_iter().get_offset()+start_pos+1; |
|
|
|
|
auto end_offset=completion_dialog->start_mark->get_iter().get_offset()+end_pos; |
|
|
|
|
auto start_offset=autocomplete_dialog->start_mark->get_iter().get_offset()+start_pos+1; |
|
|
|
|
auto end_offset=autocomplete_dialog->start_mark->get_iter().get_offset()+end_pos; |
|
|
|
|
if(start_offset!=end_offset) |
|
|
|
|
get_buffer()->select_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==':') { |
|
|
|
|
autocomplete_state=AutocompleteState::IDLE; |
|
|
|
|
autocomplete_restart(); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
auto ss_str=ss.str(); |
|
|
|
|
if (ss_str.length() > 0) { // if length is 0 the result is empty
|
|
|
|
|
(*rows)[ss.str() + " --> " + return_value] = ss_str; |
|
|
|
|
completion_dialog->add_row(ss.str() + " --> " + return_value, data.brief_comments); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
set_status(""); |
|
|
|
|
if (!rows->empty()) { |
|
|
|
|
completion_dialog_shown=true; |
|
|
|
|
get_source_buffer()->begin_user_action(); |
|
|
|
|
completion_dialog->show(); |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Source::ClangViewAutocomplete::autocomplete_check() { |
|
|
|
|
auto iter=get_buffer()->get_insert()->get_iter(); |
|
|
|
|
if(iter.backward_char() && iter.backward_char() && (get_source_buffer()->iter_has_context_class(iter, "string") || |
|
|
|
|
get_source_buffer()->iter_has_context_class(iter, "comment"))) |
|
|
|
|
return; |
|
|
|
|
std::string line=" "+get_line_before(); |
|
|
|
|
const boost::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$"); |
|
|
|
|
const boost::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$"); |
|
|
|
|
boost::smatch sm; |
|
|
|
|
if(boost::regex_match(line, sm, in_specified_namespace)) { |
|
|
|
|
prefix_mutex.lock(); |
|
|
|
|
prefix=sm[3].str(); |
|
|
|
|
prefix_mutex.unlock(); |
|
|
|
|
if(autocomplete_state==AutocompleteState::IDLE && (prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9')) |
|
|
|
|
autocomplete(); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
soft_reparse(); |
|
|
|
|
else if(boost::regex_match(line, sm, within_namespace)) { |
|
|
|
|
prefix_mutex.lock(); |
|
|
|
|
prefix=sm[3].str(); |
|
|
|
|
prefix_mutex.unlock(); |
|
|
|
|
if(autocomplete_state==AutocompleteState::IDLE && (prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9')) |
|
|
|
|
autocomplete(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
set_status(""); |
|
|
|
|
soft_reparse(); |
|
|
|
|
start_autocomplete(); |
|
|
|
|
if(autocomplete_state!=AutocompleteState::IDLE) |
|
|
|
|
delayed_reparse_connection.disconnect(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Source::ClangViewAutocomplete::autocomplete() { |
|
|
|
|
if(parse_state!=ParseState::PROCESSING) |
|
|
|
|
return; |
|
|
|
|
if(autocomplete_state==AutocompleteState::STARTING) { |
|
|
|
|
autocomplete_state=AutocompleteState::CANCELED; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
if(autocomplete_state==AutocompleteState::CANCELED) |
|
|
|
|
return; |
|
|
|
|
autocomplete_state=AutocompleteState::STARTING; |
|
|
|
|
|
|
|
|
|
std::shared_ptr<std::map<std::string, std::string> > buffer_map=std::make_shared<std::map<std::string, std::string> >(); |
|
|
|
|
auto ustr=get_buffer()->get_text(); |
|
|
|
|
autocomplete_data.clear(); |
|
|
|
|
|
|
|
|
|
set_status("autocomplete..."); |
|
|
|
|
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_offset()+1; |
|
|
|
|
auto pos=iter.get_offset()-1; |
|
|
|
|
while(pos>=0 && ((ustr[pos]>='a' && ustr[pos]<='z') || (ustr[pos]>='A' && ustr[pos]<='Z') || (ustr[pos]>='0' && ustr[pos]<='9') || ustr[pos]=='_')) { |
|
|
|
|
ustr.replace(pos, 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--; |
|
|
|
|
} |
|
|
|
|
(*buffer_map)[this->file_path.string()]=std::move(ustr); //TODO: does this work?
|
|
|
|
|
set_status("autocomplete..."); |
|
|
|
|
if(autocomplete_thread.joinable()) |
|
|
|
|
autocomplete_thread.join(); |
|
|
|
|
autocomplete_thread=std::thread([this, ac_data, line_nr, column_nr, buffer_map](){ |
|
|
|
|
parsing_mutex.lock(); |
|
|
|
|
if(!parse_thread_stop) |
|
|
|
|
*ac_data=get_autocomplete_suggestions(line_nr, column_nr, *buffer_map); |
|
|
|
|
if(!parse_thread_stop) |
|
|
|
|
autocomplete_thread=std::thread([this, line_nr, column_nr, buffer](){ |
|
|
|
|
parse_mutex.lock(); |
|
|
|
|
if(parse_state==ParseState::PROCESSING) { |
|
|
|
|
parse_process_state=ParseProcessState::IDLE; |
|
|
|
|
autocomplete_data=autocomplete_get_suggestions(buffer->raw(), line_nr, column_nr); |
|
|
|
|
} |
|
|
|
|
if(parse_state==ParseState::PROCESSING) |
|
|
|
|
autocomplete_done(); |
|
|
|
|
else |
|
|
|
|
autocomplete_fail(); |
|
|
|
|
parsing_mutex.unlock(); |
|
|
|
|
autocomplete_error(); |
|
|
|
|
parse_mutex.unlock(); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::vector<Source::ClangViewAutocomplete::AutoCompleteData> Source::ClangViewAutocomplete::get_autocomplete_suggestions(int line_number, int column, std::map<std::string, std::string>& buffer_map) { |
|
|
|
|
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_map, line_number, column); |
|
|
|
|
auto results=clang_tu->get_code_completions(buffer, line_number, column); |
|
|
|
|
if(results.cx_results==NULL) { |
|
|
|
|
parse_thread_stop=true; |
|
|
|
|
auto expected=ParseState::PROCESSING; |
|
|
|
|
parse_state.compare_exchange_strong(expected, ParseState::RESTARTING); |
|
|
|
|
return suggestions; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(!autocomplete_cancel_starting) { |
|
|
|
|
if(autocomplete_state==AutocompleteState::STARTING) { |
|
|
|
|
prefix_mutex.lock(); |
|
|
|
|
auto prefix_copy=prefix; |
|
|
|
|
prefix_mutex.unlock(); |
|
|
|
|
@ -871,7 +909,7 @@ std::vector<Source::ClangViewAutocomplete::AutoCompleteData> Source::ClangViewAu
|
|
|
|
|
|
|
|
|
|
void Source::ClangViewAutocomplete::async_delete() { |
|
|
|
|
parsing_in_progress->cancel("canceled, freeing resources in the background"); |
|
|
|
|
parse_thread_stop=true; |
|
|
|
|
parse_state=ParseState::STOP; |
|
|
|
|
delete_thread=std::thread([this](){ |
|
|
|
|
//TODO: Is it possible to stop the clang-process in progress?
|
|
|
|
|
if(full_reparse_thread.joinable()) |
|
|
|
|
@ -885,10 +923,16 @@ void Source::ClangViewAutocomplete::async_delete() {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool Source::ClangViewAutocomplete::full_reparse() { |
|
|
|
|
if(!full_reparse_running && !parse_error) { |
|
|
|
|
full_reparse_needed=false; |
|
|
|
|
if(!full_reparse_running) { |
|
|
|
|
auto expected=ParseState::PROCESSING; |
|
|
|
|
if(!parse_state.compare_exchange_strong(expected, ParseState::RESTARTING)) { |
|
|
|
|
expected=ParseState::RESTARTING; |
|
|
|
|
if(!parse_state.compare_exchange_strong(expected, ParseState::RESTARTING)) |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
soft_reparse_needed=false; |
|
|
|
|
full_reparse_running=true; |
|
|
|
|
parse_thread_stop=true; |
|
|
|
|
if(full_reparse_thread.joinable()) |
|
|
|
|
full_reparse_thread.join(); |
|
|
|
|
full_reparse_thread=std::thread([this](){ |
|
|
|
|
@ -963,7 +1007,7 @@ Source::ClangViewAutocomplete(file_path, project_path, language) {
|
|
|
|
|
iter.forward_char(); |
|
|
|
|
} |
|
|
|
|
get_buffer()->place_cursor(iter); |
|
|
|
|
while(g_main_context_pending(NULL)) |
|
|
|
|
while(g_main_context_pending(NULL)) //TODO: minor: might crash if the buffer is saved and closed really fast right after doing auto indent
|
|
|
|
|
g_main_context_iteration(NULL, false); |
|
|
|
|
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); |
|
|
|
|
} |
|
|
|
|
@ -1349,11 +1393,12 @@ Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boo
|
|
|
|
|
|
|
|
|
|
void Source::ClangView::async_delete() { |
|
|
|
|
delayed_reparse_connection.disconnect(); |
|
|
|
|
parse_done_connection.disconnect(); |
|
|
|
|
parse_start_connection.disconnect(); |
|
|
|
|
parse_fail_connection.disconnect(); |
|
|
|
|
autocomplete_done_connection.disconnect(); |
|
|
|
|
autocomplete_fail_connection.disconnect(); |
|
|
|
|
parse_postprocess_connection.disconnect(); |
|
|
|
|
parse_preprocess_connection.disconnect(); |
|
|
|
|
parse_error_connection.disconnect(); |
|
|
|
|
autocomplete_error_connection.disconnect(); |
|
|
|
|
autocomplete_restart_connection.disconnect(); |
|
|
|
|
autocomplete_error_connection.disconnect(); |
|
|
|
|
do_restart_parse_connection.disconnect(); |
|
|
|
|
delayed_tag_similar_tokens_connection.disconnect(); |
|
|
|
|
ClangViewAutocomplete::async_delete(); |
|
|
|
|
|