Compare commits

...

69 Commits

Author SHA1 Message Date
Jørgen Lien Sellæg f19a36d91f Merge branch 'python-refactor' into 'master' 2 months ago
eidheim 7715265c68 Updated clangd language server instructions 2 months ago
eidheim bea59efc1c Language client: improvement to autocomplete after arrow and for servers that include seperation characters to the completion items 2 months ago
eidheim 210a8b35b2 Fixes #464: fixed language client autocomplete for clangd server 2 months ago
Jørgen Lien Sellæg a2e4868893 Merge branch 'master' into python-refactor 7 months ago
Jørgen Lien Sellæg 1223a72908 Merge branch 'master' into python-refactor 7 months ago
Jørgen Lien Sellæg 4e6c2c2d95 bind up tiny process lib and add test should also fix terminal warnings 5 years ago
Jørgen Lien Sellæg 1a11a5736b update lldb test 5 years ago
Jørgen Lien Sellæg 92679161f4 update bindings of ctags, but disable test of it for now 5 years ago
Jørgen Lien Sellæg 9ad7c21407 update config test to not fail. Needs updating according to new config options 5 years ago
Jørgen Lien Sellæg 8a7dd41b73 initialize gtksourceviewmm since terminal uses that now and calls to terminal will cause an null value of the sourcebuffer 5 years ago
Jørgen Lien Sellæg 5139d6e127 add new line to terminal call 5 years ago
Jørgen Lien Sellæg 5b20c32754 remove enabled print 5 years ago
Jørgen Lien Sellæg 32a70dd5b0 add basic docs 5 years ago
Jørgen Lien Sellæg 19c3e90cca hook into juci after window has been loaded 5 years ago
Jørgen Lien Sellæg c7e67a70b4 handle creation of plugins directory if it doesn't exist, remove calls to terminal as it might not have been loaded yet at this point 5 years ago
Jørgen Lien Sellæg a994405c71 load config and plugins early 5 years ago
Jørgen Lien Sellæg ad60559d97 load plugins before application, we might want to do changes before the application starts 5 years ago
Jørgen Lien Sellæg 69d5297d88 bump version number so that config file updates 5 years ago
Jørgen Lien Sellæg c72aa8cdc4 Compiling again 5 years ago
Jørgen Lien Sellæg 03cf0990f2 fix merge mistakes in tests 5 years ago
Jørgen Lien Sellæg ecc2a4d642 remove duplicate include 5 years ago
Jørgen Lien Sellæg c5d40c1d0e Add files 5 years ago
Jørgen Lien Sellæg ff0c52cc45 add debug lldb test and fix bug with functional header missing 5 years ago
Jørgen Lien Sellæg 86e6a12180 add ctags test 5 years ago
Jørgen Lien Sellæg f8a607a8ab fix some formatting and check for errors in suite 5 years ago
Jørgen Lien Sellæg c3b4f21735 fix reference count bug in type caster 5 years ago
Jørgen Lien Sellæg ca7d9830b2 extract common method 5 years ago
Jørgen Lien Sellæg be76f94374 small cleanup 5 years ago
Jørgen Lien Sellæg b736400807 fix remaining tests on windows 5 years ago
Jørgen Lien Sellæg cdf8b4548f fix tests in compile_commands for windows and fix a bug in compile_commands.cc 5 years ago
Jørgen Lien Sellæg c9055243c3 organize imports 5 years ago
Jørgen Lien Sellæg ce37b55d0b move terminal target to top 5 years ago
Jørgen Lien Sellæg 6afde6fe37 test all config options and fix two bugs in module 5 years ago
Jørgen Lien Sellæg 6d04f7cff6 add default contstructor for all subclasses of config 5 years ago
Jørgen Lien Sellæg 333fe0aa81 fix assignment bug and add default constructor 5 years ago
Jørgen Lien Sellæg 53f1c755b7 add basic config test 5 years ago
Jørgen Lien Sellæg 3cf14b7841 add tests for compile commands and fix some bugs 5 years ago
Jørgen Lien Sellæg f8127a76d2 move cmake project one level up 5 years ago
Jørgen Lien Sellæg 3368b4ffc2 test cmake bindings 5 years ago
Jørgen Lien Sellæg 95f15ef264 move python interpreter test to python module test 5 years ago
Jørgen Lien Sellæg 7778752da0 disable lldb tests on debian due to an bug in python 5 years ago
Jørgen Lien Sellæg 76e8e5dc54 add paths for windows in test environment 5 years ago
Jørgen Lien Sellæg 7307307846 compile target with files instead of linking 5 years ago
Jørgen Lien Sellæg 05b5b57997 remove references to interpreter 5 years ago
Jørgen Lien Sellæg c519c9dd94 remove interpreter wrapping 5 years ago
Jørgen Lien Sellæg 32b5df1060 remove lldb tests for build check 5 years ago
Jørgen Lien Sellæg 00e1282008 add very basic CMake test 5 years ago
Jørgen Lien Sellæg 91ba2234fe refactor test suite to make room for tests of all bindings 5 years ago
Jørgen Lien Sellæg b898db575f make sure boost filesystem is type casted consistlty 5 years ago
Jørgen Lien Sellæg 8061731851 fix bug where two classes got the same name 5 years ago
Jørgen Lien Sellæg 57a42c8f9e bind some of menu 5 years ago
Jørgen Lien Sellæg 0f4c4a7ce7 fix tests after moving pybind code 5 years ago
Jørgen Lien Sellæg ba24942106 bind git 5 years ago
Jørgen Lien Sellæg e08504e250 bind cppreference 5 years ago
Jørgen Lien Sellæg f583644a89 move binding code to respective files 5 years ago
Jørgen Lien Sellæg 891edadc48 bind dialogs module 5 years ago
Jørgen Lien Sellæg 8e6eaec757 wrap with string to ensure windows support 5 years ago
Jørgen Lien Sellæg 6ae2d94dd1 partially bind lldb debugger 5 years ago
Jørgen Lien Sellæg 3879f9b0d8 bind ctags class 5 years ago
Jørgen Lien Sellæg ee8226d224 small cleanup 5 years ago
Jørgen Lien Sellæg e55c075981 bind compile commands class 5 years ago
Jørgen Lien Sellæg 4763f50e25 bind cmake class 5 years ago
Jørgen Lien Sellæg bcd3d0eb96 bind config classes 5 years ago
Jørgen Lien Sellæg 22bf01be44 move plugin implementation to own file 5 years ago
Jørgen Lien Sellæg 1244f1a32f fix python home and path on windows 5 years ago
Jørgen Lien Sellæg 0be5d31d13 add test for terminal bindings 5 years ago
Jørgen Lien Sellæg 63c822c887 bind terminal and add type caster for filesystem path 5 years ago
Jørgen Lien Sellæg d7b60adc62 find and include python plugin dependencies 5 years ago
  1. 2
      .appveyor.yml
  2. 3
      .gitmodules
  3. 63
      CMakeLists.txt
  4. 2
      docs/install.md
  5. 9
      docs/language_servers.md
  6. 14
      docs/plugins.md
  7. 1
      lib/pybind11
  8. 21
      src/CMakeLists.txt
  9. 17
      src/cmake.cpp
  10. 4
      src/cmake.hpp
  11. 27
      src/compile_commands.cpp
  12. 5
      src/compile_commands.hpp
  13. 6
      src/config.cpp
  14. 9
      src/config.hpp
  15. 116
      src/config_module.cc
  16. 36
      src/ctags.cpp
  17. 2
      src/ctags.hpp
  18. 72
      src/debug_lldb.cpp
  19. 3
      src/debug_lldb.hpp
  20. 17
      src/dialog.cpp
  21. 2
      src/dialog.hpp
  22. 9
      src/dispatcher.cpp
  23. 3
      src/dispatcher.hpp
  24. 5
      src/documentation.cpp
  25. 2
      src/documentation.hpp
  26. 438
      src/files.hpp
  27. 63
      src/git.cpp
  28. 2
      src/git.hpp
  29. 14
      src/juci.cpp
  30. 9
      src/juci.hpp
  31. 14
      src/menu.cpp
  32. 2
      src/menu.hpp
  33. 79
      src/plugins.cc
  34. 18
      src/plugins.h
  35. 3
      src/python_bind.h
  36. 29
      src/python_module.cc
  37. 7
      src/python_module.h
  38. 28
      src/python_type_casters.h
  39. 2
      src/source_clang.cpp
  40. 14
      src/source_language_protocol.cpp
  41. 31
      src/terminal.cpp
  42. 3
      src/terminal.hpp
  43. 27
      src/tiny_process_module.cpp
  44. 6
      src/tiny_process_module.hpp
  45. 14
      src/window.cpp
  46. 5
      src/window.hpp
  47. 41
      tests/CMakeLists.txt
  48. 34
      tests/python_bindings/CMakeLists.txt
  49. 21
      tests/python_bindings/CMake_tests/cmake_test.cc
  50. 14
      tests/python_bindings/CMake_tests/cmake_test.py
  51. 27
      tests/python_bindings/CompileCommands_tests/compile_commands_test.cc
  52. 37
      tests/python_bindings/CompileCommands_tests/compile_commands_test.py
  53. 90
      tests/python_bindings/Config_tests/config_test.cc
  54. 87
      tests/python_bindings/Config_tests/config_test.py
  55. 39
      tests/python_bindings/Ctags_tests/ctags_test.cc
  56. 27
      tests/python_bindings/Ctags_tests/ctags_test.py
  57. 27
      tests/python_bindings/Debug_lldb_tests/debug_lldb_test.cc
  58. 21
      tests/python_bindings/Debug_lldb_tests/debug_lldb_test.py
  59. 1
      tests/python_bindings/PythonModule_tests/exception_test.py
  60. 25
      tests/python_bindings/PythonModule_tests/python_module_test.cc
  61. 1
      tests/python_bindings/PythonModule_tests/python_module_test.py
  62. 0
      tests/python_bindings/Terminal_tests/ls/hello_world.txt
  63. 51
      tests/python_bindings/Terminal_tests/terminal_test.cc
  64. 26
      tests/python_bindings/Terminal_tests/terminal_test.py
  65. 3
      tests/python_bindings/cmake_project/CMakeLists.txt
  66. 1
      tests/python_bindings/cmake_project/main.cpp
  67. 5
      tests/python_bindings/jucipp_test.py
  68. 26
      tests/python_bindings/test_suite.cc
  69. 17
      tests/python_bindings/test_suite.h
  70. 2
      tests/stubs/dialog.cpp
  71. 17
      tests/stubs/plugins.cc

2
.appveyor.yml

@ -12,7 +12,7 @@ cache:
before_build: before_build:
- git submodule update --init --recursive - git submodule update --init --recursive
# - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syyuu" # - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syyuu"
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm --needed -S make mingw-w64-x86_64-{cmake,toolchain,clang,gtkmm3,gtksourceviewmm3,boost,aspell,aspell-en,libgit2,universal-ctags-git,libffi}" - C:\msys64\usr\bin\bash -lc "pacman --noconfirm --needed -S make mingw-w64-x86_64-{cmake,toolchain,clang,gtkmm3,gtksourceviewmm3,boost,aspell,aspell-en,libgit2,universal-ctags-git,libffi,pygobject-devel}"
build_script: build_script:
- C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER && mkdir build && cd build && cmake -G\"MSYS Makefiles\" -DCMAKE_INSTALL_PREFIX=/mingw64 -DBUILD_TESTING=1 .. && make -j$(nproc) && make test" - C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER && mkdir build && cd build && cmake -G\"MSYS Makefiles\" -DCMAKE_INSTALL_PREFIX=/mingw64 -DBUILD_TESTING=1 .. && make -j$(nproc) && make test"

3
.gitmodules vendored

@ -4,3 +4,6 @@
[submodule "lib/libclangmm"] [submodule "lib/libclangmm"]
path = lib/libclangmm path = lib/libclangmm
url = https://gitlab.com/cppit/libclangmm url = https://gitlab.com/cppit/libclangmm
[submodule "lib/pybind11"]
path = lib/pybind11
url = https://github.com/pybind/pybind11

63
CMakeLists.txt

