Browse Source

bind terminal and add type caster for filesystem path

python
Jørgen Lien Sellæg 7 years ago committed by Jørgen Sverre Lien Sellæg
parent
commit
63c822c887
  1. 1
      CMakeLists.txt
  2. 1
      src/CMakeLists.txt
  3. 9
      src/config.cpp
  4. 7
      src/config.hpp
  5. 5
      src/files.hpp
  6. 13
      src/juci.cpp
  7. 13
      src/juci.hpp
  8. 55
      src/plugins.h
  9. 3
      src/python_bind.h
  10. 31
      src/python_interpreter.cc
  11. 13
      src/python_interpreter.h
  12. 62
      src/python_module.h
  13. 18
      src/window.cpp
  14. 11
      src/window.hpp
  15. 1
      tests/CMakeLists.txt

1
CMakeLists.txt

@ -173,6 +173,7 @@ include_directories(
${LIBGIT2_INCLUDE_DIRS} ${LIBGIT2_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}
${PYGOBJECT_INCLUDE_DIRS} ${PYGOBJECT_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/lib/pybind11/include
) )
add_subdirectory("src") add_subdirectory("src")

1
src/CMakeLists.txt

@ -54,6 +54,7 @@ set(JUCI_SOURCES
selection_dialog.cc selection_dialog.cc
tooltips.cc tooltips.cc
window.cc window.cc
python_interpreter.cc
) )
if(APPLE) if(APPLE)

9
src/config.cpp

@ -3,6 +3,7 @@
#include "filesystem.hpp" #include "filesystem.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include <algorithm> #include <algorithm>
#include <boost/algorithm/string.hpp>
#include <exception> #include <exception>
#include <iostream> #include <iostream>
@ -212,4 +213,12 @@ void Config::read(const boost::property_tree::ptree &cfg) {
log.libclang = cfg.get<bool>("log.libclang"); log.libclang = cfg.get<bool>("log.libclang");
log.language_server = cfg.get<bool>("log.language_server"); log.language_server = cfg.get<bool>("log.language_server");
auto plugins_path = cfg.get<std::string>("plugins.path");
boost::replace_all(plugins_path, "<juci_home_directory>", home_juci_path.string());
plugins.path = plugins_path;
plugins.enabled = cfg.get<bool>("plugins.enabled");
if (plugins.enabled) {
std::cout << "Plugins enabled" << std::endl;
}
} }

7
src/config.hpp

@ -113,6 +113,12 @@ public:
bool language_server; bool language_server;
}; };
class Plugins {
public:
bool enabled;
std::string path;
};
private: private:
Config(); Config();
@ -131,6 +137,7 @@ public:
Project project; Project project;
Source source; Source source;
Log log; Log log;
Plugins plugins;
boost::filesystem::path home_path; boost::filesystem::path home_path;
boost::filesystem::path home_juci_path; boost::filesystem::path home_juci_path;

5
src/files.hpp

@ -233,6 +233,11 @@ const std::string default_config_file = R"RAW({
"libclang_comment": "Outputs diagnostics for new C/C++ buffers", "libclang_comment": "Outputs diagnostics for new C/C++ buffers",
"libclang": false, "libclang": false,
"language_server": false "language_server": false
},
"plugins": {
"enabled": true,
"path_comment": "Directory where plugins are loaded from.",
"path": "<juci_home_directory>/plugins"
} }
} }
)RAW"; )RAW";

13
src/juci.cpp

