From 579f129e45071261819a54ffe772d2c463eeb656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E4=BA=88=E9=A1=BA?= Date: Fri, 18 May 2018 16:29:10 +0800 Subject: [PATCH 01/12] Reformat --- src/CMakeLists.txt | 124 +- src/autocomplete.cc | 318 +- src/autocomplete.h | 99 +- src/buildsystem/buildsystem.h | 20 + src/buildsystem/cmake.cc | 386 ++ src/buildsystem/cmake.h | 41 + src/buildsystem/meson.cc | 123 + src/buildsystem/meson.h | 18 + src/cmake.cc | 378 -- src/cmake.h | 30 - src/compile_commands.cc | 290 +- src/compile_commands.h | 31 +- src/config.cc | 347 +- src/config.h | 219 +- src/ctags.cc | 350 +- src/ctags.h | 39 +- src/debug_lldb.cc | 862 ++--- src/debug_lldb.h | 171 +- src/dialogs.cc | 131 +- src/dialogs.h | 38 +- src/dialogs_unix.cc | 30 +- src/dialogs_win.cc | 252 +- src/directories.cc | 1323 +++---- src/directories.h | 159 +- src/dispatcher.cc | 40 +- src/dispatcher.h | 34 +- src/documentation_cppreference.cc | 72 +- src/documentation_cppreference.h | 9 +- src/entrybox.cc | 161 +- src/entrybox.h | 94 +- src/files.h | 59 +- src/filesystem.cc | 365 +- src/filesystem.h | 79 +- src/git.cc | 479 +-- src/git.h | 202 +- src/info.cc | 58 +- src/info.h | 22 +- src/juci.cc | 212 +- src/juci.h | 18 +- src/menu.cc | 56 +- src/menu.h | 40 +- src/meson.cc | 117 - src/meson.h | 15 - src/notebook.cc | 1139 +++--- src/notebook.h | 175 +- src/project.cc | 968 +++--- src/project.h | 350 +- src/project_build.cc | 190 +- src/project_build.h | 130 +- src/selection_dialog.cc | 800 ++--- src/selection_dialog.h | 189 +- src/source.cc | 5408 +++++++++++++++-------------- src/source.h | 369 +- src/source_base.cc | 606 ++-- src/source_base.h | 190 +- src/source_clang.cc | 3395 +++++++++--------- src/source_clang.h | 225 +- src/source_diff.cc | 634 ++-- src/source_diff.h | 116 +- src/source_language_protocol.cc | 2728 ++++++++------- src/source_language_protocol.h | 250 +- src/source_spellcheck.cc | 891 ++--- src/source_spellcheck.h | 87 +- src/terminal.cc | 721 ++-- src/terminal.h | 85 +- src/tooltips.cc | 429 +-- src/tooltips.h | 92 +- src/usages_clang.cc | 1266 +++---- src/usages_clang.h | 254 +- src/window.cc | 3294 +++++++++--------- src/window.h | 71 +- 71 files changed, 16954 insertions(+), 16009 deletions(-) create mode 100644 src/buildsystem/buildsystem.h create mode 100644 src/buildsystem/cmake.cc create mode 100644 src/buildsystem/cmake.h create mode 100644 src/buildsystem/meson.cc create mode 100644 src/buildsystem/meson.h delete mode 100644 src/cmake.cc delete mode 100644 src/cmake.h delete mode 100644 src/meson.cc delete mode 100644 src/meson.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c32003c..b988f114 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,75 +1,77 @@ # Files used both in ../src and ../tests + +file(GLOB JUCI_SRC_BUILDSYSTEMS buildsystem/*.*) + set(JUCI_SHARED_FILES - autocomplete.cc - cmake.cc - compile_commands.cc - ctags.cc - dispatcher.cc - documentation_cppreference.cc - filesystem.cc - git.cc - menu.cc - meson.cc - project_build.cc - source.cc - source_base.cc - source_clang.cc - source_diff.cc - source_language_protocol.cc - source_spellcheck.cc - terminal.cc - usages_clang.cc -) -if(LIBLLDB_FOUND) - list(APPEND JUCI_SHARED_FILES debug_lldb.cc) -endif() + ${JUCI_SRC_BUILDSYSTEMS} + autocomplete.cc + compile_commands.cc + ctags.cc + dispatcher.cc + documentation_cppreference.cc + filesystem.cc + git.cc + menu.cc + project_build.cc + source.cc + source_base.cc + source_clang.cc + source_diff.cc + source_language_protocol.cc + source_spellcheck.cc + terminal.cc + usages_clang.cc + ) +if (LIBLLDB_FOUND) + list(APPEND JUCI_SHARED_FILES debug_lldb.cc) +endif () add_library(juci_shared STATIC ${JUCI_SHARED_FILES}) target_link_libraries(juci_shared - ${GTKMM_LIBRARIES} - ${GTKSVMM_LIBRARIES} - ${Boost_LIBRARIES} - ${LIBLLDB_LIBRARIES} - ${ASPELL_LIBRARIES} - ${LIBGIT2_LIBRARIES} - clangmm - tiny-process-library -) + ${GTKMM_LIBRARIES} + ${GTKSVMM_LIBRARIES} + ${Boost_LIBRARIES} + ${LIBLLDB_LIBRARIES} + ${ASPELL_LIBRARIES} + ${LIBGIT2_LIBRARIES} + clangmm + tiny-process-library + ) add_executable(juci - config.cc - dialogs.cc - dialogs_unix.cc - directories.cc - entrybox.cc - info.cc - juci.cc - notebook.cc - project.cc - selection_dialog.cc - tooltips.cc - window.cc -) + config.cc + dialogs.cc + dialogs_unix.cc + directories.cc + entrybox.cc + info.cc + juci.cc + notebook.cc + project.cc + selection_dialog.cc + tooltips.cc + window.cc + ) target_link_libraries(juci juci_shared) install(TARGETS juci RUNTIME DESTINATION bin) -if(${CMAKE_SYSTEM_NAME} MATCHES Linux|.*BSD|DragonFly) - install(FILES "${CMAKE_SOURCE_DIR}/share/juci.desktop" - DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") - install(FILES "${CMAKE_SOURCE_DIR}/share/juci.svg" - DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") -elseif(APPLE) - install(CODE "execute_process(COMMAND /usr/bin/python ${CMAKE_SOURCE_DIR}/share/set_icon_macos.py ${CMAKE_SOURCE_DIR}/share/juci.png ${CMAKE_INSTALL_PREFIX}/bin/juci)") -endif() +if (${CMAKE_SYSTEM_NAME} MATCHES Linux|.*BSD|DragonFly) + install(FILES "${CMAKE_SOURCE_DIR}/share/juci.desktop" + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") + install(FILES "${CMAKE_SOURCE_DIR}/share/juci.svg" + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") +elseif (APPLE) + install(CODE "execute_process(COMMAND /usr/bin/python ${CMAKE_SOURCE_DIR}/share/set_icon_macos.py ${CMAKE_SOURCE_DIR}/share/juci.png ${CMAKE_INSTALL_PREFIX}/bin/juci)") +endif () # add a target to generate API documentation with Doxygen set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules/") find_package(Plantuml) find_package(Doxygen) -if(DOXYGEN_FOUND) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) - add_custom_target(doc - ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating API documentation with Doxygen to ${CMAKE_CURRENT_BINARY_DIR}" VERBATIM - ) -endif(DOXYGEN_FOUND) +if (DOXYGEN_FOUND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) + add_custom_target(doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen to ${CMAKE_CURRENT_BINARY_DIR}" VERBATIM + ) +endif (DOXYGEN_FOUND) diff --git a/src/autocomplete.cc b/src/autocomplete.cc index 8d5b6848..374e25c7 100644 --- a/src/autocomplete.cc +++ b/src/autocomplete.cc @@ -2,178 +2,178 @@ #include "selection_dialog.h" Autocomplete::Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word) - : view(view), interactive_completion(interactive_completion), strip_word(strip_word), state(State::IDLE) { - view->get_buffer()->signal_changed().connect([this, &last_keyval] { - if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) { - cancel_reparse(); - return; - } - if(!this->view->has_focus()) - return; - if(is_continue_key(last_keyval) && (this->interactive_completion || state != State::IDLE)) - run(); - else { - stop(); - - if(is_restart_key(last_keyval) && this->interactive_completion) - run(); - } - }); - - view->get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr &mark) { - if(mark->get_name() == "insert") - stop(); - }); + : view(view), interactive_completion(interactive_completion), strip_word(strip_word), state(State::IDLE) { + view->get_buffer()->signal_changed().connect([this, &last_keyval] { + if (CompletionDialog::get() && CompletionDialog::get()->is_visible()) { + cancel_reparse(); + return; + } + if (!this->view->has_focus()) + return; + if (is_continue_key(last_keyval) && (this->interactive_completion || state != State::IDLE)) + run(); + else { + stop(); - view->signal_key_release_event().connect([](GdkEventKey *key) { - if(CompletionDialog::get() && CompletionDialog::get()->is_visible()) { - if(CompletionDialog::get()->on_key_release(key)) - return true; - } - return false; - }, false); + if (is_restart_key(last_keyval) && this->interactive_completion) + run(); + } + }); - view->signal_focus_out_event().connect([this](GdkEventFocus *event) { - stop(); - return false; - }); + view->get_buffer()->signal_mark_set().connect( + [this](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr &mark) { + if (mark->get_name() == "insert") + stop(); + }); + + view->signal_key_release_event().connect([](GdkEventKey *key) { + if (CompletionDialog::get() && CompletionDialog::get()->is_visible()) { + if (CompletionDialog::get()->on_key_release(key)) + return true; + } + return false; + }, false); + + view->signal_focus_out_event().connect([this](GdkEventFocus *event) { + stop(); + return false; + }); } void Autocomplete::run() { - if(run_check()) { - if(!is_processing()) - return; - - if(state == State::CANCELED) - state = State::RESTARTING; - - if(state != State::IDLE) - return; - - state = State::STARTING; - - before_add_rows(); - - if(thread.joinable()) - thread.join(); - auto buffer = view->get_buffer()->get_text(); - auto iter = view->get_buffer()->get_insert()->get_iter(); - auto line_nr = iter.get_line() + 1; - auto column_nr = iter.get_line_index() + 1; - if(strip_word) { - auto pos = iter.get_offset() - 1; - while(pos >= 0 && ((buffer[pos] >= 'a' && buffer[pos] <= 'z') || (buffer[pos] >= 'A' && buffer[pos] <= 'Z') || - (buffer[pos] >= '0' && buffer[pos] <= '9') || buffer[pos] == '_')) { - buffer.replace(pos, 1, " "); - column_nr--; - pos--; - } - } - thread = std::thread([this, line_nr, column_nr, buffer = std::move(buffer)] { - auto lock = get_parse_lock(); - if(!is_processing()) - return; - stop_parse(); - - auto &buffer_raw = const_cast(buffer.raw()); - rows.clear(); - add_rows(buffer_raw, line_nr, column_nr); - - if(is_processing()) { - dispatcher.post([this]() { - after_add_rows(); - if(state == State::RESTARTING) { - state = State::IDLE; - reparse(); - run(); - } - else if(state == State::CANCELED || rows.empty()) { - state = State::IDLE; - reparse(); - } - else { - auto start_iter = view->get_buffer()->get_insert()->get_iter(); - if(prefix.size() > 0 && !start_iter.backward_chars(prefix.size())) { - state = State::IDLE; - reparse(); - return; + if (run_check()) { + if (!is_processing()) + return; + + if (state == State::CANCELED) + state = State::RESTARTING; + + if (state != State::IDLE) + return; + + state = State::STARTING; + + before_add_rows(); + + if (thread.joinable()) + thread.join(); + auto buffer = view->get_buffer()->get_text(); + auto iter = view->get_buffer()->get_insert()->get_iter(); + auto line_nr = iter.get_line() + 1; + auto column_nr = iter.get_line_index() + 1; + if (strip_word) { + auto pos = iter.get_offset() - 1; + while (pos >= 0 && + ((buffer[pos] >= 'a' && buffer[pos] <= 'z') || (buffer[pos] >= 'A' && buffer[pos] <= 'Z') || + (buffer[pos] >= '0' && buffer[pos] <= '9') || buffer[pos] == '_')) { + buffer.replace(pos, 1, " "); + column_nr--; + pos--; } - CompletionDialog::create(view, view->get_buffer()->create_mark(start_iter)); - setup_dialog(); - for(auto &row : rows) { - CompletionDialog::get()->add_row(row); - row.clear(); + } + thread = std::thread([this, line_nr, column_nr, buffer = std::move(buffer)] { + auto lock = get_parse_lock(); + if (!is_processing()) + return; + stop_parse(); + + auto &buffer_raw = const_cast(buffer.raw()); + rows.clear(); + add_rows(buffer_raw, line_nr, column_nr); + + if (is_processing()) { + dispatcher.post([this]() { + after_add_rows(); + if (state == State::RESTARTING) { + state = State::IDLE; + reparse(); + run(); + } else if (state == State::CANCELED || rows.empty()) { + state = State::IDLE; + reparse(); + } else { + auto start_iter = view->get_buffer()->get_insert()->get_iter(); + if (prefix.size() > 0 && !start_iter.backward_chars(prefix.size())) { + state = State::IDLE; + reparse(); + return; + } + CompletionDialog::create(view, view->get_buffer()->create_mark(start_iter)); + setup_dialog(); + for (auto &row : rows) { + CompletionDialog::get()->add_row(row); + row.clear(); + } + state = State::IDLE; + + view->get_buffer()->begin_user_action(); + CompletionDialog::get()->show(); + } + }); + } else { + dispatcher.post([this] { + state = State::CANCELED; + on_add_rows_error(); + }); } - state = State::IDLE; - - view->get_buffer()->begin_user_action(); - CompletionDialog::get()->show(); - } }); - } - else { - dispatcher.post([this] { - state = State::CANCELED; - on_add_rows_error(); - }); - } - }); - } + } - if(state != State::IDLE) - cancel_reparse(); + if (state != State::IDLE) + cancel_reparse(); } void Autocomplete::stop() { - if(state == State::STARTING || state == State::RESTARTING) - state = State::CANCELED; + if (state == State::STARTING || state == State::RESTARTING) + state = State::CANCELED; } void Autocomplete::setup_dialog() { - CompletionDialog::get()->on_show=[this] { - on_show(); - }; - - CompletionDialog::get()->on_hide = [this]() { - view->get_buffer()->end_user_action(); - tooltips.hide(); - tooltips.clear(); - on_hide(); - reparse(); - }; - - CompletionDialog::get()->on_changed = [this](unsigned int index, const std::string &text) { - if(index >= rows.size()) { - tooltips.hide(); - return; - } - - on_changed(index, text); - - auto tooltip = get_tooltip(index); - if(tooltip.empty()) - tooltips.hide(); - else { - tooltips.clear(); - auto create_tooltip_buffer = [ this, tooltip = std::move(tooltip) ]() { - auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()); - - tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), tooltip); - - return tooltip_buffer; - }; - - auto iter = CompletionDialog::get()->start_mark->get_iter(); - tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter)); - - tooltips.show(true); - } - }; - - CompletionDialog::get()->on_select=[this](unsigned int index, const std::string &text, bool hide_window) { - if(index>=rows.size()) - return; - - on_select(index, text, hide_window); - }; + CompletionDialog::get()->on_show = [this] { + on_show(); + }; + + CompletionDialog::get()->on_hide = [this]() { + view->get_buffer()->end_user_action(); + tooltips.hide(); + tooltips.clear(); + on_hide(); + reparse(); + }; + + CompletionDialog::get()->on_changed = [this](unsigned int index, const std::string &text) { + if (index >= rows.size()) { + tooltips.hide(); + return; + } + + on_changed(index, text); + + auto tooltip = get_tooltip(index); + if (tooltip.empty()) + tooltips.hide(); + else { + tooltips.clear(); + auto create_tooltip_buffer = [this, tooltip = std::move(tooltip)]() { + auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()); + + tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), tooltip); + + return tooltip_buffer; + }; + + auto iter = CompletionDialog::get()->start_mark->get_iter(); + tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), + view->get_buffer()->create_mark(iter)); + + tooltips.show(true); + } + }; + + CompletionDialog::get()->on_select = [this](unsigned int index, const std::string &text, bool hide_window) { + if (index >= rows.size()) + return; + + on_select(index, text, hide_window); + }; } diff --git a/src/autocomplete.h b/src/autocomplete.h index 1d5939bc..b808ca04 100644 --- a/src/autocomplete.h +++ b/src/autocomplete.h @@ -1,58 +1,65 @@ #pragma once + #include "dispatcher.h" #include "tooltips.h" #include #include class Autocomplete { - Gtk::TextView *view; - bool &interactive_completion; - /// Some libraries/utilities, like libclang, require that autocomplete is started at the beginning of a word - bool strip_word; + Gtk::TextView *view; + bool &interactive_completion; + /// Some libraries/utilities, like libclang, require that autocomplete is started at the beginning of a word + bool strip_word; - Dispatcher dispatcher; + Dispatcher dispatcher; public: - enum class State { IDLE, STARTING, RESTARTING, CANCELED }; - - std::string prefix; - std::mutex prefix_mutex; - std::vector rows; - Tooltips tooltips; - - std::atomic state; - - std::thread thread; - - std::function is_processing = [] { return true; }; - std::function reparse = [] {}; - std::function cancel_reparse = [] {}; - std::function>()> get_parse_lock = [] { return nullptr; }; - std::function stop_parse = [] {}; - - std::function is_continue_key = [](guint) { return false; }; - std::function is_restart_key = [](guint) { return false; }; - std::function run_check = [] { return false; }; - - std::function before_add_rows = [] {}; - std::function after_add_rows = [] {}; - std::function on_add_rows_error = [] {}; - - /// The handler is not run in the main loop. - std::function add_rows = [](std::string &, int, int) {}; - - std::function on_show = [] {}; - std::function on_hide = [] {}; - std::function on_changed = [](unsigned int index, const std::string &text) {}; - std::function on_select = [](unsigned int index, const std::string &text, bool hide_window) {}; - - std::function get_tooltip = [](unsigned int index) {return std::string();}; - - Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word); - - void run(); - void stop(); - + enum class State { + IDLE, STARTING, RESTARTING, CANCELED + }; + + std::string prefix; + std::mutex prefix_mutex; + std::vector rows; + Tooltips tooltips; + + std::atomic state; + + std::thread thread; + + std::function is_processing = [] { return true; }; + std::function reparse = [] {}; + std::function cancel_reparse = [] {}; + std::function>()> get_parse_lock = [] { return nullptr; }; + std::function stop_parse = [] {}; + + std::function is_continue_key = [](guint) { return false; }; + std::function is_restart_key = [](guint) { return false; }; + std::function run_check = [] { return false; }; + + std::function before_add_rows = [] {}; + std::function after_add_rows = [] {}; + std::function on_add_rows_error = [] {}; + + /// The handler is not run in the main loop. + std::function add_rows = [](std::string &, int, int) {}; + + std::function on_show = [] {}; + std::function on_hide = [] {}; + std::function on_changed = [](unsigned int index, + const std::string &text) {}; + std::function on_select = [](unsigned int index, + const std::string &text, + bool hide_window) {}; + + std::function get_tooltip = [](unsigned int index) { return std::string(); }; + + Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word); + + void run(); + + void stop(); + private: - void setup_dialog(); + void setup_dialog(); }; diff --git a/src/buildsystem/buildsystem.h b/src/buildsystem/buildsystem.h new file mode 100644 index 00000000..d384bec2 --- /dev/null +++ b/src/buildsystem/buildsystem.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +class BuildSystemBase { +public: + auto get_project_path() const { return project_path; } + void set_project_path(const boost::filesystem::path& rhs) { project_path = rhs; } + + virtual bool update_default_build(const boost::filesystem::path &default_build_path, bool force = false); + + virtual bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force = false); + + virtual boost::filesystem::path + get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); + +private: + boost::filesystem::path project_path; +}; diff --git a/src/buildsystem/cmake.cc b/src/buildsystem/cmake.cc new file mode 100644 index 00000000..c656c536 --- /dev/null +++ b/src/buildsystem/cmake.cc @@ -0,0 +1,386 @@ +#include "cmake.h" +#include "../filesystem.h" +#include "../dialogs.h" +#include "../config.h" +#include "../terminal.h" +#include +#include "../compile_commands.h" + +CMake::CMake(const boost::filesystem::path &path) { + const auto find_cmake_project = [](const boost::filesystem::path &cmake_path) { + for (auto &line: filesystem::read_lines(cmake_path)) { + const static std::regex project_regex("^ *project *\\(.*\\r?$", std::regex::icase); + std::smatch sm; + if (std::regex_match(line, sm, project_regex)) + return true; + } + return false; + }; + + auto search_path = boost::filesystem::is_directory(path) ? path : path.parent_path(); + while (true) { + auto search_cmake_path = search_path / "CMakeLists.txt"; + if (boost::filesystem::exists(search_cmake_path)) { + paths.emplace(paths.begin(), search_cmake_path); + if (find_cmake_project(search_cmake_path)) { + set_project_path(search_path); + break; + } + } + if (search_path == search_path.root_directory()) + break; + search_path = search_path.parent_path(); + } +} + +bool CMake::update_default_build(const boost::filesystem::path &default_build_path, bool force) { + if (get_project_path().empty() || !boost::filesystem::exists(get_project_path() / "CMakeLists.txt") || + default_build_path.empty()) + return false; + + if (!boost::filesystem::exists(default_build_path)) { + boost::system::error_code ec; + boost::filesystem::create_directories(default_build_path, ec); + if (ec) { + Terminal::get().print("Error: could not create " + default_build_path.string() + ": " + ec.message() + "\n", + true); + return false; + } + } + + if (!force && boost::filesystem::exists(default_build_path / "compile_commands.json")) + return true; + + auto compile_commands_path = default_build_path / "compile_commands.json"; + Dialog::Message message("Creating/updating default build"); + auto exit_status = Terminal::get().process(Config::get().project.cmake.command + ' ' + + filesystem::escape_argument(get_project_path().string()) + + " -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", default_build_path); + message.hide(); + if (exit_status == EXIT_SUCCESS) { +#ifdef _WIN32 //Temporary fix to MSYS2's libclang + auto compile_commands_file = filesystem::read(compile_commands_path); + auto replace_drive = [&compile_commands_file](const std::string ¶m) { + size_t pos = 0; + auto param_size = param.length(); + while ((pos = compile_commands_file.find(param + "/", pos)) != std::string::npos) { + if (pos + param_size + 1 < compile_commands_file.size()) + compile_commands_file.replace(pos, param_size + 2, + param + compile_commands_file[pos + param_size + 1] + ":"); + else + break; + } + }; + replace_drive("-I"); + replace_drive("-isystem "); + filesystem::write(compile_commands_path, compile_commands_file); +#endif + return true; + } + return false; +} + +bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) { + if (get_project_path().empty() || !boost::filesystem::exists(get_project_path() / "CMakeLists.txt") || debug_build_path.empty()) + return false; + + if (!boost::filesystem::exists(debug_build_path)) { + boost::system::error_code ec; + boost::filesystem::create_directories(debug_build_path, ec); + if (ec) { + Terminal::get().print("Error: could not create " + debug_build_path.string() + ": " + ec.message() + "\n", + true); + return false; + } + } + + if (!force && boost::filesystem::exists(debug_build_path / "CMakeCache.txt")) + return true; + + Dialog::Message message("Creating/updating debug build"); + auto exit_status = Terminal::get().process(Config::get().project.cmake.command + ' ' + + filesystem::escape_argument(get_project_path().string()) + + " -DCMAKE_BUILD_TYPE=Debug", debug_build_path); + message.hide(); + if (exit_status == EXIT_SUCCESS) + return true; + return false; +} + +boost::filesystem::path +CMake::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { + // CMake does not store in compile_commands.json if an object is part of an executable or not. + // Therefore, executables are first attempted found in the cmake files. These executables + // are then used to identify if a file in compile_commands.json is part of an executable or not + + auto parameters = get_functions_parameters("add_executable"); + + std::vector cmake_executables; + for (auto ¶meter: parameters) { + if (parameter.second.size() > 1 && parameter.second[0].size() > 0 && + parameter.second[0].compare(0, 2, "${") != 0) { + auto executable = (parameter.first.parent_path() / parameter.second[0]).string(); + auto project_path_str = get_project_path().string(); + size_t pos = executable.find(project_path_str); + if (pos != std::string::npos) + executable.replace(pos, project_path_str.size(), build_path.string()); + cmake_executables.emplace_back(executable); + } + } + + + CompileCommands compile_commands(build_path); + std::vector> command_files_and_maybe_executables; + for (auto &command: compile_commands.commands) { + auto command_file = filesystem::get_normal_path(command.file); + auto values = command.parameter_values("-o"); + if (!values.empty()) { + size_t pos; + values[0].erase(0, 11); + if ((pos = values[0].find(".dir")) != std::string::npos) { + auto executable = command.directory / values[0].substr(0, pos); + command_files_and_maybe_executables.emplace_back(command_file, executable); + } + } + } + + size_t best_match_size = -1; + boost::filesystem::path best_match_executable; + + for (auto &cmake_executable: cmake_executables) { + for (auto &command_file_and_maybe_executable: command_files_and_maybe_executables) { + auto &command_file = command_file_and_maybe_executable.first; + auto &maybe_executable = command_file_and_maybe_executable.second; + if (cmake_executable == maybe_executable) { + if (command_file == file_path) + return maybe_executable; + auto command_file_directory = command_file.parent_path(); + if (filesystem::file_in_path(file_path, command_file_directory)) { + auto size = static_cast(std::distance(command_file_directory.begin(), + command_file_directory.end())); + if (best_match_size == static_cast(-1) || best_match_size < size) { + best_match_size = size; + best_match_executable = maybe_executable; + } + } + } + } + } + if (!best_match_executable.empty()) + return best_match_executable; + + for (auto &command_file_and_maybe_executable: command_files_and_maybe_executables) { + auto &command_file = command_file_and_maybe_executable.first; + auto &maybe_executable = command_file_and_maybe_executable.second; + if (command_file == file_path) + return maybe_executable; + auto command_file_directory = command_file.parent_path(); + if (filesystem::file_in_path(file_path, command_file_directory)) { + auto size = static_cast(std::distance(command_file_directory.begin(), + command_file_directory.end())); + if (best_match_size == static_cast(-1) || best_match_size < size) { + best_match_size = size; + best_match_executable = maybe_executable; + } + } + } + return best_match_executable; +} + +void CMake::read_files() { + for (auto &path: paths) + files.emplace_back(filesystem::read(path)); +} + +void CMake::remove_tabs() { + for (auto &file: files) { + for (auto &chr: file) { + if (chr == '\t') + chr = ' '; + } + } +} + +void CMake::remove_comments() { + for (auto &file: files) { + size_t pos = 0; + size_t comment_start; + bool inside_comment = false; + while (pos < file.size()) { + if (!inside_comment && file[pos] == '#') { + comment_start = pos; + inside_comment = true; + } + if (inside_comment && file[pos] == '\n') { + file.erase(comment_start, pos - comment_start); + pos -= pos - comment_start; + inside_comment = false; + } + pos++; + } + if (inside_comment) + file.erase(comment_start); + } +} + +void CMake::remove_newlines_inside_parentheses() { + for (auto &file: files) { + size_t pos = 0; + bool inside_para = false; + bool inside_quote = false; + char last_char = 0; + while (pos < file.size()) { + if (!inside_quote && file[pos] == '"' && last_char != '\\') + inside_quote = true; + else if (inside_quote && file[pos] == '"' && last_char != '\\') + inside_quote = false; + + else if (!inside_quote && file[pos] == '(') + inside_para = true; + else if (!inside_quote && file[pos] == ')') + inside_para = false; + + else if (inside_para && file[pos] == '\n') + file.replace(pos, 1, 1, ' '); + last_char = file[pos]; + pos++; + } + } +} + +void CMake::parse_variable_parameters(std::string &data) { + size_t pos = 0; + bool inside_quote = false; + char last_char = 0; + while (pos < data.size()) { + if (!inside_quote && data[pos] == '"' && last_char != '\\') { + inside_quote = true; + data.erase(pos, + 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test} + pos--; + } else if (inside_quote && data[pos] == '"' && last_char != '\\') { + inside_quote = false; + data.erase(pos, + 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test} + pos--; + } else if (!inside_quote && data[pos] == ' ' && pos + 1 < data.size() && data[pos + 1] == ' ') { + data.erase(pos, 1); + pos--; + } + + if (pos != static_cast(-1)) + last_char = data[pos]; + pos++; + } + for (auto &var: variables) { + auto pos = data.find("${" + var.first + '}'); + while (pos != std::string::npos) { + data.replace(pos, var.first.size() + 3, var.second); + pos = data.find("${" + var.first + '}'); + } + } + + //Remove variables we do not know: + pos = data.find("${"); + auto pos_end = data.find("}", pos + 2); + while (pos != std::string::npos && pos_end != std::string::npos) { + data.erase(pos, pos_end - pos + 1); + pos = data.find("${"); + pos_end = data.find("}", pos + 2); + } +} + +void CMake::parse() { + read_files(); + remove_tabs(); + remove_comments(); + remove_newlines_inside_parentheses(); + parsed = true; +} + +std::vector CMake::get_function_parameters(std::string &data) { + std::vector parameters; + size_t pos = 0; + size_t parameter_pos = 0; + bool inside_quote = false; + char last_char = 0; + while (pos < data.size()) { + if (!inside_quote && data[pos] == '"' && last_char != '\\') { + inside_quote = true; + data.erase(pos, 1); + pos--; + } else if (inside_quote && data[pos] == '"' && last_char != '\\') { + inside_quote = false; + data.erase(pos, 1); + pos--; + } else if (!inside_quote && pos + 1 < data.size() && data[pos] == ' ' && data[pos + 1] == ' ') { + data.erase(pos, 1); + pos--; + } else if (!inside_quote && data[pos] == ' ') { + parameters.emplace_back(data.substr(parameter_pos, pos - parameter_pos)); + if (pos + 1 < data.size()) + parameter_pos = pos + 1; + } + + if (pos != static_cast(-1)) + last_char = data[pos]; + pos++; + } + parameters.emplace_back(data.substr(parameter_pos)); + for (auto &var: variables) { + for (auto ¶meter: parameters) { + auto pos = parameter.find("${" + var.first + '}'); + while (pos != std::string::npos) { + parameter.replace(pos, var.first.size() + 3, var.second); + pos = parameter.find("${" + var.first + '}'); + } + } + } + return parameters; +} + +std::vector > > +CMake::get_functions_parameters(const std::string &name) { + const std::regex function_regex("^ *" + name + " *\\( *(.*)\\) *\\r?$", std::regex::icase); + variables.clear(); + if (!parsed) + parse(); + std::vector > > functions; + for (size_t c = 0; c < files.size(); ++c) { + size_t pos = 0; + while (pos < files[c].size()) { + auto start_line = pos; + auto end_line = files[c].find('\n', start_line); + if (end_line == std::string::npos) + end_line = files[c].size(); + if (end_line > start_line) { + auto line = files[c].substr(start_line, end_line - start_line); + std::smatch sm; + const static std::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *\\r?$", + std::regex::icase); + const static std::regex project_regex("^ *project *\\( *([^ ]+).*\\) *\\r?$", std::regex::icase); + if (std::regex_match(line, sm, set_regex)) { + auto data = sm[2].str(); + while (data.size() > 0 && data.back() == ' ') + data.pop_back(); + parse_variable_parameters(data); + variables[sm[1].str()] = data; + } else if (std::regex_match(line, sm, project_regex)) { + auto data = sm[1].str(); + parse_variable_parameters(data); + variables["CMAKE_PROJECT_NAME"] = data; //TODO: is this variable deprecated/non-standard? + variables["PROJECT_NAME"] = data; + } + if (std::regex_match(line, sm, function_regex)) { + auto data = sm[1].str(); + while (data.size() > 0 && data.back() == ' ') + data.pop_back(); + auto parameters = get_function_parameters(data); + functions.emplace_back(paths[c], parameters); + } + } + pos = end_line + 1; + } + } + return functions; +} diff --git a/src/buildsystem/cmake.h b/src/buildsystem/cmake.h new file mode 100644 index 00000000..9ea4c41b --- /dev/null +++ b/src/buildsystem/cmake.h @@ -0,0 +1,41 @@ +#pragma once + +#include "buildsystem.h" +#include +#include + +class CMake : public BuildSystemBase { +public: + CMake(const boost::filesystem::path &path); + + bool update_default_build(const boost::filesystem::path &default_build_path, bool force = false) override; + + bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force = false) override; + + boost::filesystem::path + get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) override; + +private: + std::vector paths; + std::vector files; + std::unordered_map variables; + + void read_files(); + + void remove_tabs(); + + void remove_comments(); + + void remove_newlines_inside_parentheses(); + + void parse_variable_parameters(std::string &data); + + void parse(); + + std::vector get_function_parameters(std::string &data); + + std::vector > > + get_functions_parameters(const std::string &name); + + bool parsed = false; +}; diff --git a/src/buildsystem/meson.cc b/src/buildsystem/meson.cc new file mode 100644 index 00000000..9b2b994c --- /dev/null +++ b/src/buildsystem/meson.cc @@ -0,0 +1,123 @@ +#include "meson.h" +#include "../filesystem.h" +#include "../compile_commands.h" +#include +#include "../terminal.h" +#include "../dialogs.h" +#include "../config.h" + +Meson::Meson(const boost::filesystem::path &path) { + const auto find_project = [](const boost::filesystem::path &file_path) { + for (auto &line: filesystem::read_lines(file_path)) { + const static std::regex project_regex("^ *project *\\(.*\\r?$", std::regex::icase); + std::smatch sm; + if (std::regex_match(line, sm, project_regex)) + return true; + } + return false; + }; + + auto search_path = boost::filesystem::is_directory(path) ? path : path.parent_path(); + while (true) { + auto search_file = search_path / "meson.build"; + if (boost::filesystem::exists(search_file)) { + if (find_project(search_file)) { + project_path = search_path; + break; + } + } + if (search_path == search_path.root_directory()) + break; + search_path = search_path.parent_path(); + } +} + +bool Meson::update_default_build(const boost::filesystem::path &default_build_path, bool force) { + if (project_path.empty() || !boost::filesystem::exists(project_path / "meson.build") || default_build_path.empty()) + return false; + + if (!boost::filesystem::exists(default_build_path)) { + boost::system::error_code ec; + boost::filesystem::create_directories(default_build_path, ec); + if (ec) { + Terminal::get().print("Error: could not create " + default_build_path.string() + ": " + ec.message() + "\n", + true); + return false; + } + } + + auto compile_commands_path = default_build_path / "compile_commands.json"; + bool compile_commands_exists = boost::filesystem::exists(compile_commands_path); + if (!force && compile_commands_exists) + return true; + + Dialog::Message message("Creating/updating default build"); + auto exit_status = Terminal::get().process( + Config::get().project.meson.command + ' ' + (compile_commands_exists ? "--internal regenerate " : "") + + filesystem::escape_argument(project_path.string()), default_build_path); + message.hide(); + if (exit_status == EXIT_SUCCESS) + return true; + return false; +} + +bool Meson::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) { + if (project_path.empty() || !boost::filesystem::exists(project_path / "meson.build") || debug_build_path.empty()) + return false; + + if (!boost::filesystem::exists(debug_build_path)) { + boost::system::error_code ec; + boost::filesystem::create_directories(debug_build_path, ec); + if (ec) { + Terminal::get().print("Error: could not create " + debug_build_path.string() + ": " + ec.message() + "\n", + true); + return false; + } + } + + bool compile_commands_exists = boost::filesystem::exists(debug_build_path / "compile_commands.json"); + if (!force && compile_commands_exists) + return true; + + Dialog::Message message("Creating/updating debug build"); + auto exit_status = Terminal::get().process( + Config::get().project.meson.command + ' ' + (compile_commands_exists ? "--internal regenerate " : "") + + "--buildtype debug " + filesystem::escape_argument(project_path.string()), debug_build_path); + message.hide(); + if (exit_status == EXIT_SUCCESS) + return true; + return false; +} + +boost::filesystem::path +Meson::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { + CompileCommands compile_commands(build_path); + + size_t best_match_size = -1; + boost::filesystem::path best_match_executable; + for (auto &command: compile_commands.commands) { + auto command_file = filesystem::get_normal_path(command.file); + auto values = command.parameter_values("-o"); + if (!values.empty()) { + size_t pos; + if ((pos = values[0].find("@")) != std::string::npos) { + if (pos + 1 < values[0].size() && values[0].compare(pos + 1, 3, "exe") == 0) { + auto executable = build_path / values[0].substr(0, pos); + if (command_file == file_path) + return executable; + auto command_file_directory = command_file.parent_path(); + if (filesystem::file_in_path(file_path, command_file_directory)) { + auto size = static_cast(std::distance(command_file_directory.begin(), + command_file_directory.end())); + if (best_match_size == static_cast(-1) || best_match_size < size) { + best_match_size = size; + best_match_executable = executable; + } + } + } + } + } + } + + return best_match_executable; +} diff --git a/src/buildsystem/meson.h b/src/buildsystem/meson.h new file mode 100644 index 00000000..17b9a540 --- /dev/null +++ b/src/buildsystem/meson.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +class Meson { +public: + Meson(const boost::filesystem::path &path); + + boost::filesystem::path project_path; + + bool update_default_build(const boost::filesystem::path &default_build_path, bool force = false); + + bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force = false); + + boost::filesystem::path + get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); +}; diff --git a/src/cmake.cc b/src/cmake.cc deleted file mode 100644 index f3945acb..00000000 --- a/src/cmake.cc +++ /dev/null @@ -1,378 +0,0 @@ -#include "cmake.h" -#include "filesystem.h" -#include "dialogs.h" -#include "config.h" -#include "terminal.h" -#include -#include "compile_commands.h" - -CMake::CMake(const boost::filesystem::path &path) { - const auto find_cmake_project=[](const boost::filesystem::path &cmake_path) { - for(auto &line: filesystem::read_lines(cmake_path)) { - const static std::regex project_regex("^ *project *\\(.*\\r?$", std::regex::icase); - std::smatch sm; - if(std::regex_match(line, sm, project_regex)) - return true; - } - return false; - }; - - auto search_path=boost::filesystem::is_directory(path)?path:path.parent_path(); - while(true) { - auto search_cmake_path=search_path/"CMakeLists.txt"; - if(boost::filesystem::exists(search_cmake_path)) { - paths.emplace(paths.begin(), search_cmake_path); - if(find_cmake_project(search_cmake_path)) { - project_path=search_path; - break; - } - } - if(search_path==search_path.root_directory()) - break; - search_path=search_path.parent_path(); - } -} - -bool CMake::update_default_build(const boost::filesystem::path &default_build_path, bool force) { - if(project_path.empty() || !boost::filesystem::exists(project_path/"CMakeLists.txt") || default_build_path.empty()) - return false; - - if(!boost::filesystem::exists(default_build_path)) { - boost::system::error_code ec; - boost::filesystem::create_directories(default_build_path, ec); - if(ec) { - Terminal::get().print("Error: could not create "+default_build_path.string()+": "+ec.message()+"\n", true); - return false; - } - } - - if(!force && boost::filesystem::exists(default_build_path/"compile_commands.json")) - return true; - - auto compile_commands_path=default_build_path/"compile_commands.json"; - Dialog::Message message("Creating/updating default build"); - auto exit_status=Terminal::get().process(Config::get().project.cmake.command+' '+ - filesystem::escape_argument(project_path.string())+" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", default_build_path); - message.hide(); - if(exit_status==EXIT_SUCCESS) { -#ifdef _WIN32 //Temporary fix to MSYS2's libclang - auto compile_commands_file=filesystem::read(compile_commands_path); - auto replace_drive = [&compile_commands_file](const std::string& param) { - size_t pos=0; - auto param_size = param.length(); - while((pos=compile_commands_file.find(param+"/", pos))!=std::string::npos) { - if(pos+param_size+1 cmake_executables; - for(auto ¶meter: parameters) { - if(parameter.second.size()>1 && parameter.second[0].size()>0 && parameter.second[0].compare(0, 2, "${")!=0) { - auto executable=(parameter.first.parent_path()/parameter.second[0]).string(); - auto project_path_str=project_path.string(); - size_t pos=executable.find(project_path_str); - if(pos!=std::string::npos) - executable.replace(pos, project_path_str.size(), build_path.string()); - cmake_executables.emplace_back(executable); - } - } - - - CompileCommands compile_commands(build_path); - std::vector> command_files_and_maybe_executables; - for(auto &command: compile_commands.commands) { - auto command_file=filesystem::get_normal_path(command.file); - auto values=command.parameter_values("-o"); - if(!values.empty()) { - size_t pos; - values[0].erase(0, 11); - if((pos=values[0].find(".dir"))!=std::string::npos) { - auto executable=command.directory/values[0].substr(0, pos); - command_files_and_maybe_executables.emplace_back(command_file, executable); - } - } - } - - size_t best_match_size=-1; - boost::filesystem::path best_match_executable; - - for(auto &cmake_executable: cmake_executables) { - for(auto &command_file_and_maybe_executable: command_files_and_maybe_executables) { - auto &command_file=command_file_and_maybe_executable.first; - auto &maybe_executable=command_file_and_maybe_executable.second; - if(cmake_executable==maybe_executable) { - if(command_file==file_path) - return maybe_executable; - auto command_file_directory=command_file.parent_path(); - if(filesystem::file_in_path(file_path, command_file_directory)) { - auto size=static_cast(std::distance(command_file_directory.begin(), command_file_directory.end())); - if(best_match_size==static_cast(-1) || best_match_size(std::distance(command_file_directory.begin(), command_file_directory.end())); - if(best_match_size==static_cast(-1) || best_match_size(-1)) - last_char=data[pos]; - pos++; - } - for(auto &var: variables) { - auto pos=data.find("${"+var.first+'}'); - while(pos!=std::string::npos) { - data.replace(pos, var.first.size()+3, var.second); - pos=data.find("${"+var.first+'}'); - } - } - - //Remove variables we do not know: - pos=data.find("${"); - auto pos_end=data.find("}", pos+2); - while(pos!=std::string::npos && pos_end!=std::string::npos) { - data.erase(pos, pos_end-pos+1); - pos=data.find("${"); - pos_end=data.find("}", pos+2); - } -} - -void CMake::parse() { - read_files(); - remove_tabs(); - remove_comments(); - remove_newlines_inside_parentheses(); - parsed=true; -} - -std::vector CMake::get_function_parameters(std::string &data) { - std::vector parameters; - size_t pos=0; - size_t parameter_pos=0; - bool inside_quote=false; - char last_char=0; - while(pos(-1)) - last_char=data[pos]; - pos++; - } - parameters.emplace_back(data.substr(parameter_pos)); - for(auto &var: variables) { - for(auto ¶meter: parameters) { - auto pos=parameter.find("${"+var.first+'}'); - while(pos!=std::string::npos) { - parameter.replace(pos, var.first.size()+3, var.second); - pos=parameter.find("${"+var.first+'}'); - } - } - } - return parameters; -} - -std::vector > > CMake::get_functions_parameters(const std::string &name) { - const std::regex function_regex("^ *"+name+" *\\( *(.*)\\) *\\r?$", std::regex::icase); - variables.clear(); - if(!parsed) - parse(); - std::vector > > functions; - for(size_t c=0;cstart_line) { - auto line=files[c].substr(start_line, end_line-start_line); - std::smatch sm; - const static std::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *\\r?$", std::regex::icase); - const static std::regex project_regex("^ *project *\\( *([^ ]+).*\\) *\\r?$", std::regex::icase); - if(std::regex_match(line, sm, set_regex)) { - auto data=sm[2].str(); - while(data.size()>0 && data.back()==' ') - data.pop_back(); - parse_variable_parameters(data); - variables[sm[1].str()]=data; - } - else if(std::regex_match(line, sm, project_regex)) { - auto data=sm[1].str(); - parse_variable_parameters(data); - variables["CMAKE_PROJECT_NAME"]=data; //TODO: is this variable deprecated/non-standard? - variables["PROJECT_NAME"]=data; - } - if(std::regex_match(line, sm, function_regex)) { - auto data=sm[1].str(); - while(data.size()>0 && data.back()==' ') - data.pop_back(); - auto parameters=get_function_parameters(data); - functions.emplace_back(paths[c], parameters); - } - } - pos=end_line+1; - } - } - return functions; -} diff --git a/src/cmake.h b/src/cmake.h deleted file mode 100644 index f87010a3..00000000 --- a/src/cmake.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include -#include -#include -#include - -class CMake { -public: - CMake(const boost::filesystem::path &path); - boost::filesystem::path project_path; - - bool update_default_build(const boost::filesystem::path &default_build_path, bool force=false); - bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force=false); - - boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); - -private: - std::vector paths; - std::vector files; - std::unordered_map variables; - void read_files(); - void remove_tabs(); - void remove_comments(); - void remove_newlines_inside_parentheses(); - void parse_variable_parameters(std::string &data); - void parse(); - std::vector get_function_parameters(std::string &data); - std::vector > > get_functions_parameters(const std::string &name); - bool parsed=false; -}; diff --git a/src/compile_commands.cc b/src/compile_commands.cc index 5f11afe8..7f283d8d 100644 --- a/src/compile_commands.cc +++ b/src/compile_commands.cc @@ -4,161 +4,159 @@ #include std::vector CompileCommands::Command::parameter_values(const std::string ¶meter_name) const { - std::vector parameter_values; - - bool found_argument=false; - for(auto ¶meter: parameters) { - if(found_argument) { - parameter_values.emplace_back(parameter); - found_argument=false; + std::vector parameter_values; + + bool found_argument = false; + for (auto ¶meter: parameters) { + if (found_argument) { + parameter_values.emplace_back(parameter); + found_argument = false; + } else if (parameter == parameter_name) + found_argument = true; } - else if(parameter==parameter_name) - found_argument=true; - } - - return parameter_values; + + return parameter_values; } CompileCommands::CompileCommands(const boost::filesystem::path &build_path) { - try { - boost::property_tree::ptree root_pt; - boost::property_tree::json_parser::read_json((build_path/"compile_commands.json").string(), root_pt); - - auto commands_pt=root_pt.get_child(""); - for(auto &command: commands_pt) { - boost::filesystem::path directory=command.second.get("directory"); - auto parameters_str=command.second.get("command"); - boost::filesystem::path file=command.second.get("file"); - - std::vector parameters; - bool backslash=false; - bool single_quote=false; - bool double_quote=false; - size_t parameter_start_pos=std::string::npos; - size_t parameter_size=0; - auto add_parameter=[¶meters, ¶meters_str, ¶meter_start_pos, ¶meter_size] { - auto parameter=parameters_str.substr(parameter_start_pos, parameter_size); - // Remove escaping - for(size_t c=0;c("directory"); + auto parameters_str = command.second.get("command"); + boost::filesystem::path file = command.second.get("file"); + + std::vector parameters; + bool backslash = false; + bool single_quote = false; + bool double_quote = false; + size_t parameter_start_pos = std::string::npos; + size_t parameter_size = 0; + auto add_parameter = [¶meters, ¶meters_str, ¶meter_start_pos, ¶meter_size] { + auto parameter = parameters_str.substr(parameter_start_pos, parameter_size); + // Remove escaping + for (size_t c = 0; c < parameter.size() - 1; ++c) { + if (parameter[c] == '\\') + parameter.replace(c, 2, std::string() + parameter[c + 1]); + } + parameters.emplace_back(parameter); + }; + for (size_t c = 0; c < parameters_str.size(); ++c) { + if (backslash) + backslash = false; + else if (parameters_str[c] == '\\') + backslash = true; + else if ((parameters_str[c] == ' ' || parameters_str[c] == '\t') && !backslash && !single_quote && + !double_quote) { + if (parameter_start_pos != std::string::npos) { + add_parameter(); + parameter_start_pos = std::string::npos; + parameter_size = 0; + } + continue; + } else if (parameters_str[c] == '\'' && !backslash && !double_quote) { + single_quote = !single_quote; + continue; + } else if (parameters_str[c] == '\"' && !backslash && !single_quote) { + double_quote = !double_quote; + continue; + } + + if (parameter_start_pos == std::string::npos) + parameter_start_pos = c; + ++parameter_size; + } + if (parameter_start_pos != std::string::npos) + add_parameter(); + + commands.emplace_back(Command{directory, parameters, boost::filesystem::absolute(file, build_path)}); } - - if(parameter_start_pos==std::string::npos) - parameter_start_pos=c; - ++parameter_size; - } - if(parameter_start_pos!=std::string::npos) - add_parameter(); - - commands.emplace_back(Command{directory, parameters, boost::filesystem::absolute(file, build_path)}); } - } - catch(...) {} + catch (...) {} } -std::vector CompileCommands::get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { - std::string default_std_argument="-std=c++1y"; - - std::vector arguments; - if(!build_path.empty()) { - clangmm::CompilationDatabase db(build_path.string()); - if(db) { - clangmm::CompileCommands commands(file_path.string(), db); - auto cmds = commands.get_commands(); - for (auto &cmd : cmds) { - auto cmd_arguments = cmd.get_arguments(); - bool ignore_next=false; - for (size_t c = 1; c < cmd_arguments.size(); c++) { - if(ignore_next) { - ignore_next=false; - continue; - } - else if(cmd_arguments[c]=="-o" || cmd_arguments[c]=="-c") { - ignore_next=true; - continue; - } - arguments.emplace_back(cmd_arguments[c]); - } - } - } - else - arguments.emplace_back(default_std_argument); - } - else - arguments.emplace_back(default_std_argument); - - auto clang_version_string=clangmm::to_string(clang_getClangVersion()); - const static 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/lib64/clang/"+clang_version+"/include"); // For Fedora -#if defined(__APPLE__) && CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR<32 // TODO: remove during 2018 if llvm3.7 is no longer in homebrew (CINDEX_VERSION_MINOR=32 equals clang-3.8 I think) - arguments.emplace_back("-I/usr/local/Cellar/llvm/"+clang_version+"/lib/clang/"+clang_version+"/include"); - arguments.emplace_back("-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1"); - arguments.emplace_back("-I/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1"); //Added for OS X 10.11 +std::vector +CompileCommands::get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { + std::string default_std_argument = "-std=c++1y"; + + std::vector arguments; + if (!build_path.empty()) { + clangmm::CompilationDatabase db(build_path.string()); + if (db) { + clangmm::CompileCommands commands(file_path.string(), db); + auto cmds = commands.get_commands(); + for (auto &cmd : cmds) { + auto cmd_arguments = cmd.get_arguments(); + bool ignore_next = false; + for (size_t c = 1; c < cmd_arguments.size(); c++) { + if (ignore_next) { + ignore_next = false; + continue; + } else if (cmd_arguments[c] == "-o" || cmd_arguments[c] == "-c") { + ignore_next = true; + continue; + } + arguments.emplace_back(cmd_arguments[c]); + } + } + } else + arguments.emplace_back(default_std_argument); + } else + arguments.emplace_back(default_std_argument); + + auto clang_version_string = clangmm::to_string(clang_getClangVersion()); + const static 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/lib64/clang/" + clang_version + "/include"); // For Fedora +#if defined(__APPLE__) && CINDEX_VERSION_MAJOR == 0 && CINDEX_VERSION_MINOR < 32 // TODO: remove during 2018 if llvm3.7 is no longer in homebrew (CINDEX_VERSION_MINOR=32 equals clang-3.8 I think) + arguments.emplace_back("-I/usr/local/Cellar/llvm/"+clang_version+"/lib/clang/"+clang_version+"/include"); + arguments.emplace_back("-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1"); + arguments.emplace_back("-I/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1"); //Added for OS X 10.11 #endif #ifdef _WIN32 - auto env_msystem_prefix=std::getenv("MSYSTEM_PREFIX"); - if(env_msystem_prefix!=nullptr) - arguments.emplace_back("-I"+(boost::filesystem::path(env_msystem_prefix)/"lib/clang"/clang_version/"include").string()); + auto env_msystem_prefix = std::getenv("MSYSTEM_PREFIX"); + if (env_msystem_prefix != nullptr) + arguments.emplace_back("-I" + (boost::filesystem::path(env_msystem_prefix) / "lib/clang" / clang_version / + "include").string()); #endif - } - arguments.emplace_back("-fretain-comments-from-system-headers"); - - auto extension=file_path.extension().string(); - if(extension==".h" || //TODO: temporary fix for .h-files (parse as c++) - extension!=".c") - arguments.emplace_back("-xc++"); - - if(extension.empty() || (1 #include #include class CompileCommands { public: - class Command { - public: - boost::filesystem::path directory; - std::vector parameters; - boost::filesystem::path file; - - std::vector parameter_values(const std::string ¶meter_name) const; - }; - - CompileCommands(const boost::filesystem::path &build_path); - std::vector commands; - - /// Return arguments for the given file using libclangmm - static std::vector get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); + class Command { + public: + boost::filesystem::path directory; + std::vector parameters; + boost::filesystem::path file; + + std::vector parameter_values(const std::string ¶meter_name) const; + }; + + CompileCommands(const boost::filesystem::path &build_path); + + std::vector commands; + + /// Return arguments for the given file using libclangmm + static std::vector + get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); }; diff --git a/src/config.cc b/src/config.cc index 934b4388..625da547 100644 --- a/src/config.cc +++ b/src/config.cc @@ -7,198 +7,203 @@ #include Config::Config() { - home_path=filesystem::get_home_path(); - if(home_path.empty()) - throw std::runtime_error("Could not find home path"); - home_juci_path=home_path/".juci"; + home_path = filesystem::get_home_path(); + if (home_path.empty()) + throw std::runtime_error("Could not find home path"); + home_juci_path = home_path / ".juci"; } void Config::load() { - auto config_json = (home_juci_path/"config"/"config.json").string(); // This causes some redundant copies, but assures windows support - boost::property_tree::ptree cfg; - try { - find_or_create_config_files(); - boost::property_tree::json_parser::read_json(config_json, cfg); - update(cfg); - read(cfg); - } - catch(const std::exception &e) { - dispatcher.post([config_json, e_what=std::string(e.what())] { - ::Terminal::get().print("Error: could not parse "+config_json+": "+e_what+"\n", true); - }); - std::stringstream ss; - ss << default_config_file; - boost::property_tree::read_json(ss, cfg); - read(cfg); - } + auto config_json = (home_juci_path / "config" / + "config.json").string(); // This causes some redundant copies, but assures windows support + boost::property_tree::ptree cfg; + try { + find_or_create_config_files(); + boost::property_tree::json_parser::read_json(config_json, cfg); + update(cfg); + read(cfg); + } + catch (const std::exception &e) { + dispatcher.post([config_json, e_what = std::string(e.what())] { + ::Terminal::get().print("Error: could not parse " + config_json + ": " + e_what + "\n", true); + }); + std::stringstream ss; + ss << default_config_file; + boost::property_tree::read_json(ss, cfg); + read(cfg); + } } void Config::find_or_create_config_files() { - auto config_dir = home_juci_path/"config"; - auto config_json = config_dir/"config.json"; - - boost::filesystem::create_directories(config_dir); // io exp captured by calling method - - if (!boost::filesystem::exists(config_json)) - filesystem::write(config_json, default_config_file); - - auto juci_style_path = home_juci_path/"styles"; - boost::filesystem::create_directories(juci_style_path); // io exp captured by calling method - - juci_style_path/="juci-light.xml"; - if(!boost::filesystem::exists(juci_style_path)) - filesystem::write(juci_style_path, juci_light_style); - juci_style_path=juci_style_path.parent_path(); - juci_style_path/="juci-dark.xml"; - if(!boost::filesystem::exists(juci_style_path)) - filesystem::write(juci_style_path, juci_dark_style); - juci_style_path=juci_style_path.parent_path(); - juci_style_path/="juci-dark-blue.xml"; - if(!boost::filesystem::exists(juci_style_path)) - filesystem::write(juci_style_path, juci_dark_blue_style); + auto config_dir = home_juci_path / "config"; + auto config_json = config_dir / "config.json"; + + boost::filesystem::create_directories(config_dir); // io exp captured by calling method + + if (!boost::filesystem::exists(config_json)) + filesystem::write(config_json, default_config_file); + + auto juci_style_path = home_juci_path / "styles"; + boost::filesystem::create_directories(juci_style_path); // io exp captured by calling method + + juci_style_path /= "juci-light.xml"; + if (!boost::filesystem::exists(juci_style_path)) + filesystem::write(juci_style_path, juci_light_style); + juci_style_path = juci_style_path.parent_path(); + juci_style_path /= "juci-dark.xml"; + if (!boost::filesystem::exists(juci_style_path)) + filesystem::write(juci_style_path, juci_dark_style); + juci_style_path = juci_style_path.parent_path(); + juci_style_path /= "juci-dark-blue.xml"; + if (!boost::filesystem::exists(juci_style_path)) + filesystem::write(juci_style_path, juci_dark_blue_style); } void Config::update(boost::property_tree::ptree &cfg) { - boost::property_tree::ptree default_cfg; - bool cfg_ok=true; - if(cfg.get("version")!=JUCI_VERSION) { - std::stringstream ss; - ss << default_config_file; - boost::property_tree::read_json(ss, default_cfg); - cfg_ok=false; - auto it_version=cfg.find("version"); - if(it_version!=cfg.not_found()) { - make_version_dependent_corrections(cfg, default_cfg, it_version->second.data()); - it_version->second.data()=JUCI_VERSION; - } - - auto style_path=home_juci_path/"styles"; - filesystem::write(style_path/"juci-light.xml", juci_light_style); - filesystem::write(style_path/"juci-dark.xml", juci_dark_style); - filesystem::write(style_path/"juci-dark-blue.xml", juci_dark_blue_style); - } - else - return; - cfg_ok&=add_missing_nodes(cfg, default_cfg); - cfg_ok&=remove_deprecated_nodes(cfg, default_cfg); - if(!cfg_ok) - boost::property_tree::write_json((home_juci_path/"config"/"config.json").string(), cfg); -} + boost::property_tree::ptree default_cfg; + bool cfg_ok = true; + if (cfg.get("version") != JUCI_VERSION) { + std::stringstream ss; + ss << default_config_file; + boost::property_tree::read_json(ss, default_cfg); + cfg_ok = false; + auto it_version = cfg.find("version"); + if (it_version != cfg.not_found()) { + make_version_dependent_corrections(cfg, default_cfg, it_version->second.data()); + it_version->second.data() = JUCI_VERSION; + } -void Config::make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version) { - auto &keybindings_cfg=cfg.get_child("keybindings"); - try { - if(version<="1.2.4") { - auto it_file_print=keybindings_cfg.find("print"); - if(it_file_print!=keybindings_cfg.not_found() && it_file_print->second.data()=="p") { - dispatcher.post([] { - ::Terminal::get().print("Preference change: keybindings.print set to \"\"\n"); - }); - it_file_print->second.data()=""; - } - } - } - catch(const std::exception &e) { - std::cerr << "Error correcting preferences: " << e.what() << std::endl; - } + auto style_path = home_juci_path / "styles"; + filesystem::write(style_path / "juci-light.xml", juci_light_style); + filesystem::write(style_path / "juci-dark.xml", juci_dark_style); + filesystem::write(style_path / "juci-dark-blue.xml", juci_dark_blue_style); + } else + return; + cfg_ok &= add_missing_nodes(cfg, default_cfg); + cfg_ok &= remove_deprecated_nodes(cfg, default_cfg); + if (!cfg_ok) + boost::property_tree::write_json((home_juci_path / "config" / "config.json").string(), cfg); } -bool Config::add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) { - if(parent_path.size()>0) - parent_path+="."; - bool unchanged=true; - for(auto &node: default_cfg) { - auto path=parent_path+node.first; +void Config::make_version_dependent_corrections(boost::property_tree::ptree &cfg, + const boost::property_tree::ptree &default_cfg, + const std::string &version) { + auto &keybindings_cfg = cfg.get_child("keybindings"); try { - cfg.get(path); + if (version <= "1.2.4") { + auto it_file_print = keybindings_cfg.find("print"); + if (it_file_print != keybindings_cfg.not_found() && it_file_print->second.data() == "p") { + dispatcher.post([] { + ::Terminal::get().print("Preference change: keybindings.print set to \"\"\n"); + }); + it_file_print->second.data() = ""; + } + } } - catch(const std::exception &e) { - cfg.add(path, node.second.data()); - unchanged=false; + catch (const std::exception &e) { + std::cerr << "Error correcting preferences: " << e.what() << std::endl; } - unchanged&=add_missing_nodes(cfg, node.second, path); - } - return unchanged; } -bool Config::remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) { - if(parent_path.size()>0) - parent_path+="."; - bool unchanged=true; - for(auto it=cfg.begin();it!=cfg.end();) { - auto path=parent_path+it->first; - try { - default_cfg.get(path); - unchanged&=remove_deprecated_nodes(it->second, default_cfg, path); - ++it; +bool Config::add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, + std::string parent_path) { + if (parent_path.size() > 0) + parent_path += "."; + bool unchanged = true; + for (auto &node: default_cfg) { + auto path = parent_path + node.first; + try { + cfg.get(path); + } + catch (const std::exception &e) { + cfg.add(path, node.second.data()); + unchanged = false; + } + unchanged &= add_missing_nodes(cfg, node.second, path); } - catch(const std::exception &e) { - it=cfg.erase(it); - unchanged=false; + return unchanged; +} + +bool Config::remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, + std::string parent_path) { + if (parent_path.size() > 0) + parent_path += "."; + bool unchanged = true; + for (auto it = cfg.begin(); it != cfg.end();) { + auto path = parent_path + it->first; + try { + default_cfg.get(path); + unchanged &= remove_deprecated_nodes(it->second, default_cfg, path); + ++it; + } + catch (const std::exception &e) { + it = cfg.erase(it); + unchanged = false; + } } - } - return unchanged; + return unchanged; } void Config::read(const boost::property_tree::ptree &cfg) { - auto keybindings_pt = cfg.get_child("keybindings"); - for (auto &i : keybindings_pt) { - menu.keys[i.first] = i.second.get_value(); - } - - auto source_json = cfg.get_child("source"); - source.style=source_json.get("style"); - source.font=source_json.get("font"); - source.cleanup_whitespace_characters=source_json.get("cleanup_whitespace_characters"); - source.show_whitespace_characters=source_json.get("show_whitespace_characters"); - source.format_style_on_save=source_json.get("format_style_on_save"); - source.format_style_on_save_if_style_file_found=source_json.get("format_style_on_save_if_style_file_found"); - source.smart_brackets=source_json.get("smart_brackets"); - source.smart_inserts=source_json.get("smart_inserts"); - if(source.smart_inserts) - source.smart_brackets=true; - source.show_map = source_json.get("show_map"); - source.map_font_size = source_json.get("map_font_size"); - source.show_git_diff = source_json.get("show_git_diff"); - source.show_background_pattern = source_json.get("show_background_pattern"); - source.show_right_margin = source_json.get("show_right_margin"); - source.right_margin_position = source_json.get("right_margin_position"); - source.spellcheck_language = source_json.get("spellcheck_language"); - source.default_tab_char = source_json.get("default_tab_char"); - source.default_tab_size = source_json.get("default_tab_size"); - source.auto_tab_char_and_size = source_json.get("auto_tab_char_and_size"); - source.tab_indents_line = source_json.get("tab_indents_line"); - source.wrap_lines = source_json.get("wrap_lines"); - source.highlight_current_line = source_json.get("highlight_current_line"); - source.show_line_numbers = source_json.get("show_line_numbers"); - source.enable_multiple_cursors = source_json.get("enable_multiple_cursors"); - source.auto_reload_changed_files = source_json.get("auto_reload_changed_files"); - source.clang_format_style = source_json.get("clang_format_style"); - source.clang_usages_threads = static_cast(source_json.get("clang_usages_threads")); - auto pt_doc_search=cfg.get_child("documentation_searches"); - for(auto &pt_doc_search_lang: pt_doc_search) { - source.documentation_searches[pt_doc_search_lang.first].separator=pt_doc_search_lang.second.get("separator"); - auto &queries=source.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(); + auto keybindings_pt = cfg.get_child("keybindings"); + for (auto &i : keybindings_pt) { + menu.keys[i.first] = i.second.get_value(); } - } - - window.theme_name=cfg.get("gtk_theme.name"); - window.theme_variant=cfg.get("gtk_theme.variant"); - window.version = cfg.get("version"); - - project.default_build_path=cfg.get("project.default_build_path"); - project.debug_build_path=cfg.get("project.debug_build_path"); - project.cmake.command=cfg.get("project.cmake.command"); - project.cmake.compile_command=cfg.get("project.cmake.compile_command"); - project.meson.command=cfg.get("project.meson.command"); - project.meson.compile_command=cfg.get("project.meson.compile_command"); - project.save_on_compile_or_run=cfg.get("project.save_on_compile_or_run"); - project.clear_terminal_on_compile=cfg.get("project.clear_terminal_on_compile"); - project.ctags_command=cfg.get("project.ctags_command"); - project.python_command=cfg.get("project.python_command"); - - terminal.history_size=cfg.get("terminal.history_size"); - terminal.font=cfg.get("terminal.font"); + + auto source_json = cfg.get_child("source"); + source.style = source_json.get("style"); + source.font = source_json.get("font"); + source.cleanup_whitespace_characters = source_json.get("cleanup_whitespace_characters"); + source.show_whitespace_characters = source_json.get("show_whitespace_characters"); + source.format_style_on_save = source_json.get("format_style_on_save"); + source.format_style_on_save_if_style_file_found = source_json.get("format_style_on_save_if_style_file_found"); + source.smart_brackets = source_json.get("smart_brackets"); + source.smart_inserts = source_json.get("smart_inserts"); + if (source.smart_inserts) + source.smart_brackets = true; + source.show_map = source_json.get("show_map"); + source.map_font_size = source_json.get("map_font_size"); + source.show_git_diff = source_json.get("show_git_diff"); + source.show_background_pattern = source_json.get("show_background_pattern"); + source.show_right_margin = source_json.get("show_right_margin"); + source.right_margin_position = source_json.get("right_margin_position"); + source.spellcheck_language = source_json.get("spellcheck_language"); + source.default_tab_char = source_json.get("default_tab_char"); + source.default_tab_size = source_json.get("default_tab_size"); + source.auto_tab_char_and_size = source_json.get("auto_tab_char_and_size"); + source.tab_indents_line = source_json.get("tab_indents_line"); + source.wrap_lines = source_json.get("wrap_lines"); + source.highlight_current_line = source_json.get("highlight_current_line"); + source.show_line_numbers = source_json.get("show_line_numbers"); + source.enable_multiple_cursors = source_json.get("enable_multiple_cursors"); + source.auto_reload_changed_files = source_json.get("auto_reload_changed_files"); + source.clang_format_style = source_json.get("clang_format_style"); + source.clang_usages_threads = static_cast(source_json.get("clang_usages_threads")); + auto pt_doc_search = cfg.get_child("documentation_searches"); + for (auto &pt_doc_search_lang: pt_doc_search) { + source.documentation_searches[pt_doc_search_lang.first].separator = pt_doc_search_lang.second.get( + "separator"); + auto &queries = source.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(); + } + } + + window.theme_name = cfg.get("gtk_theme.name"); + window.theme_variant = cfg.get("gtk_theme.variant"); + window.version = cfg.get("version"); + + project.default_build_path = cfg.get("project.default_build_path"); + project.debug_build_path = cfg.get("project.debug_build_path"); + project.cmake.command = cfg.get("project.cmake.command"); + project.cmake.compile_command = cfg.get("project.cmake.compile_command"); + project.meson.command = cfg.get("project.meson.command"); + project.meson.compile_command = cfg.get("project.meson.compile_command"); + project.save_on_compile_or_run = cfg.get("project.save_on_compile_or_run"); + project.clear_terminal_on_compile = cfg.get("project.clear_terminal_on_compile"); + project.ctags_command = cfg.get("project.ctags_command"); + project.python_command = cfg.get("project.python_command"); + + terminal.history_size = cfg.get("terminal.history_size"); + terminal.font = cfg.get("terminal.font"); } diff --git a/src/config.h b/src/config.h index 1c1000be..5ec14d86 100644 --- a/src/config.h +++ b/src/config.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -9,117 +10,129 @@ class Config { public: - class Menu { - public: - std::unordered_map keys; - }; - - class Window { - public: - std::string theme_name; - std::string theme_variant; - std::string version; - }; - - class Terminal { - public: - int history_size; - std::string font; - }; - - class Project { - public: - class CMake { + class Menu { + public: + std::unordered_map keys; + }; + + class Window { public: - std::string command; - std::string compile_command; + std::string theme_name; + std::string theme_variant; + std::string version; }; - class Meson { + + class Terminal { public: - std::string command; - std::string compile_command; + int history_size; + std::string font; }; - - std::string default_build_path; - std::string debug_build_path; - CMake cmake; - Meson meson; - bool save_on_compile_or_run; - bool clear_terminal_on_compile; - std::string ctags_command; - std::string python_command; - }; - - class Source { - public: - class DocumentationSearch { + + class Project { public: - std::string separator; - std::unordered_map queries; + class CMake { + public: + std::string command; + std::string compile_command; + }; + + class Meson { + public: + std::string command; + std::string compile_command; + }; + + std::string default_build_path; + std::string debug_build_path; + CMake cmake; + Meson meson; + bool save_on_compile_or_run; + bool clear_terminal_on_compile; + std::string ctags_command; + std::string python_command; + }; + + class Source { + public: + class DocumentationSearch { + public: + std::string separator; + std::unordered_map queries; + }; + + std::string style; + std::string font; + std::string spellcheck_language; + + bool cleanup_whitespace_characters; + std::string show_whitespace_characters; + + bool format_style_on_save; + bool format_style_on_save_if_style_file_found; + + bool smart_brackets; + bool smart_inserts; + + bool show_map; + std::string map_font_size; + bool show_git_diff; + bool show_background_pattern; + bool show_right_margin; + unsigned right_margin_position; + + bool auto_tab_char_and_size; + char default_tab_char; + unsigned default_tab_size; + bool tab_indents_line; + bool wrap_lines; + bool highlight_current_line; + bool show_line_numbers; + bool enable_multiple_cursors; + bool auto_reload_changed_files; + + std::string clang_format_style; + unsigned clang_usages_threads; + + std::unordered_map documentation_searches; }; - - std::string style; - std::string font; - std::string spellcheck_language; - - bool cleanup_whitespace_characters; - std::string show_whitespace_characters; - - bool format_style_on_save; - bool format_style_on_save_if_style_file_found; - - bool smart_brackets; - bool smart_inserts; - - bool show_map; - std::string map_font_size; - bool show_git_diff; - bool show_background_pattern; - bool show_right_margin; - unsigned right_margin_position; - - bool auto_tab_char_and_size; - char default_tab_char; - unsigned default_tab_size; - bool tab_indents_line; - bool wrap_lines; - bool highlight_current_line; - bool show_line_numbers; - bool enable_multiple_cursors; - bool auto_reload_changed_files; - - std::string clang_format_style; - unsigned clang_usages_threads; - - std::unordered_map documentation_searches; - }; + private: - Config(); + Config(); + public: - static Config &get() { - static Config singleton; - return singleton; - } - - void load(); - - Menu menu; - Window window; - Terminal terminal; - Project project; - Source source; - - boost::filesystem::path home_path; - boost::filesystem::path home_juci_path; + static Config &get() { + static Config singleton; + return singleton; + } + + void load(); + + Menu menu; + Window window; + Terminal terminal; + Project project; + Source source; + + boost::filesystem::path home_path; + boost::filesystem::path home_juci_path; private: - /// Used to dispatch Terminal outputs after juCi++ GUI setup and configuration - Dispatcher dispatcher; - - void find_or_create_config_files(); - void update(boost::property_tree::ptree &cfg); - void make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version); - bool add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path=""); - bool remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path=""); - void read(const boost::property_tree::ptree &cfg); + /// Used to dispatch Terminal outputs after juCi++ GUI setup and configuration + Dispatcher dispatcher; + + void find_or_create_config_files(); + + void update(boost::property_tree::ptree &cfg); + + void + make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, + const std::string &version); + + bool add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, + std::string parent_path = ""); + + bool remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, + std::string parent_path = ""); + + void read(const boost::property_tree::ptree &cfg); }; diff --git a/src/ctags.cc b/src/ctags.cc index 31a0845f..e51213c1 100644 --- a/src/ctags.cc +++ b/src/ctags.cc @@ -8,199 +8,199 @@ #include #include -std::pair > Ctags::get_result(const boost::filesystem::path &path) { - auto build=Project::Build::create(path); - auto run_path=build->project_path; - std::string exclude; - if(!run_path.empty()) { - auto relative_default_path=filesystem::get_relative_path(build->get_default_path(), run_path); - if(!relative_default_path.empty()) - exclude+=" --exclude="+relative_default_path.string(); - - auto relative_debug_path=filesystem::get_relative_path(build->get_debug_path(), run_path); - if(!relative_debug_path.empty()) - exclude+=" --exclude="+relative_debug_path.string(); - } - else { - boost::system::error_code ec; - if(boost::filesystem::is_directory(path, ec) || ec) - run_path=path; - else - run_path=path.parent_path(); - } - - std::stringstream stdin_stream; - //TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below - auto stdout_stream=std::make_unique(); - auto command=Config::get().project.ctags_command+exclude+" --fields=ns --sort=foldcase -I \"override noexcept\" -f - -R *"; - Terminal::get().process(stdin_stream, *stdout_stream, command, run_path); - return {run_path, std::move(stdout_stream)}; +std::pair > +Ctags::get_result(const boost::filesystem::path &path) { + auto build = Project::Build::create(path); + auto run_path = build->project_path; + std::string exclude; + if (!run_path.empty()) { + auto relative_default_path = filesystem::get_relative_path(build->get_default_path(), run_path); + if (!relative_default_path.empty()) + exclude += " --exclude=" + relative_default_path.string(); + + auto relative_debug_path = filesystem::get_relative_path(build->get_debug_path(), run_path); + if (!relative_debug_path.empty()) + exclude += " --exclude=" + relative_debug_path.string(); + } else { + boost::system::error_code ec; + if (boost::filesystem::is_directory(path, ec) || ec) + run_path = path; + else + run_path = path.parent_path(); + } + + std::stringstream stdin_stream; + //TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below + auto stdout_stream = std::make_unique(); + auto command = Config::get().project.ctags_command + exclude + + " --fields=ns --sort=foldcase -I \"override noexcept\" -f - -R *"; + Terminal::get().process(stdin_stream, *stdout_stream, command, run_path); + return {run_path, std::move(stdout_stream)}; } Ctags::Location Ctags::get_location(const std::string &line, bool markup) { - Location location; + Location location; #ifdef _WIN32 - auto line_fixed=line; - if(!line_fixed.empty() && line_fixed.back()=='\r') - line_fixed.pop_back(); + auto line_fixed = line; + if (!line_fixed.empty() && line_fixed.back() == '\r') + line_fixed.pop_back(); #else - auto &line_fixed=line; + auto &line_fixed=line; #endif - const static std::regex regex("^([^\t]+)\t([^\t]+)\t(?:/\\^)?([ \t]*)(.+?)(\\$/)?;\"\tline:([0-9]+)\t?[a-zA-Z]*:?(.*)$"); - std::smatch sm; - if(std::regex_match(line_fixed, sm, regex)) { - location.symbol=sm[1].str(); - //fix location.symbol for operators - if(9='a' && chr<='z') || (chr>='A' && chr<='Z') || (chr>='0' && chr<='9') || chr=='_')) - location.symbol.erase(8, 1); - } - - location.file_path=sm[2].str(); - location.source=sm[4].str(); - try { - location.line=std::stoul(sm[6])-1; - } - catch(const std::exception&) { - location.line=0; - } - location.scope=sm[7].str(); - if(!sm[5].str().empty()) { - location.index=sm[3].str().size(); - - size_t pos=location.source.find(location.symbol); - if(pos!=std::string::npos) - location.index+=pos; - - if(markup) { - location.source=Glib::Markup::escape_text(location.source); - auto symbol=Glib::Markup::escape_text(location.symbol); - pos=-1; - while((pos=location.source.find(symbol, pos+1))!=std::string::npos) { - location.source.insert(pos+symbol.size(), ""); - location.source.insert(pos, ""); - pos+=7+symbol.size(); + const static std::regex regex( + "^([^\t]+)\t([^\t]+)\t(?:/\\^)?([ \t]*)(.+?)(\\$/)?;\"\tline:([0-9]+)\t?[a-zA-Z]*:?(.*)$"); + std::smatch sm; + if (std::regex_match(line_fixed, sm, regex)) { + location.symbol = sm[1].str(); + //fix location.symbol for operators + if (9 < location.symbol.size() && location.symbol[8] == ' ' && location.symbol.compare(0, 8, "operator") == 0) { + auto &chr = location.symbol[9]; + if (!((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr == '_')) + location.symbol.erase(8, 1); } - } - } - else { - location.index=0; - location.source=location.symbol; - if(markup) - location.source=""+Glib::Markup::escape_text(location.source)+""; - } - } - else - std::cerr << "Warning (ctags): please report to the juCi++ project that the following line was not parsed:\n" << line << std::endl; - - return location; + + location.file_path = sm[2].str(); + location.source = sm[4].str(); + try { + location.line = std::stoul(sm[6]) - 1; + } + catch (const std::exception &) { + location.line = 0; + } + location.scope = sm[7].str(); + if (!sm[5].str().empty()) { + location.index = sm[3].str().size(); + + size_t pos = location.source.find(location.symbol); + if (pos != std::string::npos) + location.index += pos; + + if (markup) { + location.source = Glib::Markup::escape_text(location.source); + auto symbol = Glib::Markup::escape_text(location.symbol); + pos = -1; + while ((pos = location.source.find(symbol, pos + 1)) != std::string::npos) { + location.source.insert(pos + symbol.size(), ""); + location.source.insert(pos, ""); + pos += 7 + symbol.size(); + } + } + } else { + location.index = 0; + location.source = location.symbol; + if (markup) + location.source = "" + Glib::Markup::escape_text(location.source) + ""; + } + } else + std::cerr << "Warning (ctags): please report to the juCi++ project that the following line was not parsed:\n" + << line << std::endl; + + return location; } ///Split up a type into its various significant parts std::vector Ctags::get_type_parts(const std::string type) { - std::vector parts; - size_t text_start=-1; - for(size_t c=0;c='0' && chr<='9') || (chr>='a' && chr<='z') || (chr>='A' && chr<='Z') || chr=='_' || chr=='~') { - if(text_start==static_cast(-1)) - text_start=c; - } - else { - if(text_start!=static_cast(-1)) { - parts.emplace_back(type.substr(text_start, c-text_start)); - text_start=-1; - } - if(chr=='*' || chr=='&') - parts.emplace_back(std::string()+chr); + std::vector parts; + size_t text_start = -1; + for (size_t c = 0; c < type.size(); ++c) { + auto &chr = type[c]; + if ((chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || chr == '_' || + chr == '~') { + if (text_start == static_cast(-1)) + text_start = c; + } else { + if (text_start != static_cast(-1)) { + parts.emplace_back(type.substr(text_start, c - text_start)); + text_start = -1; + } + if (chr == '*' || chr == '&') + parts.emplace_back(std::string() + chr); + } } - } - return parts; + return parts; } -std::vector Ctags::get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type) { - auto result=get_result(path); - result.second->seekg(0, std::ios::end); - if(result.second->tellg()==0) - return std::vector(); - result.second->seekg(0, std::ios::beg); - - //insert name into type - size_t c=0; - size_t bracket_count=0; - for(;c') - --bracket_count; - else if(bracket_count==0 && type[c]=='(') - break; - } - auto full_type=type; - full_type.insert(c, name); - - auto parts=get_type_parts(full_type); - - std::string line; - long best_score=LONG_MIN; - std::vector best_locations; - while(std::getline(*result.second, line)) { - if(line.size()>2048) - continue; - auto location=Ctags::get_location(line, false); - if(!location.scope.empty()) { - if(location.scope+"::"+location.symbol!=name) - continue; +std::vector +Ctags::get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type) { + auto result = get_result(path); + result.second->seekg(0, std::ios::end); + if (result.second->tellg() == 0) + return std::vector(); + result.second->seekg(0, std::ios::beg); + + //insert name into type + size_t c = 0; + size_t bracket_count = 0; + for (; c < type.size(); ++c) { + if (type[c] == '<') + ++bracket_count; + else if (type[c] == '>') + --bracket_count; + else if (bracket_count == 0 && type[c] == '(') + break; } - else if(location.symbol!=name) - continue; - - location.file_path=result.first/location.file_path; - - auto source_parts=get_type_parts(location.source); - - //Find match score - long score=0; - size_t source_index=0; - for(auto &part: parts) { - bool found=false; - for(auto c=source_index;c best_locations; + while (std::getline(*result.second, line)) { + if (line.size() > 2048) + continue; + auto location = Ctags::get_location(line, false); + if (!location.scope.empty()) { + if (location.scope + "::" + location.symbol != name) + continue; + } else if (location.symbol != name) + continue; + + location.file_path = result.first / location.file_path; + + auto source_parts = get_type_parts(location.source); + + //Find match score + long score = 0; + size_t source_index = 0; + for (auto &part: parts) { + bool found = false; + for (auto c = source_index; c < source_parts.size(); ++c) { + if (part == source_parts[c]) { + source_index = c + 1; + ++score; + found = true; + break; + } + } + if (!found) + --score; } - } - if(!found) - --score; - } - size_t index=0; - for(auto &source_part: source_parts) { - bool found=false; - for(auto c=index;cbest_score) { - best_score=score; - best_locations.clear(); - best_locations.emplace_back(location); + + if (score > best_score) { + best_score = score; + best_locations.clear(); + best_locations.emplace_back(location); + } else if (score == best_score) + best_locations.emplace_back(location); } - else if(score==best_score) - best_locations.emplace_back(location); - } - - return best_locations; + + return best_locations; } diff --git a/src/ctags.h b/src/ctags.h index f39ddfb2..0d0aefad 100644 --- a/src/ctags.h +++ b/src/ctags.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -6,22 +7,26 @@ class Ctags { public: - class Location { - public: - boost::filesystem::path file_path; - unsigned long line; - unsigned long index; - std::string symbol; - std::string scope; - std::string source; - operator bool() const { return !file_path.empty(); } - }; - - static std::pair > get_result(const boost::filesystem::path &path); - - static Location get_location(const std::string &line, bool markup); - - static std::vector get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type); + class Location { + public: + boost::filesystem::path file_path; + unsigned long line; + unsigned long index; + std::string symbol; + std::string scope; + std::string source; + + operator bool() const { return !file_path.empty(); } + }; + + static std::pair > + get_result(const boost::filesystem::path &path); + + static Location get_location(const std::string &line, bool markup); + + static std::vector + get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type); + private: - static std::vector get_type_parts(const std::string type); + static std::vector get_type_parts(const std::string type); }; diff --git a/src/debug_lldb.cc b/src/debug_lldb.cc index f70b3aaa..7bfa7172 100644 --- a/src/debug_lldb.cc +++ b/src/debug_lldb.cc @@ -1,8 +1,10 @@ #include "debug_lldb.h" #include + #ifdef __APPLE__ #include #endif + #include #include #include "terminal.h" @@ -13,519 +15,529 @@ extern char **environ; void log(const char *msg, void *) { - std::cout << "debugger log: " << msg << std::endl; + std::cout << "debugger log: " << msg << std::endl; } -Debug::LLDB::LLDB(): state(lldb::StateType::eStateInvalid), buffer_size(131072) { - if(!getenv("LLDB_DEBUGSERVER_PATH")) { +Debug::LLDB::LLDB() : state(lldb::StateType::eStateInvalid), buffer_size(131072) { + if (!getenv("LLDB_DEBUGSERVER_PATH")) { #ifdef __APPLE__ - std::string debug_server_path("/usr/local/opt/llvm/bin/debugserver"); - if(boost::filesystem::exists(debug_server_path)) - setenv("LLDB_DEBUGSERVER_PATH", debug_server_path.c_str(), 0); + std::string debug_server_path("/usr/local/opt/llvm/bin/debugserver"); + if(boost::filesystem::exists(debug_server_path)) + setenv("LLDB_DEBUGSERVER_PATH", debug_server_path.c_str(), 0); #else - auto debug_server_path = filesystem::get_executable("lldb-server").string(); - if(debug_server_path != "lldb-server") - setenv("LLDB_DEBUGSERVER_PATH", debug_server_path.c_str(), 0); + auto debug_server_path = filesystem::get_executable("lldb-server").string(); + if (debug_server_path != "lldb-server") + setenv("LLDB_DEBUGSERVER_PATH", debug_server_path.c_str(), 0); #endif - } + } } -std::tuple, std::string, std::vector > Debug::LLDB::parse_run_arguments(const std::string &command) { - std::vector environment; - std::string executable; - std::vector arguments; - - size_t start_pos=std::string::npos; - bool quote=false; - bool double_quote=false; - size_t backslash_count=0; - for(size_t c=0;c<=command.size();c++) { - if(c==command.size() || (!quote && !double_quote && backslash_count%2==0 && command[c]==' ')) { - if(c>0 && start_pos!=std::string::npos) { - auto argument=command.substr(start_pos, c-start_pos); - if(executable.empty()) { - //Check for environment variable - bool env_arg=false; - for(size_t c=0;c='a' && argument[c]<='z') || (argument[c]>='A' && argument[c]<='Z') || - (argument[c]>='0' && argument[c]<='9') || argument[c]=='_') - continue; - else if(argument[c]=='=' && c+1, std::string, std::vector > +Debug::LLDB::parse_run_arguments(const std::string &command) { + std::vector environment; + std::string executable; + std::vector arguments; + + size_t start_pos = std::string::npos; + bool quote = false; + bool double_quote = false; + size_t backslash_count = 0; + for (size_t c = 0; c <= command.size(); c++) { + if (c == command.size() || (!quote && !double_quote && backslash_count % 2 == 0 && command[c] == ' ')) { + if (c > 0 && start_pos != std::string::npos) { + auto argument = command.substr(start_pos, c - start_pos); + if (executable.empty()) { + //Check for environment variable + bool env_arg = false; + for (size_t c = 0; c < argument.size(); ++c) { + if ((argument[c] >= 'a' && argument[c] <= 'z') || (argument[c] >= 'A' && argument[c] <= 'Z') || + (argument[c] >= '0' && argument[c] <= '9') || argument[c] == '_') + continue; + else if (argument[c] == '=' && c + 1 < argument.size()) { + environment.emplace_back( + argument.substr(0, c + 1) + filesystem::unescape_argument(argument.substr(c + 1))); + env_arg = true; + break; + } else + break; + } + + if (!env_arg) { + executable = filesystem::unescape_argument(argument); #ifdef _WIN32 - if(remote_host.empty()) - executable+=".exe"; + if (remote_host.empty()) + executable += ".exe"; #endif - } - } + } + } else + arguments.emplace_back(filesystem::unescape_argument(argument)); + start_pos = std::string::npos; + } + } else if (command[c] == '\'' && backslash_count % 2 == 0 && !double_quote) + quote = !quote; + else if (command[c] == '"' && backslash_count % 2 == 0 && !quote) + double_quote = !double_quote; + else if (command[c] == '\\' && !quote && !double_quote) + ++backslash_count; else - arguments.emplace_back(filesystem::unescape_argument(argument)); - start_pos=std::string::npos; - } + backslash_count = 0; + if (c < command.size() && start_pos == std::string::npos && command[c] != ' ') + start_pos = c; } - else if(command[c]=='\'' && backslash_count%2==0 && !double_quote) - quote=!quote; - else if(command[c]=='"' && backslash_count%2==0 && !quote) - double_quote=!double_quote; - else if(command[c]=='\\' && !quote && !double_quote) - ++backslash_count; - else - backslash_count=0; - if(c > &breakpoints, - const std::vector &startup_commands, const std::string &remote_host) { - if(!debugger) { - lldb::SBDebugger::Initialize(); - debugger=std::make_unique(lldb::SBDebugger::Create(true, log, nullptr)); - listener=std::make_unique("juCi++ lldb listener"); - } - - //Create executable string and argument array - auto parsed_run_arguments=parse_run_arguments(command); - auto &environment_from_arguments=std::get<0>(parsed_run_arguments); - auto &executable=std::get<1>(parsed_run_arguments); - auto &arguments=std::get<2>(parsed_run_arguments); - - std::vector argv; - for(size_t c=0;cCreateTarget(executable.c_str()); - if(!target.IsValid()) { - Terminal::get().async_print("Error (debug): Could not create debug target to: "+executable+'\n', true); - for(auto &handler: on_exit) - handler(-1); - return; - } - - //Set breakpoints - for(auto &breakpoint: breakpoints) { - if(!(target.BreakpointCreateByLocation(breakpoint.first.string().c_str(), breakpoint.second)).IsValid()) { - Terminal::get().async_print("Error (debug): Could not create breakpoint at: "+breakpoint.first.string()+":"+std::to_string(breakpoint.second)+'\n', true); - for(auto &handler: on_exit) - handler(-1); - return; + const std::vector > &breakpoints, + const std::vector &startup_commands, const std::string &remote_host) { + if (!debugger) { + lldb::SBDebugger::Initialize(); + debugger = std::make_unique(lldb::SBDebugger::Create(true, log, nullptr)); + listener = std::make_unique("juCi++ lldb listener"); } - } - - lldb::SBError error; - if(!remote_host.empty()) { - auto connect_string="connect://"+remote_host; - process = std::make_unique(target.ConnectRemote(*listener, connect_string.c_str(), "gdb-remote", error)); - if(error.Fail()) { - Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); - for(auto &handler: on_exit) - handler(-1); - return; + + //Create executable string and argument array + auto parsed_run_arguments = parse_run_arguments(command); + auto &environment_from_arguments = std::get<0>(parsed_run_arguments); + auto &executable = std::get<1>(parsed_run_arguments); + auto &arguments = std::get<2>(parsed_run_arguments); + + std::vector argv; + for (size_t c = 0; c < arguments.size(); c++) + argv.emplace_back(arguments[c].c_str()); + argv.emplace_back(nullptr); + + auto target = debugger->CreateTarget(executable.c_str()); + if (!target.IsValid()) { + Terminal::get().async_print("Error (debug): Could not create debug target to: " + executable + '\n', true); + for (auto &handler: on_exit) + handler(-1); + return; } - lldb::SBEvent event; - while(true) { - if(listener->GetNextEvent(event)) { - if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged)>0) { - auto state=process->GetStateFromEvent(event); - this->state=state; - if(state==lldb::StateType::eStateConnected) - break; + + //Set breakpoints + for (auto &breakpoint: breakpoints) { + if (!(target.BreakpointCreateByLocation(breakpoint.first.string().c_str(), breakpoint.second)).IsValid()) { + Terminal::get().async_print( + "Error (debug): Could not create breakpoint at: " + breakpoint.first.string() + ":" + + std::to_string(breakpoint.second) + '\n', true); + for (auto &handler: on_exit) + handler(-1); + return; } - } } - - // Create environment array - std::vector environment; - for(auto &e: environment_from_arguments) - environment.emplace_back(e.c_str()); - environment.emplace_back(nullptr); - - process->RemoteLaunch(argv.data(), environment.data(), nullptr, nullptr, nullptr, nullptr, lldb::eLaunchFlagNone, false, error); - if(!error.Fail()) - process->Continue(); - } - else { - // Create environment array - std::vector environment; - for(auto &e: environment_from_arguments) - environment.emplace_back(e.c_str()); - size_t environ_size=0; - while(environ[environ_size]!=nullptr) - ++environ_size; - for(size_t c=0;c(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error)); - } - if(error.Fail()) { - Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); - for(auto &handler: on_exit) - handler(-1); - return; - } - if(debug_thread.joinable()) - debug_thread.join(); - for(auto &handler: on_start) - handler(*process); - - for(auto &command: startup_commands) { - lldb::SBCommandReturnObject command_return_object; - debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, false); - } - - debug_thread=std::thread([this]() { - lldb::SBEvent event; - while(true) { - std::unique_lock lock(mutex); - if(listener->GetNextEvent(event)) { - if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged)>0) { - auto state=process->GetStateFromEvent(event); - this->state=state; - - if(state==lldb::StateType::eStateStopped) { - for(uint32_t c=0;cGetNumThreads();c++) { - auto thread=process->GetThreadAtIndex(c); - if(thread.GetStopReason()>=2) { - process->SetSelectedThreadByIndexID(thread.GetIndexID()); - break; - } - } - } - - lock.unlock(); - for(auto &handler: on_event) - handler(event); - lock.lock(); - - if(state==lldb::StateType::eStateExited || state==lldb::StateType::eStateCrashed) { - auto exit_status=state==lldb::StateType::eStateCrashed?-1:process->GetExitStatus(); - lock.unlock(); - for(auto &handler: on_exit) - handler(exit_status); - lock.lock(); - process.reset(); - this->state=lldb::StateType::eStateInvalid; + + lldb::SBError error; + if (!remote_host.empty()) { + auto connect_string = "connect://" + remote_host; + process = std::make_unique( + target.ConnectRemote(*listener, connect_string.c_str(), "gdb-remote", error)); + if (error.Fail()) { + Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true); + for (auto &handler: on_exit) + handler(-1); return; - } } - if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDOUT)>0) { - char buffer[buffer_size]; - size_t n; - while((n=process->GetSTDOUT(buffer, buffer_size))!=0) - Terminal::get().async_print(std::string(buffer, n)); - } - //TODO: for some reason stderr is redirected to stdout - if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDERR)>0) { - char buffer[buffer_size]; - size_t n; - while((n=process->GetSTDERR(buffer, buffer_size))!=0) - Terminal::get().async_print(std::string(buffer, n), true); + lldb::SBEvent event; + while (true) { + if (listener->GetNextEvent(event)) { + if ((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) { + auto state = process->GetStateFromEvent(event); + this->state = state; + if (state == lldb::StateType::eStateConnected) + break; + } + } } - } - lock.unlock(); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + // Create environment array + std::vector environment; + for (auto &e: environment_from_arguments) + environment.emplace_back(e.c_str()); + environment.emplace_back(nullptr); + + process->RemoteLaunch(argv.data(), environment.data(), nullptr, nullptr, nullptr, nullptr, + lldb::eLaunchFlagNone, false, error); + if (!error.Fail()) + process->Continue(); + } else { + // Create environment array + std::vector environment; + for (auto &e: environment_from_arguments) + environment.emplace_back(e.c_str()); + size_t environ_size = 0; + while (environ[environ_size] != nullptr) + ++environ_size; + for (size_t c = 0; c < environ_size; ++c) + environment.emplace_back(environ[c]); + environment.emplace_back(nullptr); + + process = std::make_unique( + target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, + path.string().c_str(), lldb::eLaunchFlagNone, false, error)); } - }); + if (error.Fail()) { + Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true); + for (auto &handler: on_exit) + handler(-1); + return; + } + if (debug_thread.joinable()) + debug_thread.join(); + for (auto &handler: on_start) + handler(*process); + + for (auto &command: startup_commands) { + lldb::SBCommandReturnObject command_return_object; + debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, false); + } + + debug_thread = std::thread([this]() { + lldb::SBEvent event; + while (true) { + std::unique_lock lock(mutex); + if (listener->GetNextEvent(event)) { + if ((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) { + auto state = process->GetStateFromEvent(event); + this->state = state; + + if (state == lldb::StateType::eStateStopped) { + for (uint32_t c = 0; c < process->GetNumThreads(); c++) { + auto thread = process->GetThreadAtIndex(c); + if (thread.GetStopReason() >= 2) { + process->SetSelectedThreadByIndexID(thread.GetIndexID()); + break; + } + } + } + + lock.unlock(); + for (auto &handler: on_event) + handler(event); + lock.lock(); + + if (state == lldb::StateType::eStateExited || state == lldb::StateType::eStateCrashed) { + auto exit_status = state == lldb::StateType::eStateCrashed ? -1 : process->GetExitStatus(); + lock.unlock(); + for (auto &handler: on_exit) + handler(exit_status); + lock.lock(); + process.reset(); + this->state = lldb::StateType::eStateInvalid; + return; + } + } + if ((event.GetType() & lldb::SBProcess::eBroadcastBitSTDOUT) > 0) { + char buffer[buffer_size]; + size_t n; + while ((n = process->GetSTDOUT(buffer, buffer_size)) != 0) + Terminal::get().async_print(std::string(buffer, n)); + } + //TODO: for some reason stderr is redirected to stdout + if ((event.GetType() & lldb::SBProcess::eBroadcastBitSTDERR) > 0) { + char buffer[buffer_size]; + size_t n; + while ((n = process->GetSTDERR(buffer, buffer_size)) != 0) + Terminal::get().async_print(std::string(buffer, n), true); + } + } + lock.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + }); } void Debug::LLDB::continue_debug() { - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped) - process->Continue(); + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateStopped) + process->Continue(); } void Debug::LLDB::stop() { - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateRunning) { - auto error=process->Stop(); - if(error.Fail()) - Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); - } + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateRunning) { + auto error = process->Stop(); + if (error.Fail()) + Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true); + } } void Debug::LLDB::kill() { - std::unique_lock lock(mutex); - if(process) { - auto error=process->Kill(); - if(error.Fail()) - Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); - } + std::unique_lock lock(mutex); + if (process) { + auto error = process->Kill(); + if (error.Fail()) + Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true); + } } void Debug::LLDB::step_over() { - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped) { - process->GetSelectedThread().StepOver(); - } + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateStopped) { + process->GetSelectedThread().StepOver(); + } } void Debug::LLDB::step_into() { - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped) { - process->GetSelectedThread().StepInto(); - } + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateStopped) { + process->GetSelectedThread().StepInto(); + } } void Debug::LLDB::step_out() { - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped) { - process->GetSelectedThread().StepOut(); - } + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateStopped) { + process->GetSelectedThread().StepOut(); + } } std::pair Debug::LLDB::run_command(const std::string &command) { - std::pair command_return; - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped || state==lldb::StateType::eStateRunning) { - lldb::SBCommandReturnObject command_return_object; - debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true); - auto output=command_return_object.GetOutput(); - if(output) - command_return.first=output; - auto error=command_return_object.GetError(); - if(error) - command_return.second=error; - } - return command_return; + std::pair command_return; + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateStopped || state == lldb::StateType::eStateRunning) { + lldb::SBCommandReturnObject command_return_object; + debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true); + auto output = command_return_object.GetOutput(); + if (output) + command_return.first = output; + auto error = command_return_object.GetError(); + if (error) + command_return.second = error; + } + return command_return; } std::vector Debug::LLDB::get_backtrace() { - std::vector backtrace; - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped) { - auto thread=process->GetSelectedThread(); - for(uint32_t c_f=0;c_f backtrace; + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateStopped) { + auto thread = process->GetSelectedThread(); + for (uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) { + Frame backtrace_frame; + auto frame = thread.GetFrameAtIndex(c_f); + + backtrace_frame.index = c_f; + + if (frame.GetFunctionName() != nullptr) + backtrace_frame.function_name = frame.GetFunctionName(); + + auto module_filename = frame.GetModule().GetFileSpec().GetFilename(); + if (module_filename != nullptr) { + backtrace_frame.module_filename = module_filename; + } + + auto line_entry = frame.GetLineEntry(); + if (line_entry.IsValid()) { + lldb::SBStream stream; + line_entry.GetFileSpec().GetDescription(stream); + auto column = line_entry.GetColumn(); + if (column == 0) + column = 1; + backtrace_frame.file_path = filesystem::get_normal_path(stream.GetData()); + backtrace_frame.line_nr = line_entry.GetLine(); + backtrace_frame.line_index = column; + } + backtrace.emplace_back(backtrace_frame); + } } - } - return backtrace; + return backtrace; } std::vector Debug::LLDB::get_variables() { - std::vector variables; - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped) { - for(uint32_t c_t=0;c_tGetNumThreads();c_t++) { - auto thread=process->GetThreadAtIndex(c_t); - for(uint32_t c_f=0;c_f variables; + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateStopped) { + for (uint32_t c_t = 0; c_t < process->GetNumThreads(); c_t++) { + auto thread = process->GetThreadAtIndex(c_t); + for (uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) { + auto frame = thread.GetFrameAtIndex(c_f); + auto values = frame.GetVariables(true, true, true, false); + for (uint32_t value_index = 0; value_index < values.GetSize(); value_index++) { + lldb::SBStream stream; + auto value = values.GetValueAtIndex(value_index); + + Debug::LLDB::Variable variable; + variable.thread_index_id = thread.GetIndexID(); + variable.frame_index = c_f; + if (value.GetName() != nullptr) + variable.name = value.GetName(); + value.GetDescription(stream); + variable.value = stream.GetData(); + + auto declaration = value.GetDeclaration(); + if (declaration.IsValid()) { + variable.declaration_found = true; + variable.line_nr = declaration.GetLine(); + variable.line_index = declaration.GetColumn(); + if (variable.line_index == 0) + variable.line_index = 1; + + auto file_spec = declaration.GetFileSpec(); + variable.file_path = filesystem::get_normal_path(file_spec.GetDirectory()); + variable.file_path /= file_spec.GetFilename(); + } else { + variable.declaration_found = false; + auto line_entry = frame.GetLineEntry(); + if (line_entry.IsValid()) { + variable.line_nr = line_entry.GetLine(); + variable.line_index = line_entry.GetColumn(); + if (variable.line_index == 0) + variable.line_index = 1; + + auto file_spec = line_entry.GetFileSpec(); + variable.file_path = filesystem::get_normal_path(file_spec.GetDirectory()); + variable.file_path /= file_spec.GetFilename(); + } + } + variables.emplace_back(variable); + } } - } - variables.emplace_back(variable); } - } } - } - return variables; + return variables; } void Debug::LLDB::select_frame(uint32_t frame_index, uint32_t thread_index_id) { - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped) { - if(thread_index_id!=0) - process->SetSelectedThreadByIndexID(thread_index_id); - process->GetSelectedThread().SetSelectedFrame(frame_index);; - } + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateStopped) { + if (thread_index_id != 0) + process->SetSelectedThreadByIndexID(thread_index_id); + process->GetSelectedThread().SetSelectedFrame(frame_index);; + } } void Debug::LLDB::cancel() { - kill(); - if(debug_thread.joinable()) - debug_thread.join(); + kill(); + if (debug_thread.joinable()) + debug_thread.join(); } -std::string Debug::LLDB::get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { - std::string variable_value; - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped) { - auto frame=process->GetSelectedThread().GetSelectedFrame(); - - auto values=frame.GetVariables(true, true, true, false); - //First try to find variable based on name, file and line number - if(!file_path.empty()) { - for(uint32_t value_index=0;value_index lock(mutex); + if (state == lldb::StateType::eStateStopped) { + auto frame = process->GetSelectedThread().GetSelectedFrame(); + + auto values = frame.GetVariables(true, true, true, false); + //First try to find variable based on name, file and line number + if (!file_path.empty()) { + for (uint32_t value_index = 0; value_index < values.GetSize(); value_index++) { + lldb::SBStream stream; + auto value = values.GetValueAtIndex(value_index); + + if (value.GetName() != nullptr && value.GetName() == variable) { + auto declaration = value.GetDeclaration(); + if (declaration.IsValid()) { + if (declaration.GetLine() == line_nr && + (declaration.GetColumn() == 0 || declaration.GetColumn() == line_index)) { + auto file_spec = declaration.GetFileSpec(); + auto value_decl_path = filesystem::get_normal_path(file_spec.GetDirectory()); + value_decl_path /= file_spec.GetFilename(); + if (value_decl_path == file_path) { + value.GetDescription(stream); + variable_value = stream.GetData(); + break; + } + } + } + } + } + } + if (variable_value.empty()) { + //In case a variable is missing file and line number, only do check on name + auto value = frame.GetValueForVariablePath(variable.c_str()); + if (value.IsValid()) { + lldb::SBStream stream; value.GetDescription(stream); - variable_value=stream.GetData(); - break; - } + variable_value = stream.GetData(); } - } } - } - } - if(variable_value.empty()) { - //In case a variable is missing file and line number, only do check on name - auto value=frame.GetValueForVariablePath(variable.c_str()); - if(value.IsValid()) { - lldb::SBStream stream; - value.GetDescription(stream); - variable_value=stream.GetData(); - } } - } - return variable_value; + return variable_value; } -std::string Debug::LLDB::get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { - std::string return_value; - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateStopped) { - auto thread=process->GetSelectedThread(); - auto thread_return_value=thread.GetStopReturnValue(); - if(thread_return_value.IsValid()) { - auto line_entry=thread.GetSelectedFrame().GetLineEntry(); - if(line_entry.IsValid()) { - lldb::SBStream stream; - line_entry.GetFileSpec().GetDescription(stream); - if(filesystem::get_normal_path(stream.GetData())==file_path && line_entry.GetLine()==line_nr && - (line_entry.GetColumn()==0 || line_entry.GetColumn()==line_index)) { - lldb::SBStream stream; - thread_return_value.GetDescription(stream); - return_value=stream.GetData(); +std::string +Debug::LLDB::get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { + std::string return_value; + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateStopped) { + auto thread = process->GetSelectedThread(); + auto thread_return_value = thread.GetStopReturnValue(); + if (thread_return_value.IsValid()) { + auto line_entry = thread.GetSelectedFrame().GetLineEntry(); + if (line_entry.IsValid()) { + lldb::SBStream stream; + line_entry.GetFileSpec().GetDescription(stream); + if (filesystem::get_normal_path(stream.GetData()) == file_path && line_entry.GetLine() == line_nr && + (line_entry.GetColumn() == 0 || line_entry.GetColumn() == line_index)) { + lldb::SBStream stream; + thread_return_value.GetDescription(stream); + return_value = stream.GetData(); + } + } } - } } - } - return return_value; + return return_value; } bool Debug::LLDB::is_invalid() { - std::unique_lock lock(mutex); - return state==lldb::StateType::eStateInvalid; + std::unique_lock lock(mutex); + return state == lldb::StateType::eStateInvalid; } bool Debug::LLDB::is_stopped() { - std::unique_lock lock(mutex); - return state==lldb::StateType::eStateStopped; + std::unique_lock lock(mutex); + return state == lldb::StateType::eStateStopped; } bool Debug::LLDB::is_running() { - std::unique_lock lock(mutex); - return state==lldb::StateType::eStateRunning; + std::unique_lock lock(mutex); + return state == lldb::StateType::eStateRunning; } void Debug::LLDB::add_breakpoint(const boost::filesystem::path &file_path, int line_nr) { - std::unique_lock lock(mutex); - if(state==lldb::eStateStopped || state==lldb::eStateRunning) { - if(!(process->GetTarget().BreakpointCreateByLocation(file_path.string().c_str(), line_nr)).IsValid()) - Terminal::get().async_print("Error (debug): Could not create breakpoint at: "+file_path.string()+":"+std::to_string(line_nr)+'\n', true); - } + std::unique_lock lock(mutex); + if (state == lldb::eStateStopped || state == lldb::eStateRunning) { + if (!(process->GetTarget().BreakpointCreateByLocation(file_path.string().c_str(), line_nr)).IsValid()) + Terminal::get().async_print("Error (debug): Could not create breakpoint at: " + file_path.string() + ":" + + std::to_string(line_nr) + '\n', true); + } } void Debug::LLDB::remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) { - std::unique_lock lock(mutex); - if(state==lldb::eStateStopped || state==lldb::eStateRunning) { - auto target=process->GetTarget(); - for(int line_nr_try=line_nr;line_nr_try(line_nr_try)) { - auto file_spec=line_entry.GetFileSpec(); - auto breakpoint_path=filesystem::get_normal_path(file_spec.GetDirectory()); - breakpoint_path/=file_spec.GetFilename(); - if(breakpoint_path==file_path) { - if(!target.BreakpointDelete(breakpoint.GetID())) - Terminal::get().async_print("Error (debug): Could not delete breakpoint at: "+file_path.string()+":"+std::to_string(line_nr)+'\n', true); - return; + std::unique_lock lock(mutex); + if (state == lldb::eStateStopped || state == lldb::eStateRunning) { + auto target = process->GetTarget(); + for (int line_nr_try = line_nr; line_nr_try < line_count; line_nr_try++) { + for (uint32_t b_index = 0; b_index < target.GetNumBreakpoints(); b_index++) { + auto breakpoint = target.GetBreakpointAtIndex(b_index); + for (uint32_t l_index = 0; l_index < breakpoint.GetNumLocations(); l_index++) { + auto line_entry = breakpoint.GetLocationAtIndex(l_index).GetAddress().GetLineEntry(); + if (line_entry.GetLine() == static_cast(line_nr_try)) { + auto file_spec = line_entry.GetFileSpec(); + auto breakpoint_path = filesystem::get_normal_path(file_spec.GetDirectory()); + breakpoint_path /= file_spec.GetFilename(); + if (breakpoint_path == file_path) { + if (!target.BreakpointDelete(breakpoint.GetID())) + Terminal::get().async_print( + "Error (debug): Could not delete breakpoint at: " + file_path.string() + ":" + + std::to_string(line_nr) + '\n', true); + return; + } + } + } } - } } - } } - } } void Debug::LLDB::write(const std::string &buffer) { - std::unique_lock lock(mutex); - if(state==lldb::StateType::eStateRunning) { - process->PutSTDIN(buffer.c_str(), buffer.size()); - } + std::unique_lock lock(mutex); + if (state == lldb::StateType::eStateRunning) { + process->PutSTDIN(buffer.c_str(), buffer.size()); + } } diff --git a/src/debug_lldb.h b/src/debug_lldb.h index 42560c3f..1b948a7b 100644 --- a/src/debug_lldb.h +++ b/src/debug_lldb.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -7,82 +8,102 @@ #include namespace Debug { - class LLDB { - public: - class Frame { + class LLDB { public: - uint32_t index; - std::string module_filename; - boost::filesystem::path file_path; - std::string function_name; - int line_nr; - int line_index; - }; - class Variable { + class Frame { + public: + uint32_t index; + std::string module_filename; + boost::filesystem::path file_path; + std::string function_name; + int line_nr; + int line_index; + }; + + class Variable { + public: + uint32_t thread_index_id; + uint32_t frame_index; + std::string name; + std::string value; + bool declaration_found; + boost::filesystem::path file_path; + int line_nr; + int line_index; + }; + + private: + LLDB(); + public: - uint32_t thread_index_id; - uint32_t frame_index; - std::string name; - std::string value; - bool declaration_found; - boost::filesystem::path file_path; - int line_nr; - int line_index; + static LLDB &get() { + static LLDB singleton; + return singleton; + } + + std::list> on_start; + /// The handlers are not run in the main loop. + std::list> on_exit; + /// The handlers are not run in the main loop. + std::list> on_event; + + std::mutex mutex; + + void start(const std::string &command, const boost::filesystem::path &path = "", + const std::vector > &breakpoints = {}, + const std::vector &startup_commands = {}, const std::string &remote_host = ""); + + void continue_debug(); //can't use continue as function name + void stop(); + + void kill(); + + void step_over(); + + void step_into(); + + void step_out(); + + std::pair run_command(const std::string &command); + + std::vector get_backtrace(); + + std::vector get_variables(); + + void select_frame(uint32_t frame_index, uint32_t thread_index_id = 0); + + void cancel(); + + std::string + get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, + unsigned int line_index); + + std::string + get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index); + + bool is_invalid(); + + bool is_stopped(); + + bool is_running(); + + void add_breakpoint(const boost::filesystem::path &file_path, int line_nr); + + void remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count); + + void write(const std::string &buffer); + + private: + std::tuple, std::string, std::vector> + parse_run_arguments(const std::string &command); + + std::unique_ptr debugger; + std::unique_ptr listener; + std::unique_ptr process; + std::thread debug_thread; + + lldb::StateType state; + + size_t buffer_size; }; - private: - LLDB(); - public: - static LLDB &get() { - static LLDB singleton; - return singleton; - } - - std::list> on_start; - /// The handlers are not run in the main loop. - std::list> on_exit; - /// The handlers are not run in the main loop. - std::list> on_event; - - std::mutex mutex; - - void start(const std::string &command, const boost::filesystem::path &path="", - const std::vector > &breakpoints={}, - const std::vector &startup_commands={}, const std::string &remote_host=""); - void continue_debug(); //can't use continue as function name - void stop(); - void kill(); - void step_over(); - void step_into(); - void step_out(); - std::pair run_command(const std::string &command); - std::vector get_backtrace(); - std::vector get_variables(); - void select_frame(uint32_t frame_index, uint32_t thread_index_id=0); - - void cancel(); - - std::string get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index); - std::string get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index); - - bool is_invalid(); - bool is_stopped(); - bool is_running(); - - void add_breakpoint(const boost::filesystem::path &file_path, int line_nr); - void remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count); - - void write(const std::string &buffer); - - private: - std::tuple, std::string, std::vector> parse_run_arguments(const std::string &command); - - std::unique_ptr debugger; - std::unique_ptr listener; - std::unique_ptr process; - std::thread debug_thread; - - lldb::StateType state; - - size_t buffer_size; - }; } diff --git a/src/dialogs.cc b/src/dialogs.cc index ab5bc550..f4194848 100644 --- a/src/dialogs.cc +++ b/src/dialogs.cc @@ -1,79 +1,80 @@ #include "dialogs.h" #include -Dialog::Message::Message(const std::string &text): Gtk::Window(Gtk::WindowType::WINDOW_POPUP) { - auto g_application=g_application_get_default(); - auto gio_application=Glib::wrap(g_application, true); - auto application=Glib::RefPtr::cast_static(gio_application); - set_transient_for(*application->get_active_window()); - - set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); - set_modal(true); - set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_NOTIFICATION); - property_decorated()=false; - set_skip_taskbar_hint(true); - - auto box=Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL)); - auto label=Gtk::manage(new Gtk::Label(text)); - label->set_padding(10, 10); - box->pack_start(*label); - add(*box); - - show_all_children(); - show_now(); - - while(Gtk::Main::events_pending()) - Gtk::Main::iteration(false); +Dialog::Message::Message(const std::string &text) : Gtk::Window(Gtk::WindowType::WINDOW_POPUP) { + auto g_application = g_application_get_default(); + auto gio_application = Glib::wrap(g_application, true); + auto application = Glib::RefPtr::cast_static(gio_application); + set_transient_for(*application->get_active_window()); + + set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); + set_modal(true); + set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_NOTIFICATION); + property_decorated() = false; + set_skip_taskbar_hint(true); + + auto box = Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL)); + auto label = Gtk::manage(new Gtk::Label(text)); + label->set_padding(10, 10); + box->pack_start(*label); + add(*box); + + show_all_children(); + show_now(); + + while (Gtk::Main::events_pending()) + Gtk::Main::iteration(false); } bool Dialog::Message::on_delete_event(GdkEventAny *event) { - return true; + return true; } std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::string &title, - const std::vector> &buttons, - Gtk::FileChooserAction action) { - // Workaround for crash on MacOS when filtering files in file/folder dialogs. - // See also https://github.com/cppit/jucipp/issues/259. - // TODO 2018: check if this bug has been fixed + const std::vector> &buttons, + Gtk::FileChooserAction action) { + // Workaround for crash on MacOS when filtering files in file/folder dialogs. + // See also https://github.com/cppit/jucipp/issues/259. + // TODO 2018: check if this bug has been fixed #ifdef __APPLE__ - class FileChooserDialog : public Gtk::FileChooserDialog { - Gtk::FileChooserAction action; - public: - FileChooserDialog(const Glib::ustring& title, Gtk::FileChooserAction action) : Gtk::FileChooserDialog(title, action), action(action) {} - protected: - bool on_key_press_event(GdkEventKey *key_event) override { - if(action==Gtk::FileChooserAction::FILE_CHOOSER_ACTION_OPEN || action==Gtk::FileChooserAction::FILE_CHOOSER_ACTION_SELECT_FOLDER) { - auto unicode=gdk_keyval_to_unicode(key_event->keyval); - if(unicode>31 && unicode!=127) - return true; + class FileChooserDialog : public Gtk::FileChooserDialog { + Gtk::FileChooserAction action; + public: + FileChooserDialog(const Glib::ustring& title, Gtk::FileChooserAction action) : Gtk::FileChooserDialog(title, action), action(action) {} + protected: + bool on_key_press_event(GdkEventKey *key_event) override { + if(action==Gtk::FileChooserAction::FILE_CHOOSER_ACTION_OPEN || action==Gtk::FileChooserAction::FILE_CHOOSER_ACTION_SELECT_FOLDER) { + auto unicode=gdk_keyval_to_unicode(key_event->keyval); + if(unicode>31 && unicode!=127) + return true; + } + return Gtk::FileChooserDialog::on_key_press_event(key_event); } - return Gtk::FileChooserDialog::on_key_press_event(key_event); - } - }; - FileChooserDialog dialog(title, action); + }; + FileChooserDialog dialog(title, action); #else - Gtk::FileChooserDialog dialog(title, action); + Gtk::FileChooserDialog dialog(title, action); #endif - - auto g_application=g_application_get_default(); - auto gio_application=Glib::wrap(g_application, true); - auto application=Glib::RefPtr::cast_static(gio_application); - dialog.set_transient_for(*application->get_active_window()); - dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); - - if(title=="Save File As") - gtk_file_chooser_set_filename(reinterpret_cast(dialog.gobj()), path.string().c_str()); - else if(!path.empty()) - gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), path.string().c_str()); - else { - boost::system::error_code ec; - auto current_path=boost::filesystem::current_path(ec); - if(!ec) - gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), current_path.string().c_str()); - } - - for (auto &button : buttons) - dialog.add_button(button.first, button.second); - return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : ""; + + auto g_application = g_application_get_default(); + auto gio_application = Glib::wrap(g_application, true); + auto application = Glib::RefPtr::cast_static(gio_application); + dialog.set_transient_for(*application->get_active_window()); + dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); + + if (title == "Save File As") + gtk_file_chooser_set_filename(reinterpret_cast(dialog.gobj()), path.string().c_str()); + else if (!path.empty()) + gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), path.string().c_str()); + else { + boost::system::error_code ec; + auto current_path = boost::filesystem::current_path(ec); + if (!ec) + gtk_file_chooser_set_current_folder(reinterpret_cast(dialog.gobj()), + current_path.string().c_str()); + } + + for (auto &button : buttons) + dialog.add_button(button.first, button.second); + return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : ""; } diff --git a/src/dialogs.h b/src/dialogs.h index e1deeb53..b3e57730 100644 --- a/src/dialogs.h +++ b/src/dialogs.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -6,21 +7,26 @@ class Dialog { public: - static std::string open_folder(const boost::filesystem::path &path); - static std::string open_file(const boost::filesystem::path &path); - static std::string new_file(const boost::filesystem::path &path); - static std::string new_folder(const boost::filesystem::path &path); - static std::string save_file_as(const boost::filesystem::path &path); - - class Message : public Gtk::Window { - public: - Message(const std::string &text); - protected: - bool on_delete_event(GdkEventAny *event) override; - }; - + static std::string open_folder(const boost::filesystem::path &path); + + static std::string open_file(const boost::filesystem::path &path); + + static std::string new_file(const boost::filesystem::path &path); + + static std::string new_folder(const boost::filesystem::path &path); + + static std::string save_file_as(const boost::filesystem::path &path); + + class Message : public Gtk::Window { + public: + Message(const std::string &text); + + protected: + bool on_delete_event(GdkEventAny *event) override; + }; + private: - static std::string gtk_dialog(const boost::filesystem::path &path, const std::string &title, - const std::vector> &buttons, - Gtk::FileChooserAction gtk_options); + static std::string gtk_dialog(const boost::filesystem::path &path, const std::string &title, + const std::vector> &buttons, + Gtk::FileChooserAction gtk_options); }; diff --git a/src/dialogs_unix.cc b/src/dialogs_unix.cc index 2512ce8a..865bf3d9 100644 --- a/src/dialogs_unix.cc +++ b/src/dialogs_unix.cc @@ -1,32 +1,32 @@ #include "dialogs.h" std::string Dialog::open_folder(const boost::filesystem::path &path) { - return gtk_dialog(path, "Open Folder", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Open", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); + return gtk_dialog(path, "Open 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(const boost::filesystem::path &path) { - return gtk_dialog(path, "New File", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_SAVE); + return gtk_dialog(path, "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(const boost::filesystem::path &path) { - return gtk_dialog(path, "New Folder", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); + return gtk_dialog(path, "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::open_file(const boost::filesystem::path &path) { - return gtk_dialog(path, "Open File", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Select", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_OPEN); + return gtk_dialog(path, "Open File", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Select", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_OPEN); } std::string Dialog::save_file_as(const boost::filesystem::path &path) { - return gtk_dialog(path, "Save File As", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Save", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_SAVE); + return gtk_dialog(path, "Save File As", + {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, + Gtk::FILE_CHOOSER_ACTION_SAVE); } diff --git a/src/dialogs_win.cc b/src/dialogs_win.cc index 72d18691..6322a9ae 100644 --- a/src/dialogs_win.cc +++ b/src/dialogs_win.cc @@ -12,152 +12,154 @@ #include #include //TODO: remove + using namespace std; //TODO: remove class Win32Dialog { public: - Win32Dialog() {}; - - ~Win32Dialog() { - if(dialog!=nullptr) - dialog->Release(); - } - - /** Returns the selected item's path as a string */ - std::string open(const std::wstring &title, unsigned option=0) { - if(!init(CLSID_FileOpenDialog)) - return ""; - - if(!set_title(title) || !add_option(option)) - return ""; - if(!set_folder()) - return ""; - - return show(); - } - - std::string save(const std::wstring &title, const boost::filesystem::path &file_path="", unsigned option=0) { - if(!init(CLSID_FileSaveDialog)) - return ""; - - if(!set_title(title) || !add_option(option)) - return ""; - if(!set_folder()) - return ""; - std::vector extensions; - if(!file_path.empty()) { - if(file_path.has_extension() && file_path.filename()!=file_path.extension()) { - auto extension=(L"*"+file_path.extension().native()).c_str(); - extensions.emplace_back(COMDLG_FILTERSPEC{extension, extension}); - if(!set_default_file_extension(extension)) - return ""; - } + Win32Dialog() {}; + + ~Win32Dialog() { + if (dialog != nullptr) + dialog->Release(); + } + + /** Returns the selected item's path as a string */ + std::string open(const std::wstring &title, unsigned option = 0) { + if (!init(CLSID_FileOpenDialog)) + return ""; + + if (!set_title(title) || !add_option(option)) + return ""; + if (!set_folder()) + return ""; + + return show(); + } + + std::string save(const std::wstring &title, const boost::filesystem::path &file_path = "", unsigned option = 0) { + if (!init(CLSID_FileSaveDialog)) + return ""; + + if (!set_title(title) || !add_option(option)) + return ""; + if (!set_folder()) + return ""; + std::vector extensions; + if (!file_path.empty()) { + if (file_path.has_extension() && file_path.filename() != file_path.extension()) { + auto extension = (L"*" + file_path.extension().native()).c_str(); + extensions.emplace_back(COMDLG_FILTERSPEC{extension, extension}); + if (!set_default_file_extension(extension)) + return ""; + } + } + extensions.emplace_back(COMDLG_FILTERSPEC{L"All files", L"*.*"}); + if (dialog->SetFileTypes(extensions.size(), extensions.data()) != S_OK) + return ""; + + return show(); } - extensions.emplace_back(COMDLG_FILTERSPEC{L"All files", L"*.*"}); - if(dialog->SetFileTypes(extensions.size(), extensions.data())!=S_OK) - return ""; - - return show(); - } private: - IFileDialog *dialog=nullptr; - DWORD options; - - bool init(CLSID type) { - if(CoCreateInstance(type, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog))!=S_OK) - return false; - if(dialog->GetOptions(&options)!=S_OK) - return false; - return true; - } - - /** available options are listed at https://msdn.microsoft.com/en-gb/library/windows/desktop/dn457282(v=vs.85).aspx */ - bool add_option(unsigned option) { - if(dialog->SetOptions(options | option)!=S_OK) - return false; - return true; - } - - bool set_title(const std::wstring &title) { - if(dialog->SetTitle(title.c_str())!=S_OK) - return false; - return true; - } - - /** Sets the extensions the browser can find */ - bool set_default_file_extension(const std::wstring &file_extension) { - if(dialog->SetDefaultExtension(file_extension.c_str())!=S_OK) - return false; - return true; - } - - /** Sets the directory to start browsing */ - bool set_folder() { - auto g_application=g_application_get_default(); //TODO: Post issue that Gio::Application::get_default should return pointer and not Glib::RefPtr - auto gio_application=Glib::wrap(g_application, true); - auto application=Glib::RefPtr::cast_static(gio_application); - - auto current_path=application->window->notebook.get_current_folder(); - boost::system::error_code ec; - if(current_path.empty()) - current_path=boost::filesystem::current_path(ec); - if(ec) - return false; - - std::wstring path=current_path.native(); - size_t pos=0; - while((pos=path.find(L'/', pos))!=std::wstring::npos) {//TODO: issue bug report on boost::filesystem::path::native on MSYS2 - path.replace(pos, 1, L"\\"); - pos++; + IFileDialog *dialog = nullptr; + DWORD options; + + bool init(CLSID type) { + if (CoCreateInstance(type, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog)) != S_OK) + return false; + if (dialog->GetOptions(&options) != S_OK) + return false; + return true; + } + + /** available options are listed at https://msdn.microsoft.com/en-gb/library/windows/desktop/dn457282(v=vs.85).aspx */ + bool add_option(unsigned option) { + if (dialog->SetOptions(options | option) != S_OK) + return false; + return true; + } + + bool set_title(const std::wstring &title) { + if (dialog->SetTitle(title.c_str()) != S_OK) + return false; + return true; + } + + /** Sets the extensions the browser can find */ + bool set_default_file_extension(const std::wstring &file_extension) { + if (dialog->SetDefaultExtension(file_extension.c_str()) != S_OK) + return false; + return true; + } + + /** Sets the directory to start browsing */ + bool set_folder() { + auto g_application = g_application_get_default(); //TODO: Post issue that Gio::Application::get_default should return pointer and not Glib::RefPtr + auto gio_application = Glib::wrap(g_application, true); + auto application = Glib::RefPtr::cast_static(gio_application); + + auto current_path = application->window->notebook.get_current_folder(); + boost::system::error_code ec; + if (current_path.empty()) + current_path = boost::filesystem::current_path(ec); + if (ec) + return false; + + std::wstring path = current_path.native(); + size_t pos = 0; + while ((pos = path.find(L'/', pos)) != + std::wstring::npos) {//TODO: issue bug report on boost::filesystem::path::native on MSYS2 + path.replace(pos, 1, L"\\"); + pos++; + } + + IShellItem *folder = nullptr; + if (SHCreateItemFromParsingName(path.c_str(), nullptr, IID_PPV_ARGS(&folder)) != S_OK) + return false; + if (dialog->SetFolder(folder) != S_OK) + return false; + folder->Release(); + return true; + } + + std::string show() { + if (dialog->Show(nullptr) != S_OK) + return ""; + IShellItem *result = nullptr; + if (dialog->GetResult(&result) != S_OK) + return ""; + LPWSTR file_path = nullptr; + auto hresult = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); + result->Release(); + if (hresult != S_OK) + return ""; + std::wstring file_path_wstring(file_path); + std::string file_path_string(file_path_wstring.begin(), file_path_wstring.end()); + CoTaskMemFree(file_path); + return file_path_string; } - - IShellItem *folder = nullptr; - if(SHCreateItemFromParsingName(path.c_str(), nullptr, IID_PPV_ARGS(&folder))!=S_OK) - return false; - if(dialog->SetFolder(folder)!=S_OK) - return false; - folder->Release(); - return true; - } - - std::string show() { - if(dialog->Show(nullptr)!=S_OK) - return ""; - IShellItem *result = nullptr; - if(dialog->GetResult(&result)!=S_OK) - return ""; - LPWSTR file_path = nullptr; - auto hresult=result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); - result->Release(); - if(hresult!=S_OK) - return ""; - std::wstring file_path_wstring(file_path); - std::string file_path_string(file_path_wstring.begin(), file_path_wstring.end()); - CoTaskMemFree(file_path); - return file_path_string; - } }; std::string Dialog::open_folder() { - return Win32Dialog().open(L"Open Folder", FOS_PICKFOLDERS); + return Win32Dialog().open(L"Open Folder", FOS_PICKFOLDERS); } std::string Dialog::new_file() { - return Win32Dialog().save(L"New File"); + return Win32Dialog().save(L"New File"); } std::string Dialog::new_folder() { - //Win32 (IFileDialog) does not support create folder... - return gtk_dialog("New Folder", - {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, - Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); + //Win32 (IFileDialog) does not support create folder... + return gtk_dialog("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::open_file() { - return Win32Dialog().open(L"Open File"); + return Win32Dialog().open(L"Open File"); } std::string Dialog::save_file_as(const boost::filesystem::path &file_path) { - return Win32Dialog().save(L"Save File As", file_path); + return Win32Dialog().save(L"Save File As", file_path); } diff --git a/src/directories.cc b/src/directories.cc index 0ceb032c..299f379c 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -6,717 +6,726 @@ #include "filesystem.h" #include "entrybox.h" -bool Directories::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const { - return true; +bool Directories::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, + const Gtk::SelectionData &selection_data) const { + return true; } -bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) { - auto &directories=Directories::get(); - - auto get_target_folder=[this, &directories](const TreeModel::Path &path) { - if(path.size()==1) - return directories.path; - else { - auto it=get_iter(path); - if(it) { - auto prev_path=path; - prev_path.up(); - it=get_iter(prev_path); - if(it) - return it->get_value(directories.column_record.path); - } - else { - auto prev_path=path; - prev_path.up(); - if(prev_path.size()==1) - return directories.path; +bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &path, + const Gtk::SelectionData &selection_data) { + auto &directories = Directories::get(); + + auto get_target_folder = [this, &directories](const TreeModel::Path &path) { + if (path.size() == 1) + return directories.path; else { - prev_path.up(); - it=get_iter(prev_path); - if(it) - return it->get_value(directories.column_record.path); + auto it = get_iter(path); + if (it) { + auto prev_path = path; + prev_path.up(); + it = get_iter(prev_path); + if (it) + return it->get_value(directories.column_record.path); + } else { + auto prev_path = path; + prev_path.up(); + if (prev_path.size() == 1) + return directories.path; + else { + prev_path.up(); + it = get_iter(prev_path); + if (it) + return it->get_value(directories.column_record.path); + } + } } - } - } - return boost::filesystem::path(); - }; - - auto it=directories.get_selection()->get_selected(); - if(it) { - auto source_path=it->get_value(directories.column_record.path); - if(source_path.empty()) - return false; - - auto target_path=get_target_folder(path); - target_path/=source_path.filename(); - - if(source_path==target_path) - return false; - - if(boost::filesystem::exists(target_path)) { - Terminal::get().print("Error: could not move file: "+target_path.string()+" already exists\n", true); - return false; - } - - bool is_directory=boost::filesystem::is_directory(source_path); - - if(is_directory) - Directories::get().remove_path(source_path); - - boost::system::error_code ec; - boost::filesystem::rename(source_path, target_path, ec); - if(ec) { - Terminal::get().print("Error: could not move file: "+ec.message()+'\n', true); - return false; - } - - for(size_t c=0;cfile_path, source_path)) { - auto file_it=view->file_path.begin(); - for(auto source_it=source_path.begin();source_it!=source_path.end();source_it++) - file_it++; - auto new_file_path=target_path; - for(;file_it!=view->file_path.end();file_it++) - new_file_path/=*file_it; - view->rename(new_file_path); + return boost::filesystem::path(); + }; + + auto it = directories.get_selection()->get_selected(); + if (it) { + auto source_path = it->get_value(directories.column_record.path); + if (source_path.empty()) + return false; + + auto target_path = get_target_folder(path); + target_path /= source_path.filename(); + + if (source_path == target_path) + return false; + + if (boost::filesystem::exists(target_path)) { + Terminal::get().print("Error: could not move file: " + target_path.string() + " already exists\n", true); + return false; + } + + bool is_directory = boost::filesystem::is_directory(source_path); + + if (is_directory) + Directories::get().remove_path(source_path); + + boost::system::error_code ec; + boost::filesystem::rename(source_path, target_path, ec); + if (ec) { + Terminal::get().print("Error: could not move file: " + ec.message() + '\n', true); + return false; + } + + for (size_t c = 0; c < Notebook::get().size(); c++) { + auto view = Notebook::get().get_view(c); + if (is_directory) { + if (filesystem::file_in_path(view->file_path, source_path)) { + auto file_it = view->file_path.begin(); + for (auto source_it = source_path.begin(); source_it != source_path.end(); source_it++) + file_it++; + auto new_file_path = target_path; + for (; file_it != view->file_path.end(); file_it++) + new_file_path /= *file_it; + view->rename(new_file_path); + } + } else if (view->file_path == source_path) { + view->rename(target_path); + break; + } } - } - else if(view->file_path==source_path) { - view->rename(target_path); - break; - } + + Directories::get().update(); + Directories::get().on_save_file(target_path); + directories.select(target_path); } - - Directories::get().update(); - Directories::get().on_save_file(target_path); - directories.select(target_path); - } - - EntryBox::get().hide(); - return false; + + EntryBox::get().hide(); + return false; } -bool Directories::TreeStore::drag_data_delete_vfunc (const Gtk::TreeModel::Path &path) { - return false; +bool Directories::TreeStore::drag_data_delete_vfunc(const Gtk::TreeModel::Path &path) { + return false; } Directories::Directories() : Gtk::ListViewText(1) { - set_enable_tree_lines(true); - - tree_store = TreeStore::create(); - tree_store->set_column_types(column_record); - set_model(tree_store); - - get_column(0)->set_title(""); - - auto renderer=dynamic_cast(get_column(0)->get_first_cell()); - get_column(0)->set_cell_data_func(*renderer, [this] (Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &iter) { - if(auto renderer_text=dynamic_cast(renderer)) - renderer_text->property_markup()=iter->get_value(column_record.markup); - }); - - get_style_context()->add_class("juci_directories"); - - tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING); - set_enable_search(true); //TODO: why does this not work in OS X? - set_search_column(column_record.name); - - signal_row_activated().connect([this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column){ - auto iter = tree_store->get_iter(path); - if (iter) { - auto filesystem_path=iter->get_value(column_record.path); - if(filesystem_path!="") { - if (boost::filesystem::is_directory(boost::filesystem::path(filesystem_path))) - row_expanded(path) ? collapse_row(path) : expand_row(path, false); - else - Notebook::get().open(filesystem_path); - } - } - }); - - signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path){ - if(iter->children().begin()->get_value(column_record.path)=="") - add_or_update_path(iter->get_value(column_record.path), *iter, true); - return false; - }); - signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path){ - this->remove_path(iter->get_value(column_record.path)); - }); - - enable_model_drag_source(); - enable_model_drag_dest(); - - auto new_file_label = "New File"; - auto new_file_function = [this] { - if(menu_popup_row_path.empty()) - return; - EntryBox::get().clear(); - EntryBox::get().entries.emplace_back("", [this, source_path=menu_popup_row_path](const std::string &content) { - bool is_directory=boost::filesystem::is_directory(source_path); - auto target_path = (is_directory ? source_path : source_path.parent_path())/content; - if(!boost::filesystem::exists(target_path)) { - if(filesystem::write(target_path, "")) { - update(); - Notebook::get().open(target_path); - on_save_file(target_path); - } - else { - Terminal::get().print("Error: could not create "+target_path.string()+'\n', true); - return; + set_enable_tree_lines(true); + + tree_store = TreeStore::create(); + tree_store->set_column_types(column_record); + set_model(tree_store); + + get_column(0)->set_title(""); + + auto renderer = dynamic_cast(get_column(0)->get_first_cell()); + get_column(0)->set_cell_data_func(*renderer, + [this](Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &iter) { + if (auto renderer_text = dynamic_cast(renderer)) + renderer_text->property_markup() = iter->get_value(column_record.markup); + }); + + get_style_context()->add_class("juci_directories"); + + tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING); + set_enable_search(true); //TODO: why does this not work in OS X? + set_search_column(column_record.name); + + signal_row_activated().connect([this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) { + auto iter = tree_store->get_iter(path); + if (iter) { + auto filesystem_path = iter->get_value(column_record.path); + if (filesystem_path != "") { + if (boost::filesystem::is_directory(boost::filesystem::path(filesystem_path))) + row_expanded(path) ? collapse_row(path) : expand_row(path, false); + else + Notebook::get().open(filesystem_path); + } } - } - else { - Terminal::get().print("Error: could not create "+target_path.string()+": already exists\n", true); - return; - } - - EntryBox::get().hide(); }); - auto entry_it=EntryBox::get().entries.begin(); - entry_it->set_placeholder_text("Filename"); - EntryBox::get().buttons.emplace_back("Create New File", [entry_it](){ - entry_it->activate(); + + signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) { + if (iter->children().begin()->get_value(column_record.path) == "") + add_or_update_path(iter->get_value(column_record.path), *iter, true); + return false; }); - EntryBox::get().show(); - }; - - menu_item_new_file.set_label(new_file_label); - menu_item_new_file.signal_activate().connect(new_file_function); - menu.append(menu_item_new_file); - - menu_root_item_new_file.set_label(new_file_label); - menu_root_item_new_file.signal_activate().connect(new_file_function); - menu_root.append(menu_root_item_new_file); - - auto new_folder_label = "New Folder"; - auto new_folder_function = [this] { - if(menu_popup_row_path.empty()) - return; - EntryBox::get().clear(); - EntryBox::get().entries.emplace_back("", [this, source_path=menu_popup_row_path](const std::string &content) { - bool is_directory=boost::filesystem::is_directory(source_path); - auto target_path = (is_directory ? source_path : source_path.parent_path())/content; - if(!boost::filesystem::exists(target_path)) { - boost::system::error_code ec; - boost::filesystem::create_directory(target_path, ec); - if(!ec) { - update(); - select(target_path); - } - else { - Terminal::get().print("Error: could not create "+target_path.string()+": "+ec.message(), true); - return; - } - } - else { - Terminal::get().print("Error: could not create "+target_path.string()+": already exists\n", true); - return; - } - - EntryBox::get().hide(); + signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) { + this->remove_path(iter->get_value(column_record.path)); }); - auto entry_it=EntryBox::get().entries.begin(); - entry_it->set_placeholder_text("Folder name"); - EntryBox::get().buttons.emplace_back("Create New Folder", [entry_it](){ - entry_it->activate(); + + enable_model_drag_source(); + enable_model_drag_dest(); + + auto new_file_label = "New File"; + auto new_file_function = [this] { + if (menu_popup_row_path.empty()) + return; + EntryBox::get().clear(); + EntryBox::get().entries.emplace_back("", [this, source_path = menu_popup_row_path](const std::string &content) { + bool is_directory = boost::filesystem::is_directory(source_path); + auto target_path = (is_directory ? source_path : source_path.parent_path()) / content; + if (!boost::filesystem::exists(target_path)) { + if (filesystem::write(target_path, "")) { + update(); + Notebook::get().open(target_path); + on_save_file(target_path); + } else { + Terminal::get().print("Error: could not create " + target_path.string() + '\n', true); + return; + } + } else { + Terminal::get().print("Error: could not create " + target_path.string() + ": already exists\n", true); + return; + } + + EntryBox::get().hide(); + }); + auto entry_it = EntryBox::get().entries.begin(); + entry_it->set_placeholder_text("Filename"); + EntryBox::get().buttons.emplace_back("Create New File", [entry_it]() { + entry_it->activate(); + }); + EntryBox::get().show(); + }; + + menu_item_new_file.set_label(new_file_label); + menu_item_new_file.signal_activate().connect(new_file_function); + menu.append(menu_item_new_file); + + menu_root_item_new_file.set_label(new_file_label); + menu_root_item_new_file.signal_activate().connect(new_file_function); + menu_root.append(menu_root_item_new_file); + + auto new_folder_label = "New Folder"; + auto new_folder_function = [this] { + if (menu_popup_row_path.empty()) + return; + EntryBox::get().clear(); + EntryBox::get().entries.emplace_back("", [this, source_path = menu_popup_row_path](const std::string &content) { + bool is_directory = boost::filesystem::is_directory(source_path); + auto target_path = (is_directory ? source_path : source_path.parent_path()) / content; + if (!boost::filesystem::exists(target_path)) { + boost::system::error_code ec; + boost::filesystem::create_directory(target_path, ec); + if (!ec) { + update(); + select(target_path); + } else { + Terminal::get().print("Error: could not create " + target_path.string() + ": " + ec.message(), + true); + return; + } + } else { + Terminal::get().print("Error: could not create " + target_path.string() + ": already exists\n", true); + return; + } + + EntryBox::get().hide(); + }); + auto entry_it = EntryBox::get().entries.begin(); + entry_it->set_placeholder_text("Folder name"); + EntryBox::get().buttons.emplace_back("Create New Folder", [entry_it]() { + entry_it->activate(); + }); + EntryBox::get().show(); + }; + + menu_item_new_folder.set_label(new_folder_label); + menu_item_new_folder.signal_activate().connect(new_folder_function); + menu.append(menu_item_new_folder); + + menu_root_item_new_folder.set_label(new_folder_label); + menu_root_item_new_folder.signal_activate().connect(new_folder_function); + menu_root.append(menu_root_item_new_folder); + + menu.append(menu_item_separator); + + menu_item_rename.set_label("Rename"); + menu_item_rename.signal_activate().connect([this] { + if (menu_popup_row_path.empty()) + return; + EntryBox::get().clear(); + EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), + [this, source_path = menu_popup_row_path](const std::string &content) { + bool is_directory = boost::filesystem::is_directory(source_path); + + auto target_path = source_path.parent_path() / content; + + if (boost::filesystem::exists(target_path)) { + Terminal::get().print( + "Error: could not rename to " + target_path.string() + + ": already exists\n", true); + return; + } + + if (is_directory) + this->remove_path(source_path); + + boost::system::error_code ec; + boost::filesystem::rename(source_path, target_path, ec); + if (ec) { + Terminal::get().print( + "Error: could not rename " + source_path.string() + ": " + + ec.message() + '\n', true); + return; + } + update(); + on_save_file(target_path); + select(target_path); + + for (size_t c = 0; c < Notebook::get().size(); c++) { + auto view = Notebook::get().get_view(c); + if (is_directory) { + if (filesystem::file_in_path(view->file_path, source_path)) { + auto file_it = view->file_path.begin(); + for (auto source_it = source_path.begin(); + source_it != source_path.end(); source_it++) + file_it++; + auto new_file_path = target_path; + for (; file_it != view->file_path.end(); file_it++) + new_file_path /= *file_it; + view->rename(new_file_path); + } + } else if (view->file_path == source_path) { + view->rename(target_path); + + std::string old_language_id; + if (view->language) + old_language_id = view->language->get_id(); + view->language = Source::guess_language(target_path); + std::string new_language_id; + if (view->language) + new_language_id = view->language->get_id(); + if (new_language_id != old_language_id) + Terminal::get().print( + "Warning: language for " + target_path.string() + + " has changed. Please reopen the file\n"); + } + } + + EntryBox::get().hide(); + }); + auto entry_it = EntryBox::get().entries.begin(); + entry_it->set_placeholder_text("Filename"); + EntryBox::get().buttons.emplace_back("Rename file", [entry_it]() { + entry_it->activate(); + }); + EntryBox::get().show(); }); - EntryBox::get().show(); - }; - - menu_item_new_folder.set_label(new_folder_label); - menu_item_new_folder.signal_activate().connect(new_folder_function); - menu.append(menu_item_new_folder); - - menu_root_item_new_folder.set_label(new_folder_label); - menu_root_item_new_folder.signal_activate().connect(new_folder_function); - menu_root.append(menu_root_item_new_folder); - - menu.append(menu_item_separator); - - menu_item_rename.set_label("Rename"); - menu_item_rename.signal_activate().connect([this] { - if(menu_popup_row_path.empty()) - return; - EntryBox::get().clear(); - EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), [this, source_path=menu_popup_row_path](const std::string &content){ - bool is_directory=boost::filesystem::is_directory(source_path); - - auto target_path=source_path.parent_path()/content; - - if(boost::filesystem::exists(target_path)) { - Terminal::get().print("Error: could not rename to "+target_path.string()+": already exists\n", true); - return; - } - - if(is_directory) - this->remove_path(source_path); - - boost::system::error_code ec; - boost::filesystem::rename(source_path, target_path, ec); - if(ec) { - Terminal::get().print("Error: could not rename "+source_path.string()+": "+ec.message()+'\n', true); - return; - } - update(); - on_save_file(target_path); - select(target_path); - - for(size_t c=0;cfile_path, source_path)) { - auto file_it=view->file_path.begin(); - for(auto source_it=source_path.begin();source_it!=source_path.end();source_it++) - file_it++; - auto new_file_path=target_path; - for(;file_it!=view->file_path.end();file_it++) - new_file_path/=*file_it; - view->rename(new_file_path); - } - } - else if(view->file_path==source_path) { - view->rename(target_path); - - std::string old_language_id; - if(view->language) - old_language_id=view->language->get_id(); - view->language=Source::guess_language(target_path); - std::string new_language_id; - if(view->language) - new_language_id=view->language->get_id(); - if(new_language_id!=old_language_id) - Terminal::get().print("Warning: language for "+target_path.string()+" has changed. Please reopen the file\n"); + menu.append(menu_item_rename); + + menu_item_delete.set_label("Delete"); + menu_item_delete.signal_activate().connect([this] { + if (menu_popup_row_path.empty()) + return; + Gtk::MessageDialog dialog(*static_cast(get_toplevel()), "Delete!", false, Gtk::MESSAGE_QUESTION, + Gtk::BUTTONS_YES_NO); + dialog.set_default_response(Gtk::RESPONSE_NO); + dialog.set_secondary_text("Are you sure you want to delete " + menu_popup_row_path.string() + "?"); + int result = dialog.run(); + if (result == Gtk::RESPONSE_YES) { + bool is_directory = boost::filesystem::is_directory(menu_popup_row_path); + + boost::system::error_code ec; + boost::filesystem::remove_all(menu_popup_row_path, ec); + if (ec) + Terminal::get().print( + "Error: could not delete " + menu_popup_row_path.string() + ": " + ec.message() + "\n", true); + else { + update(); + + for (size_t c = 0; c < Notebook::get().size(); c++) { + auto view = Notebook::get().get_view(c); + + if (is_directory) { + if (filesystem::file_in_path(view->file_path, menu_popup_row_path)) + view->get_buffer()->set_modified(); + } else if (view->file_path == menu_popup_row_path) + view->get_buffer()->set_modified(); + } + } } - } - - EntryBox::get().hide(); }); - auto entry_it=EntryBox::get().entries.begin(); - entry_it->set_placeholder_text("Filename"); - EntryBox::get().buttons.emplace_back("Rename file", [entry_it](){ - entry_it->activate(); - }); - EntryBox::get().show(); - }); - menu.append(menu_item_rename); - - menu_item_delete.set_label("Delete"); - menu_item_delete.signal_activate().connect([this] { - if(menu_popup_row_path.empty()) - return; - Gtk::MessageDialog dialog(*static_cast(get_toplevel()), "Delete!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO); - dialog.set_default_response(Gtk::RESPONSE_NO); - dialog.set_secondary_text("Are you sure you want to delete "+menu_popup_row_path.string()+"?"); - int result = dialog.run(); - if(result==Gtk::RESPONSE_YES) { - bool is_directory=boost::filesystem::is_directory(menu_popup_row_path); - - boost::system::error_code ec; - boost::filesystem::remove_all(menu_popup_row_path, ec); - if(ec) - Terminal::get().print("Error: could not delete "+menu_popup_row_path.string()+": "+ec.message()+"\n", true); - else { - update(); - - for(size_t c=0;cfile_path, menu_popup_row_path)) - view->get_buffer()->set_modified(); - } - else if(view->file_path==menu_popup_row_path) - view->get_buffer()->set_modified(); - } - } - } - }); - menu.append(menu_item_delete); - - menu.show_all(); - menu.accelerate(*this); - - menu_root.show_all(); - menu_root.accelerate(*this); - - set_headers_clickable(); - forall([this](Gtk::Widget &widget) { - if(widget.get_name()=="GtkButton") { - widget.signal_button_press_event().connect([this](GdkEventButton *event) { - if(event->type==GDK_BUTTON_PRESS && event->button==GDK_BUTTON_SECONDARY && !path.empty()) { - menu_popup_row_path=this->path; - menu_root.popup(event->button, event->time); + menu.append(menu_item_delete); + + menu.show_all(); + menu.accelerate(*this); + + menu_root.show_all(); + menu_root.accelerate(*this); + + set_headers_clickable(); + forall([this](Gtk::Widget &widget) { + if (widget.get_name() == "GtkButton") { + widget.signal_button_press_event().connect([this](GdkEventButton *event) { + if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY && !path.empty()) { + menu_popup_row_path = this->path; + menu_root.popup(event->button, event->time); + } + return true; + }); } - return true; - }); - } - }); + }); } Directories::~Directories() { - dispatcher.disconnect(); + dispatcher.disconnect(); } void Directories::open(const boost::filesystem::path &dir_path) { - boost::system::error_code ec; - if(dir_path.empty() || !boost::filesystem::exists(dir_path, ec) || ec) - return; - - tree_store->clear(); - - path=filesystem::get_normal_path(dir_path); - - //TODO: report that set_title does not handle '_' correctly? - auto title=path.filename().string(); - size_t pos=0; - while((pos=title.find('_', pos))!=std::string::npos) { - title.replace(pos, 1, "__"); - pos+=2; - } - get_column(0)->set_title(title); - - for(auto &directory: directories) { - if(directory.second.repository) - directory.second.repository->clear_saved_status(); - } - directories.clear(); - - add_or_update_path(path, Gtk::TreeModel::Row(), true); + boost::system::error_code ec; + if (dir_path.empty() || !boost::filesystem::exists(dir_path, ec) || ec) + return; + + tree_store->clear(); + + path = filesystem::get_normal_path(dir_path); + + //TODO: report that set_title does not handle '_' correctly? + auto title = path.filename().string(); + size_t pos = 0; + while ((pos = title.find('_', pos)) != std::string::npos) { + title.replace(pos, 1, "__"); + pos += 2; + } + get_column(0)->set_title(title); + + for (auto &directory: directories) { + if (directory.second.repository) + directory.second.repository->clear_saved_status(); + } + directories.clear(); + + add_or_update_path(path, Gtk::TreeModel::Row(), true); } void Directories::update() { - std::vector > saved_directories; - for(auto &directory: directories) - saved_directories.emplace_back(directory.first, directory.second.row); - for(auto &directory: saved_directories) - add_or_update_path(directory.first, directory.second, false); + std::vector > saved_directories; + for (auto &directory: directories) + saved_directories.emplace_back(directory.first, directory.second.row); + for (auto &directory: saved_directories) + add_or_update_path(directory.first, directory.second, false); } void Directories::on_save_file(boost::filesystem::path file_path) { - auto it=directories.find(file_path.parent_path().string()); - if(it!=directories.end()) { - if(it->second.repository) - it->second.repository->clear_saved_status(); - colorize_path(it->first, true); - } + auto it = directories.find(file_path.parent_path().string()); + if (it != directories.end()) { + if (it->second.repository) + it->second.repository->clear_saved_status(); + colorize_path(it->first, true); + } } void Directories::select(const boost::filesystem::path &select_path) { - if(path=="") - return; - - if(!filesystem::file_in_path(select_path, path)) - return; - - //return if the select_path is already selected - auto iter=get_selection()->get_selected(); - if(iter) { - if(iter->get_value(column_record.path)==select_path) - return; - } - - std::list paths; - boost::filesystem::path parent_path; - if(boost::filesystem::is_directory(select_path)) - parent_path=select_path; - else - parent_path=select_path.parent_path(); - - //check if select_path is already expanded - if(directories.find(parent_path.string())!=directories.end()) { - //set cursor at select_path and return - tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter){ - if(iter->get_value(column_record.path)==select_path) { - auto tree_path=Gtk::TreePath(iter); - expand_to_path(tree_path); - set_cursor(tree_path); - return true; - } - return false; - }); - return; - } - - paths.emplace_front(parent_path); - while(parent_path!=path) { - parent_path=parent_path.parent_path(); + if (path == "") + return; + + if (!filesystem::file_in_path(select_path, path)) + return; + + //return if the select_path is already selected + auto iter = get_selection()->get_selected(); + if (iter) { + if (iter->get_value(column_record.path) == select_path) + return; + } + + std::list paths; + boost::filesystem::path parent_path; + if (boost::filesystem::is_directory(select_path)) + parent_path = select_path; + else + parent_path = select_path.parent_path(); + + //check if select_path is already expanded + if (directories.find(parent_path.string()) != directories.end()) { + //set cursor at select_path and return + tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter) { + if (iter->get_value(column_record.path) == select_path) { + auto tree_path = Gtk::TreePath(iter); + expand_to_path(tree_path); + set_cursor(tree_path); + return true; + } + return false; + }); + return; + } + paths.emplace_front(parent_path); - } - - //expand to select_path - for(auto &a_path: paths) { - tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator &iter){ - if(iter->get_value(column_record.path)==a_path) { - add_or_update_path(a_path, *iter, true); - return true; - } - return false; - }); - } - - //set cursor at select_path - tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter){ - if(iter->get_value(column_record.path)==select_path) { - auto tree_path=Gtk::TreePath(iter); - expand_to_path(tree_path); - set_cursor(tree_path); - return true; + while (parent_path != path) { + parent_path = parent_path.parent_path(); + paths.emplace_front(parent_path); } - return false; - }); + + //expand to select_path + for (auto &a_path: paths) { + tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator &iter) { + if (iter->get_value(column_record.path) == a_path) { + add_or_update_path(a_path, *iter, true); + return true; + } + return false; + }); + } + + //set cursor at select_path + tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter) { + if (iter->get_value(column_record.path) == select_path) { + auto tree_path = Gtk::TreePath(iter); + expand_to_path(tree_path); + set_cursor(tree_path); + return true; + } + return false; + }); } -bool Directories::on_button_press_event(GdkEventButton* event) { - if(event->type==GDK_BUTTON_PRESS && event->button==GDK_BUTTON_SECONDARY) { - EntryBox::get().hide(); - Gtk::TreeModel::Path path; - if(get_path_at_pos(static_cast(event->x), static_cast(event->y), path)) { - menu_popup_row_path=get_model()->get_iter(path)->get_value(column_record.path); - if(menu_popup_row_path.empty()) { - auto parent=get_model()->get_iter(path)->parent(); - if(parent) - menu_popup_row_path=parent->get_value(column_record.path); - else { - menu_popup_row_path=this->path; - menu_root.popup(event->button, event->time); - return true; +bool Directories::on_button_press_event(GdkEventButton *event) { + if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) { + EntryBox::get().hide(); + Gtk::TreeModel::Path path; + if (get_path_at_pos(static_cast(event->x), static_cast(event->y), path)) { + menu_popup_row_path = get_model()->get_iter(path)->get_value(column_record.path); + if (menu_popup_row_path.empty()) { + auto parent = get_model()->get_iter(path)->parent(); + if (parent) + menu_popup_row_path = parent->get_value(column_record.path); + else { + menu_popup_row_path = this->path; + menu_root.popup(event->button, event->time); + return true; + } + } + menu.popup(event->button, event->time); + return true; + } else if (!this->path.empty()) { + menu_popup_row_path = this->path; + menu_root.popup(event->button, event->time); + return true; } - } - menu.popup(event->button, event->time); - return true; } - else if(!this->path.empty()) { - menu_popup_row_path=this->path; - menu_root.popup(event->button, event->time); - return true; - } - } - - return Gtk::TreeView::on_button_press_event(event); + + return Gtk::TreeView::on_button_press_event(event); } -void Directories::add_or_update_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, bool include_parent_paths) { - auto path_it=directories.find(dir_path.string()); - if(!boost::filesystem::exists(dir_path)) { - if(path_it!=directories.end()) - directories.erase(path_it); - return; - } - - if(path_it==directories.end()) { - auto g_file=Gio::File::create_for_path(dir_path.string()); - auto monitor=g_file->monitor_directory(Gio::FileMonitorFlags::FILE_MONITOR_WATCH_MOVES); - auto path_and_row=std::make_shared >(dir_path, row); - auto connection=std::make_shared(); - - std::shared_ptr repository; - try { - repository=Git::get_repository(dir_path); +void Directories::add_or_update_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, + bool include_parent_paths) { + auto path_it = directories.find(dir_path.string()); + if (!boost::filesystem::exists(dir_path)) { + if (path_it != directories.end()) + directories.erase(path_it); + return; } - catch(const std::exception &) {} - - monitor->signal_changed().connect([this, connection, path_and_row, repository] (const Glib::RefPtr &file, - const Glib::RefPtr&, - Gio::FileMonitorEvent monitor_event) { - if(monitor_event!=Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { - if(repository) - repository->clear_saved_status(); - connection->disconnect(); - *connection=Glib::signal_timeout().connect([path_and_row, this]() { - if(directories.find(path_and_row->first.string())!=directories.end()) - add_or_update_path(path_and_row->first, path_and_row->second, true); - return false; - }, 500); - } - }); - - std::shared_ptr repository_connection(new sigc::connection(), [](sigc::connection *connection) { - connection->disconnect(); - delete connection; - }); - - if(repository) { - auto connection=std::make_shared(); - *repository_connection=repository->monitor->signal_changed().connect([this, connection, path_and_row](const Glib::RefPtr &file, - const Glib::RefPtr&, - Gio::FileMonitorEvent monitor_event) { - if(monitor_event!=Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { - connection->disconnect(); - *connection=Glib::signal_timeout().connect([this, path_and_row] { - if(directories.find(path_and_row->first.string())!=directories.end()) - colorize_path(path_and_row->first, false); - return false; - }, 500); + + if (path_it == directories.end()) { + auto g_file = Gio::File::create_for_path(dir_path.string()); + auto monitor = g_file->monitor_directory(Gio::FileMonitorFlags::FILE_MONITOR_WATCH_MOVES); + auto path_and_row = std::make_shared >(dir_path, row); + auto connection = std::make_shared(); + + std::shared_ptr repository; + try { + repository = Git::get_repository(dir_path); } - }); + catch (const std::exception &) {} + + monitor->signal_changed().connect( + [this, connection, path_and_row, repository](const Glib::RefPtr &file, + const Glib::RefPtr &, + Gio::FileMonitorEvent monitor_event) { + if (monitor_event != Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { + if (repository) + repository->clear_saved_status(); + connection->disconnect(); + *connection = Glib::signal_timeout().connect([path_and_row, this]() { + if (directories.find(path_and_row->first.string()) != directories.end()) + add_or_update_path(path_and_row->first, path_and_row->second, true); + return false; + }, 500); + } + }); + + std::shared_ptr repository_connection(new sigc::connection(), + [](sigc::connection *connection) { + connection->disconnect(); + delete connection; + }); + + if (repository) { + auto connection = std::make_shared(); + *repository_connection = repository->monitor->signal_changed().connect( + [this, connection, path_and_row](const Glib::RefPtr &file, + const Glib::RefPtr &, + Gio::FileMonitorEvent monitor_event) { + if (monitor_event != Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { + connection->disconnect(); + *connection = Glib::signal_timeout().connect([this, path_and_row] { + if (directories.find(path_and_row->first.string()) != directories.end()) + colorize_path(path_and_row->first, false); + return false; + }, 500); + } + }); + } + directories[dir_path.string()] = {row, monitor, repository, repository_connection}; } - directories[dir_path.string()]={row, monitor, repository, repository_connection}; - } - - Gtk::TreeNodeChildren children(row?row.children():tree_store->children()); - if(children) { - if(children.begin()->get_value(column_record.path)=="") - tree_store->erase(children.begin()); - } - std::unordered_set not_deleted; - boost::filesystem::directory_iterator end_it; - for(boost::filesystem::directory_iterator it(dir_path);it!=end_it;it++) { - auto filename=it->path().filename().string(); - bool already_added=false; - if(children) { - for(auto &child: children) { - if(child->get_value(column_record.name)==filename) { - not_deleted.emplace(filename); - already_added=true; - break; + + Gtk::TreeNodeChildren children(row ? row.children() : tree_store->children()); + if (children) { + if (children.begin()->get_value(column_record.path) == "") + tree_store->erase(children.begin()); + } + std::unordered_set not_deleted; + boost::filesystem::directory_iterator end_it; + for (boost::filesystem::directory_iterator it(dir_path); it != end_it; it++) { + auto filename = it->path().filename().string(); + bool already_added = false; + if (children) { + for (auto &child: children) { + if (child->get_value(column_record.name) == filename) { + not_deleted.emplace(filename); + already_added = true; + break; + } + } + } + if (!already_added) { + auto child = tree_store->append(children); + not_deleted.emplace(filename); + child->set_value(column_record.name, filename); + child->set_value(column_record.markup, Glib::Markup::escape_text(filename)); + child->set_value(column_record.path, it->path()); + if (boost::filesystem::is_directory(it->path())) { + child->set_value(column_record.id, '1' + filename); + auto grandchild = tree_store->append(child->children()); + grandchild->set_value(column_record.name, std::string("(empty)")); + grandchild->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); + grandchild->set_value(column_record.type, PathType::UNKNOWN); + } else { + child->set_value(column_record.id, '2' + filename); + + auto language = Source::guess_language(it->path().filename()); + if (!language) + child->set_value(column_record.type, PathType::UNKNOWN); + } } - } } - if(!already_added) { - auto child = tree_store->append(children); - not_deleted.emplace(filename); - child->set_value(column_record.name, filename); - child->set_value(column_record.markup, Glib::Markup::escape_text(filename)); - child->set_value(column_record.path, it->path()); - if (boost::filesystem::is_directory(it->path())) { - child->set_value(column_record.id, '1'+filename); - auto grandchild=tree_store->append(child->children()); - grandchild->set_value(column_record.name, std::string("(empty)")); - grandchild->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); - grandchild->set_value(column_record.type, PathType::UNKNOWN); - } - else { - child->set_value(column_record.id, '2'+filename); - - auto language=Source::guess_language(it->path().filename()); - if(!language) - child->set_value(column_record.type, PathType::UNKNOWN); - } + if (children) { + for (auto it = children.begin(); it != children.end();) { + if (not_deleted.count(it->get_value(column_record.name)) == 0) { + it = tree_store->erase(it); + } else + it++; + } } - } - if(children) { - for(auto it=children.begin();it!=children.end();) { - if(not_deleted.count(it->get_value(column_record.name))==0) { - it=tree_store->erase(it); - } - else - it++; + if (!children) { + auto child = tree_store->append(children); + child->set_value(column_record.name, std::string("(empty)")); + child->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); + child->set_value(column_record.type, PathType::UNKNOWN); } - } - if(!children) { - auto child=tree_store->append(children); - child->set_value(column_record.name, std::string("(empty)")); - child->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); - child->set_value(column_record.type, PathType::UNKNOWN); - } - - colorize_path(dir_path, include_parent_paths); + + colorize_path(dir_path, include_parent_paths); } void Directories::remove_path(const boost::filesystem::path &dir_path) { - auto it=directories.find(dir_path.string()); - if(it==directories.end()) - return; - auto children=it->second.row->children(); - - for(auto it=directories.begin();it!=directories.end();) { - if(filesystem::file_in_path(it->first, dir_path)) - it=directories.erase(it); - else - it++; - } - - if(children) { - while(children) { - tree_store->erase(children.begin()); + auto it = directories.find(dir_path.string()); + if (it == directories.end()) + return; + auto children = it->second.row->children(); + + for (auto it = directories.begin(); it != directories.end();) { + if (filesystem::file_in_path(it->first, dir_path)) + it = directories.erase(it); + else + it++; + } + + if (children) { + while (children) { + tree_store->erase(children.begin()); + } + auto child = tree_store->append(children); + child->set_value(column_record.name, std::string("(empty)")); + child->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); + child->set_value(column_record.type, PathType::UNKNOWN); } - auto child=tree_store->append(children); - child->set_value(column_record.name, std::string("(empty)")); - child->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); - child->set_value(column_record.type, PathType::UNKNOWN); - } } void Directories::colorize_path(const boost::filesystem::path &dir_path, bool include_parent_paths) { - auto it=directories.find(dir_path.string()); - if(it==directories.end()) - return; - - if(it!=directories.end() && it->second.repository) { - auto repository=it->second.repository; - std::thread git_status_thread([this, dir_path, repository, include_parent_paths] { - Git::Repository::Status status; - try { - status=repository->get_status(); - } - catch(const std::exception &e) { - Terminal::get().async_print(std::string("Error (git): ")+e.what()+'\n', true); - } - - dispatcher.post([this, dir_path=std::move(dir_path), include_parent_paths, status=std::move(status)] { - auto it=directories.find(dir_path.string()); - if(it==directories.end()) - return; - - auto normal_color=get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL); - Gdk::RGBA gray; - gray.set_rgba(0.5, 0.5, 0.5); - Gdk::RGBA yellow; - yellow.set_rgba(1.0, 1.0, 0.2); - double factor=0.5; - yellow.set_red(normal_color.get_red()+factor*(yellow.get_red()-normal_color.get_red())); - yellow.set_green(normal_color.get_green()+factor*(yellow.get_green()-normal_color.get_green())); - yellow.set_blue(normal_color.get_blue()+factor*(yellow.get_blue()-normal_color.get_blue())); - Gdk::RGBA green; - green.set_rgba(0.0, 1.0, 0.0); - factor=0.4; - green.set_red(normal_color.get_red()+factor*(green.get_red()-normal_color.get_red())); - green.set_green(normal_color.get_green()+factor*(green.get_green()-normal_color.get_green())); - green.set_blue(normal_color.get_blue()+factor*(green.get_blue()-normal_color.get_blue())); - - do { - Gtk::TreeNodeChildren children(it->second.row?it->second.row.children():tree_store->children()); - if(!children) - return; - - for(auto &child: children) { - auto name=Glib::Markup::escape_text(child.get_value(column_record.name)); - auto path=child.get_value(column_record.path); - Gdk::RGBA *color; - if(status.modified.find(path.generic_string())!=status.modified.end()) - color=&yellow; - else if(status.added.find(path.generic_string())!=status.added.end()) - color=&green; - else - color=&normal_color; - - std::stringstream ss; - ss << '#' << std::setfill('0') << std::hex; - ss << std::setw(2) << std::hex << (color->get_red_u()>>8); - ss << std::setw(2) << std::hex << (color->get_green_u()>>8); - ss << std::setw(2) << std::hex << (color->get_blue_u()>>8); - child.set_value(column_record.markup, ""+name+""); - - auto type=child.get_value(column_record.type); - if(type==PathType::UNKNOWN) - child.set_value(column_record.markup, ""+child.get_value(column_record.markup)+""); - } - - if(!include_parent_paths) - break; - - auto path=boost::filesystem::path(it->first); - if(boost::filesystem::exists(path/".git")) - break; - if(path==path.root_directory()) - break; - auto parent_path=boost::filesystem::path(it->first).parent_path(); - it=directories.find(parent_path.string()); - } while(it!=directories.end()); - }); - }); - git_status_thread.detach(); - } + auto it = directories.find(dir_path.string()); + if (it == directories.end()) + return; + + if (it != directories.end() && it->second.repository) { + auto repository = it->second.repository; + std::thread git_status_thread([this, dir_path, repository, include_parent_paths] { + Git::Repository::Status status; + try { + status = repository->get_status(); + } + catch (const std::exception &e) { + Terminal::get().async_print(std::string("Error (git): ") + e.what() + '\n', true); + } + + dispatcher.post([this, dir_path = std::move(dir_path), include_parent_paths, status = std::move(status)] { + auto it = directories.find(dir_path.string()); + if (it == directories.end()) + return; + + auto normal_color = get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL); + Gdk::RGBA gray; + gray.set_rgba(0.5, 0.5, 0.5); + Gdk::RGBA yellow; + yellow.set_rgba(1.0, 1.0, 0.2); + double factor = 0.5; + yellow.set_red(normal_color.get_red() + factor * (yellow.get_red() - normal_color.get_red())); + yellow.set_green(normal_color.get_green() + factor * (yellow.get_green() - normal_color.get_green())); + yellow.set_blue(normal_color.get_blue() + factor * (yellow.get_blue() - normal_color.get_blue())); + Gdk::RGBA green; + green.set_rgba(0.0, 1.0, 0.0); + factor = 0.4; + green.set_red(normal_color.get_red() + factor * (green.get_red() - normal_color.get_red())); + green.set_green(normal_color.get_green() + factor * (green.get_green() - normal_color.get_green())); + green.set_blue(normal_color.get_blue() + factor * (green.get_blue() - normal_color.get_blue())); + + do { + Gtk::TreeNodeChildren children(it->second.row ? it->second.row.children() : tree_store->children()); + if (!children) + return; + + for (auto &child: children) { + auto name = Glib::Markup::escape_text(child.get_value(column_record.name)); + auto path = child.get_value(column_record.path); + Gdk::RGBA *color; + if (status.modified.find(path.generic_string()) != status.modified.end()) + color = &yellow; + else if (status.added.find(path.generic_string()) != status.added.end()) + color = &green; + else + color = &normal_color; + + std::stringstream ss; + ss << '#' << std::setfill('0') << std::hex; + ss << std::setw(2) << std::hex << (color->get_red_u() >> 8); + ss << std::setw(2) << std::hex << (color->get_green_u() >> 8); + ss << std::setw(2) << std::hex << (color->get_blue_u() >> 8); + child.set_value(column_record.markup, + "" + name + ""); + + auto type = child.get_value(column_record.type); + if (type == PathType::UNKNOWN) + child.set_value(column_record.markup, + "" + child.get_value(column_record.markup) + ""); + } + + if (!include_parent_paths) + break; + + auto path = boost::filesystem::path(it->first); + if (boost::filesystem::exists(path / ".git")) + break; + if (path == path.root_directory()) + break; + auto parent_path = boost::filesystem::path(it->first).parent_path(); + it = directories.find(parent_path.string()); + } while (it != directories.end()); + }); + }); + git_status_thread.detach(); + } } diff --git a/src/directories.h b/src/directories.h index da6191fb..6caaea48 100644 --- a/src/directories.h +++ b/src/directories.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -12,82 +13,96 @@ #include "dispatcher.h" class Directories : public Gtk::ListViewText { - class DirectoryData { - public: - Gtk::TreeModel::Row row; - Glib::RefPtr monitor; - std::shared_ptr repository; - std::shared_ptr connection; - }; - - enum class PathType {KNOWN, UNKNOWN}; - - class TreeStore : public Gtk::TreeStore { - protected: - TreeStore() {} - - bool row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const override; - bool drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) override; - bool drag_data_delete_vfunc (const Gtk::TreeModel::Path &path) override; - - public: - class ColumnRecord : public Gtk::TreeModel::ColumnRecord { + class DirectoryData { + public: + Gtk::TreeModel::Row row; + Glib::RefPtr monitor; + std::shared_ptr repository; + std::shared_ptr connection; + }; + + enum class PathType { + KNOWN, UNKNOWN + }; + + class TreeStore : public Gtk::TreeStore { + protected: + TreeStore() {} + + bool row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, + const Gtk::SelectionData &selection_data) const override; + + bool drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) override; + + bool drag_data_delete_vfunc(const Gtk::TreeModel::Path &path) override; + public: - ColumnRecord() { - add(id); - add(name); - add(markup); - add(path); - add(type); - } - Gtk::TreeModelColumn id; - Gtk::TreeModelColumn name; - Gtk::TreeModelColumn markup; - Gtk::TreeModelColumn path; - Gtk::TreeModelColumn type; + class ColumnRecord : public Gtk::TreeModel::ColumnRecord { + public: + ColumnRecord() { + add(id); + add(name); + add(markup); + add(path); + add(type); + } + + Gtk::TreeModelColumn id; + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn markup; + Gtk::TreeModelColumn path; + Gtk::TreeModelColumn type; + }; + + static Glib::RefPtr create() { return Glib::RefPtr(new TreeStore()); } }; - - static Glib::RefPtr create() {return Glib::RefPtr(new TreeStore());} - }; - Directories(); + Directories(); + public: - static Directories &get() { - static Directories singleton; - return singleton; - } - ~Directories(); - - void open(const boost::filesystem::path &dir_path=""); - void update(); - void on_save_file(boost::filesystem::path file_path); - void select(const boost::filesystem::path &path); - - boost::filesystem::path path; - + static Directories &get() { + static Directories singleton; + return singleton; + } + + ~Directories(); + + void open(const boost::filesystem::path &dir_path = ""); + + void update(); + + void on_save_file(boost::filesystem::path file_path); + + void select(const boost::filesystem::path &path); + + boost::filesystem::path path; + protected: - bool on_button_press_event(GdkEventButton *event) override; - + bool on_button_press_event(GdkEventButton *event) override; + private: - void add_or_update_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, bool include_parent_paths); - void remove_path(const boost::filesystem::path &dir_path); - void colorize_path(const boost::filesystem::path &dir_path, bool include_parent_paths); - - Glib::RefPtr tree_store; - TreeStore::ColumnRecord column_record; - - std::unordered_map directories; - - Dispatcher dispatcher; - - Gtk::Menu menu; - Gtk::MenuItem menu_item_new_file; - Gtk::MenuItem menu_item_new_folder; - Gtk::SeparatorMenuItem menu_item_separator; - Gtk::MenuItem menu_item_rename; - Gtk::MenuItem menu_item_delete; - Gtk::Menu menu_root; - Gtk::MenuItem menu_root_item_new_file; - Gtk::MenuItem menu_root_item_new_folder; - boost::filesystem::path menu_popup_row_path; + void add_or_update_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, + bool include_parent_paths); + + void remove_path(const boost::filesystem::path &dir_path); + + void colorize_path(const boost::filesystem::path &dir_path, bool include_parent_paths); + + Glib::RefPtr tree_store; + TreeStore::ColumnRecord column_record; + + std::unordered_map directories; + + Dispatcher dispatcher; + + Gtk::Menu menu; + Gtk::MenuItem menu_item_new_file; + Gtk::MenuItem menu_item_new_folder; + Gtk::SeparatorMenuItem menu_item_separator; + Gtk::MenuItem menu_item_rename; + Gtk::MenuItem menu_item_delete; + Gtk::Menu menu_root; + Gtk::MenuItem menu_root_item_new_file; + Gtk::MenuItem menu_root_item_new_folder; + boost::filesystem::path menu_popup_row_path; }; diff --git a/src/dispatcher.cc b/src/dispatcher.cc index 5ad2d602..1a0651de 100644 --- a/src/dispatcher.cc +++ b/src/dispatcher.cc @@ -2,30 +2,30 @@ #include Dispatcher::Dispatcher() { - connection=dispatcher.connect([this] { - std::vector>::iterator> its; - { - std::unique_lock lock(functions_mutex); - if(functions.empty()) - return; - its.reserve(functions.size()); - for(auto it=functions.begin();it!=functions.end();++it) - its.emplace_back(it); - } - for(auto &it: its) - (*it)(); - { - std::unique_lock lock(functions_mutex); - for(auto &it: its) - functions.erase(it); - } - }); + connection = dispatcher.connect([this] { + std::vector>::iterator> its; + { + std::unique_lock lock(functions_mutex); + if (functions.empty()) + return; + its.reserve(functions.size()); + for (auto it = functions.begin(); it != functions.end(); ++it) + its.emplace_back(it); + } + for (auto &it: its) + (*it)(); + { + std::unique_lock lock(functions_mutex); + for (auto &it: its) + functions.erase(it); + } + }); } Dispatcher::~Dispatcher() { - disconnect(); + disconnect(); } void Dispatcher::disconnect() { - connection.disconnect(); + connection.disconnect(); } diff --git a/src/dispatcher.h b/src/dispatcher.h index 84766285..6fbab177 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -6,22 +7,23 @@ class Dispatcher { private: - std::list> functions; - std::mutex functions_mutex; - Glib::Dispatcher dispatcher; - sigc::connection connection; + std::list> functions; + std::mutex functions_mutex; + Glib::Dispatcher dispatcher; + sigc::connection connection; public: - Dispatcher(); - ~Dispatcher(); - - template - void post(T &&function) { - { - std::unique_lock lock(functions_mutex); - functions.emplace_back(std::forward(function)); + Dispatcher(); + + ~Dispatcher(); + + template + void post(T &&function) { + { + std::unique_lock lock(functions_mutex); + functions.emplace_back(std::forward(function)); + } + dispatcher(); } - dispatcher(); - } - - void disconnect(); + + void disconnect(); }; diff --git a/src/documentation_cppreference.cc b/src/documentation_cppreference.cc index 060e791d..ef7365bd 100644 --- a/src/documentation_cppreference.cc +++ b/src/documentation_cppreference.cc @@ -1,10 +1,12 @@ -#include "documentation_cppreference.h" -#include +#include +"documentation_cppreference.h" +#include + std::string Documentation::CppReference::get_url(const std::string symbol) noexcept { - // Copied from http://upload.cppreference.com/mwiki/images/d/df/html_book_20170409.zip/reference/cppreference-export-ns0,4,8,10.xml - // Using raw string instead of map to reduce compile time - const static std::string symbol_urls = R"(size_t c/types/size_t +// Copied from http://upload.cppreference.com/mwiki/images/d/df/html_book_20170409.zip/reference/cppreference-export-ns0,4,8,10.xml +// Using raw string instead of map to reduce compile time +const static std::string symbol_urls = R"(size_t c/types/size_t ptrdiff_t c/types/ptrdiff_t nullptr_t c/types/nullptr_t NULL c/types/NULL @@ -12173,34 +12175,34 @@ std::experimental::filesystem::is_socket cpp/experimental/fs/is_socket std::experimental::filesystem::is_symlink cpp/experimental/fs/is_symlink std::experimental::filesystem::status_known cpp/experimental/fs/status_known"} )"; - - class SymbolToUrl { - public: - SymbolToUrl(const std::string &symbol_urls) { - size_t symbol_start=0; - size_t symbol_end=std::string::npos; - size_t url_start=std::string::npos; - for(size_t c=0;c map; - }; - - static SymbolToUrl symbol_to_url(symbol_urls); - auto it=symbol_to_url.map.find(symbol); - if(it==symbol_to_url.map.end()) - return std::string(); - return "http://en.cppreference.com/w/"+it->second; + +class SymbolToUrl { +public: +SymbolToUrl(const std::string &symbol_urls) { +size_t symbol_start=0; +size_t symbol_end=std::string::npos; +size_t url_start=std::string::npos; +for (size_t c=0;c map; +}; + +static SymbolToUrl symbol_to_url(symbol_urls); +auto it=symbol_to_url.map.find(symbol); +if (it==symbol_to_url.map.end()) +return std::string(); +return "http://en.cppreference.com/w/"+it->second; } diff --git a/src/documentation_cppreference.h b/src/documentation_cppreference.h index 7a61f837..db9bf26e 100644 --- a/src/documentation_cppreference.h +++ b/src/documentation_cppreference.h @@ -1,9 +1,10 @@ #pragma once + #include namespace Documentation { - class CppReference { - public: - static std::string get_url(const std::string symbol) noexcept; - }; + class CppReference { + public: + static std::string get_url(const std::string symbol) noexcept; + }; } diff --git a/src/entrybox.cc b/src/entrybox.cc index b5cd6638..5a81ef1e 100644 --- a/src/entrybox.cc +++ b/src/entrybox.cc @@ -2,96 +2,101 @@ std::unordered_map > EntryBox::entry_histories; -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](){ - if(this->on_activate) { - auto &history=EntryBox::entry_histories[get_placeholder_text()]; - auto text=get_text(); - if(history.size()==0 || (history.size()>0 && *history.begin()!=text)) - history.emplace(history.begin(), text); - selected_history=0; - this->on_activate(text); - } - }); - signal_key_press_event().connect([this](GdkEventKey* key){ - if(key->keyval==GDK_KEY_Up || key->keyval==GDK_KEY_KP_Up) { - auto &history=entry_histories[get_placeholder_text()]; - if(history.size()>0) { - selected_history++; - if(selected_history>=history.size()) - selected_history=history.size()-1; - set_text(history[selected_history]); - set_position(-1); - } - } - if(key->keyval==GDK_KEY_Down || key->keyval==GDK_KEY_KP_Down) { - auto &history=entry_histories[get_placeholder_text()]; - if(history.size()>0) { - if(selected_history!=0) - selected_history--; - set_text(history[selected_history]); - set_position(-1); - } - } - return false; - }); +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]() { + if (this->on_activate) { + auto &history = EntryBox::entry_histories[get_placeholder_text()]; + auto text = get_text(); + if (history.size() == 0 || (history.size() > 0 && *history.begin() != text)) + history.emplace(history.begin(), text); + selected_history = 0; + this->on_activate(text); + } + }); + signal_key_press_event().connect([this](GdkEventKey *key) { + if (key->keyval == GDK_KEY_Up || key->keyval == GDK_KEY_KP_Up) { + auto &history = entry_histories[get_placeholder_text()]; + if (history.size() > 0) { + selected_history++; + if (selected_history >= history.size()) + selected_history = history.size() - 1; + set_text(history[selected_history]); + set_position(-1); + } + } + if (key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_KP_Down) { + auto &history = entry_histories[get_placeholder_text()]; + if (history.size() > 0) { + if (selected_history != 0) + selected_history--; + set_text(history[selected_history]); + set_position(-1); + } + } + return false; + }); } -EntryBox::Button::Button(const std::string& label, std::function on_activate) : Gtk::Button(label), on_activate(on_activate) { - set_focus_on_click(false); - signal_clicked().connect([this](){ - if(this->on_activate) - this->on_activate(); - }); +EntryBox::Button::Button(const std::string &label, std::function on_activate) : Gtk::Button(label), + on_activate(on_activate) { + set_focus_on_click(false); + signal_clicked().connect([this]() { + if (this->on_activate) + this->on_activate(); + }); } -EntryBox::ToggleButton::ToggleButton(const std::string& label, std::function on_activate) : Gtk::ToggleButton(label), on_activate(on_activate) { - set_focus_on_click(false); - signal_clicked().connect([this](){ - if(this->on_activate) - this->on_activate(); - }); +EntryBox::ToggleButton::ToggleButton(const std::string &label, std::function on_activate) : Gtk::ToggleButton( + label), on_activate(on_activate) { + set_focus_on_click(false); + signal_clicked().connect([this]() { + if (this->on_activate) + this->on_activate(); + }); } -EntryBox::Label::Label(std::function update) : Gtk::Label(), update(update) { - if(this->update) - this->update(-1, ""); +EntryBox::Label::Label(std::function update) + : Gtk::Label(), update(update) { + if (this->update) + this->update(-1, ""); } -EntryBox::EntryBox() : Gtk::Box(Gtk::ORIENTATION_VERTICAL), upper_box(Gtk::ORIENTATION_HORIZONTAL), lower_box(Gtk::ORIENTATION_HORIZONTAL) { - pack_start(upper_box, Gtk::PACK_SHRINK); - pack_start(lower_box, Gtk::PACK_SHRINK); - this->set_focus_chain({&lower_box}); +EntryBox::EntryBox() : Gtk::Box(Gtk::ORIENTATION_VERTICAL), upper_box(Gtk::ORIENTATION_HORIZONTAL), + lower_box(Gtk::ORIENTATION_HORIZONTAL) { + pack_start(upper_box, Gtk::PACK_SHRINK); + pack_start(lower_box, Gtk::PACK_SHRINK); + this->set_focus_chain({&lower_box}); } void EntryBox::clear() { - Gtk::Box::hide(); - entries.clear(); - buttons.clear(); - toggle_buttons.clear(); - labels.clear(); + Gtk::Box::hide(); + entries.clear(); + buttons.clear(); + toggle_buttons.clear(); + labels.clear(); } void EntryBox::show() { - std::vector focus_chain; - for(auto& entry: entries) { - lower_box.pack_start(entry, Gtk::PACK_SHRINK); - focus_chain.emplace_back(&entry); - } - for(auto& button: buttons) - lower_box.pack_start(button, Gtk::PACK_SHRINK); - for(auto& toggle_button: toggle_buttons) - lower_box.pack_start(toggle_button, Gtk::PACK_SHRINK); - for(auto& label: labels) - upper_box.pack_start(label, Gtk::PACK_SHRINK); - lower_box.set_focus_chain(focus_chain); - show_all(); - if(entries.size()>0) { - entries.begin()->grab_focus(); - entries.begin()->select_region(0, entries.begin()->get_text_length()); - } + std::vector focus_chain; + for (auto &entry: entries) { + lower_box.pack_start(entry, Gtk::PACK_SHRINK); + focus_chain.emplace_back(&entry); + } + for (auto &button: buttons) + lower_box.pack_start(button, Gtk::PACK_SHRINK); + for (auto &toggle_button: toggle_buttons) + lower_box.pack_start(toggle_button, Gtk::PACK_SHRINK); + for (auto &label: labels) + upper_box.pack_start(label, Gtk::PACK_SHRINK); + lower_box.set_focus_chain(focus_chain); + show_all(); + if (entries.size() > 0) { + entries.begin()->grab_focus(); + entries.begin()->select_region(0, entries.begin()->get_text_length()); + } } diff --git a/src/entrybox.h b/src/entrybox.h index 3a722be9..107ba085 100644 --- a/src/entrybox.h +++ b/src/entrybox.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include "gtkmm.h" @@ -8,47 +9,60 @@ class EntryBox : public Gtk::Box { public: - class Entry : public Gtk::Entry { - public: - Entry(const std::string& content="", std::function on_activate=nullptr, unsigned width_chars=-1); - std::function on_activate; - private: - size_t selected_history; - }; - class Button : public Gtk::Button { - public: - Button(const std::string& label, std::function on_activate=nullptr); - std::function on_activate; - }; - class ToggleButton : public Gtk::ToggleButton { - public: - ToggleButton(const std::string& label, std::function on_activate=nullptr); - std::function on_activate; - }; - class Label : public Gtk::Label { - public: - Label(std::function update=nullptr); - std::function update; - }; - + class Entry : public Gtk::Entry { + public: + Entry(const std::string &content = "", std::function on_activate = nullptr, + unsigned width_chars = -1); + + std::function on_activate; + private: + size_t selected_history; + }; + + class Button : public Gtk::Button { + public: + Button(const std::string &label, std::function on_activate = nullptr); + + std::function on_activate; + }; + + class ToggleButton : public Gtk::ToggleButton { + public: + ToggleButton(const std::string &label, std::function on_activate = nullptr); + + std::function on_activate; + }; + + class Label : public Gtk::Label { + public: + Label(std::function update = nullptr); + + std::function update; + }; + private: - EntryBox(); + EntryBox(); + public: - static EntryBox &get() { - static EntryBox singleton; - return singleton; - } - - Gtk::Box upper_box; - Gtk::Box lower_box; - void clear(); - void hide() {clear();} - void show(); - std::list entries; - std::list