@ -28,7 +28,7 @@ include(CPack)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
add_compile_options(-pthread -Wall -Wextra -Wno-unused-parameter -Wno-deprecated-declarations) add_compile_options(-pthread -Wall -Wextra -Wno-unused-parameter -Wno-deprecated-declarations -fvisibility=hidden)
add_definitions(-DJUCI_VERSION="${JUCI_VERSION}") add_definitions(-DJUCI_VERSION="${JUCI_VERSION}")
if(CMAKE_BUILD_TYPE STREQUAL "") if(CMAKE_BUILD_TYPE STREQUAL "")
add_compile_options(-O3) add_compile_options(-O3)
@ -78,13 +78,69 @@ option(LIBCLANG_PATH "Use custom path for libclang")
option(LIBLLDB_PATH "Use custom path for liblldb") option(LIBLLDB_PATH "Use custom path for liblldb")
option(FLATPAK_SANDBOX "Runs from within a flatpak sandbox") option(FLATPAK_SANDBOX "Runs from within a flatpak sandbox")
if(${CMAKE_VERSION} VERSION_GREATER 3.11.4)
if (${CMAKE_HOST_WIN32})
if("$ENV{APPVEYOR}" STREQUAL "True")
set(MINGW_PATH "C:/msys64/mingw64")
else()
set(MINGW_PATH "$ENV{MSYSTEM_PREFIX}")
endif()
set(MINGW_PYTHON_VERSION 3.7)
set(MINGW_LIBRARY_DIR "${MINGW_PATH}/lib")
set(MINGW_INCLUDE_DIR "${MINGW_PATH}/include")
find_file(Python3_LIBRARIES "libpython${MINGW_PYTHON_VERSION}m.dll.a" HINTS ${MINGW_LIBRARY_DIR} NO_DEFAULT_PATH)
find_path(Python3_INCLUDE_DIRS "Python.h" HINTS "${MINGW_INCLUDE_DIR}/python${MINGW_PYTHON_VERSION}m" NO_DEFAULT_PATH)
if (EXISTS ${Python3_LIBRARIES} AND EXISTS ${Python3_INCLUDE_DIRS})
set(Python3_FOUND True)
set(Python3_LIBRARY_DIRS ${MINGW_LIBRARY_DIR})
set(Python3_VERSION_MAJOR 3)
set(Python3_VERSION_MINOR 7)
endif()
else()
find_package(Python3 COMPONENTS Development)
endif()
else()
find_package(PythonLibs 3)
message(${PYTHONLIBS_VERSION_STRING})
endif()
if(${Python3_FOUND})
if(${CMAKE_HOST_WIN32})
set(PYTHON_MODULE_EXTENSION True)
endif()
set(PYTHONLIBS_FOUND ${Python3_FOUND})
set(PYTHON_LIBRARIES ${Python3_LIBRARIES})
set(PYTHON_INCLUDE_DIRS ${Python3_INCLUDE_DIRS})
set(PYTHON_LIBRARY_DIR ${Python3_LIBRARY_DIRS}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR})
endif()
include(FindPkgConfig)
set(PYGOBJECT_FOUND False)
if(${PYTHONLIBS_FOUND})
add_subdirectory(lib/pybind11)
pkg_check_modules(PYGOBJECT pygobject-3.0)
if("${PYGOBJECT_FOUND}" STREQUAL "")
set(PYGOBJECT_FOUND False)
endif()
endif()
if(${PYGOBJECT_FOUND})
add_definitions(-DPYTHON_HOME_DIR=L"${PYTHON_LIBRARY_DIR}")
endif()
set(BUILD_TESTING_SAVED ${BUILD_TESTING})
set(BUILD_TESTING OFF CACHE BOOL "Disable sub-project tests" FORCE)
set(BUILD_TESTING ${BUILD_TESTING_SAVED} CACHE BOOL "Set to previous value" FORCE)
if(POLICY CMP0167) if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW) cmake_policy(SET CMP0167 NEW)
endif() endif()
find_package(Boost 1.54 COMPONENTS REQUIRED filesystem serialization) find_package(Boost 1.54 COMPONENTS REQUIRED filesystem serialization)
find_package(ASPELL REQUIRED) find_package(ASPELL REQUIRED)
include(FindPkgConfig)
pkg_check_modules(GTKMM gtkmm-3.0 REQUIRED) pkg_check_modules(GTKMM gtkmm-3.0 REQUIRED)
pkg_check_modules(GTKSVMM gtksourceviewmm-3.0) pkg_check_modules(GTKSVMM gtksourceviewmm-3.0)
if(NOT GTKSVMM_FOUND) if(NOT GTKSVMM_FOUND)
@ -155,6 +211,9 @@ include_directories(
${LIBCLANG_INCLUDE_DIRS} ${LIBCLANG_INCLUDE_DIRS}
${ASPELL_INCLUDE_DIR} ${ASPELL_INCLUDE_DIR}
${LIBGIT2_INCLUDE_DIRS} ${LIBGIT2_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
${PYGOBJECT_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/lib/pybind11/include
${CMAKE_SOURCE_DIR}/lib/json/include ${CMAKE_SOURCE_DIR}/lib/json/include
) )

2
docs/install.md

@ -207,7 +207,7 @@ make install
Install dependencies (replace `x86_64` with `i686` for 32-bit MSYS2 installs): Install dependencies (replace `x86_64` with `i686` for 32-bit MSYS2 installs):
```sh ```sh
pacman -S git mingw-w64-x86_64-cmake make mingw-w64-x86_64-toolchain mingw-w64-x86_64-clang mingw-w64-x86_64-gtkmm3 mingw-w64-x86_64-gtksourceviewmm3 mingw-w64-x86_64-boost mingw-w64-x86_64-aspell mingw-w64-x86_64-aspell-en mingw-w64-x86_64-libgit2 mingw-w64-x86_64-universal-ctags-git pacman -S git make mingw-w64-x86_64-{cmake,toolchain,clang,gtkmm3,gtksourceviewmm3,boost,aspell,aspell-en,libgit2,universal-ctags-git,pygobject-devel}
``` ```
Note that juCi++ must be built and run in a MinGW Shell (for instance MinGW-w64 Win64 Shell). Note that juCi++ must be built and run in a MinGW Shell (for instance MinGW-w64 Win64 Shell).

9
docs/language_servers.md

@ -182,12 +182,13 @@ chmod 755 /usr/local/bin/glsl-language-server
**Note that we recommend using the builtin C/C++ support in juCi++ instead.** **Note that we recommend using the builtin C/C++ support in juCi++ instead.**
- Prerequisites: - Prerequisites:
- A language server installed on your system, for example clangd or ccls - A C/C++ language server installed on your system, for example clangd
Create symbolic link to enable language server in juCi++. For example, if you have the clangd Create executable to enable server in juCi++:
installed and available on path:
```sh ```sh
# Usually as root: # Usually as root:
ln -s `which clangd` /usr/local/bin/clang-language-server echo '#!/bin/sh
clangd' > /usr/local/bin/clang-language-server
chmod 755 /usr/local/bin/clang-language-server
``` ```

14
docs/plugins.md

@ -0,0 +1,14 @@
# Plugins
## Getting started
Plugins are stored in .juci/plugins
### Basic hello world
from Jucipp import Terminal
print("Hello, world! From before the application is loaded")
def init_hook():
t = Terminal()
t.print("Hello, world! From after the application is started.\n")

1
lib/pybind11

@ -0,0 +1 @@
Subproject commit 9a19306fbf30642ca331d0ec88e7da54a96860f9

21
src/CMakeLists.txt

@ -15,6 +15,7 @@ set(JUCI_SHARED_FILES
menu.cpp menu.cpp
meson.cpp meson.cpp
project_build.cpp project_build.cpp
plugins.cc
snippets.cpp snippets.cpp
source.cpp source.cpp
source_base.cpp source_base.cpp
@ -24,9 +25,12 @@ set(JUCI_SHARED_FILES
source_language_protocol.cpp source_language_protocol.cpp
source_spellcheck.cpp source_spellcheck.cpp
terminal.cpp terminal.cpp
tiny_process_module.cpp
tooltips.cpp tooltips.cpp
usages_clang.cpp usages_clang.cpp
utility.cpp utility.cpp
python_module.cc
config_module.cc
workers.cpp workers.cpp
) )
if(LIBLLDB_FOUND) if(LIBLLDB_FOUND)
@ -54,14 +58,25 @@ set(JUCI_FILES
notebook.cpp notebook.cpp
project.cpp project.cpp
selection_dialog.cpp selection_dialog.cpp
tooltips.cpp
window.cpp window.cpp
plugins.cc
) )
if(APPLE) if(APPLE)
list(APPEND JUCI_FILES window_macos.m) list(APPEND JUCI_SOURCES window_macos.m)
endif()
set(JUCI_TARGET_LIBRARIES
juci_shared
)
if(${PYTHONLIBS_FOUND})
list(APPEND JUCI_TARGET_LIBRARIES pybind11 ${PYTHON_LIBRARIES} ${PYGOBJECT_LIBRARIES})
endif() endif()
add_executable(juci ${JUCI_FILES}) add_executable(juci ${JUCI_SOURCES})
target_link_libraries(juci juci_shared) target_link_libraries(juci ${JUCI_TARGET_LIBRARIES})
if(APPLE) if(APPLE)
target_link_libraries(juci "-framework Foundation -framework AppKit") target_link_libraries(juci "-framework Foundation -framework AppKit")

17
src/cmake.cpp

@ -357,6 +357,23 @@ void CMake::parse_file(const std::string &src, std::map<std::string, std::list<s
} }
} }
void CMake::init_module(pybind11::module &api) {
py::class_<CMake>(api, "CMake")
.def(py::init<const boost::filesystem::path &>())
.def_readwrite("project_path", &CMake::project_path)
.def("update_default_build", &CMake::update_default_build,
py::arg("default_build_path"),
py::arg("force") = false)
.def("update_debug_build", &CMake::update_debug_build,
py::arg("debug_build_path"),
py::arg("force") = false)
.def("get_executable", &CMake::get_executable,
py::arg("build_path"),
py::arg("file_path"))
;
}
bool CMake::create_file_api_query(const boost::filesystem::path &build_path) { bool CMake::create_file_api_query(const boost::filesystem::path &build_path) {
auto query_directory = build_path / ".cmake" / "api" / "v1" / "query" / "client-jucipp"; auto query_directory = build_path / ".cmake" / "api" / "v1" / "query" / "client-jucipp";
auto query_file = query_directory / "query.json"; auto query_file = query_directory / "query.json";

4
src/cmake.hpp

@ -4,16 +4,16 @@
#include <list> #include <list>
#include <map> #include <map>
#include <vector> #include <vector>
#include "python_bind.h"
class CMake { class CMake {
public: public:
CMake(const boost::filesystem::path &path); CMake(const boost::filesystem::path &path);
boost::filesystem::path project_path; boost::filesystem::path project_path;
bool update_default_build(const boost::filesystem::path &default_build_path, bool force = false); bool update_default_build(const boost::filesystem::path &default_build_path, bool force = false);
bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force = false); bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force = false);
boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
static void init_module(py::module &api);
private: private:
std::vector<boost::filesystem::path> paths; std::vector<boost::filesystem::path> paths;

27
src/compile_commands.cpp

@ -1,6 +1,9 @@
#include "compile_commands.hpp" #include "compile_commands.hpp"
#include "clangmm.hpp" #include "clangmm.hpp"
#include "config.hpp" #include "config.hpp"
#include "python_type_casters.h"
#include <pybind11/stl.h>
#include "terminal.hpp"
#include "filesystem.hpp" #include "filesystem.hpp"
#include "json.hpp" #include "json.hpp"
#include "utility.hpp" #include "utility.hpp"
@ -207,3 +210,27 @@ bool CompileCommands::is_source(const boost::filesystem::path &path) {
else else
return false; return false;
} }
void CompileCommands::init_module(py::module &api) {
py::class_<CompileCommands> compile_commands(api, "CompileCommands");
py::class_<CompileCommands::Command>(compile_commands, "Command")
.def_readwrite("directory", &CompileCommands::Command::directory)
.def_readwrite("parameters", &CompileCommands::Command::parameters)
.def_readwrite("file", &CompileCommands::Command::file)
.def("parameter_values", &CompileCommands::Command::parameter_values,
py::arg("parameter_name"))
;
compile_commands
.def(py::init<const boost::filesystem::path &>())
.def_readwrite("commands", &CompileCommands::commands)
.def_static("get_arguments", &CompileCommands::get_arguments,
py::arg("build_path"),
py::arg("file_path"))
.def_static("is_header", &CompileCommands::is_header,
py::arg("path"))
.def_static("is_source", &CompileCommands::is_source,
py::arg("path"))
;
}

5
src/compile_commands.hpp

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "python_bind.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
@ -10,16 +11,14 @@ public:
boost::filesystem::path directory; boost::filesystem::path directory;
std::vector<std::string> arguments; std::vector<std::string> arguments;
boost::filesystem::path file; boost::filesystem::path file;
std::vector<std::string> get_argument_values(const std::string &argument_name) const; std::vector<std::string> get_argument_values(const std::string &argument_name) const;
}; };
CompileCommands(const boost::filesystem::path &build_path); CompileCommands(const boost::filesystem::path &build_path);
std::vector<Command> commands; std::vector<Command> commands;
/// Return arguments for the given file using libclangmm /// Return arguments for the given file using libclangmm
static std::vector<std::string> get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); static std::vector<std::string> get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
static bool is_header(const boost::filesystem::path &path); static bool is_header(const boost::filesystem::path &path);
static bool is_source(const boost::filesystem::path &path); static bool is_source(const boost::filesystem::path &path);
static void init_module(py::module &api);
}; };

6
src/config.cpp

