diff --git a/src/config.cc b/src/config.cc index cb434b7..d7bcaa1 100644 --- a/src/config.cc +++ b/src/config.cc @@ -20,14 +20,13 @@ MainConfig::MainConfig() { } void MainConfig::find_or_create_config_files() { - std::vector files = {"config.json", "menu.xml", "plugins.py"}; + std::vector files = {"config.json", "plugins.py"}; boost::filesystem::create_directories(boost::filesystem::path(Singleton::config_dir())); for (auto &file : files) { auto path = boost::filesystem::path(Singleton::config_dir() + file); if (!boost::filesystem::is_regular_file(path)) { if (file == "config.json") juci::filesystem::write(path, configjson); if (file == "plugins.py") juci::filesystem::write(path, pluginspy); - if (file == "menu.xml") juci::filesystem::write(path, menuxml); } } diff --git a/src/directories.cc b/src/directories.cc index 52a31c7..5a761a4 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -29,6 +29,9 @@ Directories::Directories() : stop_update_thread(false) { tree_store = Gtk::TreeStore::create(column_record); tree_view.set_model(tree_store); tree_view.append_column("", column_record.name); + auto renderer=dynamic_cast(tree_view.get_column(0)->get_first_cell()); + tree_view.get_column(0)->add_attribute(renderer->property_foreground_rgba(), column_record.color); + tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING); tree_view.set_enable_search(true); //TODO: why does this not work in OS X? tree_view.set_search_column(column_record.name); @@ -67,6 +70,9 @@ Directories::Directories() : stop_update_thread(false) { } auto child=tree_store->append(iter->children()); child->set_value(column_record.name, std::string("(empty)")); + Gdk::RGBA rgba; + rgba.set_rgba(0.5, 0.5, 0.5); + child->set_value(column_record.color, rgba); } }); @@ -243,9 +249,20 @@ void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::T child->set_value(column_record.id, "a"+filename); auto grandchild=tree_store->append(child->children()); grandchild->set_value(column_record.name, std::string("(empty)")); + Gdk::RGBA rgba; + rgba.set_rgba(0.5, 0.5, 0.5); + grandchild->set_value(column_record.color, rgba); } - else + else { child->set_value(column_record.id, "b"+filename); + + auto language=Source::guess_language(it->path().filename()); + if(!language) { + Gdk::RGBA rgba; + rgba.set_rgba(0.5, 0.5, 0.5); + child->set_value(column_record.color, rgba); + } + } } } } @@ -261,5 +278,8 @@ void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::T if(!*children) { auto child=tree_store->append(*children); child->set_value(column_record.name, std::string("(empty)")); + Gdk::RGBA rgba; + rgba.set_rgba(0.5, 0.5, 0.5); + child->set_value(column_record.color, rgba); } } diff --git a/src/directories.h b/src/directories.h index c34e319..74878a7 100644 --- a/src/directories.h +++ b/src/directories.h @@ -24,10 +24,12 @@ public: add(id); add(name); add(path); + add(color); } Gtk::TreeModelColumn id; Gtk::TreeModelColumn name; Gtk::TreeModelColumn path; + Gtk::TreeModelColumn color; }; Directories(); @@ -46,6 +48,7 @@ private: Gtk::TreeView tree_view; Glib::RefPtr tree_store; ColumnRecord column_record; + std::unordered_map > last_write_times; std::mutex update_mutex; std::thread update_thread; diff --git a/src/files.h b/src/files.h index 50476c7..6ded01b 100644 --- a/src/files.h +++ b/src/files.h @@ -83,62 +83,6 @@ const std::string configjson = " }\n" "}\n"; -const std::string menuxml = -"\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"\n"; - const std::string juci_light_style = "\n" "\n" diff --git a/src/menu.cc b/src/menu.cc index 10daca9..6f0d2ef 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -14,6 +14,62 @@ Menu::Menu() : box(Gtk::ORIENTATION_VERTICAL) { action_group->add(Gtk::Action::create("SourceMenu", "_Source")); action_group->add(Gtk::Action::create("PluginMenu", "_Plugins")); action_group->add(Gtk::Action::create("HelpMenu", "Help")); + + ui_xml = + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; } Gtk::Widget& Menu::get_widget() { @@ -22,7 +78,7 @@ Gtk::Widget& Menu::get_widget() { void Menu::build() { try { - ui_manager->add_ui_from_string(ui); + ui_manager->add_ui_from_string(ui_xml); } catch (const Glib::Error &ex) { std::cerr << "building menu failed" << ex.what(); diff --git a/src/menu.h b/src/menu.h index 9dcc6eb..257854e 100644 --- a/src/menu.h +++ b/src/menu.h @@ -13,7 +13,7 @@ public: Gtk::Box box; std::unordered_map key_map; - std::string ui; + std::string ui_xml; Glib::RefPtr ui_manager; Glib::RefPtr action_group; }; diff --git a/src/notebook.cc b/src/notebook.cc index 7f472b5..40b9874 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -32,7 +32,15 @@ int Notebook::size() { } Source::View* Notebook::get_view(int page) { - return source_views.at(page); + return source_views.at(get_index(page)); +} + +size_t Notebook::get_index(int page) { + for(size_t c=0;con_update_status=[this](Source::View* view, const std::string &status) { + if(get_current_page()!=-1 && get_current_view()==view) + Singleton::status()->set_text(status+" "); + }; + source_views.back()->on_update_info=[this](Source::View* view, const std::string &info) { + if(get_current_page()!=-1 && get_current_view()==view) + Singleton::info()->set_text(" "+info); + }; scrolled_windows.emplace_back(new Gtk::ScrolledWindow()); hboxes.emplace_back(new Gtk::HBox()); @@ -82,7 +99,9 @@ void Notebook::open(const boost::filesystem::path &file_path) { hboxes.back()->pack_start(*scrolled_windows.back(), true, true); std::string title=file_path.filename().string(); + append_page(*hboxes.back(), title); + set_tab_reorderable(*hboxes.back(), true); show_all_children(); set_current_page(size()-1); set_focus_child(*source_views.back()); @@ -105,10 +124,6 @@ void Notebook::open(const boost::filesystem::path &file_path) { set_tab_label_text(*(get_nth_page(page)), title); }); - get_current_view()->on_update_status=[this](Source::View* view, const std::string &status) { - if(get_current_page()!=-1 && get_current_view()==view) - Singleton::status()->set_text(status); - }; DEBUG("end"); } @@ -176,15 +191,16 @@ bool Notebook::close_current_page() { } } int page = get_current_page(); + int index=get_index(page); remove_page(page); - auto source_view=source_views.at(page); + auto source_view=source_views.at(index); if(auto source_clang_view=dynamic_cast(source_view)) source_clang_view->async_delete(); else delete source_view; - source_views.erase(source_views.begin()+ page); - scrolled_windows.erase(scrolled_windows.begin()+page); - hboxes.erase(hboxes.begin()+page); + source_views.erase(source_views.begin()+index); + scrolled_windows.erase(scrolled_windows.begin()+index); + hboxes.erase(hboxes.begin()+index); } DEBUG("end true"); return true; diff --git a/src/notebook.h b/src/notebook.h index 3c75e0c..68f6d99 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -14,6 +14,7 @@ class Notebook : public Gtk::Notebook { public: Notebook(Directories &directories); Source::View* get_view(int page); + size_t get_index(int page); int size(); Source::View* get_current_view(); bool close_current_page(); diff --git a/src/singletons.cc b/src/singletons.cc index dd9d926..4633763 100644 --- a/src/singletons.cc +++ b/src/singletons.cc @@ -17,3 +17,9 @@ Gtk::Label *Singleton::status() { status_=std::unique_ptr(new Gtk::Label()); return status_.get(); } +std::unique_ptr Singleton::info_=std::unique_ptr(); +Gtk::Label *Singleton::info() { + if(!info_) + info_=std::unique_ptr(new Gtk::Label()); + return info_.get(); +} diff --git a/src/singletons.h b/src/singletons.h index de07113..8fa6a86 100644 --- a/src/singletons.h +++ b/src/singletons.h @@ -31,9 +31,11 @@ public: static std::string style_dir() { return std::string(getenv("HOME")) + "/.juci/styles/"; } static Terminal *terminal(); static Gtk::Label *status(); + static Gtk::Label *info(); private: static std::unique_ptr terminal_; static std::unique_ptr status_; + static std::unique_ptr info_; }; #endif // JUCI_SINGLETONS_H_ diff --git a/src/source.cc b/src/source.cc index 2fa0193..98cb8ff 100644 --- a/src/source.cc +++ b/src/source.cc @@ -48,13 +48,22 @@ Glib::RefPtr Source::guess_language(const boost::filesystem::path ////////////// AspellConfig* Source::View::spellcheck_config=NULL; -Source::View::View(const boost::filesystem::path &file_path): file_path(file_path) { +Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr language): file_path(file_path) { set_smart_home_end(Gsv::SMART_HOME_END_BEFORE); + get_source_buffer()->begin_not_undoable_action(); - if(juci::filesystem::read(file_path, get_buffer())==-1) - Singleton::terminal()->print("Error: "+file_path.string()+" is not a valid UTF-8 file."); + if(language) { + if(juci::filesystem::read_non_utf8(file_path, get_buffer())==-1) + Singleton::terminal()->print("Warning: "+file_path.string()+" is not a valid UTF-8 file. Saving might corrupt the file.\n"); + } + else { + if(juci::filesystem::read(file_path, get_buffer())==-1) + Singleton::terminal()->print("Error: "+file_path.string()+" is not a valid UTF-8 file.\n"); + } get_source_buffer()->end_not_undoable_action(); + get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(0)); + search_settings = gtk_source_search_settings_new(); gtk_source_search_settings_set_wrap_around(search_settings, true); search_context = gtk_source_search_context_new(get_source_buffer()->gobj(), search_settings); @@ -193,8 +202,8 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat if(ends_line || *iter=='/' || *iter=='*') //iter_has_context_class is sadly bugged backward_success=context_iter.backward_char(); if(backward_success) { + if(last_keyval_is_backspace && !is_word_iter(iter) && iter.forward_char()) {} //backspace fix if((spellcheck_all && !get_source_buffer()->iter_has_context_class(context_iter, "no-spell-check")) || get_source_buffer()->iter_has_context_class(context_iter, "comment") || get_source_buffer()->iter_has_context_class(context_iter, "string")) { - if(last_keyval_is_backspace && !is_word_iter(iter) && iter.forward_char()) {} //backspace fix if(!is_word_iter(iter)) { //Might have used space or - to split two words auto first=iter; auto second=iter; @@ -211,6 +220,20 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat spellcheck_word(word.first, word.second); } } + else { + auto tags=iter.get_tags(); + bool has_spellcheck_error=false; + for(auto &tag: tags) { + if(tag->property_name()=="spellcheck_error") { + has_spellcheck_error=true; + break; + } + } + if(has_spellcheck_error) { + auto word=spellcheck_get_word(iter); + get_buffer()->remove_tag_by_name("spellcheck_error", word.first, word.second); + } + } } } }); @@ -230,6 +253,12 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat } } if(need_suggestions) { + auto iter=get_buffer()->get_insert()->get_iter(); + if(!((spellcheck_all && !get_source_buffer()->iter_has_context_class(iter, "no-spell-check")) || get_source_buffer()->iter_has_context_class(iter, "comment") || get_source_buffer()->iter_has_context_class(iter, "string"))) { + auto word=spellcheck_get_word(iter); + get_buffer()->remove_tag_by_name("spellcheck_error", word.first, word.second); + return false; + } spellcheck_suggestions_dialog=std::unique_ptr(new SelectionDialog(*this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()), false)); spellcheck_suggestions_dialog->on_hide=[this](){ spellcheck_suggestions_dialog_shown=false; @@ -256,6 +285,10 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat }); } + get_buffer()->signal_changed().connect([this](){ + set_info(info); + }); + set_tooltip_events(); } @@ -307,6 +340,7 @@ void Source::View::set_tooltip_events() { }, 500); type_tooltips.hide(); diagnostic_tooltips.hide(); + set_info(info); } }); @@ -521,6 +555,14 @@ void Source::View::set_status(const std::string &status) { on_update_status(this, status); } +void Source::View::set_info(const std::string &info) { + this->info=info; + auto iter=get_buffer()->get_insert()->get_iter(); + auto positions=std::to_string(iter.get_line()+1)+":"+std::to_string(iter.get_line_offset()+1); + if(on_update_info) + on_update_info(this, positions+" "+info); +} + std::string Source::View::get_line(const Gtk::TextIter &iter) { auto line_start_it = get_buffer()->get_iter_at_line(iter.get_line()); auto line_end_it = iter; @@ -857,7 +899,7 @@ std::pair Source::View::find_tab_char_and_size() { } bool Source::View::is_word_iter(const Gtk::TextIter& iter) { - return ((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==39 || *iter>=128); + return ((*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==39 || *iter>=128); } std::pair Source::View::spellcheck_get_word(Gtk::TextIter iter) { @@ -908,7 +950,7 @@ std::vector Source::View::spellcheck_get_suggestions(const Gtk::Tex ///////////////////// //// GenericView //// ///////////////////// -Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language) : View(file_path) { +Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language) : View(file_path, language) { if(language) { get_source_buffer()->set_language(language); Singleton::terminal()->print("Language for file "+file_path.string()+" set to "+language->get_name()+".\n"); @@ -928,8 +970,8 @@ Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib: //////////////////////// clang::Index Source::ClangViewParse::clang_index(0, 0); -Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path): -Source::View(file_path), project_path(project_path) { +Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): +Source::View(file_path, language), project_path(project_path), parse_error(false) { DEBUG("start"); auto scheme = get_source_buffer()->get_style_scheme(); auto tag_table=get_buffer()->get_tag_table(); @@ -983,6 +1025,7 @@ Source::View(file_path), project_path(project_path) { 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(); @@ -1025,7 +1068,8 @@ void Source::ClangViewParse::init_parse() { } pos++; } - init_syntax_highlighting(buffer_map); + 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..."); @@ -1042,31 +1086,25 @@ void Source::ClangViewParse::init_parse() { parse_start(); } else if (parse_thread_mapped && parsing_mutex.try_lock() && parse_thread_buffer_map_mutex.try_lock()) { - int status=reparse(parse_thread_buffer_map); + 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; - if(status!=0) - parse_thread_stop=true; parsing_mutex.unlock(); parse_thread_buffer_map_mutex.unlock(); if(status!=0) { parse_fail(); - return; + parse_thread_stop=true; } - parse_done(); + else + parse_done(); } } }); } -void Source::ClangViewParse::init_syntax_highlighting(const std::map &buffers) { - std::vector arguments = get_compilation_commands(); - clang_tu = std::unique_ptr(new clang::TranslationUnit(clang_index, - file_path.string(), - arguments, - buffers)); - clang_tokens=clang_tu->get_tokens(0, buffers.find(file_path.string())->second.size()-1); -} - std::map Source::ClangViewParse::get_buffer_map() const { std::map buffer_map; buffer_map[file_path.string()]=get_source_buffer()->get_text(); @@ -1077,23 +1115,12 @@ void Source::ClangViewParse::start_reparse() { parse_thread_mapped=false; source_readable=false; delayed_reparse_connection.disconnect(); - if(!parse_thread_stop) { - delayed_reparse_connection=Glib::signal_timeout().connect([this]() { - source_readable=false; - parse_thread_go=true; - set_status("parsing..."); - return false; - }, 1000); - } -} - -int Source::ClangViewParse::reparse(const std::map &buffer) { - int status = clang_tu->ReparseTranslationUnit(buffer); - if(status!=0) { - return status; - } - clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer_map.find(file_path.string())->second.size()-1); - return status; + 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() { @@ -1192,6 +1219,8 @@ void Source::ClangViewParse::update_diagnostics() { get_buffer()->remove_tag_by_name("def:warning_underline", get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag_by_name("def:error_underline", get_buffer()->begin(), get_buffer()->end()); auto diagnostics=clang_tu->get_diagnostics(); + size_t warnings=0; + size_t errors=0; 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 @@ -1213,10 +1242,14 @@ void Source::ClangViewParse::update_diagnostics() { auto start=get_buffer()->get_iter_at_line_index(diagnostic.offsets.first.line-1, start_line_index); 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) + if(diagnostic.severity<=CXDiagnostic_Warning) { diagnostic_tag_name="def:warning"; - else + warnings++; + } + else { diagnostic_tag_name="def:error"; + errors++; + } auto spelling=diagnostic.spelling; auto severity_spelling=diagnostic.severity_spelling; @@ -1237,6 +1270,20 @@ void Source::ClangViewParse::update_diagnostics() { } } } + std::string diagnostic_info; + if(warnings>0) { + diagnostic_info+=std::to_string(warnings)+" warning"; + if(warnings>1) + diagnostic_info+='s'; + } + if(errors>0) { + if(warnings>0) + diagnostic_info+=", "; + diagnostic_info+=std::to_string(errors)+" error"; + if(errors>1) + diagnostic_info+='s'; + } + set_info(" "+diagnostic_info); } void Source::ClangViewParse::update_types() { @@ -1401,8 +1448,8 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { ////////////////////////////// //// ClangViewAutocomplete /// ////////////////////////////// -Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path): -Source::ClangViewParse(file_path, project_path), autocomplete_cancel_starting(false) { +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(); @@ -1436,6 +1483,23 @@ Source::ClangViewParse(file_path, project_path), autocomplete_cancel_starting(fa return false; }); + + 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) { @@ -1489,6 +1553,10 @@ void Source::ClangViewAutocomplete::start_autocomplete() { } void Source::ClangViewAutocomplete::autocomplete() { + if(parse_thread_stop) { + return; + } + if(!autocomplete_starting) { autocomplete_starting=true; autocomplete_cancel_starting=false; @@ -1505,6 +1573,7 @@ void Source::ClangViewAutocomplete::autocomplete() { 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) { @@ -1581,16 +1650,23 @@ void Source::ClangViewAutocomplete::autocomplete() { parsing_mutex.lock(); if(!parse_thread_stop) *ac_data=move(get_autocomplete_suggestions(line_nr, column_nr, *buffer_map)); - autocomplete_done(); + 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 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; @@ -1617,12 +1693,46 @@ get_autocomplete_suggestions(int line_number, int column, std::mapcancel("canceled, freeing resources in the background"); + 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): -Source::ClangViewAutocomplete(file_path, project_path) { +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()=Pango::WEIGHT_BOLD; @@ -1767,53 +1877,9 @@ 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) { +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); } - - 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; - }); -} - -void Source::ClangView::async_delete() { - parsing_in_progress->cancel("canceled, freeing resources in the background"); - 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::ClangView::restart_parse() { - if(!restart_parse_running && !parse_thread_stop) { - 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; } diff --git a/src/source.h b/src/source.h index e28b9d5..8fd6409 100644 --- a/src/source.h +++ b/src/source.h @@ -52,7 +52,7 @@ namespace Source { class View : public Gsv::View { public: - View(const boost::filesystem::path &file_path); + View(const boost::filesystem::path &file_path, Glib::RefPtr language); ~View(); void search_highlight(const std::string &text, bool case_sensitive, bool regex); @@ -75,7 +75,11 @@ namespace Source { std::function rename_similar_tokens; std::function on_update_status; + std::function on_update_info; + void set_status(const std::string &status); + void set_info(const std::string &info); std::string status; + std::string info; protected: bool source_readable; Tooltips diagnostic_tooltips; @@ -84,9 +88,7 @@ namespace Source { gdouble on_motion_last_y; sigc::connection delayed_tooltips_connection; void set_tooltip_events(); - - void set_status(const std::string &status); - + std::string get_line(const Gtk::TextIter &iter); std::string get_line(Glib::RefPtr mark); std::string get_line(int line_nr); @@ -134,7 +136,7 @@ namespace Source { class ClangViewParse : public View { public: - ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path); + ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); ~ClangViewParse(); boost::filesystem::path project_path; void start_reparse(); @@ -150,15 +152,13 @@ namespace Source { std::shared_ptr parsing_in_progress; std::thread parse_thread; std::atomic parse_thread_stop; + std::atomic parse_error; std::regex bracket_regex; std::regex no_bracket_statement_regex; std::regex no_bracket_no_para_statement_regex; private: std::map get_buffer_map() const; - // inits the syntax highligthing on file open - void init_syntax_highlighting(const std::map &buffers); - int reparse(const std::map &buffers); void update_syntax(); std::set last_syntax_tags; void update_diagnostics(); @@ -178,7 +178,9 @@ namespace Source { class ClangViewAutocomplete : public ClangViewParse { public: - ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path); + 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; @@ -190,16 +192,23 @@ namespace Source { 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; 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); + ClangViewRefactor(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); ~ClangViewRefactor(); private: Glib::RefPtr similar_tokens_tag; @@ -212,14 +221,6 @@ namespace Source { class ClangView : public ClangViewRefactor { public: ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); - void async_delete(); - bool restart_parse(); - private: - 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 Source #endif // JUCI_SOURCE_H_ diff --git a/src/sourcefile.cc b/src/sourcefile.cc index 2ee7219..ef9fdd6 100644 --- a/src/sourcefile.cc +++ b/src/sourcefile.cc @@ -25,15 +25,6 @@ int juci::filesystem::read(const std::string &path, Glib::RefPtrinsert_at_cursor(ustr); @@ -69,6 +60,35 @@ int juci::filesystem::read(const std::string &path, Glib::RefPtr text_buffer) { + std::ifstream input(path, std::ofstream::binary); + + if(input) { + //need to read the whole file to make this work... + std::stringstream ss; + ss << input.rdbuf(); + Glib::ustring ustr=std::move(ss.str()); + + bool valid=true; + Glib::ustring::iterator iter; + while(!ustr.validate(iter)) { + auto next_char_iter=iter; + next_char_iter++; + ustr.replace(iter, next_char_iter, "?"); + valid=false; + } + + text_buffer->insert_at_cursor(ustr); + + input.close(); + if(valid) + return 1; + else + return -1; + } + return 0; +} + //Only use on small files std::vector juci::filesystem::read_lines(const std::string &path) { std::vector res; diff --git a/src/sourcefile.h b/src/sourcefile.h index 9ddc937..4507ab7 100644 --- a/src/sourcefile.h +++ b/src/sourcefile.h @@ -13,6 +13,9 @@ namespace juci { static int read(const std::string &path, Glib::RefPtr text_buffer); static int read(const boost::filesystem::path &path, Glib::RefPtr text_buffer) { return read(path.string(), text_buffer); } + static int read_non_utf8(const std::string &path, Glib::RefPtr text_buffer); + static int read_non_utf8(const boost::filesystem::path &path, Glib::RefPtr text_buffer) { return read_non_utf8(path.string(), text_buffer); } + static std::vector read_lines(const std::string &path); static std::vector read_lines(const boost::filesystem::path &path) { return read_lines(path.string()); }; diff --git a/src/tooltips.cc b/src/tooltips.cc index 113ce64..f5082d9 100644 --- a/src/tooltips.cc +++ b/src/tooltips.cc @@ -1,8 +1,8 @@ #include "tooltips.h" #include "singletons.h" -#include -using namespace std; +#include //TODO: remove +using namespace std; //TODO: remove namespace sigc { #ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE diff --git a/src/window.cc b/src/window.cc index 3b09318..13bfa5a 100644 --- a/src/window.cc +++ b/src/window.cc @@ -24,8 +24,6 @@ namespace sigc { } void Window::generate_keybindings() { - boost::filesystem::path path(Singleton::config_dir() + "menu.xml"); - menu.ui = juci::filesystem::read(path); for (auto &i : Singleton::Config::window()->keybindings) { auto key = i.second.get_value(); menu.key_map[i.first] = key; @@ -54,8 +52,9 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil terminal_scrolled_window.add(*Singleton::terminal()); terminal_vbox.pack_start(terminal_scrolled_window); - status_hbox.pack_end(*Singleton::status(), Gtk::PACK_SHRINK); - terminal_vbox.pack_end(status_hbox, Gtk::PACK_SHRINK); + info_and_status_hbox.pack_start(*Singleton::info(), Gtk::PACK_SHRINK); + info_and_status_hbox.pack_end(*Singleton::status(), Gtk::PACK_SHRINK); + terminal_vbox.pack_end(info_and_status_hbox, Gtk::PACK_SHRINK); vpaned.pack2(terminal_vbox, true, true); box.pack_end(vpaned); @@ -110,7 +109,8 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil } } - Singleton::status()->set_text(notebook.get_current_view()->status); + notebook.get_current_view()->set_status(notebook.get_current_view()->status); + notebook.get_current_view()->set_info(notebook.get_current_view()->info); } }); notebook.signal_page_removed().connect([this](Gtk::Widget* page, guint page_num) { @@ -322,10 +322,14 @@ void Window::create_menu() { }); menu.action_group->add(Gtk::Action::create("WindowCloseTab", "Close Tab"), Gtk::AccelKey(menu.key_map["close_tab"]), [this]() { notebook.close_current_page(); - if(notebook.get_current_page()!=-1) - Singleton::status()->set_text(notebook.get_current_view()->status); - else + if(notebook.get_current_page()!=-1) { + notebook.get_current_view()->set_status(notebook.get_current_view()->status); + notebook.get_current_view()->set_info(notebook.get_current_view()->info); + } + else { Singleton::status()->set_text(""); + Singleton::info()->set_text(""); + } }); menu.action_group->add(Gtk::Action::create("HelpAbout", "About"), [this] () { about.show(); diff --git a/src/window.h b/src/window.h index 693b526..5c8b349 100644 --- a/src/window.h +++ b/src/window.h @@ -33,7 +33,7 @@ private: Gtk::VBox notebook_vbox; Gtk::VBox terminal_vbox; Gtk::ScrolledWindow terminal_scrolled_window; - Gtk::HBox status_hbox; + Gtk::HBox info_and_status_hbox; Gtk::AboutDialog about; EntryBox entry_box; Menu menu;