From fdede18bd8b989a090b63fc555a46a895a24c3dd Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 27 Nov 2015 09:10:26 +0100 Subject: [PATCH 01/19] Now possible to perform clang processing without copy of buffer from Glib::ustring. --- src/source_clang.cc | 58 +++++++++++++++++++-------------------------- src/source_clang.h | 7 +++--- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/source_clang.cc b/src/source_clang.cc index ad5e596..b2f763b 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -36,10 +36,10 @@ Source::View(file_path, project_path, language), parse_error(false) { //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(); + if(parse_thread_buffer_mutex.try_lock()) { + parse_thread_buffer=get_buffer()->get_text(); parse_thread_mapped=true; - parse_thread_buffer_map_mutex.unlock(); + parse_thread_buffer_mutex.unlock(); } parse_thread_go=true; }); @@ -113,22 +113,21 @@ void Source::ClangViewParse::init_parse() { parse_thread_mapped=false; parse_thread_stop=false; - 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(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(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..."); @@ -144,15 +143,15 @@ void Source::ClangViewParse::init_parse() { 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); + else if (parse_thread_mapped && parsing_mutex.try_lock() && parse_thread_buffer_mutex.try_lock()) { + auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer.raw()); if(status==0) - clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer_map.find(file_path.string())->second.size()-1); + clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer.bytes()-1); else parse_error=true; parse_thread_go=false; parsing_mutex.unlock(); - parse_thread_buffer_map_mutex.unlock(); + parse_thread_buffer_mutex.unlock(); if(status!=0) { parse_fail(); parse_thread_stop=true; @@ -164,12 +163,6 @@ void Source::ClangViewParse::init_parse() { }); } -std::map Source::ClangViewParse::get_buffer_map() const { - std::map buffer_map; - buffer_map[file_path.string()]=get_source_buffer()->get_text(); - return buffer_map; -} - void Source::ClangViewParse::soft_reparse() { parse_thread_mapped=false; parsed=false; @@ -807,25 +800,24 @@ void Source::ClangViewAutocomplete::autocomplete() { } }); - std::shared_ptr > buffer_map=std::make_shared >(); - auto ustr=get_buffer()->get_text(); + set_status("autocomplete..."); + if(autocomplete_thread.joinable()) + autocomplete_thread.join(); + auto buffer=std::make_shared(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](){ + autocomplete_thread=std::thread([this, ac_data, line_nr, column_nr, buffer](){ parsing_mutex.lock(); if(!parse_thread_stop) - *ac_data=get_autocomplete_suggestions(line_nr, column_nr, *buffer_map); + *ac_data=get_autocomplete_suggestions(buffer->raw(), line_nr, column_nr); if(!parse_thread_stop) autocomplete_done(); else @@ -835,9 +827,9 @@ void Source::ClangViewAutocomplete::autocomplete() { } } -std::vector Source::ClangViewAutocomplete::get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map) { +std::vector Source::ClangViewAutocomplete::get_autocomplete_suggestions(const std::string &buffer, int line_number, int column) { std::vector 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; return suggestions; diff --git a/src/source_clang.h b/src/source_clang.h index 8ed8157..2e6a4d0 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -53,7 +53,6 @@ namespace Source { sigc::connection parse_start_connection; sigc::connection parse_fail_connection; private: - std::map get_buffer_map() const; void update_syntax(); std::set last_syntax_tags; void update_diagnostics(); @@ -64,8 +63,8 @@ namespace Source { Glib::Dispatcher parse_done; Glib::Dispatcher parse_start; Glib::Dispatcher parse_fail; - std::map parse_thread_buffer_map; - std::mutex parse_thread_buffer_map_mutex; + Glib::ustring parse_thread_buffer; + std::mutex parse_thread_buffer_mutex; std::atomic parse_thread_go; std::atomic parse_thread_mapped; }; @@ -96,7 +95,7 @@ namespace Source { void autocomplete(); std::unique_ptr completion_dialog; bool completion_dialog_shown=false; - std::vector get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map); + std::vector get_autocomplete_suggestions(const std::string &buffer, int line_number, int column); Glib::Dispatcher autocomplete_done; Glib::Dispatcher autocomplete_fail; bool autocomplete_starting=false; From ac213ebc444e042a100df7fe85c8063d7f895635 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 27 Nov 2015 16:35:42 +0100 Subject: [PATCH 02/19] Updated libclangmm link. --- .gitmodules | 2 +- libclangmm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2428fa6..2cd00f1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "libclangmm"] path = libclangmm url = https://github.com/cppit/libclangmm.git - branch = v0.9.3 + branch = v0.9.4 diff --git a/libclangmm b/libclangmm index f1f784d..c1f4eef 160000 --- a/libclangmm +++ b/libclangmm @@ -1 +1 @@ -Subproject commit f1f784dd02d78da35a27034c6cc854e676d4f78b +Subproject commit c1f4eef139e0ca6c36b0faceaac57f519b41acca From 7b78a6aea9ee5a8ecbf2935b397be50bcae34737 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sat, 28 Nov 2015 17:04:07 +0100 Subject: [PATCH 03/19] Clang parse cleanup, at least somewhat more understandable now. Also more robust. --- src/source_clang.cc | 126 ++++++++++++++++++++++++-------------------- src/source_clang.h | 26 ++++----- 2 files changed, 83 insertions(+), 69 deletions(-) diff --git a/src/source_clang.cc b/src/source_clang.cc index b2f763b..8025e34 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -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 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_mutex.try_lock()) { - parse_thread_buffer=get_buffer()->get_text(); - parse_thread_mapped=true; - parse_thread_buffer_mutex.unlock(); + parse_start_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_done_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_fail_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,13 +104,14 @@ 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_process_state=ParseProcessState::STARTING; + parse_state=ParseState::WORKING; auto buffer=get_buffer()->get_text(); //Remove includes for first parse for initial syntax highlighting @@ -131,46 +131,53 @@ void Source::ClangViewParse::init_parse() { 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::WORKING && (parse_process_state==ParseProcessState::IDLE || parse_process_state==ParseProcessState::PREPROCESSING || parse_process_state==ParseProcessState::POSTPROCESSING)) std::this_thread::sleep_for(std::chrono::milliseconds(10)); - if(parse_thread_stop) + if(parse_state!=ParseState::WORKING) break; - if(!parse_thread_mapped) { - parse_thread_go=false; - parse_start(); - } - else if (parse_thread_mapped && parsing_mutex.try_lock() && parse_thread_buffer_mutex.try_lock()) { - auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer.raw()); - if(status==0) - clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer.bytes()-1); - else - parse_error=true; - parse_thread_go=false; - parsing_mutex.unlock(); - parse_thread_buffer_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()) { + if(parse_process_state==ParseProcessState::PROCESSING) { + 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_mutex.unlock(); + } + else { + parse_state=ParseState::STOP; + parse_mutex.unlock(); + parse_error(); + } } else - parse_done(); + parse_mutex.unlock(); } } }); } void Source::ClangViewParse::soft_reparse() { - parse_thread_mapped=false; parsed=false; + if(parse_state!=ParseState::WORKING) + return; + parse_process_state=ParseProcessState::IDLE; delayed_reparse_connection.disconnect(); delayed_reparse_connection=Glib::signal_timeout().connect([this]() { parsed=false; - parse_thread_go=true; - set_status("parsing..."); + auto expected=ParseProcessState::IDLE; + if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING)) + set_status("parsing..."); return false; }, 1000); } @@ -655,7 +662,7 @@ 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; }); } @@ -711,9 +718,8 @@ void Source::ClangViewAutocomplete::start_autocomplete() { } void Source::ClangViewAutocomplete::autocomplete() { - if(parse_thread_stop) { + if(parse_state!=ParseState::WORKING) return; - } if(!autocomplete_starting) { autocomplete_starting=true; @@ -815,14 +821,16 @@ void Source::ClangViewAutocomplete::autocomplete() { pos--; } autocomplete_thread=std::thread([this, ac_data, line_nr, column_nr, buffer](){ - parsing_mutex.lock(); - if(!parse_thread_stop) + parse_mutex.lock(); + if(parse_state==ParseState::WORKING) { + parse_process_state=ParseProcessState::IDLE; *ac_data=get_autocomplete_suggestions(buffer->raw(), line_nr, column_nr); - if(!parse_thread_stop) + } + if(parse_state==ParseState::WORKING) autocomplete_done(); else autocomplete_fail(); - parsing_mutex.unlock(); + parse_mutex.unlock(); }); } } @@ -831,7 +839,8 @@ std::vector Source::ClangViewAu std::vector suggestions; auto results=clang_tu->get_code_completions(buffer, line_number, column); if(results.cx_results==NULL) { - parse_thread_stop=true; + auto expected=ParseState::WORKING; + parse_state.compare_exchange_strong(expected, ParseState::RESTARTING); return suggestions; } @@ -863,7 +872,7 @@ std::vector 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()) @@ -877,10 +886,15 @@ void Source::ClangViewAutocomplete::async_delete() { } bool Source::ClangViewAutocomplete::full_reparse() { - if(!full_reparse_running && !parse_error) { + if(!full_reparse_running) { + auto expected=ParseState::WORKING; + 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](){ diff --git a/src/source_clang.h b/src/source_clang.h index 2e6a4d0..eb944c4 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -12,6 +12,9 @@ namespace Source { class ClangViewParse : public View { + protected: + enum class ParseProcessState {IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING}; + enum class ParseState {WORKING, RESTARTING, STOP}; public: class TokenRange { public: @@ -28,16 +31,12 @@ namespace Source { void soft_reparse() override; protected: - void init_parse(); + void parse_initialize(); std::unique_ptr clang_tu; - std::mutex parsing_mutex; std::unique_ptr clang_tokens; sigc::connection delayed_reparse_connection; std::shared_ptr parsing_in_progress; - std::thread parse_thread; - std::atomic parse_thread_stop; - std::atomic parse_error; void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) override; void show_type_tooltips(const Gdk::Rectangle &rectangle) override; @@ -49,24 +48,25 @@ namespace Source { std::set diagnostic_offsets; std::vector fix_its; + std::thread parse_thread; + std::mutex parse_mutex; + std::atomic parse_process_state; + std::atomic parse_state; sigc::connection parse_done_connection; sigc::connection parse_start_connection; sigc::connection parse_fail_connection; private: + Glib::Dispatcher parse_preprocess; + Glib::Dispatcher parse_postprocess; + Glib::Dispatcher parse_error; + Glib::ustring parse_thread_buffer; + void update_syntax(); std::set last_syntax_tags; void update_diagnostics(); static clang::Index clang_index; std::vector get_compilation_commands(); - - Glib::Dispatcher parse_done; - Glib::Dispatcher parse_start; - Glib::Dispatcher parse_fail; - Glib::ustring parse_thread_buffer; - std::mutex parse_thread_buffer_mutex; - std::atomic parse_thread_go; - std::atomic parse_thread_mapped; }; class ClangViewAutocomplete : public ClangViewParse { From 9f3ffcef5606b8735bdbeca5c879d2c9ab497efd Mon Sep 17 00:00:00 2001 From: eidheim Date: Sat, 28 Nov 2015 18:10:03 +0100 Subject: [PATCH 04/19] Minor extra cleanup to parsing. --- src/source_clang.cc | 18 +++++++++--------- src/source_clang.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/source_clang.cc b/src/source_clang.cc index 8025e34..b679fc1 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -111,7 +111,7 @@ void Source::ClangViewParse::parse_initialize() { if(parse_thread.joinable()) parse_thread.join(); parse_process_state=ParseProcessState::STARTING; - parse_state=ParseState::WORKING; + parse_state=ParseState::PROCESSING; auto buffer=get_buffer()->get_text(); //Remove includes for first parse for initial syntax highlighting @@ -133,9 +133,9 @@ void Source::ClangViewParse::parse_initialize() { set_status("parsing..."); parse_thread=std::thread([this]() { while(true) { - while(parse_state==ParseState::WORKING && (parse_process_state==ParseProcessState::IDLE || parse_process_state==ParseProcessState::PREPROCESSING || parse_process_state==ParseProcessState::POSTPROCESSING)) + while(parse_state==ParseState::PROCESSING && (parse_process_state==ParseProcessState::IDLE || parse_process_state==ParseProcessState::PREPROCESSING || parse_process_state==ParseProcessState::POSTPROCESSING)) std::this_thread::sleep_for(std::chrono::milliseconds(10)); - if(parse_state!=ParseState::WORKING) + if(parse_state!=ParseState::PROCESSING) break; auto expected=ParseProcessState::STARTING; if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) @@ -169,7 +169,7 @@ void Source::ClangViewParse::parse_initialize() { void Source::ClangViewParse::soft_reparse() { parsed=false; - if(parse_state!=ParseState::WORKING) + if(parse_state!=ParseState::PROCESSING) return; parse_process_state=ParseProcessState::IDLE; delayed_reparse_connection.disconnect(); @@ -718,7 +718,7 @@ void Source::ClangViewAutocomplete::start_autocomplete() { } void Source::ClangViewAutocomplete::autocomplete() { - if(parse_state!=ParseState::WORKING) + if(parse_state!=ParseState::PROCESSING) return; if(!autocomplete_starting) { @@ -822,11 +822,11 @@ void Source::ClangViewAutocomplete::autocomplete() { } autocomplete_thread=std::thread([this, ac_data, line_nr, column_nr, buffer](){ parse_mutex.lock(); - if(parse_state==ParseState::WORKING) { + if(parse_state==ParseState::PROCESSING) { parse_process_state=ParseProcessState::IDLE; *ac_data=get_autocomplete_suggestions(buffer->raw(), line_nr, column_nr); } - if(parse_state==ParseState::WORKING) + if(parse_state==ParseState::PROCESSING) autocomplete_done(); else autocomplete_fail(); @@ -839,7 +839,7 @@ std::vector Source::ClangViewAu std::vector suggestions; auto results=clang_tu->get_code_completions(buffer, line_number, column); if(results.cx_results==NULL) { - auto expected=ParseState::WORKING; + auto expected=ParseState::PROCESSING; parse_state.compare_exchange_strong(expected, ParseState::RESTARTING); return suggestions; } @@ -887,7 +887,7 @@ void Source::ClangViewAutocomplete::async_delete() { bool Source::ClangViewAutocomplete::full_reparse() { if(!full_reparse_running) { - auto expected=ParseState::WORKING; + 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)) diff --git a/src/source_clang.h b/src/source_clang.h index eb944c4..5163b8d 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -13,8 +13,8 @@ namespace Source { class ClangViewParse : public View { protected: + enum class ParseState {PROCESSING, RESTARTING, STOP}; enum class ParseProcessState {IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING}; - enum class ParseState {WORKING, RESTARTING, STOP}; public: class TokenRange { public: @@ -50,8 +50,8 @@ namespace Source { std::thread parse_thread; std::mutex parse_mutex; - std::atomic parse_process_state; std::atomic parse_state; + std::atomic parse_process_state; sigc::connection parse_done_connection; sigc::connection parse_start_connection; sigc::connection parse_fail_connection; From 01ae71eb81cd4249d6273b2c31d405952a0c6f06 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sat, 28 Nov 2015 19:40:44 +0100 Subject: [PATCH 05/19] Another minor extra cleanup to parsing. --- src/source_clang.cc | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/source_clang.cc b/src/source_clang.cc index b679fc1..95bdc29 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -110,8 +110,8 @@ void Source::ClangViewParse::parse_initialize() { parsed=false; if(parse_thread.joinable()) parse_thread.join(); - parse_process_state=ParseProcessState::STARTING; parse_state=ParseState::PROCESSING; + parse_process_state=ParseProcessState::STARTING; auto buffer=get_buffer()->get_text(); //Remove includes for first parse for initial syntax highlighting @@ -141,27 +141,23 @@ void Source::ClangViewParse::parse_initialize() { if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) parse_preprocess(); else if (parse_process_state==ParseProcessState::PROCESSING && parse_mutex.try_lock()) { - if(parse_process_state==ParseProcessState::PROCESSING) { - 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_mutex.unlock(); - } - else { - parse_state=ParseState::STOP; + 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_error(); + parse_postprocess(); } + else + parse_mutex.unlock(); } - else + else { + parse_state=ParseState::STOP; parse_mutex.unlock(); + parse_error(); + } } } }); From fd4668bfe4f313aeee46cc593daaf1d823a76c67 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 29 Nov 2015 11:07:34 +0100 Subject: [PATCH 06/19] Major cleanup of autocomplete, and a couple of minor fixes. --- src/source.cc | 3 +- src/source_clang.cc | 375 ++++++++++++++++++++++++-------------------- src/source_clang.h | 17 +- 3 files changed, 220 insertions(+), 175 deletions(-) diff --git a/src/source.cc b/src/source.cc index 839958b..b9216f8 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1104,9 +1104,8 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { while(!end_sentence_iter.starts_line() && (*end_sentence_iter==' ' || *end_sentence_iter=='\t' || end_sentence_iter.ends_line()) && end_sentence_iter.backward_char()) {} - if(!end_sentence_iter.ends_line()) + if(!end_sentence_iter.ends_line() && *end_sentence_iter!=' ' && *end_sentence_iter!='\t') end_sentence_iter.forward_char(); - if(iter==end_line_iter) { if((key->state&GDK_SHIFT_MASK)>0) get_buffer()->move_mark_by_name("insert", end_sentence_iter); diff --git a/src/source_clang.cc b/src/source_clang.cc index 95bdc29..324e842 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -133,7 +133,7 @@ void Source::ClangViewParse::parse_initialize() { set_status("parsing..."); parse_thread=std::thread([this]() { while(true) { - while(parse_state==ParseState::PROCESSING && (parse_process_state==ParseProcessState::IDLE || parse_process_state==ParseProcessState::PREPROCESSING || parse_process_state==ParseProcessState::POSTPROCESSING)) + 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_state!=ParseState::PROCESSING) break; @@ -609,27 +609,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 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& 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; } @@ -637,18 +655,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_done_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_fail_connection=autocomplete_fail.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](){ @@ -665,173 +729,149 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_cancel_s 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()) +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; - 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(); + autocomplete_dialog=std::unique_ptr(new CompletionDialog(*this, get_buffer()->create_mark(start_iter))); + autocomplete_dialog_rows.clear(); + autocomplete_dialog->on_hide=[this](){ + get_source_buffer()->end_user_action(); + autocomplete_state=AutocompleteState::IDLE; + parsed=false; + soft_reparse(); + }; + 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); + if(bracket_pos!=std::string::npos) { + row=row.substr(0, bracket_pos); } - 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(); + 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; + 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(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=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(); + } } } - else - autocomplete_cancel_starting=true; - if(autocomplete_starting || completion_dialog_shown) - delayed_reparse_connection.disconnect(); + }; +} + +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 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(); } + if(autocomplete_state!=AutocompleteState::IDLE) + delayed_reparse_connection.disconnect(); } void Source::ClangViewAutocomplete::autocomplete() { if(parse_state!=ParseState::PROCESSING) return; - - if(!autocomplete_starting) { - autocomplete_starting=true; - autocomplete_cancel_starting=false; - std::shared_ptr > ac_data=std::make_shared >(); - autocomplete_done_connection.disconnect(); - autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){ - autocomplete_starting=false; - if(!autocomplete_cancel_starting) { - 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(new CompletionDialog(*this, get_buffer()->create_mark(start_iter))); - auto rows=std::make_shared >(); - completion_dialog->on_hide=[this](){ - get_source_buffer()->end_user_action(); - completion_dialog_shown=false; - 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()); - auto iter=get_buffer()->get_insert()->get_iter(); - if(*iter=='<' || *iter=='(') { - auto bracket_pos=row.find(*iter); - if(bracket_pos!=std::string::npos) { - row=row.substr(0, bracket_pos); - } - } - get_buffer()->insert(completion_dialog->start_mark->get_iter(), row); - 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(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; - 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)); - } - } - }; - 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(); - } - else - soft_reparse(); - } - else { - set_status(""); - soft_reparse(); - start_autocomplete(); - } - }); - - set_status("autocomplete..."); - if(autocomplete_thread.joinable()) - autocomplete_thread.join(); - auto buffer=std::make_shared(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 && (((*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, ac_data, line_nr, column_nr, buffer](){ - parse_mutex.lock(); - if(parse_state==ParseState::PROCESSING) { - parse_process_state=ParseProcessState::IDLE; - *ac_data=get_autocomplete_suggestions(buffer->raw(), line_nr, column_nr); - } - if(parse_state==ParseState::PROCESSING) - autocomplete_done(); - else - autocomplete_fail(); - parse_mutex.unlock(); - }); + if(autocomplete_state==AutocompleteState::STARTING) { + autocomplete_state=AutocompleteState::CANCELED; + return; + } + if(autocomplete_state==AutocompleteState::CANCELED) + return; + autocomplete_state=AutocompleteState::STARTING; + + autocomplete_data.clear(); + + set_status("autocomplete..."); + if(autocomplete_thread.joinable()) + autocomplete_thread.join(); + auto buffer=std::make_shared(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 && (((*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](){ + 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(); + parse_mutex.unlock(); + }); } -std::vector Source::ClangViewAutocomplete::get_autocomplete_suggestions(const std::string &buffer, int line_number, int column) { +std::vector Source::ClangViewAutocomplete::autocomplete_get_suggestions(const std::string &buffer, int line_number, int column) { std::vector suggestions; auto results=clang_tu->get_code_completions(buffer, line_number, column); if(results.cx_results==NULL) { @@ -839,8 +879,8 @@ std::vector Source::ClangViewAu 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(); @@ -1355,6 +1395,7 @@ void Source::ClangView::async_delete() { parse_start_connection.disconnect(); parse_fail_connection.disconnect(); autocomplete_done_connection.disconnect(); + autocomplete_restart_connection.disconnect(); autocomplete_fail_connection.disconnect(); do_restart_parse_connection.disconnect(); delayed_tag_similar_tokens_connection.disconnect(); diff --git a/src/source_clang.h b/src/source_clang.h index 5163b8d..6c284d7 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -70,6 +70,8 @@ namespace Source { }; class ClangViewAutocomplete : public ClangViewParse { + protected: + enum class AutocompleteState {IDLE, STARTING, CANCELED, SHOWN}; public: class AutoCompleteData { public: @@ -87,19 +89,22 @@ namespace Source { protected: std::thread autocomplete_thread; sigc::connection autocomplete_done_connection; + sigc::connection autocomplete_restart_connection; sigc::connection autocomplete_fail_connection; sigc::connection do_delete_object_connection; sigc::connection do_restart_parse_connection; private: - void start_autocomplete(); + std::atomic autocomplete_state; + void autocomplete_dialog_setup(); + void autocomplete_check(); void autocomplete(); - std::unique_ptr completion_dialog; - bool completion_dialog_shown=false; - std::vector get_autocomplete_suggestions(const std::string &buffer, int line_number, int column); + std::vector autocomplete_data; + std::unique_ptr autocomplete_dialog; + std::unordered_map autocomplete_dialog_rows; + std::vector autocomplete_get_suggestions(const std::string &buffer, int line_number, int column); Glib::Dispatcher autocomplete_done; + Glib::Dispatcher autocomplete_restart; Glib::Dispatcher autocomplete_fail; - bool autocomplete_starting=false; - std::atomic autocomplete_cancel_starting; guint last_keyval=0; std::string prefix; std::mutex prefix_mutex; From 18c34be06d0479dbbfae909e13b09162c9f140f1 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 29 Nov 2015 11:28:03 +0100 Subject: [PATCH 07/19] Tiny cleanup. --- src/source.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source.cc b/src/source.cc index b9216f8..0feff40 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1104,7 +1104,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { while(!end_sentence_iter.starts_line() && (*end_sentence_iter==' ' || *end_sentence_iter=='\t' || end_sentence_iter.ends_line()) && end_sentence_iter.backward_char()) {} - if(!end_sentence_iter.ends_line() && *end_sentence_iter!=' ' && *end_sentence_iter!='\t') + if(!end_sentence_iter.ends_line() && !end_sentence_iter.starts_line()) end_sentence_iter.forward_char(); if(iter==end_line_iter) { if((key->state&GDK_SHIFT_MASK)>0) From 7e598ae91a5c4be5b01a4029a057f1f102c82032 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 29 Nov 2015 21:14:59 +0100 Subject: [PATCH 08/19] Cleanup of soft_reparse uses, and fixed a bug where implementation files were not correctly reparsed after using rename that included a header file. --- src/notebook.cc | 18 ++++++++---------- src/notebook.h | 2 +- src/source.h | 4 ++-- src/source_clang.cc | 2 ++ src/window.cc | 9 +++++---- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index a6c04ae..510088b 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -164,7 +164,7 @@ void Notebook::configure(int view_nr) { #endif } -bool Notebook::save(int page, bool reparse_needed) { +bool Notebook::save(int page) { JDEBUG("start"); if(page>=size()) { JDEBUG("end false"); @@ -199,14 +199,12 @@ bool Notebook::save(int page, bool reparse_needed) { } if(filesystem::write(view->file_path, view->get_buffer())) { - if(reparse_needed) { - if(auto clang_view=dynamic_cast(view)) { - if(clang_view->language->get_id()=="chdr" || clang_view->language->get_id()=="cpphdr") { - for(auto a_view: source_views) { - if(auto a_clang_view=dynamic_cast(a_view)) { - if(clang_view!=a_clang_view) - a_clang_view->soft_reparse_needed=true; - } + if(auto clang_view=dynamic_cast(view)) { + if(clang_view->language->get_id()=="chdr" || clang_view->language->get_id()=="cpphdr") { + for(auto a_view: source_views) { + if(auto a_clang_view=dynamic_cast(a_view)) { + if(clang_view!=a_clang_view) + a_clang_view->soft_reparse_needed=true; } } } @@ -248,7 +246,7 @@ bool Notebook::save(int page, bool reparse_needed) { bool Notebook::save_current() { if(get_current_page()==-1) return false; - return save(get_current_page(), true); + return save(get_current_page()); } bool Notebook::close_current_page() { diff --git a/src/notebook.h b/src/notebook.h index 15e9cd1..4abf709 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -18,7 +18,7 @@ public: Source::View* get_current_view(); bool close_current_page(); void open(const boost::filesystem::path &file_path); - bool save(int page, bool reparse_needed=false); + bool save(int page); bool save_current(); void configure(int view_nr); boost::filesystem::path get_current_folder(); diff --git a/src/source.h b/src/source.h index d2046a0..54a3951 100644 --- a/src/source.h +++ b/src/source.h @@ -104,8 +104,8 @@ namespace Source { bool soft_reparse_needed=false; bool full_reparse_needed=false; - virtual void soft_reparse() {} - virtual bool full_reparse() {return true;} + virtual void soft_reparse() {soft_reparse_needed=false;} + virtual bool full_reparse() {full_reparse_needed=false; return true;} protected: bool parsed=false; Tooltips diagnostic_tooltips; diff --git a/src/source_clang.cc b/src/source_clang.cc index 324e842..9727400 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -164,6 +164,7 @@ void Source::ClangViewParse::parse_initialize() { } void Source::ClangViewParse::soft_reparse() { + soft_reparse_needed=false; parsed=false; if(parse_state!=ParseState::PROCESSING) return; @@ -922,6 +923,7 @@ void Source::ClangViewAutocomplete::async_delete() { } bool Source::ClangViewAutocomplete::full_reparse() { + full_reparse_needed=false; if(!full_reparse_running) { auto expected=ParseState::PROCESSING; if(!parse_state.compare_exchange_strong(expected, ParseState::RESTARTING)) { diff --git a/src/window.cc b/src/window.cc index 7b812bd..17de97a 100644 --- a/src/window.cc +++ b/src/window.cc @@ -96,12 +96,9 @@ Window::Window() : compiling(false) { if(view->full_reparse_needed) { if(!view->full_reparse()) Singleton::terminal->async_print("Error: failed to reparse "+view->file_path.string()+". Please reopen the file manually.\n", true); - view->full_reparse_needed=false; } - else if(view->soft_reparse_needed) { + else if(view->soft_reparse_needed) view->soft_reparse(); - view->soft_reparse_needed=false; - } view->set_status(view->status); view->set_info(view->info); @@ -884,6 +881,7 @@ void Window::rename_token_entry() { label_it->update(0, ""); entry_box.entries.emplace_back(token->spelling, [this, token](const std::string& content){ if(notebook.get_current_page()!=-1 && content!=token->spelling) { + std::vector modified_pages; for(int c=0;crename_similar_tokens) { @@ -891,9 +889,12 @@ void Window::rename_token_entry() { if(number>0) { Singleton::terminal->print("Replaced "+std::to_string(number)+" occurrences in file "+view->file_path.string()+"\n"); notebook.save(c); + modified_pages.emplace_back(c); } } } + for(auto &page: modified_pages) + notebook.get_view(page)->soft_reparse_needed=false; entry_box.hide(); } }); From 44091f86b6627b8897a7bcd8d606e67acda33799 Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 30 Nov 2015 04:27:26 +0100 Subject: [PATCH 09/19] Directories now derive from Gtk::TreeView instead of Gtk::ScrolledWindow. --- src/directories.cc | 32 +++++++++++++++----------------- src/directories.h | 3 +-- src/window.cc | 3 ++- src/window.h | 1 + 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/directories.cc b/src/directories.cc index 9aa8421..21c23d8 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -21,26 +21,24 @@ namespace sigc { #endif } -Directories::Directories() : stop_update_thread(false) { - add(tree_view); - set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); +Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { tree_store = Gtk::TreeStore::create(column_record); - tree_view.set_model(tree_store); - tree_view.append_column("", column_record.name); - auto renderer=dynamic_cast(tree_view.get_column(0)->get_first_cell()); - tree_view.get_column(0)->add_attribute(renderer->property_foreground_rgba(), column_record.color); + set_model(tree_store); + append_column("", column_record.name); + auto renderer=dynamic_cast(get_column(0)->get_first_cell()); + get_column(0)->add_attribute(renderer->property_foreground_rgba(), column_record.color); tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING); - tree_view.set_enable_search(true); //TODO: why does this not work in OS X? - tree_view.set_search_column(column_record.name); + set_enable_search(true); //TODO: why does this not work in OS X? + set_search_column(column_record.name); - tree_view.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){ + signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){ auto iter = tree_store->get_iter(path); if (iter) { auto path_str=iter->get_value(column_record.path); if(path_str!="") { if (boost::filesystem::is_directory(boost::filesystem::path(path_str))) { - tree_view.row_expanded(path) ? tree_view.collapse_row(path) : tree_view.expand_row(path, false); + row_expanded(path) ? collapse_row(path) : expand_row(path, false); } else { if(on_row_activated) on_row_activated(path_str); @@ -49,7 +47,7 @@ Directories::Directories() : stop_update_thread(false) { } }); - tree_view.signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){ + signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){ if(iter->children().begin()->get_value(column_record.path)=="") { update_mutex.lock(); add_path(iter->get_value(column_record.path), *iter); @@ -57,7 +55,7 @@ Directories::Directories() : stop_update_thread(false) { } return false; }); - tree_view.signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){ + signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path){ update_mutex.lock(); last_write_times.erase(iter->get_value(column_record.path)); update_mutex.unlock(); @@ -128,9 +126,9 @@ void Directories::open(const boost::filesystem::path& dir_path) { cmake=std::unique_ptr(new CMake(dir_path)); auto project=cmake->get_functions_parameters("project"); if(project.size()>0 && project[0].second.size()>0) - tree_view.get_column(0)->set_title(project[0].second[0]); + get_column(0)->set_title(project[0].second[0]); else - tree_view.get_column(0)->set_title(""); + get_column(0)->set_title(""); update_mutex.lock(); add_path(dir_path, Gtk::TreeModel::Row()); update_mutex.unlock(); @@ -185,8 +183,8 @@ void Directories::select(const boost::filesystem::path &path) { tree_store->foreach_iter([this, &path](const Gtk::TreeModel::iterator& iter){ if(iter->get_value(column_record.path)==path.string()) { auto tree_path=Gtk::TreePath(iter); - tree_view.expand_to_path(tree_path); - tree_view.set_cursor(tree_path); + expand_to_path(tree_path); + set_cursor(tree_path); return true; } return false; diff --git a/src/directories.h b/src/directories.h index 9435504..3921012 100644 --- a/src/directories.h +++ b/src/directories.h @@ -10,7 +10,7 @@ #include #include -class Directories : public Gtk::ScrolledWindow { +class Directories : public Gtk::TreeView { public: class ColumnRecord : public Gtk::TreeModel::ColumnRecord { public: @@ -38,7 +38,6 @@ public: private: void add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row); - Gtk::TreeView tree_view; Glib::RefPtr tree_store; ColumnRecord column_record; diff --git a/src/window.cc b/src/window.cc index 17de97a..a7429a0 100644 --- a/src/window.cc +++ b/src/window.cc @@ -34,7 +34,8 @@ Window::Window() : compiling(false) { add(vpaned); - directory_and_notebook_panes.pack1(*Singleton::directories, Gtk::SHRINK); + directories_scrolled_window.add(*Singleton::directories); + directory_and_notebook_panes.pack1(directories_scrolled_window, Gtk::SHRINK); notebook_vbox.pack_start(notebook); notebook_vbox.pack_end(entry_box, Gtk::PACK_SHRINK); directory_and_notebook_panes.pack2(notebook_vbox, Gtk::SHRINK); diff --git a/src/window.h b/src/window.h index d02f23c..5fb7598 100644 --- a/src/window.h +++ b/src/window.h @@ -19,6 +19,7 @@ private: Gtk::Paned directory_and_notebook_panes; Gtk::VBox notebook_vbox; Gtk::VBox terminal_vbox; + Gtk::ScrolledWindow directories_scrolled_window; Gtk::ScrolledWindow terminal_scrolled_window; Gtk::HBox info_and_status_hbox; Gtk::AboutDialog about; From ce7eedc058a8736489be5f3a429e72d794729f25 Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 30 Nov 2015 04:41:41 +0100 Subject: [PATCH 10/19] Added override to on_key_press_event and on_button_press_event in source.h --- src/source.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/source.h b/src/source.h index 54a3951..329eb28 100644 --- a/src/source.h +++ b/src/source.h @@ -102,6 +102,9 @@ namespace Source { void set_tab_char_and_size(char tab_char, unsigned tab_size); std::pair get_tab_char_and_size() {return {tab_char, tab_size};} + bool on_key_press_event(GdkEventKey* key) override; + bool on_button_press_event(GdkEventButton *event) override; + bool soft_reparse_needed=false; bool full_reparse_needed=false; virtual void soft_reparse() {soft_reparse_needed=false;} @@ -133,9 +136,6 @@ namespace Source { bool find_right_bracket_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter); bool find_left_bracket_backward(Gtk::TextIter iter, Gtk::TextIter &found_iter); - bool on_key_press_event(GdkEventKey* key); - bool on_button_press_event(GdkEventButton *event); - std::pair find_tab_char_and_size(); unsigned tab_size; char tab_char; From 4dc55579a1aa39ad8e27a3cef258beb1d3873491 Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 30 Nov 2015 05:03:48 +0100 Subject: [PATCH 11/19] Moved on_key_press_event and on_button_press_event to protected as in the Gtkmm classes. --- src/source.h | 6 +++--- src/source_clang.h | 6 ++++-- src/window.h | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/source.h b/src/source.h index 329eb28..4be6e15 100644 --- a/src/source.h +++ b/src/source.h @@ -102,9 +102,6 @@ namespace Source { void set_tab_char_and_size(char tab_char, unsigned tab_size); std::pair get_tab_char_and_size() {return {tab_char, tab_size};} - bool on_key_press_event(GdkEventKey* key) override; - bool on_button_press_event(GdkEventButton *event) override; - bool soft_reparse_needed=false; bool full_reparse_needed=false; virtual void soft_reparse() {soft_reparse_needed=false;} @@ -136,6 +133,9 @@ namespace Source { bool find_right_bracket_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter); bool find_left_bracket_backward(Gtk::TextIter iter, Gtk::TextIter &found_iter); + bool on_key_press_event(GdkEventKey* key) override; + bool on_button_press_event(GdkEventButton *event) override; + std::pair find_tab_char_and_size(); unsigned tab_size; char tab_char; diff --git a/src/source_clang.h b/src/source_clang.h index 6c284d7..2309d5a 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -25,7 +25,6 @@ namespace Source { }; ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); - bool on_key_press_event(GdkEventKey* key) override; void configure() override; @@ -41,6 +40,8 @@ namespace Source { void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) override; void show_type_tooltips(const Gdk::Rectangle &rectangle) override; + bool on_key_press_event(GdkEventKey* key) override; + boost::regex bracket_regex; boost::regex no_bracket_statement_regex; boost::regex no_bracket_no_para_statement_regex; @@ -82,11 +83,12 @@ namespace Source { }; ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); - bool on_key_press_event(GdkEventKey* key) override; virtual void async_delete(); bool full_reparse() override; protected: + bool on_key_press_event(GdkEventKey* key) override; + std::thread autocomplete_thread; sigc::connection autocomplete_done_connection; sigc::connection autocomplete_restart_connection; diff --git a/src/window.h b/src/window.h index 5fb7598..7c301d6 100644 --- a/src/window.h +++ b/src/window.h @@ -11,6 +11,7 @@ public: Window(); Notebook notebook; +protected: bool on_key_press_event(GdkEventKey *event) override; bool on_delete_event(GdkEventAny *event) override; From b11a75761586719b5334200f8f6e945c03659511 Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 30 Nov 2015 06:40:15 +0100 Subject: [PATCH 12/19] Spelling fix in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d36d1d2..3e5dc7c 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ towards libclang with speed and ease of use in mind. * C++ warnings and errors on the fly * C++ Fix-its * Automated CMake processing -* Fast C++ autocomletion (including external libraries) -* Keyword and buffer autocomletion for other file types +* Fast C++ autocompletion (including external libraries) +* Keyword and buffer autocompletion for other file types * Tooltips showing type information and doxygen documentation * Refactoring across files * Highlighting of similar types From 63f93d3aebbb96fdca0448aa2e35136cad0e59d9 Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 30 Nov 2015 06:44:03 +0100 Subject: [PATCH 13/19] Minor update of features in README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e5dc7c..4224b87 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,10 @@ towards libclang with speed and ease of use in mind. * Fast C++ autocompletion (including external libraries) * Keyword and buffer autocompletion for other file types * Tooltips showing type information and doxygen documentation -* Refactoring across files +* Rename refactoring across files * Highlighting of similar types -* Documentation search +* Automated documentation search +* Go to methods and usages * Spell checking depending on file context * Run shell commands within JuCi++, even on Windows * Regex search and replace From 5ed9b6eb72f4907c8fbe35854f1bf001dc5ea84e Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 30 Nov 2015 09:33:35 +0100 Subject: [PATCH 14/19] Added close symbol on tabs. --- src/notebook.cc | 66 +++++++++++++++++++++++++++++++++++++------------ src/notebook.h | 10 +++++++- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index 510088b..472b626 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -27,6 +27,17 @@ namespace sigc { #endif } +Notebook::TabLabel::TabLabel(const std::string &title) : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) { + label.set_text(title); + button.set_image_from_icon_name("window-close-symbolic", Gtk::ICON_SIZE_MENU); + button.set_border_width(0.0); + button.set_use_underline(false); + button.set_relief(Gtk::ReliefStyle::RELIEF_NONE); + pack_start(label, Gtk::PACK_SHRINK); + pack_end(button, Gtk::PACK_SHRINK); + show_all(); +} + Notebook::Notebook() : Gtk::Notebook(), last_index(-1) { Gsv::init(); @@ -115,8 +126,19 @@ void Notebook::open(const boost::filesystem::path &file_path) { #endif configure(source_views.size()-1); + //Set up tab label std::string title=file_path.filename().string(); - append_page(*hboxes.back(), title); + tab_labels.emplace_back(new TabLabel(title)); + auto source_view=source_views.back(); + tab_labels.back()->button.signal_clicked().connect([this, source_view](){ + for(int c=0;cget_buffer()->set_modified(false); get_current_view()->grab_focus(); //Add star on tab label when the page is not saved: - auto source_view=get_current_view(); get_current_view()->get_buffer()->signal_modified_changed().connect([this, source_view]() { std::string title=source_view->file_path.filename().string(); if(source_view->get_buffer()->get_modified()) @@ -144,7 +165,7 @@ void Notebook::open(const boost::filesystem::path &file_path) { } } if(page!=-1) - set_tab_label_text(*(get_nth_page(page)), title); + tab_labels.at(page)->label.set_text(title); }); JDEBUG("end"); @@ -249,22 +270,28 @@ bool Notebook::save_current() { return save(get_current_page()); } -bool Notebook::close_current_page() { +bool Notebook::close(int page) { JDEBUG("start"); - if (get_current_page()!=-1) { - if(get_current_view()->get_buffer()->get_modified()){ - if(!save_modified_dialog()) { + if (page!=-1) { + auto view=get_view(page); + if(view->get_buffer()->get_modified()){ + if(!save_modified_dialog(page)) { JDEBUG("end false"); return false; } } - int page = get_current_page(); - int index=get_index(page); - - if(last_index!=static_cast(-1)) { - set_current_page(page_num(*hboxes.at(last_index))); - last_index=-1; + auto index=get_index(page); + if(page==get_current_page()) { + if(last_index!=static_cast(-1)) { + set_current_page(page_num(*hboxes.at(last_index))); + last_index=-1; + } } + else if(index==last_index) + last_index=-1; + else if(index(-1)) + last_index--; + remove_page(page); #if GTKSOURCEVIEWMM_MAJOR_VERSION > 2 & GTKSOURCEVIEWMM_MINOR_VERSION > 17 source_maps.erase(source_maps.begin()+index); @@ -277,11 +304,18 @@ bool Notebook::close_current_page() { source_views.erase(source_views.begin()+index); scrolled_windows.erase(scrolled_windows.begin()+index); hboxes.erase(hboxes.begin()+index); + tab_labels.erase(tab_labels.begin()+index); } JDEBUG("end true"); return true; } +bool Notebook::close_current_page() { + if(get_current_page()==-1) + return false; + return close(get_current_page()); +} + boost::filesystem::path Notebook::get_current_folder() { boost::filesystem::path current_path; @@ -293,13 +327,13 @@ boost::filesystem::path Notebook::get_current_folder() { return current_path; } -bool Notebook::save_modified_dialog() { +bool Notebook::save_modified_dialog(int page) { Gtk::MessageDialog dialog((Gtk::Window&)(*get_toplevel()), "Save file!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO); dialog.set_default_response(Gtk::RESPONSE_YES); - dialog.set_secondary_text("Do you want to save: " + get_current_view()->file_path.string()+" ?"); + dialog.set_secondary_text("Do you want to save: " + get_view(page)->file_path.string()+" ?"); int result = dialog.run(); if(result==Gtk::RESPONSE_YES) { - save_current(); + save(page); return true; } else if(result==Gtk::RESPONSE_NO) { diff --git a/src/notebook.h b/src/notebook.h index 4abf709..870c7cf 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -10,12 +10,19 @@ #include class Notebook : public Gtk::Notebook { + class TabLabel : public Gtk::Box { + public: + TabLabel(const std::string &title); + Gtk::Label label; + Gtk::Button button; + }; public: Notebook(); Source::View* get_view(int page); size_t get_index(int page); int size(); Source::View* get_current_view(); + bool close(int page); bool close_current_page(); void open(const boost::filesystem::path &file_path); bool save(int page); @@ -24,11 +31,12 @@ public: boost::filesystem::path get_current_folder(); private: - bool save_modified_dialog(); + bool save_modified_dialog(int page); std::vector source_views; //Is NOT freed in destructor, this is intended for quick program exit. std::vector > source_maps; std::vector > scrolled_windows; std::vector > hboxes; + std::vector > tab_labels; size_t last_index; }; From e842470871bd6c2855af019577ea5d0a970ef982 Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 1 Dec 2015 08:44:07 +0100 Subject: [PATCH 15/19] Improved feature list --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4224b87..eee04aa 100644 --- a/README.md +++ b/README.md @@ -8,19 +8,19 @@ towards libclang with speed and ease of use in mind. ## Features * Platform independent * Fast and responsive (written in C++) -* Syntax highlighting (even C++11/14, and more than 100 other file types) +* Syntax highlighting for more than 100 different file types * C++ warnings and errors on the fly * C++ Fix-its * Automated CMake processing -* Fast C++ autocompletion (including external libraries) +* Fast C++ autocompletion * Keyword and buffer autocompletion for other file types -* Tooltips showing type information and doxygen documentation -* Rename refactoring across files -* Highlighting of similar types -* Automated documentation search -* Go to methods and usages +* Tooltips showing type information and doxygen documentation (C++) +* Rename refactoring across files (C++) +* Highlighting of similar types (C++) +* Automated documentation search (C++) +* Go to methods and usages (C++) * Spell checking depending on file context -* Run shell commands within JuCi++, even on Windows +* Run shell commands within JuCi++ * Regex search and replace * Smart paste, keys and indentation * Auto-indentation of C++ file buffers through [clang-format](http://clang.llvm.org/docs/ClangFormat.html) From d4f72430cb5e6c8e83e3573b8d2bbe9c0128c377 Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 1 Dec 2015 21:05:58 +0100 Subject: [PATCH 16/19] Fixed a crash when pressing enter at the first line when having a space at the end. Also a minor fix if one press enter at the last line. --- src/source.cc | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/source.cc b/src/source.cc index 0feff40..983ae28 100644 --- a/src/source.cc +++ b/src/source.cc @@ -143,8 +143,7 @@ Source::View::View(const boost::filesystem::path &file_path, const boost::filesy auto first=iter; auto second=iter; if(last_keyval_is_return) { - while(first && !first.ends_line()) - first.backward_char(); + while(first && !first.ends_line() && first.backward_char()) {} if(first.backward_char() && second.forward_char()) { get_buffer()->remove_tag_by_name("spellcheck_error", first, second); auto word=spellcheck_get_word(first); @@ -947,35 +946,34 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { while((*end_blank_iter==' ' || *end_blank_iter=='\t') && !end_blank_iter.ends_line() && end_blank_iter.forward_char()) {} start_blank_iter.backward_char(); - while((*start_blank_iter==' ' || *start_blank_iter=='\t' || start_blank_iter.ends_line()) && + while((*start_blank_iter==' ' || *start_blank_iter=='\t') && !start_blank_iter.starts_line() && start_blank_iter.backward_char()) {} - if(!start_blank_iter.starts_line()) { + + if(start_blank_iter.starts_line() && (*start_blank_iter==' ' || *start_blank_iter=='\t')) + get_buffer()->erase(iter, end_blank_iter); + else { start_blank_iter.forward_char(); get_buffer()->erase(start_blank_iter, end_blank_iter); } - else - get_buffer()->erase(iter, end_blank_iter); - iter=get_buffer()->get_insert()->get_iter(); + iter=get_buffer()->get_insert()->get_iter(); int line_nr=iter.get_line(); auto tabs_end_iter=get_tabs_end_iter(); auto line_tabs=get_line_before(tabs_end_iter); if((line_nr+1)get_line_count()) { auto next_line_tabs_end_iter=get_tabs_end_iter(line_nr+1); auto next_line_tabs=get_line_before(next_line_tabs_end_iter); - if(iter.ends_line()) { - if(next_line_tabs.size()>line_tabs.size()) { - get_source_buffer()->insert_at_cursor("\n"+next_line_tabs); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } + if(iter.ends_line() && next_line_tabs.size()>line_tabs.size()) { + get_source_buffer()->insert_at_cursor("\n"+next_line_tabs); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; } - get_source_buffer()->insert_at_cursor("\n"+line_tabs); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; } + get_source_buffer()->insert_at_cursor("\n"+line_tabs); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; } //Indent right when clicking tab, no matter where in the line the cursor is. Also works on selected text. else if(key->keyval==GDK_KEY_Tab) { From a665752e3bdb36076c4a5f4779c68b7550f213e6 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 2 Dec 2015 07:57:38 +0100 Subject: [PATCH 17/19] Fixed crash that happened when going to a declaration in an unopened file, and then right after closing the new file. --- src/window.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/window.cc b/src/window.cc index a7429a0..a66dbb6 100644 --- a/src/window.cc +++ b/src/window.cc @@ -395,20 +395,21 @@ void Window::set_menu_actions() { notebook.open(declaration_file); auto line=static_cast(location.line)-1; auto index=static_cast(location.index)-1; - auto buffer=notebook.get_current_view()->get_buffer(); - line=std::min(line, buffer->get_line_count()-1); + auto view=notebook.get_current_view(); + line=std::min(line, view->get_buffer()->get_line_count()-1); if(line>=0) { - auto iter=buffer->get_iter_at_line(line); + auto iter=view->get_buffer()->get_iter_at_line(line); while(!iter.ends_line()) iter.forward_char(); auto end_line_index=iter.get_line_index(); index=std::min(index, end_line_index); - buffer->place_cursor(buffer->get_iter_at_line_index(line, index)); while(g_main_context_pending(NULL)) g_main_context_iteration(NULL, false); - if(notebook.get_current_page()!=-1) + if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) { + view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(line, index)); notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); + } } } } From af62c9a3f7425ebf067e0276176c2ae033094419 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 2 Dec 2015 08:13:16 +0100 Subject: [PATCH 18/19] Cleanup of the code around while(g_main_context_pending(NULL)) loops --- src/source_clang.cc | 2 +- src/window.cc | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/source_clang.cc b/src/source_clang.cc index 9727400..102a533 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -1007,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); } diff --git a/src/window.cc b/src/window.cc index a66dbb6..c9f91dc 100644 --- a/src/window.cc +++ b/src/window.cc @@ -334,10 +334,12 @@ void Window::set_menu_actions() { }); menu->add_action("source_center_cursor", [this]() { if(notebook.get_current_page()!=-1) { + auto view=notebook.get_current_view(); + while(g_main_context_pending(NULL)) g_main_context_iteration(NULL, false); - if(notebook.get_current_page()!=-1) - notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); + if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) + view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); } }); @@ -408,7 +410,7 @@ void Window::set_menu_actions() { g_main_context_iteration(NULL, false); if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) { view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(line, index)); - notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); + view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); } } } @@ -844,16 +846,18 @@ void Window::goto_line_entry() { if(notebook.get_current_page()!=-1) { entry_box.entries.emplace_back("", [this](const std::string& content){ if(notebook.get_current_page()!=-1) { - auto buffer=notebook.get_current_view()->get_buffer(); + auto view=notebook.get_current_view(); try { auto line = stoi(content); - if(line>0 && line<=buffer->get_line_count()) { + if(line>0 && line<=view->get_buffer()->get_line_count()) { line--; - buffer->place_cursor(buffer->get_iter_at_line(line)); + while(g_main_context_pending(NULL)) g_main_context_iteration(NULL, false); - if(notebook.get_current_page()!=-1) - notebook.get_current_view()->scroll_to(buffer->get_insert(), 0.0, 1.0, 0.5); + if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) { + view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line(line)); + view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); + } } } catch(const std::exception &e) {} From d8ab25db47247635d2ff60ace62d9beeeb09097b Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 2 Dec 2015 13:09:30 +0100 Subject: [PATCH 19/19] Renamed a few variables to more appropriate names --- src/source_clang.cc | 22 +++++++++++----------- src/source_clang.h | 10 +++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/source_clang.cc b/src/source_clang.cc index 102a533..f0c64b0 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -35,7 +35,7 @@ Source::View(file_path, project_path, language) { 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_preprocess.connect([this]{ + 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)) @@ -45,7 +45,7 @@ Source::View(file_path, project_path, language) { else parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING); }); - parse_done_connection=parse_postprocess.connect([this](){ + 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)) { @@ -57,7 +57,7 @@ Source::View(file_path, project_path, language) { parse_mutex.unlock(); } }); - parse_fail_connection=parse_error.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(""); @@ -663,7 +663,7 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_state(Au return false; }); - autocomplete_done_connection=autocomplete_done.connect([this](){ + autocomplete_error_connection=autocomplete_done.connect([this](){ if(autocomplete_state==AutocompleteState::CANCELED) { set_status(""); soft_reparse(); @@ -710,7 +710,7 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_state(Au autocomplete_restart_connection=autocomplete_restart.connect([this]() { autocomplete_check(); }); - autocomplete_fail_connection=autocomplete_fail.connect([this]() { + 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(); @@ -867,7 +867,7 @@ void Source::ClangViewAutocomplete::autocomplete() { if(parse_state==ParseState::PROCESSING) autocomplete_done(); else - autocomplete_fail(); + autocomplete_error(); parse_mutex.unlock(); }); } @@ -1393,12 +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(); + parse_postprocess_connection.disconnect(); + parse_preprocess_connection.disconnect(); + parse_error_connection.disconnect(); + autocomplete_error_connection.disconnect(); autocomplete_restart_connection.disconnect(); - autocomplete_fail_connection.disconnect(); + autocomplete_error_connection.disconnect(); do_restart_parse_connection.disconnect(); delayed_tag_similar_tokens_connection.disconnect(); ClangViewAutocomplete::async_delete(); diff --git a/src/source_clang.h b/src/source_clang.h index 2309d5a..117d8d1 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -53,9 +53,9 @@ namespace Source { std::mutex parse_mutex; std::atomic parse_state; std::atomic parse_process_state; - sigc::connection parse_done_connection; - sigc::connection parse_start_connection; - sigc::connection parse_fail_connection; + sigc::connection parse_preprocess_connection; + sigc::connection parse_postprocess_connection; + sigc::connection parse_error_connection; private: Glib::Dispatcher parse_preprocess; Glib::Dispatcher parse_postprocess; @@ -92,7 +92,7 @@ namespace Source { std::thread autocomplete_thread; sigc::connection autocomplete_done_connection; sigc::connection autocomplete_restart_connection; - sigc::connection autocomplete_fail_connection; + sigc::connection autocomplete_error_connection; sigc::connection do_delete_object_connection; sigc::connection do_restart_parse_connection; private: @@ -106,7 +106,7 @@ namespace Source { std::vector autocomplete_get_suggestions(const std::string &buffer, int line_number, int column); Glib::Dispatcher autocomplete_done; Glib::Dispatcher autocomplete_restart; - Glib::Dispatcher autocomplete_fail; + Glib::Dispatcher autocomplete_error; guint last_keyval=0; std::string prefix; std::mutex prefix_mutex;