From b4f4af5af47315b6b2c09e2c24d33409740c4678 Mon Sep 17 00:00:00 2001 From: eidheim Date: Thu, 15 Oct 2015 09:44:00 +0200 Subject: [PATCH] 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 {