From 908a7092bc345bd96d8a3143e8355ff95f0a24b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Sun, 13 Sep 2015 18:51:40 +0200 Subject: [PATCH 01/16] Clean dialogs and add some windows support --- src/CMakeLists.txt | 9 +- src/dialogs.cc | 50 +++++++++ src/dialogs.h | 14 +++ src/dialogs_win.cc | 52 ++++++++++ src/juci.cc | 2 +- src/singletons.cc | 7 ++ src/singletons.h | 2 + src/window.cc | 253 ++++++++++++++------------------------------- src/window.h | 7 -- 9 files changed, 208 insertions(+), 188 deletions(-) create mode 100644 src/dialogs.cc create mode 100644 src/dialogs.h create mode 100644 src/dialogs_win.cc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3cbdb24..deec0b9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,13 +88,18 @@ set(source_files juci.h tooltips.cc singletons.h singletons.cc + dialogs.h cmake.h cmake.cc) if(MSYS) - list(APPEND source_files terminal_win.cc) + list(APPEND source_files terminal_win.cc dialogs_win.cc) + list(APPEND source_files dialogs_win.cc) + message("MSYS detected") else() - list(APPEND source_files terminal.cc) + list(APPEND source_files terminal.cc dialogs.cc) + list(APPEND source_files dialogs.cc) + message("UNIX detected") endif() if(${validation}) diff --git a/src/dialogs.cc b/src/dialogs.cc new file mode 100644 index 0000000..8ff3882 --- /dev/null +++ b/src/dialogs.cc @@ -0,0 +1,50 @@ +#include "dialogs.h" +#include "singletons.h" +#include +#include + +std::string open_dialog(const std::string &title, + const std::vector> buttons, + Gtk::FileChooserAction gtk_options) { + Gtk::FileChooserDialog dialog(title, gtk_options); + if(Singleton::directories()->current_path!="") + gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), Singleton::directories()->current_path.string().c_str()); + else + gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); + dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS); + // dialog.set_transient_for(parent); TODO add parent + for (auto &button : buttons) + dialog.add_button(button.first, button.second); + return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : ""; +} + + +std::string Dialog::select_folder() { + return open_dialog("Please choose a folder", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Open", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); +} + +std::string Dialog::new_file() { + return open_dialog("Please create a new file", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_SAVE); +} + +std::string Dialog::new_folder() { + return open_dialog("Please create a new folder", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); +} + +std::string Dialog::select_file() { + return open_dialog("Please choose a folder", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Select", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_OPEN); +} + +std::string Dialog::save_file() { + return open_dialog("Please choose a file", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Save", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_OPEN); +} \ No newline at end of file diff --git a/src/dialogs.h b/src/dialogs.h new file mode 100644 index 0000000..5acf6ea --- /dev/null +++ b/src/dialogs.h @@ -0,0 +1,14 @@ +#ifndef JUCI_DIALOG_H_ +#define JUCI_DIALOG_H_ +#include + +class Dialog { + public: + static std::string select_folder(); + static std::string select_file(); + static std::string new_file(); + static std::string new_folder(); + static std::string save_file(); +}; // namespace Dialog + +#endif // JUCI_DIALOG_H_ \ No newline at end of file diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc new file mode 100644 index 0000000..f245f31 --- /dev/null +++ b/src/dialogs_win.cc @@ -0,0 +1,52 @@ +#include "dialogs.h" +#include +#include + +std::string Dialog::select_folder() { + char selected_folder[MAX_PATH]; + char init_path[MAX_PATH]; + auto title = "Please select a folder"; + selected_folder[0] = 0; + init_path[0] = 0; + + BROWSEINFOA browse; + browse.hwndOwner = 0; + browse.pidlRoot = NULL; + browse.lpszTitle = title; + browse.pszDisplayName = init_path; + browse.ulFlags = 0; + browse.lpfn = NULL; + auto result = SHBrowseForFolder(&browse); + + if(result) + SHGetPathFromIDListA(result, selected_folder); + + std::string dir(selected_folder); + return dir; +} +/* +std::string Dialog::new_file() { + return open_dialog("Please create a new file", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_SAVE); +} + +std::string Dialog::new_folder() { + return open_dialog("Please create a new folder", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); +} + +std::string Dialog::select_file() { + return open_dialog("Please choose a folder", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Select", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_OPEN); +} + +std::string Dialog::save_file() { + return open_dialog("Please choose a file", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Save", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_OPEN); +} + +*/ \ No newline at end of file diff --git a/src/juci.cc b/src/juci.cc index a97b8ee..c83eb47 100644 --- a/src/juci.cc +++ b/src/juci.cc @@ -45,7 +45,7 @@ void app::on_activate() { bool first_directory=true; for(auto &directory: directories) { if(first_directory) { - window->directories.open(directory); + Singleton::directories()->open(directory); first_directory=false; } else { diff --git a/src/singletons.cc b/src/singletons.cc index 4633763..9040d55 100644 --- a/src/singletons.cc +++ b/src/singletons.cc @@ -5,12 +5,19 @@ std::unique_ptr Singleton::Config::directories_=std::unique std::unique_ptr Singleton::Config::terminal_=std::unique_ptr(new Terminal::Config()); std::unique_ptr Singleton::Config::window_ = std::unique_ptr(new Window::Config()); std::unique_ptr Singleton::terminal_=std::unique_ptr(); +std::unique_ptr Singleton::directories_=std::unique_ptr(); Terminal *Singleton::terminal() { if(!terminal_) terminal_=std::unique_ptr(new Terminal()); return terminal_.get(); } + +Directories *Singleton::directories() { + if(!directories_) + directories_=std::unique_ptr(new Directories()); + return directories_.get(); +} std::unique_ptr Singleton::status_=std::unique_ptr(); Gtk::Label *Singleton::status() { if(!status_) diff --git a/src/singletons.h b/src/singletons.h index 8fa6a86..33297ea 100644 --- a/src/singletons.h +++ b/src/singletons.h @@ -30,12 +30,14 @@ public: static std::string log_dir() { return std::string(getenv("HOME")) + "/.juci/log/"; } static std::string style_dir() { return std::string(getenv("HOME")) + "/.juci/styles/"; } static Terminal *terminal(); + static Directories *directories(); static Gtk::Label *status(); static Gtk::Label *info(); private: static std::unique_ptr terminal_; static std::unique_ptr status_; static std::unique_ptr info_; + static std::unique_ptr directories_; }; #endif // JUCI_SINGLETONS_H_ diff --git a/src/window.cc b/src/window.cc index 13bfa5a..0daf779 100644 --- a/src/window.cc +++ b/src/window.cc @@ -5,6 +5,7 @@ #include "config.h" //#include "api.h" #include +#include "dialogs.h" #include //TODO: remove using namespace std; //TODO: remove @@ -30,7 +31,7 @@ void Window::generate_keybindings() { } } -Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compiling(false) { +Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(*Singleton::directories()), compiling(false) { DEBUG("start"); set_title("juCi++"); set_default_size(600, 400); @@ -42,7 +43,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil create_menu(); box.pack_start(menu.get_widget(), Gtk::PACK_SHRINK); - directory_and_notebook_panes.pack1(directories, Gtk::SHRINK); + directory_and_notebook_panes.pack1(*Singleton::directories(), Gtk::SHRINK); notebook_vbox.pack_start(notebook); notebook_vbox.pack_end(entry_box, Gtk::PACK_SHRINK); directory_and_notebook_panes.pack2(notebook_vbox, Gtk::SHRINK); @@ -60,7 +61,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil box.pack_end(vpaned); show_all_children(); - directories.on_row_activated=[this](const std::string &file) { + Singleton::directories()->on_row_activated=[this](const std::string &file) { notebook.open(file); }; @@ -100,7 +101,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil if(auto menu_item=dynamic_cast(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceRename"))) menu_item->set_sensitive((bool)notebook.get_current_view()->rename_similar_tokens); - directories.select(notebook.get_current_view()->file_path); + Singleton::directories()->select(notebook.get_current_view()->file_path); if(auto source_view=dynamic_cast(notebook.get_current_view())) { if(source_view->reparse_needed) { @@ -140,23 +141,86 @@ void Window::create_menu() { hide(); }); menu.action_group->add(Gtk::Action::create("FileNewFile", "New File"), Gtk::AccelKey(menu.key_map["new_file"]), [this]() { - new_file_dialog(); + boost::filesystem::path path = Dialog::new_file(); + if(path!="") { + if(boost::filesystem::exists(path)) { + Singleton::terminal()->print("Error: "+path.string()+" already exists.\n"); + } + else { + if(juci::filesystem::write(path)) { + if(Singleton::directories()->current_path!="") + Singleton::directories()->update(); + notebook.open(path.string()); + Singleton::terminal()->print("New file "+path.string()+" created.\n"); + } + else + Singleton::terminal()->print("Error: could not create new file "+path.string()+".\n"); + } + } }); menu.action_group->add(Gtk::Action::create("FileNewFolder", "New Folder"), Gtk::AccelKey(menu.key_map["new_folder"]), [this]() { - new_folder_dialog(); + auto time_now=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + boost::filesystem::path path = Dialog::new_folder(); + if(boost::filesystem::last_write_time(path)>=time_now) { + if(Singleton::directories()->current_path!="") + Singleton::directories()->update(); + Singleton::terminal()->print("New folder "+path.string()+" created.\n"); + } + else + Singleton::terminal()->print("Error: "+path.string()+" already exists.\n"); + Singleton::directories()->select(path); }); menu.action_group->add(Gtk::Action::create("FileNewProject", "New Project")); menu.action_group->add(Gtk::Action::create("FileNewProjectCpp", "C++"), [this]() { - new_cpp_project_dialog(); + boost::filesystem::path project_path = Dialog::new_folder(); + auto project_name=project_path.filename().string(); + for(size_t c=0;cprint("Error: "+cmakelists_path.string()+" already exists.\n"); + return; + } + if(boost::filesystem::exists(cpp_main_path)) { + Singleton::terminal()->print("Error: "+cpp_main_path.string()+" already exists.\n"); + return; + } + std::string cmakelists="cmake_minimum_required(VERSION 2.8)\n\nproject("+project_name+")\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++1y -Wall\")\n\nadd_executable("+project_name+" main.cpp)\n"; + std::string cpp_main="#include \n\nusing namespace std;\n\nint main() {\n cout << \"Hello World!\" << endl;\n\n return 0;\n}\n"; + if(juci::filesystem::write(cmakelists_path, cmakelists) && juci::filesystem::write(cpp_main_path, cpp_main)) { + Singleton::directories()->open(project_path); + notebook.open(cpp_main_path); + Singleton::terminal()->print("C++ project "+project_name+" created.\n"); + } + else + Singleton::terminal()->print("Error: Could not create project "+project_path.string()+"\n"); }); menu.action_group->add(Gtk::Action::create("FileOpenFile", "Open File"), Gtk::AccelKey(menu.key_map["open_file"]), [this]() { - open_file_dialog(); + notebook.open(Dialog::select_file()); }); menu.action_group->add(Gtk::Action::create("FileOpenFolder", "Open Folder"), Gtk::AccelKey(menu.key_map["open_folder"]), [this]() { - open_folder_dialog(); + Singleton::directories()->open(Dialog::select_folder()); }); menu.action_group->add(Gtk::Action::create("FileSaveAs", "Save As"), Gtk::AccelKey(menu.key_map["save_as"]), [this]() { - save_file_dialog(); + auto path = Dialog::save_file(); + if(path.size()>0) { + std::ofstream file(path); + if(file) { + file << notebook.get_current_view()->get_buffer()->get_text(); + file.close(); + if(Singleton::directories()->current_path!="") + Singleton::directories()->update(); + notebook.open(path); + Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path.string()+"\n"); + } + else + Singleton::terminal()->print("Error saving file\n"); + } }); menu.action_group->add(Gtk::Action::create("FileSave", "Save"), Gtk::AccelKey(menu.key_map["save"]), [this]() { @@ -301,7 +365,7 @@ void Window::create_menu() { if(content!="") { last_run_command=content; Singleton::terminal()->async_print("Running: "+content+'\n'); - Singleton::terminal()->async_execute(content, directories.current_path, [this, content](int exit_code){ + Singleton::terminal()->async_execute(content, Singleton::directories()->current_path, [this, content](int exit_code){ Singleton::terminal()->async_print(content+" returned: "+boost::lexical_cast(exit_code)+'\n'); }); } @@ -388,173 +452,6 @@ void Window::hide() { Gtk::Window::hide(); } -void Window::new_file_dialog() { - Gtk::FileChooserDialog dialog("Please create a new file", Gtk::FILE_CHOOSER_ACTION_SAVE); - if(directories.current_path!="") - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str()); - else - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); - dialog.set_transient_for(*this); - dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS); - dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL); - dialog.add_button("Save", Gtk::RESPONSE_OK); - - int result = dialog.run(); - if(result==Gtk::RESPONSE_OK) { - boost::filesystem::path path = dialog.get_filename(); - if(path!="") { - if(boost::filesystem::exists(path)) { - Singleton::terminal()->print("Error: "+path.string()+" already exists.\n"); - } - else { - if(juci::filesystem::write(path)) { - if(directories.current_path!="") - directories.update(); - notebook.open(path.string()); - Singleton::terminal()->print("New file "+path.string()+" created.\n"); - } - else - Singleton::terminal()->print("Error: could not create new file "+path.string()+".\n"); - } - } - } -} - -void Window::new_folder_dialog() { - auto time_now=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - Gtk::FileChooserDialog dialog("Please create a new folder", Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); - if(directories.current_path!="") - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str()); - else - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); - dialog.set_transient_for(*this); - dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS); - dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL); - dialog.add_button("Create", Gtk::RESPONSE_OK); - - int result = dialog.run(); - if(result==Gtk::RESPONSE_OK) { - boost::filesystem::path path=dialog.get_filename(); - if(boost::filesystem::last_write_time(path)>=time_now) { - if(directories.current_path!="") - directories.update(); - Singleton::terminal()->print("New folder "+path.string()+" created.\n"); - } - else - Singleton::terminal()->print("Error: "+path.string()+" already exists.\n"); - directories.select(path); - } -} - -void Window::new_cpp_project_dialog() { - Gtk::FileChooserDialog dialog("Please create and/or choose a folder", Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); - if(directories.current_path!="") - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str()); - else - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); - dialog.set_transient_for(*this); - dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS); - dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL); - dialog.add_button("Create", Gtk::RESPONSE_OK); - - int result = dialog.run(); - if(result==Gtk::RESPONSE_OK) { - boost::filesystem::path project_path=dialog.get_filename(); - auto project_name=project_path.filename().string(); - for(size_t c=0;cprint("Error: "+cmakelists_path.string()+" already exists.\n"); - return; - } - if(boost::filesystem::exists(cpp_main_path)) { - Singleton::terminal()->print("Error: "+cpp_main_path.string()+" already exists.\n"); - return; - } - std::string cmakelists="cmake_minimum_required(VERSION 2.8)\n\nproject("+project_name+")\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++1y -Wall\")\n\nadd_executable("+project_name+" main.cpp)\n"; - std::string cpp_main="#include \n\nusing namespace std;\n\nint main() {\n cout << \"Hello World!\" << endl;\n\n return 0;\n}\n"; - if(juci::filesystem::write(cmakelists_path, cmakelists) && juci::filesystem::write(cpp_main_path, cpp_main)) { - directories.open(project_path); - notebook.open(cpp_main_path); - Singleton::terminal()->print("C++ project "+project_name+" created.\n"); - } - else - Singleton::terminal()->print("Error: Could not create project "+project_path.string()+"\n"); - } -} - -void Window::open_folder_dialog() { - Gtk::FileChooserDialog dialog("Please choose a folder", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); - if(directories.current_path!="") - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str()); - else - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); - dialog.set_transient_for(*this); - dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS); - dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL); - dialog.add_button("Open", Gtk::RESPONSE_OK); - - int result = dialog.run(); - - if(result==Gtk::RESPONSE_OK) { - std::string project_path=dialog.get_filename(); - directories.open(project_path); - } -} - -void Window::open_file_dialog() { - Gtk::FileChooserDialog dialog("Please choose a file", Gtk::FILE_CHOOSER_ACTION_OPEN); - if(directories.current_path!="") - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), directories.current_path.string().c_str()); - else - gtk_file_chooser_set_current_folder((GtkFileChooser*)dialog.gobj(), boost::filesystem::current_path().string().c_str()); - dialog.set_transient_for(*this); - dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS); - dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL); - dialog.add_button("Open", Gtk::RESPONSE_OK); - - int result = dialog.run(); - - if(result==Gtk::RESPONSE_OK) { - std::string path = dialog.get_filename(); - notebook.open(path); - } -} - -void Window::save_file_dialog() { - if(notebook.get_current_page()==-1) - return; - Gtk::FileChooserDialog dialog(*this, "Please choose a file", Gtk::FILE_CHOOSER_ACTION_SAVE); - gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), notebook.get_current_view()->file_path.string().c_str()); - dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS); - dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL); - dialog.add_button("Save", Gtk::RESPONSE_OK); - - int result = dialog.run(); - if(result==Gtk::RESPONSE_OK) { - auto path = dialog.get_filename(); - if(path.size()>0) { - std::ofstream file(path); - if(file) { - file << notebook.get_current_view()->get_buffer()->get_text(); - file.close(); - if(directories.current_path!="") - directories.update(); - notebook.open(path); - Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path.string()+"\n"); - } - else - Singleton::terminal()->print("Error saving file\n"); - } - } -} - void Window::search_and_replace_entry() { entry_box.clear(); entry_box.labels.emplace_back(); diff --git a/src/window.h b/src/window.h index 5c8b349..5cfb91f 100644 --- a/src/window.h +++ b/src/window.h @@ -12,7 +12,6 @@ class Window : public Gtk::Window { public: Window(); - Directories directories; Notebook notebook; class Config { public: @@ -41,12 +40,6 @@ private: void create_menu(); void hide(); - void new_file_dialog(); - void new_folder_dialog(); - void new_cpp_project_dialog(); - void open_folder_dialog(); - void open_file_dialog(); - void save_file_dialog(); void search_and_replace_entry(); void goto_line_entry(); void rename_token_entry(); From 9df554750540bdfe1b14a223bdddd784624fe45f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Sun, 13 Sep 2015 21:25:18 +0200 Subject: [PATCH 02/16] Change implementation to Vista and up --- src/dialogs_win.cc | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc index f245f31..c9e6f57 100644 --- a/src/dialogs_win.cc +++ b/src/dialogs_win.cc @@ -1,33 +1,26 @@ #include "dialogs.h" -#include -#include +#include +#include +#include +#include + +#ifndef check(__fun__) +#define MESSAGE "An error occurred when trying open windows dialog" +#define check(__fun__) auto __hr__ = __fun__; if(FAILED(__hr__)) Singleton::terminal()->print(MESSAGE) +#endif + std::string Dialog::select_folder() { - char selected_folder[MAX_PATH]; - char init_path[MAX_PATH]; - auto title = "Please select a folder"; - selected_folder[0] = 0; - init_path[0] = 0; - - BROWSEINFOA browse; - browse.hwndOwner = 0; - browse.pidlRoot = NULL; - browse.lpszTitle = title; - browse.pszDisplayName = init_path; - browse.ulFlags = 0; - browse.lpfn = NULL; - auto result = SHBrowseForFolder(&browse); + IFileOpenDialog *dialog = nullptr; + check(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog))); + DWORD options; + check(dialog->GetOptions(&options)); - if(result) - SHGetPathFromIDListA(result, selected_folder); - - std::string dir(selected_folder); - return dir; } /* std::string Dialog::new_file() { return open_dialog("Please create a new file", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, + {std::make_pair("Cancel", Gtk::RESPONSECANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, Gtk::FILE_CHOOSER_ACTION_SAVE); } From ddeeb1998906bd9bf11671d98427be84dd12465b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Mon, 21 Sep 2015 18:42:22 +0200 Subject: [PATCH 03/16] Vista implementation of pick folder --- src/CMakeLists.txt | 6 ++-- src/dialogs_win.cc | 81 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index deec0b9..7bf09cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -93,18 +93,18 @@ set(source_files juci.h cmake.cc) if(MSYS) - list(APPEND source_files terminal_win.cc dialogs_win.cc) + list(APPEND source_files terminal_win.cc) list(APPEND source_files dialogs_win.cc) message("MSYS detected") else() - list(APPEND source_files terminal.cc dialogs.cc) + list(APPEND source_files terminal.cc) list(APPEND source_files dialogs.cc) message("UNIX detected") endif() if(${validation}) add_executable(${project_name} ${source_files}) - + # add_library(${module} SHARED # api # api_ext) diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc index c9e6f57..acf03bd 100644 --- a/src/dialogs_win.cc +++ b/src/dialogs_win.cc @@ -1,45 +1,78 @@ #include "dialogs.h" +#include #include #include #include -#include +#include "singletons.h" +#include -#ifndef check(__fun__) +#ifndef check #define MESSAGE "An error occurred when trying open windows dialog" -#define check(__fun__) auto __hr__ = __fun__; if(FAILED(__hr__)) Singleton::terminal()->print(MESSAGE) +HRESULT __hr__; +#define check(__fun__) __hr__ = __fun__; if(FAILED(__hr__)) Singleton::terminal()->print(MESSAGE) #endif +class win_string { + public: + win_string() : str(nullptr) { } + ~win_string() { CoTaskMemFree(static_cast(str)); } + std::string operator()(){ + std::string res; + if (str != nullptr) { + std::wstringstream ss; + ss << str; + res = std::string(ss.str().begin(), ss.str().end()); + } + return res; + } + wchar_t** operator&() { return &str; } + private: + wchar_t* str; +}; -std::string Dialog::select_folder() { - IFileOpenDialog *dialog = nullptr; - check(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog))); - DWORD options; - check(dialog->GetOptions(&options)); +class CommonDialog { + public: + CommonDialog() : dialog(nullptr) { + check(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog))); + check(dialog->GetOptions(&options)); + } + CommonDialog(const std::string &title, unsigned option) : CommonDialog() { + set_title(title); + add_option(option); + } + void set_title(const std::string &title) { check(dialog->SetTitle(title)); } + void add_option(unsigned option) { check(dialog->SetOptions(options | option)); } + std::string show() { + check(dialog->Show(nullptr)); + IShellItem *result = nullptr; + check(dialog->GetResult(&result)); + win_string str; + check(result->GetDisplayName(SIGDN_FILESYSPATH, &str));; + result->Release(); + return str(); + } + private: + IFileOpenDialog * dialog; + DWORD options; +}; + +std::string Dialog::select_folder() { + return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); } -/* + std::string Dialog::new_file() { - return open_dialog("Please create a new file", - {std::make_pair("Cancel", Gtk::RESPONSECANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_SAVE); + return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); } std::string Dialog::new_folder() { - return open_dialog("Please create a new folder", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); + return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); } std::string Dialog::select_file() { - return open_dialog("Please choose a folder", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Select", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_OPEN); + return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); } std::string Dialog::save_file() { - return open_dialog("Please choose a file", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Save", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_OPEN); -} - -*/ \ No newline at end of file + return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); +} \ No newline at end of file From 317e5a8528be28cd0f586b71cbe1cd251c578c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Mon, 21 Sep 2015 19:19:47 +0200 Subject: [PATCH 04/16] Rename logging function due to namespace conflict --- src/config.cc | 4 ++-- src/directories.cc | 14 +++++++------- src/juci.cc | 2 +- src/logging.h | 14 +++++++------- src/notebook.cc | 18 +++++++++--------- src/source.cc | 6 +++--- src/terminal.cc | 8 ++++---- src/window.cc | 4 ++-- 8 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/config.cc b/src/config.cc index d7bcaa1..75e750c 100644 --- a/src/config.cc +++ b/src/config.cc @@ -65,7 +65,7 @@ void MainConfig::GenerateSource() { void MainConfig::GenerateDirectoryFilter() { auto dir_cfg=Singleton::Config::directories(); - DEBUG("Fetching directory filter"); + JDEBUG("Fetching 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"); @@ -73,5 +73,5 @@ void MainConfig::GenerateDirectoryFilter() { dir_cfg->exceptions.emplace_back(i.second.get_value()); for ( auto &i : ignore_json ) dir_cfg->ignored.emplace_back(i.second.get_value()); - DEBUG("Directory filter fetched"); + JDEBUG("Directory filter fetched"); } diff --git a/src/directories.cc b/src/directories.cc index 5a761a4..5416936 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -23,7 +23,7 @@ namespace sigc { } Directories::Directories() : stop_update_thread(false) { - DEBUG("start"); + JDEBUG("start"); add(tree_view); set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); tree_store = Gtk::TreeStore::create(column_record); @@ -120,7 +120,7 @@ Directories::~Directories() { } void Directories::open(const boost::filesystem::path& dir_path) { - DEBUG("start"); + JDEBUG("start"); if(dir_path=="") return; @@ -142,21 +142,21 @@ void Directories::open(const boost::filesystem::path& dir_path) { current_path=dir_path; - DEBUG("end"); + JDEBUG("end"); } void Directories::update() { - DEBUG("start"); + JDEBUG("start"); update_mutex.lock(); for(auto &last_write_time: last_write_times) { add_path(last_write_time.first, last_write_time.second.first); } update_mutex.unlock(); - DEBUG("end"); + JDEBUG("end"); } void Directories::select(const boost::filesystem::path &path) { - DEBUG("start"); + JDEBUG("start"); if(current_path=="") return; @@ -196,7 +196,7 @@ void Directories::select(const boost::filesystem::path &path) { } return false; }); - DEBUG("end"); + JDEBUG("end"); } bool Directories::ignored(std::string path) { diff --git a/src/juci.cc b/src/juci.cc index c83eb47..c4164bc 100644 --- a/src/juci.cc +++ b/src/juci.cc @@ -9,7 +9,7 @@ void init_logging() { boost::log::add_common_attributes(); boost::log::add_file_log(boost::log::keywords::file_name = Singleton::log_dir() + "juci.log", boost::log::keywords::auto_flush = true); - INFO("Logging initalized"); + JINFO("Logging initalized"); } int app::on_command_line(const Glib::RefPtr &cmd) { diff --git a/src/logging.h b/src/logging.h index fbd9f89..7fd780b 100644 --- a/src/logging.h +++ b/src/logging.h @@ -15,12 +15,12 @@ #define INFO_VAR(x) BOOST_LOG_TRIVIAL(info) << "** Info: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; #define WARNING_VAR(x) BOOST_LOG_TRIVIAL(warning) << "** Warning: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; #define FATAL_VAR(x) BOOST_LOG_TRIVIAL(fatal) << "** Fatal: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; -#define ERROR_VAR(x) BOOST_LOG_TRIVIAL(error) << "** Error: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; -#define TRACE(x) BOOST_LOG_TRIVIAL(trace) << "** Trace: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define DEBUG(x) BOOST_LOG_TRIVIAL(debug) << "** Debug: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define INFO(x) BOOST_LOG_TRIVIAL(info) << "** Info: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define WARNING(x) BOOST_LOG_TRIVIAL(warning) << "** Warning: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define FATAL(x) BOOST_LOG_TRIVIAL(fatal) << "** Fatal: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define ERROR(x) BOOST_LOG_TRIVIAL(error) << "** Error: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JERROR_VAR(x) BOOST_LOG_TRIVIAL(error) << "** Error: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; +#define JTRACE(x) BOOST_LOG_TRIVIAL(trace) << "** Trace: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JDEBUG(x) BOOST_LOG_TRIVIAL(debug) << "** Debug: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JINFO(x) BOOST_LOG_TRIVIAL(info) << "** Info: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JWARNING(x) BOOST_LOG_TRIVIAL(warning) << "** Warning: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JFATAL(x) BOOST_LOG_TRIVIAL(fatal) << "** Fatal: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JERROR(x) BOOST_LOG_TRIVIAL(error) << "** Error: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; #endif // JUCI_LOGGING_H_ diff --git a/src/notebook.cc b/src/notebook.cc index 40b9874..041f8c1 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -48,7 +48,7 @@ Source::View* Notebook::get_current_view() { } void Notebook::open(const boost::filesystem::path &file_path) { - DEBUG("start"); + JDEBUG("start"); for(int c=0;cfile_path) { set_current_page(c); @@ -124,13 +124,13 @@ void Notebook::open(const boost::filesystem::path &file_path) { set_tab_label_text(*(get_nth_page(page)), title); }); - DEBUG("end"); + JDEBUG("end"); } bool Notebook::save(int page, bool reparse_needed) { - DEBUG("start"); + JDEBUG("start"); if(page>=size()) { - DEBUG("end false"); + JDEBUG("end false"); return false; } auto view=get_view(page); @@ -166,12 +166,12 @@ bool Notebook::save(int page, bool reparse_needed) { } } } - DEBUG("end true"); + JDEBUG("end true"); return true; } Singleton::terminal()->print("Error: could not save file " +view->file_path.string()+"\n"); } - DEBUG("end false"); + JDEBUG("end false"); return false; } @@ -182,11 +182,11 @@ bool Notebook::save_current() { } bool Notebook::close_current_page() { - DEBUG("start"); + JDEBUG("start"); if (get_current_page()!=-1) { if(get_current_view()->get_buffer()->get_modified()){ if(!save_modified_dialog()) { - DEBUG("end false"); + JDEBUG("end false"); return false; } } @@ -202,7 +202,7 @@ bool Notebook::close_current_page() { scrolled_windows.erase(scrolled_windows.begin()+index); hboxes.erase(hboxes.begin()+index); } - DEBUG("end true"); + JDEBUG("end true"); return true; } diff --git a/src/source.cc b/src/source.cc index 98cb8ff..b25a30c 100644 --- a/src/source.cc +++ b/src/source.cc @@ -972,7 +972,7 @@ clang::Index Source::ClangViewParse::clang_index(0, 0); Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): Source::View(file_path, language), project_path(project_path), parse_error(false) { - DEBUG("start"); + JDEBUG("start"); auto scheme = get_source_buffer()->get_style_scheme(); auto tag_table=get_buffer()->get_tag_table(); for (auto &item : Singleton::Config::source()->clang_types) { @@ -991,7 +991,7 @@ Source::View(file_path, language), project_path(project_path), parse_error(false // // 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()); + JINFO("Style " + item.second + " not found in " + scheme->get_name()); } } @@ -1039,7 +1039,7 @@ Source::View(file_path, language), project_path(project_path), parse_error(false bracket_regex=std::regex(std::string("^(")+tab_char+"*).*\\{ *$"); no_bracket_statement_regex=std::regex(std::string("^(")+tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$"); no_bracket_no_para_statement_regex=std::regex(std::string("^(")+tab_char+"*)(else|try|do) *$"); - DEBUG("end"); + JDEBUG("end"); } Source::ClangViewParse::~ClangViewParse() { diff --git a/src/terminal.cc b/src/terminal.cc index eb96fcf..7cee28b 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -154,7 +154,7 @@ Terminal::Terminal() { } int Terminal::execute(const std::string &command, const boost::filesystem::path &path) { - DEBUG("start"); + JDEBUG("start"); int stdin_fd, stdout_fd, stderr_fd; auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd); @@ -192,14 +192,14 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path close(stdout_fd); close(stderr_fd); - DEBUG("end"); + JDEBUG("end"); return exit_code; } } void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function callback) { std::thread async_execute_thread([this, command, path, callback](){ - DEBUG("start"); + JDEBUG("start"); int stdin_fd, stdout_fd, stderr_fd; async_executes_mutex.lock(); stdin_buffer.clear(); @@ -254,7 +254,7 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem if(callback) callback(exit_code); - DEBUG("end"); + JDEBUG("end"); } }); async_execute_thread.detach(); diff --git a/src/window.cc b/src/window.cc index 0daf779..8ac72a6 100644 --- a/src/window.cc +++ b/src/window.cc @@ -32,7 +32,7 @@ void Window::generate_keybindings() { } Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(*Singleton::directories()), compiling(false) { - DEBUG("start"); + JDEBUG("start"); set_title("juCi++"); set_default_size(600, 400); set_events(Gdk::POINTER_MOTION_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::SCROLL_MASK); @@ -133,7 +133,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(*Singleton::director about.set_comments("This is an open source IDE with high-end features to make your programming experience juicy"); about.set_license_type(Gtk::License::LICENSE_MIT_X11); about.set_transient_for(*this); - DEBUG("end"); + JDEBUG("end"); } // Window constructor void Window::create_menu() { From 2ea1e1821abf88c1b5ff1b42ac67fdd24dadfb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Mon, 21 Sep 2015 19:19:47 +0200 Subject: [PATCH 05/16] Rename logging function due to namespace conflict --- src/config.cc | 4 ++-- src/directories.cc | 14 +++++++------- src/juci.cc | 2 +- src/logging.h | 14 +++++++------- src/notebook.cc | 18 +++++++++--------- src/source.cc | 6 +++--- src/terminal.cc | 8 ++++---- src/terminal_win.cc | 4 ++-- src/window.cc | 4 ++-- 9 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/config.cc b/src/config.cc index d7bcaa1..75e750c 100644 --- a/src/config.cc +++ b/src/config.cc @@ -65,7 +65,7 @@ void MainConfig::GenerateSource() { void MainConfig::GenerateDirectoryFilter() { auto dir_cfg=Singleton::Config::directories(); - DEBUG("Fetching directory filter"); + JDEBUG("Fetching 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"); @@ -73,5 +73,5 @@ void MainConfig::GenerateDirectoryFilter() { dir_cfg->exceptions.emplace_back(i.second.get_value()); for ( auto &i : ignore_json ) dir_cfg->ignored.emplace_back(i.second.get_value()); - DEBUG("Directory filter fetched"); + JDEBUG("Directory filter fetched"); } diff --git a/src/directories.cc b/src/directories.cc index 5a761a4..5416936 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -23,7 +23,7 @@ namespace sigc { } Directories::Directories() : stop_update_thread(false) { - DEBUG("start"); + JDEBUG("start"); add(tree_view); set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); tree_store = Gtk::TreeStore::create(column_record); @@ -120,7 +120,7 @@ Directories::~Directories() { } void Directories::open(const boost::filesystem::path& dir_path) { - DEBUG("start"); + JDEBUG("start"); if(dir_path=="") return; @@ -142,21 +142,21 @@ void Directories::open(const boost::filesystem::path& dir_path) { current_path=dir_path; - DEBUG("end"); + JDEBUG("end"); } void Directories::update() { - DEBUG("start"); + JDEBUG("start"); update_mutex.lock(); for(auto &last_write_time: last_write_times) { add_path(last_write_time.first, last_write_time.second.first); } update_mutex.unlock(); - DEBUG("end"); + JDEBUG("end"); } void Directories::select(const boost::filesystem::path &path) { - DEBUG("start"); + JDEBUG("start"); if(current_path=="") return; @@ -196,7 +196,7 @@ void Directories::select(const boost::filesystem::path &path) { } return false; }); - DEBUG("end"); + JDEBUG("end"); } bool Directories::ignored(std::string path) { diff --git a/src/juci.cc b/src/juci.cc index c83eb47..c4164bc 100644 --- a/src/juci.cc +++ b/src/juci.cc @@ -9,7 +9,7 @@ void init_logging() { boost::log::add_common_attributes(); boost::log::add_file_log(boost::log::keywords::file_name = Singleton::log_dir() + "juci.log", boost::log::keywords::auto_flush = true); - INFO("Logging initalized"); + JINFO("Logging initalized"); } int app::on_command_line(const Glib::RefPtr &cmd) { diff --git a/src/logging.h b/src/logging.h index fbd9f89..7fd780b 100644 --- a/src/logging.h +++ b/src/logging.h @@ -15,12 +15,12 @@ #define INFO_VAR(x) BOOST_LOG_TRIVIAL(info) << "** Info: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; #define WARNING_VAR(x) BOOST_LOG_TRIVIAL(warning) << "** Warning: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; #define FATAL_VAR(x) BOOST_LOG_TRIVIAL(fatal) << "** Fatal: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; -#define ERROR_VAR(x) BOOST_LOG_TRIVIAL(error) << "** Error: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; -#define TRACE(x) BOOST_LOG_TRIVIAL(trace) << "** Trace: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define DEBUG(x) BOOST_LOG_TRIVIAL(debug) << "** Debug: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define INFO(x) BOOST_LOG_TRIVIAL(info) << "** Info: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define WARNING(x) BOOST_LOG_TRIVIAL(warning) << "** Warning: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define FATAL(x) BOOST_LOG_TRIVIAL(fatal) << "** Fatal: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; -#define ERROR(x) BOOST_LOG_TRIVIAL(error) << "** Error: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JERROR_VAR(x) BOOST_LOG_TRIVIAL(error) << "** Error: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): " << #x << "=<" << x << ">"; +#define JTRACE(x) BOOST_LOG_TRIVIAL(trace) << "** Trace: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JDEBUG(x) BOOST_LOG_TRIVIAL(debug) << "** Debug: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JINFO(x) BOOST_LOG_TRIVIAL(info) << "** Info: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JWARNING(x) BOOST_LOG_TRIVIAL(warning) << "** Warning: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JFATAL(x) BOOST_LOG_TRIVIAL(fatal) << "** Fatal: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; +#define JERROR(x) BOOST_LOG_TRIVIAL(error) << "** Error: " << __FILE__ << "@" << __func__ << "(" << __LINE__ << "): \"" << x << "\""; #endif // JUCI_LOGGING_H_ diff --git a/src/notebook.cc b/src/notebook.cc index 40b9874..041f8c1 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -48,7 +48,7 @@ Source::View* Notebook::get_current_view() { } void Notebook::open(const boost::filesystem::path &file_path) { - DEBUG("start"); + JDEBUG("start"); for(int c=0;cfile_path) { set_current_page(c); @@ -124,13 +124,13 @@ void Notebook::open(const boost::filesystem::path &file_path) { set_tab_label_text(*(get_nth_page(page)), title); }); - DEBUG("end"); + JDEBUG("end"); } bool Notebook::save(int page, bool reparse_needed) { - DEBUG("start"); + JDEBUG("start"); if(page>=size()) { - DEBUG("end false"); + JDEBUG("end false"); return false; } auto view=get_view(page); @@ -166,12 +166,12 @@ bool Notebook::save(int page, bool reparse_needed) { } } } - DEBUG("end true"); + JDEBUG("end true"); return true; } Singleton::terminal()->print("Error: could not save file " +view->file_path.string()+"\n"); } - DEBUG("end false"); + JDEBUG("end false"); return false; } @@ -182,11 +182,11 @@ bool Notebook::save_current() { } bool Notebook::close_current_page() { - DEBUG("start"); + JDEBUG("start"); if (get_current_page()!=-1) { if(get_current_view()->get_buffer()->get_modified()){ if(!save_modified_dialog()) { - DEBUG("end false"); + JDEBUG("end false"); return false; } } @@ -202,7 +202,7 @@ bool Notebook::close_current_page() { scrolled_windows.erase(scrolled_windows.begin()+index); hboxes.erase(hboxes.begin()+index); } - DEBUG("end true"); + JDEBUG("end true"); return true; } diff --git a/src/source.cc b/src/source.cc index 98cb8ff..b25a30c 100644 --- a/src/source.cc +++ b/src/source.cc @@ -972,7 +972,7 @@ clang::Index Source::ClangViewParse::clang_index(0, 0); Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): Source::View(file_path, language), project_path(project_path), parse_error(false) { - DEBUG("start"); + JDEBUG("start"); auto scheme = get_source_buffer()->get_style_scheme(); auto tag_table=get_buffer()->get_tag_table(); for (auto &item : Singleton::Config::source()->clang_types) { @@ -991,7 +991,7 @@ Source::View(file_path, language), project_path(project_path), parse_error(false // // 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()); + JINFO("Style " + item.second + " not found in " + scheme->get_name()); } } @@ -1039,7 +1039,7 @@ Source::View(file_path, language), project_path(project_path), parse_error(false bracket_regex=std::regex(std::string("^(")+tab_char+"*).*\\{ *$"); no_bracket_statement_regex=std::regex(std::string("^(")+tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$"); no_bracket_no_para_statement_regex=std::regex(std::string("^(")+tab_char+"*)(else|try|do) *$"); - DEBUG("end"); + JDEBUG("end"); } Source::ClangViewParse::~ClangViewParse() { diff --git a/src/terminal.cc b/src/terminal.cc index eb96fcf..7cee28b 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -154,7 +154,7 @@ Terminal::Terminal() { } int Terminal::execute(const std::string &command, const boost::filesystem::path &path) { - DEBUG("start"); + JDEBUG("start"); int stdin_fd, stdout_fd, stderr_fd; auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd); @@ -192,14 +192,14 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path close(stdout_fd); close(stderr_fd); - DEBUG("end"); + JDEBUG("end"); return exit_code; } } void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function callback) { std::thread async_execute_thread([this, command, path, callback](){ - DEBUG("start"); + JDEBUG("start"); int stdin_fd, stdout_fd, stderr_fd; async_executes_mutex.lock(); stdin_buffer.clear(); @@ -254,7 +254,7 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem if(callback) callback(exit_code); - DEBUG("end"); + JDEBUG("end"); } }); async_execute_thread.detach(); diff --git a/src/terminal_win.cc b/src/terminal_win.cc index e79014d..8cd9238 100644 --- a/src/terminal_win.cc +++ b/src/terminal_win.cc @@ -332,7 +332,7 @@ void Terminal::kill_async_executes(bool force) { } int Terminal::print(const std::string &message, bool bold){ - INFO("Terminal: PrintMessage"); + JINFO("Terminal: PrintMessage"); if(bold) get_buffer()->insert_with_tag(get_buffer()->end(), message, bold_tag); else @@ -349,7 +349,7 @@ int Terminal::print(const std::string &message, bool bold){ } void Terminal::print(int line_nr, const std::string &message){ - INFO("Terminal: PrintMessage at line " << line_nr); + JINFO("Terminal: PrintMessage at line " << line_nr); auto iter=get_buffer()->get_iter_at_line(line_nr); while(!iter.ends_line()) iter++; diff --git a/src/window.cc b/src/window.cc index 0daf779..8ac72a6 100644 --- a/src/window.cc +++ b/src/window.cc @@ -32,7 +32,7 @@ void Window::generate_keybindings() { } Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(*Singleton::directories()), compiling(false) { - DEBUG("start"); + JDEBUG("start"); set_title("juCi++"); set_default_size(600, 400); set_events(Gdk::POINTER_MOTION_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::SCROLL_MASK); @@ -133,7 +133,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(*Singleton::director about.set_comments("This is an open source IDE with high-end features to make your programming experience juicy"); about.set_license_type(Gtk::License::LICENSE_MIT_X11); about.set_transient_for(*this); - DEBUG("end"); + JDEBUG("end"); } // Window constructor void Window::create_menu() { From f9269690cb31cd3b315cc8000d5c706d27be7311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Mon, 21 Sep 2015 19:56:04 +0200 Subject: [PATCH 06/16] Create enum for options --- src/dialogs_win.cc | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc index acf03bd..e6ab2fc 100644 --- a/src/dialogs_win.cc +++ b/src/dialogs_win.cc @@ -57,22 +57,31 @@ class CommonDialog { DWORD options; }; +enum FILEOPENOPTIONS { + OVERWRITEPROMPT = 0x2, STRICTFILETYPES = 0x4, NOCHANGEDIR = 0x8, PICKFOLDERS = 0x20, + FORCEFILESYSTEM = 0x40, ALLNONSTORAGEITEMS = 0x80, NOVALIDATE = 0x100, ALLOWMULTISELECT = 0x200, + PATHMUSTEXIST = 0x800, FILEMUSTEXIST = 0x1000, CREATEPROMPT = 0x2000, SHAREAWARE = 0x4000, + NOREADONLYRETURN = 0x8000, NOTESTFILECREATE = 0x10000, HIDEMRUPLACES = 0x20000, + HIDEPINNEDPLACES = 0x40000, NODEREFERENCELINKS = 0x100000, DONTADDTORECENT = 0x2000000, + FORCESHOWHIDDEN = 0x10000000, DEFAULTNOMINIMODE = 0x20000000, FORCEPREVIEWPANEON = 0x40000000 +}; + std::string Dialog::select_folder() { - return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); + return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); } std::string Dialog::new_file() { - return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); + return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); } std::string Dialog::new_folder() { - return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); + return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); } std::string Dialog::select_file() { - return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); + return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); } std::string Dialog::save_file() { - return (CommonDialog("Please select a folder", FOS_PICKFOLDERS)).show(); + return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); } \ No newline at end of file From 893ebe79bf501ada8e4e073bc1b671ce6ddb27ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Mon, 28 Sep 2015 22:42:55 +0200 Subject: [PATCH 07/16] Better fault toleranse when exitin dialogs unusually --- src/CMakeLists.txt | 6 ++-- src/dialogs.cc | 2 +- src/dialogs_win.cc | 77 ++++------------------------------------------ src/singletons.h | 6 ++-- src/window.cc | 26 +++++++++++----- 5 files changed, 33 insertions(+), 84 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7bf09cc..db08352 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,7 +88,6 @@ set(source_files juci.h tooltips.cc singletons.h singletons.cc - dialogs.h cmake.h cmake.cc) @@ -98,6 +97,7 @@ if(MSYS) message("MSYS detected") else() list(APPEND source_files terminal.cc) + list(APPEND source_files dialogs.h) list(APPEND source_files dialogs.cc) message("UNIX detected") endif() @@ -115,6 +115,7 @@ if(${validation}) ${GTKMM_INCLUDE_DIRS} ${GTKSVMM_INCLUDE_DIRS} ${LCL_INCLUDE_DIRS} + "C:/msys64/mingw64/include/dialogs" ${LIBCLANG_INCLUDE_DIRS} ${ASPELL_INCLUDE_DIR}) @@ -124,6 +125,7 @@ if(${validation}) ${Boost_LIBRARY_DIRS} # ${PYTHON_INCLUDE_DIRS} ${LCL_LIBRARY_DIRS} + C:/msys64/mingw64/bin ${LIBCLANG_LIBRARY_DIRS}) # set_target_properties(${module} @@ -132,11 +134,11 @@ if(${validation}) # target_link_libraries(${module} ${PYTHON_LIBRARIES} ${Boost_LIBRARIES}) # target_link_libraries(${module} ${Boost_LIBRARIES}) - target_link_libraries(${project_name} ${LIBCLANG_LIBRARIES} ${LCL_LIBRARIES} ${GTKMM_LIBRARIES} + "C:/msys64/mingw64/bin/libdialogs.dll" ${GTKSVMM_LIBRARIES} ${Boost_LIBRARIES} ${ASPELL_LIBRARIES} diff --git a/src/dialogs.cc b/src/dialogs.cc index 8ff3882..e1e424f 100644 --- a/src/dialogs.cc +++ b/src/dialogs.cc @@ -1,4 +1,4 @@ -#include "dialogs.h" +#include #include "singletons.h" #include #include diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc index e6ab2fc..24133b1 100644 --- a/src/dialogs_win.cc +++ b/src/dialogs_win.cc @@ -1,87 +1,22 @@ #include "dialogs.h" -#include -#include -#include -#include -#include "singletons.h" -#include - -#ifndef check -#define MESSAGE "An error occurred when trying open windows dialog" -HRESULT __hr__; -#define check(__fun__) __hr__ = __fun__; if(FAILED(__hr__)) Singleton::terminal()->print(MESSAGE) -#endif - -class win_string { - public: - win_string() : str(nullptr) { } - ~win_string() { CoTaskMemFree(static_cast(str)); } - std::string operator()(){ - std::string res; - if (str != nullptr) { - std::wstringstream ss; - ss << str; - res = std::string(ss.str().begin(), ss.str().end()); - } - return res; - } - wchar_t** operator&() { return &str; } - private: - wchar_t* str; -}; - -class CommonDialog { - public: - CommonDialog() : dialog(nullptr) { - check(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog))); - check(dialog->GetOptions(&options)); - } - CommonDialog(const std::string &title, unsigned option) : CommonDialog() { - set_title(title); - add_option(option); - } - void set_title(const std::string &title) { check(dialog->SetTitle(title)); } - void add_option(unsigned option) { check(dialog->SetOptions(options | option)); } - std::string show() { - check(dialog->Show(nullptr)); - IShellItem *result = nullptr; - check(dialog->GetResult(&result)); - win_string str; - check(result->GetDisplayName(SIGDN_FILESYSPATH, &str));; - result->Release(); - return str(); - } - - private: - IFileOpenDialog * dialog; - DWORD options; -}; - -enum FILEOPENOPTIONS { - OVERWRITEPROMPT = 0x2, STRICTFILETYPES = 0x4, NOCHANGEDIR = 0x8, PICKFOLDERS = 0x20, - FORCEFILESYSTEM = 0x40, ALLNONSTORAGEITEMS = 0x80, NOVALIDATE = 0x100, ALLOWMULTISELECT = 0x200, - PATHMUSTEXIST = 0x800, FILEMUSTEXIST = 0x1000, CREATEPROMPT = 0x2000, SHAREAWARE = 0x4000, - NOREADONLYRETURN = 0x8000, NOTESTFILECREATE = 0x10000, HIDEMRUPLACES = 0x20000, - HIDEPINNEDPLACES = 0x40000, NODEREFERENCELINKS = 0x100000, DONTADDTORECENT = 0x2000000, - FORCESHOWHIDDEN = 0x10000000, DEFAULTNOMINIMODE = 0x20000000, FORCEPREVIEWPANEON = 0x40000000 -}; +#include std::string Dialog::select_folder() { - return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); + return c_select_folder(); } std::string Dialog::new_file() { - return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); + return c_new_file(); } std::string Dialog::new_folder() { - return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); + return c_new_folder(); } std::string Dialog::select_file() { - return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); + return c_select_file(); } std::string Dialog::save_file() { - return (CommonDialog("Please select a folder", PICKFOLDERS)).show(); + return c_save_file(); } \ No newline at end of file diff --git a/src/singletons.h b/src/singletons.h index 33297ea..f4fc2be 100644 --- a/src/singletons.h +++ b/src/singletons.h @@ -26,9 +26,9 @@ public: static std::unique_ptr directories_; static std::unique_ptr terminal_; }; - static std::string config_dir() { return std::string(getenv("HOME")) + "/.juci/config/"; } - static std::string log_dir() { return std::string(getenv("HOME")) + "/.juci/log/"; } - static std::string style_dir() { return std::string(getenv("HOME")) + "/.juci/styles/"; } + static std::string config_dir() { return std::string(getenv("AppData")) + "/.juci/config/"; } + static std::string log_dir() { return std::string(getenv("AppData")) + "/.juci/log/"; } + static std::string style_dir() { return std::string(getenv("AppData")) + "/.juci/styles/"; } static Terminal *terminal(); static Directories *directories(); static Gtk::Label *status(); diff --git a/src/window.cc b/src/window.cc index 8ac72a6..7eeef82 100644 --- a/src/window.cc +++ b/src/window.cc @@ -38,6 +38,10 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(*Singleton::director set_events(Gdk::POINTER_MOTION_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::SCROLL_MASK); add(box); + auto i = Gtk::IconTheme::get_default(); + for (auto &c : i->get_search_path()) + Singleton::terminal()->print(c + "\n"); + generate_keybindings(); //PluginApi(&this->notebook, &this->menu); create_menu(); @@ -161,13 +165,17 @@ void Window::create_menu() { menu.action_group->add(Gtk::Action::create("FileNewFolder", "New Folder"), Gtk::AccelKey(menu.key_map["new_folder"]), [this]() { auto time_now=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); boost::filesystem::path path = Dialog::new_folder(); - if(boost::filesystem::last_write_time(path)>=time_now) { - if(Singleton::directories()->current_path!="") - Singleton::directories()->update(); - Singleton::terminal()->print("New folder "+path.string()+" created.\n"); + if(boost::filesystem::exists(path)) { + if(boost::filesystem::last_write_time(path)>=time_now) { + if(Singleton::directories()->current_path!="") + Singleton::directories()->update(); + Singleton::terminal()->print("New folder "+path.string()+" created.\n"); + } + else + Singleton::terminal()->print("Error: "+path.string()+" already exists.\n"); + } else { + Singleton::terminal()->print("Cancel \n"); } - else - Singleton::terminal()->print("Error: "+path.string()+" already exists.\n"); Singleton::directories()->select(path); }); menu.action_group->add(Gtk::Action::create("FileNewProject", "New Project")); @@ -204,7 +212,11 @@ void Window::create_menu() { notebook.open(Dialog::select_file()); }); menu.action_group->add(Gtk::Action::create("FileOpenFolder", "Open Folder"), Gtk::AccelKey(menu.key_map["open_folder"]), [this]() { - Singleton::directories()->open(Dialog::select_folder()); + auto path = Dialog::select_folder(); + if (boost::filesystem::exists(path)) + Singleton::directories()->open(path); + else + Singleton::terminal()->print("Cancel \n"); }); menu.action_group->add(Gtk::Action::create("FileSaveAs", "Save As"), Gtk::AccelKey(menu.key_map["save_as"]), [this]() { auto path = Dialog::save_file(); From 90ab171054db4383143c1a606b7c659bbcba5f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Thu, 8 Oct 2015 15:09:11 +0200 Subject: [PATCH 08/16] More dynamic cmakelists and guarding of windows code --- src/CMakeLists.txt | 12 ++++++++++-- src/dialogs_win.cc | 4 +++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 82e9cfe..aa08c91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,6 +70,7 @@ set(source_files juci.h sourcefile.cc window.cc window.h + dialogs.h # api.h # api.cc notebook.cc @@ -88,11 +89,16 @@ set(source_files juci.h if(MSYS) list(APPEND source_files terminal_win.cc) - list(APPEND source_files dialogs_win.cc) + list(APPEND source_files dialogs_win.cc) + set(DIALOGS_INCLUDE_DIRS "../WIN-packages/") + # if (64 bit) + set(DIALOGS_LIBRARY_DIRS "../WIN-packages/x64/libdialogs.dll") + # else + # set(DIALOGS_LIBRARY_DIRS "../WIN-packages/x32/libdialogs.dll") + #TODO figure out how to do this.... message("MSYS detected") else() list(APPEND source_files terminal.cc) - list(APPEND source_files dialogs.h) list(APPEND source_files dialogs.cc) message("UNIX detected") endif() @@ -110,6 +116,7 @@ if(${validation}) ${GTKMM_INCLUDE_DIRS} ${GTKSVMM_INCLUDE_DIRS} ${LCL_INCLUDE_DIRS} + ${DIALOGS_INCLUDE_DIRS} ${LIBCLANG_INCLUDE_DIRS} ${ASPELL_INCLUDE_DIR}) @@ -134,6 +141,7 @@ if(${validation}) ${GTKSVMM_LIBRARIES} ${Boost_LIBRARIES} ${ASPELL_LIBRARIES} + ${DIALOGS_LIBRARIES} # ${PYTHON_LIBRARIES} ) diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc index 24133b1..dd3ed51 100644 --- a/src/dialogs_win.cc +++ b/src/dialogs_win.cc @@ -1,3 +1,4 @@ +#ifdef _WIN32 #include "dialogs.h" #include @@ -19,4 +20,5 @@ std::string Dialog::select_file() { std::string Dialog::save_file() { return c_save_file(); -} \ No newline at end of file +} +#endif \ No newline at end of file From ec7f35e392ca35b874aa7d6137482797be88bd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Thu, 8 Oct 2015 15:33:51 +0200 Subject: [PATCH 09/16] Add precompiled libraries --- WIN-packages/x64/libdialogs.dll | Bin 0 -> 74752 bytes WIN-packages/x86/libdialogs.dll | Bin 0 -> 26624 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 WIN-packages/x64/libdialogs.dll create mode 100644 WIN-packages/x86/libdialogs.dll diff --git a/WIN-packages/x64/libdialogs.dll b/WIN-packages/x64/libdialogs.dll new file mode 100644 index 0000000000000000000000000000000000000000..79bce1bbfa13b57360df67271a6cf37b0e96dc6e GIT binary patch literal 74752 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 W$Enj@AyxoltZGcc;^%+wf&T)2c1O?v literal 0 HcmV?d00001 diff --git a/WIN-packages/x86/libdialogs.dll b/WIN-packages/x86/libdialogs.dll new file mode 100644 index 0000000000000000000000000000000000000000..0ecb1c9ac7ce7e41d581a88b9287f82a27d841d4 GIT binary patch literal 26624 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 P0sR<1MFhhC;OqYY#$R{s literal 0 HcmV?d00001 From f15720112eb64524bfd64c27f8f5b5ad074a9411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Thu, 8 Oct 2015 15:42:29 +0200 Subject: [PATCH 10/16] Add header for dlls --- WIN-packages/dialogs_win.h | 11 +++++++++++ src/dialogs_win.cc | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 WIN-packages/dialogs_win.h diff --git a/WIN-packages/dialogs_win.h b/WIN-packages/dialogs_win.h new file mode 100644 index 0000000..37a1d37 --- /dev/null +++ b/WIN-packages/dialogs_win.h @@ -0,0 +1,11 @@ +extern "C" { +#ifndef JUCI_C_DIALOGS +#define JUCI_C_DIALOGS + __declspec(dllimport) const char* c_select_folder(); + __declspec(dllimport) const char* c_select_file(); + __declspec(dllimport) const char* c_new_file(); + __declspec(dllimport) const char* c_new_folder(); + __declspec(dllimport) const char* c_save_file(); +#endif // JUCI_C_DIALOGS +}; + diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc index dd3ed51..e6891ee 100644 --- a/src/dialogs_win.cc +++ b/src/dialogs_win.cc @@ -1,6 +1,6 @@ #ifdef _WIN32 #include "dialogs.h" -#include +#include std::string Dialog::select_folder() { return c_select_folder(); @@ -21,4 +21,4 @@ std::string Dialog::select_file() { std::string Dialog::save_file() { return c_save_file(); } -#endif \ No newline at end of file +#endif From 97034b43b6535fd43653ae2675d4e608e46bdd5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Thu, 8 Oct 2015 16:05:03 +0200 Subject: [PATCH 11/16] Tested on windows and debian --- src/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aa08c91..a3d2b33 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,11 +90,12 @@ set(source_files juci.h if(MSYS) list(APPEND source_files terminal_win.cc) list(APPEND source_files dialogs_win.cc) - set(DIALOGS_INCLUDE_DIRS "../WIN-packages/") + set(DIALOGS_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/WIN-packages/") # if (64 bit) - set(DIALOGS_LIBRARY_DIRS "../WIN-packages/x64/libdialogs.dll") + set(DIALOGS_LIBRARIES "${CMAKE_SOURCE_DIR}/WIN-packages/x64/libdialogs.dll") + message(${DIALOGS_LIBRARIES}) # else - # set(DIALOGS_LIBRARY_DIRS "../WIN-packages/x32/libdialogs.dll") + # set(DIALOGS_LIBRARIES "${CMAKE_SOURCE_DIR}/WIN-packages/x32/libdialogs.dll") #TODO figure out how to do this.... message("MSYS detected") else() From 7dd4f9783c967f16ace8ecf4c0e1c44edcaadc79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Thu, 8 Oct 2015 16:10:31 +0200 Subject: [PATCH 12/16] Use reference instead of copy --- src/dialogs.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dialogs.cc b/src/dialogs.cc index 8ff3882..8009e02 100644 --- a/src/dialogs.cc +++ b/src/dialogs.cc @@ -4,7 +4,7 @@ #include std::string open_dialog(const std::string &title, - const std::vector> buttons, + const std::vector> &buttons, Gtk::FileChooserAction gtk_options) { Gtk::FileChooserDialog dialog(title, gtk_options); if(Singleton::directories()->current_path!="") @@ -47,4 +47,5 @@ std::string Dialog::save_file() { return open_dialog("Please choose a file", {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Save", Gtk::RESPONSE_OK)}, Gtk::FILE_CHOOSER_ACTION_OPEN); -} \ No newline at end of file +} + From bf92d588c473689a6f9baae8b2b0fa5fbccac470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Fri, 16 Oct 2015 13:06:14 +0200 Subject: [PATCH 13/16] Remove directories from Notebook --- src/notebook.cc | 4 +++- src/notebook.h | 3 +-- src/window.cc | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/notebook.cc b/src/notebook.cc index c22c448..9299009 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -27,7 +27,7 @@ namespace sigc { #endif } -Notebook::Notebook(Directories &directories) : Gtk::Notebook(), directories(directories) { +Notebook::Notebook() : Gtk::Notebook() { Gsv::init(); } @@ -71,6 +71,7 @@ void Notebook::open(const boost::filesystem::path &file_path) { auto language=Source::guess_language(file_path); if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { boost::filesystem::path project_path; + auto directories = *Singleton::directories(); if(directories.cmake && directories.cmake->project_path!="" && file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/') { project_path=directories.cmake->project_path; if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json")) @@ -180,6 +181,7 @@ bool Notebook::save(int page, bool reparse_needed) { //If CMakeLists.txt have been modified: //TODO: recreate cmake even without directories open? if(view->file_path.filename()=="CMakeLists.txt") { + auto directories = *Singleton::directories(); if(directories.cmake && directories.cmake->project_path!="" && view->file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/' && CMake::create_compile_commands(directories.cmake->project_path)) { for(auto source_view: source_views) { if(auto source_clang_view=dynamic_cast(source_view)) { diff --git a/src/notebook.h b/src/notebook.h index b8f6b69..3af9887 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -12,7 +12,7 @@ class Notebook : public Gtk::Notebook { public: - Notebook(Directories &directories); + Notebook(); Source::View* get_view(int page); size_t get_index(int page); int size(); @@ -25,7 +25,6 @@ public: private: bool save_modified_dialog(); - Directories &directories; std::vector source_views; //Is NOT freed in destructor, this is intended for quick program exit. std::vector > source_maps; std::vector > scrolled_windows; diff --git a/src/window.cc b/src/window.cc index 55da6cd..880a3d8 100644 --- a/src/window.cc +++ b/src/window.cc @@ -30,7 +30,7 @@ void Window::generate_keybindings() { } } -Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(*Singleton::directories()), compiling(false) { +Window::Window() : box(Gtk::ORIENTATION_VERTICAL), compiling(false) { JDEBUG("start"); set_title("juCi++"); set_events(Gdk::POINTER_MOTION_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::SCROLL_MASK); From 140ae6a821beb87f6e93836ceb2627a6a304e329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Lien=20Sell=C3=A6g?= Date: Fri, 16 Oct 2015 13:08:22 +0200 Subject: [PATCH 14/16] Fix indention and error message --- src/window.cc | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/window.cc b/src/window.cc index 880a3d8..851fac5 100644 --- a/src/window.cc +++ b/src/window.cc @@ -195,7 +195,7 @@ void Window::create_menu() { else Singleton::terminal()->print("Error: "+path.string()+" already exists.\n"); } else { - Singleton::terminal()->print("Cancel \n"); + Singleton::terminal()->print("Dialog was closed \n"); } Singleton::directories()->select(path); }); @@ -237,24 +237,24 @@ void Window::create_menu() { if (boost::filesystem::exists(path)) Singleton::directories()->open(path); else - Singleton::terminal()->print("Cancel \n"); + Singleton::terminal()->print("Dialog was closed \n"); }); menu.action_group->add(Gtk::Action::create("FileSaveAs", "Save As"), Gtk::AccelKey(menu.key_map["save_as"]), [this]() { - auto path = Dialog::save_file(); - if(path.size()>0) { - std::ofstream file(path); - if(file) { - file << notebook.get_current_view()->get_buffer()->get_text(); - file.close(); - if(Singleton::directories()->current_path!="") - Singleton::directories()->update(); - notebook.open(path); - Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path.string()+"\n"); - } - else - Singleton::terminal()->print("Error saving file\n"); + auto path = Dialog::save_file(); + if(path.size()>0) { + std::ofstream file(path); + if(file) { + file << notebook.get_current_view()->get_buffer()->get_text(); + file.close(); + if(Singleton::directories()->current_path!="") + Singleton::directories()->update(); + notebook.open(path); + Singleton::terminal()->print("File saved to: " + notebook.get_current_view()->file_path.string()+"\n"); } - }); + else + Singleton::terminal()->print("Error saving file\n"); + } +}); menu.action_group->add(Gtk::Action::create("Preferences", "Preferences..."), Gtk::AccelKey(menu.key_map["preferences"]), [this]() { notebook.open(Singleton::config_dir()+"config.json"); }); From 9fc60c10722489e50230490b0116ef4356c12d84 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sat, 17 Oct 2015 10:13:08 +0200 Subject: [PATCH 15/16] Minor fixes to #57. --- WIN-packages/README.md | 1 + src/CMakeLists.txt | 6 ++---- src/dialogs.cc | 2 +- src/notebook.cc | 12 ++++++------ src/window.cc | 12 +++++------- 5 files changed, 15 insertions(+), 18 deletions(-) create mode 100644 WIN-packages/README.md diff --git a/WIN-packages/README.md b/WIN-packages/README.md new file mode 100644 index 0000000..11b5099 --- /dev/null +++ b/WIN-packages/README.md @@ -0,0 +1 @@ +Source code for the dll's can be found at https://github.com/cppit/dialogs \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a3d2b33..907b215 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -89,19 +89,17 @@ set(source_files juci.h if(MSYS) list(APPEND source_files terminal_win.cc) - list(APPEND source_files dialogs_win.cc) - set(DIALOGS_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/WIN-packages/") + list(APPEND source_files dialogs_win.cc) + set(DIALOGS_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/WIN-packages/") # if (64 bit) set(DIALOGS_LIBRARIES "${CMAKE_SOURCE_DIR}/WIN-packages/x64/libdialogs.dll") message(${DIALOGS_LIBRARIES}) # else # set(DIALOGS_LIBRARIES "${CMAKE_SOURCE_DIR}/WIN-packages/x32/libdialogs.dll") #TODO figure out how to do this.... - message("MSYS detected") else() list(APPEND source_files terminal.cc) list(APPEND source_files dialogs.cc) - message("UNIX detected") endif() if(${validation}) diff --git a/src/dialogs.cc b/src/dialogs.cc index 8009e02..e8588e5 100644 --- a/src/dialogs.cc +++ b/src/dialogs.cc @@ -46,6 +46,6 @@ std::string Dialog::select_file() { std::string Dialog::save_file() { return open_dialog("Please choose a file", {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Save", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_OPEN); + Gtk::FILE_CHOOSER_ACTION_SAVE); } diff --git a/src/notebook.cc b/src/notebook.cc index 9299009..ec4d2c5 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -71,9 +71,9 @@ void Notebook::open(const boost::filesystem::path &file_path) { auto language=Source::guess_language(file_path); if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { boost::filesystem::path project_path; - auto directories = *Singleton::directories(); - if(directories.cmake && directories.cmake->project_path!="" && file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/') { - project_path=directories.cmake->project_path; + auto directories = Singleton::directories(); + if(directories->cmake && directories->cmake->project_path!="" && file_path.generic_string().substr(0, directories->cmake->project_path.generic_string().size()+1)==directories->cmake->project_path.generic_string()+'/') { + project_path=directories->cmake->project_path; if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json")) CMake::create_compile_commands(project_path); } @@ -181,11 +181,11 @@ bool Notebook::save(int page, bool reparse_needed) { //If CMakeLists.txt have been modified: //TODO: recreate cmake even without directories open? if(view->file_path.filename()=="CMakeLists.txt") { - auto directories = *Singleton::directories(); - if(directories.cmake && directories.cmake->project_path!="" && view->file_path.generic_string().substr(0, directories.cmake->project_path.generic_string().size()+1)==directories.cmake->project_path.generic_string()+'/' && CMake::create_compile_commands(directories.cmake->project_path)) { + auto directories = Singleton::directories(); + if(directories->cmake && directories->cmake->project_path!="" && view->file_path.generic_string().substr(0, directories->cmake->project_path.generic_string().size()+1)==directories->cmake->project_path.generic_string()+'/' && CMake::create_compile_commands(directories->cmake->project_path)) { for(auto source_view: source_views) { if(auto source_clang_view=dynamic_cast(source_view)) { - if(directories.cmake->project_path.string()==source_clang_view->project_path) { + if(directories->cmake->project_path.string()==source_clang_view->project_path) { if(source_clang_view->restart_parse()) Singleton::terminal()->async_print("Reparsing "+source_clang_view->file_path.string()+"\n"); else diff --git a/src/window.cc b/src/window.cc index 851fac5..93add35 100644 --- a/src/window.cc +++ b/src/window.cc @@ -186,7 +186,7 @@ void Window::create_menu() { menu.action_group->add(Gtk::Action::create("FileNewFolder", "New Folder"), Gtk::AccelKey(menu.key_map["new_folder"]), [this]() { auto time_now=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); boost::filesystem::path path = Dialog::new_folder(); - if(boost::filesystem::exists(path)) { + if(path!="" && boost::filesystem::exists(path)) { if(boost::filesystem::last_write_time(path)>=time_now) { if(Singleton::directories()->current_path!="") Singleton::directories()->update(); @@ -194,14 +194,14 @@ void Window::create_menu() { } else Singleton::terminal()->print("Error: "+path.string()+" already exists.\n"); - } else { - Singleton::terminal()->print("Dialog was closed \n"); } Singleton::directories()->select(path); }); menu.action_group->add(Gtk::Action::create("FileNewProject", "New Project")); menu.action_group->add(Gtk::Action::create("FileNewProjectCpp", "C++"), [this]() { boost::filesystem::path project_path = Dialog::new_folder(); + if(project_path=="") + return; auto project_name=project_path.filename().string(); for(size_t c=0;cadd(Gtk::Action::create("FileOpenFolder", "Open Folder"), Gtk::AccelKey(menu.key_map["open_folder"]), [this]() { auto path = Dialog::select_folder(); - if (boost::filesystem::exists(path)) + if (path!="" && boost::filesystem::exists(path)) Singleton::directories()->open(path); - else - Singleton::terminal()->print("Dialog was closed \n"); }); menu.action_group->add(Gtk::Action::create("FileSaveAs", "Save As"), Gtk::AccelKey(menu.key_map["save_as"]), [this]() { auto path = Dialog::save_file(); - if(path.size()>0) { + if(path!="") { std::ofstream file(path); if(file) { file << notebook.get_current_view()->get_buffer()->get_text(); From 17e45b1e63b7aedc8b57796e8f767cb701493595 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sat, 17 Oct 2015 11:01:44 +0200 Subject: [PATCH 16/16] Resolved conflicts in #68. --- .gitmodules | 3 + README.md | 40 +- docs/install.md | 36 +- libclangmm | 1 + src/CMakeLists.txt | 118 ++-- src/cmake.cc | 6 +- src/config.cc | 9 + src/entrybox.cc | 6 +- src/entrybox.h | 2 +- src/files.h | 19 +- src/menu.cc | 10 +- src/notebook.cc | 58 +- src/notebook.h | 2 +- src/source.cc | 1366 +++++++------------------------------------ src/source.h | 171 ++---- src/source_clang.cc | 1236 +++++++++++++++++++++++++++++++++++++++ src/source_clang.h | 128 ++++ src/window.cc | 141 ++++- src/window.h | 2 +- 19 files changed, 1949 insertions(+), 1405 deletions(-) create mode 100644 .gitmodules create mode 160000 libclangmm create mode 100644 src/source_clang.cc create mode 100644 src/source_clang.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..59face8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libclangmm"] + path = libclangmm + url = https://github.com/eidheim/libclangmm.git diff --git a/README.md b/README.md index 14a0b36..b769f72 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,41 @@ # juCi++ -###### a lightweight C++-IDE with support for C++11 and C++14. +###### a lightweight platform independent C++-IDE with support for C++11 and C++14. ## About Current IDEs struggle with C++ support due to the complexity of the programming language. juCI++, however, is designed especially -towards libclang with speed in mind. +towards libclang with speed and ease of use in mind. ## Features -* Fast and responsive +* Platform independent +* Fast and responsive (written in C++) * Syntax highlighting (even C++11/14, and more than 100 other file types) * C++ warnings and errors on the fly -* Fast C++ autocomletion (even external libraries) +* C++ Fix-its +* Automated CMake processing +* Fast C++ autocomletion (including external libraries) +* Keyword and buffer autocomletion for other file types * Tooltips showing type information and doxygen documentation * Refactoring across files * Highlighting of similar types +* Documentation search * Spell checking depending on file context -* Basic editor functionality -* Write your own plugins in python (disabled at the moment) +* Run shell commands within JuCi++, even on Windows +* Regex search and replace +* Smart paste, keys and indentation +* Source minimap +* Full UTF-8 support + +See [enhancements](https://github.com/cppit/jucipp/labels/enhancement) for planned features. ## Dependencies ## -* libboost-filesystem-dev -* libboost-log-dev -* libboost-test-dev -* libboost-thread-dev -* libboost-system-dev -* libgtkmm-3.0-dev -* libgtksourceview2.0-dev -* libgtksourceviewmm-3.0-dev -* libaspell-dev -* libclang-dev +* boost-filesystem +* boost-log +* boost-thread +* boost-system +* gtkmm-3.0 +* gtksourceviewmm-3.0 +* aspell +* libclang * [libclangmm](http://github.com/cppit/libclangmm/) ## Installation ## diff --git a/docs/install.md b/docs/install.md index ca6052a..5877e17 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,13 +1,14 @@ -# juCi++ -## Installation guide ## -Before installation, please install libclangmm, see [installation guide](http://github.com/cppit/libclangmm/blob/master/docs/install.md). +# juCi++ Installation Guide ## Debian/Ubuntu 15 +Install dependencies: ```sh -sudo apt-get install pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev git +sudo apt-get install git cmake make g++ libclang-dev pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev ``` + +Get juCi++ source, compile and install: ```sh -git clone http://github.com/cppit/jucipp.git +git clone --recursive http://github.com/cppit/jucipp cd jucipp cmake . make @@ -15,15 +16,18 @@ sudo make install ``` ## Ubuntu 14/Linux Mint 17 +Install dependencies: ```sh sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install g++-4.9 sudo apt-get remove g++-4.8 -sudo apt-get install pkg-config libboost-system1.55-dev libboost-thread1.55-dev libboost-filesystem1.55-dev libboost-log1.55-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev git +sudo apt-get install git cmake make g++ libclang-3.6-dev pkg-config libboost-system1.55-dev libboost-thread1.55-dev libboost-filesystem1.55-dev libboost-log1.55-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev ``` + +Get juCi++ source, compile and install: ```sh -git clone http://github.com/cppit/jucipp.git +git clone --recursive http://github.com/cppit/jucipp cd jucipp cmake . make @@ -31,12 +35,14 @@ sudo make install ``` ## OS X with Homebrew (http://brew.sh/) +Install dependencies (installing llvm may take some time): ```sh -brew install pkg-config boost gtkmm3 homebrew/x11/gtksourceviewmm3 aspell git +brew install cmake --with-clang llvm pkg-config boost gtkmm3 homebrew/x11/gtksourceviewmm3 aspell ``` +Get juCi++ source, compile and install: ```sh -git clone https://github.com/cppit/jucipp.git +git clone --recursive https://github.com/cppit/jucipp cd jucipp cmake . make @@ -44,19 +50,15 @@ make install ``` ##Windows with MSYS2 (https://msys2.github.io/) -Install dependencies (replace x86_64 with i686 for 32-bit MSYS2 installs): +Install dependencies (replace `x86_64` with `i686` for 32-bit MSYS2 installs): ```sh -pacman -S patch autoconf automake-wrapper mingw-w64-x86_64-gtkmm3 mingw-w64-x86_64-gtksourceviewmm3 mingw-w64-x86_64-boost mingw-w64-x86_64-aspell mingw-w64-x86_64-aspell-en git +pacman -S git mingw-w64-x86_64-cmake make mingw-w64-x86_64-toolchain mingw-w64-x86_64-clang mingw-w64-x86_64-gtkmm3 mingw-w64-x86_64-gtksourceviewmm3 mingw-w64-x86_64-boost mingw-w64-x86_64-aspell mingw-w64-x86_64-aspell-en ``` -Get juCi++ source: +Get juCi++ source, compile and install (replace `mingw64` with `mingw32` for 32-bit MSYS2 installs): ```sh -git clone https://github.com/cppit/jucipp.git +git clone --recursive https://github.com/cppit/jucipp cd jucipp -``` - -Compile and install juCi++ source (replace mingw64 with mingw32 for 32-bit MSYS2 installs): -```sh cmake -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=/mingw64 . make make install diff --git a/libclangmm b/libclangmm new file mode 160000 index 0000000..e060dec --- /dev/null +++ b/libclangmm @@ -0,0 +1 @@ +Subproject commit e060dec32bb8306eff6f9114092cdbefe8fea5fb diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 907b215..a79cfe7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,47 +12,16 @@ endif() INCLUDE(FindPkgConfig) -set(validation true) - -function(install_help APPLE UNIX WINDOWS) - message("Install package with:") - if(UNIX) - if(APPLE) - message("brew install ${APPLE}") - else() - message("sudo apt-get install ${UNIX}") - endif(APPLE) - endif(UNIX) - if(WINDOWS) - #message("choco install ${WINDOWS}") #Removed this for the time being - endif(WINDOWS) -endfunction(install_help) - -function(validate FOUND APPLE UNIX WINDOWS) - if(!${FOUND}) - set(validation false) - install_help(${APPLE} ${UNIX} ${WINDOWS}) - endif() -endfunction(validate) - -find_package(LibClangmm) -validate(${LCL_FOUND} "clangmm" "clangmm" "clangmm") - -find_package(LibClang) -validate(${LIBCLANG_FOUND} "clang" "libclang-dev" "llvm") +find_package(LibClang REQUIRED) #find_package(PythonLibs 2.7) -#validate(${PYTHONLIBS_FOUND} "python" "libpython-dev" "python") #find_package(Boost 1.55 COMPONENTS python thread log system filesystem REQUIRED) find_package(Boost 1.55 COMPONENTS thread log system filesystem REQUIRED) -validate(${Boost_FOUND} "boost" "libboost-all-dev" "boost") -pkg_check_modules(GTKMM gtkmm-3.0) # The name GTKMM is set here for the variables abouve -validate(${GTKMM_FOUND} "gtkmm" "libgtkmm-dev" "gtkmm") +pkg_check_modules(GTKMM gtkmm-3.0 REQUIRED) # The name GTKMM is set here for the variables abouve -pkg_check_modules(GTKSVMM gtksourceviewmm-3.0) -validate(${GTKSVMM_FOUND} "gtksvmm" "libgtksvmm-dev" "gtkmmsv") +pkg_check_modules(GTKSVMM gtksourceviewmm-3.0 REQUIRED) find_package(ASPELL REQUIRED) @@ -62,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 @@ -85,7 +56,22 @@ set(source_files juci.h singletons.h singletons.cc cmake.h - cmake.cc) + cmake.cc + + ../libclangmm/src/CodeCompleteResults.cc + ../libclangmm/src/CompilationDatabase.cc + ../libclangmm/src/CompileCommand.cc + ../libclangmm/src/CompileCommands.cc + ../libclangmm/src/CompletionString.cc + ../libclangmm/src/Cursor.cc + ../libclangmm/src/Index.cc + ../libclangmm/src/SourceLocation.cc + ../libclangmm/src/SourceRange.cc + ../libclangmm/src/Token.cc + ../libclangmm/src/Tokens.cc + ../libclangmm/src/TranslationUnit.cc + ../libclangmm/src/Diagnostic.cc + ../libclangmm/src/Utility.cc) if(MSYS) list(APPEND source_files terminal_win.cc) @@ -102,30 +88,30 @@ else() list(APPEND source_files dialogs.cc) endif() -if(${validation}) - add_executable(${project_name} ${source_files}) - +add_executable(${project_name} ${source_files}) + # add_library(${module} SHARED # api # api_ext) - include_directories( - ${Boost_INCLUDE_DIRS} +include_directories( + ${Boost_INCLUDE_DIRS} # ${PYTHON_INCLUDE_DIRS} - ${GTKMM_INCLUDE_DIRS} - ${GTKSVMM_INCLUDE_DIRS} - ${LCL_INCLUDE_DIRS} - ${DIALOGS_INCLUDE_DIRS} - ${LIBCLANG_INCLUDE_DIRS} - ${ASPELL_INCLUDE_DIR}) - - link_directories( - ${GTKMM_LIBRARY_DIRS} - ${GTKSVMM_LIBRARY_DIRS} - ${Boost_LIBRARY_DIRS} + ${GTKMM_INCLUDE_DIRS} + ${GTKSVMM_INCLUDE_DIRS} + ${LIBCLANG_INCLUDE_DIRS} + ${ASPELL_INCLUDE_DIR} + ${DIALOGS_INCLUDE_DIRS} + ../libclangmm/src +) + +link_directories( + ${GTKMM_LIBRARY_DIRS} + ${GTKSVMM_LIBRARY_DIRS} + ${Boost_LIBRARY_DIRS} # ${PYTHON_INCLUDE_DIRS} - ${LCL_LIBRARY_DIRS} - ${LIBCLANG_LIBRARY_DIRS}) + ${LIBCLANG_LIBRARY_DIRS} +) # set_target_properties(${module} # PROPERTIES PREFIX "" @@ -133,21 +119,19 @@ if(${validation}) # target_link_libraries(${module} ${PYTHON_LIBRARIES} ${Boost_LIBRARIES}) # target_link_libraries(${module} ${Boost_LIBRARIES}) - target_link_libraries(${project_name} - ${LIBCLANG_LIBRARIES} - ${LCL_LIBRARIES} - ${GTKMM_LIBRARIES} - ${GTKSVMM_LIBRARIES} - ${Boost_LIBRARIES} - ${ASPELL_LIBRARIES} - ${DIALOGS_LIBRARIES} + +target_link_libraries(${project_name} + ${LIBCLANG_LIBRARIES} + ${GTKMM_LIBRARIES} + ${GTKSVMM_LIBRARIES} + ${Boost_LIBRARIES} + ${ASPELL_LIBRARIES} + ${DIALOGS_LIBRARIES} # ${PYTHON_LIBRARIES} - ) +) # install(TARGETS ${project_name} ${module} - install(TARGETS ${project_name} - RUNTIME DESTINATION bin +install(TARGETS ${project_name} + RUNTIME DESTINATION bin # LIBRARY DESTINATION ${lib_install_path} - ) -endif(${validation}) - +) diff --git a/src/cmake.cc b/src/cmake.cc index d112a26..76b4aa0 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -142,11 +142,11 @@ void CMake::find_variables() { const std::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$"); std::smatch sm; if(std::regex_match(line, sm, set_regex)) { - std::string data=sm[2]; + auto data=sm[2].str(); while(data.size()>0 && data.back()==' ') data.pop_back(); parse_variable_parameters(data); - variables[sm[1]]=data; + variables[sm[1].str()]=data; } } pos=end_line+1; @@ -264,7 +264,7 @@ std::vector > > CMak const std::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$"); std::smatch sm; if(std::regex_match(line, sm, function_regex)) { - std::string data=sm[1]; + auto data=sm[1].str(); while(data.size()>0 && data.back()==' ') data.pop_back(); auto parameters=get_function_parameters(data); diff --git a/src/config.cc b/src/config.cc index ca52e43..6b98535 100644 --- a/src/config.cc +++ b/src/config.cc @@ -134,6 +134,15 @@ void MainConfig::GenerateSource() { for (auto &i : source_json.get_child("clang_types")) source_cfg->clang_types[i.first] = i.second.get_value(); + + auto pt_doc_search=cfg.get_child("documentation_searches"); + for(auto &pt_doc_search_lang: pt_doc_search) { + source_cfg->documentation_searches[pt_doc_search_lang.first].separator=pt_doc_search_lang.second.get("separator"); + auto &queries=source_cfg->documentation_searches.find(pt_doc_search_lang.first)->second.queries; + for(auto &i: pt_doc_search_lang.second.get_child("queries")) { + queries[i.first]=i.second.get_value(); + } + } } void MainConfig::GenerateDirectoryFilter() { diff --git a/src/entrybox.cc b/src/entrybox.cc index a8bbb85..08a3a5e 100644 --- a/src/entrybox.cc +++ b/src/entrybox.cc @@ -16,8 +16,9 @@ namespace sigc { std::unordered_map > EntryBox::entry_histories; -EntryBox::Entry::Entry(const std::string& content, std::function on_activate, unsigned length) : Gtk::Entry(), on_activate(on_activate) { - set_max_length(length); +EntryBox::Entry::Entry(const std::string& content, std::function on_activate, unsigned width_chars) : Gtk::Entry(), on_activate(on_activate) { + set_max_length(0); + set_width_chars(width_chars); set_text(content); selected_history=0; signal_activate().connect([this](){ @@ -92,7 +93,6 @@ void EntryBox::clear() { void EntryBox::show() { std::vector focus_chain; for(auto& entry: entries) { - entry.set_max_length(0); lower_box.pack_start(entry, Gtk::PACK_SHRINK); focus_chain.emplace_back(&entry); } diff --git a/src/entrybox.h b/src/entrybox.h index e7bdebd..6f8cecf 100644 --- a/src/entrybox.h +++ b/src/entrybox.h @@ -12,7 +12,7 @@ class EntryBox : public Gtk::Box { public: class Entry : public Gtk::Entry { public: - Entry(const std::string& content="", std::function on_activate=nullptr, unsigned length=50); + Entry(const std::string& content="", std::function on_activate=nullptr, unsigned width_chars=-1); std::function on_activate; private: size_t selected_history; diff --git a/src/files.h b/src/files.h index 5c9b436..8f49573 100644 --- a/src/files.h +++ b/src/files.h @@ -1,6 +1,6 @@ #include -#define JUCI_VERSION "0.9.2" +#define JUCI_VERSION "0.9.3" const std::string configjson = "{\n" @@ -65,15 +65,20 @@ const std::string configjson = " \"edit_undo\": \"z\",\n" " \"edit_redo\": \"z\",\n" " \"edit_find\": \"f\",\n" +" \"edit_set_tab\": \"\",\n" " \"source_spellcheck\": \"\",\n" " \"source_spellcheck_clear\": \"\",\n" " \"source_spellcheck_next_error\": \"e\",\n" +" \"source_indentation_set_buffer_tab\": \"\",\n" +" \"source_indentation_auto_indent_buffer\": \"\",\n" " \"source_goto_line\": \"g\",\n" " \"source_center_cursor\": \"l\",\n" " \"source_goto_declaration\": \"d\",\n" " \"source_goto_method\": \"m\",\n" " \"source_rename\": \"r\",\n" +" \"source_find_documentation\": \"d\",\n" " \"source_goto_next_diagnostic\": \"e\",\n" +" \"source_apply_fix_its\": \"space\",\n" " \"compile_and_run\": \"Return\",\n" " \"compile\": \"Return\",\n" " \"run_command\": \"Return\",\n" @@ -91,6 +96,18 @@ const std::string configjson = #endif " \"make_command\": \"make\"\n" " },\n" +" \"documentation_searches\": {\n" +" \"clang\": {\n" +" \"separator\": \"::\",\n" +" \"queries\": {\n" +" \"@empty\": \"https://www.google.com/search?btnI&q=c%2B%2B+\",\n" +" \"std\": \"https://www.google.com/search?btnI&q=site:http://www.cplusplus.com/reference/+\",\n" +" \"boost\": \"https://www.google.com/search?btnI&q=site:http://www.boost.org/doc/libs/1_59_0/+\",\n" +" \"Gtk\": \"https://www.google.com/search?btnI&q=site:https://developer.gnome.org/gtkmm/stable/+\",\n" +" \"@any\": \"https://www.google.com/search?btnI&q=\"\n" +" }\n" +" }\n" +" },\n" " \"directoryfilter\": {\n" " \"ignore\": [\n" " ],\n" diff --git a/src/menu.cc b/src/menu.cc index 5ae64be..ce9b287 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -49,16 +49,24 @@ Menu::Menu() { " \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" diff --git a/src/notebook.cc b/src/notebook.cc index ec4d2c5..4cf8d7d 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -69,28 +69,25 @@ void Notebook::open(const boost::filesystem::path &file_path) { can_read.close(); auto language=Source::guess_language(file_path); - if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { - boost::filesystem::path project_path; - auto directories = Singleton::directories(); - if(directories->cmake && directories->cmake->project_path!="" && file_path.generic_string().substr(0, directories->cmake->project_path.generic_string().size()+1)==directories->cmake->project_path.generic_string()+'/') { - project_path=directories->cmake->project_path; - if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json")) - CMake::create_compile_commands(project_path); - } - else { - project_path=file_path.parent_path(); - CMake cmake(project_path); - if(cmake.project_path!="") { - project_path=cmake.project_path; - Singleton::terminal()->print("Project path for "+file_path.string()+" set to "+project_path.string()+"\n"); - } - else - Singleton::terminal()->print("Error: could not find project path for "+file_path.string()+"\n"); + boost::filesystem::path project_path; + auto directories=Singleton::directories(); + if(directories->cmake && directories->cmake->project_path!="" && file_path.generic_string().substr(0, directories->cmake->project_path.generic_string().size()+1)==directories->cmake->project_path.generic_string()+'/') + project_path=directories->cmake->project_path; + else { + project_path=file_path.parent_path(); + CMake cmake(project_path); + if(cmake.project_path!="") { + project_path=cmake.project_path; + Singleton::terminal()->print("Project path for "+file_path.string()+" set to "+project_path.string()+"\n"); } + } + if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) { + if(boost::filesystem::exists(project_path.string()+"/CMakeLists.txt") && !boost::filesystem::exists(project_path.string()+"/compile_commands.json")) + CMake::create_compile_commands(project_path); source_views.emplace_back(new Source::ClangView(file_path, project_path, language)); } else - source_views.emplace_back(new Source::GenericView(file_path, language)); + source_views.emplace_back(new Source::GenericView(file_path, project_path, language)); source_views.back()->on_update_status=[this](Source::View* view, const std::string &status) { if(get_current_page()!=-1 && get_current_view()==view) @@ -166,10 +163,12 @@ bool Notebook::save(int page, bool reparse_needed) { if(juci::filesystem::write(view->file_path, view->get_buffer())) { if(reparse_needed) { if(auto clang_view=dynamic_cast(view)) { - for(auto a_view: source_views) { - if(auto a_clang_view=dynamic_cast(a_view)) { - if(clang_view!=a_clang_view) - a_clang_view->reparse_needed=true; + if(clang_view->language->get_id()=="chdr" || clang_view->language->get_id()=="cpphdr") { + for(auto a_view: source_views) { + if(auto a_clang_view=dynamic_cast(a_view)) { + if(clang_view!=a_clang_view) + a_clang_view->reparse_needed=true; + } } } } @@ -179,13 +178,22 @@ bool Notebook::save(int page, bool reparse_needed) { Singleton::terminal()->print("File saved to: " +view->file_path.string()+"\n"); //If CMakeLists.txt have been modified: - //TODO: recreate cmake even without directories open? + boost::filesystem::path project_path; if(view->file_path.filename()=="CMakeLists.txt") { - auto directories = Singleton::directories(); + auto directories=Singleton::directories(); if(directories->cmake && directories->cmake->project_path!="" && view->file_path.generic_string().substr(0, directories->cmake->project_path.generic_string().size()+1)==directories->cmake->project_path.generic_string()+'/' && CMake::create_compile_commands(directories->cmake->project_path)) { + project_path=directories->cmake->project_path; + } + else { + CMake cmake(view->file_path.parent_path()); + if(cmake.project_path!="" && CMake::create_compile_commands(cmake.project_path)) { + project_path=cmake.project_path; + } + } + if(project_path!="") { for(auto source_view: source_views) { if(auto source_clang_view=dynamic_cast(source_view)) { - if(directories->cmake->project_path.string()==source_clang_view->project_path) { + if(project_path==source_clang_view->project_path) { if(source_clang_view->restart_parse()) Singleton::terminal()->async_print("Reparsing "+source_clang_view->file_path.string()+"\n"); else diff --git a/src/notebook.h b/src/notebook.h index 3af9887..12388ed 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 40257eb..154232f 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" @@ -42,14 +41,49 @@ Glib::RefPtr Source::guess_language(const boost::filesystem::path return language; } +Source::FixIt::FixIt(const std::string &source, const std::pair &offsets) : source(source), offsets(offsets) { + if(source.size()==0) + type=Type::ERASE; + else { + if(this->offsets.first==this->offsets.second) + type=Type::INSERT; + else + type=Type::REPLACE; + } +} + +std::string Source::FixIt::string(Glib::RefPtr buffer) { + auto iter=buffer->get_iter_at_line_index(offsets.first.line-1, offsets.first.index-1); + unsigned first_line_offset=iter.get_line_offset()+1; + iter=buffer->get_iter_at_line_index(offsets.second.line-1, offsets.second.index-1); + unsigned second_line_offset=iter.get_line_offset()+1; + + std::string text; + if(type==Type::INSERT) { + text+="Insert "+source+" at "; + text+=std::to_string(offsets.first.line)+":"+std::to_string(first_line_offset); + } + else if(type==Type::REPLACE) { + text+="Replace "; + text+=std::to_string(offsets.first.line)+":"+std::to_string(first_line_offset)+" - "; + text+=std::to_string(offsets.second.line)+":"+std::to_string(second_line_offset); + text+=" with "+source; + } + else { + text+="Erase "; + text+=std::to_string(offsets.first.line)+":"+std::to_string(first_line_offset)+" - "; + text+=std::to_string(offsets.second.line)+":"+std::to_string(second_line_offset); + } + + return text; +} + ////////////// //// View //// ////////////// AspellConfig* Source::View::spellcheck_config=NULL; -Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr language): file_path(file_path) { - set_smart_home_end(Gsv::SMART_HOME_END_BEFORE); - +Source::View::View(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language): file_path(file_path), project_path(project_path), language(language) { get_source_buffer()->begin_not_undoable_action(); if(language) { if(juci::filesystem::read_non_utf8(file_path, get_buffer())==-1) @@ -228,6 +262,35 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtrdefault_tab_char; + tab_size=Singleton::Config::source()->default_tab_size; + if(Singleton::Config::source()->auto_tab_char_and_size) { + auto tab_char_and_size=find_tab_char_and_size(); + if(tab_char_and_size.first!=0) { + if(tab_char!=tab_char_and_size.first || tab_size!=tab_char_and_size.second) { + std::string tab_str; + if(tab_char_and_size.first==' ') + tab_str=""; + else + tab_str=""; + Singleton::terminal()->print("Tab char and size for file "+file_path.string()+" set to: "+tab_str+", "+std::to_string(tab_char_and_size.second)+".\n"); + } + + tab_char=tab_char_and_size.first; + tab_size=tab_char_and_size.second; + } + } + set_tab_char_and_size(tab_char, tab_size); +} + +void Source::View::set_tab_char_and_size(char tab_char, unsigned tab_size) { + this->tab_char=tab_char; + this->tab_size=tab_size; + + tab.clear(); + for(unsigned c=0;cremove_tag_by_name("spellcheck_error", get_buffer()->begin(), get_buffer()->end()); - - tab_char=Singleton::Config::source()->default_tab_char; - tab_size=Singleton::Config::source()->default_tab_size; - if(Singleton::Config::source()->auto_tab_char_and_size) { - auto tab_char_and_size=find_tab_char_and_size(); - if(tab_char_and_size.first!=0) { - if(tab_char!=tab_char_and_size.first || tab_size!=tab_char_and_size.second) { - std::string tab_str; - if(tab_char_and_size.first==' ') - tab_str=""; - else - tab_str=""; - Singleton::terminal()->print("Tab char and size for file "+file_path.string()+" set to: "+tab_str+", "+std::to_string(tab_char_and_size.second)+".\n"); - } - - tab_char=tab_char_and_size.first; - tab_size=tab_char_and_size.second; - } - } - tab.clear(); - for(unsigned c=0;cget_has_selection() && std::regex_match(line, sm, tabs_regex) && sm[2].str().size()==0) { - prefix_tabs=sm[1].str(); + auto tabs_end_iter=get_tabs_end_iter(); + if(!get_buffer()->get_has_selection() && tabs_end_iter.ends_line()) { + prefix_tabs=get_line_before(tabs_end_iter); Glib::ustring::size_type start_line=0; Glib::ustring::size_type end_line=0; @@ -704,15 +743,12 @@ std::string Source::View::get_line(const Gtk::TextIter &iter) { std::string line(get_source_buffer()->get_text(line_start_it, line_end_it)); return line; } - std::string Source::View::get_line(Glib::RefPtr mark) { return get_line(mark->get_iter()); } - std::string Source::View::get_line(int line_nr) { return get_line(get_buffer()->get_iter_at_line(line_nr)); } - std::string Source::View::get_line() { return get_line(get_buffer()->get_insert()); } @@ -722,15 +758,28 @@ std::string Source::View::get_line_before(const Gtk::TextIter &iter) { std::string line(get_source_buffer()->get_text(line_it, iter)); return line; } - std::string Source::View::get_line_before(Glib::RefPtr mark) { return get_line_before(mark->get_iter()); } - std::string Source::View::get_line_before() { return get_line_before(get_buffer()->get_insert()); } +Gtk::TextIter Source::View::get_tabs_end_iter(const Gtk::TextIter &iter) { + return get_tabs_end_iter(iter.get_line()); +} +Gtk::TextIter Source::View::get_tabs_end_iter(Glib::RefPtr mark) { + return get_tabs_end_iter(mark->get_iter()); +} +Gtk::TextIter Source::View::get_tabs_end_iter(int line_nr) { + auto sentence_iter = get_source_buffer()->get_iter_at_line(line_nr); + while((*sentence_iter==' ' || *sentence_iter=='\t') && !sentence_iter.ends_line() && sentence_iter.forward_char()) {} + return sentence_iter; +} +Gtk::TextIter Source::View::get_tabs_end_iter() { + return get_tabs_end_iter(get_buffer()->get_insert()); +} + bool Source::View::find_start_of_closed_expression(Gtk::TextIter iter, Gtk::TextIter &found_iter) { int count1=0; int count2=0; @@ -883,29 +932,40 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { last_keyval_is_return=false; get_source_buffer()->begin_user_action(); + auto iter=get_buffer()->get_insert()->get_iter(); //Indent as in next or previous line - if(key->keyval==GDK_KEY_Return && !get_buffer()->get_has_selection()) { - auto insert_it=get_buffer()->get_insert()->get_iter(); - int line_nr=insert_it.get_line(); - auto line=get_line_before(); - std::smatch sm; - if(std::regex_match(line, sm, tabs_regex)) { - if((line_nr+1)get_line_count()) { - string next_line=get_line(line_nr+1); - auto line_end_iter=get_buffer()->get_iter_at_line(line_nr+1); - if(line_end_iter.backward_char()) { - std::smatch sm2; - if(insert_it==line_end_iter && std::regex_match(next_line, sm2, tabs_regex)) { - if(sm2[1].str().size()>sm[1].str().size()) { - get_source_buffer()->insert_at_cursor("\n"+sm2[1].str()); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - } + if(key->keyval==GDK_KEY_Return && !get_buffer()->get_has_selection() && !iter.starts_line()) { + //First remove spaces or tabs around cursor + auto start_blank_iter=iter; + auto end_blank_iter=iter; + while((*end_blank_iter==' ' || *end_blank_iter=='\t') && + !end_blank_iter.ends_line() && end_blank_iter.forward_char()) {} + start_blank_iter.backward_char(); + while((*start_blank_iter==' ' || *start_blank_iter=='\t' || start_blank_iter.ends_line()) && + !start_blank_iter.starts_line() && start_blank_iter.backward_char()) {} + if(!start_blank_iter.starts_line()) { + start_blank_iter.forward_char(); + get_buffer()->erase(start_blank_iter, end_blank_iter); + } + else + get_buffer()->erase(iter, end_blank_iter); + iter=get_buffer()->get_insert()->get_iter(); + + int line_nr=iter.get_line(); + auto tabs_end_iter=get_tabs_end_iter(); + auto line_tabs=get_line_before(tabs_end_iter); + if((line_nr+1)get_line_count()) { + auto next_line_tabs_end_iter=get_tabs_end_iter(line_nr+1); + auto next_line_tabs=get_line_before(next_line_tabs_end_iter); + if(iter.ends_line()) { + if(next_line_tabs.size()>line_tabs.size()) { + get_source_buffer()->insert_at_cursor("\n"+next_line_tabs); + scroll_to(get_source_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; } } - get_source_buffer()->insert_at_cursor("\n"+sm[1].str()); + get_source_buffer()->insert_at_cursor("\n"+line_tabs); scroll_to(get_source_buffer()->get_insert()); get_source_buffer()->end_user_action(); return true; @@ -913,27 +973,27 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { } //Indent right when clicking tab, no matter where in the line the cursor is. Also works on selected text. else if(key->keyval==GDK_KEY_Tab) { - auto iter=get_buffer()->get_insert()->get_iter(); //Special case if insert is at beginning of empty line: if(iter.starts_line() && iter.ends_line() && !get_buffer()->get_has_selection()) { auto prev_line_iter=iter; while(prev_line_iter.starts_line() && prev_line_iter.backward_char()) {} - auto line=get_line_before(prev_line_iter); - std::smatch sm; - if(std::regex_match(line, sm, tabs_regex)) { - auto tabs=sm[1].str(); - auto next_line_iter=iter; - while(next_line_iter.starts_line() && next_line_iter.forward_char()) {} - line=get_line(next_line_iter); - if(std::regex_match(line, sm, tabs_regex)) { - if(sm[1].str().size()=tab_size) { - get_buffer()->insert_at_cursor(tabs); - get_source_buffer()->end_user_action(); - return true; - } - } + auto prev_line_tabs_end_iter=get_tabs_end_iter(prev_line_iter); + auto previous_line_tabs=get_line_before(prev_line_tabs_end_iter); + + auto next_line_iter=iter; + while(next_line_iter.starts_line() && next_line_iter.forward_char()) {} + auto next_line_tabs_end_iter=get_tabs_end_iter(next_line_iter); + auto next_line_tabs=get_line_before(next_line_tabs_end_iter); + + std::string tabs; + if(previous_line_tabs.size()=tab_size) { + get_buffer()->insert_at_cursor(tabs); + get_source_buffer()->end_user_action(); + return true; } } @@ -960,15 +1020,16 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { std::vector ignore_line; for(int line_nr=line_start;line_nr<=line_end;line_nr++) { auto line_it = get_source_buffer()->get_iter_at_line(line_nr); - if(!get_buffer()->get_has_selection() || line_it!=selection_end) { - string line=get_line(line_nr); - std::smatch sm; - if(std::regex_match(line, sm, tabs_regex) && (sm[1].str().size()>0 || sm[2].str().size()==0)) { - if(sm[2].str().size()>0) { - indent_left_steps=std::min(indent_left_steps, (unsigned)sm[1].str().size()); + if(!get_buffer()->get_has_selection() || line_it!=selection_end) { + auto tabs_end_iter=get_tabs_end_iter(line_nr); + auto line_tabs=get_line_before(tabs_end_iter); + + if(line_tabs.size()>0 || tabs_end_iter.ends_line()) { + if(!tabs_end_iter.ends_line()) { + indent_left_steps=std::min(indent_left_steps, static_cast(line_tabs.size())); ignore_line.push_back(false); } - else if((unsigned)sm[1].str().size()(line_tabs.size())get_iter_at_line(line_nr); Gtk::TextIter line_plus_it=line_it; if(!get_buffer()->get_has_selection() || line_it!=selection_end) { - if(indent_left_steps==0 || line_plus_it.forward_chars(indent_left_steps)) - if(!ignore_line.at(line_nr-line_start)) - get_source_buffer()->erase(line_it, line_plus_it); + line_plus_it.forward_chars(indent_left_steps); + if(!ignore_line.at(line_nr-line_start)) + get_source_buffer()->erase(line_it, line_plus_it); } } get_source_buffer()->end_user_action(); @@ -994,42 +1055,92 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { } //"Smart" backspace key else if(key->keyval==GDK_KEY_BackSpace && !get_buffer()->get_has_selection()) { - auto insert_it=get_buffer()->get_insert()->get_iter(); auto line=get_line_before(); - std::smatch sm; - if(std::regex_match(line, sm, tabs_regex) && sm[1].str().size()==line.size()) { - auto line_start_iter=insert_it; + bool do_smart_backspace=true; + for(auto &chr: line) { + if(chr!=' ' && chr!='\t') { + do_smart_backspace=false; + break; + } + } + if(do_smart_backspace) { + auto line_start_iter=iter; if(line_start_iter.backward_chars(line.size())) - get_buffer()->erase(insert_it, line_start_iter); + get_buffer()->erase(iter, line_start_iter); } } //"Smart" delete key else if(key->keyval==GDK_KEY_Delete && !get_buffer()->get_has_selection()) { - auto insert_iter=get_buffer()->get_insert()->get_iter(); - auto iter=insert_iter; - bool perform_smart_delete=false; - if(iter.starts_line() && iter.ends_line()) {} - else if(iter.ends_line() && iter.forward_char()) { - if(!iter.ends_line()) { - bool first_line=true; - while((*iter==' ' || *iter=='\t' || (first_line && iter.ends_line())) && iter.forward_char()) { - perform_smart_delete=true; - if(first_line && iter.ends_line()) - first_line=false; - } + auto insert_iter=iter; + bool do_smart_delete=true; + do { + if(*iter!=' ' && *iter!='\t' && !iter.ends_line()) { + do_smart_delete=false; + break; } + if(iter.ends_line()) { + if(!iter.forward_char()) + do_smart_delete=false; + break; + } + } while(iter.forward_char()); + if(do_smart_delete) { + if(!insert_iter.starts_line()) + while((*iter==' ' || *iter=='\t') && iter.forward_char()) {} + if(iter.backward_char()) + get_buffer()->erase(insert_iter, iter); + } + } + //Next two are smart home/end keys that works with wrapped lines + //Note that smart end goes FIRST to end of line to avoid hiding empty chars after expressions + else if(key->keyval==GDK_KEY_End && (key->state&GDK_CONTROL_MASK)==0) { + auto end_line_iter=iter; + while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {} + auto end_sentence_iter=end_line_iter; + while(!end_sentence_iter.starts_line() && + (*end_sentence_iter==' ' || *end_sentence_iter=='\t' || end_sentence_iter.ends_line()) && + end_sentence_iter.backward_char()) {} + if(!end_sentence_iter.ends_line()) + end_sentence_iter.forward_char(); + + if(iter==end_line_iter) { + if((key->state&GDK_SHIFT_MASK)>0) + get_buffer()->move_mark_by_name("insert", end_sentence_iter); + else + get_buffer()->place_cursor(end_sentence_iter); } else { - while((*iter==' ' || *iter=='\t') && iter.forward_char()) { - perform_smart_delete=true; - if(iter.ends_line()) { - iter.forward_char(); - break; - } - } + if((key->state&GDK_SHIFT_MASK)>0) + get_buffer()->move_mark_by_name("insert", end_line_iter); + else + get_buffer()->place_cursor(end_line_iter); } - if(perform_smart_delete && iter.backward_char()) - get_buffer()->erase(insert_iter, iter); + scroll_to(get_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; + } + else if(key->keyval==GDK_KEY_Home && (key->state&GDK_CONTROL_MASK)==0) { + auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); + auto start_sentence_iter=start_line_iter; + while(!start_sentence_iter.ends_line() && + (*start_sentence_iter==' ' || *start_sentence_iter=='\t') && + start_sentence_iter.forward_char()) {} + + if(iter>start_sentence_iter || iter==start_line_iter) { + if((key->state&GDK_SHIFT_MASK)>0) + get_buffer()->move_mark_by_name("insert", start_sentence_iter); + else + get_buffer()->place_cursor(start_sentence_iter); + } + else { + if((key->state&GDK_SHIFT_MASK)>0) + get_buffer()->move_mark_by_name("insert", start_line_iter); + else + get_buffer()->place_cursor(start_line_iter); + } + scroll_to(get_buffer()->get_insert()); + get_source_buffer()->end_user_action(); + return true; } bool stop=Gsv::View::on_key_press_event(key); @@ -1059,25 +1170,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) { @@ -1170,7 +1271,7 @@ std::vector Source::View::spellcheck_get_suggestions(const Gtk::Tex ///////////////////// //// GenericView //// ///////////////////// -Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language) : View(file_path, language) { +Source::GenericView::GenericView(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language) : View(file_path, project_path, language) { configure(); spellcheck_all=true; @@ -1249,1020 +1350,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, language), project_path(project_path), parse_error(false) { - 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(); - }); -} - -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 - JINFO("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(); - 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 - 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"; - warnings++; - } - else { - diagnostic_tag_name="def:error"; - errors++; - } - - auto spelling=diagnostic.spelling; - auto severity_spelling=diagnostic.severity_spelling; - auto create_tooltip_buffer=[this, spelling, severity_spelling, diagnostic_tag_name]() { - 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"); - 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(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::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(); - - //Indent depending on if/else/etc and brackets - if(key->keyval==GDK_KEY_Return) { - auto 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_line=get_line_before(start_of_sentence_iter); - std::smatch sm; - std::string tabs; - if(std::regex_match(start_line, sm, tabs_regex)) { - tabs=sm[1].str(); - } - - if(iter.backward_char() && *iter=='{') { - auto found_iter=iter; - bool found_right_bracket=find_right_bracket_forward(iter, found_iter); - - bool has_bracket=false; - if(found_right_bracket) { - auto line=get_line_before(found_iter); - if(std::regex_match(line, sm, tabs_regex)) { - if(tabs.size()==sm[1].str().size()) - has_bracket=true; - } - } - 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 line=get_line_before(found_iter); - if(std::regex_match(line, sm, tabs_regex)) { - tabs=sm[1].str(); - for(size_t c=0;cinsert_at_cursor("\n"+tabs+tab); - 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 start_left_bracket_sentence=get_line_before(start_of_left_bracket_sentence_iter); - if(std::regex_match(start_left_bracket_sentence, sm, tabs_regex)) { - if(tabs.size()==(sm[1].str().size()+tab_size)) { - auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); - auto start_line_plus_tab_size=start_line_iter; - for(size_t c=0;cerase(start_line_iter, start_line_plus_tab_size); - } - else { - get_source_buffer()->insert_at_cursor("\n"+tabs+tab); - scroll_to(get_source_buffer()->get_insert()); - get_source_buffer()->end_user_action(); - return true; - } - } - } - } - } - 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; - if(tab_size==0 || 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.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()); - get_buffer()->insert(completion_dialog->start_mark->get_iter(), row); - if(hide_window) { - char find_char=row.back(); - if(find_char==')' || find_char=='>') { - if(find_char==')') - find_char='('; - else - find_char='<'; - size_t pos=row.find(find_char); - if(pos!=std::string::npos) { - auto start_offset=completion_dialog->start_mark->get_iter().get_offset()+pos+1; - auto end_offset=completion_dialog->start_mark->get_iter().get_offset()+row.size()-1; - if(start_offset!=end_offset) - get_buffer()->select_range(get_buffer()->get_iter_at_offset(start_offset), get_buffer()->get_iter_at_offset(end_offset)); - } - } - } - }; - 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"); - 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(); - } - }; - - goto_next_diagnostic=[this]() { - if(source_readable) { - auto insert_offset=get_buffer()->get_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); - } - } - }; -} - -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 c69f178..2b60633 100644 --- a/src/source.h +++ b/src/source.h @@ -1,20 +1,13 @@ #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 @@ -23,6 +16,12 @@ namespace Source { class Config { public: + class DocumentationSearch { + public: + std::string separator; + std::unordered_map queries; + }; + std::string style; std::string font; std::string spellcheck_language; @@ -37,22 +36,8 @@ namespace Source { bool highlight_current_line; bool show_line_numbers; std::unordered_map clang_types; - }; - - class Range { - public: - Range(std::pair offsets, int kind): - offsets(offsets), kind(kind) {} - std::pair offsets; - int kind; - }; - - class AutoCompleteData { - public: - explicit AutoCompleteData(const std::vector &chunks) : - chunks(chunks) { } - std::vector chunks; - std::string brief_comments; + + std::unordered_map documentation_searches; }; class Token { @@ -70,10 +55,33 @@ namespace Source { std::string spelling; std::string usr; }; + + class Offset { + public: + Offset() {} + Offset(unsigned line, unsigned index): line(line), index(index) {} + bool operator==(const Offset &o) {return (line==o.line && index==o.index);} + + unsigned line; + unsigned index; + }; + + class FixIt { + public: + enum class Type {INSERT, REPLACE, ERASE}; + + FixIt(const std::string &source, const std::pair &offsets); + + std::string string(Glib::RefPtr buffer); + + Type type; + std::string source; + std::pair offsets; + }; class View : public Gsv::View { public: - View(const boost::filesystem::path &file_path, Glib::RefPtr language); + View(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language); ~View(); virtual void configure(); @@ -89,12 +97,16 @@ namespace Source { void paste(); boost::filesystem::path file_path; + boost::filesystem::path project_path; + Glib::RefPtr language; - std::function()> get_declaration_location; + std::function()> get_declaration_location; std::function goto_method; std::function get_token; + std::function()> get_token_data; std::function rename_similar_tokens; std::function goto_next_diagnostic; + std::function apply_fix_its; std::function on_update_status; std::function on_update_info; @@ -106,6 +118,9 @@ namespace Source { void spellcheck(); void remove_spellcheck_errors(); void goto_next_spellcheck_error(); + + void set_tab_char_and_size(char tab_char, unsigned tab_size); + std::pair get_tab_char_and_size() {return {tab_char, tab_size};} protected: bool source_readable; Tooltips diagnostic_tooltips; @@ -124,6 +139,10 @@ namespace Source { std::string get_line_before(const Gtk::TextIter &iter); std::string get_line_before(Glib::RefPtr mark); std::string get_line_before(); + Gtk::TextIter get_tabs_end_iter(const Gtk::TextIter &iter); + Gtk::TextIter get_tabs_end_iter(Glib::RefPtr mark); + Gtk::TextIter get_tabs_end_iter(int line_nr); + Gtk::TextIter get_tabs_end_iter(); bool find_start_of_closed_expression(Gtk::TextIter iter, Gtk::TextIter &found_iter); bool find_open_expression_symbol(Gtk::TextIter iter, const Gtk::TextIter &until_iter, Gtk::TextIter &found_iter); @@ -137,7 +156,6 @@ namespace Source { unsigned tab_size; char tab_char; std::string tab; - std::regex tabs_regex; bool spellcheck_all=false; std::unique_ptr spellcheck_suggestions_dialog; @@ -169,106 +187,9 @@ namespace Source { static Glib::RefPtr create() {return Glib::RefPtr(new CompletionBuffer());} }; public: - GenericView(const boost::filesystem::path &file_path, Glib::RefPtr language); + GenericView(const boost::filesystem::path &file_path, const boost::filesystem::path &project_path, Glib::RefPtr language); void parse_language_file(Glib::RefPtr &completion_buffer, bool &has_context_class, const boost::property_tree::ptree &pt); }; - - class ClangViewParse : public View { - public: - ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language); - ~ClangViewParse(); - void configure(); - - boost::filesystem::path project_path; - 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; - 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: - 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; - 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..4e39072 --- /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) { + JDEBUG("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(); + }); + + JDEBUG("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 + JINFO("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.first.index=clang_offsets.first.index; + offsets.second.line=clang_offsets.second.line; + offsets.second.index=clang_offsets.second.index; + + fix_its.emplace_back(fix_it.source, offsets); + + if(fix_its_string.size()>0) + fix_its_string+='\n'; + fix_its_string+=fix_its.back().string(get_buffer()); + 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(); + auto clang_offset=referenced.get_source_location().get_offset(); + location.second.line=clang_offset.line; + location.second.index=clang_offset.index; + 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_index(fix_it.offsets.first.line-1, fix_it.offsets.first.index-1); + auto end_iter=get_buffer()->get_iter_at_line_index(fix_it.offsets.second.line-1, fix_it.offsets.second.index-1); + fix_it_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter)); + } + size_t c=0; + 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..e029a58 --- /dev/null +++ b/src/source_clang.h @@ -0,0 +1,128 @@ +#ifndef JUCI_SOURCE_CLANG_H_ +#define JUCI_SOURCE_CLANG_H_ + +#include +#include +#include +#include +#include +#include "clangmm.h" +#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.cc b/src/window.cc index 93add35..142a8c1 100644 --- a/src/window.cc +++ b/src/window.cc @@ -116,6 +116,12 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), compiling(false) { if(auto menu_item=dynamic_cast(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceGotoNextDiagnostic"))) menu_item->set_sensitive((bool)notebook.get_current_view()->goto_next_diagnostic); + + if(auto menu_item=dynamic_cast(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceApplyFixIts"))) + menu_item->set_sensitive((bool)notebook.get_current_view()->apply_fix_its); + + if(auto menu_item=dynamic_cast(menu.ui_manager->get_widget("/MenuBar/SourceMenu/SourceFindDocumentation"))) + menu_item->set_sensitive((bool)notebook.get_current_view()->get_token_data); Singleton::directories()->select(notebook.get_current_view()->file_path); @@ -327,6 +333,13 @@ void Window::create_menu() { if(notebook.get_current_page()!=-1) notebook.get_current_view()->goto_next_spellcheck_error(); }); + menu.action_group->add(Gtk::Action::create("SourceIndentation", "Indentation")); + menu.action_group->add(Gtk::Action::create("SourceIndentationSetBufferTab", "Set Current Buffer Tab"), Gtk::AccelKey(menu.key_map["source_indentation_set_buffer_tab"]), [this]() { + set_tab_entry(); + }); + menu.action_group->add(Gtk::Action::create("SourceIndentationAutoIndentBuffer", "Auto-Indent Current Buffer"), Gtk::AccelKey(menu.key_map["source_indentation_auto_indent_buffer"]), [this]() { + Singleton::terminal()->print("Auto-Indent Current Buffer will soon be implemented.\n"); + }); menu.action_group->add(Gtk::Action::create("SourceGotoLine", "Go to Line"), Gtk::AccelKey(menu.key_map["source_goto_line"]), [this]() { goto_line_entry(); }); @@ -363,6 +376,46 @@ void Window::create_menu() { menu.action_group->add(Gtk::Action::create("SourceRename", "Rename"), Gtk::AccelKey(menu.key_map["source_rename"]), [this]() { rename_token_entry(); }); + menu.action_group->add(Gtk::Action::create("SourceFindDocumentation", "Find Documentation"), Gtk::AccelKey(menu.key_map["source_find_documentation"]), [this]() { + if(notebook.get_current_page()!=-1) { + if(notebook.get_current_view()->get_token_data) { + auto data=notebook.get_current_view()->get_token_data(); + if(data.size()>0) { + auto documentation_search=Singleton::Config::source()->documentation_searches.find(data[0]); + if(documentation_search!=Singleton::Config::source()->documentation_searches.end()) { + std::string token_query; + for(size_t c=1;c0) { + if(token_query.size()>0) + token_query+=documentation_search->second.separator; + token_query+=data[c]; + } + } + if(token_query.size()>0) { + std::unordered_map::iterator query; + if(data[1].size()>0) + query=documentation_search->second.queries.find(data[1]); + else + query=documentation_search->second.queries.find("@empty"); + if(query==documentation_search->second.queries.end()) + query=documentation_search->second.queries.find("@any"); + + if(query!=documentation_search->second.queries.end()) { + std::string uri=query->second+token_query; +#ifdef __APPLE__ + Singleton::terminal()->execute("open \""+uri+"\""); +#else + GError* error=NULL; + gtk_show_uri(NULL, uri.c_str(), GDK_CURRENT_TIME, &error); + g_clear_error(&error); +#endif + } + } + } + } + } + } + }); menu.action_group->add(Gtk::Action::create("SourceGotoNextDiagnostic", "Go to next Diagnostic"), Gtk::AccelKey(menu.key_map["source_goto_next_diagnostic"]), [this]() { if(notebook.get_current_page()!=-1) { if(notebook.get_current_view()->goto_next_diagnostic) { @@ -370,6 +423,13 @@ void Window::create_menu() { } } }); + menu.action_group->add(Gtk::Action::create("SourceApplyFixIts", "Apply Fix-Its"), Gtk::AccelKey(menu.key_map["source_apply_fix_its"]), [this]() { + if(notebook.get_current_page()!=-1) { + if(notebook.get_current_view()->apply_fix_its) { + notebook.get_current_view()->apply_fix_its(); + } + } + }); menu.action_group->add(Gtk::Action::create("ProjectCompileAndRun", "Compile and Run"), Gtk::AccelKey(menu.key_map["compile_and_run"]), [this]() { if(notebook.get_current_page()==-1 || compiling) @@ -425,12 +485,28 @@ void Window::create_menu() { }); menu.action_group->add(Gtk::Action::create("ProjectRunCommand", "Run Command"), Gtk::AccelKey(menu.key_map["run_command"]), [this]() { entry_box.clear(); + entry_box.labels.emplace_back(); + auto label_it=entry_box.labels.begin(); + label_it->update=[label_it](int state, const std::string& message){ + label_it->set_text("Run Command directory order: file project path, file directory, opened directory, current directory"); + }; + label_it->update(0, ""); entry_box.entries.emplace_back(last_run_command, [this](const std::string& content){ if(content!="") { last_run_command=content; + boost::filesystem::path run_path; + if(notebook.get_current_page()!=-1) { + if(notebook.get_current_view()->project_path!="") + run_path=notebook.get_current_view()->project_path; + else + run_path=notebook.get_current_view()->file_path.parent_path(); + } + else + run_path=Singleton::directories()->current_path; Singleton::terminal()->async_print("Running: "+content+'\n'); - Singleton::terminal()->async_execute(content, Singleton::directories()->current_path, [this, content](int exit_code) { - Singleton::terminal()->async_print(content + " returned: " + std::to_string(exit_code)+'\n'); + + Singleton::terminal()->async_execute(content, run_path, [this, content](int exit_code){ + Singleton::terminal()->async_print(content+" returned: "+std::to_string(exit_code)+'\n'); }); } entry_box.hide(); @@ -617,6 +693,65 @@ void Window::search_and_replace_entry() { entry_box.show(); } +void Window::set_tab_entry() { + entry_box.clear(); + if(notebook.get_current_page()!=-1) { + auto tab_char_and_size=notebook.get_current_view()->get_tab_char_and_size(); + + entry_box.labels.emplace_back(); + auto label_it=entry_box.labels.begin(); + + entry_box.entries.emplace_back(std::to_string(tab_char_and_size.second)); + auto entry_tab_size_it=entry_box.entries.begin(); + entry_tab_size_it->set_placeholder_text("Tab size"); + + char tab_char=tab_char_and_size.first; + std::string tab_char_string; + if(tab_char==' ') + tab_char_string="space"; + else if(tab_char=='\t') + tab_char_string="tab"; + + entry_box.entries.emplace_back(tab_char_string); + auto entry_tab_char_it=entry_box.entries.rbegin(); + entry_tab_char_it->set_placeholder_text("Tab char"); + + const auto activate_function=[this, entry_tab_char_it, entry_tab_size_it, label_it](const std::string& content){ + if(notebook.get_current_page()!=-1) { + char tab_char=0; + unsigned tab_size=0; + try { + tab_size = static_cast(stoul(entry_tab_size_it->get_text())); + std::string tab_char_string=entry_tab_char_it->get_text(); + std::transform(tab_char_string.begin(), tab_char_string.end(), tab_char_string.begin(), ::tolower); + if(tab_char_string=="space") + tab_char=' '; + else if(tab_char_string=="tab") + tab_char='\t'; + } + catch(const std::exception &e) {} + + if(tab_char!=0 && tab_size>0) { + notebook.get_current_view()->set_tab_char_and_size(tab_char, tab_size); + entry_box.hide(); + } + else { + label_it->set_text("Tab size must be >0 and tab char set to either 'space' or 'tab'"); + } + } + }; + + entry_tab_char_it->on_activate=activate_function; + entry_tab_size_it->on_activate=activate_function; + + entry_box.buttons.emplace_back("Set tab in current buffer", [this, entry_tab_char_it](){ + entry_tab_char_it->activate(); + }); + + entry_box.show(); + } +} + void Window::goto_line_entry() { entry_box.clear(); if(notebook.get_current_page()!=-1) { @@ -656,7 +791,7 @@ void Window::rename_token_entry() { entry_box.labels.emplace_back(); auto label_it=entry_box.labels.begin(); label_it->update=[label_it](int state, const std::string& message){ - label_it->set_text("Warning: only opened and parsed tabs will have its content renamed, and modified files will be saved."); + label_it->set_text("Warning: only opened and parsed tabs will have its content renamed, and modified files will be saved"); }; label_it->update(0, ""); entry_box.entries.emplace_back(token->spelling, [this, token](const std::string& content){ diff --git a/src/window.h b/src/window.h index a0c13ea..74975da 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 { @@ -43,6 +42,7 @@ private: void create_menu(); void hide(); void search_and_replace_entry(); + void set_tab_entry(); void goto_line_entry(); void rename_token_entry(); void generate_keybindings();