@ -4,6 +4,7 @@
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <algorithm> #include <algorithm>
#include <boost/algorithm/string.hpp>
#include <exception> #include <exception>
#include <iostream> #include <iostream>
#include <thread> #include <thread>
@ -206,6 +207,11 @@ void Config::read(const JSON &cfg) {
terminal.clear_on_run_command = terminal_json.boolean("clear_on_run_command", JSON::ParseOptions::accept_string); terminal.clear_on_run_command = terminal_json.boolean("clear_on_run_command", JSON::ParseOptions::accept_string);
terminal.hide_entry_on_run_command = terminal_json.boolean("hide_entry_on_run_command", JSON::ParseOptions::accept_string); terminal.hide_entry_on_run_command = terminal_json.boolean("hide_entry_on_run_command", JSON::ParseOptions::accept_string);
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");
auto log_json = cfg.object("log"); auto log_json = cfg.object("log");
log.libclang = log_json.boolean("libclang", JSON::ParseOptions::accept_string); log.libclang = log_json.boolean("libclang", JSON::ParseOptions::accept_string);
log.language_server = log_json.boolean("language_server", JSON::ParseOptions::accept_string); log.language_server = log_json.boolean("language_server", JSON::ParseOptions::accept_string);

9
src/config.hpp

@ -6,6 +6,7 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "python_bind.h"
class Config { class Config {
public: public:
@ -118,6 +119,12 @@ public:
bool language_server; bool language_server;
}; };
class Plugins {
public:
bool enabled;
std::string path;
};
private: private:
Config(); Config();
@ -135,9 +142,11 @@ 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;
static void init_module(py::module &api);
private: private:
/// Used to dispatch Terminal outputs after juCi++ GUI setup and configuration /// Used to dispatch Terminal outputs after juCi++ GUI setup and configuration

116
src/config_module.cc

@ -0,0 +1,116 @@
#include "config.hpp"
#include "python_type_casters.h"
#include <pybind11/stl.h>
void Config::init_module(py::module &api) {
py::class_<Config, std::unique_ptr<Config, py::nodelete>> config(api, "Config");
py::class_<Config::Menu>(config, "Menu")
.def(py::init())
.def_readwrite("keys", &Config::Menu::keys)
;
py::class_<Config::Theme>(config, "Theme")
.def(py::init())
.def_readwrite("name", &Config::Theme::name)
.def_readwrite("variant", &Config::Theme::variant)
.def_readwrite("font", &Config::Theme::font)
;
py::class_<Config::Terminal>(config, "Terminal")
.def(py::init())
.def_readwrite("history_size", &Config::Terminal::history_size)
.def_readwrite("font", &Config::Terminal::font)
;
py::class_<Config::Project> project(config, "Project");
py::class_<Config::Project::CMake>(project, "CMake")
.def(py::init())
.def_readwrite("command", &Config::Project::CMake::command)
.def_readwrite("compile_command", &Config::Project::CMake::compile_command)
;
py::class_<Config::Project::Meson>(project, "Meson")
.def(py::init())
.def_readwrite("command", &Config::Project::Meson::command)
.def_readwrite("compile_command", &Config::Project::Meson::compile_command)
;
project
.def(py::init())
.def_readwrite("default_build_path", &Config::Project::default_build_path)
.def_readwrite("debug_build_path", &Config::Project::debug_build_path)
.def_readwrite("cmake", &Config::Project::cmake)
.def_readwrite("meson", &Config::Project::meson)
.def_readwrite("save_on_compile_or_run", &Config::Project::save_on_compile_or_run)
// .def_readwrite("clear_terminal_on_compile", &Config::Project::clear_terminal_on_compile)
.def_readwrite("ctags_command", &Config::Project::ctags_command)
.def_readwrite("python_command", &Config::Project::python_command)
;
py::class_<Config::Source> source(config, "Source");
py::class_<Config::Source::DocumentationSearch>(source, "DocumentationSearch")
.def(py::init())
.def_readwrite("separator", &Config::Source::DocumentationSearch::separator)
.def_readwrite("queries", &Config::Source::DocumentationSearch::queries)
;
source
.def(py::init())
.def_readwrite("style", &Config::Source::style)
.def_readwrite("font", &Config::Source::font)
.def_readwrite("spellcheck_language", &Config::Source::spellcheck_language)
.def_readwrite("cleanup_whitespace_characters", &Config::Source::cleanup_whitespace_characters)
.def_readwrite("show_whitespace_characters", &Config::Source::show_whitespace_characters)
.def_readwrite("format_style_on_save", &Config::Source::format_style_on_save)
.def_readwrite("format_style_on_save_if_style_file_found", &Config::Source::format_style_on_save_if_style_file_found)
.def_readwrite("smart_inserts", &Config::Source::smart_inserts)
.def_readwrite("show_map", &Config::Source::show_map)
.def_readwrite("map_font_size", &Config::Source::map_font_size)
.def_readwrite("show_git_diff", &Config::Source::show_git_diff)
.def_readwrite("show_background_pattern", &Config::Source::show_background_pattern)
.def_readwrite("show_right_margin", &Config::Source::show_right_margin)
.def_readwrite("right_margin_position", &Config::Source::right_margin_position)
.def_readwrite("auto_tab_char_and_size", &Config::Source::auto_tab_char_and_size)
.def_readwrite("default_tab_char", &Config::Source::default_tab_char)
.def_readwrite("default_tab_size", &Config::Source::default_tab_size)
.def_readwrite("tab_indents_line", &Config::Source::tab_indents_line)
// .def_readwrite("wrap_lines", &Config::Source::wrap_lines)
.def_readwrite("highlight_current_line", &Config::Source::highlight_current_line)
.def_readwrite("show_line_numbers", &Config::Source::show_line_numbers)
.def_readwrite("enable_multiple_cursors", &Config::Source::enable_multiple_cursors)
.def_readwrite("auto_reload_changed_files", &Config::Source::auto_reload_changed_files)
.def_readwrite("clang_format_style", &Config::Source::clang_format_style)
.def_readwrite("clang_usages_threads", &Config::Source::clang_usages_threads)
.def_readwrite("documentation_searches", &Config::Source::documentation_searches)
;
py::class_<Config::Log>(config, "Log")
.def(py::init())
.def_readwrite("libclang", &Config::Log::libclang)
.def_readwrite("language_server", &Config::Log::language_server)
;
py::class_<Config::Plugins>(config, "Plugins")
.def(py::init())
.def_readwrite("enabled", &Config::Plugins::enabled)
.def_readwrite("path", &Config::Plugins::path)
;
config
.def(py::init([]() { return &(Config::get()); }))
.def("load", &Config::load)
.def_readonly("version", &Config::version)
.def_readwrite("menu", &Config::menu)
.def_readwrite("theme", &Config::theme)
.def_readwrite("terminal", &Config::terminal)
.def_readwrite("project", &Config::project)
.def_readwrite("source", &Config::source)
.def_readwrite("log", &Config::log)
.def_readwrite("plugins", &Config::plugins)
.def_readwrite("home_path", &Config::home_path)
.def_readwrite("home_juci_path", &Config::home_juci_path)
;
}

36
src/ctags.cpp

@ -334,3 +334,39 @@ std::vector<Ctags::Location> Ctags::get_locations(const boost::filesystem::path
return best_locations; return best_locations;
} }
void Ctags::init_module(py::module &api) {
py::class_<Ctags> ctags(api, "Ctags");
py::class_<Ctags::Location>(api, "Location")
.def(py::init<>())
.def_readwrite("file_path", &Ctags::Location::file_path)
.def_readwrite("line", &Ctags::Location::line)
.def_readwrite("index", &Ctags::Location::index)
.def_readwrite("symbol", &Ctags::Location::symbol)
.def_readwrite("scope", &Ctags::Location::scope)
.def_readwrite("source", &Ctags::Location::source)
.def_readwrite("kind", &Ctags::Location::kind)
.def("__bool__", &Ctags::Location::operator bool)
;
ctags
.def(py::init<const boost::filesystem::path &, bool, bool, std::string>(),
py::arg("path"),
py::arg("enable_scope"),
py::arg("enable_kind"),
py::arg("languages"))
.def("get_location", &Ctags::get_location,
py::arg("line"),
py::arg("add_markup"),
py::arg("symbol_ends_with_open_parenthesis"))
.def_static("get_locations", &Ctags::get_locations,
py::arg("path"),
py::arg("name"),
py::arg("type"),
py::arg("languages"))
.def_readwrite("project_path", &Ctags::project_path)
.def("__bool__", &Ctags::operator bool)
;
}

2
src/ctags.hpp

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "python_bind.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -28,6 +29,7 @@ public:
std::stringstream output; std::stringstream output;
static std::vector<Location> get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type, const std::string &languages = {}); static std::vector<Location> get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type, const std::string &languages = {});
static void init_module(py::module &api);
private: private:
bool enable_scope, enable_kind; bool enable_scope, enable_kind;

72
src/debug_lldb.cpp

@ -6,6 +6,8 @@
#include "utility.hpp" #include "utility.hpp"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <iostream> #include <iostream>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
extern char **environ; extern char **environ;
@ -560,3 +562,73 @@ void Debug::LLDB::write(const std::string &buffer) {
process->PutSTDIN(buffer.c_str(), buffer.size()); process->PutSTDIN(buffer.c_str(), buffer.size());
} }
} }
void Debug::LLDB::init_module(pybind11::module &api) {
py::class_<Debug::LLDB, std::unique_ptr<Debug::LLDB, py::nodelete>> dbg(api, "LLDB");
py::class_<Debug::LLDB::Frame>(dbg, "Frame")
.def_readwrite("index", &Debug::LLDB::Frame::index)
.def_readwrite("module_filename", &Debug::LLDB::Frame::module_filename)
.def_readwrite("file_path", &Debug::LLDB::Frame::file_path)
.def_readwrite("function_name", &Debug::LLDB::Frame::function_name)
.def_readwrite("line_nr", &Debug::LLDB::Frame::line_nr)
.def_readwrite("line_index", &Debug::LLDB::Frame::line_index)
;
py::class_<Debug::LLDB::Variable>(dbg, "Variable")
.def_readwrite("thread_index_id", &Debug::LLDB::Variable::thread_index_id)
.def_readwrite("frame_index", &Debug::LLDB::Variable::frame_index)
.def_readwrite("name", &Debug::LLDB::Variable::name)
.def_readwrite("value", &Debug::LLDB::Variable::value)
.def_readwrite("declaration_found", &Debug::LLDB::Variable::declaration_found)
.def_readwrite("file_path", &Debug::LLDB::Variable::file_path)
.def_readwrite("line_nr", &Debug::LLDB::Variable::line_nr)
.def_readwrite("line_index", &Debug::LLDB::Variable::line_index)
;
// py::class_<lldb::SBProcess>(api, "SBProcess");
// .def_readwrite("on_start", &Debug::LLDB::on_start)
// .def_readwrite("on_event", &Debug::LLDB::on_event)
dbg
.def(py::init([]() { return &(Debug::LLDB::get()); }))
.def_static("destroy", &Debug::LLDB::destroy)
.def_readwrite("on_exit", &Debug::LLDB::on_exit)
.def("continue_debug", &Debug::LLDB::continue_debug)
.def("stop", &Debug::LLDB::stop)
.def("kill", &Debug::LLDB::kill)
.def("step_over", &Debug::LLDB::step_over)
.def("step_into", &Debug::LLDB::step_into)
.def("step_out", &Debug::LLDB::step_out)
.def("is_invalid", &Debug::LLDB::is_invalid)
.def("is_stopped", &Debug::LLDB::is_stopped)
.def("is_running", &Debug::LLDB::is_running)
.def("get_backtrace", &Debug::LLDB::get_backtrace)
.def("get_variables", &Debug::LLDB::get_variables)
.def("start", &Debug::LLDB::start,
py::arg("command"),
py::arg("path") = "",
py::arg("breakpoints") = std::vector<std::pair<boost::filesystem::path, int>>(),
py::arg("startup_commands") = std::vector<std::string>(),
py::arg("remote_host") = "")
.def("run_command", &Debug::LLDB::run_command,
py::arg("command"))
.def("select_frame", &Debug::LLDB::select_frame,
py::arg("frame_index"),
py::arg("thread_index_id") = 0)
.def("get_return_value", &Debug::LLDB::get_return_value,
py::arg("file_path"),
py::arg("line_nr"),
py::arg("line_index"))
.def("add_breakpoint", &Debug::LLDB::add_breakpoint,
py::arg("file_path"),
py::arg("line_nr"))
.def("remove_breakpoint", &Debug::LLDB::remove_breakpoint,
py::arg("file_path"),
py::arg("line_nr"),
py::arg("line_count"))
.def("write", &Debug::LLDB::write,
py::arg("buffer"))
;
}

3
src/debug_lldb.hpp

@ -5,6 +5,7 @@
#include <lldb/API/LLDB.h> #include <lldb/API/LLDB.h>
#include <thread> #include <thread>
#include <tuple> #include <tuple>
#include "python_bind.h"
namespace Debug { namespace Debug {
class LLDB { class LLDB {
@ -82,8 +83,8 @@ namespace Debug {
void add_breakpoint(const boost::filesystem::path &file_path, int line_nr) EXCLUDES(mutex); void add_breakpoint(const boost::filesystem::path &file_path, int line_nr) EXCLUDES(mutex);
void remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) EXCLUDES(mutex); void remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) EXCLUDES(mutex);
void write(const std::string &buffer) EXCLUDES(mutex); void write(const std::string &buffer) EXCLUDES(mutex);
static void init_module(py::module &api);
private: private:
std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> parse_run_arguments(const std::string &command); std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> parse_run_arguments(const std::string &command);

17
src/dialog.cpp

@ -98,6 +98,23 @@ std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::s
return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : ""; return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : "";
} }
void Dialog::init_module(py::module &api) {
py::class_<Dialog>(api, "Dialog")
.def_static("open_folder", Dialog::open_folder,
py::arg("path"))
.def_static("open_file", Dialog::open_file,
py::arg("path"))
.def_static("new_file", Dialog::new_file,
py::arg("path"))
.def_static("new_folder", Dialog::new_folder,
py::arg("path"))
.def_static("save_file_as", Dialog::save_file_as,
py::arg("path"))
;
}
std::string Dialog::open_folder(const boost::filesystem::path &path) { std::string Dialog::open_folder(const boost::filesystem::path &path) {
return gtk_dialog(path, "Open Folder", return gtk_dialog(path, "Open Folder",
{std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Open", Gtk::RESPONSE_OK)}, {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Open", Gtk::RESPONSE_OK)},

2
src/dialog.hpp

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "python_bind.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <gtkmm.h> #include <gtkmm.h>
#include <string> #include <string>
@ -29,4 +30,5 @@ public:
static std::string new_file(const boost::filesystem::path &path); static std::string new_file(const boost::filesystem::path &path);
static std::string new_folder(const boost::filesystem::path &path); static std::string new_folder(const boost::filesystem::path &path);
static std::string save_file_as(const boost::filesystem::path &path); static std::string save_file_as(const boost::filesystem::path &path);
static void init_module(py::module &api);
}; };

9
src/dispatcher.cpp

@ -40,3 +40,12 @@ void Dispatcher::reset() {
functions.clear(); functions.clear();
connect(); connect();
} }
void Dispatcher::init_module(py::module &api) {
py::class_<Dispatcher>(api, "Dispatcher")
.def(py::init())
.def("disconnect", &Dispatcher::disconnect)
.def("post", &Dispatcher::post<std::function<void()> &>)
;
}

3
src/dispatcher.hpp

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "mutex.hpp" #include "mutex.hpp"
#include "python_bind.h"
#include <functional> #include <functional>
#include <gtkmm.h> #include <gtkmm.h>
#include <list> #include <list>
@ -32,4 +33,6 @@ public:
/// Must be called from main GUI thread /// Must be called from main GUI thread
void reset(); void reset();
static void init_module(py::module &api);
}; };

