diff --git a/docs/install.md b/docs/install.md index cf0744b..256527b 100644 --- a/docs/install.md +++ b/docs/install.md @@ -86,7 +86,7 @@ make install ``` ##Windows with MSYS2 (https://msys2.github.io/) -**MSYS2 does not yet support lldb, but you can still compile juCi++ without debug support. Also for the time being, MSYS2 must be installed in the default MSYS2 folder (C:\msys64 or C:\msys32).** +**MSYS2 does not yet support lldb, but you can still compile juCi++ without debug support.** Note that juCi++ must be run in a MinGW Shell (for instance MinGW-w64 Win64 Shell). diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 533baf8..07885da 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,21 +10,17 @@ if(APPLE) set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig") endif() -if(UNIX) #Checking if compiling on Ubuntu that has a buggy menu system +if(UNIX) #Checking if compiling on Ubuntu that for instance has a buggy menu system find_program(LSB_RELEASE_BIN lsb_release) if(LSB_RELEASE_BIN) execute_process(COMMAND ${LSB_RELEASE_BIN} -is OUTPUT_VARIABLE DISTRIBUTION OUTPUT_STRIP_TRAILING_WHITESPACE) if((DISTRIBUTION STREQUAL Ubuntu) OR (DISTRIBUTION STREQUAL LinuxMint)) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DJUCI_UBUNTU_BUGGED_MENU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DJUCI_UBUNTU") endif() endif() endif() -if(MSYS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSYS_PROCESS_USE_SH -DJUCI_CMAKE_INSTALL_PREFIX=\\\"${CMAKE_INSTALL_PREFIX}\\\"") -endif() - INCLUDE(FindPkgConfig) find_package(LibClang REQUIRED) @@ -84,6 +80,10 @@ set(source_files juci.h cmake.h cmake.cc dialogs.cc + project.h + project.cc + dispatcher.h + dispatcher.cc ../libclangmm/src/CodeCompleteResults.cc ../libclangmm/src/CompilationDatabase.cc @@ -103,7 +103,7 @@ set(source_files juci.h ../tiny-process-library/process.cpp) if(LIBLLDB_FOUND) - list(APPEND source_files debug.h debug.cc) + list(APPEND source_files debug_clang.h debug_clang.cc) endif() if(MSYS) diff --git a/src/cmake.cc b/src/cmake.cc index f5d1260..cff8f1b 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -36,7 +36,7 @@ CMake::CMake(const boost::filesystem::path &path) { } boost::filesystem::path CMake::get_default_build_path(const boost::filesystem::path &project_path) { - boost::filesystem::path default_build_path=Config::get().terminal.default_build_path; + boost::filesystem::path default_build_path=Config::get().project.default_build_path; const std::string path_variable_project_directory_name=""; size_t pos=0; @@ -56,7 +56,7 @@ boost::filesystem::path CMake::get_default_build_path(const boost::filesystem::p } boost::filesystem::path CMake::get_debug_build_path(const boost::filesystem::path &project_path) { - boost::filesystem::path debug_build_path=Config::get().terminal.debug_build_path; + boost::filesystem::path debug_build_path=Config::get().project.debug_build_path; const std::string path_variable_project_directory_name=""; size_t pos=0; @@ -72,7 +72,7 @@ boost::filesystem::path CMake::get_debug_build_path(const boost::filesystem::pat const std::string path_variable_default_build_path=""; pos=0; debug_build_path_string=debug_build_path.string(); - auto default_build_path=Config::get().terminal.default_build_path; + auto default_build_path=Config::get().project.default_build_path; while((pos=debug_build_path_string.find(path_variable_default_build_path, pos))!=std::string::npos) { debug_build_path_string.replace(pos, path_variable_default_build_path.size(), default_build_path); pos+=default_build_path.size(); @@ -112,7 +112,7 @@ bool CMake::create_default_build(const boost::filesystem::path &project_path, bo 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().terminal.cmake_command+" "+ + auto exit_status=Terminal::get().process(Config::get().project.cmake_command+" "+ filesystem::escape_argument(project_path)+" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", default_build_path); message.hide(); if(exit_status==EXIT_SUCCESS) { @@ -162,7 +162,7 @@ bool CMake::create_debug_build(const boost::filesystem::path &project_path) { std::unique_ptr message; message=std::unique_ptr(new Dialog::Message("Creating/updating debug build")); - auto exit_status=Terminal::get().process(Config::get().terminal.cmake_command+" "+ + auto exit_status=Terminal::get().process(Config::get().project.cmake_command+" "+ filesystem::escape_argument(project_path)+" -DCMAKE_BUILD_TYPE=Debug", debug_build_path); if(message) message->hide(); diff --git a/src/config.cc b/src/config.cc index 1d79e39..aa5c7c1 100644 --- a/src/config.cc +++ b/src/config.cc @@ -5,6 +5,7 @@ #include #include "filesystem.h" #include "terminal.h" +#include Config::Config() { std::vector environment_variables = {"JUCI_HOME", "HOME", "AppData"}; @@ -25,6 +26,17 @@ Config::Config() { searched_envs+="]"; throw std::runtime_error("One of these environment variables needs to point to a writable directory to save configuration: " + searched_envs); } + +#ifdef _WIN32 + auto env_WD=std::getenv("WD"); + auto env_MSYSTEM=std::getenv("MSYSTEM"); + if(env_WD!=NULL && env_MSYSTEM!=NULL) { + terminal.msys2_mingw_path=boost::filesystem::path(env_WD).parent_path().parent_path().parent_path(); + std::string msystem=env_MSYSTEM; + std::transform(msystem.begin(), msystem.end(), msystem.begin(), ::tolower); + terminal.msys2_mingw_path/=msystem; + } +#endif } void Config::load() { @@ -84,15 +96,15 @@ void Config::retrieve_config() { window.theme_variant=cfg.get("gtk_theme.variant"); window.version = cfg.get("version"); window.default_size = {cfg.get("default_window_size.width"), cfg.get("default_window_size.height")}; - window.save_on_compile_or_run=cfg.get("project.save_on_compile_or_run"); - terminal.default_build_path=cfg.get("project.default_build_path"); - terminal.debug_build_path=cfg.get("project.debug_build_path"); - terminal.make_command=cfg.get("project.make_command"); - terminal.cmake_command=cfg.get("project.cmake_command"); - terminal.history_size=cfg.get("terminal_history_size"); + project.save_on_compile_or_run=cfg.get("project.save_on_compile_or_run"); + project.default_build_path=cfg.get("project.default_build_path"); + project.debug_build_path=cfg.get("project.debug_build_path"); + project.make_command=cfg.get("project.make_command"); + project.cmake_command=cfg.get("project.cmake_command"); - terminal.clang_format_command=cfg.get("project.clang_format_command", "clang-format"); + terminal.history_size=cfg.get("terminal_history_size"); + terminal.clang_format_command="clang-format"; #ifdef __linux if(terminal.clang_format_command=="clang-format" && !boost::filesystem::exists("/usr/bin/clang-format") && !boost::filesystem::exists("/usr/local/bin/clang-format")) { diff --git a/src/config.h b/src/config.h index 7e6069b..1add042 100644 --- a/src/config.h +++ b/src/config.h @@ -20,17 +20,25 @@ public: std::string theme_variant; std::string version; std::pair default_size; - bool save_on_compile_or_run; }; class Terminal { + public: + std::string clang_format_command; + int history_size; + +#ifdef _WIN32 + boost::filesystem::path msys2_mingw_path; +#endif + }; + + class Project { public: std::string default_build_path; std::string debug_build_path; std::string cmake_command; std::string make_command; - std::string clang_format_command; - int history_size; + bool save_on_compile_or_run; }; class Source { @@ -74,6 +82,7 @@ public: Menu menu; Window window; Terminal terminal; + Project project; Source source; const boost::filesystem::path& juci_home_path() const { return home; } diff --git a/src/debug.h b/src/debug.h deleted file mode 100644 index 07fd4d2..0000000 --- a/src/debug.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef JUCI_DEBUG_H_ -#define JUCI_DEBUG_H_ - -#include -#include -#include -#include -#include -#include -#include - -class Debug { -public: - class Frame { - public: - uint32_t index; - std::string module_filename; - std::string 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; - boost::filesystem::path file_path; - int line_nr; - int line_index; - }; -private: - Debug(); -public: - static Debug &get() { - static Debug singleton; - return singleton; - } - - void start(const std::string &command, const boost::filesystem::path &path="", - std::shared_ptr > > breakpoints=nullptr, - std::function callback=nullptr, - std::function status_callback=nullptr, - std::function stop_callback=nullptr); - 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 delete_debug(); //can't use delete as function name - - 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::unique_ptr debugger; - std::unique_ptr listener; - std::unique_ptr process; - std::thread debug_thread; - - lldb::StateType state; - std::mutex event_mutex; - - size_t buffer_size; -}; - -#endif diff --git a/src/debug.cc b/src/debug_clang.cc similarity index 89% rename from src/debug.cc rename to src/debug_clang.cc index ed27d14..54f0d45 100644 --- a/src/debug.cc +++ b/src/debug_clang.cc @@ -1,4 +1,4 @@ -#include "debug.h" +#include "debug_clang.h" #include #ifdef __APPLE__ #include @@ -27,7 +27,7 @@ void log(const char *msg, void *) { std::cout << "debugger log: " << msg << std::endl; } -Debug::Debug(): state(lldb::StateType::eStateInvalid), buffer_size(131072) { +Debug::Clang::Clang(): state(lldb::StateType::eStateInvalid), buffer_size(131072) { #ifdef __APPLE__ auto debugserver_path=boost::filesystem::path("/usr/local/opt/llvm/bin/debugserver"); if(boost::filesystem::exists(debugserver_path)) @@ -35,8 +35,8 @@ Debug::Debug(): state(lldb::StateType::eStateInvalid), buffer_size(131072) { #endif } -void Debug::start(const std::string &command, const boost::filesystem::path &path, - std::shared_ptr > > breakpoints, +void Debug::Clang::start(const std::string &command, const boost::filesystem::path &path, + const std::vector > &breakpoints, std::function callback, std::function status_callback, std::function stop_callback) { @@ -88,14 +88,12 @@ void Debug::start(const std::string &command, const boost::filesystem::path &pat } //Set breakpoints - if(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); - if(callback) - callback(-1); - return; - } + 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); + if(callback) + callback(-1); + return; } } @@ -216,14 +214,14 @@ void Debug::start(const std::string &command, const boost::filesystem::path &pat }); } -void Debug::continue_debug() { +void Debug::Clang::continue_debug() { event_mutex.lock(); if(state==lldb::StateType::eStateStopped) process->Continue(); event_mutex.unlock(); } -void Debug::stop() { +void Debug::Clang::stop() { event_mutex.lock(); if(state==lldb::StateType::eStateRunning) { auto error=process->Stop(); @@ -233,7 +231,7 @@ void Debug::stop() { event_mutex.unlock(); } -void Debug::kill() { +void Debug::Clang::kill() { event_mutex.lock(); if(process) { auto error=process->Kill(); @@ -243,7 +241,7 @@ void Debug::kill() { event_mutex.unlock(); } -void Debug::step_over() { +void Debug::Clang::step_over() { event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { process->GetSelectedThread().StepOver(); @@ -251,7 +249,7 @@ void Debug::step_over() { event_mutex.unlock(); } -void Debug::step_into() { +void Debug::Clang::step_into() { event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { process->GetSelectedThread().StepInto(); @@ -259,7 +257,7 @@ void Debug::step_into() { event_mutex.unlock(); } -void Debug::step_out() { +void Debug::Clang::step_out() { event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { process->GetSelectedThread().StepOut(); @@ -267,7 +265,7 @@ void Debug::step_out() { event_mutex.unlock(); } -std::pair Debug::run_command(const std::string &command) { +std::pair Debug::Clang::run_command(const std::string &command) { std::pair command_return; event_mutex.lock(); if(state==lldb::StateType::eStateStopped || state==lldb::StateType::eStateRunning) { @@ -280,7 +278,7 @@ std::pair Debug::run_command(const std::string &comman return command_return; } -std::vector Debug::get_backtrace() { +std::vector Debug::Clang::get_backtrace() { std::vector backtrace; event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { @@ -317,8 +315,8 @@ std::vector Debug::get_backtrace() { return backtrace; } -std::vector Debug::get_variables() { - std::vector variables; +std::vector Debug::Clang::get_variables() { + std::vector variables; event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { for(uint32_t c_t=0;c_tGetNumThreads();c_t++) { @@ -332,7 +330,7 @@ std::vector Debug::get_variables() { auto declaration=value.GetDeclaration(); if(declaration.IsValid()) { - Debug::Variable variable; + Debug::Clang::Variable variable; variable.thread_index_id=thread.GetIndexID(); variable.frame_index=c_f; @@ -361,7 +359,7 @@ std::vector Debug::get_variables() { return variables; } -void Debug::select_frame(uint32_t frame_index, uint32_t thread_index_id) { +void Debug::Clang::select_frame(uint32_t frame_index, uint32_t thread_index_id) { event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { if(thread_index_id!=0) @@ -371,13 +369,13 @@ void Debug::select_frame(uint32_t frame_index, uint32_t thread_index_id) { event_mutex.unlock(); } -void Debug::delete_debug() { +void Debug::Clang::delete_debug() { kill(); if(debug_thread.joinable()) debug_thread.join(); } -std::string Debug::get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { +std::string Debug::Clang::get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { std::string variable_value; event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { @@ -419,7 +417,7 @@ std::string Debug::get_value(const std::string &variable, const boost::filesyste return variable_value; } -std::string Debug::get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { +std::string Debug::Clang::get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { std::string return_value; event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { @@ -443,7 +441,7 @@ std::string Debug::get_return_value(const boost::filesystem::path &file_path, un return return_value; } -bool Debug::is_invalid() { +bool Debug::Clang::is_invalid() { bool invalid; event_mutex.lock(); invalid=state==lldb::StateType::eStateInvalid; @@ -451,7 +449,7 @@ bool Debug::is_invalid() { return invalid; } -bool Debug::is_stopped() { +bool Debug::Clang::is_stopped() { bool stopped; event_mutex.lock(); stopped=state==lldb::StateType::eStateStopped; @@ -459,7 +457,7 @@ bool Debug::is_stopped() { return stopped; } -bool Debug::is_running() { +bool Debug::Clang::is_running() { bool running; event_mutex.lock(); running=state==lldb::StateType::eStateRunning; @@ -467,7 +465,7 @@ bool Debug::is_running() { return running; } -void Debug::add_breakpoint(const boost::filesystem::path &file_path, int line_nr) { +void Debug::Clang::add_breakpoint(const boost::filesystem::path &file_path, int line_nr) { event_mutex.lock(); if(state==lldb::eStateStopped || state==lldb::eStateRunning) { if(!(process->GetTarget().BreakpointCreateByLocation(file_path.string().c_str(), line_nr)).IsValid()) @@ -476,7 +474,7 @@ void Debug::add_breakpoint(const boost::filesystem::path &file_path, int line_nr event_mutex.unlock(); } -void Debug::remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) { +void Debug::Clang::remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) { event_mutex.lock(); if(state==lldb::eStateStopped || state==lldb::eStateRunning) { auto target=process->GetTarget(); @@ -503,7 +501,7 @@ void Debug::remove_breakpoint(const boost::filesystem::path &file_path, int line event_mutex.unlock(); } -void Debug::write(const std::string &buffer) { +void Debug::Clang::write(const std::string &buffer) { event_mutex.lock(); if(state==lldb::StateType::eStateRunning) { process->PutSTDIN(buffer.c_str(), buffer.size()); diff --git a/src/debug_clang.h b/src/debug_clang.h new file mode 100644 index 0000000..0e9001a --- /dev/null +++ b/src/debug_clang.h @@ -0,0 +1,85 @@ +#ifndef JUCI_DEBUG_CLANG_H_ +#define JUCI_DEBUG_CLANG_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Debug { + class Clang { + public: + class Frame { + public: + uint32_t index; + std::string module_filename; + std::string 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; + boost::filesystem::path file_path; + int line_nr; + int line_index; + }; + private: + Clang(); + public: + static Clang &get() { + static Clang singleton; + return singleton; + } + + void start(const std::string &command, const boost::filesystem::path &path="", + const std::vector > &breakpoints={}, + std::function callback=nullptr, + std::function status_callback=nullptr, + std::function stop_callback=nullptr); + 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 delete_debug(); //can't use delete as function name + + 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::unique_ptr debugger; + std::unique_ptr listener; + std::unique_ptr process; + std::thread debug_thread; + + lldb::StateType state; + std::mutex event_mutex; + + size_t buffer_size; + }; +} + +#endif diff --git a/src/dialogs.cc b/src/dialogs.cc index a62c9ac..32d234b 100644 --- a/src/dialogs.cc +++ b/src/dialogs.cc @@ -1,5 +1,6 @@ #include "dialogs.h" #include "window.h" +#include "notebook.h" #include namespace sigc { @@ -34,7 +35,7 @@ std::string Dialog::gtk_dialog(const std::string &title, dialog.set_transient_for(Window::get()); - auto current_path=Window::get().notebook.get_current_folder(); + auto current_path=Notebook::get().get_current_folder(); boost::system::error_code ec; if(current_path.empty()) current_path=boost::filesystem::current_path(ec); diff --git a/src/directories.cc b/src/directories.cc index 7301030..be1d779 100644 --- a/src/directories.cc +++ b/src/directories.cc @@ -22,6 +22,8 @@ namespace sigc { } Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { + this->set_enable_tree_lines(true); + tree_store = Gtk::TreeStore::create(column_record); set_model(tree_store); append_column("", column_record.name); @@ -77,17 +79,7 @@ Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { child->set_value(column_record.color, rgba); } }); - - update_dispatcher.connect([this](){ - update_mutex.lock(); - for(auto &path: update_paths) { - if(last_write_times.count(path)>0) - add_path(path, last_write_times.at(path).first); - } - update_paths.clear(); - update_mutex.unlock(); - }); - + update_thread=std::thread([this](){ while(!stop_update_thread) { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); @@ -105,8 +97,17 @@ Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { else it=last_write_times.erase(it); } - if(update_paths.size()>0) - update_dispatcher(); + if(update_paths.size()>0) { + dispatcher.push([this] { + update_mutex.lock(); + for(auto &path: update_paths) { + if(last_write_times.count(path)>0) + add_path(path, last_write_times.at(path).first); + } + update_paths.clear(); + update_mutex.unlock(); + }); + } } update_mutex.unlock(); } @@ -116,6 +117,7 @@ Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) { Directories::~Directories() { stop_update_thread=true; update_thread.join(); + dispatcher.disconnect(); } void Directories::open(const boost::filesystem::path& dir_path) { diff --git a/src/directories.h b/src/directories.h index 9314605..5e5e374 100644 --- a/src/directories.h +++ b/src/directories.h @@ -9,6 +9,7 @@ #include #include #include +#include "dispatcher.h" class Directories : public Gtk::TreeView { public: @@ -51,7 +52,7 @@ private: std::mutex update_mutex; std::thread update_thread; std::atomic stop_update_thread; - Glib::Dispatcher update_dispatcher; + Dispatcher dispatcher; std::vector update_paths; }; diff --git a/src/dispatcher.cc b/src/dispatcher.cc new file mode 100644 index 0000000..bc862f2 --- /dev/null +++ b/src/dispatcher.cc @@ -0,0 +1,30 @@ +#include "dispatcher.h" + +Dispatcher::Dispatcher() { + connection=dispatcher.connect([this] { + functions_mutex.lock(); + for(auto &function: functions) { + function(); + } + functions.clear(); + functions_mutex.unlock(); + }); +} + +Dispatcher::~Dispatcher() { + disconnect(); + functions_mutex.lock(); + functions.clear(); + functions_mutex.unlock(); +} + +void Dispatcher::push(std::function &&function) { + functions_mutex.lock(); + functions.emplace_back(function); + functions_mutex.unlock(); + dispatcher(); +} + +void Dispatcher::disconnect() { + connection.disconnect(); +} diff --git a/src/dispatcher.h b/src/dispatcher.h new file mode 100644 index 0000000..5a326e4 --- /dev/null +++ b/src/dispatcher.h @@ -0,0 +1,20 @@ +#ifndef DISPATCHER_H_ +#define DISPATCHER_H_ +#include +#include +#include + +class Dispatcher { +private: + std::vector> functions; + std::mutex functions_mutex; + Glib::Dispatcher dispatcher; + sigc::connection connection; +public: + Dispatcher(); + ~Dispatcher(); + void push(std::function &&function); + void disconnect(); +}; + +#endif /* DISPATCHER_H_ */ diff --git a/src/files.h b/src/files.h index 75941a1..1a7debb 100644 --- a/src/files.h +++ b/src/files.h @@ -125,7 +125,7 @@ const std::string configjson = " \"debug_build_path_comment\": \"Use to insert the project top level directory name, and to insert your default_build_path setting.\",\n" " \"debug_build_path\": \"/debug\",\n" #ifdef _WIN32 -" \"cmake_command\": \"cmake -G\\\"MSYS Makefiles\\\" -DCMAKE_INSTALL_PREFIX="+JUCI_CMAKE_INSTALL_PREFIX+"\",\n" +" \"cmake_command\": \"cmake -G\\\"MSYS Makefiles\\\"\",\n" #else " \"cmake_command\": \"cmake\",\n" #endif diff --git a/src/juci.cc b/src/juci.cc index 3c52331..14e95a1 100644 --- a/src/juci.cc +++ b/src/juci.cc @@ -1,5 +1,6 @@ #include "juci.h" #include "window.h" +#include "notebook.h" #include "directories.h" #include "menu.h" #include "config.h" @@ -68,7 +69,7 @@ void Application::on_activate() { } for(auto &file: files) - Window::get().notebook.open(file); + Notebook::get().open(file); for(auto &error: errors) Terminal::get().print(error, true); @@ -79,16 +80,12 @@ void Application::on_startup() { Menu::get().build(); - auto object = Menu::get().builder->get_object("juci-menu"); - auto juci_menu = Glib::RefPtr::cast_dynamic(object); - object = Menu::get().builder->get_object("window-menu"); - auto window_menu = Glib::RefPtr::cast_dynamic(object); - if (!juci_menu || !window_menu) { + if (!Menu::get().juci_menu || !Menu::get().window_menu) { std::cerr << "Menu not found." << std::endl; } else { - set_app_menu(juci_menu); - set_menubar(window_menu); + set_app_menu(Menu::get().juci_menu); + set_menubar(Menu::get().window_menu); } } diff --git a/src/menu.cc b/src/menu.cc index 3627947..bc00538 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -7,7 +7,7 @@ Menu::Menu() { auto accels=Config::get().menu.keys; for(auto &accel: accels) { -#ifdef JUCI_UBUNTU_BUGGED_MENU +#ifdef JUCI_UBUNTU size_t pos=0; std::string second=accel.second; while((pos=second.find('<', pos))!=std::string::npos) { @@ -404,6 +404,10 @@ void Menu::build() { try { builder->add_from_string(ui_xml); + auto object = Menu::get().builder->get_object("juci-menu"); + juci_menu = Glib::RefPtr::cast_dynamic(object); + object = Menu::get().builder->get_object("window-menu"); + window_menu = Glib::RefPtr::cast_dynamic(object); } catch (const Glib::Error &ex) { std::cerr << "building menu failed: " << ex.what(); diff --git a/src/menu.h b/src/menu.h index 1018eeb..9e3fc56 100644 --- a/src/menu.h +++ b/src/menu.h @@ -19,8 +19,12 @@ public: void set_keys(); void build(); - Glib::RefPtr builder; + Glib::RefPtr juci_menu; + Glib::RefPtr window_menu; + +private: + Glib::RefPtr builder; std::string ui_xml; }; #endif // JUCI_MENU_H_ diff --git a/src/notebook.cc b/src/notebook.cc index 9e3b73f..f973961 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -27,31 +27,31 @@ namespace sigc { Notebook::TabLabel::TabLabel(const std::string &title) : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) { set_can_focus(false); - label.set_text(title); + label.set_text(title+' '); label.set_can_focus(false); button.set_image_from_icon_name("window-close-symbolic", Gtk::ICON_SIZE_MENU); button.set_can_focus(false); button.set_relief(Gtk::ReliefStyle::RELIEF_NONE); + //Based on http://www.micahcarrick.com/gtk-notebook-tabs-with-close-button.html - std::string data = ".button {\n" - "-GtkButton-default-border : 0px;\n" - "-GtkButton-default-outside-border : 0px;\n" - "-GtkButton-inner-border: 0px;\n" - "-GtkWidget-focus-line-width : 0px;\n" - "-GtkWidget-focus-padding : 0px;\n" - "padding: 0px;\n" - "}"; auto provider = Gtk::CssProvider::create(); - provider->load_from_data(data); + provider->load_from_data(".button {border: 0px; outline-width: 0px; margin: 0px; padding: 0px;}"); button.get_style_context()->add_provider(provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + pack_start(label, Gtk::PACK_SHRINK); pack_end(button, Gtk::PACK_SHRINK); + show_all(); } Notebook::Notebook() : Gtk::Notebook(), last_index(-1) { Gsv::init(); + auto provider = Gtk::CssProvider::create(); + provider->load_from_data(".notebook {padding: 0px; -GtkNotebook-tab-overlap: 0px;} .notebook tab {border-radius: 5px;padding: 4px;}"); + get_style_context()->add_provider(provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + get_style_context()->set_junction_sides(Gtk::JunctionSides::JUNCTION_BOTTOM); + signal_switch_page().connect([this](Gtk::Widget* page, guint page_num) { last_index=-1; }); @@ -166,7 +166,9 @@ void Notebook::open(const boost::filesystem::path &file_path) { get_current_view()->get_buffer()->signal_modified_changed().connect([this, source_view]() { std::string title=source_view->file_path.filename().string(); if(source_view->get_buffer()->get_modified()) - title+="*"; + title+='*'; + else + title+=' '; int page=-1; for(int c=0;c +#include "menu.h" +#include "notebook.h" +#ifdef JUCI_ENABLE_DEBUG +#include "debug_clang.h" +#endif + +std::unordered_map Project::run_arguments; +std::unordered_map Project::debug_run_arguments; +std::atomic Project::compiling; +std::atomic Project::debugging; +std::pair > Project::debug_stop; +boost::filesystem::path Project::debug_last_stop_file_path; + +std::unique_ptr Project::current_language; + +void Project::debug_update_status(const std::string &debug_status) { + if(debug_status.empty()) + debug_status_label().set_text(""); + else + debug_status_label().set_text("debug: "+debug_status); + auto &menu=Menu::get(); + menu.actions["debug_stop"]->set_enabled(!debug_status.empty()); + menu.actions["debug_kill"]->set_enabled(!debug_status.empty()); + menu.actions["debug_step_over"]->set_enabled(!debug_status.empty()); + menu.actions["debug_step_into"]->set_enabled(!debug_status.empty()); + menu.actions["debug_step_out"]->set_enabled(!debug_status.empty()); + menu.actions["debug_backtrace"]->set_enabled(!debug_status.empty()); + menu.actions["debug_show_variables"]->set_enabled(!debug_status.empty()); + menu.actions["debug_run_command"]->set_enabled(!debug_status.empty()); + menu.actions["debug_goto_stop"]->set_enabled(!debug_status.empty()); +} + +void Project::debug_update_stop() { + for(int c=0;cfile_path==debug_last_stop_file_path) { + view->get_source_buffer()->remove_source_marks(view->get_buffer()->begin(), view->get_buffer()->end(), "debug_stop"); + break; + } + } + //Add debug stop source mark + for(int c=0;cfile_path==debug_stop.first) { + if(debug_stop.second.first-1get_buffer()->get_line_count()) { + view->get_source_buffer()->create_source_mark("debug_stop", view->get_buffer()->get_iter_at_line(debug_stop.second.first-1)); + debug_last_stop_file_path=debug_stop.first; + } + break; + } + } + if(Notebook::get().get_current_page()!=-1) + Notebook::get().get_current_view()->get_buffer()->place_cursor(Notebook::get().get_current_view()->get_buffer()->get_insert()->get_iter()); +} + +std::unique_ptr Project::get_language() { + if(Notebook::get().get_current_page()!=-1) { + auto language_id=Notebook::get().get_current_view()->language->get_id(); + if(language_id=="markdown") + return std::unique_ptr(new Project::Markdown()); + if(language_id=="python") + return std::unique_ptr(new Project::Python()); + if(language_id=="js") + return std::unique_ptr(new Project::JavaScript()); + if(language_id=="html") + return std::unique_ptr(new Project::HTML()); + } + + return std::unique_ptr(new Project::Clang()); +} + +std::unique_ptr Project::Clang::get_cmake() { + boost::filesystem::path path; + if(Notebook::get().get_current_page()!=-1) + path=Notebook::get().get_current_view()->file_path.parent_path(); + else + path=Directories::get().current_path; + if(path.empty()) + return nullptr; + auto cmake=std::unique_ptr(new CMake(path)); + if(cmake->project_path.empty()) + return nullptr; + if(!CMake::create_default_build(cmake->project_path)) + return nullptr; + return cmake; +} + +std::pair Project::Clang::get_run_arguments() { + auto cmake=get_cmake(); + if(!cmake) + return {"", ""}; + + auto project_path=cmake->project_path.string(); + auto run_arguments_it=run_arguments.find(project_path); + std::string arguments; + if(run_arguments_it!=run_arguments.end()) + arguments=run_arguments_it->second; + + if(arguments.empty()) { + auto executable=cmake->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); + + if(executable!="") { + auto project_path=cmake->project_path; + auto build_path=CMake::get_default_build_path(project_path); + if(!build_path.empty()) { + size_t pos=executable.find(project_path.string()); + if(pos!=std::string::npos) + executable.replace(pos, project_path.string().size(), build_path.string()); + } + arguments=filesystem::escape_argument(executable); + } + else + arguments=filesystem::escape_argument(CMake::get_default_build_path(cmake->project_path)); + } + + return {project_path, arguments}; +} + +void Project::Clang::compile() { + auto cmake=get_cmake(); + if(!cmake) + return; + + auto default_build_path=CMake::get_default_build_path(cmake->project_path); + if(default_build_path.empty()) + return; + compiling=true; + Terminal::get().print("Compiling project "+cmake->project_path.string()+"\n"); + Terminal::get().async_process(Config::get().project.make_command, default_build_path, [this](int exit_status) { + compiling=false; + }); +} + +void Project::Clang::compile_and_run() { + auto cmake=get_cmake(); + if(!cmake) + return; + auto project_path=cmake->project_path; + + auto default_build_path=CMake::get_default_build_path(project_path); + if(default_build_path.empty()) + return; + + auto run_arguments_it=run_arguments.find(project_path.string()); + std::string arguments; + if(run_arguments_it!=run_arguments.end()) + arguments=run_arguments_it->second; + + if(arguments.empty()) { + arguments=cmake->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); + if(arguments.empty()) { + Terminal::get().print("Could not find add_executable in the following paths:\n"); + for(auto &path: cmake->paths) + Terminal::get().print(" "+path.string()+"\n"); + Terminal::get().print("Solution: either use Project Set Run Arguments, or open a source file within a directory where add_executable is set.\n", true); + return; + } + size_t pos=arguments.find(project_path.string()); + if(pos!=std::string::npos) + arguments.replace(pos, project_path.string().size(), default_build_path.string()); + arguments=filesystem::escape_argument(arguments); + } + + compiling=true; + Terminal::get().print("Compiling and running "+arguments+"\n"); + Terminal::get().async_process(Config::get().project.make_command, default_build_path, [this, arguments, default_build_path](int exit_status){ + compiling=false; + if(exit_status==EXIT_SUCCESS) { + Terminal::get().async_process(arguments, default_build_path, [this, arguments](int exit_status){ + Terminal::get().async_print(arguments+" returned: "+std::to_string(exit_status)+'\n'); + }); + } + }); +} + +#ifdef JUCI_ENABLE_DEBUG +std::pair Project::Clang::debug_get_run_arguments() { + auto cmake=get_cmake(); + if(!cmake) + return {"", ""}; + + auto project_path=cmake->project_path.string(); + auto run_arguments_it=debug_run_arguments.find(project_path); + std::string arguments; + if(run_arguments_it!=debug_run_arguments.end()) + arguments=run_arguments_it->second; + + if(arguments.empty()) { + auto executable=cmake->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); + + if(executable!="") { + auto project_path=cmake->project_path; + auto build_path=CMake::get_debug_build_path(project_path); + if(!build_path.empty()) { + size_t pos=executable.find(project_path.string()); + if(pos!=std::string::npos) + executable.replace(pos, project_path.string().size(), build_path.string()); + } + arguments=filesystem::escape_argument(executable); + } + else + arguments=filesystem::escape_argument(CMake::get_debug_build_path(cmake->project_path)); + } + + return {project_path, arguments}; +} + +void Project::Clang::debug_start() { + auto cmake=get_cmake(); + if(!cmake) + return; + auto project_path=cmake->project_path; + + auto debug_build_path=CMake::get_debug_build_path(project_path); + if(debug_build_path.empty()) + return; + if(!CMake::create_debug_build(project_path)) + return; + + auto run_arguments_it=debug_run_arguments.find(project_path.string()); + std::string run_arguments; + if(run_arguments_it!=debug_run_arguments.end()) + run_arguments=run_arguments_it->second; + + if(run_arguments.empty()) { + run_arguments=cmake->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string(); + if(run_arguments.empty()) { + Terminal::get().print("Could not find add_executable in the following paths:\n"); + for(auto &path: cmake->paths) + Terminal::get().print(" "+path.string()+"\n"); + Terminal::get().print("Solution: either use Debug Set Run Arguments, or open a source file within a directory where add_executable is set.\n", true); + return; + } + size_t pos=run_arguments.find(project_path.string()); + if(pos!=std::string::npos) + run_arguments.replace(pos, project_path.string().size(), debug_build_path.string()); + run_arguments=filesystem::escape_argument(run_arguments); + } + + auto breakpoints=std::make_shared > >(); + for(int c=0;cproject_path) { + auto iter=view->get_buffer()->begin(); + if(view->get_source_buffer()->get_source_marks_at_iter(iter, "debug_breakpoint").size()>0) + breakpoints->emplace_back(view->file_path, iter.get_line()+1); + while(view->get_source_buffer()->forward_iter_to_source_mark(iter, "debug_breakpoint")) + breakpoints->emplace_back(view->file_path, iter.get_line()+1); + } + } + + debugging=true; + Terminal::get().print("Compiling and debugging "+run_arguments+"\n"); + Terminal::get().async_process(Config::get().project.make_command, debug_build_path, [this, breakpoints, run_arguments, debug_build_path](int exit_status){ + if(exit_status!=EXIT_SUCCESS) + debugging=false; + else { + debug_start_mutex.lock(); + Debug::Clang::get().start(run_arguments, debug_build_path, *breakpoints, [this, run_arguments](int exit_status){ + debugging=false; + Terminal::get().async_print(run_arguments+" returned: "+std::to_string(exit_status)+'\n'); + }, [this](const std::string &status) { + dispatcher.push([this, status] { + debug_update_status(status); + }); + }, [this](const boost::filesystem::path &file_path, int line_nr, int line_index) { + dispatcher.push([this, file_path, line_nr, line_index] { + Project::debug_stop.first=file_path; + Project::debug_stop.second.first=line_nr; + Project::debug_stop.second.second=line_index; + + debug_update_stop(); + }); + }); + debug_start_mutex.unlock(); + } + }); +} + +void Project::Clang::debug_continue() { + Debug::Clang::get().continue_debug(); +} + +void Project::Clang::debug_stop() { + if(debugging) + Debug::Clang::get().stop(); +} + +void Project::Clang::debug_kill() { + if(debugging) + Debug::Clang::get().kill(); +} + +void Project::Clang::debug_step_over() { + if(debugging) + Debug::Clang::get().step_over(); +} + +void Project::Clang::debug_step_into() { + if(debugging) + Debug::Clang::get().step_into(); +} + +void Project::Clang::debug_step_out() { + if(debugging) + Debug::Clang::get().step_out(); +} + +void Project::Clang::debug_backtrace() { + if(debugging && Notebook::get().get_current_page()!=-1) { + auto backtrace=Debug::Clang::get().get_backtrace(); + + auto view=Notebook::get().get_current_view(); + auto iter=view->get_iter_for_dialog(); + view->selection_dialog=std::unique_ptr(new SelectionDialog(*view, view->get_buffer()->create_mark(iter), true, true)); + auto rows=std::make_shared >(); + if(backtrace.size()==0) + return; + + for(auto &frame: backtrace) { + std::string row=""+frame.module_filename+""; + + //Shorten frame.function_name if it is too long + if(frame.function_name.size()>120) { + frame.function_name=frame.function_name.substr(0, 58)+"...."+frame.function_name.substr(frame.function_name.size()-58); + } + if(frame.file_path.empty()) + row+=" - "+Glib::Markup::escape_text(frame.function_name); + else { + auto file_path=boost::filesystem::path(frame.file_path).filename().string(); + row+=":"+Glib::Markup::escape_text(file_path)+":"+std::to_string(frame.line_nr)+" - "+Glib::Markup::escape_text(frame.function_name); + } + (*rows)[row]=frame; + view->selection_dialog->add_row(row); + } + + view->selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { + auto frame=rows->at(selected); + if(!frame.file_path.empty()) { + Notebook::get().open(frame.file_path); + if(Notebook::get().get_current_page()!=-1) { + auto view=Notebook::get().get_current_view(); + + Debug::Clang::get().select_frame(frame.index); + + view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(frame.line_nr-1, frame.line_index-1)); + + while(g_main_context_pending(NULL)) + g_main_context_iteration(NULL, false); + if(Notebook::get().get_current_page()!=-1 && Notebook::get().get_current_view()==view) + view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); + } + } + }; + view->selection_dialog->show(); + } +} + +void Project::Clang::debug_show_variables() { + if(debugging && Notebook::get().get_current_page()!=-1) { + auto variables=Debug::Clang::get().get_variables(); + + auto view=Notebook::get().get_current_view(); + auto iter=view->get_iter_for_dialog(); + view->selection_dialog=std::unique_ptr(new SelectionDialog(*view, view->get_buffer()->create_mark(iter), true, true)); + auto rows=std::make_shared >(); + if(variables.size()==0) + return; + + for(auto &variable: variables) { + std::string row="#"+std::to_string(variable.thread_index_id)+":#"+std::to_string(variable.frame_index)+":"+variable.file_path.filename().string()+":"+std::to_string(variable.line_nr)+" - "+Glib::Markup::escape_text(variable.name)+""; + + (*rows)[row]=variable; + view->selection_dialog->add_row(row); + } + + view->selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { + auto variable=rows->at(selected); + if(!variable.file_path.empty()) { + Notebook::get().open(variable.file_path); + if(Notebook::get().get_current_page()!=-1) { + auto view=Notebook::get().get_current_view(); + + Debug::Clang::get().select_frame(variable.frame_index, variable.thread_index_id); + + view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(variable.line_nr-1, variable.line_index-1)); + + while(g_main_context_pending(NULL)) + g_main_context_iteration(NULL, false); + if(Notebook::get().get_current_page()!=-1 && Notebook::get().get_current_view()==view) + view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); + } + } + }; + + view->selection_dialog->on_hide=[this]() { + debug_variable_tooltips.hide(); + debug_variable_tooltips.clear(); + }; + + view->selection_dialog->on_changed=[this, rows, iter](const std::string &selected) { + if(selected.empty()) { + debug_variable_tooltips.hide(); + return; + } + if(Notebook::get().get_current_page()!=-1) { + auto view=Notebook::get().get_current_view(); + debug_variable_tooltips.clear(); + auto create_tooltip_buffer=[this, rows, view, selected]() { + auto variable=rows->at(selected); + auto tooltip_buffer=Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()); + + Glib::ustring value=variable.value; + if(!value.empty()) { + Glib::ustring::iterator iter; + while(!value.validate(iter)) { + auto next_char_iter=iter; + next_char_iter++; + value.replace(iter, next_char_iter, "?"); + } + tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), value.substr(0, value.size()-1), "def:note"); + } + + return tooltip_buffer; + }; + + debug_variable_tooltips.emplace_back(create_tooltip_buffer, *view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter)); + + debug_variable_tooltips.show(true); + } + }; + + view->selection_dialog->show(); + } +} + +void Project::Clang::debug_run_command(const std::string &command) { + if(debugging) { + auto command_return=Debug::Clang::get().run_command(command); + Terminal::get().async_print(command_return.first); + Terminal::get().async_print(command_return.second, true); + } +} + +void Project::Clang::debug_add_breakpoint(const boost::filesystem::path &file_path, int line_nr) { + Debug::Clang::get().add_breakpoint(file_path, line_nr); +} + +void Project::Clang::debug_remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) { + Debug::Clang::get().remove_breakpoint(file_path, line_nr, line_count); +} + +bool Project::Clang::debug_is_running() { + return Debug::Clang::get().is_running(); +} + +void Project::Clang::debug_write(const std::string &buffer) { + Debug::Clang::get().write(buffer); +} + +void Project::Clang::debug_delete() { + debug_start_mutex.lock(); + Debug::Clang::get().delete_debug(); + debug_start_mutex.unlock(); +} +#endif + +Project::Markdown::~Markdown() { + if(!last_temp_path.empty()) { + boost::filesystem::remove(last_temp_path); + last_temp_path=boost::filesystem::path(); + } +} + +void Project::Markdown::compile_and_run() { + if(!last_temp_path.empty()) { + boost::filesystem::remove(last_temp_path); + last_temp_path=boost::filesystem::path(); + } + + std::stringstream stdin_stream, stdout_stream; + auto exit_status=Terminal::get().process(stdin_stream, stdout_stream, "markdown "+Notebook::get().get_current_view()->file_path.string()); + if(exit_status==0) { + boost::system::error_code ec; + auto temp_path=boost::filesystem::temp_directory_path(ec); + if(!ec) { + temp_path/=boost::filesystem::unique_path(); + temp_path+=".html"; + if(!boost::filesystem::exists(temp_path)) { + last_temp_path=temp_path; + std::ofstream file_stream(temp_path.string(), std::fstream::binary); + file_stream << stdout_stream.rdbuf(); + file_stream.close(); + + auto uri=temp_path.string(); +#ifdef __APPLE__ + Terminal::get().process("open \""+uri+"\""); +#else +#ifdef __linux + uri="file://"+uri; +#endif + GError* error=NULL; + gtk_show_uri(NULL, uri.c_str(), GDK_CURRENT_TIME, &error); + g_clear_error(&error); +#endif + } + } + } +} + +void Project::Python::compile_and_run() { + auto command="python "+Notebook::get().get_current_view()->file_path.string(); + Terminal::get().print("Running "+command+"\n"); + Terminal::get().async_process(command, Notebook::get().get_current_view()->file_path.parent_path(), [command](int exit_status) { + Terminal::get().async_print(command+" returned: "+std::to_string(exit_status)+'\n'); + }); +} + +void Project::JavaScript::compile_and_run() { + auto command="node "+Notebook::get().get_current_view()->file_path.string(); + Terminal::get().print("Running "+command+"\n"); + Terminal::get().async_process(command, Notebook::get().get_current_view()->file_path.parent_path(), [command](int exit_status) { + Terminal::get().async_print(command+" returned: "+std::to_string(exit_status)+'\n'); + }); +} + +void Project::HTML::compile_and_run() { + auto uri=Notebook::get().get_current_view()->file_path.string(); +#ifdef __APPLE__ + Terminal::get().process("open \""+uri+"\""); +#else +#ifdef __linux + uri="file://"+uri; +#endif + GError* error=NULL; + gtk_show_uri(NULL, uri.c_str(), GDK_CURRENT_TIME, &error); + g_clear_error(&error); +#endif +} diff --git a/src/project.h b/src/project.h new file mode 100644 index 0000000..e3f1fc7 --- /dev/null +++ b/src/project.h @@ -0,0 +1,127 @@ +#ifndef JUCI_PROJECT_H_ +#define JUCI_PROJECT_H_ + +#include +#include "cmake.h" +#include +#include "directories.h" +#include +#include +#include "tooltips.h" +#include "dispatcher.h" +#include + +class Project { +private: + static boost::filesystem::path debug_last_stop_file_path; +public: + static std::unordered_map run_arguments; + static std::unordered_map debug_run_arguments; + static std::atomic compiling; + static std::atomic debugging; + static std::pair > debug_stop; + static void debug_update_stop(); + static void debug_update_status(const std::string &debug_status); + + static Gtk::Label &debug_status_label() { + static Gtk::Label label; + return label; + } + + class Language { + public: + Language() {} + virtual ~Language() {} + + virtual std::pair get_run_arguments() {return {"", ""};} + virtual void compile() {} + virtual void compile_and_run() {} + + virtual std::pair debug_get_run_arguments() {return {"", ""};} + Tooltips debug_variable_tooltips; + virtual void debug_start() {} + virtual void debug_continue() {} + virtual void debug_stop() {} + virtual void debug_kill() {} + virtual void debug_step_over() {} + virtual void debug_step_into() {} + virtual void debug_step_out() {} + virtual void debug_backtrace() {} + virtual void debug_show_variables() {} + virtual void debug_run_command(const std::string &command) {} + virtual void debug_add_breakpoint(const boost::filesystem::path &file_path, int line_nr) {} + virtual void debug_remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) {} + virtual bool debug_is_running() { return false; } + virtual void debug_write(const std::string &buffer) {} + virtual void debug_delete() {} + }; + + class Clang : public Language { + private: + Dispatcher dispatcher; + public: + Clang() : Language() {} + ~Clang() { dispatcher.disconnect(); } + + std::unique_ptr get_cmake(); + + std::pair get_run_arguments() override; + void compile() override; + void compile_and_run() override; + + std::mutex debug_start_mutex; + #ifdef JUCI_ENABLE_DEBUG + std::pair debug_get_run_arguments() override; + void debug_start() override; + void debug_continue() override; + void debug_stop() override; + void debug_kill() override; + void debug_step_over() override; + void debug_step_into() override; + void debug_step_out() override; + void debug_backtrace() override; + void debug_show_variables() override; + void debug_run_command(const std::string &command) override; + void debug_add_breakpoint(const boost::filesystem::path &file_path, int line_nr) override; + void debug_remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) override; + bool debug_is_running() override; + void debug_write(const std::string &buffer) override; + void debug_delete() override; + #endif + }; + + class Markdown : public Language { + public: + Markdown() : Language() {} + ~Markdown(); + + boost::filesystem::path last_temp_path; + void compile_and_run() override; + }; + + class Python : public Language { + public: + Python() : Language() {} + + void compile_and_run() override; + }; + + class JavaScript : public Language { + public: + JavaScript() : Language() {} + + void compile_and_run() override; + }; + + class HTML : public Language { + public: + HTML() : Language() {} + + void compile_and_run() override; + }; + + static std::unique_ptr get_language(); + static std::unique_ptr current_language; +}; + +#endif // JUCI_PROJECT_H_ diff --git a/src/selectiondialog.cc b/src/selectiondialog.cc index 842072e..445f4d1 100644 --- a/src/selectiondialog.cc +++ b/src/selectiondialog.cc @@ -38,10 +38,6 @@ void ListViewText::append(const std::string& value) { new_row->set_value(column_record.text, value); } -void ListViewText::clear() { - list_store->clear(); -} - SelectionDialogBase::SelectionDialogBase(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry, bool use_markup): text_view(text_view), list_view_text(use_markup), start_mark(start_mark), show_search_entry(show_search_entry) { if(!show_search_entry) @@ -121,7 +117,6 @@ void SelectionDialogBase::hide() { window->hide(); if(on_hide) on_hide(); - list_view_text.clear(); } void SelectionDialogBase::move() { diff --git a/src/selectiondialog.h b/src/selectiondialog.h index 732652f..8223918 100644 --- a/src/selectiondialog.h +++ b/src/selectiondialog.h @@ -17,7 +17,6 @@ public: bool use_markup; ListViewText(bool use_markup); void append(const std::string& value); - void clear(); private: Glib::RefPtr list_store; ColumnRecord column_record; diff --git a/src/source_clang.cc b/src/source_clang.cc index 7c6e12d..2130993 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -3,7 +3,7 @@ #include "terminal.h" #include "cmake.h" #ifdef JUCI_ENABLE_DEBUG -#include "debug.h" +#include "debug_clang.h" #endif namespace sigc { @@ -35,36 +35,6 @@ Source::View(file_path, project_path, language) { configure(); parsing_in_progress=Terminal::get().print_in_progress("Parsing "+file_path.string()); - //GTK-calls must happen in main thread, so the parse_thread - //sends signals to the main thread that it is to call the following functions: - parse_preprocess_connection=parse_preprocess.connect([this]{ - auto expected=ParseProcessState::PREPROCESSING; - if(parse_mutex.try_lock()) { - if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING)) - parse_thread_buffer=get_buffer()->get_text(); - parse_mutex.unlock(); - } - else - parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING); - }); - parse_postprocess_connection=parse_postprocess.connect([this](){ - if(parse_mutex.try_lock()) { - auto expected=ParseProcessState::POSTPROCESSING; - if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) { - update_syntax(); - update_diagnostics(); - parsed=true; - set_status(""); - } - parse_mutex.unlock(); - } - }); - parse_error_connection=parse_error.connect([this](){ - Terminal::get().print("Error: failed to reparse "+this->file_path.string()+".\n", true); - set_status(""); - set_info(""); - parsing_in_progress->cancel("failed"); - }); parse_initialize(); get_buffer()->signal_changed().connect([this]() { @@ -140,8 +110,18 @@ void Source::ClangViewParse::parse_initialize() { if(parse_state!=ParseState::PROCESSING) break; auto expected=ParseProcessState::STARTING; - if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) - parse_preprocess(); + if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) { + dispatcher.push([this] { + auto expected=ParseProcessState::PREPROCESSING; + if(parse_mutex.try_lock()) { + if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING)) + parse_thread_buffer=get_buffer()->get_text(); + parse_mutex.unlock(); + } + else + parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING); + }); + } else if (parse_process_state==ParseProcessState::PROCESSING && parse_mutex.try_lock()) { auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer.raw()); parsing_in_progress->done("done"); @@ -150,7 +130,18 @@ void Source::ClangViewParse::parse_initialize() { if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::POSTPROCESSING)) { clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer.bytes()-1); parse_mutex.unlock(); - parse_postprocess(); + dispatcher.push([this] { + if(parse_mutex.try_lock()) { + auto expected=ParseProcessState::POSTPROCESSING; + if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) { + update_syntax(); + update_diagnostics(); + parsed=true; + set_status(""); + } + parse_mutex.unlock(); + } + }); } else parse_mutex.unlock(); @@ -158,7 +149,12 @@ void Source::ClangViewParse::parse_initialize() { else { parse_state=ParseState::STOP; parse_mutex.unlock(); - parse_error(); + dispatcher.push([this] { + Terminal::get().print("Error: failed to reparse "+this->file_path.string()+".\n", true); + set_status(""); + set_info(""); + parsing_in_progress->cancel("failed"); + }); } } } @@ -204,10 +200,8 @@ std::vector Source::ClangViewParse::get_compilation_commands() { arguments.emplace_back("-I/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1"); //Added for OS X 10.11 #endif #ifdef _WIN32 - arguments.emplace_back("-IC:/msys32/mingw32/lib/clang/"+clang_version+"/include"); - arguments.emplace_back("-IC:/msys32/mingw64/lib/clang/"+clang_version+"/include"); - arguments.emplace_back("-IC:/msys64/mingw32/lib/clang/"+clang_version+"/include"); - arguments.emplace_back("-IC:/msys64/mingw64/lib/clang/"+clang_version+"/include"); + if(!Config::get().terminal.msys2_mingw_path.empty()) + arguments.emplace_back("-I"+(Config::get().terminal.msys2_mingw_path/"lib/clang"/clang_version/"include").string()); #endif } arguments.emplace_back("-fretain-comments-from-system-headers"); @@ -418,15 +412,15 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle) tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), "\n\n"+brief_comment, "def:note"); #ifdef JUCI_ENABLE_DEBUG - if(Debug::get().is_stopped()) { + if(Debug::Clang::get().is_stopped()) { auto location=token.get_cursor().get_referenced().get_source_location(); Glib::ustring value_type="Value"; - Glib::ustring debug_value=Debug::get().get_value(token.get_spelling(), location.get_path(), location.get_offset().line, location.get_offset().index); + Glib::ustring debug_value=Debug::Clang::get().get_value(token.get_spelling(), location.get_path(), location.get_offset().line, location.get_offset().index); if(debug_value.empty()) { value_type="Return value"; auto cursor=token.get_cursor(); auto offsets=cursor.get_source_range().get_offsets(); - debug_value=Debug::get().get_return_value(cursor.get_source_location().get_path(), offsets.first.line, offsets.first.index); + debug_value=Debug::Clang::get().get_return_value(cursor.get_source_location().get_path(), offsets.first.line, offsets.first.index); } if(!debug_value.empty()) { size_t pos=debug_value.find(" = "); @@ -713,70 +707,11 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_state(Au return false; }); - autocomplete_done_connection=autocomplete_done.connect([this](){ - if(autocomplete_state==AutocompleteState::CANCELED) { - set_status(""); - soft_reparse(); - autocomplete_state=AutocompleteState::IDLE; - } - else if(autocomplete_state==AutocompleteState::RESTARTING) { - set_status(""); - soft_reparse(); - autocomplete_state=AutocompleteState::IDLE; - autocomplete_restart(); - } - else { - autocomplete_dialog_setup(); - - for (auto &data : autocomplete_data) { - std::string row; - std::string return_value; - for (auto &chunk : data.chunks) { - if(chunk.kind==clang::CompletionChunk_ResultType) - return_value=chunk.chunk; - else if(chunk.kind!=clang::CompletionChunk_Informative) - row+=chunk.chunk; - } - data.chunks.clear(); - if (!row.empty()) { - auto row_insert_on_selection=row; - if(!return_value.empty()) - row+=" --> " + return_value; - autocomplete_dialog_rows[row] = std::pair(std::move(row_insert_on_selection), std::move(data.brief_comments)); - autocomplete_dialog->add_row(row); - } - } - autocomplete_data.clear(); - set_status(""); - autocomplete_state=AutocompleteState::IDLE; - if (!autocomplete_dialog_rows.empty()) { - get_source_buffer()->begin_user_action(); - autocomplete_dialog->show(); - } - else - soft_reparse(); - } - }); - - autocomplete_restart_connection=autocomplete_restart.connect([this]() { - autocomplete_check(); - }); - autocomplete_error_connection=autocomplete_error.connect([this]() { - Terminal::get().print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true); - autocomplete_state=AutocompleteState::CANCELED; - full_reparse(); - }); - - do_delete_object_connection=do_delete_object.connect([this](){ + do_delete_object.connect([this](){ if(delete_thread.joinable()) delete_thread.join(); - do_delete_object_connection.disconnect(); delete this; }); - do_full_reparse.connect([this](){ - parse_initialize(); - full_reparse_running=false; - }); } bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) { @@ -847,7 +782,7 @@ void Source::ClangViewAutocomplete::autocomplete_dialog_setup() { //new autocomplete after for instance when selecting "std::" auto iter=get_buffer()->get_insert()->get_iter(); if(iter.backward_char() && *iter==':') - autocomplete_restart(); + autocomplete_check(); } } }; @@ -918,8 +853,6 @@ void Source::ClangViewAutocomplete::autocomplete() { autocomplete_state=AutocompleteState::STARTING; - autocomplete_data.clear(); - set_status("autocomplete..."); if(autocomplete_thread.joinable()) autocomplete_thread.join(); @@ -938,12 +871,62 @@ void Source::ClangViewAutocomplete::autocomplete() { parse_mutex.lock(); if(parse_state==ParseState::PROCESSING) { parse_process_state=ParseProcessState::IDLE; - autocomplete_data=autocomplete_get_suggestions(buffer->raw(), line_nr, column_nr); + auto autocomplete_data=std::make_shared >(autocomplete_get_suggestions(buffer->raw(), line_nr, column_nr)); + + if(parse_state==ParseState::PROCESSING) { + dispatcher.push([this, autocomplete_data] { + if(autocomplete_state==AutocompleteState::CANCELED) { + set_status(""); + soft_reparse(); + autocomplete_state=AutocompleteState::IDLE; + } + else if(autocomplete_state==AutocompleteState::RESTARTING) { + set_status(""); + soft_reparse(); + autocomplete_state=AutocompleteState::IDLE; + autocomplete_check(); + } + else { + autocomplete_dialog_setup(); + + for (auto &data : *autocomplete_data) { + std::string row; + std::string return_value; + for (auto &chunk : data.chunks) { + if(chunk.kind==clang::CompletionChunk_ResultType) + return_value=chunk.chunk; + else if(chunk.kind!=clang::CompletionChunk_Informative) + row+=chunk.chunk; + } + data.chunks.clear(); + if (!row.empty()) { + auto row_insert_on_selection=row; + if(!return_value.empty()) + row+=" --> " + return_value; + autocomplete_dialog_rows[row] = std::pair(std::move(row_insert_on_selection), std::move(data.brief_comments)); + autocomplete_dialog->add_row(row); + } + } + autocomplete_data->clear(); + set_status(""); + autocomplete_state=AutocompleteState::IDLE; + if (!autocomplete_dialog_rows.empty()) { + get_source_buffer()->begin_user_action(); + autocomplete_dialog->show(); + } + else + soft_reparse(); + } + }); + } + else { + dispatcher.push([this] { + Terminal::get().print("Error: autocomplete failed, reparsing "+this->file_path.string()+"\n", true); + autocomplete_state=AutocompleteState::CANCELED; + full_reparse(); + }); + } } - if(parse_state==ParseState::PROCESSING) - autocomplete_done(); - else - autocomplete_error(); parse_mutex.unlock(); }); } @@ -1017,7 +1000,10 @@ bool Source::ClangViewAutocomplete::full_reparse() { parse_thread.join(); if(autocomplete_thread.joinable()) autocomplete_thread.join(); - do_full_reparse(); + dispatcher.push([this] { + parse_initialize(); + full_reparse_running=false; + }); }); return true; } @@ -1479,14 +1465,8 @@ Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boo } void Source::ClangView::async_delete() { + dispatcher.disconnect(); delayed_reparse_connection.disconnect(); - parse_postprocess_connection.disconnect(); - parse_preprocess_connection.disconnect(); - parse_error_connection.disconnect(); - autocomplete_done_connection.disconnect(); - autocomplete_restart_connection.disconnect(); - autocomplete_error_connection.disconnect(); - do_restart_parse_connection.disconnect(); delayed_tag_similar_tokens_connection.disconnect(); ClangViewAutocomplete::async_delete(); } diff --git a/src/source_clang.h b/src/source_clang.h index 93b226d..c29d863 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -9,6 +9,7 @@ #include "clangmm.h" #include "source.h" #include "terminal.h" +#include "dispatcher.h" namespace Source { class ClangViewParse : public View { @@ -30,6 +31,7 @@ namespace Source { void soft_reparse() override; protected: + Dispatcher dispatcher; void parse_initialize(); std::unique_ptr clang_tu; std::unique_ptr clang_tokens; @@ -53,13 +55,7 @@ namespace Source { std::mutex parse_mutex; std::atomic parse_state; std::atomic parse_process_state; - sigc::connection parse_preprocess_connection; - sigc::connection parse_postprocess_connection; - sigc::connection parse_error_connection; private: - Glib::Dispatcher parse_preprocess; - Glib::Dispatcher parse_postprocess; - Glib::Dispatcher parse_error; Glib::ustring parse_thread_buffer; void update_syntax(); @@ -90,29 +86,19 @@ namespace Source { bool on_key_press_event(GdkEventKey* key) override; std::thread autocomplete_thread; - sigc::connection autocomplete_done_connection; - sigc::connection autocomplete_restart_connection; - sigc::connection autocomplete_error_connection; - sigc::connection do_delete_object_connection; - sigc::connection do_restart_parse_connection; private: std::atomic autocomplete_state; void autocomplete_dialog_setup(); void autocomplete_check(); void autocomplete(); - std::vector autocomplete_data; std::unordered_map > autocomplete_dialog_rows; std::vector autocomplete_get_suggestions(const std::string &buffer, int line_number, int column); Tooltips autocomplete_tooltips; - Glib::Dispatcher autocomplete_done; - Glib::Dispatcher autocomplete_restart; - Glib::Dispatcher autocomplete_error; guint last_keyval=0; std::string prefix; std::mutex prefix_mutex; Glib::Dispatcher do_delete_object; - Glib::Dispatcher do_full_reparse; std::thread delete_thread; std::thread full_reparse_thread; bool full_reparse_running=false; diff --git a/src/terminal.cc b/src/terminal.cc index 3738356..cbd8739 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -2,14 +2,9 @@ #include #include "logging.h" #include "config.h" -#ifdef JUCI_ENABLE_DEBUG -#include "debug.h" -#endif +#include "project.h" Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) { - waiting_print.connect([this](){ - Terminal::get().async_print(line_nr-1, "."); - }); start(start_msg); } @@ -25,7 +20,7 @@ void Terminal::InProgress::start(const std::string& msg) { size_t c=0; while(!stop) { if(c%100==0) - waiting_print(); + Terminal::get().async_print(line_nr-1, "."); std::this_thread::sleep_for(std::chrono::milliseconds(10)); c++; } @@ -49,25 +44,6 @@ void Terminal::InProgress::cancel(const std::string& msg) { Terminal::Terminal() { bold_tag=get_buffer()->create_tag(); bold_tag->property_weight()=PANGO_WEIGHT_BOLD; - - async_print_dispatcher.connect([this](){ - async_print_strings_mutex.lock(); - if(async_print_strings.size()>0) { - for(auto &string_bold: async_print_strings) - print(string_bold.first, string_bold.second); - async_print_strings.clear(); - } - async_print_strings_mutex.unlock(); - }); - async_print_on_line_dispatcher.connect([this](){ - async_print_on_line_strings_mutex.lock(); - if(async_print_on_line_strings.size()>0) { - for(auto &line_string: async_print_on_line_strings) - print(line_string.first, line_string.second); - async_print_on_line_strings.clear(); - } - async_print_on_line_strings_mutex.unlock(); - }); } int Terminal::process(const std::string &command, const boost::filesystem::path &path, bool use_pipes) { @@ -228,55 +204,41 @@ size_t Terminal::print(const std::string &message, bool bold){ return static_cast(get_buffer()->end().get_line())+deleted_lines; } -void Terminal::print(size_t line_nr, const std::string &message){ - if(line_nrget_iter_at_line(static_cast(line_nr-deleted_lines)); - while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {} - get_buffer()->insert(end_line_iter, umessage); -} - std::shared_ptr Terminal::print_in_progress(std::string start_msg) { std::shared_ptr in_progress=std::shared_ptr(new Terminal::InProgress(start_msg)); return in_progress; } void Terminal::async_print(const std::string &message, bool bold) { - async_print_strings_mutex.lock(); - bool dispatch=true; - if(async_print_strings.size()>0) - dispatch=false; - async_print_strings.emplace_back(message, bold); - async_print_strings_mutex.unlock(); - if(dispatch) - async_print_dispatcher(); + dispatcher.push([this, message, bold] { + Terminal::get().print(message, bold); + }); } -void Terminal::async_print(int line_nr, const std::string &message) { - async_print_on_line_strings_mutex.lock(); - bool dispatch=true; - if(async_print_on_line_strings.size()>0) - dispatch=false; - async_print_on_line_strings.emplace_back(line_nr, message); - async_print_on_line_strings_mutex.unlock(); - if(dispatch) - async_print_on_line_dispatcher(); +void Terminal::async_print(size_t line_nr, const std::string &message) { + dispatcher.push([this, line_nr, message] { + if(line_nrget_iter_at_line(static_cast(line_nr-deleted_lines)); + while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {} + get_buffer()->insert(end_line_iter, umessage); + }); } bool Terminal::on_key_press_event(GdkEventKey *event) { processes_mutex.lock(); bool debug_is_running=false; #ifdef JUCI_ENABLE_DEBUG - debug_is_running=Debug::get().is_running(); + debug_is_running=Project::current_language?Project::current_language->debug_is_running():false; #endif if(processes.size()>0 || debug_is_running) { get_buffer()->place_cursor(get_buffer()->end()); @@ -298,7 +260,7 @@ bool Terminal::on_key_press_event(GdkEventKey *event) { stdin_buffer+='\n'; if(debug_is_running) { #ifdef JUCI_ENABLE_DEBUG - Debug::get().write(stdin_buffer); + Project::current_language->debug_write(stdin_buffer); #endif } else diff --git a/src/terminal.h b/src/terminal.h index 5137510..dd74ad9 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -9,6 +9,7 @@ #include #include #include "process.hpp" +#include "dispatcher.h" class Terminal : public Gtk::TextView { public: @@ -22,7 +23,7 @@ public: void start(const std::string& msg); size_t line_nr; std::atomic stop; - Glib::Dispatcher waiting_print; + std::thread wait_thread; }; @@ -41,19 +42,13 @@ public: void kill_async_processes(bool force=false); size_t print(const std::string &message, bool bold=false); - void print(size_t line_nr, const std::string &message); std::shared_ptr print_in_progress(std::string start_msg); void async_print(const std::string &message, bool bold=false); - void async_print(int line_nr, const std::string &message); + void async_print(size_t line_nr, const std::string &message); protected: bool on_key_press_event(GdkEventKey *event); private: - Glib::Dispatcher async_print_dispatcher; - Glib::Dispatcher async_print_on_line_dispatcher; - std::vector > async_print_strings; - std::vector > async_print_on_line_strings; - std::mutex async_print_strings_mutex; - std::mutex async_print_on_line_strings_mutex; + Dispatcher dispatcher; Glib::RefPtr bold_tag; std::vector > processes; diff --git a/src/window.cc b/src/window.cc index ad492d6..76081ff 100644 --- a/src/window.cc +++ b/src/window.cc @@ -6,9 +6,6 @@ //#include "api.h" #include "dialogs.h" #include "filesystem.h" -#ifdef JUCI_ENABLE_DEBUG -#include "debug.h" -#endif namespace sigc { #ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE @@ -24,11 +21,13 @@ namespace sigc { #endif } -Window::Window() : compiling(false), debugging(false) { +Window::Window() : notebook(Notebook::get()) { JDEBUG("start"); set_title("juCi++"); set_events(Gdk::POINTER_MOTION_MASK|Gdk::FOCUS_CHANGE_MASK|Gdk::SCROLL_MASK|Gdk::LEAVE_NOTIFY_MASK); + set_menu_actions(); + configure(); set_default_size(Config::get().window.default_size.first, Config::get().window.default_size.second); @@ -49,11 +48,12 @@ Window::Window() : compiling(false), debugging(false) { terminal_vbox.pack_start(terminal_scrolled_window); info_and_status_hbox.pack_start(notebook.info, Gtk::PACK_SHRINK); + #if GTK_VERSION_GE(3, 12) - info_and_status_hbox.set_center_widget(debug_status_label); + info_and_status_hbox.set_center_widget(Project::debug_status_label()); #else - debug_status_label.set_halign(Gtk::Align::ALIGN_CENTER); - info_and_status_hbox.pack_start(debug_status_label); + Project::debug_status_label().set_halign(Gtk::Align::ALIGN_CENTER); + info_and_status_hbox.pack_start(Project::debug_status_label()); #endif info_and_status_hbox.pack_end(notebook.status, Gtk::PACK_SHRINK); terminal_vbox.pack_end(info_and_status_hbox, Gtk::PACK_SHRINK); @@ -121,74 +121,6 @@ Window::Window() : compiling(false), debugging(false) { about.hide(); }); - debug_update_stop.connect([this](){ - debug_stop_mutex.lock(); - for(int c=0;cfile_path==debug_last_stop_file_path) { - view->get_source_buffer()->remove_source_marks(view->get_buffer()->begin(), view->get_buffer()->end(), "debug_stop"); - break; - } - } - //Add debug stop source mark - for(int c=0;cfile_path==debug_stop.first) { - if(debug_stop.second.first-1get_buffer()->get_line_count()) { - view->get_source_buffer()->create_source_mark("debug_stop", view->get_buffer()->get_iter_at_line(debug_stop.second.first-1)); - debug_last_stop_file_path=debug_stop.first; - } - break; - } - } - if(notebook.get_current_page()!=-1) - notebook.get_current_view()->get_buffer()->place_cursor(notebook.get_current_view()->get_buffer()->get_insert()->get_iter()); - debug_stop_mutex.unlock(); - }); - -#ifdef JUCI_ENABLE_DEBUG - auto &menu=Menu::get(); - menu.actions["debug_stop"]->set_enabled(false); - menu.actions["debug_kill"]->set_enabled(false); - menu.actions["debug_step_over"]->set_enabled(false); - menu.actions["debug_step_into"]->set_enabled(false); - menu.actions["debug_step_out"]->set_enabled(false); - menu.actions["debug_backtrace"]->set_enabled(false); - menu.actions["debug_show_variables"]->set_enabled(false); - menu.actions["debug_run_command"]->set_enabled(false); - menu.actions["debug_goto_stop"]->set_enabled(false); -#endif - debug_update_status.connect([this](){ - debug_status_mutex.lock(); - if(debug_status.empty()) { - debug_status_label.set_text(""); - auto &menu=Menu::get(); - menu.actions["debug_stop"]->set_enabled(false); - menu.actions["debug_kill"]->set_enabled(false); - menu.actions["debug_step_over"]->set_enabled(false); - menu.actions["debug_step_into"]->set_enabled(false); - menu.actions["debug_step_out"]->set_enabled(false); - menu.actions["debug_backtrace"]->set_enabled(false); - menu.actions["debug_show_variables"]->set_enabled(false); - menu.actions["debug_run_command"]->set_enabled(false); - menu.actions["debug_goto_stop"]->set_enabled(false); - } - else { - debug_status_label.set_text("debug: "+debug_status); - auto &menu=Menu::get(); - menu.actions["debug_stop"]->set_enabled(); - menu.actions["debug_kill"]->set_enabled(); - menu.actions["debug_step_over"]->set_enabled(); - menu.actions["debug_step_into"]->set_enabled(); - menu.actions["debug_step_out"]->set_enabled(); - menu.actions["debug_backtrace"]->set_enabled(); - menu.actions["debug_show_variables"]->set_enabled(); - menu.actions["debug_run_command"]->set_enabled(); - menu.actions["debug_goto_stop"]->set_enabled(); - } - debug_status_mutex.unlock(); - }); - about.set_version(Config::get().window.version); about.set_authors({"(in order of appearance)", "Ted Johan Kristoffersen", @@ -203,22 +135,6 @@ Window::Window() : compiling(false), debugging(false) { JDEBUG("end"); } // Window constructor -std::unique_ptr Window::get_cmake() { - boost::filesystem::path path; - if(notebook.get_current_page()!=-1) - path=notebook.get_current_view()->file_path.parent_path(); - else - path=Directories::get().current_path; - if(path.empty()) - return nullptr; - auto cmake=std::unique_ptr(new CMake(path)); - if(cmake->project_path.empty()) - return nullptr; - if(!CMake::create_default_build(cmake->project_path)) - return nullptr; - return cmake; -} - void Window::configure() { Config::get().load(); auto style_context = Gtk::StyleContext::create(); @@ -591,33 +507,11 @@ void Window::set_menu_actions() { }); menu.add_action("project_set_run_arguments", [this]() { - auto cmake=get_cmake(); - if(!cmake) + auto project_language=Project::get_language(); + auto run_arguments=std::make_shared >(project_language->get_run_arguments()); + if(run_arguments->second.empty()) return; - auto project_path=std::make_shared(cmake->project_path); - auto run_arguments_it=project_run_arguments.find(project_path->string()); - std::string run_arguments; - if(run_arguments_it!=project_run_arguments.end()) - run_arguments=run_arguments_it->second; - - if(run_arguments.empty()) { - auto executable=cmake->get_executable(notebook.get_current_page()!=-1?notebook.get_current_view()->file_path:"").string(); - - if(executable!="") { - auto project_path=cmake->project_path; - auto default_build_path=CMake::get_default_build_path(project_path); - if(!default_build_path.empty()) { - size_t pos=executable.find(project_path.string()); - if(pos!=std::string::npos) - executable.replace(pos, project_path.string().size(), default_build_path.string()); - } - run_arguments=filesystem::escape_argument(executable); - } - else - run_arguments=filesystem::escape_argument(CMake::get_default_build_path(cmake->project_path)); - } - entry_box.clear(); entry_box.labels.emplace_back(); auto label_it=entry_box.labels.begin(); @@ -625,8 +519,8 @@ void Window::set_menu_actions() { label_it->set_text("Set empty to let juCi++ deduce executable"); }; label_it->update(0, ""); - entry_box.entries.emplace_back(run_arguments, [this, project_path](const std::string& content){ - project_run_arguments[project_path->string()]=content; + entry_box.entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ + Project::run_arguments[run_arguments->first]=content; entry_box.hide(); }, 50); auto entry_it=entry_box.entries.begin(); @@ -637,75 +531,24 @@ void Window::set_menu_actions() { entry_box.show(); }); menu.add_action("compile_and_run", [this]() { - if(compiling) + if(Project::compiling || Project::debugging) return; - if(Config::get().window.save_on_compile_or_run) + if(Config::get().project.save_on_compile_or_run) notebook.save_project_files(); - auto cmake=get_cmake(); - if(!cmake) - return; - auto project_path=cmake->project_path; - - auto default_build_path=CMake::get_default_build_path(project_path); - if(default_build_path.empty()) - return; - - auto run_arguments_it=project_run_arguments.find(project_path.string()); - std::string run_arguments; - if(run_arguments_it!=project_run_arguments.end()) - run_arguments=run_arguments_it->second; - - std::string command; - if(!run_arguments.empty()) { - command=run_arguments; - } - else { - command=cmake->get_executable(notebook.get_current_page()!=-1?notebook.get_current_view()->file_path:"").string(); - if(command.empty()) { - Terminal::get().print("Could not find add_executable in the following paths:\n"); - for(auto &path: cmake->paths) - Terminal::get().print(" "+path.string()+"\n"); - Terminal::get().print("Solution: either use Project Set Run Arguments, or open a source file within a directory where add_executable is set.\n", true); - return; - } - size_t pos=command.find(project_path.string()); - if(pos!=std::string::npos) - command.replace(pos, project_path.string().size(), default_build_path.string()); - command=filesystem::escape_argument(command); - } - - compiling=true; - Terminal::get().print("Compiling and running "+command+"\n"); - Terminal::get().async_process(Config::get().terminal.make_command, default_build_path, [this, command, default_build_path](int exit_status){ - compiling=false; - if(exit_status==EXIT_SUCCESS) { - Terminal::get().async_process(command, default_build_path, [this, command](int exit_status){ - Terminal::get().async_print(command+" returned: "+std::to_string(exit_status)+'\n'); - }); - } - }); + Project::current_language=Project::get_language(); + Project::current_language->compile_and_run(); }); menu.add_action("compile", [this]() { - if(compiling) + if(Project::compiling || Project::debugging) return; - if(Config::get().window.save_on_compile_or_run) + if(Config::get().project.save_on_compile_or_run) notebook.save_project_files(); - - auto cmake=get_cmake(); - if(!cmake) - return; - - auto default_build_path=CMake::get_default_build_path(cmake->project_path); - if(default_build_path.empty()) - return; - compiling=true; - Terminal::get().print("Compiling project "+cmake->project_path.string()+"\n"); - Terminal::get().async_process(Config::get().terminal.make_command, default_build_path, [this](int exit_status){ - compiling=false; - }); + + Project::current_language=Project::get_language(); + Project::current_language->compile(); }); menu.add_action("run_command", [this]() { @@ -745,32 +588,10 @@ void Window::set_menu_actions() { #ifdef JUCI_ENABLE_DEBUG menu.add_action("debug_set_run_arguments", [this]() { - auto cmake=get_cmake(); - if(!cmake) + auto project_language=Project::get_language(); + auto run_arguments=std::make_shared >(project_language->debug_get_run_arguments()); + if(run_arguments->second.empty()) return; - - auto project_path=std::make_shared(cmake->project_path); - auto run_arguments_it=debug_run_arguments.find(project_path->string()); - std::string run_arguments; - if(run_arguments_it!=debug_run_arguments.end()) - run_arguments=run_arguments_it->second; - - if(run_arguments.empty()) { - auto executable=cmake->get_executable(notebook.get_current_page()!=-1?notebook.get_current_view()->file_path:"").string(); - - if(executable!="") { - auto project_path=cmake->project_path; - auto debug_build_path=CMake::get_debug_build_path(project_path); - if(!debug_build_path.empty()) { - size_t pos=executable.find(project_path.string()); - if(pos!=std::string::npos) - executable.replace(pos, project_path.string().size(), debug_build_path.string()); - } - run_arguments=filesystem::escape_argument(executable); - } - else - run_arguments=filesystem::escape_argument(CMake::get_debug_build_path(cmake->project_path)); - } entry_box.clear(); entry_box.labels.emplace_back(); @@ -779,258 +600,66 @@ void Window::set_menu_actions() { label_it->set_text("Set empty to let juCi++ deduce executable"); }; label_it->update(0, ""); - entry_box.entries.emplace_back(run_arguments, [this, project_path](const std::string& content){ - debug_run_arguments[project_path->string()]=content; + entry_box.entries.emplace_back(run_arguments->second, [this, run_arguments](const std::string& content){ + Project::debug_run_arguments[run_arguments->first]=content; entry_box.hide(); }, 50); auto entry_it=entry_box.entries.begin(); - entry_it->set_placeholder_text("Project: Set Run Arguments"); - entry_box.buttons.emplace_back("Project: set run arguments", [this, entry_it](){ + entry_it->set_placeholder_text("Debug: Set Run Arguments"); + entry_box.buttons.emplace_back("Debug: set run arguments", [this, entry_it](){ entry_it->activate(); }); entry_box.show(); }); menu.add_action("debug_start_continue", [this](){ - if(debugging) { - Debug::get().continue_debug(); + if(Project::compiling) + return; + else if(Project::debugging) { + Project::current_language->debug_continue(); return; } - if(Config::get().window.save_on_compile_or_run) + if(Config::get().project.save_on_compile_or_run) notebook.save_project_files(); - auto cmake=get_cmake(); - if(!cmake) - return; - auto project_path=cmake->project_path; - - auto debug_build_path=CMake::get_debug_build_path(project_path); - if(debug_build_path.empty()) - return; - if(!CMake::create_debug_build(project_path)) - return; - - auto run_arguments_it=debug_run_arguments.find(project_path.string()); - std::string run_arguments; - if(run_arguments_it!=debug_run_arguments.end()) - run_arguments=run_arguments_it->second; - - std::string command; - if(!run_arguments.empty()) { - command=run_arguments; - } - else { - command=cmake->get_executable(notebook.get_current_page()!=-1?notebook.get_current_view()->file_path:"").string(); - if(command.empty()) { - Terminal::get().print("Could not find add_executable in the following paths:\n"); - for(auto &path: cmake->paths) - Terminal::get().print(" "+path.string()+"\n"); - Terminal::get().print("Solution: either use Debug Set Run Arguments, or open a source file within a directory where add_executable is set.\n", true); - return; - } - size_t pos=command.find(project_path.string()); - if(pos!=std::string::npos) - command.replace(pos, project_path.string().size(), debug_build_path.string()); - command=filesystem::escape_argument(command); - } - - auto breakpoints=std::make_shared > >(); - for(int c=0;cproject_path) { - auto iter=view->get_buffer()->begin(); - if(view->get_source_buffer()->get_source_marks_at_iter(iter, "debug_breakpoint").size()>0) - breakpoints->emplace_back(view->file_path, iter.get_line()+1); - while(view->get_source_buffer()->forward_iter_to_source_mark(iter, "debug_breakpoint")) - breakpoints->emplace_back(view->file_path, iter.get_line()+1); - } - } + Project::current_language=Project::get_language(); - debugging=true; - Terminal::get().print("Compiling and debugging "+command+"\n"); - Terminal::get().async_process(Config::get().terminal.make_command, debug_build_path, [this, breakpoints, command, debug_build_path](int exit_status){ - if(exit_status!=EXIT_SUCCESS) - debugging=false; - else { - debug_start_mutex.lock(); - Debug::get().start(command, debug_build_path, breakpoints, [this, command](int exit_status){ - debugging=false; - Terminal::get().async_print(command+" returned: "+std::to_string(exit_status)+'\n'); - }, [this](const std::string &status) { - debug_status_mutex.lock(); - debug_status=status; - debug_status_mutex.unlock(); - debug_update_status(); - }, [this](const boost::filesystem::path &file_path, int line_nr, int line_index) { - debug_stop_mutex.lock(); - debug_stop.first=file_path; - debug_stop.second.first=line_nr; - debug_stop.second.second=line_index; - debug_stop_mutex.unlock(); - debug_update_stop(); - //Remove debug stop source mark - }); - debug_start_mutex.unlock(); - } - }); + Project::current_language->debug_start(); }); menu.add_action("debug_stop", [this]() { - if(debugging) { - Debug::get().stop(); - } + if(Project::current_language) + Project::current_language->debug_stop(); }); menu.add_action("debug_kill", [this]() { - if(debugging) { - Debug::get().kill(); - } + if(Project::current_language) + Project::current_language->debug_kill(); }); menu.add_action("debug_step_over", [this]() { - if(debugging) - Debug::get().step_over(); + if(Project::current_language) + Project::current_language->debug_step_over(); }); menu.add_action("debug_step_into", [this]() { - if(debugging) - Debug::get().step_into(); + if(Project::current_language) + Project::current_language->debug_step_into(); }); menu.add_action("debug_step_out", [this]() { - if(debugging) - Debug::get().step_out(); + if(Project::current_language) + Project::current_language->debug_step_out(); }); menu.add_action("debug_backtrace", [this]() { - if(debugging && notebook.get_current_page()!=-1) { - auto backtrace=Debug::get().get_backtrace(); - - auto view=notebook.get_current_view(); - auto iter=view->get_iter_for_dialog(); - view->selection_dialog=std::unique_ptr(new SelectionDialog(*view, view->get_buffer()->create_mark(iter), true, true)); - auto rows=std::make_shared >(); - if(backtrace.size()==0) - return; - - for(auto &frame: backtrace) { - std::string row=""+frame.module_filename+""; - - //Shorten frame.function_name if it is too long - if(frame.function_name.size()>120) { - frame.function_name=frame.function_name.substr(0, 58)+"...."+frame.function_name.substr(frame.function_name.size()-58); - } - if(frame.file_path.empty()) - row+=" - "+Glib::Markup::escape_text(frame.function_name); - else { - auto file_path=boost::filesystem::path(frame.file_path).filename().string(); - row+=":"+Glib::Markup::escape_text(file_path)+":"+std::to_string(frame.line_nr)+" - "+Glib::Markup::escape_text(frame.function_name); - } - (*rows)[row]=frame; - view->selection_dialog->add_row(row); - } - - view->selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { - auto frame=rows->at(selected); - if(!frame.file_path.empty()) { - notebook.open(frame.file_path); - if(notebook.get_current_page()!=-1) { - auto view=notebook.get_current_view(); - - Debug::get().select_frame(frame.index); - - view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(frame.line_nr-1, frame.line_index-1)); - - while(g_main_context_pending(NULL)) - g_main_context_iteration(NULL, false); - if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) - view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); - } - } - }; - view->selection_dialog->show(); - } + if(Project::current_language) + Project::current_language->debug_backtrace(); }); menu.add_action("debug_show_variables", [this]() { - if(debugging && notebook.get_current_page()!=-1) { - auto variables=Debug::get().get_variables(); - - auto view=notebook.get_current_view(); - auto iter=view->get_iter_for_dialog(); - view->selection_dialog=std::unique_ptr(new SelectionDialog(*view, view->get_buffer()->create_mark(iter), true, true)); - auto rows=std::make_shared >(); - if(variables.size()==0) - return; - - for(auto &variable: variables) { - std::string row="#"+std::to_string(variable.thread_index_id)+":#"+std::to_string(variable.frame_index)+":"+variable.file_path.filename().string()+":"+std::to_string(variable.line_nr)+" - "+Glib::Markup::escape_text(variable.name)+""; - - (*rows)[row]=variable; - view->selection_dialog->add_row(row); - } - - view->selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) { - auto variable=rows->at(selected); - if(!variable.file_path.empty()) { - notebook.open(variable.file_path); - if(notebook.get_current_page()!=-1) { - auto view=notebook.get_current_view(); - - Debug::get().select_frame(variable.frame_index, variable.thread_index_id); - - view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(variable.line_nr-1, variable.line_index-1)); - - while(g_main_context_pending(NULL)) - g_main_context_iteration(NULL, false); - if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) - view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); - } - } - }; - - view->selection_dialog->on_hide=[this]() { - debug_variable_tooltips.hide(); - debug_variable_tooltips.clear(); - }; - - view->selection_dialog->on_changed=[this, rows, iter](const std::string &selected) { - if(selected.empty()) { - debug_variable_tooltips.hide(); - return; - } - if(notebook.get_current_page()!=-1) { - auto view=notebook.get_current_view(); - debug_variable_tooltips.clear(); - auto create_tooltip_buffer=[this, rows, view, selected]() { - auto variable=rows->at(selected); - auto tooltip_buffer=Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()); - - Glib::ustring value=variable.value; - if(!value.empty()) { - Glib::ustring::iterator iter; - while(!value.validate(iter)) { - auto next_char_iter=iter; - next_char_iter++; - value.replace(iter, next_char_iter, "?"); - } - tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), value.substr(0, value.size()-1), "def:note"); - } - - return tooltip_buffer; - }; - - debug_variable_tooltips.emplace_back(create_tooltip_buffer, *view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter)); - - debug_variable_tooltips.show(true); - } - }; - - view->selection_dialog->show(); - } + if(Project::current_language) + Project::current_language->debug_show_variables(); }); menu.add_action("debug_run_command", [this]() { entry_box.clear(); entry_box.entries.emplace_back(last_run_debug_command, [this](const std::string& content){ if(content!="") { - if(debugging) { - auto command_return=Debug::get().run_command(content); - Terminal::get().async_print(command_return.first); - Terminal::get().async_print(command_return.second, true); - } + if(Project::current_language) + Project::current_language->debug_run_command(content); last_run_debug_command=content; } entry_box.hide(); @@ -1044,39 +673,33 @@ void Window::set_menu_actions() { }); menu.add_action("debug_toggle_breakpoint", [this](){ if(notebook.get_current_page()!=-1) { - auto view=notebook.get_current_view(); - bool debug_is_stopped_or_running=Debug::get().is_stopped() || Debug::get().is_running(); - if(Debug::get().is_invalid() || debug_is_stopped_or_running) { - auto line_nr=view->get_buffer()->get_insert()->get_iter().get_line(); - - if(view->get_source_buffer()->get_source_marks_at_line(line_nr, "debug_breakpoint").size()>0) { - auto start_iter=view->get_buffer()->get_iter_at_line(line_nr); - auto end_iter=start_iter; - while(!end_iter.ends_line() && end_iter.forward_char()) {} - view->get_source_buffer()->remove_source_marks(start_iter, end_iter, "debug_breakpoint"); - if(debug_is_stopped_or_running) - Debug::get().remove_breakpoint(view->file_path, line_nr+1, view->get_buffer()->get_line_count()+1); - } - else { - view->get_source_buffer()->create_source_mark("debug_breakpoint", view->get_buffer()->get_insert()->get_iter()); - if(debug_is_stopped_or_running) - Debug::get().add_breakpoint(view->file_path, line_nr+1); - } + auto view=notebook.get_current_view(); + auto line_nr=view->get_buffer()->get_insert()->get_iter().get_line(); + + if(view->get_source_buffer()->get_source_marks_at_line(line_nr, "debug_breakpoint").size()>0) { + auto start_iter=view->get_buffer()->get_iter_at_line(line_nr); + auto end_iter=start_iter; + while(!end_iter.ends_line() && end_iter.forward_char()) {} + view->get_source_buffer()->remove_source_marks(start_iter, end_iter, "debug_breakpoint"); + if(Project::current_language && Project::debugging) + Project::current_language->debug_remove_breakpoint(view->file_path, line_nr+1, view->get_buffer()->get_line_count()+1); + } + else { + view->get_source_buffer()->create_source_mark("debug_breakpoint", view->get_buffer()->get_insert()->get_iter()); + if(Project::current_language && Project::debugging) + Project::current_language->debug_add_breakpoint(view->file_path, line_nr+1); } } }); menu.add_action("debug_goto_stop", [this](){ - if(debugging) { - debug_stop_mutex.lock(); - auto debug_stop_copy=debug_stop; - debug_stop_mutex.unlock(); - if(!debug_stop_copy.first.empty()) { - notebook.open(debug_stop_copy.first); + if(Project::debugging) { + if(!Project::debug_stop.first.empty()) { + notebook.open(Project::debug_stop.first); if(notebook.get_current_page()!=-1) { auto view=notebook.get_current_view(); - int line_nr=debug_stop_copy.second.first-1; - int line_index=debug_stop_copy.second.second-1; + int line_nr=Project::debug_stop.second.first-1; + int line_index=Project::debug_stop.second.second-1; if(line_nrget_buffer()->get_line_count()) { auto iter=view->get_buffer()->get_iter_at_line(line_nr); auto end_line_iter=iter; @@ -1091,11 +714,13 @@ void Window::set_menu_actions() { if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view) view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); } - debug_update_stop(); + Project::debug_update_stop(); } } } }); + + Project::debug_update_status(""); #endif menu.add_action("next_tab", [this]() { @@ -1192,9 +817,8 @@ bool Window::on_delete_event(GdkEventAny *event) { } Terminal::get().kill_async_processes(); #ifdef JUCI_ENABLE_DEBUG - debug_start_mutex.lock(); - Debug::get().delete_debug(); - debug_start_mutex.unlock(); + if(Project::current_language) + Project::current_language->debug_delete(); #endif return false; } diff --git a/src/window.h b/src/window.h index a3485a2..c2ad3ba 100644 --- a/src/window.h +++ b/src/window.h @@ -1,23 +1,22 @@ #ifndef JUCI_WINDOW_H_ #define JUCI_WINDOW_H_ -#include "gtkmm.h" +#include #include "entrybox.h" #include "notebook.h" #include "cmake.h" -#include "tooltips.h" +#include "project.h" #include class Window : public Gtk::ApplicationWindow { private: Window(); + Notebook ¬ebook; //convenience reference public: static Window &get() { static Window singleton; return singleton; } - - Notebook notebook; protected: bool on_key_press_event(GdkEventKey *event) override; @@ -33,26 +32,6 @@ private: Gtk::HBox info_and_status_hbox; Gtk::AboutDialog about; EntryBox entry_box; - - std::atomic compiling; - - std::atomic debugging; - Gtk::Label debug_status_label; - - std::mutex debug_start_mutex; - std::pair > debug_stop; - boost::filesystem::path debug_last_stop_file_path; - std::mutex debug_stop_mutex; - Glib::Dispatcher debug_update_stop; - std::string debug_status; - std::mutex debug_status_mutex; - Glib::Dispatcher debug_update_status; - - Tooltips debug_variable_tooltips; - - std::unique_ptr get_cmake(); - std::unordered_map project_run_arguments; - std::unordered_map debug_run_arguments; void configure(); void set_menu_actions();