@ -55,12 +55,11 @@ int Application::on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine>
void Application::on_activate() { void Application::on_activate() {
std::vector<std::pair<int, int>> file_offsets; std::vector<std::pair<int, int>> file_offsets;
std::string current_file; std::string current_file;
Window::get().load_session(directories, files, file_offsets, current_file, directories.empty() && files.empty()); window.init();
window.load_session(directories, files, file_offsets, current_file, directories.empty() && files.empty());
Window::get().add_widgets(); window.add_widgets();
add_window(window);
add_window(Window::get()); window.show();
Window::get().show();
bool first_directory = true; bool first_directory = true;
for(auto &directory : directories) { for(auto &directory : directories) {
@ -132,7 +131,7 @@ void Application::on_startup() {
} }
} }
Application::Application() : Gtk::Application("no.sout.juci", Gio::APPLICATION_NON_UNIQUE | Gio::APPLICATION_HANDLES_COMMAND_LINE) { Application::Application() : Gtk::Application("no.sout.juci", Gio::APPLICATION_NON_UNIQUE | Gio::APPLICATION_HANDLES_COMMAND_LINE), window(plugins) {
Glib::set_application_name("juCi++"); Glib::set_application_name("juCi++");
//Gtk::MessageDialog without buttons caused text to be selected, this prevents that //Gtk::MessageDialog without buttons caused text to be selected, this prevents that

13
src/juci.hpp

@ -1,4 +1,10 @@
#pragma once #pragma once
#include <boost/filesystem.hpp>
#include <gtkmm.h>
#include "window.h"
#ifdef JUCI_ENABLE_PLUGINS
#include "plugins.h"
#endif
/** /**
\mainpage \mainpage
juCi++ is a lightweight C++ IDE written in C++ juCi++ is a lightweight C++ IDE written in C++
@ -25,9 +31,6 @@
[juCi++] --> [tiny-process-library] : use [juCi++] --> [tiny-process-library] : use
\enduml \enduml
*/ */
#include <boost/filesystem.hpp>
#include <gtkmm.h>
class Application : public Gtk::Application { class Application : public Gtk::Application {
public: public:
Application(); Application();
@ -39,4 +42,8 @@ private:
std::vector<boost::filesystem::path> directories; std::vector<boost::filesystem::path> directories;
std::vector<std::pair<boost::filesystem::path, size_t>> files; std::vector<std::pair<boost::filesystem::path, size_t>> files;
std::vector<std::string> errors; std::vector<std::string> errors;
Window window;
#ifdef JUCI_ENABLE_PLUGINS
Plugins plugins;
#endif
}; };

55
src/plugins.h

@ -0,0 +1,55 @@
#pragma once
#include "config.h"
#include "menu.h"
#include "python_interpreter.h"
#include "python_module.h"
#include <pybind11/embed.h>
#include <pybind11/functional.h>
class __attribute__((visibility("default")))
Plugins {
public:
Plugins() : jucipp_module("Jucipp", Module::init_jucipp_module) {
auto &config = Config::get();
config.load();
auto sys = py::module::import("sys");
sys.attr("path").cast<py::list>().append(config.plugins.path);
// sys.attr("excepthook") = py::cpp_function([](py::object type, py::object value, py::object traceback) {
// Terminal::get().print("const std::string &message\n");
// });
}
void load() {
boost::filesystem::directory_iterator end_it;
for(boost::filesystem::directory_iterator it(Config::get().plugins.path); it != end_it; it++) {
auto module_name = it->path().stem().string();
if(module_name.empty())
continue;
const auto is_directory = boost::filesystem::is_directory(it->path());
const auto has_py_extension = it->path().extension() == ".py";
const auto is_pycache = module_name == "__pycache__";
if((is_directory && !is_pycache) || has_py_extension) {
try {
auto module = interpreter.add_module(module_name);
if (module) {
Terminal::get().print("Reloading plugin ´" + module_name + "´\n");
interpreter.reload_module(module);
} else {
Terminal::get().print("Loading plugin ´" + module_name + "´\n");
py::module::import(module_name.c_str());
}
}
catch(py::error_already_set &error) {
Terminal::get().print("Error loading plugin `" + module_name + "`:\n" + error.what() + "\n");
}
}
if(interpreter.error())
std::cerr << py::error_already_set().what() << std::endl;
}
}
private:
py::detail::embedded_module jucipp_module;
Python::Interpreter interpreter;
};

3
src/python_bind.h

@ -0,0 +1,3 @@
#pragma once
#include <pybind11/pybind11.h>
namespace py = pybind11;

31
src/python_interpreter.cc

@ -0,0 +1,31 @@
#include "python_interpreter.h"
pybind11::module Python::Interpreter::add_module(const std::string &module_name) {
return pybind11::reinterpret_borrow<pybind11::module>(PyImport_AddModule(module_name.c_str()));
}
pybind11::object Python::Interpreter::error() {
return pybind11::reinterpret_borrow<pybind11::object>(PyErr_Occurred());
}
pybind11::module Python::Interpreter::reload_module(pybind11::module &module) {
auto reload = pybind11::reinterpret_steal<pybind11::module>(PyImport_ReloadModule(module.ptr()));
if(!reload) {
throw pybind11::error_already_set();
}
return reload;
}
#include <pybind11/embed.h>
#include <iostream>
Python::Interpreter::~Interpreter() {
if (error()){
std::cout << py::error_already_set().what() << std::endl;
}
py::finalize_interpreter();
}
Python::Interpreter::Interpreter() {
py::initialize_interpreter();
}

13
src/python_interpreter.h

@ -0,0 +1,13 @@
#pragma once
#include "python_bind.h"
namespace Python {
class Interpreter {
public:
pybind11::module static add_module(const std::string &module_name);
pybind11::module static reload_module(pybind11::module &module);
pybind11::object static error();
~Interpreter();
Interpreter();
};
}; // namespace Python

62
src/python_module.h

@ -0,0 +1,62 @@
#pragma once
#include "python_bind.h"
#include "terminal.h"
#include <boost/filesystem.hpp>
#include <pybind11/stl.h>
namespace pybind11 {
namespace detail {
template <>
struct type_caster<boost::filesystem::path> {
public:
PYBIND11_TYPE_CASTER(boost::filesystem::path, _("str"));
bool load(handle src, bool) {
const std::string path = pybind11::str(src);
value = path;
return !PyErr_Occurred();
}
static handle cast(boost::filesystem::path src, return_value_policy, handle) {
return pybind11::str(src.string());
}
};
}
}
class Module {
static void init_terminal_module(pybind11::module &api) {
py::class_<Terminal, std::unique_ptr<Terminal, py::nodelete>>(api, "Terminal")
.def(py::init([]() { return &(Terminal::get()); }))
.def("process", (int (Terminal::*)(const std::string &, const boost::filesystem::path &, bool)) & Terminal::process,
py::arg("command"),
py::arg("path") = "",
py::arg("use_pipes") = false)
.def("async_process", (void (Terminal::*)(const std::string &, const boost::filesystem::path &, const std::function<void(int)> &, bool)) & Terminal::async_process,
py::arg("command"),
py::arg("path") = "",
py::arg("callback") = nullptr,
py::arg("quiet") = false)
.def("kill_last_async_process", &Terminal::kill_last_async_process,
py::arg("force") = false)
.def("kill_async_processes", &Terminal::kill_async_processes,
py::arg("force") = false)
.def("print", &Terminal::print,
py::arg("message"),
py::arg("bold") = false)
.def("async_print", (void (Terminal::*)(const std::string &, bool)) & Terminal::async_print,
py::arg("message"),
py::arg("bold") = false)
.def("async_print", (void (Terminal::*)(size_t, const std::string &)) & Terminal::async_print,
py::arg("line_nr"),
py::arg("message"))
.def("configure", &Terminal::configure)
.def("clear", &Terminal::clear);
};
public:
static auto init_jucipp_module() {
auto api = py::module("Jucipp", "API");
Module::init_terminal_module(api);
return api.ptr();
};
};

18
src/window.cpp

@ -16,24 +16,24 @@
#include "selection_dialog.hpp" #include "selection_dialog.hpp"
#include "terminal.hpp" #include "terminal.hpp"
Window::Window() { Window::Window(Plugins &i) : plugins(i) {
Gsv::init(); Gsv::init();
set_title("juCi++"); set_title("juCi++");
get_style_context()->add_class("juci_window"); get_style_context()->add_class("juci_window");
set_events(Gdk::POINTER_MOTION_MASK | Gdk::FOCUS_CHANGE_MASK | Gdk::SCROLL_MASK | Gdk::LEAVE_NOTIFY_MASK); set_events(Gdk::POINTER_MOTION_MASK | Gdk::FOCUS_CHANGE_MASK | Gdk::SCROLL_MASK | Gdk::LEAVE_NOTIFY_MASK);
}
void Window::init() {
auto visual = get_screen()->get_rgba_visual(); auto visual = get_screen()->get_rgba_visual();
if(visual) if(visual)
gtk_widget_set_visual(reinterpret_cast<GtkWidget *>(gobj()), visual->gobj()); gtk_widget_set_visual(reinterpret_cast<GtkWidget *>(gobj()), visual->gobj());
auto provider = Gtk::CssProvider::create(); auto provider = Gtk::CssProvider::create();
auto screen = get_screen(); auto screen = get_screen();
std::string border_radius_style; std::string border_radius_style;
if(screen->get_rgba_visual()) if(screen->get_rgba_visual())
border_radius_style = "border-radius: 5px; "; border_radius_style = "border-radius: 5px; ";
#if GTK_VERSION_GE(3, 20) #if GTK_VERSION_GE(3, 20)
std::string notebook_style(".juci_notebook tab {border-radius: 5px 5px 0 0; padding: 0 4px; margin: 0;}"); n std::string notebook_style(".juci_notebook tab {border-radius: 5px 5px 0 0; padding: 0 4px; margin: 0;}");
#else #else
std::string notebook_style(".juci_notebook {-GtkNotebook-tab-overlap: 0px;} .juci_notebook tab {border-radius: 5px 5px 0 0; padding: 4px 4px;}"); std::string notebook_style(".juci_notebook {-GtkNotebook-tab-overlap: 0px;} .juci_notebook tab {border-radius: 5px 5px 0 0; padding: 4px 4px;}");
#endif #endif
@ -160,7 +160,10 @@ Window::Window() {
about.set_comments("This is an open source IDE with high-end features to make your programming experience juicy"); about.set_comments("This is an open source IDE with high-end features to make your programming experience juicy");
about.set_license_type(Gtk::License::LICENSE_MIT_X11); about.set_license_type(Gtk::License::LICENSE_MIT_X11);
about.set_transient_for(*this); about.set_transient_for(*this);
} // Window constructor if(Config::get().plugins.enabled) {
plugins.load();
}
}
void Window::configure() { void Window::configure() {
Config::get().load(); Config::get().load();
@ -274,8 +277,9 @@ void Window::set_menu_actions() {
auto &menu = Menu::get(); auto &menu = Menu::get();
menu.add_action("about", [this]() { menu.add_action("about", [this]() {
about.show(); plugins.load();
about.present(); // about.show();
// about.present();
}); });
menu.add_action("preferences", []() { menu.add_action("preferences", []() {
Notebook::get().open(Config::get().home_juci_path / "config" / "config.json"); Notebook::get().open(Config::get().home_juci_path / "config" / "config.json");

11
src/window.hpp

@ -3,24 +3,21 @@
#include <atomic> #include <atomic>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <gtkmm.h> #include <gtkmm.h>
#include "plugins.h"
class Window : public Gtk::ApplicationWindow { class Window : public Gtk::ApplicationWindow {
Window();
public: public:
static Window &get() { Window(Plugins &p);
static Window singleton;
return singleton;
}
void add_widgets(); void add_widgets();
void save_session(); void save_session();
void load_session(std::vector<boost::filesystem::path> &directories, std::vector<std::pair<boost::filesystem::path, size_t>> &files, std::vector<std::pair<int, int>> &file_offsets, std::string &current_file, bool read_directories_and_files); void load_session(std::vector<boost::filesystem::path> &directories, std::vector<std::pair<boost::filesystem::path, size_t>> &files, std::vector<std::pair<int, int>> &file_offsets, std::string &current_file, bool read_directories_and_files);
void init();
protected: protected:
bool on_key_press_event(GdkEventKey *event) override; bool on_key_press_event(GdkEventKey *event) override;
bool on_delete_event(GdkEventAny *event) override; bool on_delete_event(GdkEventAny *event) override;
private: private:
Plugins& plugins;
Gtk::AboutDialog about; Gtk::AboutDialog about;
Gtk::ScrolledWindow directories_scrolled_window, terminal_scrolled_window; Gtk::ScrolledWindow directories_scrolled_window, terminal_scrolled_window;

1
tests/CMakeLists.txt

@ -9,7 +9,6 @@ include_directories(
${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/libclangmm/src ${CMAKE_SOURCE_DIR}/libclangmm/src
${CMAKE_SOURCE_DIR}/tiny-process-library ${CMAKE_SOURCE_DIR}/tiny-process-library
${CMAKE_SOURCE_DIR}/pybind11
) )
add_library(test_stubs OBJECT add_library(test_stubs OBJECT

Loading…
Cancel
Save