5
src/documentation.cpp

@ -12711,3 +12711,8 @@ std::experimental::filesystem::status_known cpp/experimental/fs/status_known
return std::string(); return std::string();
return "http://en.cppreference.com/w/" + it->second; return "http://en.cppreference.com/w/" + it->second;
} }
void Documentation::CppReference::init_module(py::module &api) {
py::class_<CppReference>(api, "CppReference")
.def("get_url", &CppReference::get_url);
}

2
src/documentation.hpp

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "python_bind.h"
#include <string> #include <string>
#include <vector> #include <vector>
@ -7,5 +8,6 @@ namespace Documentation {
public: public:
static std::vector<std::string> get_headers(const std::string &symbol) noexcept; static std::vector<std::string> get_headers(const std::string &symbol) noexcept;
static std::string get_url(const std::string &symbol) noexcept; static std::string get_url(const std::string &symbol) noexcept;
static void init_module(py::module &api);
}; };
} // namespace Documentation } // namespace Documentation

438
src/files.hpp

@ -0,0 +1,438 @@
#pragma once
#include <string>
/// If you add or remove nodes from the default_config_file, increase the juci
/// version number (JUCI_VERSION) in ../CMakeLists.txt to automatically apply
/// the changes to user's ~/.juci/config/config.json files
const std::string default_config_file = R"RAW({
"version": ")RAW" + std::string(JUCI_VERSION) + R"RAW(",
"gtk_theme": {
"name_comment": "Use \"\" for default theme, At least these two exist on all systems: Adwaita, Raleigh",
"name": "",
"variant_comment": "Use \"\" for default variant, and \"dark\" for dark theme variant. Note that not all themes support dark variant, but for instance Adwaita does",
"variant": "",
"font_comment": "Set to override theme font, for instance: \"Arial 12\"",
"font": ""
},
"source": {
"style_comment": "Use \"\" for default style, and for instance juci-dark or juci-dark-blue together with dark gtk_theme variant. Styles from normal gtksourceview install: classic, cobalt, kate, oblivion, solarized-dark, solarized-light, tango",
"style": "juci-light",
"font_comment": "Use \"\" for default font, and for instance \"Monospace 12\" to also set size",)RAW"
#ifdef __APPLE__
R"RAW(
"font": "Menlo",)RAW"
#else
#ifdef _WIN32
R"RAW(
"font": "Consolas",)RAW"
#else
R"RAW(
"font": "Monospace",)RAW"
#endif
#endif
R"RAW(
"cleanup_whitespace_characters_comment": "Remove trailing whitespace characters on save, and add trailing newline if missing",
"cleanup_whitespace_characters": false,
"show_whitespace_characters_comment": "Determines what kind of whitespaces should be drawn. Use comma-separated list of: space, tab, newline, nbsp, leading, text, trailing or all",
"show_whitespace_characters": "",
"format_style_on_save_comment": "Performs style format on save if supported on language in buffer",
"format_style_on_save": false,
"format_style_on_save_if_style_file_found_comment": "Format style if format file is found, even if format_style_on_save is false",
"format_style_on_save_if_style_file_found": true,
"smart_brackets_comment": "If smart_inserts is enabled, this option is automatically enabled. When inserting an already closed bracket, the cursor might instead be moved, avoiding the need of arrow keys after autocomplete",
"smart_brackets": true,
"smart_inserts_comment": "When for instance inserting (, () gets inserted. Applies to: (), [], \", '. Also enables pressing ; inside an expression before a final ) to insert ; at the end of line, and deletions of empty insertions",
"smart_inserts": true,
"show_map": true,
"map_font_size": "1",
"show_git_diff": true,
"show_background_pattern": true,
"show_right_margin": false,
"right_margin_position": 80,
"spellcheck_language_comment": "Use \"\" to set language from your locale settings",
"spellcheck_language": "en_US",
"auto_tab_char_and_size_comment": "Use false to always use default tab char and size",
"auto_tab_char_and_size": true,
"default_tab_char_comment": "Use \"\t\" for regular tab",
"default_tab_char": " ",
"default_tab_size": 2,
"tab_indents_line": true,
"word_wrap_comment": "Specify language ids that should enable word wrap, for instance: chdr, c, cpphdr, cpp, js, python, or all to enable word wrap for all languages",
"word_wrap": "markdown, latex",
"highlight_current_line": true,
"show_line_numbers": true,
"enable_multiple_cursors": false,
"auto_reload_changed_files": true,
"search_for_selection": true,
"clang_format_style_comment": "IndentWidth, AccessModifierOffset and UseTab are set automatically. See http://clang.llvm.org/docs/ClangFormatStyleOptions.html",
"clang_format_style": "ColumnLimit: 0, NamespaceIndentation: All",
"clang_tidy_enable_comment": "Enable clang-tidy in new C/C++ buffers",
"clang_tidy_enable": false,
"clang_tidy_checks_comment": "In new C/C++ buffers, these checks are appended to the value of 'Checks' in the .clang-tidy file, if any",
"clang_tidy_checks": "",
"clang_usages_threads_comment": "The number of threads used in finding usages in unparsed files. -1 corresponds to the number of cores available, and 0 disables the search",
"clang_usages_threads": -1,
"clang_detailed_preprocessing_record_comment": "Set to true to, at the cost of increased resource use, include all macro definitions and instantiations when parsing new C/C++ buffers. You should reopen buffers and delete build/.usages_clang after changing this option.",
"clang_detailed_preprocessing_record": false,
"debug_place_cursor_at_stop": false
},
"terminal": {
"history_size": 10000,
"font_comment": "Use \"\" to use source.font with slightly smaller size",
"font": "",
"clear_on_compile": true,
"clear_on_run_command": false
},
"project": {
"default_build_path_comment": "Use <project_directory_name> to insert the project top level directory name",
"default_build_path": "./build",
"debug_build_path_comment": "Use <project_directory_name> to insert the project top level directory name, and <default_build_path> to insert your default_build_path setting.",
"debug_build_path": "<default_build_path>/debug",
"cmake": {)RAW"
#ifdef _WIN32
R"RAW(
"command": "cmake -G\"MSYS Makefiles\"",)RAW"
#else
R"RAW(
"command": "cmake",)RAW"
#endif
R"RAW(
"compile_command": "cmake --build ."
},
"meson": {
"command": "meson",
"compile_command": "ninja"
},
"default_build_management_system_comment": "Select which build management system to use when creating a new C or C++ project, for instance \"cmake\" or \"meson\"",
"default_build_management_system": "cmake",
"save_on_compile_or_run": true,)RAW"
#ifdef JUCI_USE_UCTAGS
R"RAW(
"ctags_command": "uctags",)RAW"
#else
R"RAW(
"ctags_command": "ctags",)RAW"
#endif
R"RAW(
"grep_command": "grep",
"cargo_command": "cargo",
"python_command": "python -u",
"markdown_command": "grip -b"
},
"keybindings": {
"preferences": "<primary>comma",
"snippets": "",
"quit": "<primary>q",
"file_new_file": "<primary>n",
"file_new_folder": "<primary><shift>n",
"file_open_file": "<primary>o",
"file_open_folder": "<primary><shift>o",
"file_reload_file": "",
"file_save": "<primary>s",
"file_save_as": "<primary><shift>s",
"file_close_file": "<primary>w",
"file_close_folder": "",
"file_close_project": "",
"file_print": "",
"edit_undo": "<primary>z",
"edit_redo": "<primary><shift>z",
"edit_cut": "<primary>x",
"edit_cut_lines": "<primary><shift>x",
"edit_copy": "<primary>c",
"edit_copy_lines": "<primary><shift>c",
"edit_paste": "<primary>v",
"edit_extend_selection": "<primary><shift>a",
"edit_shrink_selection": "<primary><shift><alt>a",
"edit_show_or_hide": "",
"edit_find": "<primary>f",
"source_spellcheck": "",
"source_spellcheck_clear": "",
"source_spellcheck_next_error": "<primary><shift>e",
"source_git_next_diff": "<primary>k",
"source_git_show_diff": "<alt>k",
"source_indentation_set_buffer_tab": "",
"source_indentation_auto_indent_buffer": "<primary><shift>i",
"source_goto_line": "<primary>g",
"source_center_cursor": "<primary>l",
"source_cursor_history_back": "<alt>Left",
"source_cursor_history_forward": "<alt>Right",
"source_show_completion_comment" : "Add completion keybinding to disable interactive autocompletion",
"source_show_completion" : "",
"source_find_file": "<primary>p",
"source_find_symbol": "<primary><shift>f",
"source_find_pattern": "<alt><shift>f",
"source_comments_toggle": "<primary>slash",
"source_comments_add_documentation": "<primary><alt>slash",
"source_find_documentation": "<primary><shift>d",
"source_goto_declaration": "<primary>d",
"source_goto_type_declaration": "<alt><shift>d",
"source_goto_implementation": "<primary>i",
"source_goto_usage": "<primary>u",
"source_goto_method": "<primary>m",
"source_rename": "<primary>r",
"source_implement_method": "<primary><shift>m",
"source_goto_next_diagnostic": "<primary>e",
"source_apply_fix_its": "<control>space",
"project_set_run_arguments": "",
"project_compile_and_run": "<primary>Return",
"project_compile": "<primary><shift>Return",
"project_run_command": "<alt>Return",
"project_kill_last_running": "<primary>Escape",
"project_force_kill_last_running": "<primary><shift>Escape",
"debug_set_run_arguments": "",
"debug_start_continue": "<primary>y",
"debug_stop": "<primary><shift>y",
"debug_kill": "<primary><shift>k",
"debug_step_over": "<primary>j",
"debug_step_into": "<primary>t",
"debug_step_out": "<primary><shift>t",
"debug_backtrace": "<primary><shift>j",
"debug_show_variables": "<primary><shift>b",
"debug_run_command": "<alt><shift>Return",
"debug_toggle_breakpoint": "<primary>b",
"debug_show_breakpoints": "<primary><shift><alt>b",
"debug_goto_stop": "<primary><shift>l",)RAW"
#ifdef __linux
R"RAW(
"window_next_tab": "<primary>Tab",
"window_previous_tab": "<primary><shift>Tab",)RAW"
#else
R"RAW(
"window_next_tab": "<primary><alt>Right",
"window_previous_tab": "<primary><alt>Left",)RAW"
#endif
R"RAW(
"window_goto_tab": "",
"window_toggle_split": "",
"window_split_source_buffer": "",)RAW"
#ifdef __APPLE__
R"RAW(
"window_toggle_full_screen": "<primary><control>f",)RAW"
#else
R"RAW(
"window_toggle_full_screen": "F11",)RAW"
#endif
R"RAW(
"window_toggle_tabs": "",
"window_toggle_zen_mode": "",
"window_clear_terminal": ""
},
"documentation_searches": {
"clang": {
"separator": "::",
"queries": {
"@empty": "https://www.google.com/search?q=c%2B%2B+",
"std": "https://www.google.com/search?q=site:http://www.cplusplus.com/reference/+",
"boost": "https://www.google.com/search?q=site:http://www.boost.org/doc/libs/1_59_0/+",
"Gtk": "https://www.google.com/search?q=site:https://developer.gnome.org/gtkmm/stable/+",
"@any": "https://www.google.com/search?q="
}
}
},
"log": {
"libclang_comment": "Outputs diagnostics for new C/C++ buffers",
"libclang": false,
"language_server": false
},
"plugins": {
"enabled": true,
"path_comment": "Directory where plugins are loaded from.",
"path": "<juci_home_directory>/plugins"
}
}
)RAW";
const std::string juci_light_style = R"RAW(<?xml version="1.0" encoding="UTF-8"?>
<style-scheme id="juci-light" _name="juci" version="1.0">
<author>juCi++ team</author>
<_description>Default juCi++ style</_description>
<!-- Palette -->
<color name="white" value="#FFFFFF"/>
<color name="black" value="#000000"/>
<color name="gray" value="#888888"/>
<color name="red" value="#CC0000"/>
<color name="green" value="#008800"/>
<color name="blue" value="#0000FF"/>
<color name="dark-blue" value="#002299"/>
<color name="yellow" value="#FFFF00"/>
<color name="light-yellow" value="#FFFF88"/>
<color name="orange" value="#FF8800"/>
<color name="purple" value="#990099"/>
<style name="text" foreground="#000000" background="#FFFFFF"/>
<style name="background-pattern" background="#rgba(0,0,0,.03)"/>
<style name="selection" background="#4A90D9"/>
<!-- Current Line Highlighting -->
<style name="current-line" background="#rgba(0,0,0,.07)"/>
<!-- Bracket Matching -->
<style name="bracket-match" foreground="white" background="gray" bold="true"/>
<style name="bracket-mismatch" foreground="white" background="#FF0000" bold="true"/>
<!-- Search Matching -->
<style name="search-match" foreground="#000000" background="#FFFF00"/>
<!-- Language specifics -->
<style name="def:builtin" foreground="blue"/>
<style name="def:constant" foreground="blue"/>
<style name="def:boolean" foreground="red"/>
<style name="def:decimal" foreground="red"/>
<style name="def:base-n-integer" foreground="red"/>
<style name="def:floating-point" foreground="red"/>
<style name="def:complex" foreground="red"/>
<style name="def:character" foreground="red"/>
<style name="def:special-char" foreground="red"/>
<!-- Language specifics used by clang-parser in default config -->
<style name="def:string" foreground="red"/>
<style name="def:comment" foreground="gray"/>
<style name="def:statement" foreground="blue"/>
<style name="def:type" foreground="blue"/>
<style name="def:function" foreground="dark-blue"/>
<style name="def:identifier" foreground="purple"/>
<style name="def:preprocessor" foreground="green"/>
<style name="def:error" foreground="red"/>
<style name="def:warning" foreground="orange"/>
<style name="def:note" foreground="black" background="light-yellow"/>
<style name="diff:added-line" foreground="green"/>
<style name="diff:removed-line" foreground="red"/>
<style name="diff:changed-line" foreground="orange"/>
<style name="diff:diff-file" use-style="def:type"/>
<style name="diff:location" use-style="def:statement"/>
<style name="diff:special-case" use-style="def:constant"/>
</style-scheme>
)RAW";
const std::string juci_dark_style = R"RAW(<?xml version="1.0" encoding="UTF-8"?>
<style-scheme id="juci-dark" _name="juci" version="1.0">
<author>juCi++ team</author>
<_description>Dark juCi++ style</_description>
<!-- Palette -->
<color name="white" value="#D6D6D6"/>
<color name="black" value="#202428"/>
<color name="gray" value="#919191"/>
<color name="red" value="#FF9999"/>
<color name="yellow" value="#EEEE66"/>
<color name="green" value="#AACC99"/>
<color name="blue" value="#88AAFF"/>
<color name="light-blue" value="#AABBEE"/>
<color name="purple" value="#DD99DD"/>
<style name="text" foreground="white" background="black"/>
<style name="background-pattern" background="#rgba(255,255,255,.04)"/>
<style name="selection" background="#215D9C"/>
<!-- Current Line Highlighting -->
<style name="current-line" background="#rgba(255,255,255,.05)"/>
<!-- Bracket Matching -->
<style name="bracket-match" foreground="black" background="gray" bold="true"/>
<style name="bracket-mismatch" foreground="black" background="#FF0000" bold="true"/>
<!-- Search Matching -->
<style name="search-match" foreground="#000000" background="#FFFF00"/>
<!-- Language specifics -->
<style name="def:builtin" foreground="blue"/>
<style name="def:constant" foreground="blue"/>
<style name="def:boolean" foreground="red"/>
<style name="def:decimal" foreground="red"/>
<style name="def:base-n-integer" foreground="red"/>
<style name="def:floating-point" foreground="red"/>
<style name="def:complex" foreground="red"/>
<style name="def:character" foreground="red"/>
<style name="def:special-char" foreground="red"/>
<!-- Language specifics used by clang-parser in default config -->
<style name="def:string" foreground="red"/>
<style name="def:comment" foreground="gray"/>
<style name="def:statement" foreground="blue"/>
<style name="def:type" foreground="blue"/>
<style name="def:function" foreground="light-blue"/>
<style name="def:identifier" foreground="purple"/>
<style name="def:preprocessor" foreground="green"/>
<style name="def:error" foreground="red"/>
<style name="def:warning" foreground="yellow"/>
<style name="def:note" foreground="#E6E6E6" background="#383F46"/>
<style name="diff:added-line" foreground="green"/>
<style name="diff:removed-line" foreground="red"/>
<style name="diff:changed-line" foreground="yellow"/>
<style name="diff:diff-file" use-style="def:type"/>
<style name="diff:location" use-style="def:statement"/>
<style name="diff:special-case" use-style="def:constant"/>
</style-scheme>
)RAW";
const std::string juci_dark_blue_style = R"RAW(<?xml version="1.0" encoding="UTF-8"?>
<style-scheme id="juci-dark-blue" _name="juci" version="1.0">
<author>juCi++ team</author>
<_description>Dark blue juCi++ style based on the Emacs deeper blue theme</_description>
<!-- Palette -->
<color name="white" value="#D6D6D6"/>
<color name="dark-blue" value="#202233"/>
<color name="gray" value="#919191"/>
<color name="red" value="#FF7777"/>
<color name="yellow" value="#FFE100"/>
<color name="light-yellow" value="#EAC595"/>
<color name="blue" value="#00CCFF"/>
<color name="green" value="#14ECA8"/>
<color name="light-blue" value="#8BFAFF"/>
<color name="light-green" value="#A0DB6B"/>
<style name="text" foreground="white" background="dark-blue"/>
<style name="background-pattern" background="#rgba(255,255,255,.04)"/>
<style name="selection" background="#215D9C"/>
<!-- Current Line Highlighting -->
<style name="current-line" background="#rgba(255,255,255,.05)"/>
<!-- Bracket Matching -->
<style name="bracket-match" foreground="dark-blue" background="gray" bold="true"/>
<style name="bracket-mismatch" foreground="dark-blue" background="#FF0000" bold="true"/>
<!-- Search Matching -->
<style name="search-match" foreground="#000000" background="#FFFF00"/>
<!-- Language specifics -->
<style name="def:builtin" foreground="blue"/>
<style name="def:constant" foreground="blue"/>
<style name="def:boolean" foreground="light-yellow"/>
<style name="def:decimal" foreground="light-yellow"/>
<style name="def:base-n-integer" foreground="light-yellow"/>
<style name="def:floating-point" foreground="light-yellow"/>
<style name="def:complex" foreground="light-yellow"/>
<style name="def:character" foreground="light-yellow"/>
<style name="def:special-char" foreground="light-yellow"/>
<!-- Language specifics used by clang-parser in default config -->
<style name="def:string" foreground="light-yellow"/>
<style name="def:comment" foreground="gray"/>
<style name="def:statement" foreground="blue"/>
<style name="def:type" foreground="blue"/>
<style name="def:function" foreground="light-blue"/>
<style name="def:identifier" foreground="light-green"/>
<style name="def:preprocessor" foreground="yellow"/>
<style name="def:error" foreground="red"/>
<style name="def:warning" foreground="yellow"/>
<style name="def:note" foreground="#E6E6E6" background="#383C59"/>
<style name="diff:added-line" foreground="green"/>
<style name="diff:removed-line" foreground="red"/>
<style name="diff:changed-line" foreground="yellow"/>
<style name="diff:diff-file" use-style="def:type"/>
<style name="diff:location" use-style="def:statement"/>
<style name="diff:special-case" use-style="def:constant"/>
</style-scheme>
)RAW";

