diff --git a/CMakeLists.txt b/CMakeLists.txt index 59e1e52..1bc6e2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,5 +11,6 @@ add_subdirectory("src") find_program(XDG_DESKTOP_MENU_EXECUTABLE xdg-desktop-menu) if(XDG_DESKTOP_MENU_EXECUTABLE) + file(MAKE_DIRECTORY "/usr/share/desktop-directories") #Workaround for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=730621 install(CODE "execute_process(COMMAND ${XDG_DESKTOP_MENU_EXECUTABLE} install --novendor share/juci.desktop)") endif() diff --git a/docs/install.md b/docs/install.md index 3311d5b..b9e58fe 100644 --- a/docs/install.md +++ b/docs/install.md @@ -3,7 +3,7 @@ ## Debian/Ubuntu 15 Install dependencies: ```sh -sudo apt-get install git cmake make g++ libclang-dev pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev +sudo apt-get install git cmake make g++ libclang-dev pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libboost-regex-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev sudo apt-get install clang-format-3.6 || sudo apt-get install clang-format-3.5 ``` @@ -19,11 +19,7 @@ sudo make install ## Ubuntu 14/Linux Mint 17 Install dependencies: ```sh -sudo add-apt-repository ppa:ubuntu-toolchain-r/test -sudo apt-get update -sudo apt-get install g++-4.9 -sudo apt-get remove g++-4.8 -sudo apt-get install git cmake make g++ libclang-3.6-dev clang-format-3.6 pkg-config libboost-system1.55-dev libboost-thread1.55-dev libboost-filesystem1.55-dev libboost-log1.55-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev +sudo apt-get install git cmake make g++ libclang-3.6-dev clang-format-3.6 pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libboost-regex-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev ``` Get juCi++ source, compile and install: diff --git a/share/juci.desktop b/share/juci.desktop index be6f7cf..134cceb 100644 --- a/share/juci.desktop +++ b/share/juci.desktop @@ -1,8 +1,10 @@ [Desktop Entry] +Version=1.0 Name=juCi++ Comment=A lightweight IDE Exec=juci %F Terminal=false Type=Application StartupNotify=true -MimeType=text/plain; +MimeType=text/plain;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-cmake; +Categories=Utility;Development;TextEditor; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7c5bf99..1cf1d18 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,7 +32,7 @@ find_package(LibClang REQUIRED) #find_package(PythonLibs 2.7) #find_package(Boost 1.55 COMPONENTS python thread log system filesystem REQUIRED) -find_package(Boost 1.55 COMPONENTS thread log system filesystem REQUIRED) +find_package(Boost 1.54 COMPONENTS thread log system filesystem regex REQUIRED) pkg_check_modules(GTKMM gtkmm-3.0 REQUIRED) # The name GTKMM is set here for the variables abouve diff --git a/src/cmake.cc b/src/cmake.cc index 3e2f1c9..03c9004 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -1,7 +1,7 @@ #include "cmake.h" #include "singletons.h" #include "filesystem.h" -#include +#include #include "dialogs.h" #include //TODO: remove @@ -10,9 +10,9 @@ using namespace std; //TODO: remove CMake::CMake(const boost::filesystem::path &path) { const auto find_cmake_project=[this](const boost::filesystem::path &cmake_path) { for(auto &line: filesystem::read_lines(cmake_path)) { - const std::regex project_regex("^ *project *\\(.*$"); - std::smatch sm; - if(std::regex_match(line, sm, project_regex)) { + const boost::regex project_regex("^ *project *\\(.*$"); + boost::smatch sm; + if(boost::regex_match(line, sm, project_regex)) { return true; } } @@ -142,9 +142,9 @@ void CMake::find_variables() { end_line=file.size(); if(end_line>start_line) { auto line=file.substr(start_line, end_line-start_line); - const std::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$"); - std::smatch sm; - if(std::regex_match(line, sm, set_regex)) { + const boost::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$"); + boost::smatch sm; + if(boost::regex_match(line, sm, set_regex)) { auto data=sm[2].str(); while(data.size()>0 && data.back()==' ') data.pop_back(); @@ -264,9 +264,9 @@ std::vector > > CMak end_line=file.size(); if(end_line>start_line) { auto line=file.substr(start_line, end_line-start_line); - const std::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$"); - std::smatch sm; - if(std::regex_match(line, sm, function_regex)) { + const boost::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$"); + boost::smatch sm; + if(boost::regex_match(line, sm, function_regex)) { auto data=sm[1].str(); while(data.size()>0 && data.back()==' ') data.pop_back(); diff --git a/src/config.cc b/src/config.cc index 094d57e..2f7ea47 100644 --- a/src/config.cc +++ b/src/config.cc @@ -81,7 +81,6 @@ void Config::retrieve_config() { menu.keys[i.first] = i.second.get_value(); } get_source(); - get_directory_filter(); window.theme_name=cfg.get("gtk_theme.name"); window.theme_variant=cfg.get("gtk_theme.variant"); @@ -188,15 +187,3 @@ void Config::get_source() { } } } - -void Config::get_directory_filter() { - boost::property_tree::ptree dir_json = cfg.get_child("directoryfilter"); - boost::property_tree::ptree ignore_json = dir_json.get_child("ignore"); - boost::property_tree::ptree except_json = dir_json.get_child("exceptions"); - directories.exceptions.clear(); - directories.ignored.clear(); - for ( auto &i : except_json ) - directories.exceptions.emplace_back(i.second.get_value()); - for ( auto &i : ignore_json ) - directories.ignored.emplace_back(i.second.get_value()); -} diff --git a/src/config.h b/src/config.h index 0bfb91d..99e398e 100644 --- a/src/config.h +++ b/src/config.h @@ -31,12 +31,6 @@ public: int history_size; }; - class Directories { - public: - std::vector ignored; - std::vector exceptions; - }; - class Source { public: class DocumentationSearch { @@ -72,7 +66,6 @@ public: Menu menu; Window window; Terminal terminal; - Directories directories; Source source; const boost::filesystem::path& juci_home_path() const { return home; } @@ -83,7 +76,6 @@ private: bool check_config_file(const boost::property_tree::ptree &default_cfg, std::string parent_path=""); void update_config_file(); void get_source(); - void get_directory_filter(); boost::property_tree::ptree cfg; boost::filesystem::path home; diff --git a/src/dialogs.cc b/src/dialogs.cc index 8f66ab0..afcd1e0 100644 --- a/src/dialogs.cc +++ b/src/dialogs.cc @@ -42,9 +42,11 @@ std::string Dialog::gtk_dialog(const std::string &title, dialog.set_transient_for(*application->window); auto current_path=application->window->notebook.get_current_folder(); + boost::system::error_code ec; if(current_path.empty()) - current_path=boost::filesystem::current_path(); - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), current_path.string().c_str()); + current_path=boost::filesystem::current_path(ec); + if(!ec) + gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), current_path.string().c_str()); if (!file_name.empty()) gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), file_name.c_str()); diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc index 377bf16..72d1869 100644 --- a/src/dialogs_win.cc +++ b/src/dialogs_win.cc @@ -99,8 +99,11 @@ private: auto application=Glib::RefPtr::cast_static(gio_application); auto current_path=application->window->notebook.get_current_folder(); + boost::system::error_code ec; if(current_path.empty()) - current_path=boost::filesystem::current_path(); + current_path=boost::filesystem::current_path(ec); + if(ec) + return false; std::wstring path=current_path.native(); size_t pos=0; diff --git a/src/directories.cc b/src/directories.cc index 14d774f..89f19f6 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -90,19 +90,16 @@ Directories::Directories() : stop_update_thread(false) { update_mutex.lock(); if(update_paths.size()==0) { for(auto it=last_write_times.begin();it!=last_write_times.end();) { - try { - if(boost::filesystem::exists(it->first)) { //Added for older boost versions (no exception thrown) - if(it->second.secondfirst)) { - update_paths.emplace_back(it->first); - } - it++; + boost::system::error_code ec; + auto last_write_time=boost::filesystem::last_write_time(it->first, ec); + if(!ec) { + if(it->second.secondfirst); } - else - it=last_write_times.erase(it); + it++; } - catch(const std::exception &e) { + else it=last_write_times.erase(it); - } } if(update_paths.size()>0) update_dispatcher(); @@ -197,23 +194,12 @@ void Directories::select(const boost::filesystem::path &path) { JDEBUG("end"); } -bool Directories::ignored(std::string path) { - std::transform(path.begin(), path.end(), path.begin(), ::tolower); - - for(std::string &i : Singleton::config->directories.exceptions) { - if(i == path) - return false; - } - for(auto &i : Singleton::config->directories.ignored) { - if(path.find(i, 0) != std::string::npos) - return true; - } - - return false; -} - void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &parent) { - last_write_times[dir_path.string()]={parent, boost::filesystem::last_write_time(dir_path)}; + boost::system::error_code ec; + auto last_write_time=boost::filesystem::last_write_time(dir_path, ec); + if(ec) + return; + last_write_times[dir_path.string()]={parent, last_write_time}; std::unique_ptr children; //Gtk::TreeNodeChildren is missing default constructor... if(parent) children=std::unique_ptr(new Gtk::TreeNodeChildren(parent.children())); @@ -227,39 +213,37 @@ void Directories::add_path(const boost::filesystem::path& dir_path, const Gtk::T boost::filesystem::directory_iterator end_it; for(boost::filesystem::directory_iterator it(dir_path);it!=end_it;it++) { auto filename=it->path().filename().string(); - if (!ignored(filename)) { - bool already_added=false; - if(*children) { - for(auto &child: *children) { - if(child.get_value(column_record.name)==filename) { - not_deleted.emplace(filename); - already_added=true; - break; - } + bool already_added=false; + if(*children) { + for(auto &child: *children) { + if(child.get_value(column_record.name)==filename) { + not_deleted.emplace(filename); + already_added=true; + break; } } - if(!already_added) { - auto child = tree_store->append(*children); - not_deleted.emplace(filename); - child->set_value(column_record.name, filename); - child->set_value(column_record.path, it->path().string()); - if (boost::filesystem::is_directory(*it)) { - child->set_value(column_record.id, "a"+filename); - auto grandchild=tree_store->append(child->children()); - grandchild->set_value(column_record.name, std::string("(empty)")); + } + if(!already_added) { + auto child = tree_store->append(*children); + not_deleted.emplace(filename); + child->set_value(column_record.name, filename); + child->set_value(column_record.path, it->path().string()); + if (boost::filesystem::is_directory(it->path())) { + 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 { + 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); - grandchild->set_value(column_record.color, rgba); - } - 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); - } + child->set_value(column_record.color, rgba); } } } diff --git a/src/directories.h b/src/directories.h index 62ef8cd..9435504 100644 --- a/src/directories.h +++ b/src/directories.h @@ -38,7 +38,6 @@ public: private: void add_path(const boost::filesystem::path& dir_path, const Gtk::TreeModel::Row &row); - bool ignored(std::string path); Gtk::TreeView tree_view; Glib::RefPtr tree_store; ColumnRecord column_record; diff --git a/src/files.h b/src/files.h index a14f680..dc6e635 100644 --- a/src/files.h +++ b/src/files.h @@ -1,6 +1,6 @@ #include -#define JUCI_VERSION "0.9.5" +#define JUCI_VERSION "0.9.6" const std::string configjson = "{\n" @@ -85,6 +85,7 @@ const std::string configjson = " \"source_center_cursor\": \"l\",\n" " \"source_find_documentation\": \"d\",\n" " \"source_goto_declaration\": \"d\",\n" +" \"source_goto_usage\": \"u\",\n" " \"source_goto_method\": \"m\",\n" " \"source_rename\": \"r\",\n" " \"source_goto_next_diagnostic\": \"e\",\n" @@ -117,12 +118,6 @@ const std::string configjson = " \"@any\": \"https://www.google.com/search?btnI&q=\"\n" " }\n" " }\n" -" },\n" -" \"directoryfilter\": {\n" -" \"ignore\": [\n" -" ],\n" -" \"exceptions\": [\n" -" ]\n" " }\n" "}\n"; diff --git a/src/menu.cc b/src/menu.cc index 735882a..60b8e9c 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -211,6 +211,11 @@ void Menu::init() { +accels["source_goto_declaration"]+ //For Ubuntu... " " " " + " _Go to Usage" + " app.source_goto_usage" + +accels["source_goto_usage"]+ //For Ubuntu... + " " + " " " _Go to Method" " app.source_goto_method" +accels["source_goto_method"]+ //For Ubuntu... diff --git a/src/selectiondialog.cc b/src/selectiondialog.cc index 012ecb8..ba1daaa 100644 --- a/src/selectiondialog.cc +++ b/src/selectiondialog.cc @@ -15,8 +15,36 @@ namespace sigc { #endif } -SelectionDialogBase::SelectionDialogBase(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry): text_view(text_view), -start_mark(start_mark), show_search_entry(show_search_entry), list_view_text(1, false, Gtk::SelectionMode::SELECTION_BROWSE) { +ListViewText::ListViewText(bool use_markup) : Gtk::TreeView(), use_markup(use_markup) { + list_store = Gtk::ListStore::create(column_record); + set_model(list_store); + append_column("", cell_renderer); + if(use_markup) + get_column(0)->add_attribute(cell_renderer.property_markup(), column_record.text); + else + get_column(0)->add_attribute(cell_renderer.property_text(), column_record.text); + + get_selection()->set_mode(Gtk::SelectionMode::SELECTION_BROWSE); + set_enable_search(true); + set_headers_visible(false); + set_hscroll_policy(Gtk::ScrollablePolicy::SCROLL_NATURAL); + set_activate_on_single_click(true); + set_hover_selection(false); + set_rules_hint(true); +} + +void ListViewText::ListViewText::append(const std::string& value) { + auto new_row=list_store->append(); + new_row->set_value(column_record.text, value); +} + +void ListViewText::ListViewText::hide() { + Gtk::TreeView::hide(); + list_store->clear(); +} + +SelectionDialogBase::SelectionDialogBase(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry, bool use_markup): text_view(text_view), +list_view_text(use_markup), start_mark(start_mark), show_search_entry(show_search_entry) { if(!show_search_entry) window=std::unique_ptr(new Gtk::Window(Gtk::WindowType::WINDOW_POPUP)); else @@ -27,13 +55,6 @@ start_mark(start_mark), show_search_entry(show_search_entry), list_view_text(1, window->property_decorated()=false; window->set_skip_taskbar_hint(true); scrolled_window.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC); - list_view_text.set_enable_search(true); - list_view_text.set_headers_visible(false); - list_view_text.set_hscroll_policy(Gtk::ScrollablePolicy::SCROLL_NATURAL); - list_view_text.set_activate_on_single_click(true); - list_view_text.set_hover_selection(false); - list_view_text.set_rules_hint(true); - //list_view_text.set_fixed_height_mode(true); //TODO: This is buggy on OS X, remember to post an issue on GTK+ 3 list_view_text.signal_realize().connect([this](){ resize(); @@ -89,8 +110,8 @@ void SelectionDialogBase::hide() { } void SelectionDialogBase::update_tooltips() { - if(list_view_text.get_selected().size()>0) { - auto it=list_view_text.get_selection()->get_selected(); + auto it=list_view_text.get_selection()->get_selected(); + if(it) { std::string row; it->get_value(0, row); if(row!=last_row || last_row.size()==0) { @@ -155,7 +176,7 @@ void SelectionDialogBase::resize() { } } -SelectionDialog::SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry) : SelectionDialogBase(text_view, start_mark, show_search_entry) {} +SelectionDialog::SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry, bool use_markup) : SelectionDialogBase(text_view, start_mark, show_search_entry, use_markup) {} void SelectionDialog::show() { SelectionDialogBase::show(); @@ -168,6 +189,14 @@ void SelectionDialog::show() { auto search_key_lc=*search_key; std::transform(row_lc.begin(), row_lc.end(), row_lc.begin(), ::tolower); std::transform(search_key_lc.begin(), search_key_lc.end(), search_key_lc.begin(), ::tolower); + if(list_view_text.use_markup) { + size_t pos=0; + while((pos=row_lc.find('<', pos))!=std::string::npos) { + auto pos2=row_lc.find('>', pos+1); + row_lc.erase(pos, pos2-pos+1); + } + search_key_lc=Glib::Markup::escape_text(search_key_lc); + } if(row_lc.find(search_key_lc)!=std::string::npos) return true; return false; @@ -221,8 +250,8 @@ void SelectionDialog::show() { }); auto activate=[this](){ - if(on_select && list_view_text.get_selected().size()>0) { - auto it=list_view_text.get_selection()->get_selected(); + auto it=list_view_text.get_selection()->get_selected(); + if(on_select && it) { std::string row; it->get_value(0, row); on_select(row, true); @@ -288,7 +317,7 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) { return false; } -CompletionDialog::CompletionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark) : SelectionDialogBase(text_view, start_mark, false) {} +CompletionDialog::CompletionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark) : SelectionDialogBase(text_view, start_mark, false, false) {} void CompletionDialog::show() { SelectionDialogBase::show(); @@ -344,8 +373,8 @@ void CompletionDialog::show() { void CompletionDialog::select(bool hide_window) { row_in_entry=true; - if(list_view_text.get_selected().size()>0) { - auto it=list_view_text.get_selection()->get_selected(); + auto it=list_view_text.get_selection()->get_selected(); + if(it) { std::string row; it->get_value(0, row); if(on_select) diff --git a/src/selectiondialog.h b/src/selectiondialog.h index 499c86f..b13fffb 100644 --- a/src/selectiondialog.h +++ b/src/selectiondialog.h @@ -6,9 +6,28 @@ #include "tooltips.h" #include +class ListViewText : public Gtk::TreeView { + class ColumnRecord : public Gtk::TreeModel::ColumnRecord { + public: + ColumnRecord() { + add(text); + } + Gtk::TreeModelColumn text; + }; +public: + bool use_markup; + ListViewText(bool use_markup); + void append(const std::string& value); + void hide(); +private: + Glib::RefPtr list_store; + ColumnRecord column_record; + Gtk::CellRendererText cell_renderer; +}; + class SelectionDialogBase { public: - SelectionDialogBase(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry); + SelectionDialogBase(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry, bool use_markup); ~SelectionDialogBase(); virtual void add_row(const std::string& row, const std::string& tooltip=""); virtual void show(); @@ -25,7 +44,7 @@ protected: std::unique_ptr window; Gtk::ScrolledWindow scrolled_window; - Gtk::ListViewText list_view_text; + ListViewText list_view_text; Gtk::Entry search_entry; bool show_search_entry; std::unique_ptr tooltips; @@ -37,7 +56,7 @@ private: class SelectionDialog : public SelectionDialogBase { public: - SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry=true); + SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry=true, bool use_markup=false); bool on_key_press(GdkEventKey* key); void show(); }; diff --git a/src/source.cc b/src/source.cc index 2688854..c31c828 100644 --- a/src/source.cc +++ b/src/source.cc @@ -371,8 +371,10 @@ void Source::View::configure() { note_tag->property_foreground()=style->property_foreground(); } - if(Singleton::config->source.spellcheck_language.size()>0) + if(Singleton::config->source.spellcheck_language.size()>0) { aspell_config_replace(spellcheck_config, "lang", Singleton::config->source.spellcheck_language.c_str()); + aspell_config_replace(spellcheck_config, "encoding", "utf-8"); + } spellcheck_possible_err=new_aspell_speller(spellcheck_config); if(spellcheck_checker!=NULL) delete_aspell_speller(spellcheck_checker); @@ -533,10 +535,13 @@ void Source::View::replace_all(const std::string &replacement) { void Source::View::paste() { std::string text=Gtk::Clipboard::get()->wait_for_text(); - //remove carriage returns (which leads to crash) - for(auto it=text.begin();it!=text.end();it++) { - if(*it=='\r') { - it=text.erase(it); + //Replace carriage returns (which leads to crash) with newlines + for(size_t c=0;c language, int type, const std::string &spelling, const std::string &usr): + language(language), type(type), spelling(spelling), usr(usr) {} operator bool() const {return (type>=0 && spelling.size()>0 && usr.size()>0);} bool operator==(const Token &o) const {return (type==o.type && spelling==o.spelling && usr==o.usr);} bool operator!=(const Token &o) const {return !(*this==o);} + Glib::RefPtr language; int type; std::string spelling; std::string usr; @@ -32,11 +33,12 @@ namespace Source { class Offset { public: Offset() {} - Offset(unsigned line, unsigned index): line(line), index(index) {} + Offset(unsigned line, unsigned index, const boost::filesystem::path &file_path=""): line(line), index(index), file_path(file_path) {} bool operator==(const Offset &o) {return (line==o.line && index==o.index);} unsigned line; unsigned index; + boost::filesystem::path file_path; }; class FixIt { @@ -74,7 +76,8 @@ namespace Source { Glib::RefPtr language; std::function auto_indent; - std::function()> get_declaration_location; + std::function get_declaration_location; + std::function >(const Token &token)> get_usages; std::function goto_method; std::function get_token; std::function()> get_token_data; @@ -82,6 +85,9 @@ namespace Source { std::function goto_next_diagnostic; std::function apply_fix_its; + std::unique_ptr selection_dialog; + sigc::connection delayed_tooltips_connection; + std::function on_update_status; std::function on_update_info; void set_status(const std::string &status); @@ -103,7 +109,6 @@ namespace Source { virtual void show_type_tooltips(const Gdk::Rectangle &rectangle) {} gdouble on_motion_last_x; gdouble on_motion_last_y; - sigc::connection delayed_tooltips_connection; void set_tooltip_events(); std::string get_line(const Gtk::TextIter &iter); diff --git a/src/source_clang.cc b/src/source_clang.cc index bc35142..8fb5ed0 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -100,9 +100,9 @@ void Source::ClangViewParse::configure() { } } - 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) *$"); + bracket_regex=boost::regex("^([ \\t]*).*\\{ *$"); + no_bracket_statement_regex=boost::regex("^([ \\t]*)(if|for|else if|catch|while) *\\(.*[^;}] *$"); + no_bracket_no_para_statement_regex=boost::regex("^([ \\t]*)(else|try|do) *$"); } void Source::ClangViewParse::init_parse() { @@ -194,9 +194,9 @@ std::vector Source::ClangViewParse::get_compilation_commands() { } } 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)) { + const boost::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$"); + boost::smatch sm; + if(boost::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"); @@ -464,7 +464,7 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { 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; + boost::smatch sm; if(iter.backward_char() && *iter=='{') { auto found_iter=iter; bool found_right_bracket=find_right_bracket_forward(iter, found_iter); @@ -516,13 +516,13 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { iter.forward_char(); } } - else if(std::regex_match(line, sm, no_bracket_statement_regex)) { + else if(boost::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)) { + else if(boost::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(); @@ -530,18 +530,18 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { } //Indenting after for instance if(...)\n...;\n else if(iter.backward_char() && *iter==';') { - std::smatch sm2; + boost::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)) { + if(!boost::regex_match(previous_line, sm2, bracket_regex)) { + if(boost::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)) { + else if(boost::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(); @@ -558,7 +558,7 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { 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; + boost::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)) { @@ -689,10 +689,10 @@ void Source::ClangViewAutocomplete::start_autocomplete() { } 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)) { + const boost::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$"); + const boost::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$"); + boost::smatch sm; + if(boost::regex_match(line, sm, in_specified_namespace)) { prefix_mutex.lock(); prefix=sm[3].str(); prefix_mutex.unlock(); @@ -702,7 +702,7 @@ void Source::ClangViewAutocomplete::start_autocomplete() { else if(last_keyval=='.' && autocomplete_starting) autocomplete_cancel_starting=true; } - else if(std::regex_match(line, sm, within_namespace)) { + else if(boost::regex_match(line, sm, within_namespace)) { prefix_mutex.lock(); prefix=sm[3].str(); prefix_mutex.unlock(); @@ -984,7 +984,7 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { continue; auto referenced=cursor.get_referenced(); if(referenced) - return Token(static_cast(referenced.get_kind()), token.get_spelling(), referenced.get_usr()); + return Token(this->language, static_cast(referenced.get_kind()), token.get_spelling(), referenced.get_usr()); } } } @@ -994,7 +994,8 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { rename_similar_tokens=[this](const Token &token, const std::string &text) { size_t number=0; - if(source_readable) { + if(source_readable && token.language && + (token.language->get_id()=="chdr" || token.language->get_id()=="cpphdr" || token.language->get_id()=="c" || token.language->get_id()=="cpp" || token.language->get_id()=="objc")) { 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) { @@ -1027,7 +1028,7 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { }); get_declaration_location=[this](){ - std::pair location; + Offset location; if(source_readable) { auto iter=get_buffer()->get_insert()->get_iter(); auto line=(unsigned)iter.get_line(); @@ -1038,10 +1039,10 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { 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.file_path=referenced.get_source_location().get_path(); auto clang_offset=referenced.get_source_location().get_offset(); - location.second.line=clang_offset.line; - location.second.index=clang_offset.index; + location.line=clang_offset.line; + location.index=clang_offset.index; break; } } @@ -1051,6 +1052,49 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { return location; }; + get_usages=[this](const Token &token) { + std::vector > usages; + + if(source_readable && token.language && + (token.language->get_id()=="chdr" || token.language->get_id()=="cpphdr" || token.language->get_id()=="c" || token.language->get_id()=="cpp" || token.language->get_id()=="objc")) { + auto offsets=clang_tokens->get_similar_token_offsets(static_cast(token.type), token.spelling, token.usr); + for(auto &offset: offsets) { + size_t whitespaces_removed=0; + auto start_iter=get_buffer()->get_iter_at_line(offset.first.line-1); + while(!start_iter.ends_line() && (*start_iter==' ' || *start_iter=='\t')) { + start_iter.forward_char(); + whitespaces_removed++; + } + auto end_iter=start_iter; + while(!end_iter.ends_line()) + end_iter.forward_char(); + std::string line=Glib::Markup::escape_text(get_buffer()->get_text(start_iter, end_iter)); + + //markup token as bold + size_t token_start_pos=offset.first.index-1-whitespaces_removed; + size_t token_end_pos=offset.second.index-1-whitespaces_removed; + size_t pos=0; + while((pos=line.find('&', pos))!=std::string::npos) { + size_t pos2=line.find(';', pos+2); + if(token_start_pos>pos) { + token_start_pos+=pos2-pos; + token_end_pos+=pos2-pos; + } + else if(token_end_pos>pos) + token_end_pos+=pos2-pos; + else + break; + pos=pos2+1; + } + line.insert(token_end_pos, ""); + line.insert(token_start_pos, ""); + usages.emplace_back(Offset(offset.first.line-1, offset.first.index-1, this->file_path), line); + } + } + + return usages; + }; + goto_method=[this](){ if(source_readable) { auto iter=get_buffer()->get_insert()->get_iter(); @@ -1062,14 +1106,43 @@ Source::ClangViewAutocomplete(file_path, project_path, language) { 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))); + selection_dialog=std::unique_ptr(new SelectionDialog(*this, get_buffer()->create_mark(iter), true, true)); 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); + std::string row=std::to_string(method.second.line)+": "+Glib::Markup::escape_text(method.first); + //Add bold method token + size_t token_end_pos=row.find('('); + if(token_end_pos==0 || token_end_pos==std::string::npos) + continue; + if(token_end_pos>8 && row.substr(token_end_pos-4, 4)==">") { + token_end_pos-=8; + size_t angle_bracket_count=1; + do { + if(row.substr(token_end_pos-4, 4)==">") { + angle_bracket_count++; + token_end_pos-=4; + } + else if(row.substr(token_end_pos-4, 4)=="<") { + angle_bracket_count--; + token_end_pos-=4; + } + else + token_end_pos--; + } while(angle_bracket_count>0 && token_end_pos>4); + } + auto pos=token_end_pos; + do { + pos--; + } while(((row[pos]>='a' && row[pos]<='z') || + (row[pos]>='A' && row[pos]<='Z') || + (row[pos]>='0' && row[pos]<='9') || row[pos]=='_' || row[pos]=='~') && pos>0); + row.insert(token_end_pos, ""); + row.insert(pos+1, ""); + (*rows)[row]=method.second; + selection_dialog->add_row(row); } selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { auto offset=rows->at(selected); diff --git a/src/source_clang.h b/src/source_clang.h index 20c8214..7cb7fdb 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include "clangmm.h" #include "source.h" #include "terminal.h" @@ -42,9 +42,9 @@ namespace Source { virtual void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle); virtual 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; + boost::regex bracket_regex; + boost::regex no_bracket_statement_regex; + boost::regex no_bracket_no_para_statement_regex; std::set diagnostic_offsets; std::vector fix_its; @@ -121,7 +121,6 @@ namespace Source { void tag_similar_tokens(const Token &token); Glib::RefPtr similar_tokens_tag; Token last_tagged_token; - std::unique_ptr selection_dialog; bool renaming=false; }; diff --git a/src/window.cc b/src/window.cc index e61cba7..aa29431 100644 --- a/src/window.cc +++ b/src/window.cc @@ -172,7 +172,9 @@ void Window::set_menu_actions() { auto time_now=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); boost::filesystem::path path = Dialog::new_folder(); if(path!="" && boost::filesystem::exists(path)) { - if(boost::filesystem::last_write_time(path)>=time_now) { + boost::system::error_code ec; + auto last_write_time=boost::filesystem::last_write_time(path, ec); + if(!ec && last_write_time>=time_now) { if(Singleton::directories->current_path!="") Singleton::directories->update(); Singleton::terminal->print("New folder "+path.string()+" created.\n"); @@ -382,10 +384,15 @@ void Window::set_menu_actions() { if(notebook.get_current_page()!=-1) { if(notebook.get_current_view()->get_declaration_location) { auto location=notebook.get_current_view()->get_declaration_location(); - if(location.first.size()>0) { - notebook.open(location.first); - auto line=static_cast(location.second.line)-1; - auto index=static_cast(location.second.index)-1; + if(!location.file_path.empty()) { + boost::filesystem::path declaration_file; + boost::system::error_code ec; + declaration_file=boost::filesystem::canonical(location.file_path, ec); + if(ec) + declaration_file=location.file_path; + notebook.open(declaration_file); + auto line=static_cast(location.line)-1; + auto index=static_cast(location.index)-1; auto buffer=notebook.get_current_view()->get_buffer(); line=std::min(line, buffer->get_line_count()-1); if(line>=0) { @@ -405,6 +412,68 @@ void Window::set_menu_actions() { } } }); + menu->add_action("source_goto_usage", [this]() { + if(notebook.get_current_page()!=-1) { + auto current_view=notebook.get_current_view(); + if(current_view->get_token && current_view->get_usages) { + auto token=current_view->get_token(); + if(token) { + auto iter=current_view->get_buffer()->get_insert()->get_iter(); + Gdk::Rectangle visible_rect; + current_view->get_visible_rect(visible_rect); + Gdk::Rectangle iter_rect; + current_view->get_iter_location(iter, iter_rect); + iter_rect.set_width(1); + if(!visible_rect.intersects(iter_rect)) { + current_view->get_iter_at_location(iter, 0, visible_rect.get_y()+visible_rect.get_height()/3); + } + current_view->selection_dialog=std::unique_ptr(new SelectionDialog(*current_view, current_view->get_buffer()->create_mark(iter), true, true)); + auto rows=std::make_shared >(); + + //First add usages in current file + auto usages=current_view->get_usages(token); + for(auto &usage: usages) { + auto iter=current_view->get_buffer()->get_iter_at_line_index(usage.first.line, usage.first.index); + auto row=std::to_string(iter.get_line()+1)+':'+std::to_string(iter.get_line_offset()+1)+' '+usage.second; + (*rows)[row]=usage.first; + current_view->selection_dialog->add_row(row); + } + //Then the remaining opened files + for(int page=0;pageget_usages) { + auto usages=view->get_usages(token); + for(auto &usage: usages) { + auto iter=view->get_buffer()->get_iter_at_line_index(usage.first.line, usage.first.index); + auto row=usage.first.file_path.filename().string()+":"+std::to_string(iter.get_line()+1)+':'+std::to_string(iter.get_line_offset()+1)+' '+usage.second; + (*rows)[row]=usage.first; + current_view->selection_dialog->add_row(row); + } + } + } + } + + if(rows->size()==0) + return; + current_view->selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { + auto offset=rows->at(selected); + boost::filesystem::path declaration_file; + boost::system::error_code ec; + declaration_file=boost::filesystem::canonical(offset.file_path, ec); + if(ec) + declaration_file=offset.file_path; + notebook.open(declaration_file); + auto view=notebook.get_current_view(); + view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(offset.line, offset.index)); + view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); + view->delayed_tooltips_connection.disconnect(); + }; + current_view->selection_dialog->show(); + } + } + } + }); menu->add_action("source_goto_method", [this]() { if(notebook.get_current_page()!=-1) { if(notebook.get_current_view()->goto_method) { @@ -567,6 +636,7 @@ void Window::activate_menu_items(bool activate) { menu->actions["source_indentation_auto_indent_buffer"]->set_enabled(activate ? static_cast(notebook.get_current_view()->auto_indent) : false); menu->actions["source_find_documentation"]->set_enabled(activate ? static_cast(notebook.get_current_view()->get_token_data) : false); menu->actions["source_goto_declaration"]->set_enabled(activate ? static_cast(notebook.get_current_view()->get_declaration_location) : false); + menu->actions["source_goto_usage"]->set_enabled(activate ? static_cast(notebook.get_current_view()->get_usages) : false); menu->actions["source_goto_method"]->set_enabled(activate ? static_cast(notebook.get_current_view()->goto_method) : false); menu->actions["source_rename"]->set_enabled(activate ? static_cast(notebook.get_current_view()->rename_similar_tokens) : false); menu->actions["source_goto_next_diagnostic"]->set_enabled(activate ? static_cast(notebook.get_current_view()->goto_next_diagnostic) : false); @@ -806,7 +876,7 @@ void Window::rename_token_entry() { if(notebook.get_current_page()!=-1) { if(notebook.get_current_view()->get_token) { auto token=std::make_shared(notebook.get_current_view()->get_token()); - if(token) { + if(*token) { entry_box.labels.emplace_back(); auto label_it=entry_box.labels.begin(); label_it->update=[label_it](int state, const std::string& message){