From 1a0d89db6be88859db62c7c9cad4069c5f59b382 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sat, 3 Oct 2015 19:58:19 +0200 Subject: [PATCH 01/37] Implemented smart home/end keys that work with wrapped lines option turned on. --- src/source.cc | 54 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/source.cc b/src/source.cc index 224ddb6..abb376a 100644 --- a/src/source.cc +++ b/src/source.cc @@ -48,8 +48,6 @@ Glib::RefPtr Source::guess_language(const boost::filesystem::path AspellConfig* Source::View::spellcheck_config=NULL; Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr language): file_path(file_path) { - set_smart_home_end(Gsv::SMART_HOME_END_BEFORE); - get_source_buffer()->begin_not_undoable_action(); if(language) { if(juci::filesystem::read_non_utf8(file_path, get_buffer())==-1) @@ -1031,6 +1029,58 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { if(perform_smart_delete && iter.backward_char()) get_buffer()->erase(insert_iter, iter); } + //Next two are smart home/end keys that works with wrapped lines + else if(key->keyval==GDK_KEY_End && (key->state&GDK_CONTROL_MASK)==0) { + auto iter=get_buffer()->get_insert()->get_iter(); + auto end_line_iter=iter; + while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {} + auto end_sentence_iter=end_line_iter; + 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.forward_char(); + + if(iterstate&GDK_SHIFT_MASK)>0) + get_buffer()->move_mark_by_name("insert", end_sentence_iter); + else + get_buffer()->place_cursor(end_sentence_iter); + } + else { + if((key->state&GDK_SHIFT_MASK)>0) + get_buffer()->move_mark_by_name("insert", end_line_iter); + else + get_buffer()->place_cursor(end_line_iter); + } + scroll_to(get_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } + else if(key->keyval==GDK_KEY_Home && (key->state&GDK_CONTROL_MASK)==0) { + auto iter=get_buffer()->get_insert()->get_iter(); + auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); + auto start_sentence_iter=start_line_iter; + while(!start_sentence_iter.ends_line() && + (*start_sentence_iter==' ' || *start_sentence_iter=='\t' || start_sentence_iter.starts_line()) && + start_sentence_iter.forward_char()) {} + + if(iter>start_sentence_iter || iter==start_line_iter) { + if((key->state&GDK_SHIFT_MASK)>0) + get_buffer()->move_mark_by_name("insert", start_sentence_iter); + else + get_buffer()->place_cursor(start_sentence_iter); + } + else { + if((key->state&GDK_SHIFT_MASK)>0) + get_buffer()->move_mark_by_name("insert", start_line_iter); + else + get_buffer()->place_cursor(start_line_iter); + } + scroll_to(get_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } bool stop=Gsv::View::on_key_press_event(key); get_source_buffer()->end_user_action(); From 8ca98031f9ea03d69079b7ab409cd71d733c5efe Mon Sep 17 00:00:00 2001 From: Ole Christian Eidheim Date: Sat, 3 Oct 2015 20:16:28 +0200 Subject: [PATCH 02/37] Now using relative links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14a0b36..4ca7d22 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ towards libclang with speed in mind. * libgtksourceviewmm-3.0-dev * libaspell-dev * libclang-dev -* [libclangmm](http://github.com/cppit/libclangmm/) +* [libclangmm](../../../libclangmm/) ## Installation ## -See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md). +See [installation guide](docs/install.md). From 98e3d033f94eb27c9b54c654635d8b89a768711c Mon Sep 17 00:00:00 2001 From: Ole Christian Eidheim Date: Sat, 3 Oct 2015 20:43:54 +0200 Subject: [PATCH 03/37] Reverted back last commit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4ca7d22..14a0b36 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ towards libclang with speed in mind. * libgtksourceviewmm-3.0-dev * libaspell-dev * libclang-dev -* [libclangmm](../../../libclangmm/) +* [libclangmm](http://github.com/cppit/libclangmm/) ## Installation ## -See [installation guide](docs/install.md). +See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md). From 0696a8e32f2df8fcf4ea8fd37356235ae5e1fa58 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 4 Oct 2015 11:23:26 +0200 Subject: [PATCH 04/37] Improved autocomplete insertion rules. --- src/source.cc | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/source.cc b/src/source.cc index abb376a..dc29f72 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1983,21 +1983,32 @@ void Source::ClangViewAutocomplete::autocomplete() { 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) { - char find_char=row.back(); - if(find_char==')' || find_char=='>') { - if(find_char==')') - find_char='('; - else - find_char='<'; - size_t pos=row.find(find_char); - if(pos!=std::string::npos) { - auto start_offset=completion_dialog->start_mark->get_iter().get_offset()+pos+1; - auto end_offset=completion_dialog->start_mark->get_iter().get_offset()+row.size()-1; - 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)); - } + 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)); } } }; From 137e2bcc181da3acdd4b9d23995006465cc6bb2a Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 4 Oct 2015 12:06:15 +0200 Subject: [PATCH 05/37] Fixes to smart delete, backspace and end. --- src/source.cc | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/source.cc b/src/source.cc index dc29f72..6d6c3b8 100644 --- a/src/source.cc +++ b/src/source.cc @@ -994,8 +994,14 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { else if(key->keyval==GDK_KEY_BackSpace && !get_buffer()->get_has_selection()) { auto insert_it=get_buffer()->get_insert()->get_iter(); auto line=get_line_before(); - std::smatch sm; - if(std::regex_match(line, sm, tabs_regex) && sm[1].str().size()==line.size()) { + bool do_smart_backspace=true; + for(auto &chr: line) { + if(chr!=' ' && chr!='\t') { + do_smart_backspace=false; + break; + } + } + if(do_smart_backspace) { auto line_start_iter=insert_it; if(line_start_iter.backward_chars(line.size())) get_buffer()->erase(insert_it, line_start_iter); @@ -1005,31 +1011,26 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { else if(key->keyval==GDK_KEY_Delete && !get_buffer()->get_has_selection()) { auto insert_iter=get_buffer()->get_insert()->get_iter(); auto iter=insert_iter; - bool perform_smart_delete=false; - if(iter.starts_line() && iter.ends_line()) {} - else if(iter.ends_line() && iter.forward_char()) { - if(!iter.ends_line()) { - bool first_line=true; - while((*iter==' ' || *iter=='\t' || (first_line && iter.ends_line())) && iter.forward_char()) { - perform_smart_delete=true; - if(first_line && iter.ends_line()) - first_line=false; - } + bool do_smart_delete=true; + do { + if(*iter!=' ' && *iter!='\t' && !iter.ends_line()) { + do_smart_delete=false; + break; } - } - else { - while((*iter==' ' || *iter=='\t') && iter.forward_char()) { - perform_smart_delete=true; - if(iter.ends_line()) { - iter.forward_char(); - break; - } + if(iter.ends_line()) { + iter.forward_char(); + break; } + } while(iter.forward_char()); + if(do_smart_delete) { + if(!insert_iter.starts_line()) + while((*iter==' ' || *iter=='\t') && iter.forward_char()) {} + if(iter.backward_char()) + get_buffer()->erase(insert_iter, iter); } - if(perform_smart_delete && iter.backward_char()) - get_buffer()->erase(insert_iter, iter); } //Next two are smart home/end keys that works with wrapped lines + //Note that smart end goes FIRST to end of line to avoid hiding empty chars after expressions else if(key->keyval==GDK_KEY_End && (key->state&GDK_CONTROL_MASK)==0) { auto iter=get_buffer()->get_insert()->get_iter(); auto end_line_iter=iter; @@ -1041,7 +1042,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { if(!end_sentence_iter.ends_line()) end_sentence_iter.forward_char(); - if(iterstate&GDK_SHIFT_MASK)>0) get_buffer()->move_mark_by_name("insert", end_sentence_iter); else From d7ec26fcf8243ef6aca7aecae410660c67bbcae7 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 4 Oct 2015 12:19:40 +0200 Subject: [PATCH 06/37] Minor bug fixes to smart keys. --- src/source.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/source.cc b/src/source.cc index 6d6c3b8..8e5f7c7 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1018,7 +1018,8 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { break; } if(iter.ends_line()) { - iter.forward_char(); + if(!iter.forward_char()) + do_smart_delete=false; break; } } while(iter.forward_char()); @@ -1063,7 +1064,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); auto start_sentence_iter=start_line_iter; while(!start_sentence_iter.ends_line() && - (*start_sentence_iter==' ' || *start_sentence_iter=='\t' || start_sentence_iter.starts_line()) && + (*start_sentence_iter==' ' || *start_sentence_iter=='\t') && start_sentence_iter.forward_char()) {} if(iter>start_sentence_iter || iter==start_line_iter) { From b37a9d38d5aee7b971d4e3025b68fe222f6a8dcd Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 5 Oct 2015 15:59:06 +0200 Subject: [PATCH 07/37] Added fix its, and some minor cleanup. --- src/files.h | 3 +- src/menu.cc | 1 + src/source.cc | 135 ++++++++++++++++++++++++++++++++++++++++++++------ src/source.h | 61 ++++++++++++++++------- src/window.cc | 10 ++++ 5 files changed, 176 insertions(+), 34 deletions(-) diff --git a/src/files.h b/src/files.h index 5c9b436..bdf6a42 100644 --- a/src/files.h +++ b/src/files.h @@ -1,6 +1,6 @@ #include -#define JUCI_VERSION "0.9.2" +#define JUCI_VERSION "0.9.3" const std::string configjson = "{\n" @@ -74,6 +74,7 @@ const std::string configjson = " \"source_goto_method\": \"m\",\n" " \"source_rename\": \"r\",\n" " \"source_goto_next_diagnostic\": \"e\",\n" +" \"source_apply_fix_its\": \"space\",\n" " \"compile_and_run\": \"Return\",\n" " \"compile\": \"Return\",\n" " \"run_command\": \"Return\",\n" diff --git a/src/menu.cc b/src/menu.cc index 5ae64be..f6590a5 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -59,6 +59,7 @@ Menu::Menu() { " \n" " \n" " \n" + " \n" " \n" " \n" " \n" diff --git a/src/source.cc b/src/source.cc index 8e5f7c7..484f00a 100644 --- a/src/source.cc +++ b/src/source.cc @@ -42,6 +42,38 @@ Glib::RefPtr Source::guess_language(const boost::filesystem::path return language; } +Source::FixIt::FixIt(const std::string &source, const std::pair &offsets) : source(source), offsets(offsets) { + if(source.size()==0) + type=Type::ERASE; + else { + if(this->offsets.first==this->offsets.second) + type=Type::INSERT; + else + type=Type::REPLACE; + } +} + +std::string Source::FixIt::string() { + std::string text; + if(type==Type::INSERT) { + text+="Insert "+source+" at "; + text+=std::to_string(offsets.first.line)+":"+std::to_string(offsets.first.offset); + } + else if(type==Type::REPLACE) { + text+="Replace "; + text+=std::to_string(offsets.first.line)+":"+std::to_string(offsets.first.offset)+" - "; + text+=std::to_string(offsets.second.line)+":"+std::to_string(offsets.second.offset); + text+=" with "+source; + } + else { + text+="Erase "; + text+=std::to_string(offsets.first.line)+":"+std::to_string(offsets.first.offset)+" - "; + text+=std::to_string(offsets.second.line)+":"+std::to_string(offsets.second.offset); + } + + return text; +} + ////////////// //// View //// ////////////// @@ -1504,7 +1536,7 @@ std::vector Source::ClangViewParse::get_compilation_commands() { } void Source::ClangViewParse::update_syntax() { - std::vector ranges; + std::vector ranges; for (auto &token : *clang_tokens) { //if(token.get_kind()==0) // PunctuationToken //ranges.emplace_back(token.offsets, (int) token.get_cursor().get_kind()); @@ -1544,11 +1576,13 @@ void Source::ClangViewParse::update_syntax() { void Source::ClangViewParse::update_diagnostics() { diagnostic_offsets.clear(); diagnostic_tooltips.clear(); + fix_its.clear(); get_buffer()->remove_tag_by_name("def:warning_underline", get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag_by_name("def:error_underline", get_buffer()->begin(), get_buffer()->end()); auto diagnostics=clang_tu->get_diagnostics(); - size_t warnings=0; - size_t errors=0; + size_t num_warnings=0; + size_t num_errors=0; + size_t num_fix_its=0; for(auto &diagnostic: diagnostics) { if(diagnostic.path==file_path.string()) { auto start_line=get_line(diagnostic.offsets.first.line-1); //index is sometimes off the line @@ -1573,19 +1607,50 @@ void Source::ClangViewParse::update_diagnostics() { std::string diagnostic_tag_name; if(diagnostic.severity<=CXDiagnostic_Warning) { diagnostic_tag_name="def:warning"; - warnings++; + num_warnings++; } else { diagnostic_tag_name="def:error"; - errors++; + num_errors++; } auto spelling=diagnostic.spelling; auto severity_spelling=diagnostic.severity_spelling; - auto create_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name]() { + + std::string fix_its_string; + unsigned fix_its_count=0; + for(auto &fix_it: diagnostic.fix_its) { + //Convert line index to line offset for correct output: + auto clang_offsets=fix_it.offsets; + std::pair offsets; + offsets.first.line=clang_offsets.first.line; + offsets.second.line=clang_offsets.second.line; + auto iter=get_buffer()->get_iter_at_line_index(clang_offsets.first.line-1, clang_offsets.first.index-1); + offsets.first.offset=iter.get_line_offset()+1; + iter=get_buffer()->get_iter_at_line_index(clang_offsets.second.line-1, clang_offsets.second.index-1); + offsets.second.offset=iter.get_line_offset()+1; + + fix_its.emplace_back(fix_it.source, offsets); + + if(fix_its_string.size()>0) + fix_its_string+='\n'; + fix_its_string+=fix_its.back().string(); + fix_its_count++; + num_fix_its++; + } + + if(fix_its_count==1) + fix_its_string.insert(0, "Fix-it:\n"); + else if(fix_its_count>1) + fix_its_string.insert(0, "Fix-its:\n"); + + auto create_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name, fix_its_string]() { auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), severity_spelling, diagnostic_tag_name); tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n"+spelling, "def:note"); + if(fix_its_string.size()>0) { + tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n\n"+fix_its_string, "def:note"); + } return tooltip_buffer; }; diagnostic_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); @@ -1600,16 +1665,23 @@ void Source::ClangViewParse::update_diagnostics() { } } std::string diagnostic_info; - if(warnings>0) { - diagnostic_info+=std::to_string(warnings)+" warning"; - if(warnings>1) + if(num_warnings>0) { + diagnostic_info+=std::to_string(num_warnings)+" warning"; + if(num_warnings>1) + diagnostic_info+='s'; + } + if(num_errors>0) { + if(num_warnings>0) + diagnostic_info+=", "; + diagnostic_info+=std::to_string(num_errors)+" error"; + if(num_errors>1) diagnostic_info+='s'; } - if(errors>0) { - if(warnings>0) + if(num_fix_its>0) { + if(num_warnings>0 || num_errors>0) diagnostic_info+=", "; - diagnostic_info+=std::to_string(errors)+" error"; - if(errors>1) + diagnostic_info+=std::to_string(num_fix_its)+" fix it"; + if(num_fix_its>1) diagnostic_info+='s'; } set_info(" "+diagnostic_info); @@ -1966,7 +2038,7 @@ void Source::ClangViewAutocomplete::autocomplete() { if(!autocomplete_starting) { autocomplete_starting=true; autocomplete_cancel_starting=false; - std::shared_ptr > ac_data=std::make_shared >(); + std::shared_ptr > ac_data=std::make_shared >(); autocomplete_done_connection.disconnect(); autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){ autocomplete_starting=false; @@ -2076,8 +2148,8 @@ void Source::ClangViewAutocomplete::autocomplete() { } } -std::vector Source::ClangViewAutocomplete::get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map) { - std::vector suggestions; +std::vector Source::ClangViewAutocomplete::get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map) { + std::vector suggestions; auto results=clang_tu->get_code_completions(buffer_map, line_number, column); if(results.cx_results==NULL) { parse_thread_stop=true; @@ -2290,6 +2362,37 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { } } }; + + apply_fix_its=[this]() { + std::vector, Glib::RefPtr > > fix_it_marks; + if(source_readable) { + for(auto &fix_it: fix_its) { + auto start_iter=get_buffer()->get_iter_at_line_offset(fix_it.offsets.first.line-1, fix_it.offsets.first.offset-1); + auto end_iter=get_buffer()->get_iter_at_line_offset(fix_it.offsets.second.line-1, fix_it.offsets.second.offset-1); + fix_it_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter)); + } + size_t c=0; + get_source_buffer()->begin_user_action(); + for(auto &fix_it: fix_its) { + if(fix_it.type==FixIt::Type::INSERT) { + get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source); + } + if(fix_it.type==FixIt::Type::REPLACE) { + get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter()); + get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source); + } + if(fix_it.type==FixIt::Type::ERASE) { + get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter()); + } + c++; + } + for(auto &mark_pair: fix_it_marks) { + get_buffer()->delete_mark(mark_pair.first); + get_buffer()->delete_mark(mark_pair.second); + } + get_source_buffer()->end_user_action(); + } + }; } void Source::ClangViewRefactor::tag_similar_tokens(const Token &token) { diff --git a/src/source.h b/src/source.h index c69f178..47a8adb 100644 --- a/src/source.h +++ b/src/source.h @@ -38,22 +38,6 @@ namespace Source { bool show_line_numbers; std::unordered_map clang_types; }; - - class Range { - public: - Range(std::pair offsets, int kind): - offsets(offsets), kind(kind) {} - std::pair offsets; - int kind; - }; - - class AutoCompleteData { - public: - explicit AutoCompleteData(const std::vector &chunks) : - chunks(chunks) { } - std::vector chunks; - std::string brief_comments; - }; class Token { public: @@ -70,6 +54,31 @@ namespace Source { std::string spelling; std::string usr; }; + + class FixIt { + public: + class Offset { + public: + Offset() {} + Offset(unsigned line, unsigned offset): line(line), offset(offset) {} + bool operator==(const Offset &o) {return (line==o.line && offset==o.offset);} + + unsigned line; + unsigned offset; + }; + + enum class Type {INSERT, REPLACE, ERASE}; + + FixIt(Type type, const std::string &source, const std::pair &offsets): + type(type), source(source), offsets(offsets) {} + FixIt(const std::string &source, const std::pair &offsets); + + std::string string(); + + Type type; + std::string source; + std::pair offsets; + }; class View : public Gsv::View { public: @@ -95,6 +104,7 @@ namespace Source { std::function get_token; std::function rename_similar_tokens; std::function goto_next_diagnostic; + std::function apply_fix_its; std::function on_update_status; std::function on_update_info; @@ -176,6 +186,14 @@ namespace Source { class ClangViewParse : public View { public: + class TokenRange { + public: + TokenRange(std::pair offsets, int kind): + offsets(offsets), kind(kind) {} + std::pair offsets; + int kind; + }; + ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); ~ClangViewParse(); void configure(); @@ -204,6 +222,7 @@ namespace Source { std::regex no_bracket_no_para_statement_regex; std::set diagnostic_offsets; + std::vector fix_its; private: std::map get_buffer_map() const; void update_syntax(); @@ -224,6 +243,14 @@ namespace Source { class ClangViewAutocomplete : public ClangViewParse { public: + class AutoCompleteData { + public: + explicit AutoCompleteData(const std::vector &chunks) : + chunks(chunks) { } + std::vector chunks; + std::string brief_comments; + }; + ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); void async_delete(); bool restart_parse(); @@ -235,7 +262,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(int line_number, int column, std::map& buffer_map); Glib::Dispatcher autocomplete_done; sigc::connection autocomplete_done_connection; Glib::Dispatcher autocomplete_fail; diff --git a/src/window.cc b/src/window.cc index 364abb4..ce37895 100644 --- a/src/window.cc +++ b/src/window.cc @@ -115,6 +115,9 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil if(auto menu_item=dynamic_cast(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceGotoNextDiagnostic"))) menu_item->set_sensitive((bool)notebook.get_current_view()->goto_next_diagnostic); + + if(auto menu_item=dynamic_cast(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceApplyFixIts"))) + menu_item->set_sensitive((bool)notebook.get_current_view()->apply_fix_its); directories.select(notebook.get_current_view()->file_path); @@ -300,6 +303,13 @@ void Window::create_menu() { } } }); + menu.action_group->add(Gtk::Action::create("SourceApplyFixIts", "Apply Fix Its"), Gtk::AccelKey(menu.key_map["source_apply_fix_its"]), [this]() { + if(notebook.get_current_page()!=-1) { + if(notebook.get_current_view()->apply_fix_its) { + notebook.get_current_view()->apply_fix_its(); + } + } + }); menu.action_group->add(Gtk::Action::create("ProjectCompileAndRun", "Compile and Run"), Gtk::AccelKey(menu.key_map["compile_and_run"]), [this]() { if(notebook.get_current_page()==-1 || compiling) From f468a601d5d6b61586d03a13cf0dafa91424d6c1 Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 6 Oct 2015 10:25:02 +0200 Subject: [PATCH 08/37] Now only reparse clang files if a header is saved. --- src/notebook.cc | 10 ++++++---- src/source.cc | 2 +- src/source.h | 1 + src/window.cc | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index e6d138a..1feebaf 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -165,10 +165,12 @@ bool Notebook::save(int page, bool reparse_needed) { if(juci::filesystem::write(view->file_path, view->get_buffer())) { if(reparse_needed) { if(auto clang_view=dynamic_cast(view)) { - for(auto a_view: source_views) { - if(auto a_clang_view=dynamic_cast(a_view)) { - if(clang_view!=a_clang_view) - a_clang_view->reparse_needed=true; + 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->reparse_needed=true; + } } } } diff --git a/src/source.cc b/src/source.cc index 484f00a..7af245f 100644 --- a/src/source.cc +++ b/src/source.cc @@ -79,7 +79,7 @@ std::string Source::FixIt::string() { ////////////// AspellConfig* Source::View::spellcheck_config=NULL; -Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr language): file_path(file_path) { +Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr language): file_path(file_path), language(language) { get_source_buffer()->begin_not_undoable_action(); if(language) { if(juci::filesystem::read_non_utf8(file_path, get_buffer())==-1) diff --git a/src/source.h b/src/source.h index 47a8adb..ce34929 100644 --- a/src/source.h +++ b/src/source.h @@ -98,6 +98,7 @@ namespace Source { void paste(); boost::filesystem::path file_path; + Glib::RefPtr language; std::function()> get_declaration_location; std::function goto_method; diff --git a/src/window.cc b/src/window.cc index ce37895..32a1845 100644 --- a/src/window.cc +++ b/src/window.cc @@ -303,7 +303,7 @@ void Window::create_menu() { } } }); - menu.action_group->add(Gtk::Action::create("SourceApplyFixIts", "Apply Fix Its"), Gtk::AccelKey(menu.key_map["source_apply_fix_its"]), [this]() { + menu.action_group->add(Gtk::Action::create("SourceApplyFixIts", "Apply Fix-Its"), Gtk::AccelKey(menu.key_map["source_apply_fix_its"]), [this]() { if(notebook.get_current_page()!=-1) { if(notebook.get_current_view()->apply_fix_its) { notebook.get_current_view()->apply_fix_its(); From 8becce510f380d816f2b420c87e91fce1e0dc64b Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 7 Oct 2015 10:13:14 +0200 Subject: [PATCH 09/37] Now tries to run cmake after saving CMakeLists.txt, even if it is outside of opened directory, or directory is not opened. --- src/notebook.cc | 25 ++++++++++++++++--------- src/source.cc | 6 +++--- src/source.h | 4 ++-- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index 1feebaf..45d1f2a 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -180,17 +180,24 @@ bool Notebook::save(int page, bool reparse_needed) { Singleton::terminal()->print("File saved to: " +view->file_path.string()+"\n"); //If CMakeLists.txt have been modified: - //TODO: recreate cmake even without directories open? + boost::filesystem::path project_path; if(view->file_path.filename()=="CMakeLists.txt") { if(directories.cmake && directories.cmake->project_path!="" && view->file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/' && CMake::create_compile_commands(directories.cmake->project_path)) { - for(auto source_view: source_views) { - if(auto source_clang_view=dynamic_cast(source_view)) { - if(directories.cmake->project_path.string()==source_clang_view->project_path) { - if(source_clang_view->restart_parse()) - Singleton::terminal()->async_print("Reparsing "+source_clang_view->file_path.string()+"\n"); - else - Singleton::terminal()->async_print("Error: failed to reparse "+source_clang_view->file_path.string()+". Please reopen the file manually.\n"); - } + project_path=directories.cmake->project_path; + } + else { + CMake cmake(view->file_path.parent_path()); + if(cmake.project_path!="" && CMake::create_compile_commands(cmake.project_path)) { + project_path=cmake.project_path; + } + } + for(auto source_view: source_views) { + if(auto source_clang_view=dynamic_cast(source_view)) { + if(project_path==source_clang_view->project_path) { + if(source_clang_view->restart_parse()) + Singleton::terminal()->async_print("Reparsing "+source_clang_view->file_path.string()+"\n"); + else + Singleton::terminal()->async_print("Error: failed to reparse "+source_clang_view->file_path.string()+". Please reopen the file manually.\n"); } } } diff --git a/src/source.cc b/src/source.cc index 7af245f..1fb536a 100644 --- a/src/source.cc +++ b/src/source.cc @@ -79,7 +79,7 @@ std::string Source::FixIt::string() { ////////////// AspellConfig* Source::View::spellcheck_config=NULL; -Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr language): file_path(file_path), language(language) { +Source::View::View(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language): file_path(file_path), project_path(project_path), language(language) { get_source_buffer()->begin_not_undoable_action(); if(language) { if(juci::filesystem::read_non_utf8(file_path, get_buffer())==-1) @@ -1254,7 +1254,7 @@ std::vector Source::View::spellcheck_get_suggestions(const Gtk::Tex ///////////////////// //// GenericView //// ///////////////////// -Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language) : View(file_path, language) { +Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language) : View(file_path, "", language) { configure(); spellcheck_all=true; @@ -1340,7 +1340,7 @@ void Source::GenericView::parse_language_file(Glib::RefPtr &co 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, language), project_path(project_path), parse_error(false) { +Source::View(file_path, project_path, language), parse_error(false) { DEBUG("start"); auto tag_table=get_buffer()->get_tag_table(); diff --git a/src/source.h b/src/source.h index ce34929..acb9180 100644 --- a/src/source.h +++ b/src/source.h @@ -82,7 +82,7 @@ namespace Source { class View : public Gsv::View { public: - View(const boost::filesystem::path &file_path, Glib::RefPtr language); + View(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language); ~View(); virtual void configure(); @@ -98,6 +98,7 @@ namespace Source { void paste(); boost::filesystem::path file_path; + boost::filesystem::path project_path; Glib::RefPtr language; std::function()> get_declaration_location; @@ -199,7 +200,6 @@ namespace Source { ~ClangViewParse(); void configure(); - boost::filesystem::path project_path; void start_reparse(); bool reparse_needed=false; protected: From b5a551462c7e316b89e158eeec29b6923181e576 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 7 Oct 2015 10:23:54 +0200 Subject: [PATCH 10/37] Improved path deduction for run command. --- src/window.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/window.cc b/src/window.cc index 32a1845..8e4dac9 100644 --- a/src/window.cc +++ b/src/window.cc @@ -368,8 +368,17 @@ void Window::create_menu() { entry_box.entries.emplace_back(last_run_command, [this](const std::string& content){ if(content!="") { last_run_command=content; + boost::filesystem::path run_path; + if(notebook.get_current_page()!=-1) { + if(notebook.get_current_view()->project_path!="") + run_path=notebook.get_current_view()->project_path; + else + run_path=notebook.get_current_view()->file_path.parent_path(); + } + else + run_path=directories.current_path; Singleton::terminal()->async_print("Running: "+content+'\n'); - Singleton::terminal()->async_execute(content, directories.current_path, [this, content](int exit_code){ + Singleton::terminal()->async_execute(content, run_path, [this, content](int exit_code){ Singleton::terminal()->async_print(content+" returned: "+std::to_string(exit_code)+'\n'); }); } From 97bae361f4298c5432e7f864cc402e29ce550f50 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 7 Oct 2015 10:48:58 +0200 Subject: [PATCH 11/37] Added label to Run Command, and some minor cleanup. --- src/notebook.cc | 18 ++++++++++-------- src/source.cc | 2 +- src/source.h | 2 +- src/window.cc | 8 +++++++- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index 45d1f2a..2987538 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -89,7 +89,7 @@ void Notebook::open(const boost::filesystem::path &file_path) { source_views.emplace_back(new Source::ClangView(file_path, project_path, language)); } else - source_views.emplace_back(new Source::GenericView(file_path, language)); + source_views.emplace_back(new Source::GenericView(file_path, "", language)); source_views.back()->on_update_status=[this](Source::View* view, const std::string &status) { if(get_current_page()!=-1 && get_current_view()==view) @@ -191,13 +191,15 @@ bool Notebook::save(int page, bool reparse_needed) { project_path=cmake.project_path; } } - for(auto source_view: source_views) { - if(auto source_clang_view=dynamic_cast(source_view)) { - if(project_path==source_clang_view->project_path) { - if(source_clang_view->restart_parse()) - Singleton::terminal()->async_print("Reparsing "+source_clang_view->file_path.string()+"\n"); - else - Singleton::terminal()->async_print("Error: failed to reparse "+source_clang_view->file_path.string()+". Please reopen the file manually.\n"); + if(project_path!="") { + for(auto source_view: source_views) { + if(auto source_clang_view=dynamic_cast(source_view)) { + if(project_path==source_clang_view->project_path) { + if(source_clang_view->restart_parse()) + Singleton::terminal()->async_print("Reparsing "+source_clang_view->file_path.string()+"\n"); + else + Singleton::terminal()->async_print("Error: failed to reparse "+source_clang_view->file_path.string()+". Please reopen the file manually.\n"); + } } } } diff --git a/src/source.cc b/src/source.cc index 1fb536a..34fe021 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1254,7 +1254,7 @@ std::vector Source::View::spellcheck_get_suggestions(const Gtk::Tex ///////////////////// //// GenericView //// ///////////////////// -Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language) : View(file_path, "", language) { +Source::GenericView::GenericView(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language) : View(file_path, project_path, language) { configure(); spellcheck_all=true; diff --git a/src/source.h b/src/source.h index acb9180..cbaaf45 100644 --- a/src/source.h +++ b/src/source.h @@ -181,7 +181,7 @@ namespace Source { static Glib::RefPtr create() {return Glib::RefPtr(new CompletionBuffer());} }; public: - GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language); + GenericView(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language); void parse_language_file(Glib::RefPtr &completion_buffer, bool &has_context_class, const boost::property_tree::ptree &pt); }; diff --git a/src/window.cc b/src/window.cc index 8e4dac9..6663fa7 100644 --- a/src/window.cc +++ b/src/window.cc @@ -365,6 +365,12 @@ void Window::create_menu() { }); menu.action_group->add(Gtk::Action::create("ProjectRunCommand", "Run Command"), Gtk::AccelKey(menu.key_map["run_command"]), [this]() { entry_box.clear(); + entry_box.labels.emplace_back(); + auto label_it=entry_box.labels.begin(); + label_it->update=[label_it](int state, const std::string& message){ + label_it->set_text("Run Command directory order: file project path, file directory, opened directory, current directory"); + }; + label_it->update(0, ""); entry_box.entries.emplace_back(last_run_command, [this](const std::string& content){ if(content!="") { last_run_command=content; @@ -777,7 +783,7 @@ void Window::rename_token_entry() { entry_box.labels.emplace_back(); auto label_it=entry_box.labels.begin(); label_it->update=[label_it](int state, const std::string& message){ - label_it->set_text("Warning: only opened and parsed tabs will have its content renamed, and modified files will be saved."); + label_it->set_text("Warning: only opened and parsed tabs will have its content renamed, and modified files will be saved"); }; label_it->update(0, ""); entry_box.entries.emplace_back(token->spelling, [this, token](const std::string& content){ From ef54dde7438acea0e55e0e1eb011419f16238916 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 7 Oct 2015 11:02:06 +0200 Subject: [PATCH 12/37] More up to date feature list. --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 14a0b36..d183885 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,24 @@ towards libclang with speed in mind. ## Features * Fast and responsive * Syntax highlighting (even C++11/14, and more than 100 other file types) -* C++ warnings and errors on the fly -* Fast C++ autocomletion (even external libraries) +* (Obj)C(++) warnings and errors on the fly +* (Obj)C(++) Fix-its +* Automated CMake processing +* Fast (Obj)C(++) autocomletion (even external libraries) +* Keyword and buffer autocomletion for other file types * Tooltips showing type information and doxygen documentation * Refactoring across files * Highlighting of similar types * Spell checking depending on file context -* Basic editor functionality +* Run shell commands within JuCi++, even on Windows +* Regex search and replace +* Smart paste, keys and indentation +* Source minimap +* Full UTF-8 support * Write your own plugins in python (disabled at the moment) +See [enhancements](https://github.com/cppit/jucipp/labels/enhancement) for planned features. + ## Dependencies ## * libboost-filesystem-dev * libboost-log-dev From 4189b50ccc3142159dda30c2b54217b4cfa32d0c Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 7 Oct 2015 11:09:13 +0200 Subject: [PATCH 13/37] Minor improvements. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d183885..dabdce7 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # juCi++ -###### a lightweight C++-IDE with support for C++11 and C++14. +###### a lightweight platform independent C++-IDE with support for C++11 and C++14. ## About Current IDEs struggle with C++ support due to the complexity of the programming language. juCI++, however, is designed especially -towards libclang with speed in mind. +towards libclang with speed and ease of use in mind. ## Features +* Platform independent * Fast and responsive * Syntax highlighting (even C++11/14, and more than 100 other file types) * (Obj)C(++) warnings and errors on the fly @@ -22,7 +23,7 @@ towards libclang with speed in mind. * Smart paste, keys and indentation * Source minimap * Full UTF-8 support -* Write your own plugins in python (disabled at the moment) +* Write your own plugins in Python (disabled at the moment) See [enhancements](https://github.com/cppit/jucipp/labels/enhancement) for planned features. From c4589edbe37ad917b7bfb0cae7de6f405b1adcf3 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 7 Oct 2015 11:57:48 +0200 Subject: [PATCH 14/37] Added written in C++ --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dabdce7..ab867fc 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ towards libclang with speed and ease of use in mind. ## Features * Platform independent -* Fast and responsive +* Fast and responsive (written in C++) * Syntax highlighting (even C++11/14, and more than 100 other file types) * (Obj)C(++) warnings and errors on the fly * (Obj)C(++) Fix-its From 4cebe1bf77067ed49668b63accf2157f0323c81d Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 8 Oct 2015 09:41:36 +0200 Subject: [PATCH 15/37] libclangmm is now linked through submodule. No need to install libclangmm anymore. --- .gitmodules | 3 +++ docs/install.md | 21 ++++++++++++--------- libclangmm | 1 + src/CMakeLists.txt | 23 ++++++++++++++++++----- 4 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 .gitmodules create mode 160000 libclangmm diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..59face8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libclangmm"] + path = libclangmm + url = https://github.com/eidheim/libclangmm.git diff --git a/docs/install.md b/docs/install.md index ca6052a..c5c85c8 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,11 +1,13 @@ # juCi++ ## Installation guide ## -Before installation, please install libclangmm, see [installation guide](http://github.com/cppit/libclangmm/blob/master/docs/install.md). ## Debian/Ubuntu 15 +Install dependencies: ```sh -sudo apt-get install pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev git +sudo apt-get install git cmake make g++ libclang-dev pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev ``` + +Get juCi++ source, compile and install: ```sh git clone http://github.com/cppit/jucipp.git cd jucipp @@ -15,13 +17,16 @@ sudo make install ``` ## Ubuntu 14/Linux Mint 17 +Install dependencies: ```sh sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install g++-4.9 sudo apt-get remove g++-4.8 -sudo apt-get install pkg-config libboost-system1.55-dev libboost-thread1.55-dev libboost-filesystem1.55-dev libboost-log1.55-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev git +sudo apt-get install git cmake make g++ libclang-dev pkg-config libboost-system1.55-dev libboost-thread1.55-dev libboost-filesystem1.55-dev libboost-log1.55-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev ``` + +Get juCi++ source, compile and install: ```sh git clone http://github.com/cppit/jucipp.git cd jucipp @@ -31,10 +36,12 @@ sudo make install ``` ## OS X with Homebrew (http://brew.sh/) +Install dependencies (installing llvm may take some time): ```sh -brew install pkg-config boost gtkmm3 homebrew/x11/gtksourceviewmm3 aspell git +brew install cmake --with-clang llvm pkg-config boost gtkmm3 homebrew/x11/gtksourceviewmm3 aspell ``` +Get juCi++ source, compile and install: ```sh git clone https://github.com/cppit/jucipp.git cd jucipp @@ -49,14 +56,10 @@ Install dependencies (replace x86_64 with i686 for 32-bit MSYS2 installs): pacman -S patch autoconf automake-wrapper mingw-w64-x86_64-gtkmm3 mingw-w64-x86_64-gtksourceviewmm3 mingw-w64-x86_64-boost mingw-w64-x86_64-aspell mingw-w64-x86_64-aspell-en git ``` -Get juCi++ source: +Get juCi++ source, compile and install (replace mingw64 with mingw32 for 32-bit MSYS2 installs): ```sh git clone https://github.com/cppit/jucipp.git cd jucipp -``` - -Compile and install juCi++ source (replace mingw64 with mingw32 for 32-bit MSYS2 installs): -```sh cmake -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=/mingw64 . make make install diff --git a/libclangmm b/libclangmm new file mode 160000 index 0000000..e060dec --- /dev/null +++ b/libclangmm @@ -0,0 +1 @@ +Subproject commit e060dec32bb8306eff6f9114092cdbefe8fea5fb diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 954e2ac..a0376d5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,9 +35,6 @@ function(validate FOUND APPLE UNIX WINDOWS) endif() endfunction(validate) -find_package(LibClangmm) -validate(${LCL_FOUND} "clangmm" "clangmm" "clangmm") - find_package(LibClang) validate(${LIBCLANG_FOUND} "clang" "libclang-dev" "llvm") @@ -84,7 +81,22 @@ set(source_files juci.h singletons.h singletons.cc cmake.h - cmake.cc) + cmake.cc + + ../libclangmm/src/CodeCompleteResults.cc + ../libclangmm/src/CompilationDatabase.cc + ../libclangmm/src/CompileCommand.cc + ../libclangmm/src/CompileCommands.cc + ../libclangmm/src/CompletionString.cc + ../libclangmm/src/Cursor.cc + ../libclangmm/src/Index.cc + ../libclangmm/src/SourceLocation.cc + ../libclangmm/src/SourceRange.cc + ../libclangmm/src/Token.cc + ../libclangmm/src/Tokens.cc + ../libclangmm/src/TranslationUnit.cc + ../libclangmm/src/Diagnostic.cc + ../libclangmm/src/Utility.cc) if(MSYS) list(APPEND source_files terminal_win.cc) @@ -106,7 +118,8 @@ if(${validation}) ${GTKSVMM_INCLUDE_DIRS} ${LCL_INCLUDE_DIRS} ${LIBCLANG_INCLUDE_DIRS} - ${ASPELL_INCLUDE_DIR}) + ${ASPELL_INCLUDE_DIR} + ../libclangmm/src) link_directories( ${GTKMM_LIBRARY_DIRS} From 03b6476568029094c8192b0b4035291fc469f194 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 8 Oct 2015 10:21:02 +0200 Subject: [PATCH 16/37] install docs should now be correct with respect to the libclangmm submodule. --- docs/install.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/install.md b/docs/install.md index c5c85c8..fefaa99 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,5 +1,4 @@ -# juCi++ -## Installation guide ## +# juCi++ - Installation Guide ## Debian/Ubuntu 15 Install dependencies: @@ -9,7 +8,7 @@ sudo apt-get install git cmake make g++ libclang-dev pkg-config libboost-system- Get juCi++ source, compile and install: ```sh -git clone http://github.com/cppit/jucipp.git +git clone --recursive http://github.com/cppit/jucipp cd jucipp cmake . make @@ -28,7 +27,7 @@ sudo apt-get install git cmake make g++ libclang-dev pkg-config libboost-system1 Get juCi++ source, compile and install: ```sh -git clone http://github.com/cppit/jucipp.git +git clone --recursive http://github.com/cppit/jucipp cd jucipp cmake . make @@ -43,7 +42,7 @@ brew install cmake --with-clang llvm pkg-config boost gtkmm3 homebrew/x11/gtksou Get juCi++ source, compile and install: ```sh -git clone https://github.com/cppit/jucipp.git +git clone --recursive https://github.com/cppit/jucipp cd jucipp cmake . make @@ -51,14 +50,14 @@ make install ``` ##Windows with MSYS2 (https://msys2.github.io/) -Install dependencies (replace x86_64 with i686 for 32-bit MSYS2 installs): +Install dependencies (replace `x86_64` with `i686` for 32-bit MSYS2 installs): ```sh -pacman -S patch autoconf automake-wrapper mingw-w64-x86_64-gtkmm3 mingw-w64-x86_64-gtksourceviewmm3 mingw-w64-x86_64-boost mingw-w64-x86_64-aspell mingw-w64-x86_64-aspell-en git +pacman -S git mingw-w64-x86_64-cmake make mingw-w64-x86_64-toolchain mingw-w64-x86_64-clang mingw-w64-x86_64-gtkmm3 mingw-w64-x86_64-gtksourceviewmm3 mingw-w64-x86_64-boost mingw-w64-x86_64-aspell mingw-w64-x86_64-aspell-en ``` -Get juCi++ source, compile and install (replace mingw64 with mingw32 for 32-bit MSYS2 installs): +Get juCi++ source, compile and install (replace `mingw64` with `mingw32` for 32-bit MSYS2 installs): ```sh -git clone https://github.com/cppit/jucipp.git +git clone --recursive https://github.com/cppit/jucipp cd jucipp cmake -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=/mingw64 . make From 6bd9cd5ae682ab2f763278bc546f4d3872437ea4 Mon Sep 17 00:00:00 2001 From: Ole Christian Eidheim Date: Thu, 8 Oct 2015 10:31:33 +0200 Subject: [PATCH 17/37] Minor fix --- docs/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.md b/docs/install.md index fefaa99..fd7045c 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,4 +1,4 @@ -# juCi++ - Installation Guide +# juCi++ Installation Guide ## Debian/Ubuntu 15 Install dependencies: From 7023b687424b300800be18b18acd52f01c68df80 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 8 Oct 2015 10:39:43 +0200 Subject: [PATCH 18/37] Apply Fix-its now has a key that works on most systems. --- src/files.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/files.h b/src/files.h index bdf6a42..49d4021 100644 --- a/src/files.h +++ b/src/files.h @@ -74,7 +74,7 @@ const std::string configjson = " \"source_goto_method\": \"m\",\n" " \"source_rename\": \"r\",\n" " \"source_goto_next_diagnostic\": \"e\",\n" -" \"source_apply_fix_its\": \"space\",\n" +" \"source_apply_fix_its\": \"space\",\n" " \"compile_and_run\": \"Return\",\n" " \"compile\": \"Return\",\n" " \"run_command\": \"Return\",\n" From 9d384774e69acbd5b3e74d5a7bf8653c6be5922a Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 8 Oct 2015 13:54:37 +0200 Subject: [PATCH 19/37] Now adding project_path if possible to all file types. --- src/notebook.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index 2987538..b255cfe 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -88,8 +88,12 @@ void Notebook::open(const boost::filesystem::path &file_path) { } source_views.emplace_back(new Source::ClangView(file_path, project_path, language)); } - else - source_views.emplace_back(new Source::GenericView(file_path, "", language)); + else { + boost::filesystem::path project_path; + if(directories.cmake && directories.cmake->project_path!="" && file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/') + project_path=directories.cmake->project_path; + source_views.emplace_back(new Source::GenericView(file_path, project_path, language)); + } source_views.back()->on_update_status=[this](Source::View* view, const std::string &status) { if(get_current_page()!=-1 && get_current_view()==view) From 4987da6bde9dab2741cf5fb0e9270c199b833187 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 9 Oct 2015 10:06:45 +0200 Subject: [PATCH 20/37] Project path fix and cleanup. --- src/notebook.cc | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index b255cfe..a18204c 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -69,31 +69,24 @@ void Notebook::open(const boost::filesystem::path &file_path) { can_read.close(); auto language=Source::guess_language(file_path); - if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { - boost::filesystem::path project_path; - if(directories.cmake && directories.cmake->project_path!="" && file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/') { - project_path=directories.cmake->project_path; - if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json")) - CMake::create_compile_commands(project_path); - } - else { - project_path=file_path.parent_path(); - CMake cmake(project_path); - if(cmake.project_path!="") { - project_path=cmake.project_path; - Singleton::terminal()->print("Project path for "+file_path.string()+" set to "+project_path.string()+"\n"); - } - else - Singleton::terminal()->print("Error: could not find project path for "+file_path.string()+"\n"); + boost::filesystem::path project_path; + if(directories.cmake && directories.cmake->project_path!="" && file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/') + project_path=directories.cmake->project_path; + else { + project_path=file_path.parent_path(); + CMake cmake(project_path); + if(cmake.project_path!="") { + project_path=cmake.project_path; + Singleton::terminal()->print("Project path for "+file_path.string()+" set to "+project_path.string()+"\n"); } + } + if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { + if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json")) + CMake::create_compile_commands(project_path); source_views.emplace_back(new Source::ClangView(file_path, project_path, language)); } - else { - boost::filesystem::path project_path; - if(directories.cmake && directories.cmake->project_path!="" && file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/') - project_path=directories.cmake->project_path; + else source_views.emplace_back(new Source::GenericView(file_path, project_path, language)); - } source_views.back()->on_update_status=[this](Source::View* view, const std::string &status) { if(get_current_page()!=-1 && get_current_view()==view) From 0307801336c0599b069a5cda8ac0cb3a3d0c2eee Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 9 Oct 2015 16:32:17 +0200 Subject: [PATCH 21/37] Ongoing work: Find Documentation. --- src/config.cc | 9 ++++++ src/files.h | 13 ++++++++ src/menu.cc | 2 ++ src/source.cc | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/source.h | 9 ++++++ src/window.cc | 37 ++++++++++++++++++++++- 6 files changed, 152 insertions(+), 1 deletion(-) diff --git a/src/config.cc b/src/config.cc index 13cc842..e16f22f 100644 --- a/src/config.cc +++ b/src/config.cc @@ -134,6 +134,15 @@ void MainConfig::GenerateSource() { for (auto &i : source_json.get_child("clang_types")) source_cfg->clang_types[i.first] = i.second.get_value(); + + auto pt_doc_search=cfg.get_child("documentation_searches"); + for(auto &pt_doc_search_lang: pt_doc_search) { + source_cfg->documentation_searches[pt_doc_search_lang.first].separator=pt_doc_search_lang.second.get("separator"); + auto &queries=source_cfg->documentation_searches.find(pt_doc_search_lang.first)->second.queries; + for(auto &i: pt_doc_search_lang.second.get_child("queries")) { + queries[i.first]=i.second.get_value(); + } + } } void MainConfig::GenerateDirectoryFilter() { diff --git a/src/files.h b/src/files.h index 49d4021..9fcb4d7 100644 --- a/src/files.h +++ b/src/files.h @@ -73,6 +73,7 @@ const std::string configjson = " \"source_goto_declaration\": \"d\",\n" " \"source_goto_method\": \"m\",\n" " \"source_rename\": \"r\",\n" +" \"source_find_documentation\": \"d\",\n" " \"source_goto_next_diagnostic\": \"e\",\n" " \"source_apply_fix_its\": \"space\",\n" " \"compile_and_run\": \"Return\",\n" @@ -92,6 +93,18 @@ const std::string configjson = #endif " \"make_command\": \"make\"\n" " },\n" +" \"documentation_searches\": {\n" +" \"clang\": {\n" +" \"separator\": \"::\",\n" +" \"queries\": {\n" +" \"@empty\": \"https://www.google.com/search?btnI&q=site:http://www.cplusplus.com/reference/+\",\n" +" \"std\": \"https://www.google.com/search?btnI&q=site:http://www.cplusplus.com/reference/+\",\n" +" \"boost\": \"https://www.google.com/search?btnI&q=site:http://www.boost.org/doc/libs/1_59_0/+\",\n" +" \"Gtk\": \"https://www.google.com/search?btnI&q=site:https://developer.gnome.org/gtkmm/stable/+\",\n" +" \"@any\": \"https://www.google.com/search?btnI&q=\"\n" +" }\n" +" }\n" +" },\n" " \"directoryfilter\": {\n" " \"ignore\": [\n" " ],\n" diff --git a/src/menu.cc b/src/menu.cc index f6590a5..8c19df2 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -54,6 +54,8 @@ Menu::Menu() { " \n" " \n" " \n" + " \n" + " \n" " \n" " \n" " \n" diff --git a/src/source.cc b/src/source.cc index 34fe021..d914241 100644 --- a/src/source.cc +++ b/src/source.cc @@ -2345,6 +2345,89 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { } }; + get_token_data=[this]() { + const auto find_non_word_char=[](const std::string &str, size_t start_pos) { + for(size_t c=start_pos;c='a' && str[c]<='z') || + (str[c]>='A' && str[c]<='Z') || + (str[c]>='0' && str[c]<='9') || + str[c]=='_')) + return c; + } + return std::string::npos; + }; + + std::vector data; + if(source_readable) { + auto iter=get_buffer()->get_insert()->get_iter(); + auto line=(unsigned)iter.get_line(); + auto index=(unsigned)iter.get_line_index(); + for(auto &token: *clang_tokens) { + auto cursor=token.get_cursor(); + if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) { + if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) { + auto referenced=cursor.get_referenced(); + if(referenced) { + auto usr=referenced.get_usr(); + data.emplace_back("clang"); + + //namespace - not working + size_t pos1=usr.find("@N@"); + size_t pos2; + std::string first_namespace; + while(pos1!=std::string::npos) { + pos1+=3; + pos2=find_non_word_char(usr, pos1); + if(pos1!=std::string::npos && pos2!=std::string::npos) { + auto ns=usr.substr(pos1, pos2-pos1); + if(first_namespace.size()==0) + first_namespace=ns; + else if(ns==first_namespace || ns=="std") + break; + data.emplace_back(ns); + pos1=usr.find("@N@", pos2); + } + else + pos1=std::string::npos; + } + + //type + pos1=usr.find("@T@"); + if(pos1==std::string::npos) + pos1=usr.find("@S@"); + if(pos1!=std::string::npos) { + pos1+=3; + pos2=find_non_word_char(usr, pos1); + } + if(pos1!=std::string::npos) { + if(pos2!=std::string::npos) + data.emplace_back(usr.substr(pos1, pos2-pos1)); + else + data.emplace_back(usr.substr(pos1)); + } + + //function + pos1=usr.find("@F@"); + if(pos1!=std::string::npos) { + pos1+=3; + pos2=find_non_word_char(usr, pos1); + } + if(pos1!=std::string::npos) { + if(pos2!=std::string::npos) + data.emplace_back(usr.substr(pos1, pos2-pos1)); + else + data.emplace_back(usr.substr(pos1)); + } + + break; + } + } + } + } + } + return data; + }; + goto_next_diagnostic=[this]() { if(source_readable) { auto insert_offset=get_buffer()->get_insert()->get_iter().get_offset(); diff --git a/src/source.h b/src/source.h index cbaaf45..ad9837a 100644 --- a/src/source.h +++ b/src/source.h @@ -23,6 +23,12 @@ namespace Source { class Config { public: + class DocumentationSearch { + public: + std::string separator; + std::unordered_map queries; + }; + std::string style; std::string font; std::string spellcheck_language; @@ -37,6 +43,8 @@ namespace Source { bool highlight_current_line; bool show_line_numbers; std::unordered_map clang_types; + + std::unordered_map documentation_searches; }; class Token { @@ -104,6 +112,7 @@ namespace Source { std::function()> get_declaration_location; std::function goto_method; std::function get_token; + std::function()> get_token_data; std::function rename_similar_tokens; std::function goto_next_diagnostic; std::function apply_fix_its; diff --git a/src/window.cc b/src/window.cc index 6663fa7..e83986c 100644 --- a/src/window.cc +++ b/src/window.cc @@ -117,7 +117,10 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil menu_item->set_sensitive((bool)notebook.get_current_view()->goto_next_diagnostic); if(auto menu_item=dynamic_cast(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceApplyFixIts"))) - menu_item->set_sensitive((bool)notebook.get_current_view()->apply_fix_its); + menu_item->set_sensitive((bool)notebook.get_current_view()->apply_fix_its); + + if(auto menu_item=dynamic_cast(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceFindDocumentation"))) + menu_item->set_sensitive((bool)notebook.get_current_view()->get_token_data); directories.select(notebook.get_current_view()->file_path); @@ -296,6 +299,38 @@ void Window::create_menu() { menu.action_group->add(Gtk::Action::create("SourceRename", "Rename"), Gtk::AccelKey(menu.key_map["source_rename"]), [this]() { rename_token_entry(); }); + menu.action_group->add(Gtk::Action::create("SourceFindDocumentation", "Find Documentation"), Gtk::AccelKey(menu.key_map["source_find_documentation"]), [this]() { + if(notebook.get_current_page()!=-1) { + if(notebook.get_current_view()->get_token_data) { + auto data=notebook.get_current_view()->get_token_data(); + if(data.size()>0) { + auto documentation_search=Singleton::Config::source()->documentation_searches.find(data[0]); + if(documentation_search!=Singleton::Config::source()->documentation_searches.end()) { + std::string token_query; + for(size_t c=1;c0) { + if(token_query.size()>0) + token_query+=documentation_search->second.separator; + token_query+=data[c]; + } + } + if(token_query.size()>0) { + std::unordered_map::iterator query; + if(data[1].size()>0) + query=documentation_search->second.queries.find(data[1]); + else + query=documentation_search->second.queries.find("@empty"); + if(query==documentation_search->second.queries.end()) + query=documentation_search->second.queries.find("@any"); + + if(query!=documentation_search->second.queries.end()) + Singleton::terminal()->execute("open \""+query->second+token_query+"\""); + } + } + } + } + } + }); menu.action_group->add(Gtk::Action::create("SourceGotoNextDiagnostic", "Go to next Diagnostic"), Gtk::AccelKey(menu.key_map["source_goto_next_diagnostic"]), [this]() { if(notebook.get_current_page()!=-1) { if(notebook.get_current_view()->goto_next_diagnostic) { From e9f26db92c6f71c5287142bf6436d3fb9788c610 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sat, 10 Oct 2015 09:51:22 +0200 Subject: [PATCH 22/37] Find Documentation now works on OS X. --- README.md | 1 + src/entrybox.cc | 6 +++--- src/entrybox.h | 2 +- src/files.h | 2 +- src/source.cc | 32 ++++++++++++++++++++------------ 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ab867fc..ed0e918 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ towards libclang with speed and ease of use in mind. * Tooltips showing type information and doxygen documentation * Refactoring across files * Highlighting of similar types +* Documentation search * Spell checking depending on file context * Run shell commands within JuCi++, even on Windows * Regex search and replace diff --git a/src/entrybox.cc b/src/entrybox.cc index a8bbb85..08a3a5e 100644 --- a/src/entrybox.cc +++ b/src/entrybox.cc @@ -16,8 +16,9 @@ namespace sigc { std::unordered_map > EntryBox::entry_histories; -EntryBox::Entry::Entry(const std::string& content, std::function on_activate, unsigned length) : Gtk::Entry(), on_activate(on_activate) { - set_max_length(length); +EntryBox::Entry::Entry(const std::string& content, std::function on_activate, unsigned width_chars) : Gtk::Entry(), on_activate(on_activate) { + set_max_length(0); + set_width_chars(width_chars); set_text(content); selected_history=0; signal_activate().connect([this](){ @@ -92,7 +93,6 @@ void EntryBox::clear() { void EntryBox::show() { std::vector focus_chain; for(auto& entry: entries) { - entry.set_max_length(0); lower_box.pack_start(entry, Gtk::PACK_SHRINK); focus_chain.emplace_back(&entry); } diff --git a/src/entrybox.h b/src/entrybox.h index e7bdebd..6f8cecf 100644 --- a/src/entrybox.h +++ b/src/entrybox.h @@ -12,7 +12,7 @@ class EntryBox : public Gtk::Box { public: class Entry : public Gtk::Entry { public: - Entry(const std::string& content="", std::function on_activate=nullptr, unsigned length=50); + Entry(const std::string& content="", std::function on_activate=nullptr, unsigned width_chars=-1); std::function on_activate; private: size_t selected_history; diff --git a/src/files.h b/src/files.h index 9fcb4d7..8912d95 100644 --- a/src/files.h +++ b/src/files.h @@ -97,7 +97,7 @@ const std::string configjson = " \"clang\": {\n" " \"separator\": \"::\",\n" " \"queries\": {\n" -" \"@empty\": \"https://www.google.com/search?btnI&q=site:http://www.cplusplus.com/reference/+\",\n" +" \"@empty\": \"https://www.google.com/search?btnI&q=c%2B%2B+\",\n" " \"std\": \"https://www.google.com/search?btnI&q=site:http://www.cplusplus.com/reference/+\",\n" " \"boost\": \"https://www.google.com/search?btnI&q=site:http://www.boost.org/doc/libs/1_59_0/+\",\n" " \"Gtk\": \"https://www.google.com/search?btnI&q=site:https://developer.gnome.org/gtkmm/stable/+\",\n" diff --git a/src/source.cc b/src/source.cc index d914241..02a49a6 100644 --- a/src/source.cc +++ b/src/source.cc @@ -2369,27 +2369,35 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { auto referenced=cursor.get_referenced(); if(referenced) { auto usr=referenced.get_usr(); + boost::filesystem::path referenced_path=referenced.get_source_location().get_path(); + + //Return empty if referenced is within project + if(referenced_path.generic_string().substr(0, this->project_path.generic_string().size()+1)==this->project_path.generic_string()+'/') + return data; + data.emplace_back("clang"); //namespace - not working - size_t pos1=usr.find("@N@"); - size_t pos2; - std::string first_namespace; - while(pos1!=std::string::npos) { + size_t pos1=0, pos2; + while((pos1=usr.find('@', pos1))!=std::string::npos && pos1+1 Date: Sat, 10 Oct 2015 11:01:02 +0200 Subject: [PATCH 23/37] Fixes to Find Documentation. --- src/source.cc | 49 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/source.cc b/src/source.cc index 02a49a6..4ca6811 100644 --- a/src/source.cc +++ b/src/source.cc @@ -2371,15 +2371,17 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { auto usr=referenced.get_usr(); boost::filesystem::path referenced_path=referenced.get_source_location().get_path(); + //Singleton::terminal()->print(usr+'\n', true); //TODO: remove + //Return empty if referenced is within project if(referenced_path.generic_string().substr(0, this->project_path.generic_string().size()+1)==this->project_path.generic_string()+'/') return data; data.emplace_back("clang"); - //namespace - not working - size_t pos1=0, pos2; - while((pos1=usr.find('@', pos1))!=std::string::npos && pos1+1 Date: Sat, 10 Oct 2015 12:09:21 +0200 Subject: [PATCH 24/37] Find Document is finished and now works on all platforms. --- README.md | 6 +++--- src/window.cc | 12 ++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ed0e918..81badf0 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ towards libclang with speed and ease of use in mind. * Platform independent * Fast and responsive (written in C++) * Syntax highlighting (even C++11/14, and more than 100 other file types) -* (Obj)C(++) warnings and errors on the fly -* (Obj)C(++) Fix-its +* C++ warnings and errors on the fly +* C++ Fix-its * Automated CMake processing -* Fast (Obj)C(++) autocomletion (even external libraries) +* Fast C++ autocomletion (including external libraries) * Keyword and buffer autocomletion for other file types * Tooltips showing type information and doxygen documentation * Refactoring across files diff --git a/src/window.cc b/src/window.cc index e83986c..c62ad72 100644 --- a/src/window.cc +++ b/src/window.cc @@ -323,8 +323,16 @@ void Window::create_menu() { if(query==documentation_search->second.queries.end()) query=documentation_search->second.queries.find("@any"); - if(query!=documentation_search->second.queries.end()) - Singleton::terminal()->execute("open \""+query->second+token_query+"\""); + if(query!=documentation_search->second.queries.end()) { + std::string uri=query->second+token_query; +#ifdef __APPLE__ + Singleton::terminal()->execute("open \""+uri+"\""); +#else + GError* error=NULL; + gtk_show_uri(NULL, uri.c_str(), GDK_CURRENT_TIME, &error); + g_clear_error(&error); +#endif + } } } } From 99908d4e3dca76313401b23aeffbdb2af8aeb493 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 11 Oct 2015 10:53:06 +0200 Subject: [PATCH 25/37] Added possibility to change tab size and char for a buffer. --- src/files.h | 1 + src/menu.cc | 2 ++ src/source.cc | 17 +++++++++----- src/source.h | 3 +++ src/window.cc | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/window.h | 1 + 6 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/files.h b/src/files.h index 8912d95..b6f46d2 100644 --- a/src/files.h +++ b/src/files.h @@ -65,6 +65,7 @@ const std::string configjson = " \"edit_undo\": \"z\",\n" " \"edit_redo\": \"z\",\n" " \"edit_find\": \"f\",\n" +" \"edit_set_tab\": \"\",\n" " \"source_spellcheck\": \"\",\n" " \"source_spellcheck_clear\": \"\",\n" " \"source_spellcheck_next_error\": \"e\",\n" diff --git a/src/menu.cc b/src/menu.cc index 8c19df2..fbfe933 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -43,6 +43,8 @@ Menu::Menu() { " \n" " \n" " \n" + " \n" + " \n" " \n" " \n" " \n" diff --git a/src/source.cc b/src/source.cc index 4ca6811..412e0c3 100644 --- a/src/source.cc +++ b/src/source.cc @@ -260,6 +260,17 @@ Source::View::View(const boost::filesystem::path &file_path, const boost::filesy set_tooltip_events(); } +void Source::View::set_tab_char_and_size(char tab_char, unsigned tab_size) { + this->tab_char=tab_char; + this->tab_size=tab_size; + + tab.clear(); + for(unsigned c=0;c get_tab_char_and_size() {return {tab_char, tab_size};} protected: bool source_readable; Tooltips diagnostic_tooltips; diff --git a/src/window.cc b/src/window.cc index c62ad72..3d167db 100644 --- a/src/window.cc +++ b/src/window.cc @@ -249,6 +249,9 @@ void Window::create_menu() { } } }); + menu.action_group->add(Gtk::Action::create("EditSetTab", "Set Tab Char and Size"), Gtk::AccelKey(menu.key_map["edit_set_tab"]), [this]() { + set_tab_entry(); + }); menu.action_group->add(Gtk::Action::create("SourceSpellCheck", "Spell Check")); menu.action_group->add(Gtk::Action::create("SourceSpellCheckBuffer", "Spell Check Buffer"), Gtk::AccelKey(menu.key_map["source_spellcheck"]), [this]() { @@ -787,6 +790,65 @@ void Window::search_and_replace_entry() { entry_box.show(); } +void Window::set_tab_entry() { + entry_box.clear(); + if(notebook.get_current_page()!=-1) { + auto tab_char_and_size=notebook.get_current_view()->get_tab_char_and_size(); + + entry_box.labels.emplace_back(); + auto label_it=entry_box.labels.begin(); + + entry_box.entries.emplace_back(std::to_string(tab_char_and_size.second)); + auto entry_tab_size_it=entry_box.entries.begin(); + entry_tab_size_it->set_placeholder_text("Tab size"); + + char tab_char=tab_char_and_size.first; + std::string tab_char_string; + if(tab_char==' ') + tab_char_string="space"; + else if(tab_char=='\t') + tab_char_string="tab"; + + entry_box.entries.emplace_back(tab_char_string); + auto entry_tab_char_it=entry_box.entries.rbegin(); + entry_tab_char_it->set_placeholder_text("Tab char"); + + const auto activate_function=[this, entry_tab_char_it, entry_tab_size_it, label_it](const std::string& content){ + if(notebook.get_current_page()!=-1) { + char tab_char=0; + unsigned tab_size=0; + try { + tab_size = static_cast(stoul(entry_tab_size_it->get_text())); + std::string tab_char_string=entry_tab_char_it->get_text(); + std::transform(tab_char_string.begin(), tab_char_string.end(), tab_char_string.begin(), ::tolower); + if(tab_char_string=="space") + tab_char=' '; + else if(tab_char_string=="tab") + tab_char='\t'; + } + catch(const std::exception &e) {} + + if(tab_char!=0 && tab_size>0) { + notebook.get_current_view()->set_tab_char_and_size(tab_char, tab_size); + entry_box.hide(); + } + else { + label_it->set_text("Tab size must be >0 and tab char set to either 'space' or 'tab'"); + } + } + }; + + entry_tab_char_it->on_activate=activate_function; + entry_tab_size_it->on_activate=activate_function; + + entry_box.buttons.emplace_back("Set tab char and size", [this, entry_tab_char_it](){ + entry_tab_char_it->activate(); + }); + + entry_box.show(); + } +} + void Window::goto_line_entry() { entry_box.clear(); if(notebook.get_current_page()!=-1) { diff --git a/src/window.h b/src/window.h index 095e949..4fa0460 100644 --- a/src/window.h +++ b/src/window.h @@ -50,6 +50,7 @@ private: void open_file_dialog(); void save_file_dialog(); void search_and_replace_entry(); + void set_tab_entry(); void goto_line_entry(); void rename_token_entry(); void generate_keybindings(); From 18caaeda6e629cfa764c7ea939e57b47b6ce09b8 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 11 Oct 2015 14:40:02 +0200 Subject: [PATCH 26/37] Replaced tab regex with function that finds end of tab. --- src/source.cc | 201 +++++++++++++++++++++++++------------------------- src/source.h | 5 +- src/window.cc | 4 +- 3 files changed, 107 insertions(+), 103 deletions(-) diff --git a/src/source.cc b/src/source.cc index 412e0c3..42d4ea9 100644 --- a/src/source.cc +++ b/src/source.cc @@ -258,6 +258,26 @@ Source::View::View(const boost::filesystem::path &file_path, const boost::filesy }); set_tooltip_events(); + + tab_char=Singleton::Config::source()->default_tab_char; + tab_size=Singleton::Config::source()->default_tab_size; + if(Singleton::Config::source()->auto_tab_char_and_size) { + auto tab_char_and_size=find_tab_char_and_size(); + if(tab_char_and_size.first!=0) { + if(tab_char!=tab_char_and_size.first || tab_size!=tab_char_and_size.second) { + std::string tab_str; + if(tab_char_and_size.first==' ') + tab_str=""; + else + tab_str=""; + Singleton::terminal()->print("Tab char and size for file "+file_path.string()+" set to: "+tab_str+", "+std::to_string(tab_char_and_size.second)+".\n"); + } + + tab_char=tab_char_and_size.first; + tab_size=tab_char_and_size.second; + } + } + set_tab_char_and_size(tab_char, tab_size); } void Source::View::set_tab_char_and_size(char tab_char, unsigned tab_size) { @@ -267,8 +287,6 @@ void Source::View::set_tab_char_and_size(char tab_char, unsigned tab_size) { tab.clear(); for(unsigned c=0;cremove_tag_by_name("spellcheck_error", get_buffer()->begin(), get_buffer()->end()); - - tab_char=Singleton::Config::source()->default_tab_char; - tab_size=Singleton::Config::source()->default_tab_size; - if(Singleton::Config::source()->auto_tab_char_and_size) { - auto tab_char_and_size=find_tab_char_and_size(); - if(tab_char_and_size.first!=0) { - if(tab_char!=tab_char_and_size.first || tab_size!=tab_char_and_size.second) { - std::string tab_str; - if(tab_char_and_size.first==' ') - tab_str=""; - else - tab_str=""; - Singleton::terminal()->print("Tab char and size for file "+file_path.string()+" set to: "+tab_str+", "+std::to_string(tab_char_and_size.second)+".\n"); - } - - tab_char=tab_char_and_size.first; - tab_size=tab_char_and_size.second; - } - } - set_tab_char_and_size(tab_char, tab_size); } void Source::View::set_tooltip_events() { @@ -542,8 +540,9 @@ void Source::View::paste() { auto line=get_line_before(); std::smatch sm; std::string prefix_tabs; - if(!get_buffer()->get_has_selection() && std::regex_match(line, sm, tabs_regex) && sm[2].str().size()==0) { - prefix_tabs=sm[1].str(); + auto tabs_end_iter=get_tabs_end_iter(); + if(!get_buffer()->get_has_selection() && tabs_end_iter.ends_line()) { + prefix_tabs=get_line_before(tabs_end_iter); Glib::ustring::size_type start_line=0; Glib::ustring::size_type end_line=0; @@ -741,15 +740,12 @@ std::string Source::View::get_line(const Gtk::TextIter &iter) { std::string line(get_source_buffer()->get_text(line_start_it, line_end_it)); return line; } - std::string Source::View::get_line(Glib::RefPtr mark) { return get_line(mark->get_iter()); } - std::string Source::View::get_line(int line_nr) { return get_line(get_buffer()->get_iter_at_line(line_nr)); } - std::string Source::View::get_line() { return get_line(get_buffer()->get_insert()); } @@ -759,15 +755,28 @@ std::string Source::View::get_line_before(const Gtk::TextIter &iter) { std::string line(get_source_buffer()->get_text(line_it, iter)); return line; } - std::string Source::View::get_line_before(Glib::RefPtr mark) { return get_line_before(mark->get_iter()); } - std::string Source::View::get_line_before() { return get_line_before(get_buffer()->get_insert()); } +Gtk::TextIter Source::View::get_tabs_end_iter(const Gtk::TextIter &iter) { + return get_tabs_end_iter(iter.get_line()); +} +Gtk::TextIter Source::View::get_tabs_end_iter(Glib::RefPtr mark) { + return get_tabs_end_iter(mark->get_iter()); +} +Gtk::TextIter Source::View::get_tabs_end_iter(int line_nr) { + auto sentence_iter = get_source_buffer()->get_iter_at_line(line_nr); + while((*sentence_iter==' ' || *sentence_iter=='\t') && !sentence_iter.ends_line() && sentence_iter.forward_char()) {} + return sentence_iter; +} +Gtk::TextIter Source::View::get_tabs_end_iter() { + return get_tabs_end_iter(get_buffer()->get_insert()); +} + bool Source::View::find_start_of_closed_expression(Gtk::TextIter iter, Gtk::TextIter &found_iter) { int count1=0; int count2=0; @@ -924,25 +933,20 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { if(key->keyval==GDK_KEY_Return && !get_buffer()->get_has_selection()) { auto insert_it=get_buffer()->get_insert()->get_iter(); int line_nr=insert_it.get_line(); - auto line=get_line_before(); - std::smatch sm; - if(std::regex_match(line, sm, tabs_regex)) { - if((line_nr+1)get_line_count()) { - string next_line=get_line(line_nr+1); - auto line_end_iter=get_buffer()->get_iter_at_line(line_nr+1); - if(line_end_iter.backward_char()) { - std::smatch sm2; - if(insert_it==line_end_iter && std::regex_match(next_line, sm2, tabs_regex)) { - if(sm2[1].str().size()>sm[1].str().size()) { - get_source_buffer()->insert_at_cursor("\n"+sm2[1].str()); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - } + 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(insert_it.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; } } - get_source_buffer()->insert_at_cursor("\n"+sm[1].str()); + get_source_buffer()->insert_at_cursor("\n"+line_tabs); scroll_to(get_source_buffer()->get_insert()); get_source_buffer()->end_user_action(); return true; @@ -955,22 +959,23 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { if(iter.starts_line() && iter.ends_line() && !get_buffer()->get_has_selection()) { auto prev_line_iter=iter; while(prev_line_iter.starts_line() && prev_line_iter.backward_char()) {} - auto line=get_line_before(prev_line_iter); - std::smatch sm; - if(std::regex_match(line, sm, tabs_regex)) { - auto tabs=sm[1].str(); - auto next_line_iter=iter; - while(next_line_iter.starts_line() && next_line_iter.forward_char()) {} - line=get_line(next_line_iter); - if(std::regex_match(line, sm, tabs_regex)) { - if(sm[1].str().size()=tab_size) { - get_buffer()->insert_at_cursor(tabs); - get_source_buffer()->end_user_action(); - return true; - } - } + auto prev_line_tabs_end_iter=get_tabs_end_iter(prev_line_iter); + auto previous_line_tabs=get_line_before(prev_line_tabs_end_iter); + + auto next_line_iter=iter; + while(next_line_iter.starts_line() && next_line_iter.forward_char()) {} + auto next_line_tabs_end_iter=get_tabs_end_iter(next_line_iter); + auto next_line_tabs=get_line_before(next_line_tabs_end_iter); + + std::string tabs; + if(previous_line_tabs.size()=tab_size) { + get_buffer()->insert_at_cursor(tabs); + get_source_buffer()->end_user_action(); + return true; } } @@ -997,15 +1002,16 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { std::vector ignore_line; for(int line_nr=line_start;line_nr<=line_end;line_nr++) { auto line_it = get_source_buffer()->get_iter_at_line(line_nr); - if(!get_buffer()->get_has_selection() || line_it!=selection_end) { - string line=get_line(line_nr); - std::smatch sm; - if(std::regex_match(line, sm, tabs_regex) && (sm[1].str().size()>0 || sm[2].str().size()==0)) { - if(sm[2].str().size()>0) { - indent_left_steps=std::min(indent_left_steps, (unsigned)sm[1].str().size()); + if(!get_buffer()->get_has_selection() || line_it!=selection_end) { + auto tabs_end_iter=get_tabs_end_iter(line_nr); + auto line_tabs=get_line_before(tabs_end_iter); + + if(line_tabs.size()>0 || tabs_end_iter.ends_line()) { + if(!tabs_end_iter.ends_line()) { + indent_left_steps=std::min(indent_left_steps, static_cast(line_tabs.size())); ignore_line.push_back(false); } - else if((unsigned)sm[1].str().size()(line_tabs.size())get_insert()->get_iter(); Gtk::TextIter start_of_sentence_iter; if(find_start_of_closed_expression(iter, start_of_sentence_iter)) { - auto start_line=get_line_before(start_of_sentence_iter); - std::smatch sm; - std::string tabs; - if(std::regex_match(start_line, sm, tabs_regex)) { - tabs=sm[1].str(); - } + auto start_sentence_tabs_end_iter=get_tabs_end_iter(start_of_sentence_iter); + auto tabs=get_line_before(start_sentence_tabs_end_iter); + std::smatch sm; if(iter.backward_char() && *iter=='{') { auto found_iter=iter; bool found_right_bracket=find_right_bracket_forward(iter, found_iter); bool has_bracket=false; if(found_right_bracket) { - auto line=get_line_before(found_iter); - if(std::regex_match(line, sm, tabs_regex)) { - if(tabs.size()==sm[1].str().size()) - has_bracket=true; - } + auto tabs_end_iter=get_tabs_end_iter(found_iter); + auto line_tabs=get_line_before(tabs_end_iter); + if(tabs.size()==line_tabs.size()) + has_bracket=true; } if(*get_buffer()->get_insert()->get_iter()=='}') { get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs); @@ -1830,11 +1832,11 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { auto found_iter=iter; if(find_open_expression_symbol(iter, start_of_sentence_iter, found_iter)) { auto line=get_line_before(found_iter); - if(std::regex_match(line, sm, tabs_regex)) { - tabs=sm[1].str(); - for(size_t c=0;cinsert_at_cursor("\n"+tabs+tab); @@ -1879,21 +1881,20 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { Gtk::TextIter start_of_left_bracket_sentence_iter; if(find_start_of_closed_expression(left_bracket_iter, start_of_left_bracket_sentence_iter)) { std::smatch sm; - auto start_left_bracket_sentence=get_line_before(start_of_left_bracket_sentence_iter); - if(std::regex_match(start_left_bracket_sentence, sm, tabs_regex)) { - if(tabs.size()==(sm[1].str().size()+tab_size)) { - auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); - auto start_line_plus_tab_size=start_line_iter; - for(size_t c=0;cerase(start_line_iter, start_line_plus_tab_size); - } - else { - get_source_buffer()->insert_at_cursor("\n"+tabs+tab); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } + auto tabs_end_iter=get_tabs_end_iter(start_of_left_bracket_sentence_iter); + auto tabs_start_of_sentence=get_line_before(tabs_end_iter); + if(tabs.size()==(tabs_start_of_sentence.size()+tab_size)) { + auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); + auto start_line_plus_tab_size=start_line_iter; + for(size_t c=0;cerase(start_line_iter, start_line_plus_tab_size); + } + else { + get_source_buffer()->insert_at_cursor("\n"+tabs+tab); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; } } } diff --git a/src/source.h b/src/source.h index cfa474d..214da6b 100644 --- a/src/source.h +++ b/src/source.h @@ -148,6 +148,10 @@ namespace Source { std::string get_line_before(const Gtk::TextIter &iter); std::string get_line_before(Glib::RefPtr mark); std::string get_line_before(); + Gtk::TextIter get_tabs_end_iter(const Gtk::TextIter &iter); + Gtk::TextIter get_tabs_end_iter(Glib::RefPtr mark); + Gtk::TextIter get_tabs_end_iter(int line_nr); + Gtk::TextIter get_tabs_end_iter(); bool find_start_of_closed_expression(Gtk::TextIter iter, Gtk::TextIter &found_iter); bool find_open_expression_symbol(Gtk::TextIter iter, const Gtk::TextIter &until_iter, Gtk::TextIter &found_iter); @@ -161,7 +165,6 @@ namespace Source { unsigned tab_size; char tab_char; std::string tab; - std::regex tabs_regex; bool spellcheck_all=false; std::unique_ptr spellcheck_suggestions_dialog; diff --git a/src/window.cc b/src/window.cc index 3d167db..06a1cc6 100644 --- a/src/window.cc +++ b/src/window.cc @@ -249,7 +249,7 @@ void Window::create_menu() { } } }); - menu.action_group->add(Gtk::Action::create("EditSetTab", "Set Tab Char and Size"), Gtk::AccelKey(menu.key_map["edit_set_tab"]), [this]() { + menu.action_group->add(Gtk::Action::create("EditSetTab", "Set Tab in Current Buffer"), Gtk::AccelKey(menu.key_map["edit_set_tab"]), [this]() { set_tab_entry(); }); @@ -841,7 +841,7 @@ void Window::set_tab_entry() { entry_tab_char_it->on_activate=activate_function; entry_tab_size_it->on_activate=activate_function; - entry_box.buttons.emplace_back("Set tab char and size", [this, entry_tab_char_it](){ + entry_box.buttons.emplace_back("Set tab in current buffer", [this, entry_tab_char_it](){ entry_tab_char_it->activate(); }); From 30d34eb7a38ebd1b474e3b52a0f94cbe2d46da12 Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 12 Oct 2015 20:24:46 +0200 Subject: [PATCH 27/37] Improved indentation. --- src/source.cc | 56 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/source.cc b/src/source.cc index 42d4ea9..677b298 100644 --- a/src/source.cc +++ b/src/source.cc @@ -929,16 +929,32 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { last_keyval_is_return=false; get_source_buffer()->begin_user_action(); + auto iter=get_buffer()->get_insert()->get_iter(); //Indent as in next or previous line - if(key->keyval==GDK_KEY_Return && !get_buffer()->get_has_selection()) { - auto insert_it=get_buffer()->get_insert()->get_iter(); - int line_nr=insert_it.get_line(); + if(key->keyval==GDK_KEY_Return && !get_buffer()->get_has_selection() && !iter.starts_line()) { + //First remove spaces or tabs around cursor + auto start_blank_iter=iter; + auto end_blank_iter=iter; + 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()) && + !start_blank_iter.starts_line() && start_blank_iter.backward_char()) {} + if(!start_blank_iter.starts_line()) { + 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(); + + 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(insert_it.ends_line()) { + 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()); @@ -954,7 +970,6 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { } //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) { - auto iter=get_buffer()->get_insert()->get_iter(); //Special case if insert is at beginning of empty line: if(iter.starts_line() && iter.ends_line() && !get_buffer()->get_has_selection()) { auto prev_line_iter=iter; @@ -1037,7 +1052,6 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { } //"Smart" backspace key else if(key->keyval==GDK_KEY_BackSpace && !get_buffer()->get_has_selection()) { - auto insert_it=get_buffer()->get_insert()->get_iter(); auto line=get_line_before(); bool do_smart_backspace=true; for(auto &chr: line) { @@ -1047,15 +1061,14 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { } } if(do_smart_backspace) { - auto line_start_iter=insert_it; + auto line_start_iter=iter; if(line_start_iter.backward_chars(line.size())) - get_buffer()->erase(insert_it, line_start_iter); + get_buffer()->erase(iter, line_start_iter); } } //"Smart" delete key else if(key->keyval==GDK_KEY_Delete && !get_buffer()->get_has_selection()) { - auto insert_iter=get_buffer()->get_insert()->get_iter(); - auto iter=insert_iter; + auto insert_iter=iter; bool do_smart_delete=true; do { if(*iter!=' ' && *iter!='\t' && !iter.ends_line()) { @@ -1078,7 +1091,6 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { //Next two are smart home/end keys that works with wrapped lines //Note that smart end goes FIRST to end of line to avoid hiding empty chars after expressions else if(key->keyval==GDK_KEY_End && (key->state&GDK_CONTROL_MASK)==0) { - auto iter=get_buffer()->get_insert()->get_iter(); auto end_line_iter=iter; while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {} auto end_sentence_iter=end_line_iter; @@ -1105,7 +1117,6 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { return true; } else if(key->keyval==GDK_KEY_Home && (key->state&GDK_CONTROL_MASK)==0) { - auto iter=get_buffer()->get_insert()->get_iter(); auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); auto start_sentence_iter=start_line_iter; while(!start_sentence_iter.ends_line() && @@ -1778,10 +1789,25 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { return Source::View::on_key_press_event(key); } get_source_buffer()->begin_user_action(); - + iter=get_buffer()->get_insert()->get_iter(); //Indent depending on if/else/etc and brackets - if(key->keyval==GDK_KEY_Return) { - auto iter=get_buffer()->get_insert()->get_iter(); + if(key->keyval==GDK_KEY_Return && !iter.starts_line()) { + //First remove spaces or tabs around cursor + auto start_blank_iter=iter; + auto end_blank_iter=iter; + 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()) && + !start_blank_iter.starts_line() && start_blank_iter.backward_char()) {} + if(!start_blank_iter.starts_line()) { + 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(); + Gtk::TextIter start_of_sentence_iter; if(find_start_of_closed_expression(iter, start_of_sentence_iter)) { auto start_sentence_tabs_end_iter=get_tabs_end_iter(start_of_sentence_iter); From 00e81d7ab8ce2eb8b11c4570b4a270e11ee51fad Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 12 Oct 2015 22:43:35 +0200 Subject: [PATCH 28/37] Minor fix to shift-tab at end of buffer. --- src/source.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/source.cc b/src/source.cc index 677b298..4e051b2 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1042,9 +1042,9 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line_nr); Gtk::TextIter line_plus_it=line_it; if(!get_buffer()->get_has_selection() || line_it!=selection_end) { - if(indent_left_steps==0 || line_plus_it.forward_chars(indent_left_steps)) - if(!ignore_line.at(line_nr-line_start)) - get_source_buffer()->erase(line_it, line_plus_it); + line_plus_it.forward_chars(indent_left_steps); + if(!ignore_line.at(line_nr-line_start)) + get_source_buffer()->erase(line_it, line_plus_it); } } get_source_buffer()->end_user_action(); From 6f210f70eaacadd1720d101ac430687762db946d Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 13 Oct 2015 08:55:35 +0200 Subject: [PATCH 29/37] Minor indentation fixes. --- src/source.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/source.cc b/src/source.cc index 4e051b2..b3c2075 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1857,12 +1857,13 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { iter=get_buffer()->get_insert()->get_iter(); auto found_iter=iter; if(find_open_expression_symbol(iter, start_of_sentence_iter, found_iter)) { - auto line=get_line_before(found_iter); auto tabs_end_iter=get_tabs_end_iter(found_iter); tabs=get_line_before(tabs_end_iter); auto iter=tabs_end_iter; - while(iter<=found_iter && iter.forward_char()) + while(iter<=found_iter) { tabs+=' '; + iter.forward_char(); + } } else if(std::regex_match(line, sm, no_bracket_statement_regex)) { get_source_buffer()->insert_at_cursor("\n"+tabs+tab); @@ -1945,8 +1946,8 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter(); Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line()); Gtk::TextIter line_plus_it=line_it; - if(tab_size==0 || line_plus_it.forward_chars(tab_size)) - get_source_buffer()->erase(line_it, line_plus_it); + line_plus_it.forward_chars(tab_size); + get_source_buffer()->erase(line_it, line_plus_it); } get_source_buffer()->insert_at_cursor("}"); get_source_buffer()->end_user_action(); From ff34125d63105704c08dbc7f09f8c531e3d7996e Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 13 Oct 2015 11:16:20 +0200 Subject: [PATCH 30/37] Removed non-standard cmake error messages, should now be able to google the errors if any when running cmake . --- src/CMakeLists.txt | 91 ++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 60 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a0376d5..3c95825 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,44 +12,16 @@ endif() INCLUDE(FindPkgConfig) -set(validation true) - -function(install_help APPLE UNIX WINDOWS) - message("Install package with:") - if(UNIX) - if(APPLE) - message("brew install ${APPLE}") - else() - message("sudo apt-get install ${UNIX}") - endif(APPLE) - endif(UNIX) - if(WINDOWS) - #message("choco install ${WINDOWS}") #Removed this for the time being - endif(WINDOWS) -endfunction(install_help) - -function(validate FOUND APPLE UNIX WINDOWS) - if(!${FOUND}) - set(validation false) - install_help(${APPLE} ${UNIX} ${WINDOWS}) - endif() -endfunction(validate) - find_package(LibClang) -validate(${LIBCLANG_FOUND} "clang" "libclang-dev" "llvm") #find_package(PythonLibs 2.7) -#validate(${PYTHONLIBS_FOUND} "python" "libpython-dev" "python") #find_package(Boost 1.55 COMPONENTS python thread log system filesystem REQUIRED) find_package(Boost 1.55 COMPONENTS thread log system filesystem REQUIRED) -validate(${Boost_FOUND} "boost" "libboost-all-dev" "boost") -pkg_check_modules(GTKMM gtkmm-3.0) # The name GTKMM is set here for the variables abouve -validate(${GTKMM_FOUND} "gtkmm" "libgtkmm-dev" "gtkmm") +pkg_check_modules(GTKMM gtkmm-3.0 REQUIRED) # The name GTKMM is set here for the variables abouve -pkg_check_modules(GTKSVMM gtksourceviewmm-3.0) -validate(${GTKSVMM_FOUND} "gtksvmm" "libgtksvmm-dev" "gtkmmsv") +pkg_check_modules(GTKSVMM gtksourceviewmm-3.0 REQUIRED) find_package(ASPELL REQUIRED) @@ -104,30 +76,31 @@ else() list(APPEND source_files terminal.cc) endif() -if(${validation}) - add_executable(${project_name} ${source_files}) +add_executable(${project_name} ${source_files}) # add_library(${module} SHARED # api # api_ext) - include_directories( - ${Boost_INCLUDE_DIRS} +include_directories( + ${Boost_INCLUDE_DIRS} # ${PYTHON_INCLUDE_DIRS} - ${GTKMM_INCLUDE_DIRS} - ${GTKSVMM_INCLUDE_DIRS} - ${LCL_INCLUDE_DIRS} - ${LIBCLANG_INCLUDE_DIRS} - ${ASPELL_INCLUDE_DIR} - ../libclangmm/src) - - link_directories( - ${GTKMM_LIBRARY_DIRS} - ${GTKSVMM_LIBRARY_DIRS} - ${Boost_LIBRARY_DIRS} + ${GTKMM_INCLUDE_DIRS} + ${GTKSVMM_INCLUDE_DIRS} + ${LCL_INCLUDE_DIRS} + ${LIBCLANG_INCLUDE_DIRS} + ${ASPELL_INCLUDE_DIR} + ../libclangmm/src +) + +link_directories( + ${GTKMM_LIBRARY_DIRS} + ${GTKSVMM_LIBRARY_DIRS} + ${Boost_LIBRARY_DIRS} # ${PYTHON_INCLUDE_DIRS} - ${LCL_LIBRARY_DIRS} - ${LIBCLANG_LIBRARY_DIRS}) + ${LCL_LIBRARY_DIRS} + ${LIBCLANG_LIBRARY_DIRS} +) # set_target_properties(${module} # PROPERTIES PREFIX "" @@ -136,20 +109,18 @@ if(${validation}) # target_link_libraries(${module} ${PYTHON_LIBRARIES} ${Boost_LIBRARIES}) # target_link_libraries(${module} ${Boost_LIBRARIES}) - target_link_libraries(${project_name} - ${LIBCLANG_LIBRARIES} - ${LCL_LIBRARIES} - ${GTKMM_LIBRARIES} - ${GTKSVMM_LIBRARIES} - ${Boost_LIBRARIES} - ${ASPELL_LIBRARIES} +target_link_libraries(${project_name} + ${LIBCLANG_LIBRARIES} + ${LCL_LIBRARIES} + ${GTKMM_LIBRARIES} + ${GTKSVMM_LIBRARIES} + ${Boost_LIBRARIES} + ${ASPELL_LIBRARIES} # ${PYTHON_LIBRARIES} - ) +) # install(TARGETS ${project_name} ${module} - install(TARGETS ${project_name} - RUNTIME DESTINATION bin +install(TARGETS ${project_name} + RUNTIME DESTINATION bin # LIBRARY DESTINATION ${lib_install_path} - ) -endif(${validation}) - +) From cad5a73ce2828555aa6ff7fe3ecb1d7c3be6d798 Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 13 Oct 2015 11:21:44 +0200 Subject: [PATCH 31/37] Added REQUIRED to LibClang in CMakeLists.txt. --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3c95825..9f8f1e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,7 @@ endif() INCLUDE(FindPkgConfig) -find_package(LibClang) +find_package(LibClang REQUIRED) #find_package(PythonLibs 2.7) From fc15302728ee6daea71bb49f20ece117307d1ea9 Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 13 Oct 2015 11:57:11 +0200 Subject: [PATCH 32/37] Using newer version of libclang on Ubuntu 14/Mint. --- docs/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.md b/docs/install.md index fd7045c..5877e17 100644 --- a/docs/install.md +++ b/docs/install.md @@ -22,7 +22,7 @@ sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install g++-4.9 sudo apt-get remove g++-4.8 -sudo apt-get install git cmake make g++ libclang-dev pkg-config libboost-system1.55-dev libboost-thread1.55-dev libboost-filesystem1.55-dev libboost-log1.55-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev +sudo apt-get install git cmake make g++ libclang-3.6-dev pkg-config libboost-system1.55-dev libboost-thread1.55-dev libboost-filesystem1.55-dev libboost-log1.55-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev ``` Get juCi++ source, compile and install: From 5049505da393e927bf18b97e2deca280af1470e5 Mon Sep 17 00:00:00 2001 From: Ole Christian Eidheim Date: Tue, 13 Oct 2015 12:45:58 +0200 Subject: [PATCH 33/37] Minor fix to smatch use in cmake.cc --- src/cmake.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cmake.cc b/src/cmake.cc index d112a26..76b4aa0 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -142,11 +142,11 @@ void CMake::find_variables() { const std::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$"); std::smatch sm; if(std::regex_match(line, sm, set_regex)) { - std::string data=sm[2]; + auto data=sm[2].str(); while(data.size()>0 && data.back()==' ') data.pop_back(); parse_variable_parameters(data); - variables[sm[1]]=data; + variables[sm[1].str()]=data; } } pos=end_line+1; @@ -264,7 +264,7 @@ std::vector > > CMak const std::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$"); std::smatch sm; if(std::regex_match(line, sm, function_regex)) { - std::string data=sm[1]; + auto data=sm[1].str(); while(data.size()>0 && data.back()==' ') data.pop_back(); auto parameters=get_function_parameters(data); From a1a36fda227760ee32528429418396ced597b3b8 Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 13 Oct 2015 17:09:09 +0200 Subject: [PATCH 34/37] Fixed a crash when closing a buffer while autocomplete was running. --- src/source.cc | 4 +++- src/source.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/source.cc b/src/source.cc index b3c2075..83f6d37 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1997,7 +1997,7 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_cancel_s return false; }); - autocomplete_fail.connect([this]() { + autocomplete_fail_connection=autocomplete_fail.connect([this]() { Singleton::terminal()->print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n"); restart_parse(); autocomplete_starting=false; @@ -2219,6 +2219,8 @@ std::vector Source::ClangViewAu void Source::ClangViewAutocomplete::async_delete() { parsing_in_progress->cancel("canceled, freeing resources in the background"); + autocomplete_done_connection.disconnect(); + autocomplete_fail_connection.disconnect(); parse_thread_stop=true; delete_thread=std::thread([this](){ //TODO: Is it possible to stop the clang-process in progress? diff --git a/src/source.h b/src/source.h index 214da6b..0490e3d 100644 --- a/src/source.h +++ b/src/source.h @@ -282,6 +282,7 @@ namespace Source { Glib::Dispatcher autocomplete_done; sigc::connection autocomplete_done_connection; Glib::Dispatcher autocomplete_fail; + sigc::connection autocomplete_fail_connection; bool autocomplete_starting=false; std::atomic autocomplete_cancel_starting; guint last_keyval=0; From 0fbfa2673dcc6978e733f0d31eb9622133923b45 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 14 Oct 2015 09:24:08 +0200 Subject: [PATCH 35/37] Cleanup of dependencies in README.md --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 81badf0..4bb9921 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,14 @@ towards libclang with speed and ease of use in mind. See [enhancements](https://github.com/cppit/jucipp/labels/enhancement) for planned features. ## Dependencies ## -* libboost-filesystem-dev -* libboost-log-dev -* libboost-test-dev -* libboost-thread-dev -* libboost-system-dev -* libgtkmm-3.0-dev -* libgtksourceview2.0-dev -* libgtksourceviewmm-3.0-dev -* libaspell-dev -* libclang-dev +* boost-filesystem +* boost-log +* boost-thread +* boost-system +* gtkmm-3.0 +* gtksourceviewmm-3.0 +* aspell +* libclang * [libclangmm](http://github.com/cppit/libclangmm/) ## Installation ## From b4f4af5af47315b6b2c09e2c24d33409740c4678 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 15 Oct 2015 09:44:00 +0200 Subject: [PATCH 36/37] Moved ClangViewParse to source_clang.*. --- README.md | 1 - src/CMakeLists.txt | 2 + src/notebook.h | 2 +- src/source.cc | 1241 +------------------------------------------ src/source.h | 122 +---- src/source_clang.cc | 1236 ++++++++++++++++++++++++++++++++++++++++++ src/source_clang.h | 127 +++++ src/window.h | 1 - 8 files changed, 1371 insertions(+), 1361 deletions(-) create mode 100644 src/source_clang.cc create mode 100644 src/source_clang.h diff --git a/README.md b/README.md index 4bb9921..b769f72 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ towards libclang with speed and ease of use in mind. * Smart paste, keys and indentation * Source minimap * Full UTF-8 support -* Write your own plugins in Python (disabled at the moment) See [enhancements](https://github.com/cppit/jucipp/labels/enhancement) for planned features. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9f8f1e2..964f840 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,6 +31,8 @@ set(source_files juci.h menu.cc source.h source.cc + source_clang.h + source_clang.cc selectiondialog.h selectiondialog.cc config.h diff --git a/src/notebook.h b/src/notebook.h index b8f6b69..ab81c92 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -4,7 +4,7 @@ #include #include "gtkmm.h" #include "source.h" -#include +#include "source_clang.h" #include #include #include diff --git a/src/source.cc b/src/source.cc index 83f6d37..07de736 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1,7 +1,6 @@ #include "source.h" #include "sourcefile.h" #include -#include #include "logging.h" #include #include "singletons.h" @@ -538,7 +537,6 @@ void Source::View::paste() { } auto line=get_line_before(); - std::smatch sm; std::string prefix_tabs; auto tabs_end_iter=get_tabs_end_iter(); if(!get_buffer()->get_has_selection() && tabs_end_iter.ends_line()) { @@ -1167,25 +1165,15 @@ bool Source::View::on_button_press_event(GdkEventButton *event) { } std::pair Source::View::find_tab_char_and_size() { - const std::regex indent_regex("^([ \t]+)(.*)$"); auto size=get_buffer()->get_line_count(); std::unordered_map tab_chars; std::unordered_map tab_sizes; unsigned last_tab_size=0; for(int c=0;c(str.size()-last_tab_size)); if(tab_diff>0) { @@ -1357,1224 +1345,3 @@ void Source::GenericView::parse_language_file(Glib::RefPtr &co } } } - -//////////////////////// -//// ClangViewParse //// -//////////////////////// -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) { - DEBUG("start"); - - auto tag_table=get_buffer()->get_tag_table(); - for (auto &item : Singleton::Config::source()->clang_types) { - if(!tag_table->lookup(item.second)) { - get_buffer()->create_tag(item.second); - } - } - configure(); - - 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.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_thread_go=true; - }); - parse_done.connect([this](){ - if(parse_thread_mapped) { - if(parsing_mutex.try_lock()) { - update_syntax(); - update_diagnostics(); - source_readable=true; - set_status(""); - parsing_mutex.unlock(); - } - parsing_in_progress->done("done"); - } - else { - parse_thread_go=true; - } - }); - parse_fail.connect([this](){ - Singleton::terminal()->print("Error: failed to reparse "+this->file_path.string()+".\n"); - set_status(""); - set_info(""); - parsing_in_progress->cancel("failed"); - }); - init_parse(); - - get_buffer()->signal_changed().connect([this]() { - start_reparse(); - type_tooltips.hide(); - diagnostic_tooltips.hide(); - }); - - DEBUG("end"); -} - -void Source::ClangViewParse::configure() { - Source::View::configure(); - - auto scheme = get_source_buffer()->get_style_scheme(); - auto tag_table=get_buffer()->get_tag_table(); - for (auto &item : Singleton::Config::source()->clang_types) { - auto tag = get_buffer()->get_tag_table()->lookup(item.second); - if(tag) { - auto style = scheme->get_style(item.second); - if (style) { - if (style->property_foreground_set()) - tag->property_foreground() = style->property_foreground(); - if (style->property_background_set()) - tag->property_background() = style->property_background(); - if (style->property_strikethrough_set()) - tag->property_strikethrough() = style->property_strikethrough(); - // // if (style->property_bold_set()) tag->property_weight() = style->property_bold(); - // // if (style->property_italic_set()) tag->property_italic() = style->property_italic(); - // // if (style->property_line_background_set()) tag->property_line_background() = style->property_line_background(); - // // if (style->property_underline_set()) tag->property_underline() = style->property_underline(); - } else - INFO("Style " + item.second + " not found in " + scheme->get_name()); - } - } - - bracket_regex=std::regex("^([ \\t]*).*\\{ *$"); - no_bracket_statement_regex=std::regex("^([ \\t]*)(if|for|else if|catch|while) *\\(.*[^;}] *$"); - no_bracket_no_para_statement_regex=std::regex("^([ \\t]*)(else|try|do) *$"); -} - -Source::ClangViewParse::~ClangViewParse() { - delayed_reparse_connection.disconnect(); -} - -void Source::ClangViewParse::init_parse() { - type_tooltips.hide(); - diagnostic_tooltips.hide(); - source_readable=false; - parse_thread_go=true; - parse_thread_mapped=false; - parse_thread_stop=false; - - auto buffer_map=get_buffer_map(); - //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) { - auto start_pos=pos; - pos=str.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, ' '); - } - 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); - 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) - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - if(parse_thread_stop) - 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()) { - int 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; - } - else - parse_done(); - } - } - }); -} - -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::start_reparse() { - parse_thread_mapped=false; - source_readable=false; - delayed_reparse_connection.disconnect(); - delayed_reparse_connection=Glib::signal_timeout().connect([this]() { - source_readable=false; - parse_thread_go=true; - set_status("parsing..."); - return false; - }, 1000); -} - -std::vector Source::ClangViewParse::get_compilation_commands() { - clang::CompilationDatabase db(project_path.string()); - clang::CompileCommands commands(file_path.string(), db); - std::vector cmds = commands.get_commands(); - std::vector arguments; - for (auto &i : cmds) { - std::vector lol = i.get_command_as_args(); - for (size_t a = 1; a < lol.size()-4; a++) { - arguments.emplace_back(lol[a]); - } - } - auto clang_version_string=clang::to_string(clang_getClangVersion()); - const std::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$"); - std::smatch sm; - if(std::regex_match(clang_version_string, sm, clang_version_regex)) { - auto clang_version=sm[1].str(); - arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include"); - arguments.emplace_back("-I/usr/local/Cellar/llvm/"+clang_version+"/lib/clang/"+clang_version+"/include"); - arguments.emplace_back("-IC:/msys32/mingw32/lib/clang/"+clang_version+"/include"); - arguments.emplace_back("-IC:/msys32/mingw64/lib/clang/"+clang_version+"/include"); - arguments.emplace_back("-IC:/msys64/mingw32/lib/clang/"+clang_version+"/include"); - arguments.emplace_back("-IC:/msys64/mingw64/lib/clang/"+clang_version+"/include"); - } - arguments.emplace_back("-fretain-comments-from-system-headers"); - if(file_path.extension()==".h") //TODO: temporary fix for .h-files (parse as c++) - arguments.emplace_back("-xc++"); - - return arguments; -} - -void Source::ClangViewParse::update_syntax() { - std::vector ranges; - for (auto &token : *clang_tokens) { - //if(token.get_kind()==0) // PunctuationToken - //ranges.emplace_back(token.offsets, (int) token.get_cursor().get_kind()); - if(token.get_kind()==1) // KeywordToken - ranges.emplace_back(token.offsets, 702); - else if(token.get_kind()==2) {// IdentifierToken - auto kind=(int)token.get_cursor().get_kind(); - if(kind==101 || kind==102) - kind=(int)token.get_cursor().get_referenced().get_kind(); - if(kind!=500) - ranges.emplace_back(token.offsets, kind); - } - else if(token.get_kind()==3) { // LiteralToken - ranges.emplace_back(token.offsets, 109); - } - else if(token.get_kind()==4) // CommentToken - ranges.emplace_back(token.offsets, 705); - } - if (ranges.empty() || ranges.size() == 0) { - return; - } - auto buffer = get_source_buffer(); - for(auto &tag: last_syntax_tags) - buffer->remove_tag_by_name(tag, buffer->begin(), buffer->end()); - last_syntax_tags.clear(); - for (auto &range : ranges) { - auto type_it=Singleton::Config::source()->clang_types.find(std::to_string(range.kind)); - if(type_it!=Singleton::Config::source()->clang_types.end()) { - last_syntax_tags.emplace(type_it->second); - Gtk::TextIter begin_iter = buffer->get_iter_at_line_index(range.offsets.first.line-1, range.offsets.first.index-1); - Gtk::TextIter end_iter = buffer->get_iter_at_line_index(range.offsets.second.line-1, range.offsets.second.index-1); - buffer->apply_tag_by_name(type_it->second, begin_iter, end_iter); - } - } -} - -void Source::ClangViewParse::update_diagnostics() { - diagnostic_offsets.clear(); - diagnostic_tooltips.clear(); - fix_its.clear(); - get_buffer()->remove_tag_by_name("def:warning_underline", get_buffer()->begin(), get_buffer()->end()); - get_buffer()->remove_tag_by_name("def:error_underline", get_buffer()->begin(), get_buffer()->end()); - auto diagnostics=clang_tu->get_diagnostics(); - size_t num_warnings=0; - size_t num_errors=0; - size_t num_fix_its=0; - for(auto &diagnostic: diagnostics) { - if(diagnostic.path==file_path.string()) { - auto start_line=get_line(diagnostic.offsets.first.line-1); //index is sometimes off the line - auto start_line_index=diagnostic.offsets.first.index-1; - if(start_line_index>=start_line.size()) { - if(start_line.size()==0) - start_line_index=0; - else - start_line_index=start_line.size()-1; - } - auto end_line=get_line(diagnostic.offsets.second.line-1); //index is sometimes off the line - auto end_line_index=diagnostic.offsets.second.index-1; - if(end_line_index>end_line.size()) { - if(end_line.size()==0) - end_line_index=0; - else - end_line_index=end_line.size(); - } - auto start=get_buffer()->get_iter_at_line_index(diagnostic.offsets.first.line-1, start_line_index); - diagnostic_offsets.emplace(start.get_offset()); - auto end=get_buffer()->get_iter_at_line_index(diagnostic.offsets.second.line-1, end_line_index); - std::string diagnostic_tag_name; - if(diagnostic.severity<=CXDiagnostic_Warning) { - diagnostic_tag_name="def:warning"; - num_warnings++; - } - else { - diagnostic_tag_name="def:error"; - num_errors++; - } - - auto spelling=diagnostic.spelling; - auto severity_spelling=diagnostic.severity_spelling; - - std::string fix_its_string; - unsigned fix_its_count=0; - for(auto &fix_it: diagnostic.fix_its) { - //Convert line index to line offset for correct output: - auto clang_offsets=fix_it.offsets; - std::pair offsets; - offsets.first.line=clang_offsets.first.line; - offsets.second.line=clang_offsets.second.line; - auto iter=get_buffer()->get_iter_at_line_index(clang_offsets.first.line-1, clang_offsets.first.index-1); - offsets.first.offset=iter.get_line_offset()+1; - iter=get_buffer()->get_iter_at_line_index(clang_offsets.second.line-1, clang_offsets.second.index-1); - offsets.second.offset=iter.get_line_offset()+1; - - fix_its.emplace_back(fix_it.source, offsets); - - if(fix_its_string.size()>0) - fix_its_string+='\n'; - fix_its_string+=fix_its.back().string(); - fix_its_count++; - num_fix_its++; - } - - if(fix_its_count==1) - fix_its_string.insert(0, "Fix-it:\n"); - else if(fix_its_count>1) - fix_its_string.insert(0, "Fix-its:\n"); - - auto create_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name, fix_its_string]() { - auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); - tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), severity_spelling, diagnostic_tag_name); - tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n"+spelling, "def:note"); - if(fix_its_string.size()>0) { - tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n\n"+fix_its_string, "def:note"); - } - return tooltip_buffer; - }; - diagnostic_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); - - get_buffer()->apply_tag_by_name(diagnostic_tag_name+"_underline", start, end); - auto iter=get_buffer()->get_insert()->get_iter(); - if(iter.ends_line()) { - auto next_iter=iter; - if(next_iter.forward_char()) - get_buffer()->remove_tag_by_name(diagnostic_tag_name+"_underline", iter, next_iter); - } - } - } - std::string diagnostic_info; - if(num_warnings>0) { - diagnostic_info+=std::to_string(num_warnings)+" warning"; - if(num_warnings>1) - diagnostic_info+='s'; - } - if(num_errors>0) { - if(num_warnings>0) - diagnostic_info+=", "; - diagnostic_info+=std::to_string(num_errors)+" error"; - if(num_errors>1) - diagnostic_info+='s'; - } - if(num_fix_its>0) { - if(num_warnings>0 || num_errors>0) - diagnostic_info+=", "; - diagnostic_info+=std::to_string(num_fix_its)+" fix it"; - if(num_fix_its>1) - diagnostic_info+='s'; - } - set_info(" "+diagnostic_info); -} - -void Source::ClangViewParse::show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) { - diagnostic_tooltips.show(rectangle); -} - -void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) { - if(source_readable) { - Gtk::TextIter iter; - int location_x, location_y; - window_to_buffer_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, rectangle.get_x(), rectangle.get_y(), location_x, location_y); - location_x+=(rectangle.get_width()-1)/2; - get_iter_at_location(iter, location_x, location_y); - Gdk::Rectangle iter_rectangle; - get_iter_location(iter, iter_rectangle); - if(iter_rectangle.get_x()>location_x) { - if(!iter.starts_line()) { - if(!iter.backward_char()) - return; - } - } - bool found_token=false; - if(!((*iter>='a' && *iter<='z') || (*iter>='A' && *iter<='Z') || (*iter>='0' && *iter<='9') || *iter=='_')) { - if(!iter.backward_char()) - return; - } - - while((*iter>='a' && *iter<='z') || (*iter>='A' && *iter<='Z') || (*iter>='0' && *iter<='9') || *iter=='_') { - if(!found_token) - found_token=true; - if(!iter.backward_char()) - return; - } - if(found_token && iter.forward_char()) { - auto tokens=clang_tu->get_tokens(iter.get_line()+1, iter.get_line_index()+1, - iter.get_line()+1, iter.get_line_index()+1); - - type_tooltips.clear(); - for(auto &token: *tokens) { - auto cursor=token.get_cursor(); - if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) { - if(static_cast(token.get_cursor().get_kind())==103) //These cursors are buggy - continue; - auto start=get_buffer()->get_iter_at_line_index(token.offsets.first.line-1, token.offsets.first.index-1); - auto end=get_buffer()->get_iter_at_line_index(token.offsets.second.line-1, token.offsets.second.index-1); - auto create_tooltip_buffer=[this, &token]() { - auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); - tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), "Type: "+token.get_cursor().get_type(), "def:note"); - auto brief_comment=token.get_cursor().get_brief_comments(); - if(brief_comment!="") - tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), "\n\n"+brief_comment, "def:note"); - return tooltip_buffer; - }; - - type_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); - } - } - - type_tooltips.show(); - } - } - - //type_tooltips.show(rectangle); -} - -//Clang indentation. -bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { - if(spellcheck_suggestions_dialog_shown) { - if(spellcheck_suggestions_dialog->on_key_press(key)) - return true; - } - - auto iter=get_buffer()->get_insert()->get_iter(); - if(iter.backward_char() && (get_source_buffer()->iter_has_context_class(iter, "comment") || get_source_buffer()->iter_has_context_class(iter, "string"))) - return Source::View::on_key_press_event(key); - - if(get_buffer()->get_has_selection()) { - return Source::View::on_key_press_event(key); - } - get_source_buffer()->begin_user_action(); - iter=get_buffer()->get_insert()->get_iter(); - //Indent depending on if/else/etc and brackets - if(key->keyval==GDK_KEY_Return && !iter.starts_line()) { - //First remove spaces or tabs around cursor - auto start_blank_iter=iter; - auto end_blank_iter=iter; - 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()) && - !start_blank_iter.starts_line() && start_blank_iter.backward_char()) {} - if(!start_blank_iter.starts_line()) { - 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(); - - Gtk::TextIter start_of_sentence_iter; - if(find_start_of_closed_expression(iter, start_of_sentence_iter)) { - auto start_sentence_tabs_end_iter=get_tabs_end_iter(start_of_sentence_iter); - auto tabs=get_line_before(start_sentence_tabs_end_iter); - - std::smatch sm; - if(iter.backward_char() && *iter=='{') { - auto found_iter=iter; - bool found_right_bracket=find_right_bracket_forward(iter, found_iter); - - bool has_bracket=false; - if(found_right_bracket) { - auto tabs_end_iter=get_tabs_end_iter(found_iter); - auto line_tabs=get_line_before(tabs_end_iter); - if(tabs.size()==line_tabs.size()) - has_bracket=true; - } - if(*get_buffer()->get_insert()->get_iter()=='}') { - get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs); - auto insert_it = get_source_buffer()->get_insert()->get_iter(); - if(insert_it.backward_chars(tabs.size()+1)) { - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->place_cursor(insert_it); - } - get_source_buffer()->end_user_action(); - return true; - } - else if(!has_bracket) { - //Insert new lines with bracket end - get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs+"}"); - auto insert_it = get_source_buffer()->get_insert()->get_iter(); - if(insert_it.backward_chars(tabs.size()+2)) { - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->place_cursor(insert_it); - } - get_source_buffer()->end_user_action(); - return true; - } - else { - get_source_buffer()->insert_at_cursor("\n"+tabs+tab); - scroll_to(get_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - } - auto line=get_line_before(); - iter=get_buffer()->get_insert()->get_iter(); - auto found_iter=iter; - if(find_open_expression_symbol(iter, start_of_sentence_iter, found_iter)) { - auto tabs_end_iter=get_tabs_end_iter(found_iter); - tabs=get_line_before(tabs_end_iter); - auto iter=tabs_end_iter; - while(iter<=found_iter) { - tabs+=' '; - iter.forward_char(); - } - } - else if(std::regex_match(line, sm, no_bracket_statement_regex)) { - get_source_buffer()->insert_at_cursor("\n"+tabs+tab); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - else if(std::regex_match(line, sm, no_bracket_no_para_statement_regex)) { - get_source_buffer()->insert_at_cursor("\n"+tabs+tab); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - //Indenting after for instance if(...)\n...;\n - else if(iter.backward_char() && *iter==';') { - std::smatch sm2; - size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line(); - if(line_nr>0 && tabs.size()>=tab_size) { - string previous_line=get_line(line_nr-1); - if(!std::regex_match(previous_line, sm2, bracket_regex)) { - if(std::regex_match(previous_line, sm2, no_bracket_statement_regex)) { - get_source_buffer()->insert_at_cursor("\n"+sm2[1].str()); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - else if(std::regex_match(previous_line, sm2, no_bracket_no_para_statement_regex)) { - get_source_buffer()->insert_at_cursor("\n"+sm2[1].str()); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - } - } - } - //Indenting after ':' - else if(*iter==':') { - Gtk::TextIter left_bracket_iter; - if(find_left_bracket_backward(iter, left_bracket_iter)) { - if(!left_bracket_iter.ends_line()) - left_bracket_iter.forward_char(); - Gtk::TextIter start_of_left_bracket_sentence_iter; - if(find_start_of_closed_expression(left_bracket_iter, start_of_left_bracket_sentence_iter)) { - std::smatch sm; - auto tabs_end_iter=get_tabs_end_iter(start_of_left_bracket_sentence_iter); - auto tabs_start_of_sentence=get_line_before(tabs_end_iter); - if(tabs.size()==(tabs_start_of_sentence.size()+tab_size)) { - auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); - auto start_line_plus_tab_size=start_line_iter; - for(size_t c=0;cerase(start_line_iter, start_line_plus_tab_size); - } - else { - get_source_buffer()->insert_at_cursor("\n"+tabs+tab); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - } - } - } - get_source_buffer()->insert_at_cursor("\n"+tabs); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - } - //Indent left when writing } on a new line - else if(key->keyval==GDK_KEY_braceright) { - string line=get_line_before(); - if(line.size()>=tab_size) { - for(auto c: line) { - if(c!=tab_char) { - get_source_buffer()->insert_at_cursor("}"); - get_source_buffer()->end_user_action(); - return true; - } - } - Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter(); - Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line()); - Gtk::TextIter line_plus_it=line_it; - line_plus_it.forward_chars(tab_size); - get_source_buffer()->erase(line_it, line_plus_it); - } - get_source_buffer()->insert_at_cursor("}"); - get_source_buffer()->end_user_action(); - return true; - } - - get_source_buffer()->end_user_action(); - return Source::View::on_key_press_event(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) { - get_buffer()->signal_changed().connect([this](){ - if(completion_dialog_shown) - delayed_reparse_connection.disconnect(); - start_autocomplete(); - }); - 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(); - } - }); - signal_scroll_event().connect([this](GdkEventScroll* event){ - if(completion_dialog_shown) - completion_dialog->hide(); - return false; - }, false); - signal_key_release_event().connect([this](GdkEventKey* key){ - if(completion_dialog_shown) { - if(completion_dialog->on_key_release(key)) - return true; - } - - return false; - }, false); - - signal_focus_out_event().connect([this](GdkEventFocus* event) { - autocomplete_cancel_starting=true; - if(completion_dialog_shown) - completion_dialog->hide(); - - return false; - }); - - autocomplete_fail_connection=autocomplete_fail.connect([this]() { - Singleton::terminal()->print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n"); - restart_parse(); - autocomplete_starting=false; - autocomplete_cancel_starting=false; - }); - - do_delete_object.connect([this](){ - if(delete_thread.joinable()) - delete_thread.join(); - delete this; - }); - do_restart_parse.connect([this](){ - init_parse(); - restart_parse_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)) - 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 std::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$"); - const std::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$"); - std::smatch sm; - if(std::regex_match(line, sm, in_specified_namespace)) { - 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(std::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 > 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; - source_readable=false; - start_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 - start_reparse(); - } - else { - set_status(""); - start_reparse(); - start_autocomplete(); - } - }); - - std::shared_ptr > buffer_map=std::make_shared >(); - auto ustr=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, " "); - 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_done(); - else - autocomplete_fail(); - parsing_mutex.unlock(); - }); - } -} - -std::vector Source::ClangViewAutocomplete::get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map) { - std::vector suggestions; - auto results=clang_tu->get_code_completions(buffer_map, line_number, column); - if(results.cx_results==NULL) { - parse_thread_stop=true; - return suggestions; - } - - if(!autocomplete_cancel_starting) { - prefix_mutex.lock(); - auto prefix_copy=prefix; - prefix_mutex.unlock(); - for (unsigned i = 0; i < results.size(); i++) { - auto result=results.get(i); - if(result.available()) { - auto chunks=result.get_chunks(); - bool match=false; - for(auto &chunk: chunks) { - if(chunk.kind!=clang::CompletionChunk_ResultType && chunk.kind!=clang::CompletionChunk_Informative) { - if(chunk.chunk.size()>=prefix_copy.size() && chunk.chunk.compare(0, prefix_copy.size(), prefix_copy)==0) - match=true; - break; - } - } - if(match) { - suggestions.emplace_back(std::move(chunks)); - suggestions.back().brief_comments=result.get_brief_comments(); - } - } - } - } - return suggestions; -} - -void Source::ClangViewAutocomplete::async_delete() { - parsing_in_progress->cancel("canceled, freeing resources in the background"); - autocomplete_done_connection.disconnect(); - autocomplete_fail_connection.disconnect(); - parse_thread_stop=true; - delete_thread=std::thread([this](){ - //TODO: Is it possible to stop the clang-process in progress? - if(restart_parse_thread.joinable()) - restart_parse_thread.join(); - if(parse_thread.joinable()) - parse_thread.join(); - if(autocomplete_thread.joinable()) - autocomplete_thread.join(); - do_delete_object(); - }); -} - -bool Source::ClangViewAutocomplete::restart_parse() { - if(!restart_parse_running && !parse_error) { - reparse_needed=false; - restart_parse_running=true; - parse_thread_stop=true; - if(restart_parse_thread.joinable()) - restart_parse_thread.join(); - restart_parse_thread=std::thread([this](){ - if(parse_thread.joinable()) - parse_thread.join(); - if(autocomplete_thread.joinable()) - autocomplete_thread.join(); - do_restart_parse(); - }); - return true; - } - return false; -} - -//////////////////////////// -//// ClangViewRefactor ///// -//////////////////////////// - -Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): -Source::ClangViewAutocomplete(file_path, project_path, language) { - similar_tokens_tag=get_buffer()->create_tag(); - similar_tokens_tag->property_weight()=1000; //TODO: replace with Pango::WEIGHT_ULTRAHEAVY in 2016 or so (when Ubuntu 14 is history) - - get_buffer()->signal_changed().connect([this]() { - if(!renaming && last_tagged_token) { - for(auto &mark: similar_token_marks) { - get_buffer()->remove_tag(similar_tokens_tag, mark.first->get_iter(), mark.second->get_iter()); - get_buffer()->delete_mark(mark.first); - get_buffer()->delete_mark(mark.second); - } - similar_token_marks.clear(); - last_tagged_token=Token(); - } - }); - - get_token=[this]() -> Token { - if(source_readable) { - auto iter=get_buffer()->get_insert()->get_iter(); - auto line=(unsigned)iter.get_line(); - auto index=(unsigned)iter.get_line_index(); - for(auto &token: *clang_tokens) { - auto cursor=token.get_cursor(); - if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) { - if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) { - if(static_cast(token.get_cursor().get_kind())==103) //These cursors are buggy - continue; - auto referenced=cursor.get_referenced(); - if(referenced) - return Token(static_cast(referenced.get_kind()), token.get_spelling(), referenced.get_usr()); - } - } - } - } - return Token(); - }; - - rename_similar_tokens=[this](const Token &token, const std::string &text) { - size_t number=0; - if(source_readable) { - auto offsets=clang_tokens->get_similar_token_offsets(static_cast(token.type), token.spelling, token.usr); - std::vector, Glib::RefPtr > > marks; - for(auto &offset: offsets) { - marks.emplace_back(get_buffer()->create_mark(get_buffer()->get_iter_at_line_index(offset.first.line-1, offset.first.index-1)), get_buffer()->create_mark(get_buffer()->get_iter_at_line_index(offset.second.line-1, offset.second.index-1))); - number++; - } - get_source_buffer()->begin_user_action(); - for(auto &mark: marks) { - renaming=true; - get_buffer()->erase(mark.first->get_iter(), mark.second->get_iter()); - get_buffer()->insert(mark.first->get_iter(), text); - get_buffer()->delete_mark(mark.first); - get_buffer()->delete_mark(mark.second); - } - get_source_buffer()->end_user_action(); - renaming=false; - } - return number; - }; - - get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr& mark){ - if(mark->get_name()=="insert") { - delayed_tag_similar_tokens_connection.disconnect(); - delayed_tag_similar_tokens_connection=Glib::signal_timeout().connect([this]() { - auto token=get_token(); - tag_similar_tokens(token); - return false; - }, 100); - } - }); - - get_declaration_location=[this](){ - std::pair location; - if(source_readable) { - auto iter=get_buffer()->get_insert()->get_iter(); - auto line=(unsigned)iter.get_line(); - auto index=(unsigned)iter.get_line_index(); - for(auto &token: *clang_tokens) { - auto cursor=token.get_cursor(); - if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) { - if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) { - auto referenced=cursor.get_referenced(); - if(referenced) { - location.first=referenced.get_source_location().get_path(); - location.second=referenced.get_source_location().get_offset(); - break; - } - } - } - } - } - return location; - }; - - goto_method=[this](){ - if(source_readable) { - auto iter=get_buffer()->get_insert()->get_iter(); - Gdk::Rectangle visible_rect; - get_visible_rect(visible_rect); - Gdk::Rectangle iter_rect; - get_iter_location(iter, iter_rect); - iter_rect.set_width(1); - if(!visible_rect.intersects(iter_rect)) { - get_iter_at_location(iter, 0, visible_rect.get_y()+visible_rect.get_height()/3); - } - selection_dialog=std::unique_ptr(new SelectionDialog(*this, get_buffer()->create_mark(iter))); - auto rows=std::make_shared >(); - auto methods=clang_tokens->get_cxx_methods(); - if(methods.size()==0) - return; - for(auto &method: methods) { - (*rows)[method.first]=method.second; - selection_dialog->add_row(method.first); - } - selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { - auto offset=rows->at(selected); - get_buffer()->place_cursor(get_buffer()->get_iter_at_line_index(offset.line-1, offset.index-1)); - scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); - delayed_tooltips_connection.disconnect(); - }; - selection_dialog->show(); - } - }; - - get_token_data=[this]() { - const auto find_non_word_char=[](const std::string &str, size_t start_pos) { - for(size_t c=start_pos;c='a' && str[c]<='z') || - (str[c]>='A' && str[c]<='Z') || - (str[c]>='0' && str[c]<='9') || - str[c]=='_')) - return c; - } - return std::string::npos; - }; - - std::vector data; - if(source_readable) { - auto iter=get_buffer()->get_insert()->get_iter(); - auto line=(unsigned)iter.get_line(); - auto index=(unsigned)iter.get_line_index(); - for(auto &token: *clang_tokens) { - auto cursor=token.get_cursor(); - if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) { - if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) { - auto referenced=cursor.get_referenced(); - if(referenced) { - auto usr=referenced.get_usr(); - boost::filesystem::path referenced_path=referenced.get_source_location().get_path(); - - //Singleton::terminal()->print(usr+'\n', true); //TODO: remove - - //Return empty if referenced is within project - if(referenced_path.generic_string().substr(0, this->project_path.generic_string().size()+1)==this->project_path.generic_string()+'/') - return data; - - data.emplace_back("clang"); - - //namespace - size_t pos1, pos2=0; - while((pos1=usr.find('@', pos2))!=std::string::npos && pos1+1get_insert()->get_iter().get_offset(); - for(auto offset: diagnostic_offsets) { - if(offset>insert_offset) { - get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset)); - scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); - return; - } - } - if(diagnostic_offsets.size()>0) { - auto iter=get_buffer()->get_iter_at_offset(*diagnostic_offsets.begin()); - get_buffer()->place_cursor(iter); - scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); - } - } - }; - - apply_fix_its=[this]() { - std::vector, Glib::RefPtr > > fix_it_marks; - if(source_readable) { - for(auto &fix_it: fix_its) { - auto start_iter=get_buffer()->get_iter_at_line_offset(fix_it.offsets.first.line-1, fix_it.offsets.first.offset-1); - auto end_iter=get_buffer()->get_iter_at_line_offset(fix_it.offsets.second.line-1, fix_it.offsets.second.offset-1); - fix_it_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter)); - } - size_t c=0; - get_source_buffer()->begin_user_action(); - for(auto &fix_it: fix_its) { - if(fix_it.type==FixIt::Type::INSERT) { - get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source); - } - if(fix_it.type==FixIt::Type::REPLACE) { - get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter()); - get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source); - } - if(fix_it.type==FixIt::Type::ERASE) { - get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter()); - } - c++; - } - for(auto &mark_pair: fix_it_marks) { - get_buffer()->delete_mark(mark_pair.first); - get_buffer()->delete_mark(mark_pair.second); - } - get_source_buffer()->end_user_action(); - } - }; -} - -void Source::ClangViewRefactor::tag_similar_tokens(const Token &token) { - if(source_readable) { - if(token && last_tagged_token!=token) { - for(auto &mark: similar_token_marks) { - get_buffer()->remove_tag(similar_tokens_tag, mark.first->get_iter(), mark.second->get_iter()); - get_buffer()->delete_mark(mark.first); - get_buffer()->delete_mark(mark.second); - } - similar_token_marks.clear(); - auto offsets=clang_tokens->get_similar_token_offsets(static_cast(token.type), token.spelling, token.usr); - for(auto &offset: offsets) { - auto start_iter=get_buffer()->get_iter_at_line_index(offset.first.line-1, offset.first.index-1); - auto end_iter=get_buffer()->get_iter_at_line_index(offset.second.line-1, offset.second.index-1); - get_buffer()->apply_tag(similar_tokens_tag, start_iter, end_iter); - similar_token_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter)); - } - last_tagged_token=token; - } - } - if(!token && last_tagged_token) { - for(auto &mark: similar_token_marks) { - get_buffer()->remove_tag(similar_tokens_tag, mark.first->get_iter(), mark.second->get_iter()); - get_buffer()->delete_mark(mark.first); - get_buffer()->delete_mark(mark.second); - } - similar_token_marks.clear(); - last_tagged_token=Token(); - } -} - -Source::ClangViewRefactor::~ClangViewRefactor() { - delayed_tag_similar_tokens_connection.disconnect(); -} - -Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): ClangViewRefactor(file_path, project_path, language) { - if(language) { - get_source_buffer()->set_highlight_syntax(true); - get_source_buffer()->set_language(language); - } -} diff --git a/src/source.h b/src/source.h index 0490e3d..340317d 100644 --- a/src/source.h +++ b/src/source.h @@ -1,20 +1,14 @@ #ifndef JUCI_SOURCE_H_ #define JUCI_SOURCE_H_ -#include #include #include #include "gtkmm.h" #include "clangmm.h" -#include -#include #include -#include #include "gtksourceviewmm.h" #include "terminal.h" #include "tooltips.h" #include "selectiondialog.h" -#include -#include #include #include @@ -200,119 +194,5 @@ namespace Source { void parse_language_file(Glib::RefPtr &completion_buffer, bool &has_context_class, const boost::property_tree::ptree &pt); }; - - class ClangViewParse : public View { - public: - class TokenRange { - public: - TokenRange(std::pair offsets, int kind): - offsets(offsets), kind(kind) {} - std::pair offsets; - int kind; - }; - - ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); - ~ClangViewParse(); - void configure(); - - void start_reparse(); - bool reparse_needed=false; - protected: - void init_parse(); - bool on_key_press_event(GdkEventKey* key); - 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); - void show_type_tooltips(const Gdk::Rectangle &rectangle); - - std::regex bracket_regex; - std::regex no_bracket_statement_regex; - std::regex no_bracket_no_para_statement_regex; - - std::set diagnostic_offsets; - std::vector fix_its; - private: - std::map get_buffer_map() const; - 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; - std::map parse_thread_buffer_map; - std::mutex parse_thread_buffer_map_mutex; - std::atomic parse_thread_go; - std::atomic parse_thread_mapped; - }; - - class ClangViewAutocomplete : public ClangViewParse { - public: - class AutoCompleteData { - public: - explicit AutoCompleteData(const std::vector &chunks) : - chunks(chunks) { } - std::vector chunks; - std::string brief_comments; - }; - - ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); - void async_delete(); - bool restart_parse(); - protected: - bool on_key_press_event(GdkEventKey* key); - std::thread autocomplete_thread; - private: - void start_autocomplete(); - 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); - Glib::Dispatcher autocomplete_done; - sigc::connection autocomplete_done_connection; - Glib::Dispatcher autocomplete_fail; - sigc::connection autocomplete_fail_connection; - bool autocomplete_starting=false; - std::atomic autocomplete_cancel_starting; - guint last_keyval=0; - std::string prefix; - std::mutex prefix_mutex; - - Glib::Dispatcher do_delete_object; - Glib::Dispatcher do_restart_parse; - std::thread delete_thread; - std::thread restart_parse_thread; - bool restart_parse_running=false; - }; - - class ClangViewRefactor : public ClangViewAutocomplete { - public: - ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); - ~ClangViewRefactor(); - private: - std::list, Glib::RefPtr > > similar_token_marks; - void tag_similar_tokens(const Token &token); - Glib::RefPtr similar_tokens_tag; - Token last_tagged_token; - sigc::connection delayed_tag_similar_tokens_connection; - std::unique_ptr selection_dialog; - bool renaming=false; - }; - - class ClangView : public ClangViewRefactor { - public: - ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); - }; -}; // class Source +} #endif // JUCI_SOURCE_H_ diff --git a/src/source_clang.cc b/src/source_clang.cc new file mode 100644 index 0000000..1b34510 --- /dev/null +++ b/src/source_clang.cc @@ -0,0 +1,1236 @@ +#include "source_clang.h" +#include "singletons.h" + +#include //TODO: remove +using namespace std; //TODO: remove + +namespace sigc { +#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE + template + struct functor_trait { + typedef decltype (::sigc::mem_fun(std::declval(), + &Functor::operator())) _intermediate; + typedef typename _intermediate::result_type result_type; + typedef Functor functor_type; + }; +#else + SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE +#endif +} + +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) { + DEBUG("start"); + + auto tag_table=get_buffer()->get_tag_table(); + for (auto &item : Singleton::Config::source()->clang_types) { + if(!tag_table->lookup(item.second)) { + get_buffer()->create_tag(item.second); + } + } + configure(); + + 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.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_thread_go=true; + }); + parse_done.connect([this](){ + if(parse_thread_mapped) { + if(parsing_mutex.try_lock()) { + update_syntax(); + update_diagnostics(); + source_readable=true; + set_status(""); + parsing_mutex.unlock(); + } + parsing_in_progress->done("done"); + } + else { + parse_thread_go=true; + } + }); + parse_fail.connect([this](){ + Singleton::terminal()->print("Error: failed to reparse "+this->file_path.string()+".\n"); + set_status(""); + set_info(""); + parsing_in_progress->cancel("failed"); + }); + init_parse(); + + get_buffer()->signal_changed().connect([this]() { + start_reparse(); + type_tooltips.hide(); + diagnostic_tooltips.hide(); + }); + + DEBUG("end"); +} + +void Source::ClangViewParse::configure() { + Source::View::configure(); + + auto scheme = get_source_buffer()->get_style_scheme(); + auto tag_table=get_buffer()->get_tag_table(); + for (auto &item : Singleton::Config::source()->clang_types) { + auto tag = get_buffer()->get_tag_table()->lookup(item.second); + if(tag) { + auto style = scheme->get_style(item.second); + if (style) { + if (style->property_foreground_set()) + tag->property_foreground() = style->property_foreground(); + if (style->property_background_set()) + tag->property_background() = style->property_background(); + if (style->property_strikethrough_set()) + tag->property_strikethrough() = style->property_strikethrough(); + // // if (style->property_bold_set()) tag->property_weight() = style->property_bold(); + // // if (style->property_italic_set()) tag->property_italic() = style->property_italic(); + // // if (style->property_line_background_set()) tag->property_line_background() = style->property_line_background(); + // // if (style->property_underline_set()) tag->property_underline() = style->property_underline(); + } else + INFO("Style " + item.second + " not found in " + scheme->get_name()); + } + } + + bracket_regex=std::regex("^([ \\t]*).*\\{ *$"); + no_bracket_statement_regex=std::regex("^([ \\t]*)(if|for|else if|catch|while) *\\(.*[^;}] *$"); + no_bracket_no_para_statement_regex=std::regex("^([ \\t]*)(else|try|do) *$"); +} + +Source::ClangViewParse::~ClangViewParse() { + delayed_reparse_connection.disconnect(); +} + +void Source::ClangViewParse::init_parse() { + type_tooltips.hide(); + diagnostic_tooltips.hide(); + source_readable=false; + parse_thread_go=true; + parse_thread_mapped=false; + parse_thread_stop=false; + + auto buffer_map=get_buffer_map(); + //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) { + auto start_pos=pos; + pos=str.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, ' '); + } + 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); + 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) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + if(parse_thread_stop) + 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()) { + int 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; + } + else + parse_done(); + } + } + }); +} + +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::start_reparse() { + parse_thread_mapped=false; + source_readable=false; + delayed_reparse_connection.disconnect(); + delayed_reparse_connection=Glib::signal_timeout().connect([this]() { + source_readable=false; + parse_thread_go=true; + set_status("parsing..."); + return false; + }, 1000); +} + +std::vector Source::ClangViewParse::get_compilation_commands() { + clang::CompilationDatabase db(project_path.string()); + clang::CompileCommands commands(file_path.string(), db); + std::vector cmds = commands.get_commands(); + std::vector arguments; + for (auto &i : cmds) { + std::vector lol = i.get_command_as_args(); + for (size_t a = 1; a < lol.size()-4; a++) { + arguments.emplace_back(lol[a]); + } + } + auto clang_version_string=clang::to_string(clang_getClangVersion()); + const std::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$"); + std::smatch sm; + if(std::regex_match(clang_version_string, sm, clang_version_regex)) { + auto clang_version=sm[1].str(); + arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include"); + arguments.emplace_back("-I/usr/local/Cellar/llvm/"+clang_version+"/lib/clang/"+clang_version+"/include"); + arguments.emplace_back("-IC:/msys32/mingw32/lib/clang/"+clang_version+"/include"); + arguments.emplace_back("-IC:/msys32/mingw64/lib/clang/"+clang_version+"/include"); + arguments.emplace_back("-IC:/msys64/mingw32/lib/clang/"+clang_version+"/include"); + arguments.emplace_back("-IC:/msys64/mingw64/lib/clang/"+clang_version+"/include"); + } + arguments.emplace_back("-fretain-comments-from-system-headers"); + if(file_path.extension()==".h") //TODO: temporary fix for .h-files (parse as c++) + arguments.emplace_back("-xc++"); + + return arguments; +} + +void Source::ClangViewParse::update_syntax() { + std::vector ranges; + for (auto &token : *clang_tokens) { + //if(token.get_kind()==0) // PunctuationToken + //ranges.emplace_back(token.offsets, (int) token.get_cursor().get_kind()); + if(token.get_kind()==1) // KeywordToken + ranges.emplace_back(token.offsets, 702); + else if(token.get_kind()==2) {// IdentifierToken + auto kind=(int)token.get_cursor().get_kind(); + if(kind==101 || kind==102) + kind=(int)token.get_cursor().get_referenced().get_kind(); + if(kind!=500) + ranges.emplace_back(token.offsets, kind); + } + else if(token.get_kind()==3) { // LiteralToken + ranges.emplace_back(token.offsets, 109); + } + else if(token.get_kind()==4) // CommentToken + ranges.emplace_back(token.offsets, 705); + } + if (ranges.empty() || ranges.size() == 0) { + return; + } + auto buffer = get_source_buffer(); + for(auto &tag: last_syntax_tags) + buffer->remove_tag_by_name(tag, buffer->begin(), buffer->end()); + last_syntax_tags.clear(); + for (auto &range : ranges) { + auto type_it=Singleton::Config::source()->clang_types.find(std::to_string(range.kind)); + if(type_it!=Singleton::Config::source()->clang_types.end()) { + last_syntax_tags.emplace(type_it->second); + Gtk::TextIter begin_iter = buffer->get_iter_at_line_index(range.offsets.first.line-1, range.offsets.first.index-1); + Gtk::TextIter end_iter = buffer->get_iter_at_line_index(range.offsets.second.line-1, range.offsets.second.index-1); + buffer->apply_tag_by_name(type_it->second, begin_iter, end_iter); + } + } +} + +void Source::ClangViewParse::update_diagnostics() { + diagnostic_offsets.clear(); + diagnostic_tooltips.clear(); + fix_its.clear(); + get_buffer()->remove_tag_by_name("def:warning_underline", get_buffer()->begin(), get_buffer()->end()); + get_buffer()->remove_tag_by_name("def:error_underline", get_buffer()->begin(), get_buffer()->end()); + auto diagnostics=clang_tu->get_diagnostics(); + size_t num_warnings=0; + size_t num_errors=0; + size_t num_fix_its=0; + for(auto &diagnostic: diagnostics) { + if(diagnostic.path==file_path.string()) { + auto start_line=get_line(diagnostic.offsets.first.line-1); //index is sometimes off the line + auto start_line_index=diagnostic.offsets.first.index-1; + if(start_line_index>=start_line.size()) { + if(start_line.size()==0) + start_line_index=0; + else + start_line_index=start_line.size()-1; + } + auto end_line=get_line(diagnostic.offsets.second.line-1); //index is sometimes off the line + auto end_line_index=diagnostic.offsets.second.index-1; + if(end_line_index>end_line.size()) { + if(end_line.size()==0) + end_line_index=0; + else + end_line_index=end_line.size(); + } + auto start=get_buffer()->get_iter_at_line_index(diagnostic.offsets.first.line-1, start_line_index); + diagnostic_offsets.emplace(start.get_offset()); + auto end=get_buffer()->get_iter_at_line_index(diagnostic.offsets.second.line-1, end_line_index); + std::string diagnostic_tag_name; + if(diagnostic.severity<=CXDiagnostic_Warning) { + diagnostic_tag_name="def:warning"; + num_warnings++; + } + else { + diagnostic_tag_name="def:error"; + num_errors++; + } + + auto spelling=diagnostic.spelling; + auto severity_spelling=diagnostic.severity_spelling; + + std::string fix_its_string; + unsigned fix_its_count=0; + for(auto &fix_it: diagnostic.fix_its) { + //Convert line index to line offset for correct output: + auto clang_offsets=fix_it.offsets; + std::pair offsets; + offsets.first.line=clang_offsets.first.line; + offsets.second.line=clang_offsets.second.line; + auto iter=get_buffer()->get_iter_at_line_index(clang_offsets.first.line-1, clang_offsets.first.index-1); + offsets.first.offset=iter.get_line_offset()+1; + iter=get_buffer()->get_iter_at_line_index(clang_offsets.second.line-1, clang_offsets.second.index-1); + offsets.second.offset=iter.get_line_offset()+1; + + fix_its.emplace_back(fix_it.source, offsets); + + if(fix_its_string.size()>0) + fix_its_string+='\n'; + fix_its_string+=fix_its.back().string(); + fix_its_count++; + num_fix_its++; + } + + if(fix_its_count==1) + fix_its_string.insert(0, "Fix-it:\n"); + else if(fix_its_count>1) + fix_its_string.insert(0, "Fix-its:\n"); + + auto create_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name, fix_its_string]() { + auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); + tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), severity_spelling, diagnostic_tag_name); + tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n"+spelling, "def:note"); + if(fix_its_string.size()>0) { + tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), ":\n\n"+fix_its_string, "def:note"); + } + return tooltip_buffer; + }; + diagnostic_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); + + get_buffer()->apply_tag_by_name(diagnostic_tag_name+"_underline", start, end); + auto iter=get_buffer()->get_insert()->get_iter(); + if(iter.ends_line()) { + auto next_iter=iter; + if(next_iter.forward_char()) + get_buffer()->remove_tag_by_name(diagnostic_tag_name+"_underline", iter, next_iter); + } + } + } + std::string diagnostic_info; + if(num_warnings>0) { + diagnostic_info+=std::to_string(num_warnings)+" warning"; + if(num_warnings>1) + diagnostic_info+='s'; + } + if(num_errors>0) { + if(num_warnings>0) + diagnostic_info+=", "; + diagnostic_info+=std::to_string(num_errors)+" error"; + if(num_errors>1) + diagnostic_info+='s'; + } + if(num_fix_its>0) { + if(num_warnings>0 || num_errors>0) + diagnostic_info+=", "; + diagnostic_info+=std::to_string(num_fix_its)+" fix it"; + if(num_fix_its>1) + diagnostic_info+='s'; + } + set_info(" "+diagnostic_info); +} + +void Source::ClangViewParse::show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) { + diagnostic_tooltips.show(rectangle); +} + +void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) { + if(source_readable) { + Gtk::TextIter iter; + int location_x, location_y; + window_to_buffer_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, rectangle.get_x(), rectangle.get_y(), location_x, location_y); + location_x+=(rectangle.get_width()-1)/2; + get_iter_at_location(iter, location_x, location_y); + Gdk::Rectangle iter_rectangle; + get_iter_location(iter, iter_rectangle); + if(iter_rectangle.get_x()>location_x) { + if(!iter.starts_line()) { + if(!iter.backward_char()) + return; + } + } + bool found_token=false; + if(!((*iter>='a' && *iter<='z') || (*iter>='A' && *iter<='Z') || (*iter>='0' && *iter<='9') || *iter=='_')) { + if(!iter.backward_char()) + return; + } + + while((*iter>='a' && *iter<='z') || (*iter>='A' && *iter<='Z') || (*iter>='0' && *iter<='9') || *iter=='_') { + if(!found_token) + found_token=true; + if(!iter.backward_char()) + return; + } + if(found_token && iter.forward_char()) { + auto tokens=clang_tu->get_tokens(iter.get_line()+1, iter.get_line_index()+1, + iter.get_line()+1, iter.get_line_index()+1); + + type_tooltips.clear(); + for(auto &token: *tokens) { + auto cursor=token.get_cursor(); + if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) { + if(static_cast(token.get_cursor().get_kind())==103) //These cursors are buggy + continue; + auto start=get_buffer()->get_iter_at_line_index(token.offsets.first.line-1, token.offsets.first.index-1); + auto end=get_buffer()->get_iter_at_line_index(token.offsets.second.line-1, token.offsets.second.index-1); + auto create_tooltip_buffer=[this, &token]() { + auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); + tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), "Type: "+token.get_cursor().get_type(), "def:note"); + auto brief_comment=token.get_cursor().get_brief_comments(); + if(brief_comment!="") + tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), "\n\n"+brief_comment, "def:note"); + return tooltip_buffer; + }; + + type_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); + } + } + + type_tooltips.show(); + } + } + + //type_tooltips.show(rectangle); +} + +//Clang indentation. +bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { + if(spellcheck_suggestions_dialog_shown) { + if(spellcheck_suggestions_dialog->on_key_press(key)) + return true; + } + + auto iter=get_buffer()->get_insert()->get_iter(); + if(iter.backward_char() && (get_source_buffer()->iter_has_context_class(iter, "comment") || get_source_buffer()->iter_has_context_class(iter, "string"))) + return Source::View::on_key_press_event(key); + + if(get_buffer()->get_has_selection()) { + return Source::View::on_key_press_event(key); + } + get_source_buffer()->begin_user_action(); + iter=get_buffer()->get_insert()->get_iter(); + //Indent depending on if/else/etc and brackets + if(key->keyval==GDK_KEY_Return && !iter.starts_line()) { + //First remove spaces or tabs around cursor + auto start_blank_iter=iter; + auto end_blank_iter=iter; + 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()) && + !start_blank_iter.starts_line() && start_blank_iter.backward_char()) {} + if(!start_blank_iter.starts_line()) { + 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(); + + Gtk::TextIter start_of_sentence_iter; + if(find_start_of_closed_expression(iter, start_of_sentence_iter)) { + auto start_sentence_tabs_end_iter=get_tabs_end_iter(start_of_sentence_iter); + auto tabs=get_line_before(start_sentence_tabs_end_iter); + + std::smatch sm; + if(iter.backward_char() && *iter=='{') { + auto found_iter=iter; + bool found_right_bracket=find_right_bracket_forward(iter, found_iter); + + bool has_bracket=false; + if(found_right_bracket) { + auto tabs_end_iter=get_tabs_end_iter(found_iter); + auto line_tabs=get_line_before(tabs_end_iter); + if(tabs.size()==line_tabs.size()) + has_bracket=true; + } + if(*get_buffer()->get_insert()->get_iter()=='}') { + get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs); + auto insert_it = get_source_buffer()->get_insert()->get_iter(); + if(insert_it.backward_chars(tabs.size()+1)) { + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->place_cursor(insert_it); + } + get_source_buffer()->end_user_action(); + return true; + } + else if(!has_bracket) { + //Insert new lines with bracket end + get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs+"}"); + auto insert_it = get_source_buffer()->get_insert()->get_iter(); + if(insert_it.backward_chars(tabs.size()+2)) { + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->place_cursor(insert_it); + } + get_source_buffer()->end_user_action(); + return true; + } + else { + get_source_buffer()->insert_at_cursor("\n"+tabs+tab); + scroll_to(get_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } + } + auto line=get_line_before(); + iter=get_buffer()->get_insert()->get_iter(); + auto found_iter=iter; + if(find_open_expression_symbol(iter, start_of_sentence_iter, found_iter)) { + auto tabs_end_iter=get_tabs_end_iter(found_iter); + tabs=get_line_before(tabs_end_iter); + auto iter=tabs_end_iter; + while(iter<=found_iter) { + tabs+=' '; + iter.forward_char(); + } + } + else if(std::regex_match(line, sm, no_bracket_statement_regex)) { + get_source_buffer()->insert_at_cursor("\n"+tabs+tab); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } + else if(std::regex_match(line, sm, no_bracket_no_para_statement_regex)) { + get_source_buffer()->insert_at_cursor("\n"+tabs+tab); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } + //Indenting after for instance if(...)\n...;\n + else if(iter.backward_char() && *iter==';') { + std::smatch sm2; + size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line(); + if(line_nr>0 && tabs.size()>=tab_size) { + std::string previous_line=get_line(line_nr-1); + if(!std::regex_match(previous_line, sm2, bracket_regex)) { + if(std::regex_match(previous_line, sm2, no_bracket_statement_regex)) { + get_source_buffer()->insert_at_cursor("\n"+sm2[1].str()); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } + else if(std::regex_match(previous_line, sm2, no_bracket_no_para_statement_regex)) { + get_source_buffer()->insert_at_cursor("\n"+sm2[1].str()); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } + } + } + } + //Indenting after ':' + else if(*iter==':') { + Gtk::TextIter left_bracket_iter; + if(find_left_bracket_backward(iter, left_bracket_iter)) { + if(!left_bracket_iter.ends_line()) + left_bracket_iter.forward_char(); + Gtk::TextIter start_of_left_bracket_sentence_iter; + if(find_start_of_closed_expression(left_bracket_iter, start_of_left_bracket_sentence_iter)) { + std::smatch sm; + auto tabs_end_iter=get_tabs_end_iter(start_of_left_bracket_sentence_iter); + auto tabs_start_of_sentence=get_line_before(tabs_end_iter); + if(tabs.size()==(tabs_start_of_sentence.size()+tab_size)) { + auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); + auto start_line_plus_tab_size=start_line_iter; + for(size_t c=0;cerase(start_line_iter, start_line_plus_tab_size); + } + else { + get_source_buffer()->insert_at_cursor("\n"+tabs+tab); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } + } + } + } + get_source_buffer()->insert_at_cursor("\n"+tabs); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } + } + //Indent left when writing } on a new line + else if(key->keyval==GDK_KEY_braceright) { + std::string line=get_line_before(); + if(line.size()>=tab_size) { + for(auto c: line) { + if(c!=tab_char) { + get_source_buffer()->insert_at_cursor("}"); + get_source_buffer()->end_user_action(); + return true; + } + } + Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter(); + Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line()); + Gtk::TextIter line_plus_it=line_it; + line_plus_it.forward_chars(tab_size); + get_source_buffer()->erase(line_it, line_plus_it); + } + get_source_buffer()->insert_at_cursor("}"); + get_source_buffer()->end_user_action(); + return true; + } + + get_source_buffer()->end_user_action(); + return Source::View::on_key_press_event(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) { + get_buffer()->signal_changed().connect([this](){ + if(completion_dialog_shown) + delayed_reparse_connection.disconnect(); + start_autocomplete(); + }); + 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(); + } + }); + signal_scroll_event().connect([this](GdkEventScroll* event){ + if(completion_dialog_shown) + completion_dialog->hide(); + return false; + }, false); + signal_key_release_event().connect([this](GdkEventKey* key){ + if(completion_dialog_shown) { + if(completion_dialog->on_key_release(key)) + return true; + } + + return false; + }, false); + + signal_focus_out_event().connect([this](GdkEventFocus* event) { + autocomplete_cancel_starting=true; + if(completion_dialog_shown) + completion_dialog->hide(); + + return false; + }); + + autocomplete_fail_connection=autocomplete_fail.connect([this]() { + Singleton::terminal()->print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n"); + restart_parse(); + autocomplete_starting=false; + autocomplete_cancel_starting=false; + }); + + do_delete_object.connect([this](){ + if(delete_thread.joinable()) + delete_thread.join(); + delete this; + }); + do_restart_parse.connect([this](){ + init_parse(); + restart_parse_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)) + 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 std::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$"); + const std::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$"); + std::smatch sm; + if(std::regex_match(line, sm, in_specified_namespace)) { + 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(std::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 > 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; + source_readable=false; + start_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 + start_reparse(); + } + else { + set_status(""); + start_reparse(); + start_autocomplete(); + } + }); + + std::shared_ptr > buffer_map=std::make_shared >(); + auto ustr=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, " "); + 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_done(); + else + autocomplete_fail(); + parsing_mutex.unlock(); + }); + } +} + +std::vector Source::ClangViewAutocomplete::get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map) { + std::vector suggestions; + auto results=clang_tu->get_code_completions(buffer_map, line_number, column); + if(results.cx_results==NULL) { + parse_thread_stop=true; + return suggestions; + } + + if(!autocomplete_cancel_starting) { + prefix_mutex.lock(); + auto prefix_copy=prefix; + prefix_mutex.unlock(); + for (unsigned i = 0; i < results.size(); i++) { + auto result=results.get(i); + if(result.available()) { + auto chunks=result.get_chunks(); + bool match=false; + for(auto &chunk: chunks) { + if(chunk.kind!=clang::CompletionChunk_ResultType && chunk.kind!=clang::CompletionChunk_Informative) { + if(chunk.chunk.size()>=prefix_copy.size() && chunk.chunk.compare(0, prefix_copy.size(), prefix_copy)==0) + match=true; + break; + } + } + if(match) { + suggestions.emplace_back(std::move(chunks)); + suggestions.back().brief_comments=result.get_brief_comments(); + } + } + } + } + return suggestions; +} + +void Source::ClangViewAutocomplete::async_delete() { + parsing_in_progress->cancel("canceled, freeing resources in the background"); + autocomplete_done_connection.disconnect(); + autocomplete_fail_connection.disconnect(); + parse_thread_stop=true; + delete_thread=std::thread([this](){ + //TODO: Is it possible to stop the clang-process in progress? + if(restart_parse_thread.joinable()) + restart_parse_thread.join(); + if(parse_thread.joinable()) + parse_thread.join(); + if(autocomplete_thread.joinable()) + autocomplete_thread.join(); + do_delete_object(); + }); +} + +bool Source::ClangViewAutocomplete::restart_parse() { + if(!restart_parse_running && !parse_error) { + reparse_needed=false; + restart_parse_running=true; + parse_thread_stop=true; + if(restart_parse_thread.joinable()) + restart_parse_thread.join(); + restart_parse_thread=std::thread([this](){ + if(parse_thread.joinable()) + parse_thread.join(); + if(autocomplete_thread.joinable()) + autocomplete_thread.join(); + do_restart_parse(); + }); + return true; + } + return false; +} + +//////////////////////////// +//// ClangViewRefactor ///// +//////////////////////////// +Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): +Source::ClangViewAutocomplete(file_path, project_path, language) { + similar_tokens_tag=get_buffer()->create_tag(); + similar_tokens_tag->property_weight()=1000; //TODO: replace with Pango::WEIGHT_ULTRAHEAVY in 2016 or so (when Ubuntu 14 is history) + + get_buffer()->signal_changed().connect([this]() { + if(!renaming && last_tagged_token) { + for(auto &mark: similar_token_marks) { + get_buffer()->remove_tag(similar_tokens_tag, mark.first->get_iter(), mark.second->get_iter()); + get_buffer()->delete_mark(mark.first); + get_buffer()->delete_mark(mark.second); + } + similar_token_marks.clear(); + last_tagged_token=Token(); + } + }); + + get_token=[this]() -> Token { + if(source_readable) { + auto iter=get_buffer()->get_insert()->get_iter(); + auto line=(unsigned)iter.get_line(); + auto index=(unsigned)iter.get_line_index(); + for(auto &token: *clang_tokens) { + auto cursor=token.get_cursor(); + if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) { + if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) { + if(static_cast(token.get_cursor().get_kind())==103) //These cursors are buggy + continue; + auto referenced=cursor.get_referenced(); + if(referenced) + return Token(static_cast(referenced.get_kind()), token.get_spelling(), referenced.get_usr()); + } + } + } + } + return Token(); + }; + + rename_similar_tokens=[this](const Token &token, const std::string &text) { + size_t number=0; + if(source_readable) { + auto offsets=clang_tokens->get_similar_token_offsets(static_cast(token.type), token.spelling, token.usr); + std::vector, Glib::RefPtr > > marks; + for(auto &offset: offsets) { + marks.emplace_back(get_buffer()->create_mark(get_buffer()->get_iter_at_line_index(offset.first.line-1, offset.first.index-1)), get_buffer()->create_mark(get_buffer()->get_iter_at_line_index(offset.second.line-1, offset.second.index-1))); + number++; + } + get_source_buffer()->begin_user_action(); + for(auto &mark: marks) { + renaming=true; + get_buffer()->erase(mark.first->get_iter(), mark.second->get_iter()); + get_buffer()->insert(mark.first->get_iter(), text); + get_buffer()->delete_mark(mark.first); + get_buffer()->delete_mark(mark.second); + } + get_source_buffer()->end_user_action(); + renaming=false; + } + return number; + }; + + get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr& mark){ + if(mark->get_name()=="insert") { + delayed_tag_similar_tokens_connection.disconnect(); + delayed_tag_similar_tokens_connection=Glib::signal_timeout().connect([this]() { + auto token=get_token(); + tag_similar_tokens(token); + return false; + }, 100); + } + }); + + get_declaration_location=[this](){ + std::pair location; + if(source_readable) { + auto iter=get_buffer()->get_insert()->get_iter(); + auto line=(unsigned)iter.get_line(); + auto index=(unsigned)iter.get_line_index(); + for(auto &token: *clang_tokens) { + auto cursor=token.get_cursor(); + if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) { + if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) { + auto referenced=cursor.get_referenced(); + if(referenced) { + location.first=referenced.get_source_location().get_path(); + location.second=referenced.get_source_location().get_offset(); + break; + } + } + } + } + } + return location; + }; + + goto_method=[this](){ + if(source_readable) { + auto iter=get_buffer()->get_insert()->get_iter(); + Gdk::Rectangle visible_rect; + get_visible_rect(visible_rect); + Gdk::Rectangle iter_rect; + get_iter_location(iter, iter_rect); + iter_rect.set_width(1); + if(!visible_rect.intersects(iter_rect)) { + get_iter_at_location(iter, 0, visible_rect.get_y()+visible_rect.get_height()/3); + } + selection_dialog=std::unique_ptr(new SelectionDialog(*this, get_buffer()->create_mark(iter))); + auto rows=std::make_shared >(); + auto methods=clang_tokens->get_cxx_methods(); + if(methods.size()==0) + return; + for(auto &method: methods) { + (*rows)[method.first]=method.second; + selection_dialog->add_row(method.first); + } + selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { + auto offset=rows->at(selected); + get_buffer()->place_cursor(get_buffer()->get_iter_at_line_index(offset.line-1, offset.index-1)); + scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); + delayed_tooltips_connection.disconnect(); + }; + selection_dialog->show(); + } + }; + + get_token_data=[this]() { + const auto find_non_word_char=[](const std::string &str, size_t start_pos) { + for(size_t c=start_pos;c='a' && str[c]<='z') || + (str[c]>='A' && str[c]<='Z') || + (str[c]>='0' && str[c]<='9') || + str[c]=='_')) + return c; + } + return std::string::npos; + }; + + std::vector data; + if(source_readable) { + auto iter=get_buffer()->get_insert()->get_iter(); + auto line=(unsigned)iter.get_line(); + auto index=(unsigned)iter.get_line_index(); + for(auto &token: *clang_tokens) { + auto cursor=token.get_cursor(); + if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) { + if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) { + auto referenced=cursor.get_referenced(); + if(referenced) { + auto usr=referenced.get_usr(); + boost::filesystem::path referenced_path=referenced.get_source_location().get_path(); + + //Singleton::terminal()->print(usr+'\n', true); //TODO: remove + + //Return empty if referenced is within project + if(referenced_path.generic_string().substr(0, this->project_path.generic_string().size()+1)==this->project_path.generic_string()+'/') + return data; + + data.emplace_back("clang"); + + //namespace + size_t pos1, pos2=0; + while((pos1=usr.find('@', pos2))!=std::string::npos && pos1+1get_insert()->get_iter().get_offset(); + for(auto offset: diagnostic_offsets) { + if(offset>insert_offset) { + get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset)); + scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); + return; + } + } + if(diagnostic_offsets.size()>0) { + auto iter=get_buffer()->get_iter_at_offset(*diagnostic_offsets.begin()); + get_buffer()->place_cursor(iter); + scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); + } + } + }; + + apply_fix_its=[this]() { + std::vector, Glib::RefPtr > > fix_it_marks; + if(source_readable) { + for(auto &fix_it: fix_its) { + auto start_iter=get_buffer()->get_iter_at_line_offset(fix_it.offsets.first.line-1, fix_it.offsets.first.offset-1); + auto end_iter=get_buffer()->get_iter_at_line_offset(fix_it.offsets.second.line-1, fix_it.offsets.second.offset-1); + fix_it_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter)); + } + size_t c=0; + get_source_buffer()->begin_user_action(); + for(auto &fix_it: fix_its) { + if(fix_it.type==FixIt::Type::INSERT) { + get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source); + } + if(fix_it.type==FixIt::Type::REPLACE) { + get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter()); + get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source); + } + if(fix_it.type==FixIt::Type::ERASE) { + get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter()); + } + c++; + } + for(auto &mark_pair: fix_it_marks) { + get_buffer()->delete_mark(mark_pair.first); + get_buffer()->delete_mark(mark_pair.second); + } + get_source_buffer()->end_user_action(); + } + }; +} + +void Source::ClangViewRefactor::tag_similar_tokens(const Token &token) { + if(source_readable) { + if(token && last_tagged_token!=token) { + for(auto &mark: similar_token_marks) { + get_buffer()->remove_tag(similar_tokens_tag, mark.first->get_iter(), mark.second->get_iter()); + get_buffer()->delete_mark(mark.first); + get_buffer()->delete_mark(mark.second); + } + similar_token_marks.clear(); + auto offsets=clang_tokens->get_similar_token_offsets(static_cast(token.type), token.spelling, token.usr); + for(auto &offset: offsets) { + auto start_iter=get_buffer()->get_iter_at_line_index(offset.first.line-1, offset.first.index-1); + auto end_iter=get_buffer()->get_iter_at_line_index(offset.second.line-1, offset.second.index-1); + get_buffer()->apply_tag(similar_tokens_tag, start_iter, end_iter); + similar_token_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter)); + } + last_tagged_token=token; + } + } + if(!token && last_tagged_token) { + for(auto &mark: similar_token_marks) { + get_buffer()->remove_tag(similar_tokens_tag, mark.first->get_iter(), mark.second->get_iter()); + get_buffer()->delete_mark(mark.first); + get_buffer()->delete_mark(mark.second); + } + similar_token_marks.clear(); + last_tagged_token=Token(); + } +} + +Source::ClangViewRefactor::~ClangViewRefactor() { + delayed_tag_similar_tokens_connection.disconnect(); +} + +Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): ClangViewRefactor(file_path, project_path, language) { + if(language) { + get_source_buffer()->set_highlight_syntax(true); + get_source_buffer()->set_language(language); + } +} diff --git a/src/source_clang.h b/src/source_clang.h new file mode 100644 index 0000000..4cfe02a --- /dev/null +++ b/src/source_clang.h @@ -0,0 +1,127 @@ +#ifndef JUCI_SOURCE_CLANG_H_ +#define JUCI_SOURCE_CLANG_H_ + +#include +#include +#include +#include +#include +#include "source.h" + +namespace Source { + class ClangViewParse : public View { + public: + class TokenRange { + public: + TokenRange(std::pair offsets, int kind): + offsets(offsets), kind(kind) {} + std::pair offsets; + int kind; + }; + + ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); + ~ClangViewParse(); + void configure(); + + void start_reparse(); + bool reparse_needed=false; + protected: + void init_parse(); + bool on_key_press_event(GdkEventKey* key); + 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); + void show_type_tooltips(const Gdk::Rectangle &rectangle); + + std::regex bracket_regex; + std::regex no_bracket_statement_regex; + std::regex no_bracket_no_para_statement_regex; + + std::set diagnostic_offsets; + std::vector fix_its; + private: + std::map get_buffer_map() const; + 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; + std::map parse_thread_buffer_map; + std::mutex parse_thread_buffer_map_mutex; + std::atomic parse_thread_go; + std::atomic parse_thread_mapped; + }; + + class ClangViewAutocomplete : public ClangViewParse { + public: + class AutoCompleteData { + public: + explicit AutoCompleteData(const std::vector &chunks) : + chunks(chunks) { } + std::vector chunks; + std::string brief_comments; + }; + + ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); + void async_delete(); + bool restart_parse(); + protected: + bool on_key_press_event(GdkEventKey* key); + std::thread autocomplete_thread; + private: + void start_autocomplete(); + 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); + Glib::Dispatcher autocomplete_done; + sigc::connection autocomplete_done_connection; + Glib::Dispatcher autocomplete_fail; + sigc::connection autocomplete_fail_connection; + bool autocomplete_starting=false; + std::atomic autocomplete_cancel_starting; + guint last_keyval=0; + std::string prefix; + std::mutex prefix_mutex; + + Glib::Dispatcher do_delete_object; + Glib::Dispatcher do_restart_parse; + std::thread delete_thread; + std::thread restart_parse_thread; + bool restart_parse_running=false; + }; + + class ClangViewRefactor : public ClangViewAutocomplete { + public: + ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); + ~ClangViewRefactor(); + private: + std::list, Glib::RefPtr > > similar_token_marks; + void tag_similar_tokens(const Token &token); + Glib::RefPtr similar_tokens_tag; + Token last_tagged_token; + sigc::connection delayed_tag_similar_tokens_connection; + std::unique_ptr selection_dialog; + bool renaming=false; + }; + + class ClangView : public ClangViewRefactor { + public: + ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); + }; +} + +#endif // JUCI_SOURCE_CLANG_H_ diff --git a/src/window.h b/src/window.h index 4fa0460..e14f4f8 100644 --- a/src/window.h +++ b/src/window.h @@ -6,7 +6,6 @@ #include "entrybox.h" #include "notebook.h" #include "menu.h" -#include #include class Window : public Gtk::Window { From 72d192e08c8ed8f2760db0d9ca46305b114f5ca7 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 15 Oct 2015 10:20:29 +0200 Subject: [PATCH 37/37] Cleanup of source and source_clang. --- src/source.cc | 17 +++++++++++------ src/source.h | 27 ++++++++++++--------------- src/source_clang.cc | 20 ++++++++++---------- src/source_clang.h | 1 + 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/source.cc b/src/source.cc index 07de736..154232f 100644 --- a/src/source.cc +++ b/src/source.cc @@ -52,22 +52,27 @@ Source::FixIt::FixIt(const std::string &source, const std::pair } } -std::string Source::FixIt::string() { +std::string Source::FixIt::string(Glib::RefPtr buffer) { + auto iter=buffer->get_iter_at_line_index(offsets.first.line-1, offsets.first.index-1); + unsigned first_line_offset=iter.get_line_offset()+1; + iter=buffer->get_iter_at_line_index(offsets.second.line-1, offsets.second.index-1); + unsigned second_line_offset=iter.get_line_offset()+1; + std::string text; if(type==Type::INSERT) { text+="Insert "+source+" at "; - text+=std::to_string(offsets.first.line)+":"+std::to_string(offsets.first.offset); + text+=std::to_string(offsets.first.line)+":"+std::to_string(first_line_offset); } else if(type==Type::REPLACE) { text+="Replace "; - text+=std::to_string(offsets.first.line)+":"+std::to_string(offsets.first.offset)+" - "; - text+=std::to_string(offsets.second.line)+":"+std::to_string(offsets.second.offset); + text+=std::to_string(offsets.first.line)+":"+std::to_string(first_line_offset)+" - "; + text+=std::to_string(offsets.second.line)+":"+std::to_string(second_line_offset); text+=" with "+source; } else { text+="Erase "; - text+=std::to_string(offsets.first.line)+":"+std::to_string(offsets.first.offset)+" - "; - text+=std::to_string(offsets.second.line)+":"+std::to_string(offsets.second.offset); + text+=std::to_string(offsets.first.line)+":"+std::to_string(first_line_offset)+" - "; + text+=std::to_string(offsets.second.line)+":"+std::to_string(second_line_offset); } return text; diff --git a/src/source.h b/src/source.h index 340317d..2b60633 100644 --- a/src/source.h +++ b/src/source.h @@ -3,7 +3,6 @@ #include #include #include "gtkmm.h" -#include "clangmm.h" #include #include "gtksourceviewmm.h" #include "terminal.h" @@ -57,25 +56,23 @@ namespace Source { std::string usr; }; - class FixIt { + class Offset { public: - class Offset { - public: - Offset() {} - Offset(unsigned line, unsigned offset): line(line), offset(offset) {} - bool operator==(const Offset &o) {return (line==o.line && offset==o.offset);} - - unsigned line; - unsigned offset; - }; + Offset() {} + Offset(unsigned line, unsigned index): line(line), index(index) {} + bool operator==(const Offset &o) {return (line==o.line && index==o.index);} + unsigned line; + unsigned index; + }; + + class FixIt { + public: enum class Type {INSERT, REPLACE, ERASE}; - FixIt(Type type, const std::string &source, const std::pair &offsets): - type(type), source(source), offsets(offsets) {} FixIt(const std::string &source, const std::pair &offsets); - std::string string(); + std::string string(Glib::RefPtr buffer); Type type; std::string source; @@ -103,7 +100,7 @@ namespace Source { boost::filesystem::path project_path; Glib::RefPtr language; - std::function()> get_declaration_location; + std::function()> get_declaration_location; std::function goto_method; std::function get_token; std::function()> get_token_data; diff --git a/src/source_clang.cc b/src/source_clang.cc index 1b34510..f7ed260 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -303,19 +303,17 @@ void Source::ClangViewParse::update_diagnostics() { for(auto &fix_it: diagnostic.fix_its) { //Convert line index to line offset for correct output: auto clang_offsets=fix_it.offsets; - std::pair offsets; + std::pair offsets; offsets.first.line=clang_offsets.first.line; + offsets.first.index=clang_offsets.first.index; offsets.second.line=clang_offsets.second.line; - auto iter=get_buffer()->get_iter_at_line_index(clang_offsets.first.line-1, clang_offsets.first.index-1); - offsets.first.offset=iter.get_line_offset()+1; - iter=get_buffer()->get_iter_at_line_index(clang_offsets.second.line-1, clang_offsets.second.index-1); - offsets.second.offset=iter.get_line_offset()+1; + offsets.second.index=clang_offsets.second.index; fix_its.emplace_back(fix_it.source, offsets); if(fix_its_string.size()>0) fix_its_string+='\n'; - fix_its_string+=fix_its.back().string(); + fix_its_string+=fix_its.back().string(get_buffer()); fix_its_count++; num_fix_its++; } @@ -986,7 +984,7 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { }); get_declaration_location=[this](){ - std::pair location; + std::pair location; if(source_readable) { auto iter=get_buffer()->get_insert()->get_iter(); auto line=(unsigned)iter.get_line(); @@ -998,7 +996,9 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { auto referenced=cursor.get_referenced(); if(referenced) { location.first=referenced.get_source_location().get_path(); - location.second=referenced.get_source_location().get_offset(); + auto clang_offset=referenced.get_source_location().get_offset(); + location.second.line=clang_offset.line; + location.second.index=clang_offset.index; break; } } @@ -1166,8 +1166,8 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { std::vector, Glib::RefPtr > > fix_it_marks; if(source_readable) { for(auto &fix_it: fix_its) { - auto start_iter=get_buffer()->get_iter_at_line_offset(fix_it.offsets.first.line-1, fix_it.offsets.first.offset-1); - auto end_iter=get_buffer()->get_iter_at_line_offset(fix_it.offsets.second.line-1, fix_it.offsets.second.offset-1); + auto start_iter=get_buffer()->get_iter_at_line_index(fix_it.offsets.first.line-1, fix_it.offsets.first.index-1); + auto end_iter=get_buffer()->get_iter_at_line_index(fix_it.offsets.second.line-1, fix_it.offsets.second.index-1); fix_it_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter)); } size_t c=0; diff --git a/src/source_clang.h b/src/source_clang.h index 4cfe02a..e029a58 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -6,6 +6,7 @@ #include #include #include +#include "clangmm.h" #include "source.h" namespace Source {