63
src/git.cpp

@ -268,3 +268,66 @@ boost::filesystem::path Git::path(const char *cpath, boost::optional<size_t> cpa
else else
return std::string(cpath, cpath_length); return std::string(cpath, cpath_length);
} }
void Git::init_module(py::module &api) {
py::class_<Git> git(api, "Git");
py::class_<Git::Error>(git, "Error")
.def("__bool__", &Git::Error::operator bool)
.def_readwrite("code", &Git::Error::code)
;
py::class_<Git::Repository> repository(git, "Repository");
py::class_<Git::Repository::Diff> diff(repository, "Diff");
py::class_<Git::Repository::Diff::Lines>(diff, "Lines")
.def_readwrite("added", &Git::Repository::Diff::Lines::added)
.def_readwrite("modified", &Git::Repository::Diff::Lines::modified)
.def_readwrite("removed", &Git::Repository::Diff::Lines::removed)
;
py::class_<Git::Repository::Diff::Hunk>(repository, "Lines")
.def(py::init<int, int, int, int>(),
py::arg("old_start"),
py::arg("old_size"),
py::arg("new_start"),
py::arg("new_size"))
.def_readwrite("old_lines", &Git::Repository::Diff::Hunk::old_lines)
.def_readwrite("new_lines", &Git::Repository::Diff::Hunk::new_lines)
;
diff
.def("get_lines", &Git::Repository::Diff::get_lines,
py::arg("buffer"))
.def_static("get_hunks", &Git::Repository::Diff::get_hunks,
py::arg("old_buffer"),
py::arg("new_buffer"))
.def("get_details", &Git::Repository::Diff::get_details,
py::arg("buffer"),
py::arg("line_nr"))
;
py::class_<Git::Repository::Status>(repository, "Status")
.def_readwrite("added", &Git::Repository::Status::added)
.def_readwrite("modified", &Git::Repository::Status::modified)
;
repository
.def("get_status", &Git::Repository::get_status)
.def("clear_saved_status", &Git::Repository::clear_saved_status)
.def("get_work_path", &Git::Repository::get_work_path)
.def_static("get_root_path", &Git::Repository::get_root_path,
py::arg("path"))
.def("get_diff", &Git::Repository::get_diff,
py::arg("path"))
.def("get_branch", &Git::Repository::get_branch)
;
git
.def_static("get_repository", &Git::get_repository,
py::arg("path"))
;
}

2
src/git.hpp

@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include "python_bind.h"
class Git { class Git {
friend class Repository; friend class Repository;
@ -103,4 +104,5 @@ private:
public: public:
static std::shared_ptr<Repository> get_repository(const boost::filesystem::path &path); static std::shared_ptr<Repository> get_repository(const boost::filesystem::path &path);
static void init_module(py::module &api);
}; };

14
src/juci.cpp

@ -4,6 +4,7 @@
#include "filesystem.hpp" #include "filesystem.hpp"
#include "menu.hpp" #include "menu.hpp"
#include "notebook.hpp" #include "notebook.hpp"
#include "plugins.h"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "window.hpp" #include "window.hpp"
@ -54,10 +55,9 @@ 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;
boost::filesystem::path current_file; boost::filesystem::path current_file;
Window::get().init();
Window::get().load_session(directories, files, file_offsets, current_file, directories.empty() && files.empty()); Window::get().load_session(directories, files, file_offsets, current_file, directories.empty() && files.empty());
Window::get().add_widgets(); Window::get().add_widgets();
add_window(Window::get()); add_window(Window::get());
Window::get().show(); Window::get().show();
@ -129,7 +129,7 @@ void Application::on_startup() {
set_menubar(Menu::get().window_menu); set_menubar(Menu::get().window_menu);
} }
Application::Application() : Gtk::Application("no.sout.juci", Gio::APPLICATION_NON_UNIQUE | Gio::APPLICATION_HANDLES_COMMAND_LINE) { Application::Application(Plugins &p) : Gtk::Application("no.sout.juci", Gio::APPLICATION_NON_UNIQUE | Gio::APPLICATION_HANDLES_COMMAND_LINE), window(p) {
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
@ -140,5 +140,11 @@ int main(int argc, char *argv[]) {
#ifndef _WIN32 #ifndef _WIN32
signal(SIGPIPE, SIG_IGN); // Do not terminate application when writing to a process fails signal(SIGPIPE, SIG_IGN); // Do not terminate application when writing to a process fails
#endif #endif
return Application().run(argc, argv); auto &config = Config::get();
config.load();
Plugins plugins(config);
if(Config::get().plugins.enabled) {
plugins.load();
}
return Application(plugins).run(argc, argv);
} }

9
src/juci.hpp

@ -1,4 +1,7 @@
#pragma once #pragma once
#include "window.hpp"
#include <boost/filesystem.hpp>
#include <gtkmm.h>
/** /**
\mainpage \mainpage
juCi++ is a lightweight C++ IDE written in C++ juCi++ is a lightweight C++ IDE written in C++
@ -25,12 +28,9 @@
[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(Plugins &p);
int on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine> &cmd) override; int on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine> &cmd) override;
void on_activate() override; void on_activate() override;
void on_startup() override; void on_startup() override;
@ -39,4 +39,5 @@ 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;
}; };

14
src/menu.cpp

@ -2,6 +2,7 @@
#include "config.hpp" #include "config.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include <iostream> #include <iostream>
#include <pybind11/functional.h>
#include <string> #include <string>
const Glib::ustring juci_section_xml = R"RAW(<section> const Glib::ustring juci_section_xml = R"RAW(<section>
@ -637,3 +638,16 @@ void Menu::build() {
std::cerr << "building menu failed: " << e.what(); std::cerr << "building menu failed: " << e.what();
} }
} }
void Menu::init_module(py::module &api) {
// TODO bind glib members
py::class_<Menu, std::unique_ptr<Menu, py::nodelete>>(api, "Menu")
.def(py::init([] { return &Menu::get(); }))
.def("add_action", &Menu::add_action,
py::arg("name"),
py::arg("action"))
.def("set_keys", &Menu::set_keys)
.def("build", &Menu::build)
;
}

2
src/menu.hpp

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "python_bind.h"
#include <functional> #include <functional>
#include <gtkmm.h> #include <gtkmm.h>
#include <string> #include <string>
@ -25,6 +26,7 @@ public:
std::unique_ptr<Gtk::Menu> right_click_line_menu; std::unique_ptr<Gtk::Menu> right_click_line_menu;
std::unique_ptr<Gtk::Menu> right_click_selected_menu; std::unique_ptr<Gtk::Menu> right_click_selected_menu;
std::function<void()> toggle_menu_items = [] {}; std::function<void()> toggle_menu_items = [] {};
static void init_module(py::module &api);
private: private:
Glib::RefPtr<Gtk::Builder> builder; Glib::RefPtr<Gtk::Builder> builder;

79
src/plugins.cc

@ -0,0 +1,79 @@
#include "plugins.h"
#include "config.hpp"
#include "python_module.h"
#include "terminal.hpp"
Plugins::Plugins(Config &config) : jucipp_module("Jucipp", Module::init_jucipp_module) {
#ifdef PYTHON_HOME_DIR
#ifdef _WIN32
const std::wstring python_home(PYTHON_HOME_DIR);
const std::wstring python_path(python_home + L";" + python_home + L"\\lib-dynload;" + python_home + L"\\site-packages");
Py_SetPythonHome(python_home.c_str());
Py_SetPath(python_path.c_str());
#endif
#endif
py::initialize_interpreter();
py::module::import("sys")
.attr("path")
.cast<py::list>()
.append(config.plugins.path);
}
Plugins::~Plugins() {
py::finalize_interpreter();
}
void Plugins::init_hook() {
for(auto &module_name : loaded_modules) {
auto module = py::module(py::handle(PyImport_GetModule(py::str(module_name.c_str()).ptr())), false);
if(py::hasattr(module, "init_hook")) {
py::object obj = module.attr("init_hook");
if(py::isinstance<py::function>(obj)) {
py::function func(obj);
try {
func();
}
catch(const py::error_already_set &err) {
std::cerr << err.what() << std::endl;
}
}
}
}
}
void Plugins::load() {
const auto &plugin_path = Config::get().plugins.path;
boost::system::error_code ec;
if(!boost::filesystem::exists(plugin_path, ec)) {
ec.clear();
boost::filesystem::create_directories(plugin_path, ec);
}
if(ec) {
std::cerr << ec.message() << std::endl;
return;
}
boost::filesystem::directory_iterator end_it;
for(boost::filesystem::directory_iterator it(plugin_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 = py::module::import(module_name.c_str());
loaded_modules.push_back(module_name);
}
catch(py::error_already_set &error) {
std::cerr << "Error loading plugin `" << module_name << "`:\n"
<< error.what() << "\n";
}
}
}
}

18
src/plugins.h

@ -0,0 +1,18 @@
#pragma once
#include "config.hpp"
#include "python_bind.h"
#include <pybind11/embed.h>
class __attribute__((visibility("default")))
Plugins {
public:
Plugins(Config &config);
~Plugins();
void load();
void init_hook();
private:
py::detail::embedded_module jucipp_module;
std::vector<std::string> loaded_modules;
};

3
src/python_bind.h

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

29
src/python_module.cc

@ -0,0 +1,29 @@
#include "python_module.h"
#include "cmake.hpp"
#include "compile_commands.hpp"
#include "config.hpp"
#include "ctags.hpp"
#ifdef JUCI_ENABLE_DEBUG
#include "debug_lldb.hpp"
#endif
#include "dialogs.hpp"
#include "git.hpp"
#include "terminal.hpp"
#include "tiny_process_module.hpp"
PyObject *Module::init_jucipp_module() {
auto api = py::module("Jucipp", "API");
CMake::init_module(api);
CompileCommands::init_module(api);
Config::init_module(api);
Ctags::init_module(api);
#ifdef JUCI_ENABLE_DEBUG
Debug::LLDB::init_module(api);
#endif
Dialog::init_module(api);
Dispatcher::init_module(api);
Git::init_module(api);
Terminal::init_module(api);
TinyProcessModule::init_module(api);
return api.ptr();
}

7
src/python_module.h

@ -0,0 +1,7 @@
#pragma once
#include "python_bind.h"
class Module {
public:
static PyObject *init_jucipp_module();
};

28
src/python_type_casters.h

@ -0,0 +1,28 @@
#pragma once
#include "python_bind.h"
#include <boost/filesystem.hpp>
namespace pybind11 {
namespace detail {
template <>
struct type_caster<boost::filesystem::path> {
public:
PYBIND11_TYPE_CASTER(boost::filesystem::path, _("str"));
bool load(handle src, bool) {
if (!src) {
return false;
}
try {
value = std::string(py::str(src));
} catch(...) {
return false;
}
return !PyErr_Occurred();
}
static handle cast(boost::filesystem::path src, return_value_policy, handle) {
return pybind11::str(src.string()).release();
}
};
} // namespace detail
} // namespace pybind11

2
src/source_clang.cpp

@ -893,7 +893,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
autocomplete.is_restart_key = [this](guint keyval) { autocomplete.is_restart_key = [this](guint keyval) {
auto iter = get_buffer()->get_insert()->get_iter(); auto iter = get_buffer()->get_insert()->get_iter();
iter.backward_chars(2); iter.backward_chars(2);
if(keyval == '.' || (keyval == ':' && *iter == ':') || (keyval == '>' && *iter == '-')) if(keyval == '.' || (*iter == ':' && keyval == ':') || (*iter == '-' && keyval == '>'))
return true; return true;
return false; return false;
}; };

14
src/source_language_protocol.cpp

@ -1610,7 +1610,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
autocomplete->is_restart_key = [this](guint keyval) { autocomplete->is_restart_key = [this](guint keyval) {
auto iter = get_buffer()->get_insert()->get_iter(); auto iter = get_buffer()->get_insert()->get_iter();
iter.backward_chars(2); iter.backward_chars(2);
if(keyval == '.' || (keyval == ':' && *iter == ':')) if(keyval == '.' || (*iter == ':' && keyval == ':') || (*iter == '-' && keyval == '>'))
return true; return true;
return false; return false;
}; };
@ -1657,7 +1657,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
} }
return true; return true;
} }
else if(prevprev.backward_char() && *prevprev == ':' && *prev == ':') { else if(prevprev.backward_char() && ((*prevprev == ':' && *prev == ':') || (*prevprev == '-' && *prev == '>'))) {
{ {
LockGuard lock(autocomplete->prefix_mutex); LockGuard lock(autocomplete->prefix_mutex);
autocomplete->prefix = get_buffer()->get_text(prefix_start, prefix_end); autocomplete->prefix = get_buffer()->get_text(prefix_start, prefix_end);
@ -1689,7 +1689,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
autocomplete->prefix = get_buffer()->get_text(prefix_start, prefix_end); autocomplete->prefix = get_buffer()->get_text(prefix_start, prefix_end);
} }
auto prevprev = prev; auto prevprev = prev;
autocomplete_enable_snippets = !(*prev == '.' || (prevprev.backward_char() && *prevprev == ':' && *prev == ':')); autocomplete_enable_snippets = !(*prev == '.' || (prevprev.backward_char() && ((*prevprev == ':' && *prev == ':') || (*prevprev == '-' && *prev == '>'))));
return true; return true;
} }
@ -1808,7 +1808,13 @@ void Source::LanguageProtocolView::setup_autocomplete() {
prefix = autocomplete->prefix; prefix = autocomplete->prefix;
} }
for(auto &item : items) { for(auto &item : items) {
auto label = item.string_or("label", ""); auto label = item.string_or("filterText", item.string_or("label", ""));
if(!label.empty()) {
if(starts_with(label, "."))
label.erase(0, 1);
else if(starts_with(label, "::") || starts_with(label, "->"))
label.erase(0, 2);
}
if(starts_with(label, prefix)) { if(starts_with(label, prefix)) {
auto detail = item.string_or("detail", ""); auto detail = item.string_or("detail", "");
LanguageProtocol::Documentation documentation(item.child_optional("documentation")); LanguageProtocol::Documentation documentation(item.child_optional("documentation"));

31
src/terminal.cpp

@ -7,6 +7,8 @@
#include "utility.hpp" #include "utility.hpp"
#include <future> #include <future>
#include <iostream> #include <iostream>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
#include <regex> #include <regex>
#include <thread> #include <thread>
@ -672,6 +674,35 @@ bool Terminal::on_key_press_event(GdkEventKey *event) {
return true; return true;
} }
void Terminal::init_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", &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::*)(std::string, bool)) & Terminal::async_print,
py::arg("message"),
py::arg("bold") = false)
.def("configure", &Terminal::configure)
.def("clear", &Terminal::clear)
.def_readwrite("", &Terminal::scroll_to_bottom)
;
}
void Terminal::paste() { void Terminal::paste() {
std::string text = Gtk::Clipboard::get()->wait_for_text(); std::string text = Gtk::Clipboard::get()->wait_for_text();
if(text.empty()) if(text.empty())

3
src/terminal.hpp

@ -2,6 +2,8 @@
#include "dispatcher.hpp" #include "dispatcher.hpp"
#include "mutex.hpp" #include "mutex.hpp"
#include "process.hpp" #include "process.hpp"
#include "python_bind.h"
#include "python_type_casters.h"
#include "source_base.hpp" #include "source_base.hpp"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@ -34,6 +36,7 @@ public:
void configure(); void configure();
void clear(); void clear();
static void init_module(pybind11::module &api);
std::function<void()> scroll_to_bottom; std::function<void()> scroll_to_bottom;

27
src/tiny_process_module.cpp

@ -0,0 +1,27 @@
#include "tiny_process_module.hpp"
#include <process.hpp>
void TinyProcessModule::init_module(py::module &api) {
py::class_<TinyProcessLib::Process, std::shared_ptr<TinyProcessLib::Process>> process(api, "Process");
process
// .def("kill", (void (TinyProcessLib::Process::*)(TinyProcessLib::Process::id_type, bool)) & TinyProcessLib::Process::kill,
// py::arg("id"),
// py::arg("force") = false)
// .def(py::init<const TinyProcessLib::Process::string_type &, const TinyProcessLib::Process::string_type &>(),
// py::arg("command"),
// py::arg("path") = TinyProcessLib::Process::string_type())
.def("get_id", &TinyProcessLib::Process::get_id)
.def("get_exit_status", &TinyProcessLib::Process::get_exit_status)
.def("try_get_exit_status", &TinyProcessLib::Process::try_get_exit_status,
py::arg("exit_status"))
.def("write", (bool (TinyProcessLib::Process::*)(const char *, size_t)) & TinyProcessLib::Process::write,
py::arg("bytes"),
py::arg("n"))
.def("write", (bool (TinyProcessLib::Process::*)(const std::string &)) & TinyProcessLib::Process::write,
py::arg("string"))
.def("close_stdin", &TinyProcessLib::Process::close_stdin)
.def("kill", (void (TinyProcessLib::Process::*)(bool)) & TinyProcessLib::Process::kill,
py::arg("force"))
;
}

6
src/tiny_process_module.hpp

@ -0,0 +1,6 @@
#include "python_bind.h"
class TinyProcessModule {
public:
static void init_module(py::module &api);
};

14
src/window.cpp

@ -20,17 +20,17 @@
#include "utility.hpp" #include "utility.hpp"
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
Window::Window() { Window::Window(Plugins &i) : plugins(i) {
Gsv::init(); Gsv::init();
set_title(); set_title();
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;
@ -170,6 +170,7 @@ 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);
plugins.init_hook();
} }
void Window::set_title(const boost::filesystem::path &path) { void Window::set_title(const boost::filesystem::path &path) {
@ -178,7 +179,6 @@ void Window::set_title(const boost::filesystem::path &path) {
} }
void Window::configure() { void Window::configure() {
Config::get().load();
Snippets::get().load(); Snippets::get().load();
Commands::get().load(); Commands::get().load();
auto screen = get_screen(); auto screen = get_screen();
@ -290,8 +290,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");
@ -527,6 +528,7 @@ void Window::set_menu_actions() {
if(auto view = Notebook::get().get_current_view()) { if(auto view = Notebook::get().get_current_view()) {
if(Notebook::get().save_current()) { if(Notebook::get().save_current()) {
if(view->file_path == Config::get().home_juci_path / "config" / "config.json") { if(view->file_path == Config::get().home_juci_path / "config" / "config.json") {
Config::get().load();
configure(); configure();
for(size_t c = 0; c < Notebook::get().size(); c++) { for(size_t c = 0; c < Notebook::get().size(); c++) {
Notebook::get().get_view(c)->configure(); Notebook::get().get_view(c)->configure();

5
src/window.hpp

@ -3,11 +3,11 @@
#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:
void init();
static Window &get() { static Window &get() {
static Window instance; static Window instance;
return instance; return instance;
@ -23,6 +23,7 @@ protected:
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;
Gtk::Overlay status_overlay; Gtk::Overlay status_overlay;

41
tests/CMakeLists.txt

@ -5,6 +5,7 @@ endif()
add_definitions(-DJUCI_BUILD_PATH="${CMAKE_BINARY_DIR}" -DJUCI_TESTS_PATH="${CMAKE_CURRENT_SOURCE_DIR}") add_definitions(-DJUCI_BUILD_PATH="${CMAKE_BINARY_DIR}" -DJUCI_TESTS_PATH="${CMAKE_CURRENT_SOURCE_DIR}")
include_directories(${CMAKE_SOURCE_DIR}/libclangmm/src)
include_directories(${CMAKE_SOURCE_DIR}/src) include_directories(${CMAKE_SOURCE_DIR}/src)
include_directories(${CMAKE_SOURCE_DIR}/lib/tiny-process-library) include_directories(${CMAKE_SOURCE_DIR}/lib/tiny-process-library)
@ -15,23 +16,24 @@ add_library(test_stubs OBJECT
stubs/notebook.cpp stubs/notebook.cpp
stubs/project.cpp stubs/project.cpp
stubs/selection_dialog.cpp stubs/selection_dialog.cpp
stubs/plugins.cc
) )
if(BUILD_TESTING) if(BUILD_TESTING)
add_executable(process_test process_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(process_test process_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(process_test juci_shared) target_link_libraries(process_test juci_shared ${PYTHON_LIBRARIES})
add_test(process_test process_test) add_test(process_test process_test)
add_executable(compile_commands_test compile_commands_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(compile_commands_test compile_commands_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(compile_commands_test juci_shared) target_link_libraries(compile_commands_test juci_shared ${PYTHON_LIBRARIES})
add_test(compile_commands_test compile_commands_test) add_test(compile_commands_test compile_commands_test)
add_executable(filesystem_test filesystem_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(filesystem_test filesystem_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(filesystem_test juci_shared) target_link_libraries(filesystem_test juci_shared ${PYTHON_LIBRARIES})
add_test(filesystem_test filesystem_test) add_test(filesystem_test filesystem_test)
add_executable(cmake_build_test cmake_build_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(cmake_build_test cmake_build_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(cmake_build_test juci_shared) target_link_libraries(cmake_build_test juci_shared ${PYTHON_LIBRARIES})
add_test(cmake_build_test cmake_build_test) add_test(cmake_build_test cmake_build_test)
add_executable(cmake_file_api_test cmake_file_api_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(cmake_file_api_test cmake_file_api_test.cpp $<TARGET_OBJECTS:test_stubs>)
@ -39,56 +41,59 @@ if(BUILD_TESTING)
add_test(cmake_file_api_test cmake_file_api_test) add_test(cmake_file_api_test cmake_file_api_test)
add_executable(meson_build_test meson_build_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(meson_build_test meson_build_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(meson_build_test juci_shared) target_link_libraries(meson_build_test juci_shared ${PYTHON_LIBRARIES})
add_test(meson_build_test meson_build_test) add_test(meson_build_test meson_build_test)
add_executable(source_test source_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(source_test source_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(source_test juci_shared) target_link_libraries(source_test juci_shared ${PYTHON_LIBRARIES})
add_test(source_test source_test) add_test(source_test source_test)
add_executable(source_clang_test source_clang_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(source_clang_test source_clang_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(source_clang_test juci_shared) target_link_libraries(source_clang_test juci_shared ${PYTHON_LIBRARIES})
add_test(source_clang_test source_clang_test) add_test(source_clang_test source_clang_test)
add_executable(source_generic_test source_generic_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(source_generic_test source_generic_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(source_generic_test juci_shared) target_link_libraries(source_generic_test juci_shared ${PYTHON_LIBRARIES})
add_test(source_generic_test source_generic_test) add_test(source_generic_test source_generic_test)
add_executable(source_key_test source_key_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(source_key_test source_key_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(source_key_test juci_shared) target_link_libraries(source_key_test juci_shared ${PYTHON_LIBRARIES})
add_test(source_key_test source_key_test) add_test(source_key_test source_key_test)
add_executable(terminal_test terminal_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(terminal_test terminal_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(terminal_test juci_shared) target_link_libraries(terminal_test juci_shared ${PYTHON_LIBRARIES})
add_test(terminal_test terminal_test) add_test(terminal_test terminal_test)
add_executable(usages_clang_test usages_clang_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(usages_clang_test usages_clang_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(usages_clang_test juci_shared) target_link_libraries(usages_clang_test juci_shared ${PYTHON_LIBRARIES})
add_test(usages_clang_test usages_clang_test) add_test(usages_clang_test usages_clang_test)
if(LIBLLDB_FOUND) if(LIBLLDB_FOUND)
add_executable(lldb_test lldb_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(lldb_test lldb_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(lldb_test juci_shared) target_link_libraries(lldb_test juci_shared ${PYTHON_LIBRARIES})
add_test(lldb_test lldb_test) add_test(lldb_test lldb_test)
add_subdirectory("lldb_test_files") add_subdirectory("lldb_test_files")
endif() endif()
add_executable(git_test git_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(git_test git_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(git_test juci_shared) target_link_libraries(git_test juci_shared ${PYTHON_LIBRARIES})
add_test(git_test git_test) add_test(git_test git_test)
add_executable(ctags_grep_test ctags_grep_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(ctags_grep_test ctags_grep_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(ctags_grep_test juci_shared) target_link_libraries(ctags_grep_test juci_shared ${PYTHON_LIBRARIES})
add_test(ctags_grep_test ctags_grep_test) add_test(ctags_grep_test ctags_grep_test)
add_executable(tooltips_test tooltips_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(tooltips_test tooltips_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(tooltips_test juci_shared) target_link_libraries(tooltips_test juci_shared ${PYTHON_LIBRARIES})
add_test(tooltips_test tooltips_test) add_test(tooltips_test tooltips_test)
add_executable(utility_test utility_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(utility_test utility_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(utility_test juci_shared) target_link_libraries(utility_test juci_shared ${PYTHON_LIBRARIES})
add_test(utility_test utility_test) add_test(utility_test utility_test)
add_subdirectory(./python_bindings)
add_executable(language_protocol_client_test language_protocol_client_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(language_protocol_client_test language_protocol_client_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(language_protocol_client_test juci_shared) target_link_libraries(language_protocol_client_test juci_shared)
add_test(language_protocol_client_test language_protocol_client_test) add_test(language_protocol_client_test language_protocol_client_test)
@ -99,6 +104,7 @@ if(BUILD_TESTING)
add_executable(json_test json_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(json_test json_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(json_test juci_shared) target_link_libraries(json_test juci_shared)
add_test(json_test json_test) add_test(json_test json_test)
endif() endif()
if(BUILD_FUZZING) if(BUILD_FUZZING)
@ -130,5 +136,6 @@ if(BUILD_FUZZING)
add_executable(markdown_fuzzer fuzzers/markdown.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(markdown_fuzzer fuzzers/markdown.cpp $<TARGET_OBJECTS:test_stubs>)
target_compile_options(markdown_fuzzer PRIVATE -fsanitize=address,fuzzer) target_compile_options(markdown_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_options(markdown_fuzzer PRIVATE -fsanitize=address,fuzzer) target_link_options(markdown_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_libraries(markdown_fuzzer juci_shared) target_link_libraries(markdown_fuzzer juci_shared ${PYTHON_LIBRARIES})
endif() endif()

34
tests/python_bindings/CMakeLists.txt

@ -0,0 +1,34 @@
add_library(test_suite test_suite.cc)
target_link_libraries(test_suite juci_shared ${PYTHON_LIBRARIES})
include_directories(${CMAKE_SOURCE_DIR}/tests/python_bindings)
add_executable(pb_terminal_test Terminal_tests/terminal_test.cc $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(pb_terminal_test test_suite)
add_test(pb_terminal_test pb_terminal_test)
add_executable(pb_python_module_test PythonModule_tests/python_module_test.cc $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(pb_python_module_test test_suite)
add_test(pb_python_module_test pb_python_module_test)
add_executable(pb_cmake_test CMake_tests/cmake_test.cc $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(pb_cmake_test test_suite)
add_test(pb_cmake_test pb_cmake_test)
add_executable(pb_compile_commands_test CompileCommands_tests/compile_commands_test.cc $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(pb_compile_commands_test test_suite)
add_test(pb_compile_commands_test pb_compile_commands_test)
add_executable(pb_config_test Config_tests/config_test.cc $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(pb_config_test test_suite)
add_test(pb_config_test pb_config_test)
# add_executable(pb_ctags_test Ctags_tests/ctags_test.cc $<TARGET_OBJECTS:test_stubs>)
# target_link_libraries(pb_ctags_test test_suite)
# add_test(pb_ctags_test pb_ctags_test)
if(LIBLLDB_FOUND AND NOT DEBIAN_STRETCH_FOUND)
add_executable(pb_debug_lldb_test Debug_lldb_tests/debug_lldb_test.cc $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(pb_debug_lldb_test test_suite)
add_test(pb_debug_lldb_test pb_debug_lldb_test)
endif()

21
tests/python_bindings/CMake_tests/cmake_test.cc

@ -0,0 +1,21 @@
#include "test_suite.h"
#include <iostream>
int main() {
auto &config = Config::get();
#ifdef _WIN32
config.project.cmake.command = "cmake -G\"MSYS Makefiles\" -DCMAKE_INSTALL_PREFIX=/mingw64";
#else
config.project.cmake.command = "cmake";
#endif
auto suite_name = "CMake_tests";
suite test_suite(suite_name);
auto module = py::module::import("cmake_test");
try {
module.attr("run")((test_suite.test_file_path / "cmake_project").string());
test_suite.has_assertion = true;
} catch(const py::error_already_set &error){
std::cout << error.what();
}
}

14
tests/python_bindings/CMake_tests/cmake_test.py

@ -0,0 +1,14 @@
from Jucipp import CMake
from jucipp_test import assert_equal
def run(project_path):
cmake = CMake(project_path)
assert_equal(project_path, cmake.project_path)
default_build_path = project_path + "/build"
assert_equal(True, cmake.update_default_build(default_build_path))
executable = cmake.get_executable(default_build_path, project_path)
assert_equal(default_build_path + "/cmake_project", executable)
default_debug_path = project_path + "/debug"
assert_equal(True, cmake.update_debug_build(default_debug_path))
executable = cmake.get_executable(default_debug_path, project_path)

27
tests/python_bindings/CompileCommands_tests/compile_commands_test.cc

@ -0,0 +1,27 @@
#include "cmake.hpp"
#include "test_suite.h"
#include <iostream>
int main() {
auto suite_name = "CompileCommands_tests";
suite test_suite(suite_name);
auto project_path = test_suite.test_file_path / "cmake_project";
auto &config = Config::get();
#ifdef _WIN32
std::string slash = "\\";
config.project.cmake.command = "cmake -G\"MSYS Makefiles\" -DCMAKE_INSTALL_PREFIX=/mingw64";
#else
std::string slash = "/";
config.project.cmake.command = "cmake";
#endif
CMake cmake(project_path);
cmake.update_default_build(boost::filesystem::path(project_path) / "build");
try {
auto module = py::module::import("compile_commands_test");
module.attr("run")(project_path.make_preferred().string(), slash);
test_suite.has_assertion = true;
}
catch(const py::error_already_set &error) {
std::cout << error.what();
}
}

37
tests/python_bindings/CompileCommands_tests/compile_commands_test.py

@ -0,0 +1,37 @@
from Jucipp import CompileCommands
from os import path
from jucipp_test import assert_equal
def run(project_path, slash):
build_path = project_path + slash + "build"
cc = CompileCommands(build_path)
commands = cc.commands
assert len(commands) == 1, "Wrong length of compile commands"
command = commands.pop()
assert_equal(build_path, command.directory)
assert_equal(project_path + slash + "main.cpp", command.file)
params = command.parameters
param = path.basename(params.pop())
assert_equal("main.cpp", param)
param = params.pop()
assert_equal("-c", param)
param = params.pop()
param = params.pop()
assert_equal("-o", param)
values = command.parameter_values("-c")
value = path.basename(values.pop())
assert_equal("main.cpp", value)
assert_equal(True, CompileCommands.is_source(project_path + slash + "main.cpp"))
assert_equal(False, CompileCommands.is_header(project_path + slash + "main.cpp"))
arguments = CompileCommands.get_arguments(build_path, project_path + slash + "main.cpp")
argument = arguments.pop()
assert_equal(build_path, argument)

90
tests/python_bindings/Config_tests/config_test.cc

@ -0,0 +1,90 @@
#include "test_suite.h"
#include <iostream>
int main() {
const auto suite_name = "Config_tests";
const auto doTest = [&](const std::string &test, const std::function<void(Config & config)> &assertions) {
auto &config = Config::get();
suite test_suite(suite_name);
try {
auto module = py::module::import("config_test");
module.attr(test.c_str())();
assertions(config);
test_suite.has_assertion = true;
}
catch(const py::error_already_set &error) {
std::cout << error.what();
}
};
doTest("menu", [](Config &config) {
g_assert_cmpstr(config.menu.keys.at("key").c_str(), ==, "value");
});
doTest("theme", [](Config &config) {
g_assert_cmpstr(config.theme.name.c_str(), ==, "Star Wars");
g_assert_cmpstr(config.theme.variant.c_str(), ==, "Instrumental");
g_assert_cmpstr(config.theme.font.c_str(), ==, "Imperial");
});
doTest("terminal", [](Config &config) {
g_assert_cmpstr(config.terminal.font.c_str(), ==, "Comic Sans");
g_assert_cmpuint(config.terminal.history_size, ==, 3);
});
doTest("project", [](Config &config) {
g_assert_cmpstr(config.project.default_build_path.c_str(), ==, "/build");
g_assert_cmpstr(config.project.debug_build_path.c_str(), ==, "/debug");
g_assert_cmpstr(config.project.meson.command.c_str(), ==, "meson");
g_assert_cmpstr(config.project.meson.compile_command.c_str(), ==, "meson --build");
g_assert_cmpstr(config.project.cmake.command.c_str(), ==, "cmake");
g_assert_cmpstr(config.project.cmake.compile_command.c_str(), ==, "cmake --build");
g_assert_true(config.project.save_on_compile_or_run);
// g_assert_false(config.project.clear_terminal_on_compile);
g_assert_cmpstr(config.project.ctags_command.c_str(), ==, "ctags");
g_assert_cmpstr(config.project.python_command.c_str(), ==, "python");
});
doTest("source", [](Config &config) {
g_assert_cmpstr(config.source.style.c_str(), ==, "Classical");
g_assert_cmpstr(config.source.font.c_str(), ==, "Monospaced");
g_assert_cmpstr(config.source.spellcheck_language.c_str(), ==, "Klingon");
g_assert_false(config.source.cleanup_whitespace_characters);
g_assert_cmpstr(config.source.show_whitespace_characters.c_str(), ==, "no");
g_assert_false(config.source.format_style_on_save);
g_assert_false(config.source.format_style_on_save_if_style_file_found);
g_assert_false(config.source.smart_inserts);
g_assert_false(config.source.show_map);
// g_assert_cmpstr(config.source.map_font_size.c_str(), ==, "10px");
g_assert_false(config.source.show_git_diff);
g_assert_false(config.source.show_background_pattern);
g_assert_false(config.source.show_right_margin);
g_assert_cmpuint(config.source.right_margin_position, ==, 10);
g_assert_false(config.source.auto_tab_char_and_size);
g_assert_cmpint(config.source.default_tab_char, ==, 'c');
g_assert_cmpuint(config.source.default_tab_size, ==, 1);
g_assert_false(config.source.tab_indents_line);
// g_assert_false(config.source.wrap_lines);
g_assert_false(config.source.highlight_current_line);
g_assert_false(config.source.show_line_numbers);
g_assert_false(config.source.enable_multiple_cursors);
g_assert_false(config.source.auto_reload_changed_files);
g_assert_cmpstr(config.source.clang_format_style.c_str(), ==, "CFS");
g_assert_cmpuint(config.source.clang_usages_threads, ==, 1);
g_assert_cmpuint(config.source.documentation_searches.size(), ==, 1);
auto ds = config.source.documentation_searches.at("cpp");
g_assert_cmpstr(ds.separator.c_str(), ==, "::");
g_assert_cmpint(ds.queries.size(), ==, 1);
g_assert_cmpstr(ds.queries.at("key").c_str(), ==, "value");
});
doTest("log", [](Config &config) {
g_assert_true(config.log.libclang);
g_assert_false(config.log.language_server);
});
doTest("cfg", [](Config &config) {
g_assert_cmpstr(config.home_juci_path.string().c_str(), ==, "/away");
g_assert_cmpstr(config.home_path.string().c_str(), ==, "/home");
});
}

87
tests/python_bindings/Config_tests/config_test.py

@ -0,0 +1,87 @@
from Jucipp import Config
def menu():
menu = Config.Menu()
menu.keys = {
'key': 'value',
}
Config().menu = menu
def theme():
theme = Config.Theme()
theme.name = "Star Wars"
theme.variant = "Instrumental"
theme.font = "Imperial"
Config().theme = theme
def terminal():
terminal = Config.Terminal()
terminal.font = "Comic Sans"
terminal.history_size = 3
Config().terminal = terminal
def project():
project = Config.Project()
project.default_build_path = "/build"
project.debug_build_path = "/debug"
meson = Config.Project.Meson()
meson.command = "meson"
meson.compile_command = "meson --build"
cmake = Config.Project.CMake()
cmake.command = "cmake"
cmake.compile_command = "cmake --build"
project.meson = meson
project.cmake = cmake
project.save_on_compile_or_run = True
# project.clear_terminal_on_compile = False
project.ctags_command = "ctags"
project.python_command = "python"
Config().project = project
def source():
source = Config.Source()
source.style = "Classical"
source.font = "Monospaced"
source.spellcheck_language = "Klingon"
source.cleanup_whitespace_characters = False
source.show_whitespace_characters = "no"
source.format_style_on_save = False
source.format_style_on_save_if_style_file_found = False
source.smart_inserts = False
source.show_map = False
# source.map_font_size = "10px"
source.show_git_diff = False
source.show_background_pattern = False
source.show_right_margin = False
source.right_margin_position = 10
source.auto_tab_char_and_size = False
source.default_tab_char = "c"
source.default_tab_size = 1
source.tab_indents_line = False
# source.wrap_lines = False
source.highlight_current_line = False
source.show_line_numbers = False
source.enable_multiple_cursors = False
source.auto_reload_changed_files = False
source.clang_format_style = "CFS"
source.clang_usages_threads = 1
documentation_search = Config.Source.DocumentationSearch()
documentation_search.separator = '::'
documentation_search.queries = {
'key': 'value',
}
source.documentation_searches = {
'cpp' : documentation_search
}
Config().source = source
def log():
log = Config.Log()
log.libclang = True
log.language_server = False
Config().log = log
def cfg():
config = Config()
config.home_path = "/home"
config.home_juci_path = "/away"

39
tests/python_bindings/Ctags_tests/ctags_test.cc

@ -0,0 +1,39 @@
#include "test_suite.h"
#include <iostream>
int main() {
auto &config = Config::get();
config.project.ctags_command = "ctags";
#ifdef _WIN32
std::string slash = "\\";
config.project.cmake.command =
"cmake -G\"MSYS Makefiles\" -DCMAKE_INSTALL_PREFIX=/mingw64";
#else
auto slash = "/";
config.project.cmake.command = "cmake";
#endif
auto suite_name = "Ctags_tests";
{
auto doTest = [&](auto test) {
auto test_suite = suite(suite_name);
{
auto module = py::module::import("ctags_test");
test_suite.has_assertion = false;
auto project_path = (test_suite.test_file_path / "cmake_project")
.make_preferred()
.string();
try {
module.attr(test)(project_path, slash);
test_suite.has_assertion = true;
}
catch(const std::exception &error) {
std::cout << error.what();
}
}
};
doTest("get_location");
doTest("get_locations");
}
}

27
tests/python_bindings/Ctags_tests/ctags_test.py

@ -0,0 +1,27 @@
from Jucipp import Ctags
from jucipp_test import assert_equal
def get_location(a, b):
line = 'main main.cpp /^int main() { return 0; }$/;" line:1'
location = Ctags.get_location(line, False)
if location:
assert_equal('main.cpp', location.file_path)
assert_equal(0, location.line)
assert_equal(4, location.index)
assert_equal('main', location.symbol)
assert_equal('', location.scope)
assert_equal('int main() { return 0; }', location.source)
else:
raise ValueError('File path was empty')
def get_locations(project_path, slash):
path = project_path + slash + 'main.cpp'
locations = Ctags.get_locations(project_path + slash + 'main.cpp', 'main', 'int ()')
assert_equal(len(locations), 1)
location = locations[0];
assert_equal(path, location.file_path)
assert_equal(0, location.line)
assert_equal(4, location.index)
assert_equal('main', location.symbol)
assert_equal('', location.scope)
assert_equal('int main() { return 0; }', location.source)

27
tests/python_bindings/Debug_lldb_tests/debug_lldb_test.cc

@ -0,0 +1,27 @@
#include "test_suite.h"
#include <iostream>
int main() {
auto &config = Config::get();
config.project.ctags_command = "ctags";
auto suite_name = "Debug_lldb_tests";
{
auto doTest = [&](auto test) {
auto test_suite = suite(suite_name);
auto build_path = test_suite.build_file_path / "tests" / "lldb_test_files" / "lldb_test_executable";
{
auto module = py::module::import("debug_lldb_test");
test_suite.has_assertion = false;
try {
module.attr(test)(build_path.c_str());
test_suite.has_assertion = true;
}
catch(const std::exception &error) {
std::cout << error.what();
}
}
};
doTest("start_on_exit");
}
}

21
tests/python_bindings/Debug_lldb_tests/debug_lldb_test.py

@ -0,0 +1,21 @@
from Jucipp import LLDB
from time import sleep
from jucipp_test import assert_equal
exited = False
def on_exit(exit_code):
assert_equal(0, exit_code)
global exited
exited = True
def start_on_exit(exec_path):
print(exec_path)
l = LLDB()
l.on_exit = [on_exit]
l.start(exec_path, "", [])
while not exited:
sleep(0.1)
LLDB.destroy()

1
tests/python_bindings/PythonModule_tests/exception_test.py

@ -0,0 +1 @@
invalid code

25
tests/python_bindings/PythonModule_tests/python_module_test.cc

@ -0,0 +1,25 @@
#include "python_type_casters.h"
#include <test_suite.h>
int main() {
{
suite test_suite("PythonModule_tests");
{
py::module::import("python_module_test");
test_suite.has_assertion = true;
}
}
{
suite test_suite("PythonModule_tests");
{
try {
py::module::import("exception_test");
}
catch(const py::error_already_set &error) {
test_suite.has_assertion = true;
}
}
}
return 0;
}

1
tests/python_bindings/PythonModule_tests/python_module_test.py

@ -0,0 +1 @@
import Jucipp

0
tests/python_bindings/Terminal_tests/ls/hello_world.txt

51
tests/python_bindings/Terminal_tests/terminal_test.cc

@ -0,0 +1,51 @@
#include "terminal.hpp"
#include "test_suite.h"
int main() {
const auto test_directory = "Terminal_tests";
{
suite test_suite(test_directory);
{
auto &terminal = Terminal::get();
auto connection = terminal.get_buffer()->signal_insert().connect([&](const Gtk::TextBuffer::iterator &, const Glib::ustring &msg, int) {
g_assert_cmpstr(msg.c_str(), ==, "Hello, World!\n");
test_suite.has_assertion = true;
});
auto module = py::module::import("terminal_test");
module.attr("hello_world")();
connection.disconnect();
}
}
{
suite test_suite(test_directory);
{
auto &terminal = Terminal::get();
auto connection = terminal.get_buffer()->signal_insert().connect([&](const Gtk::TextBuffer::iterator &, const Glib::ustring &msg, int) {
g_assert_cmpstr(msg.c_str(), ==, "hello_world.txt\n");
test_suite.has_assertion = true;
test_suite.app->release();
});
test_suite.app->hold();
std::thread thread([&] {
const auto ls_dir = test_suite.test_file_path / test_directory / "ls";
auto module = py::module::import("terminal_test");
auto res = module.attr("process")(ls_dir).cast<int>();
g_assert_cmpint(res, ==, 0);
});
test_suite.app->run();
thread.join();
connection.disconnect();
}
}
{
suite test_suite(test_directory);
try {
const auto ls_dir = test_suite.test_file_path / test_directory / "ls";
py::module::import("terminal_test").attr("async_process")(ls_dir);
test_suite.has_assertion = true;
}
catch(const py::error_already_set &error) {
std::cout << error.what();
}
}
}

26
tests/python_bindings/Terminal_tests/terminal_test.py

@ -0,0 +1,26 @@
from Jucipp import Terminal
from jucipp_test import assert_equal
t = Terminal()
def hello_world():
t.print("Hello, World!\n")
def clear():
t.clear()
def process(path):
p = t.process("ls", path, True)
assert_equal(p, 0)
return p
def async_print():
return t.async_print("Hello, World!")
def callback(exit_code):
assert_equal(0, exit_code)
def async_process(path):
p = t.async_process("ls", path, callback, True)
assert_equal(0, p.get_exit_status())
return p.get_exit_status()

3
tests/python_bindings/cmake_project/CMakeLists.txt

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 2.8)
project(cmake_project)
add_executable(cmake_project main.cpp)

1
tests/python_bindings/cmake_project/main.cpp

@ -0,0 +1 @@
int main() { return 0; }

5
tests/python_bindings/jucipp_test.py

@ -0,0 +1,5 @@
""" Shared test methods """
def assert_equal(expected, actual):
""" Assert two variables for equality with an useful error message """
assert actual == expected, "Expected: " + str(expected) + ", got " + str(actual)

26
tests/python_bindings/test_suite.cc

@ -0,0 +1,26 @@
#include "test_suite.h"
#include "python_module.h"
#include <gtksourceviewmm/init.h>
#include <iostream>
suite::suite(const boost::filesystem::path &path) : plugins(config) {
Gsv::init();
py::initialize_interpreter();
if(!Py_IsInitialized()) {
throw std::runtime_error("Unable to initialize interpreter");
}
auto sys = py::module::import("sys");
if(!sys) {
throw std::runtime_error("Unable to append sys path");
}
auto sys_path = sys.attr("path").cast<py::list>();
sys_path.append((test_file_path / path).string());
sys_path.append((test_file_path).string());
config.terminal.history_size = 100;
}
suite::~suite() {
if(Py_IsInitialized()) {
py::finalize_interpreter();
g_assert_true(has_assertion);
}
}

17
tests/python_bindings/test_suite.h

@ -0,0 +1,17 @@
#pragma once
#include "config.hpp"
#include "plugins.h"
#include <gtkmm.h>
class __attribute__((visibility("default")))
suite {
public:
suite(const boost::filesystem::path &path);
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create();
Config &config = Config::get();
boost::filesystem::path test_file_path = boost::filesystem::canonical(std::string(JUCI_TESTS_PATH) + "/python_bindings");
boost::filesystem::path build_file_path = boost::filesystem::canonical(JUCI_BUILD_PATH);
bool has_assertion = false;
Plugins plugins;
~suite();
};

2
tests/stubs/dialog.cpp

@ -7,3 +7,5 @@ void Dialog::Message::set_fraction(double fraction) {}
bool Dialog::Message::on_delete_event(GdkEventAny *event) { bool Dialog::Message::on_delete_event(GdkEventAny *event) {
return true; return true;
} }
void Dialog::init_module(py::module &) {}

17
tests/stubs/plugins.cc

@ -0,0 +1,17 @@
#include "plugins.h"
#include "python_module.h"
Plugins::Plugins(Config &config) : jucipp_module("Jucipp", Module::init_jucipp_module) {
#ifdef PYTHON_HOME_DIR
#ifdef _WIN32
const std::wstring python_home(PYTHON_HOME_DIR);
const std::wstring python_path(python_home + L";" + python_home + L"\\lib-dynload;" + python_home + L"\\site-packages");
Py_SetPythonHome(python_home.c_str());
Py_SetPath(python_path.c_str());
#endif
#endif
}
void Plugins::load() {}
Plugins::~Plugins() {}
Loading…
Cancel
Save