Browse Source

Merge branch 'master' of http://github.com/eidheim/jucipp

merge-requests/365/head
Jørgen Lien Sellæg 10 years ago
parent
commit
4909e94b1b
  1. 7
      CMakeLists.txt
  2. 15
      MINGW-packages/mingw-w64-gtksourceview3/0006-hack-convert-path-back-to-unix.patch
  3. 27
      MINGW-packages/mingw-w64-gtksourceview3/LICENSE
  4. 64
      MINGW-packages/mingw-w64-gtksourceview3/PKGBUILD
  5. 27
      MINGW-packages/mingw-w64-gtksourceviewmm3/LICENSE
  6. 33
      MINGW-packages/mingw-w64-gtksourceviewmm3/PKGBUILD
  7. 36
      README.md
  8. 94
      docs/install.md
  9. 68
      src/CMakeLists.txt
  10. 20
      src/cmake.cc
  11. 3
      src/cmake/Modules/FindLibClang.cmake
  12. 6
      src/cmake/Modules/FindLibClangmm.cmake
  13. 9
      src/config.cc
  14. 35
      src/entrybox.cc
  15. 8
      src/entrybox.h
  16. 17
      src/files.h
  17. 2
      src/notebook.cc
  18. 46
      src/selectiondialog.cc
  19. 5
      src/selectiondialog.h
  20. 506
      src/source.cc
  21. 40
      src/source.h
  22. 17
      src/sourcefile.cc
  23. 132
      src/terminal.cc
  24. 6
      src/terminal.h
  25. 418
      src/terminal_win.cc
  26. 4
      src/tooltips.h
  27. 15
      src/window.cc

7
CMakeLists.txt

@ -1,12 +1,9 @@
cmake_minimum_required (VERSION 2.8.4)
set(project_name juci)
set(module juci_to_python_api)
#set(module juci_to_python_api)
#### TODO WINDOWS SUPPORT ####
set(bin_install_path "/usr/local/bin")
set(lib_install_path "/usr/local/lib/python2.7/dist-packages/")
#####
#set(lib_install_path "/usr/local/lib/python2.7/dist-packages/")
project (${project_name})

15
MINGW-packages/mingw-w64-gtksourceview3/0006-hack-convert-path-back-to-unix.patch

@ -0,0 +1,15 @@
--- gtksourceview-3.13.90/configure.ac.orig 2014-08-22 00:42:28.532000000 +0400
+++ gtksourceview-3.13.90/configure.ac 2014-08-22 00:43:00.621200000 +0400
@@ -133,6 +133,12 @@
AC_MSG_RESULT([$GLADE_CATALOG_DIR])
AC_SUBST(GLADE_CATALOG_DIR)])
+case "$host" in
+ *-*-mingw*)
+ GLADE_CATALOG_DIR=`cygpath -u $GLADE_CATALOG_DIR`
+ ;;
+esac
+
# i18N stuff
IT_PROG_INTLTOOL([0.40])
AS_IF([test "$USE_NLS" = "yes"],

27
MINGW-packages/mingw-w64-gtksourceview3/LICENSE

@ -0,0 +1,27 @@
Copyright (c) 2013, Алексей
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

64
MINGW-packages/mingw-w64-gtksourceview3/PKGBUILD

@ -0,0 +1,64 @@
# Maintainer: Alexey Pavlov <alexpux@gmail.com>
_realname=gtksourceview
pkgname="${MINGW_PACKAGE_PREFIX}-${_realname}3"
pkgver=3.12.0
pkgrel=2
pkgdesc="A text widget adding syntax highlighting and more to GNOME (mingw-w64)"
arch=('any')
url="http://www.gnome.org"
license=("LGPL")
makedepends=("${MINGW_PACKAGE_PREFIX}-gcc"
"${MINGW_PACKAGE_PREFIX}-pkg-config"
"${MINGW_PACKAGE_PREFIX}-gobject-introspection"
"${MINGW_PACKAGE_PREFIX}-glade"
"${MINGW_PACKAGE_PREFIX}-vala"
"intltool"
"gtk-doc")
depends=("${MINGW_PACKAGE_PREFIX}-gtk3"
"${MINGW_PACKAGE_PREFIX}-libxml2")
options=(!libtool strip staticlibs)
source=("http://ftp.gnome.org/pub/gnome/sources/gtksourceview/${pkgver%.*}/gtksourceview-${pkgver}.tar.xz"
0006-hack-convert-path-back-to-unix.patch)
md5sums=('8850fc0aee4893668ede37a30ef05e85'
'324c9e3bb2e4fa2a4977653ce6ed6ef9')
prepare() {
cd ${_realname}-${pkgver}
patch -p1 -i ${srcdir}/0006-hack-convert-path-back-to-unix.patch
autoreconf -fi
}
build() {
mkdir -p "${srcdir}/build-${MINGW_CHOST}"
cd "${srcdir}/build-${MINGW_CHOST}"
mkdir -p docs/reference/html
cp -rf ../${_realname}-${pkgver}/docs/reference/html/* docs/reference/html
DATADIRNAME=share \
../${_realname}-${pkgver}/configure \
--prefix=${MINGW_PREFIX} \
--build=${MINGW_CHOST} \
--host=${MINGW_CHOST} \
--enable-shared \
--disable-static \
--enable-glade-catalog
LC_ALL=C make
}
package() {
cd "${srcdir}/build-${MINGW_CHOST}"
make -j1 DESTDIR="${pkgdir}" install
for ff in ${pkgdir}/${MINGW_PREFIX}/bin/libgtksourceview*.dll; do
local _reallib=$(basename $ff)
_reallib=${_reallib%.dll}
_reallib=${_reallib#lib}
sed -e "s|library=\"gtksourceview-3.0\"|library=\"${_reallib}\"|g" -i ${pkgdir}/${MINGW_PREFIX}/share/glade/catalogs/gtksourceview.xml
done
install -Dm644 "${srcdir}/${_realname}-${pkgver}/COPYING" "${pkgdir}${MINGW_PREFIX}/share/licenses/${_realname}/COPYING"
}

27
MINGW-packages/mingw-w64-gtksourceviewmm3/LICENSE

@ -0,0 +1,27 @@
Copyright (c) 2013, Алексей
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

33
MINGW-packages/mingw-w64-gtksourceviewmm3/PKGBUILD

@ -0,0 +1,33 @@
_realname=gtksourceviewmm3
pkgname="${MINGW_PACKAGE_PREFIX}-${_realname}"
pkgver=3.12.0
pkgrel=1
pkgdesc="C++ bindings for gtksourceview3 (mingw-w64)"
arch=('any')
url="https://developer.gnome.org/gtksourceviewmm/"
license=("LGPL")
makedepends=("patch" "${MINGW_PACKAGE_PREFIX}-gcc" "${MINGW_PACKAGE_PREFIX}-pkg-config")
depends=("${MINGW_PACKAGE_PREFIX}-gtksourceview3=${pkgver}")
options=('staticlibs' 'strip')
source=("https://download.gnome.org/sources/gtksourceviewmm/${pkgver%.*}/gtksourceviewmm-${pkgver}.tar.xz")
sha256sums=("73939031bcc60e6ad31a315ec84b132deba15e5732de16e75fe424a619267ace")
build() {
mkdir -p "${srcdir}/build-${MINGW_CHOST}"
cd "${srcdir}/build-${MINGW_CHOST}"
../gtksourceviewmm-${pkgver}/configure \
CXXFLAGS=-std=c++11 \
--prefix=${MINGW_PREFIX} \
--build=${MINGW_CHOST} \
--host=${MINGW_CHOST} \
--enable-shared \
--enable-static \
--disable-documentation
make
}
package() {
cd "${srcdir}/build-${MINGW_CHOST}"
make DESTDIR="${pkgdir}" install
find "${pkgdir}${MINGW_PREFIX}" -name '*.def' -o -name '*.exp' | xargs -rtl1 rm
}

36
README.md

@ -1,21 +1,23 @@
# juCi++
###### a lightweight C++-IDE with support for C++11 and C++14.
## About
A lot of IDEs struggle with good C++11/14 support. Therefore we
created juCi++. juCi++ is based of the compiler and will *always*
support new versions of C++.
Current IDEs struggle with C++ support due to the complexity of
the programming language. juCI++, however, is designed especially
towards libclang with speed in mind.
## Features
* Syntax highlighing (even C++11/14)
* Superfast autocomletion (even external libraries)
* Accurate refactoring across files
* Basic editor functionallity
* Fast and responsive
* Syntax highlighing (even C++11/14, and more than 100 other file types)
* C++ warnings and errors on the fly
* Fast C++ autocomletion (even external libraries)
* Tooltips showing type information and doxygen documentation
* Refactoring across files
* Highlighting of similar types
* write your own plugins in python (limited atm)
* Spell checking depending on file context
* Basic editor functionallity
* Write your own plugins in python (disabled at the moment)
## Dependencies ##
Please install these dependencies on your system.
* libboost-python-dev
* libboost-filesystem-dev
* libboost-log-dev
* libboost-test-dev
@ -24,19 +26,9 @@ Please install these dependencies on your system.
* libgtkmm-3.0-dev
* libgtksourceview2.0-dev
* libgtksourceviewmm-3.0-dev
* libpython-dev
* libaspell-dev
* libclang-dev
* [libclangmm](http://github.com/cppit/libclangmm/)
* cmake
* make
* clang or gcc (compiler)
## Installation ##
Quickstart:
```sh
$ cmake .
$ make
$ sudo make install
```
See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md) for more details.
See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md).

94
docs/install.md

@ -1,22 +1,90 @@
# juCi++
## Installation guide ##
Before installation, please install libclangmm see [installation guide](http://github.com/cppit/libclangmm/blob/master/docs/install.md) for installation.
## Debian
First dependencies:
Before installation, please install libclangmm, see [installation guide](http://github.com/cppit/libclangmm/blob/master/docs/install.md).
## Debian/Ubuntu
```sh
$ sudo apt-get install libboost-python-dev libboost-filesystem-dev libboost-log-dev libboost-test-dev
libboost-thread-dev libboost-system-dev libgtkmm-3.0-dev libgtksourceview2.0-dev libgtksourceviewmm-3.0-dev
libpython-dev libclang-dev make cmake gcc g++
sudo apt-get install pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev aspell-en libaspell-dev
```
Install the project:
```sh
$ git clone http://github.com/cppit/jucipp.git juci
$ cd juci
$ cmake .
$ make
$ sudo make install
git clone http://github.com/cppit/jucipp.git
cd jucipp
cmake .
make
sudo make install
```
## OS X with Homebrew (http://brew.sh/)
```sh
brew install pkg-config boost gtkmm3 gtksourceviewmm3 aspell
```
```sh
git clone https://github.com/cppit/jucipp.git
cd jucipp
cmake .
make
make install
```
##Windows with MSYS2 (https://msys2.github.io/)
Install dependencies(replace [arch] with i686 or x86_64 depending on your MSYS2 install):
```sh
pacman -S patch autoconf automake-wrapper mingw-w64-[arch]-gtkmm3 mingw-w64-[arch]-boost mingw-w64-[arch]-aspell mingw-w64-[arch]-aspell-en
```
Get juCi++ source:
```sh
git clone https://github.com/cppit/jucipp.git
cd jucipp
```
Compiling and installing gtksourceview3 and gtksourceviewmm3:
```sh
cd MINGW-packages/mingw-w64-gtksourceview3/
makepkg-mingw -sLf
pacman -U mingw-w64-[arch]-gtksourceview3-3.12.0-2-any.pkg.tar.xz
cd ../mingw-w64-gtksourceviewmm3/
makepkg-mingw -sLf
pacman -U mingw-w64-[arch]-gtksourceviewmm3-3.12.0-1-any.pkg.tar.xz
cd ../../
```
Compile and install juCi++ source:
```sh
cmake -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=/mingw[32 or 64] .
make
make install
```
<!--
## Windows with Cygwin (https://www.cygwin.com/)
**Make sure the PATH environment variable does not include paths to non-Cygwin cmake, make and g++.**
Select and install the following packages from the Cygwin-installer:
```
pkg-config libboost-devel libgtkmm3.0-devel libgtksourceviewmm3.0-devel xinit
```
Then run the following in the Cygwin Terminal:
```sh
git clone https://github.com/cppit/jucipp.git
cd jucipp
cmake .
make
make install
```
Note that we are currently working on a Windows-version without the need of an X-server.
-->
## Run
```sh
$ juci
juci
```
<!--
#### Windows
```sh
startxwin /usr/local/bin/juci
```
-->

68
src/CMakeLists.txt

@ -7,6 +7,7 @@ if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig")
endif()
INCLUDE(FindPkgConfig)
set(validation true)
@ -21,7 +22,7 @@ function(install_help APPLE UNIX WINDOWS)
endif(APPLE)
endif(UNIX)
if(WINDOWS)
message("choco install ${WINDOWS}")
#message("choco install ${WINDOWS}") #Removed this for the time being
endif(WINDOWS)
endfunction(install_help)
@ -38,10 +39,16 @@ validate(${LCL_FOUND} "clangmm" "clangmm" "clangmm")
find_package(LibClang)
validate(${LIBCLANG_FOUND} "clang" "libclang-dev" "llvm")
find_package(PythonLibs 2.7)
validate(${PYTHONLIBS_FOUND} "python" "libpython-dev" "python")
#TODO: till clang is fixed on MSYS2 ((lib)clang.dll.a is missing):
if(MSYS)
set(LIBCLANG_LIBRARIES "${CMAKE_INSTALL_PREFIX}/bin/clang.dll")
endif()
#find_package(PythonLibs 2.7)
#validate(${PYTHONLIBS_FOUND} "python" "libpython-dev" "python")
find_package(Boost 1.55 COMPONENTS python thread log system filesystem REQUIRED)
#find_package(Boost 1.55 COMPONENTS python thread log system filesystem REQUIRED)
find_package(Boost 1.55 COMPONENTS thread log system filesystem REQUIRED)
validate(${Boost_FOUND} "boost" "libboost-all-dev" "boost")
pkg_check_modules(GTKMM gtkmm-3.0) # The name GTKMM is set here for the variables abouve
@ -50,9 +57,9 @@ validate(${GTKMM_FOUND} "gtkmm" "libgtkmm-dev" "gtkmm")
pkg_check_modules(GTKSVMM gtksourceviewmm-3.0)
validate(${GTKSVMM_FOUND} "gtksvmm" "libgtksvmm-dev" "gtkmmsv")
if(${validation})
add_executable(${project_name}
juci.h
find_package(ASPELL REQUIRED)
set(source_files juci.h
juci.cc
menu.h
menu.cc
@ -66,8 +73,8 @@ if(${validation})
sourcefile.cc
window.cc
window.h
api.h
api.cc
# api.h
# api.cc
notebook.cc
notebook.h
entrybox.h
@ -75,7 +82,6 @@ if(${validation})
directories.h
directories.cc
terminal.h
terminal.cc
tooltips.h
tooltips.cc
singletons.h
@ -83,31 +89,42 @@ if(${validation})
cmake.h
cmake.cc)
add_library(${module} SHARED
api
api_ext)
if(MSYS)
list(APPEND source_files terminal_win.cc)
else()
list(APPEND source_files terminal.cc)
endif()
if(${validation})
add_executable(${project_name} ${source_files})
# add_library(${module} SHARED
# api
# api_ext)
include_directories(
${Boost_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
# ${PYTHON_INCLUDE_DIRS}
${GTKMM_INCLUDE_DIRS}
${GTKSVMM_INCLUDE_DIRS}
${LCL_INCLUDE_DIRS}
${LIBCLANG_INCLUDE_DIRS})
${LIBCLANG_INCLUDE_DIRS}
${ASPELL_INCLUDE_DIR})
link_directories(
${GTKMM_LIBRARY_DIRS}
${GTKSVMM_LIBRARY_DIRS}
${Boost_LIBRARY_DIRS}
${PYTHON_INCLUDE_DIRS}
# ${PYTHON_INCLUDE_DIRS}
${LCL_LIBRARY_DIRS}
${LIBCLANG_LIBRARY_DIRS})
set_target_properties(${module}
PROPERTIES PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib/")
# set_target_properties(${module}
# PROPERTIES PREFIX ""
# LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib/")
target_link_libraries(${module} ${PYTHON_LIBRARIES} ${Boost_LIBRARIES})
# target_link_libraries(${module} ${PYTHON_LIBRARIES} ${Boost_LIBRARIES})
# target_link_libraries(${module} ${Boost_LIBRARIES})
target_link_libraries(${project_name}
${LIBCLANG_LIBRARIES}
@ -115,11 +132,14 @@ if(${validation})
${GTKMM_LIBRARIES}
${GTKSVMM_LIBRARIES}
${Boost_LIBRARIES}
${PYTHON_LIBRARIES})
${ASPELL_LIBRARIES}
# ${PYTHON_LIBRARIES}
)
install(TARGETS ${project_name} ${module}
RUNTIME DESTINATION ${bin_install_path}
LIBRARY DESTINATION ${lib_install_path}
# install(TARGETS ${project_name} ${module}
install(TARGETS ${project_name}
RUNTIME DESTINATION bin
# LIBRARY DESTINATION ${lib_install_path}
)
endif(${validation})

20
src/cmake.cc

@ -46,9 +46,25 @@ CMake::CMake(const boost::filesystem::path &path) {
bool CMake::create_compile_commands(const boost::filesystem::path &path) {
Singleton::terminal()->print("Creating "+path.string()+"/compile_commands.json\n");
//TODO: Windows...
if(Singleton::terminal()->execute("cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path)==EXIT_SUCCESS)
if(Singleton::terminal()->execute(Singleton::Config::terminal()->cmake_command+" . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path)==EXIT_SUCCESS) {
#ifdef _WIN32 //Temporary fix to MSYS2's libclang
auto compile_commands_path=path;
compile_commands_path+="/compile_commands.json";
auto compile_commands_file=juci::filesystem::read(compile_commands_path);
size_t pos=0;
while((pos=compile_commands_file.find("-I/", pos))!=std::string::npos) {
if(pos+3<compile_commands_file.size()) {
std::string drive;
drive+=compile_commands_file[pos+3];
compile_commands_file.replace(pos, 4, "-I"+drive+":");
}
else
break;
}
juci::filesystem::write(compile_commands_path, compile_commands_file);
#endif
return true;
}
return false;
}

3
src/cmake/Modules/FindLibClang.cmake

@ -14,7 +14,8 @@
# Known LLVM release numbers.
# most recent versions come first
set(LIBCLANG_KNOWN_LLVM_VERSIONS 3.6.1
set(LIBCLANG_KNOWN_LLVM_VERSIONS 3.6.2
3.6.1
3.6
3.5.1
3.5.0 #Arch Linux

6
src/cmake/Modules/FindLibClangmm.cmake

@ -5,12 +5,10 @@
find_package(PkgConfig)
find_path(LCL_INCLUDE_DIR clangmm.h
HINTS "/usr/local/include/libclangmm/"
PATH_SUFFIXES libclangmm
)
find_library(LCL_LIBRARY NAMES clangmm
HINTS "/usr/local/lib/"
)
find_library(LCL_LIBRARY NAMES clangmm)
set(LCL_LIBRARIES ${LCL_LIBRARY} )
set(LCL_INCLUDE_DIRS ${LCL_INCLUDE_DIR} )

9
src/config.cc

@ -16,6 +16,7 @@ MainConfig::MainConfig() {
Singleton::Config::window()->theme_variant=cfg.get<std::string>("gtk_theme.variant");
Singleton::Config::terminal()->make_command=cfg.get<std::string>("project.make_command");
Singleton::Config::terminal()->cmake_command=cfg.get<std::string>("project.cmake_command");
}
void MainConfig::find_or_create_config_files() {
@ -45,12 +46,16 @@ void MainConfig::GenerateSource() {
auto source_cfg = Singleton::Config::source();
auto source_json = cfg.get_child("source");
source_cfg->spellcheck_language = source_json.get<std::string>("spellcheck_language");
source_cfg->default_tab_char = source_json.get<char>("default_tab_char");
source_cfg->default_tab_size = source_json.get<unsigned>("default_tab_size");
source_cfg->auto_tab_char_and_size = source_json.get<bool>("auto_tab_char_and_size");
source_cfg->highlight_current_line = source_json.get_value<bool>("highlight_current_line");
source_cfg->show_line_numbers = source_json.get_value<bool>("show_line_numbers");
source_cfg->wrap_lines = source_json.get<bool>("wrap_lines");
source_cfg->highlight_current_line = source_json.get<bool>("highlight_current_line");
source_cfg->show_line_numbers = source_json.get<bool>("show_line_numbers");
for (auto &i : source_json.get_child("clang_types"))
source_cfg->clang_types[i.first] = i.second.get_value<std::string>();

35
src/entrybox.cc

@ -4,12 +4,43 @@ namespace sigc {
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
}
std::unordered_map<std::string, std::vector<std::string> > EntryBox::entry_histories;
EntryBox::Entry::Entry(const std::string& content, std::function<void(const std::string& content)> on_activate, unsigned length) : Gtk::Entry(), on_activate(on_activate) {
set_max_length(length);
set_text(content);
selected_history=0;
signal_activate().connect([this](){
if(this->on_activate)
this->on_activate(get_text());
if(this->on_activate) {
auto &history=EntryBox::entry_histories[get_placeholder_text()];
auto text=get_text();
if(history.size()==0 || (history.size()>0 && *history.begin()!=text))
history.emplace(history.begin(), text);
selected_history=0;
this->on_activate(text);
}
});
signal_key_press_event().connect([this](GdkEventKey* key){
if(key->keyval==GDK_KEY_Up) {
auto &history=entry_histories[get_placeholder_text()];
if(history.size()>0) {
selected_history++;
if(selected_history>=history.size())
selected_history=history.size()-1;
set_text(history[selected_history]);
set_position(-1);
}
}
if(key->keyval==GDK_KEY_Down) {
auto &history=entry_histories[get_placeholder_text()];
if(history.size()>0) {
if(selected_history!=0)
selected_history--;
set_text(history[selected_history]);
set_position(-1);
}
}
return false;
});
}

8
src/entrybox.h

@ -4,6 +4,9 @@
#include <list>
#include <functional>
#include "gtkmm.h"
#include <unordered_map>
#include <string>
#include <vector>
class EntryBox : public Gtk::Box {
public:
@ -11,6 +14,8 @@ public:
public:
Entry(const std::string& content="", std::function<void(const std::string& content)> on_activate=nullptr, unsigned length=50);
std::function<void(const std::string& content)> on_activate;
private:
size_t selected_history;
};
class Button : public Gtk::Button {
public:
@ -39,6 +44,9 @@ public:
std::list<Button> buttons;
std::list<ToggleButton> toggle_buttons;
std::list<Label> labels;
private:
static std::unordered_map<std::string, std::vector<std::string> > entry_histories;
};
#endif // JUCI_ENTRYBOX_H_

17
src/files.h

@ -10,9 +10,14 @@ const std::string configjson =
#ifdef __APPLE__
" \"font\": \"Menlo 11\", "
#else
#ifdef _WIN32
" \"font\": \"Consolas\", "
#else
" \"font\": \"Monospace\", "
#endif
#endif
"//Use \"\" for default font, and for instance \"Monospace 12\" to also set size.\n"
" \"spellcheck_language\": \"en_US\", //Use \"\" to set language from your locale settings\n"
" \"clang_types\": {\n"
" \"8\": \"def:function\",\n"
" \"21\": \"def:function\",\n"
@ -31,6 +36,7 @@ const std::string configjson =
" \"auto_tab_char_and_size\": true, //Use false to always use default tab char and size\n"
" \"default_tab_char\": \" \", //Use \"\\t\" for regular tab\n"
" \"default_tab_size\": 2,\n"
" \"wrap_lines\": false,\n"
" \"highlight_current_line\": true,\n"
" \"show_line_numbers\": true\n"
" },\n"
@ -62,18 +68,17 @@ const std::string configjson =
" \"force_kill_last_running\": \"<primary><shift>Escape\"\n"
" },\n"
" \"project\": {\n"
#ifdef _WIN32
" \"cmake_command\": \"cmake -G\\\"MSYS Makefiles\\\"\",\n"
#else
" \"cmake_command\": \"cmake\",\n"
#endif
" \"make_command\": \"make\"\n"
" },\n"
" \"directoryfilter\": {\n"
" \"ignore\": [\n"
" \"cmake\",\n"
" \"#\",\n"
" \"~\",\n"
" \".idea\",\n"
" \".so\"\n"
" ],\n"
" \"exceptions\": [\n"
" \"cmakelists.txt\"\n"
" ]\n"
" }\n"
"}\n";

2
src/notebook.cc

@ -67,7 +67,7 @@ void Notebook::open(const boost::filesystem::path &file_path) {
else
Singleton::terminal()->print("Error: could not find project path for "+file_path.string()+"\n");
}
source_views.emplace_back(new Source::ClangView(file_path, project_path));
source_views.emplace_back(new Source::ClangView(file_path, project_path, language));
}
else
source_views.emplace_back(new Source::GenericView(file_path, language));

46
src/selectiondialog.cc

@ -64,6 +64,7 @@ void SelectionDialogBase::add_row(const std::string& row, const std::string& too
}
void SelectionDialogBase::show() {
shown=true;
move();
window->show_all();
}
@ -72,8 +73,9 @@ void SelectionDialogBase::hide() {
window->hide();
if(tooltips)
tooltips->hide();
if(on_hide)
if(on_hide && shown)
on_hide();
shown=false;
}
void SelectionDialogBase::update_tooltips() {
@ -146,7 +148,7 @@ void SelectionDialogBase::resize() {
}
}
SelectionDialog::SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark) : SelectionDialogBase(text_view, start_mark, true) {}
SelectionDialog::SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark, bool show_search_entry) : SelectionDialogBase(text_view, start_mark, show_search_entry) {}
void SelectionDialog::show() {
SelectionDialogBase::show();
@ -237,6 +239,46 @@ void SelectionDialog::show() {
}
}
bool SelectionDialog::on_key_press(GdkEventKey* key) {
if(key->keyval==GDK_KEY_Down && list_view_text.get_model()->children().size()>0) {
auto it=list_view_text.get_selection()->get_selected();
if(it) {
it++;
if(it) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(it));
}
}
else
list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
return true;
}
if(key->keyval==GDK_KEY_Up && list_view_text.get_model()->children().size()>0) {
auto it=list_view_text.get_selection()->get_selected();
if(it) {
it--;
if(it) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(it));
}
}
else {
auto last_it=list_view_text.get_model()->children().end();
last_it--;
list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it));
}
return true;
}
if(key->keyval==GDK_KEY_Return || key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) {
auto it=list_view_text.get_selection()->get_selected();
auto column=list_view_text.get_column(0);
list_view_text.row_activated(list_view_text.get_model()->get_path(it), *column);
return true;
}
hide();
if(key->keyval==GDK_KEY_Escape)
return true;
return false;
}
CompletionDialog::CompletionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark) : SelectionDialogBase(text_view, start_mark, false) {}
void CompletionDialog::show() {

5
src/selectiondialog.h

@ -31,11 +31,14 @@ protected:
std::unique_ptr<Tooltips> tooltips;
std::unordered_map<std::string, std::string> tooltip_texts;
std::string last_row;
private:
bool shown=false;
};
class SelectionDialog : public SelectionDialogBase {
public:
SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark);
SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark, bool show_search_entry=true);
bool on_key_press(GdkEventKey* key);
void show();
};

506
src/source.cc

@ -7,8 +7,8 @@
#include "singletons.h"
#include <gtksourceview/gtksource.h>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
namespace sigc {
@ -36,6 +36,8 @@ Glib::RefPtr<Gsv::Language> Source::guess_language(const boost::filesystem::path
//////////////
//// View ////
//////////////
AspellConfig* Source::View::spellcheck_config=NULL;
Source::View::View(const boost::filesystem::path &file_path): file_path(file_path) {
set_smart_home_end(Gsv::SMART_HOME_END_BEFORE);
get_source_buffer()->begin_not_undoable_action();
@ -70,11 +72,67 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat
Singleton::terminal()->print("Error: Could not find gtksourceview style: "+Singleton::Config::source()->style+'\n');
}
if(Singleton::Config::source()->wrap_lines)
set_wrap_mode(Gtk::WrapMode::WRAP_CHAR);
property_highlight_current_line() = Singleton::Config::source()->highlight_current_line;
property_show_line_numbers() = Singleton::Config::source()->show_line_numbers;
if(Singleton::Config::source()->font.size()>0)
override_font(Pango::FontDescription(Singleton::Config::source()->font));
//Create tags for diagnostic warnings and errors:
auto scheme = get_source_buffer()->get_style_scheme();
auto tag_table=get_buffer()->get_tag_table();
auto style=scheme->get_style("def:warning");
auto diagnostic_tag=get_source_buffer()->create_tag("def:warning");
auto diagnostic_tag_underline=get_source_buffer()->create_tag("def:warning_underline");
if(style && (style->property_foreground_set() || style->property_background_set())) {
Glib::ustring warning_property;
if(style->property_foreground_set()) {
warning_property=style->property_foreground().get_value();
diagnostic_tag->property_foreground() = warning_property;
}
else if(style->property_background_set())
warning_property=style->property_background().get_value();
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", Gdk::RGBA(warning_property));
}
}
style=scheme->get_style("def:error");
diagnostic_tag=get_source_buffer()->create_tag("def:error");
diagnostic_tag_underline=get_source_buffer()->create_tag("def:error_underline");
if(style && (style->property_foreground_set() || style->property_background_set())) {
Glib::ustring error_property;
if(style->property_foreground_set()) {
error_property=style->property_foreground().get_value();
diagnostic_tag->property_foreground() = error_property;
}
else if(style->property_background_set())
error_property=style->property_background().get_value();
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", Gdk::RGBA(error_property));
}
}
//TODO: clear tag_class and param_spec?
//Add tooltip foreground and background
style = scheme->get_style("def:note");
auto note_tag=get_source_buffer()->create_tag("def:note_background");
if(style->property_background_set()) {
note_tag->property_background()=style->property_background();
}
note_tag=get_source_buffer()->create_tag("def:note");
if(style->property_foreground_set()) {
note_tag->property_foreground()=style->property_foreground();
}
tab_char=Singleton::Config::source()->default_tab_char;
tab_size=Singleton::Config::source()->default_tab_size;
if(Singleton::Config::source()->auto_tab_char_and_size) {
@ -97,6 +155,159 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat
tab+=tab_char;
tabs_regex=std::regex(std::string("^(")+tab_char+"*)(.*)$");
if(spellcheck_config==NULL) {
spellcheck_config=new_aspell_config();
if(Singleton::Config::source()->spellcheck_language.size()>0)
aspell_config_replace(spellcheck_config, "lang", Singleton::Config::source()->spellcheck_language.c_str());
}
spellcheck_possible_err=new_aspell_speller(spellcheck_config);
spellcheck_checker=NULL;
if (aspell_error_number(spellcheck_possible_err) != 0)
std::cerr << "Spell check error: " << aspell_error_message(spellcheck_possible_err) << std::endl;
else {
spellcheck_checker = to_aspell_speller(spellcheck_possible_err);
auto tag=get_buffer()->create_tag("spellcheck_error");
tag->property_underline()=Pango::Underline::UNDERLINE_ERROR;
get_buffer()->signal_changed().connect([this](){
delayed_spellcheck_suggestions_connection.disconnect();
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.backward_char()) {
auto context_iter=iter;
if(context_iter.backward_char()) {
if((spellcheck_all && !get_source_buffer()->iter_has_context_class(context_iter, "no-spell-check")) || get_source_buffer()->iter_has_context_class(context_iter, "comment") || get_source_buffer()->iter_has_context_class(context_iter, "string")) {
if(*iter==32 || *iter==45) { //Might have used space or - to split two words
auto first=iter;
auto second=iter;
if(first.backward_char() && second.forward_char()) {
get_buffer()->remove_tag_by_name("spellcheck_error", first, second);
auto word=spellcheck_get_word(first);
spellcheck_word(word.first, word.second);
word=spellcheck_get_word(second);
spellcheck_word(word.first, word.second);
}
}
else {
auto word=spellcheck_get_word(iter);
spellcheck_word(word.first, word.second);
}
}
}
}
});
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iter, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(mark->get_name()=="insert") {
if(spellcheck_suggestions_dialog_shown)
spellcheck_suggestions_dialog->hide();
delayed_spellcheck_suggestions_connection.disconnect();
delayed_spellcheck_suggestions_connection=Glib::signal_timeout().connect([this]() {
auto tags=get_buffer()->get_insert()->get_iter().get_tags();
bool need_suggestions=false;
for(auto &tag: tags) {
if(tag->property_name()=="spellcheck_error") {
need_suggestions=true;
break;
}
}
if(need_suggestions) {
spellcheck_suggestions_dialog=std::unique_ptr<SelectionDialog>(new SelectionDialog(*this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()), false));
spellcheck_suggestions_dialog->on_hide=[this](){
spellcheck_suggestions_dialog_shown=false;
};
auto word=spellcheck_get_word(get_buffer()->get_insert()->get_iter());
auto suggestions=spellcheck_get_suggestions(word.first, word.second);
if(suggestions.size()==0)
return false;
for(auto &suggestion: suggestions)
spellcheck_suggestions_dialog->add_row(suggestion);
spellcheck_suggestions_dialog->on_select=[this, word](const std::string& selected, bool hide_window) {
get_source_buffer()->begin_user_action();
get_buffer()->erase(word.first, word.second);
get_buffer()->insert(get_buffer()->get_insert()->get_iter(), selected);
get_source_buffer()->end_user_action();
delayed_tooltips_connection.disconnect();
};
spellcheck_suggestions_dialog->show();
spellcheck_suggestions_dialog_shown=true;
}
return false;
}, 500);
}
});
}
set_tooltip_events();
}
void Source::View::set_tooltip_events() {
signal_motion_notify_event().connect([this](GdkEventMotion* event) {
if(on_motion_last_x!=event->x || on_motion_last_y!=event->y) {
delayed_tooltips_connection.disconnect();
if(event->state==0) {
gdouble x=event->x;
gdouble y=event->y;
delayed_tooltips_connection=Glib::signal_timeout().connect([this, x, y]() {
Tooltips::init();
Gdk::Rectangle rectangle(x, y, 1, 1);
if(source_readable) {
type_tooltips.show(rectangle);
diagnostic_tooltips.show(rectangle);
}
return false;
}, 100);
}
type_tooltips.hide();
diagnostic_tooltips.hide();
}
on_motion_last_x=event->x;
on_motion_last_y=event->y;
return false;
});
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(get_buffer()->get_has_selection() && mark->get_name()=="selection_bound")
delayed_tooltips_connection.disconnect();
if(mark->get_name()=="insert") {
delayed_tooltips_connection.disconnect();
delayed_tooltips_connection=Glib::signal_timeout().connect([this]() {
Tooltips::init();
Gdk::Rectangle rectangle;
get_iter_location(get_buffer()->get_insert()->get_iter(), rectangle);
int location_window_x, location_window_y;
buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, rectangle.get_x(), rectangle.get_y(), location_window_x, location_window_y);
rectangle.set_x(location_window_x-2);
rectangle.set_y(location_window_y);
rectangle.set_width(4);
if(source_readable) {
type_tooltips.show(rectangle);
diagnostic_tooltips.show(rectangle);
}
return false;
}, 500);
type_tooltips.hide();
diagnostic_tooltips.hide();
}
});
signal_scroll_event().connect([this](GdkEventScroll* event) {
delayed_tooltips_connection.disconnect();
type_tooltips.hide();
diagnostic_tooltips.hide();
return false;
});
signal_focus_out_event().connect([this](GdkEventFocus* event) {
delayed_tooltips_connection.disconnect();
type_tooltips.hide();
diagnostic_tooltips.hide();
return false;
});
}
void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data) {
@ -108,6 +319,8 @@ void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* pro
Source::View::~View() {
g_clear_object(&search_context);
g_clear_object(&search_settings);
delete_aspell_speller(spellcheck_checker);
}
void Source::View::search_highlight(const std::string &text, bool case_sensitive, bool regex) {
@ -280,6 +493,11 @@ string Source::View::get_line_before_insert() {
//Basic indentation
bool Source::View::on_key_press_event(GdkEventKey* key) {
if(spellcheck_suggestions_dialog_shown) {
if(spellcheck_suggestions_dialog->on_key_press(key))
return true;
}
get_source_buffer()->begin_user_action();
//Indent as in next or previous line
if(key->keyval==GDK_KEY_Return && key->state==0 && !get_buffer()->get_has_selection()) {
@ -390,6 +608,28 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
return stop;
}
bool Source::View::on_button_press_event(GdkEventButton *event) {
if(event->type==GDK_2BUTTON_PRESS) {
Gtk::TextIter start, end;
if(get_buffer()->get_selection_bounds(start, end)) {
auto iter=start;
while((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==95) {
start=iter;
if(!iter.backward_char())
break;
}
while((*end>=48 && *end<=57) || (*end>=65 && *end<=90) || (*end>=97 && *end<=122) || *end==95) {
if(!end.forward_char())
break;
}
get_buffer()->select_range(start, end);
return true;
}
}
return Gsv::View::on_button_press_event(event);
}
std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
const std::regex indent_regex("^([ \t]+).*$");
auto size=get_buffer()->get_line_count();
@ -442,6 +682,51 @@ std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
return {found_tab_char, found_tab_size};
}
std::pair<Gtk::TextIter, Gtk::TextIter> Source::View::spellcheck_get_word(Gtk::TextIter iter) {
auto start=iter;
auto end=iter;
while((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==39 || *iter>=128) {
start=iter;
if(!iter.backward_char())
break;
}
while((*end>=48 && *end<=57) || (*end>=65 && *end<=90) || (*end>=97 && *end<=122) || *iter==39 || *end>=128) {
if(!end.forward_char())
break;
}
return {start, end};
}
void Source::View::spellcheck_word(const Gtk::TextIter& start, const Gtk::TextIter& end) {
auto word=get_buffer()->get_text(start, end);
if(word.size()>0) {
auto correct = aspell_speller_check(spellcheck_checker, word.data(), word.bytes());
if(correct==0)
get_buffer()->apply_tag_by_name("spellcheck_error", start, end);
else
get_buffer()->remove_tag_by_name("spellcheck_error", start, end);
}
}
std::vector<std::string> Source::View::spellcheck_get_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end) {
auto word_with_error=get_buffer()->get_text(start, end);
const AspellWordList *suggestions = aspell_speller_suggest(spellcheck_checker, word_with_error.data(), word_with_error.bytes());
AspellStringEnumeration *elements = aspell_word_list_elements(suggestions);
std::vector<std::string> words;
const char *word;
while ((word = aspell_string_enumeration_next(elements))!= NULL) {
words.emplace_back(word);
}
delete_aspell_string_enumeration(elements);
return words;
}
/////////////////////
//// GenericView ////
/////////////////////
@ -450,6 +735,7 @@ Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib:
get_source_buffer()->set_language(language);
Singleton::terminal()->print("Language for file "+file_path.string()+" set to "+language->get_name()+".\n");
}
spellcheck_all=true;
auto completion=get_completion();
auto completion_words=Gsv::CompletionWords::create("", Glib::RefPtr<Gdk::Pixbuf>());
completion_words->register_provider(get_buffer());
@ -490,58 +776,6 @@ Source::View(file_path), project_path(project_path) {
}
INFO("Tagtable filled");
//Create tags for diagnostic warnings and errors:
auto style=scheme->get_style("def:warning");
auto diagnostic_tag=get_source_buffer()->create_tag("def:warning");
auto diagnostic_tag_underline=get_source_buffer()->create_tag("def:warning_underline");
if(style && (style->property_foreground_set() || style->property_background_set())) {
Glib::ustring warning_property;
if(style->property_foreground_set()) {
warning_property=style->property_foreground().get_value();
diagnostic_tag->property_foreground() = warning_property;
}
else if(style->property_background_set())
warning_property=style->property_background().get_value();
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", Gdk::RGBA(warning_property));
}
}
style=scheme->get_style("def:error");
diagnostic_tag=get_source_buffer()->create_tag("def:error");
diagnostic_tag_underline=get_source_buffer()->create_tag("def:error_underline");
if(style && (style->property_foreground_set() || style->property_background_set())) {
Glib::ustring error_property;
if(style->property_foreground_set()) {
error_property=style->property_foreground().get_value();
diagnostic_tag->property_foreground() = error_property;
}
else if(style->property_background_set())
error_property=style->property_background().get_value();
diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR;
auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions:
auto param_spec=g_object_class_find_property(tag_class, "underline-rgba");
if(param_spec!=NULL) {
diagnostic_tag_underline->set_property("underline-rgba", Gdk::RGBA(error_property));
}
}
//TODO: clear tag_class and param_spec?
//Add tooltip foreground and background
style = scheme->get_style("def:note");
auto note_tag=get_source_buffer()->create_tag("def:note_background");
if(style->property_background_set()) {
note_tag->property_background()=style->property_background();
}
note_tag=get_source_buffer()->create_tag("def:note");
if(style->property_foreground_set()) {
note_tag->property_foreground()=style->property_foreground();
}
parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path.string());
//GTK-calls must happen in main thread, so the parse_thread
//sends signals to the main thread that it is to call the following functions:
@ -560,7 +794,7 @@ Source::View(file_path), project_path(project_path) {
update_syntax();
update_diagnostics();
update_types();
clang_readable=true;
source_readable=true;
set_status("");
parsing_mutex.unlock();
INFO("Syntax updated");
@ -579,8 +813,6 @@ Source::View(file_path), project_path(project_path) {
diagnostic_tooltips.hide();
});
get_buffer()->signal_mark_set().connect(sigc::mem_fun(*this, &Source::ClangViewParse::on_mark_set), false);
bracket_regex=std::regex(std::string("^(")+tab_char+"*).*\\{ *$");
no_bracket_statement_regex=std::regex(std::string("^(")+tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$");
no_bracket_no_para_statement_regex=std::regex(std::string("^(")+tab_char+"*)(else|try|do) *$");
@ -590,7 +822,7 @@ void Source::ClangViewParse::init_parse() {
type_tooltips.hide();
diagnostic_tooltips.hide();
get_buffer()->remove_all_tags(get_buffer()->begin(), get_buffer()->end());
clang_readable=false;
source_readable=false;
parse_thread_go=true;
parse_thread_mapped=false;
parse_thread_stop=false;
@ -654,10 +886,10 @@ std::map<std::string, std::string> Source::ClangViewParse::get_buffer_map() cons
void Source::ClangViewParse::start_reparse() {
parse_thread_mapped=false;
clang_readable=false;
source_readable=false;
delayed_reparse_connection.disconnect();
delayed_reparse_connection=Glib::signal_timeout().connect([this]() {
clang_readable=false;
source_readable=false;
parse_thread_go=true;
set_status("parsing...");
return false;
@ -683,6 +915,39 @@ std::vector<std::string> Source::ClangViewParse::get_compilation_commands() {
}
if(file_path.extension()==".h") //TODO: temporary fix for .h-files (parse as c++)
arguments.emplace_back("-xc++");
#ifdef _WIN32 //Temporary fix to MSYS2's libclang
arguments.emplace_back("-IC:/msys32/mingw32/lib/gcc/i686-w64-mingw32/5.2.0/include");
arguments.emplace_back("-IC:/msys32/mingw32//include");
arguments.emplace_back("-IC:/msys32/mingw32/lib/gcc/i686-w64-mingw32/5.2.0/include-fixed");
arguments.emplace_back("-IC:/msys32/mingw32/i686-w64-mingw32/include");
arguments.emplace_back("-IC:/msys32/mingw32/include/c++/5.2.0");
arguments.emplace_back("-IC:/msys32/mingw32/include/c++/5.2.0/i686-w64-mingw32");
arguments.emplace_back("-IC:/msys32/mingw32/include/c++/5.2.0/backward");
arguments.emplace_back("-IC:/msys32/mingw64/lib/gcc/i686-w64-mingw32/5.2.0/include");
arguments.emplace_back("-IC:/msys32/mingw64//include");
arguments.emplace_back("-IC:/msys32/mingw64/lib/gcc/i686-w64-mingw32/5.2.0/include-fixed");
arguments.emplace_back("-IC:/msys32/mingw64/i686-w64-mingw32/include");
arguments.emplace_back("-IC:/msys32/mingw64/include/c++/5.2.0");
arguments.emplace_back("-IC:/msys32/mingw64/include/c++/5.2.0/i686-w64-mingw32");
arguments.emplace_back("-IC:/msys32/mingw64/include/c++/5.2.0/backward");
arguments.emplace_back("-IC:/msys64/mingw32/lib/gcc/i686-w64-mingw32/5.2.0/include");
arguments.emplace_back("-IC:/msys64/mingw32//include");
arguments.emplace_back("-IC:/msys64/mingw32/lib/gcc/i686-w64-mingw32/5.2.0/include-fixed");
arguments.emplace_back("-IC:/msys64/mingw32/i686-w64-mingw32/include");
arguments.emplace_back("-IC:/msys64/mingw32/include/c++/5.2.0");
arguments.emplace_back("-IC:/msys64/mingw32/include/c++/5.2.0/i686-w64-mingw32");
arguments.emplace_back("-IC:/msys64/mingw32/include/c++/5.2.0/backward");
arguments.emplace_back("-IC:/msys64/mingw64/lib/gcc/i686-w64-mingw32/5.2.0/include");
arguments.emplace_back("-IC:/msys64/mingw64//include");
arguments.emplace_back("-IC:/msys64/mingw64/lib/gcc/i686-w64-mingw32/5.2.0/include-fixed");
arguments.emplace_back("-IC:/msys64/mingw64/i686-w64-mingw32/include");
arguments.emplace_back("-IC:/msys64/mingw64/include/c++/5.2.0");
arguments.emplace_back("-IC:/msys64/mingw64/include/c++/5.2.0/i686-w64-mingw32");
arguments.emplace_back("-IC:/msys64/mingw64/include/c++/5.2.0/backward");
#endif
return arguments;
}
@ -793,62 +1058,6 @@ void Source::ClangViewParse::update_types() {
}
}
bool Source::ClangViewParse::on_motion_notify_event(GdkEventMotion* event) {
delayed_tooltips_connection.disconnect();
if(clang_readable && event->state==0) {
Gdk::Rectangle rectangle(event->x, event->y, 1, 1);
Tooltips::init();
type_tooltips.show(rectangle);
diagnostic_tooltips.show(rectangle);
}
else {
type_tooltips.hide();
diagnostic_tooltips.hide();
}
return Source::View::on_motion_notify_event(event);
}
void Source::ClangViewParse::on_mark_set(const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(get_buffer()->get_has_selection() && mark->get_name()=="selection_bound")
delayed_tooltips_connection.disconnect();
if(mark->get_name()=="insert") {
delayed_tooltips_connection.disconnect();
delayed_tooltips_connection=Glib::signal_timeout().connect([this]() {
if(clang_readable) {
Gdk::Rectangle rectangle;
get_iter_location(get_buffer()->get_insert()->get_iter(), rectangle);
int location_window_x, location_window_y;
buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, rectangle.get_x(), rectangle.get_y(), location_window_x, location_window_y);
rectangle.set_x(location_window_x-2);
rectangle.set_y(location_window_y);
rectangle.set_width(4);
Tooltips::init();
type_tooltips.show(rectangle);
diagnostic_tooltips.show(rectangle);
}
return false;
}, 500);
type_tooltips.hide();
diagnostic_tooltips.hide();
}
}
bool Source::ClangViewParse::on_focus_out_event(GdkEventFocus* event) {
delayed_tooltips_connection.disconnect();
type_tooltips.hide();
diagnostic_tooltips.hide();
return Source::View::on_focus_out_event(event);
}
bool Source::ClangViewParse::on_scroll_event(GdkEventScroll* event) {
delayed_tooltips_connection.disconnect();
type_tooltips.hide();
diagnostic_tooltips.hide();
return Source::View::on_scroll_event(event);
}
//Clang indentation
//TODO: replace indentation methods with a better implementation or maybe use libclang
bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
@ -874,7 +1083,9 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
return true;
}
}
if(next_line!=sm[1].str()+"}") {
auto next_char=*get_buffer()->get_insert()->get_iter();
auto next_line_with_end_bracket=sm[1].str()+"}";
if(next_char!='}' && next_line.substr(0, next_line_with_end_bracket.size())!=next_line_with_end_bracket) {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab+"\n"+sm[1].str()+"}");
auto insert_it = get_source_buffer()->get_insert()->get_iter();
for(size_t c=0;c<sm[1].str().size()+2;c++)
@ -884,6 +1095,16 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
get_source_buffer()->end_user_action();
return true;
}
else if(next_char=='}') {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab+"\n"+sm[1].str());
auto insert_it = get_source_buffer()->get_insert()->get_iter();
for(size_t c=0;c<sm[1].str().size()+1;c++)
insert_it--;
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
get_source_buffer()->end_user_action();
return true;
}
else {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert());
@ -986,6 +1207,14 @@ Source::ClangViewParse(file_path, project_path), autocomplete_cancel_starting(fa
return false;
}, false);
signal_focus_out_event().connect([this](GdkEventFocus* event) {
autocomplete_cancel_starting=true;
if(completion_dialog_shown) {
completion_dialog->hide();
}
return false;
});
}
bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) {
@ -997,15 +1226,6 @@ bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) {
return ClangViewParse::on_key_press_event(key);
}
bool Source::ClangViewAutocomplete::on_focus_out_event(GdkEventFocus* event) {
autocomplete_cancel_starting=true;
if(completion_dialog_shown) {
completion_dialog->hide();
}
return Source::ClangViewParse::on_focus_out_event(event);
}
void Source::ClangViewAutocomplete::start_autocomplete() {
if(!has_focus())
return;
@ -1118,16 +1338,17 @@ void Source::ClangViewAutocomplete::autocomplete() {
});
std::shared_ptr<std::map<std::string, std::string> > buffer_map=std::make_shared<std::map<std::string, std::string> >();
auto& buffer=(*buffer_map)[this->file_path.string()];
buffer=get_buffer()->get_text(get_buffer()->begin(), get_buffer()->get_insert()->get_iter());
auto iter = get_source_buffer()->get_insert()->get_iter();
auto ustr=get_buffer()->get_text();
auto iter=get_buffer()->get_insert()->get_iter();
auto line_nr=iter.get_line()+1;
auto column_nr=iter.get_line_offset()+1;
while((buffer.back()>='a' && buffer.back()<='z') || (buffer.back()>='A' && buffer.back()<='Z') || (buffer.back()>='0' && buffer.back()<='9') || buffer.back()=='_') {
buffer.pop_back();
auto pos=iter.get_offset()-1;
while(pos>=0 && ((ustr[pos]>='a' && ustr[pos]<='z') || (ustr[pos]>='A' && ustr[pos]<='Z') || (ustr[pos]>='0' && ustr[pos]<='9') || ustr[pos]=='_')) {
ustr.replace(pos, 1, " ");
column_nr--;
pos--;
}
buffer+="\n";
(*buffer_map)[this->file_path.string()]=std::move(ustr); //TODO: does this work?
set_status("autocomplete...");
if(autocomplete_thread.joinable())
autocomplete_thread.join();
@ -1190,7 +1411,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
});
get_token=[this]() -> std::string {
if(clang_readable) {
if(source_readable) {
auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index();
@ -1208,7 +1429,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
};
get_token_name=[this]() -> std::string {
if(clang_readable) {
if(source_readable) {
auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index();
@ -1224,7 +1445,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
};
tag_similar_tokens=[this](const std::string &usr){
if(clang_readable) {
if(source_readable) {
if(usr.size()>0 && last_similar_tokens_tagged!=usr) {
get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end());
auto offsets=clang_tokens->get_similar_token_offsets(usr);
@ -1242,7 +1463,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
rename_similar_tokens=[this](const std::string &usr, const std::string &text) {
size_t number=0;
if(clang_readable) {
if(source_readable) {
auto offsets=clang_tokens->get_similar_token_offsets(usr);
std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > marks;
for(auto &offset: offsets) {
@ -1265,14 +1486,18 @@ Source::ClangViewAutocomplete(file_path, project_path) {
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){
if(mark->get_name()=="insert") {
delayed_tag_similar_tokens_connection.disconnect();
delayed_tag_similar_tokens_connection=Glib::signal_timeout().connect([this]() {
auto usr=get_token();
tag_similar_tokens(usr);
return false;
}, 100);
}
});
get_declaration_location=[this](){
std::pair<std::string, clang::Offset> location;
if(clang_readable) {
if(source_readable) {
auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index();
@ -1293,7 +1518,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
};
goto_method=[this](){
if(clang_readable) {
if(source_readable) {
selection_dialog=std::unique_ptr<SelectionDialog>(new SelectionDialog(*this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter())));
auto rows=std::make_shared<std::unordered_map<std::string, clang::Offset> >();
auto methods=clang_tokens->get_cxx_methods();
@ -1315,7 +1540,12 @@ Source::ClangViewAutocomplete(file_path, project_path) {
};
}
Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path): ClangViewRefactor(file_path, project_path) {
Source::ClangView::ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language): ClangViewRefactor(file_path, project_path) {
if(language) {
get_source_buffer()->set_highlight_syntax(false);
get_source_buffer()->set_language(language);
}
do_delete_object.connect([this](){
if(delete_thread.joinable())
delete_thread.join();

40
src/source.h

@ -15,6 +15,7 @@
#include "selectiondialog.h"
#include <set>
#include <regex>
#include <aspell.h>
namespace Source {
Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path);
@ -23,9 +24,11 @@ namespace Source {
public:
std::string style;
std::string font;
std::string spellcheck_language;
bool auto_tab_char_and_size;
char default_tab_char;
unsigned default_tab_size;
bool wrap_lines;
bool highlight_current_line;
bool show_line_numbers;
std::unordered_map<std::string, std::string> clang_types;
@ -74,23 +77,44 @@ namespace Source {
std::function<void(View* view, const std::string &status)> on_update_status;
std::string status;
protected:
bool source_readable;
Tooltips diagnostic_tooltips;
Tooltips type_tooltips;
gdouble on_motion_last_x;
gdouble on_motion_last_y;
sigc::connection delayed_tooltips_connection;
void set_tooltip_events();
void set_status(const std::string &status);
std::string get_line(size_t line_number);
std::string get_line_before_insert();
bool on_key_press_event(GdkEventKey* key);
bool on_button_press_event(GdkEventButton *event);
std::pair<char, unsigned> find_tab_char_and_size();
unsigned tab_size;
char tab_char;
std::string tab;
std::regex tabs_regex;
bool spellcheck_all=false;
private:
GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings;
static void search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data);
}; // class View
static AspellConfig* spellcheck_config;
AspellCanHaveError *spellcheck_possible_err;
AspellSpeller *spellcheck_checker;
std::pair<Gtk::TextIter, Gtk::TextIter> spellcheck_get_word(Gtk::TextIter iter);
void spellcheck_word(const Gtk::TextIter& start, const Gtk::TextIter& end);
std::vector<std::string> spellcheck_get_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end);
std::unique_ptr<SelectionDialog> spellcheck_suggestions_dialog;
bool spellcheck_suggestions_dialog_shown=false;
sigc::connection delayed_spellcheck_suggestions_connection;
};
class GenericView : public View {
public:
@ -101,17 +125,14 @@ namespace Source {
public:
ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
boost::filesystem::path project_path;
void start_reparse();
protected:
void init_parse();
void start_reparse();
bool on_key_press_event(GdkEventKey* key);
bool on_focus_out_event(GdkEventFocus* event);
std::unique_ptr<clang::TranslationUnit> clang_tu;
std::mutex parsing_mutex;
std::unique_ptr<clang::Tokens> clang_tokens;
bool clang_readable;
sigc::connection delayed_reparse_connection;
sigc::connection delayed_tooltips_connection;
std::shared_ptr<Terminal::InProgress> parsing_in_progress;
std::thread parse_thread;
@ -129,12 +150,7 @@ namespace Source {
std::set<std::string> last_syntax_tags;
void update_diagnostics();
void update_types();
Tooltips diagnostic_tooltips;
Tooltips type_tooltips;
bool on_motion_notify_event(GdkEventMotion* event);
void on_mark_set(const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark);
bool on_scroll_event(GdkEventScroll* event);
static clang::Index clang_index;
std::vector<std::string> get_compilation_commands();
@ -151,7 +167,6 @@ namespace Source {
ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
protected:
bool on_key_press_event(GdkEventKey* key);
bool on_focus_out_event(GdkEventFocus* event);
std::thread autocomplete_thread;
private:
void start_autocomplete();
@ -174,13 +189,14 @@ namespace Source {
private:
Glib::RefPtr<Gtk::TextTag> similar_tokens_tag;
std::string last_similar_tokens_tagged;
sigc::connection delayed_tag_similar_tokens_connection;
std::unique_ptr<SelectionDialog> selection_dialog;
bool renaming=false;
};
class ClangView : public ClangViewRefactor {
public:
ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
ClangView(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr<Gsv::Language> language);
void async_delete();
bool restart_parse();
private:

17
src/sourcefile.cc

@ -7,7 +7,7 @@ const size_t buffer_size=131072;
//Only use on small files
std::string juci::filesystem::read(const std::string &path) {
std::stringstream ss;
std::ifstream input(path);
std::ifstream input(path, std::ofstream::binary);
if(input) {
ss << input.rdbuf();
input.close();
@ -16,12 +16,16 @@ std::string juci::filesystem::read(const std::string &path) {
}
bool juci::filesystem::read(const std::string &path, Glib::RefPtr<Gtk::TextBuffer> text_buffer) {
std::ifstream input(path);
std::ifstream input(path, std::ofstream::binary);
if(input) {
std::vector<char> buffer(buffer_size);
size_t read_length;
std::string buffer_str;
while((read_length=input.read(&buffer[0], buffer_size).gcount())>0) {
auto ustr=Glib::ustring(std::string(&buffer[0], read_length));
buffer_str+=std::string(&buffer[0], read_length);
if(buffer_str.back()>=0) {
auto ustr=Glib::ustring(buffer_str);
buffer_str="";
if(ustr.validate())
text_buffer->insert_at_cursor(ustr);
else {
@ -29,6 +33,7 @@ bool juci::filesystem::read(const std::string &path, Glib::RefPtr<Gtk::TextBuffe
return false;
}
}
}
input.close();
return true;
}
@ -38,7 +43,7 @@ bool juci::filesystem::read(const std::string &path, Glib::RefPtr<Gtk::TextBuffe
//Only use on small files
std::vector<std::string> juci::filesystem::read_lines(const std::string &path) {
std::vector<std::string> res;
std::ifstream input(path);
std::ifstream input(path, std::ofstream::binary);
if (input) {
do { res.emplace_back(); } while(getline(input, res.back()));
}
@ -48,7 +53,7 @@ std::vector<std::string> juci::filesystem::read_lines(const std::string &path) {
//Only use on small files
bool juci::filesystem::write(const std::string &path, const std::string &new_content) {
std::ofstream output(path);
std::ofstream output(path, std::ofstream::binary);
if(output)
output << new_content;
else
@ -58,7 +63,7 @@ bool juci::filesystem::write(const std::string &path, const std::string &new_con
}
bool juci::filesystem::write(const std::string &path, Glib::RefPtr<Gtk::TextBuffer> buffer) {
std::ofstream output(path);
std::ofstream output(path, std::ofstream::binary);
if(output) {
auto start_iter=buffer->begin();
auto end_iter=start_iter;

132
src/terminal.cc

@ -8,57 +8,74 @@
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
//TODO: Windows...
//A working implementation of popen3, with all pipes getting closed properly.
//TODO: Eidheim is going to publish this one on his github, along with example uses
pid_t popen3(const char *command, int &stdin, int &stdout, int &stderr) {
pid_t popen3(const std::string &command, const std::string &path, int *stdin_fd, int *stdout_fd, int *stderr_fd) {
pid_t pid;
int p_stdin[2], p_stdout[2], p_stderr[2];
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0) {
close(p_stdin[0]);
close(p_stdout[0]);
close(p_stderr[0]);
close(p_stdin[1]);
close(p_stdout[1]);
close(p_stderr[1]);
int stdin_p[2], stdout_p[2], stderr_p[2];
if(stdin_fd!=nullptr && pipe(stdin_p)!=0) {
close(stdin_p[0]);
close(stdin_p[1]);
return -1;
}
if(stdout_fd!=nullptr && pipe(stdout_p)!=0) {
if(stdin_fd!=nullptr) close(stdin_p[0]);
if(stdin_fd!=nullptr) close(stdin_p[1]);
close(stdout_p[0]);
close(stdout_p[1]);
return -1;
}
if(stderr_fd!=nullptr && pipe(stderr_p)!=0) {
if(stdin_fd!=nullptr) close(stdin_p[0]);
if(stdin_fd!=nullptr) close(stdin_p[1]);
if(stdout_fd!=nullptr) close(stdout_p[0]);
if(stdout_fd!=nullptr) close(stdout_p[1]);
close(stderr_p[0]);
close(stderr_p[1]);
return -1;
}
pid = fork();
if (pid < 0) {
close(p_stdin[0]);
close(p_stdout[0]);
close(p_stderr[0]);
close(p_stdin[1]);
close(p_stdout[1]);
close(p_stderr[1]);
if(stdin_fd!=nullptr) close(stdin_p[0]);
if(stdin_fd!=nullptr) close(stdin_p[1]);
if(stdout_fd!=nullptr) close(stdout_p[0]);
if(stdout_fd!=nullptr) close(stdout_p[1]);
if(stderr_fd!=nullptr) close(stderr_p[0]);
if(stderr_fd!=nullptr) close(stderr_p[1]);
return pid;
}
else if (pid == 0) {
close(p_stdin[1]);
close(p_stdout[0]);
close(p_stderr[0]);
dup2(p_stdin[0], 0);
dup2(p_stdout[1], 1);
dup2(p_stderr[1], 2);
if(stdin_fd!=nullptr) close(stdin_p[1]);
if(stdout_fd!=nullptr) close(stdout_p[0]);
if(stderr_fd!=nullptr) close(stderr_p[0]);
if(stdin_fd!=nullptr) dup2(stdin_p[0], 0);
if(stdout_fd!=nullptr) dup2(stdout_p[1], 1);
if(stderr_fd!=nullptr) dup2(stderr_p[1], 2);
setpgid(0, 0);
//TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe
//TODO: One solution is: echo "command;exit"|script -q /dev/null
execl("/bin/sh", "sh", "-c", command, NULL);
std::string cd_path_and_command;
if(path!="") {
cd_path_and_command="cd \""+path+"\" && "+command;
}
else
cd_path_and_command=command;
execl("/bin/sh", "sh", "-c", cd_path_and_command.c_str(), NULL);
perror("execl");
exit(EXIT_FAILURE);
}
close(p_stdin[0]);
close(p_stdout[1]);
close(p_stderr[1]);
if(stdin_fd!=nullptr) close(stdin_p[0]);
if(stdout_fd!=nullptr) close(stdout_p[1]);
if(stderr_fd!=nullptr) close(stderr_p[1]);
stdin = p_stdin[1];
stdout = p_stdout[0];
stderr = p_stderr[0];
if(stdin_fd!=nullptr) *stdin_fd = stdin_p[1];
if(stdout_fd!=nullptr) *stdout_fd = stdout_p[0];
if(stderr_fd!=nullptr) *stderr_fd = stderr_p[0];
return pid;
}
@ -137,26 +154,18 @@ Terminal::Terminal() {
}
int Terminal::execute(const std::string &command, const boost::filesystem::path &path) {
std::string cd_path_and_command;
if(path!="") {
//TODO: Windows...
cd_path_and_command="cd \""+path.string()+"\" && "+command;
}
else
cd_path_and_command=command;
int stdin, stdout, stderr;
auto pid=popen3(cd_path_and_command.c_str(), stdin, stdout, stderr);
int stdin_fd, stdout_fd, stderr_fd;
auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd);
if (pid<=0) {
async_print("Error: Failed to run command: " + command + "\n");
return -1;
}
else {
std::thread stderr_thread([this, stderr](){
std::thread stderr_thread([this, stderr_fd](){
char buffer[1024];
ssize_t n;
while ((n=read(stderr, buffer, 1024)) > 0) {
while ((n=read(stderr_fd, buffer, 1024)) > 0) {
std::string message;
for(ssize_t c=0;c<n;c++)
message+=buffer[c];
@ -164,11 +173,11 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
}
});
stderr_thread.detach();
std::thread stdout_thread([this, stdout](){
std::thread stdout_thread([this, stdout_fd](){
char buffer[1024];
ssize_t n;
INFO("read");
while ((n=read(stdout, buffer, 1024)) > 0) {
while ((n=read(stdout_fd, buffer, 1024)) > 0) {
std::string message;
for(ssize_t c=0;c<n;c++)
message+=buffer[c];
@ -179,9 +188,9 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
int exit_code;
waitpid(pid, &exit_code, 0);
close(stdin);
close(stdout);
close(stderr);
close(stdin_fd);
close(stdout_fd);
close(stderr_fd);
return exit_code;
}
@ -189,20 +198,11 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_code)> callback) {
std::thread async_execute_thread([this, command, path, callback](){
std::string cd_path_and_command;
if(path!="") {
//TODO: Windows...
cd_path_and_command="cd \""+path.string()+"\" && "+command;
}
else
cd_path_and_command=command;
int stdin, stdout, stderr;
int stdin_fd, stdout_fd, stderr_fd;
async_executes_mutex.lock();
stdin_buffer.clear();
auto pid=popen3(cd_path_and_command.c_str(), stdin, stdout, stderr);
async_executes.emplace_back(pid, stdin);
auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd);
async_executes.emplace_back(pid, stdin_fd);
async_executes_mutex.unlock();
if (pid<=0) {
@ -211,10 +211,10 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
callback(-1);
}
else {
std::thread stderr_thread([this, stderr](){
std::thread stderr_thread([this, stderr_fd](){
char buffer[1024];
ssize_t n;
while ((n=read(stderr, buffer, 1024)) > 0) {
while ((n=read(stderr_fd, buffer, 1024)) > 0) {
std::string message;
for(ssize_t c=0;c<n;c++)
message+=buffer[c];
@ -222,11 +222,11 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
}
});
stderr_thread.detach();
std::thread stdout_thread([this, stdout](){
std::thread stdout_thread([this, stdout_fd](){
char buffer[1024];
ssize_t n;
INFO("read");
while ((n=read(stdout, buffer, 1024)) > 0) {
while ((n=read(stdout_fd, buffer, 1024)) > 0) {
std::string message;
for(ssize_t c=0;c<n;c++)
message+=buffer[c];
@ -245,9 +245,9 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
}
}
stdin_buffer.clear();
close(stdin);
close(stdout);
close(stderr);
close(stdin_fd);
close(stdout_fd);
close(stderr_fd);
async_executes_mutex.unlock();
if(callback)

6
src/terminal.h

@ -13,6 +13,7 @@ class Terminal : public Gtk::TextView {
public:
class Config {
public:
std::string cmake_command;
std::string make_command;
};
@ -53,8 +54,11 @@ private:
Glib::RefPtr<Gtk::TextTag> bold_tag;
std::mutex async_executes_mutex;
#ifdef _WIN32
std::list<std::pair<void*, void*> > async_executes;
#else
std::list<std::pair<pid_t, int> > async_executes;
#endif
std::string stdin_buffer;
};

418
src/terminal_win.cc

@ -0,0 +1,418 @@
#include "terminal.h"
#include <iostream>
#include "logging.h"
#include "singletons.h"
#include <unistd.h>
#include <windows.h>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
#define BUFSIZE 1024
HANDLE popen3(const std::string &command, const std::string &path, HANDLE *stdin_h, HANDLE *stdout_h, HANDLE *stderr_h) {
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hChildStd_ERR_Rd = NULL;
HANDLE g_hChildStd_ERR_Wr = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
return NULL;
if(!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
return NULL;
}
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
return NULL;
}
if(!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
return NULL;
}
if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
return NULL;
}
if(!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_ERR_Rd);
CloseHandle(g_hChildStd_ERR_Wr);
return NULL;
}
PROCESS_INFORMATION process_info;
STARTUPINFO siStartInfo;
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_ERR_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
char* path_ptr;
if(path=="")
path_ptr=NULL;
else {
path_ptr=new char[path.size()+1];
std::strcpy(path_ptr, path.c_str());
}
char* command_cstr=new char[command.size()+1];
std::strcpy(command_cstr, command.c_str());
BOOL bSuccess = CreateProcess(NULL,
command_cstr, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
path_ptr, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&process_info); // receives PROCESS_INFORMATION
if(!bSuccess) {
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_ERR_Wr);
return NULL;
}
else {
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(process_info.hThread);
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_ERR_Wr);
}
*stdin_h=g_hChildStd_IN_Wr;
*stdout_h=g_hChildStd_OUT_Rd;
*stderr_h=g_hChildStd_ERR_Rd;
return process_info.hProcess;
}
Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) {
waiting_print.connect([this](){
Singleton::terminal()->async_print(line_nr-1, ".");
});
start(start_msg);
}
Terminal::InProgress::~InProgress() {
stop=true;
if(wait_thread.joinable())
wait_thread.join();
}
void Terminal::InProgress::start(const std::string& msg) {
line_nr=Singleton::terminal()->print(msg+"...\n");
wait_thread=std::thread([this](){
size_t c=0;
while(!stop) {
if(c%100==0)
waiting_print();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
c++;
}
});
}
void Terminal::InProgress::done(const std::string& msg) {
if(!stop) {
stop=true;
Singleton::terminal()->async_print(line_nr-1, msg);
}
}
void Terminal::InProgress::cancel(const std::string& msg) {
if(!stop) {
stop=true;
Singleton::terminal()->async_print(line_nr-1, msg);
}
}
Terminal::Terminal() {
bold_tag=get_buffer()->create_tag();
bold_tag->property_weight()=PANGO_WEIGHT_BOLD;
signal_size_allocate().connect([this](Gtk::Allocation& allocation){
auto iter=get_buffer()->end();
if(iter.backward_char()) {
auto mark=get_buffer()->create_mark(iter);
scroll_to(mark, 0.0, 1.0, 1.0);
get_buffer()->delete_mark(mark);
}
});
async_print_dispatcher.connect([this](){
async_print_strings_mutex.lock();
if(async_print_strings.size()>0) {
for(auto &string_bold: async_print_strings)
print(string_bold.first, string_bold.second);
async_print_strings.clear();
}
async_print_strings_mutex.unlock();
});
async_print_on_line_dispatcher.connect([this](){
async_print_on_line_strings_mutex.lock();
if(async_print_on_line_strings.size()>0) {
for(auto &line_string: async_print_on_line_strings)
print(line_string.first, line_string.second);
async_print_on_line_strings.clear();
}
async_print_on_line_strings_mutex.unlock();
});
}
//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
int Terminal::execute(const std::string &command, const boost::filesystem::path &path) {
HANDLE stdin_h, stdout_h, stderr_h;
auto process=popen3(command, path.string(), &stdin_h, &stdout_h, &stderr_h);
if(process==NULL) {
async_print("Error: Failed to run command: " + command + "\n");
return -1;
}
std::thread stderr_thread([this, stderr_h](){
DWORD n;
CHAR buffer[BUFSIZE];
for (;;) {
BOOL bSuccess = ReadFile(stderr_h, buffer, BUFSIZE, &n, NULL);
if(!bSuccess || n == 0)
break;
std::string message;
for(DWORD c=0;c<n;c++)
message+=buffer[c];
async_print(message, true);
}
});
stderr_thread.detach();
std::thread stdout_thread([this, stdout_h](){
DWORD n;
CHAR buffer[BUFSIZE];
for (;;) {
BOOL bSuccess = ReadFile(stdout_h, buffer, BUFSIZE, &n, NULL);
if(!bSuccess || n == 0)
break;
std::string message;
for(DWORD c=0;c<n;c++)
message+=buffer[c];
async_print(message);
}
});
stdout_thread.detach();
unsigned long exit_code;
WaitForSingleObject(process, INFINITE);
GetExitCodeProcess(process, &exit_code);
CloseHandle(process);
CloseHandle(stdin_h);
CloseHandle(stdout_h);
CloseHandle(stderr_h);
return exit_code;
}
void Terminal::async_execute(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_code)> callback) {
std::thread async_execute_thread([this, command, path, callback](){
HANDLE stdin_h, stdout_h, stderr_h;
async_executes_mutex.lock();
stdin_buffer.clear();
auto process=popen3(command, path.string(), &stdin_h, &stdout_h, &stderr_h);
if(process==NULL) {
async_executes_mutex.unlock();
async_print("Error: Failed to run command: " + command + "\n");
if(callback)
callback(-1);
return;
}
async_executes.emplace_back(process, stdin_h);
async_executes_mutex.unlock();
std::thread stderr_thread([this, stderr_h](){
DWORD n;
CHAR buffer[BUFSIZE];
for (;;) {
BOOL bSuccess = ReadFile(stderr_h, buffer, BUFSIZE, &n, NULL);
if(!bSuccess || n == 0)
break;
std::string message;
for(DWORD c=0;c<n;c++)
message+=buffer[c];
async_print(message, true);
}
});
stderr_thread.detach();
std::thread stdout_thread([this, stdout_h](){
DWORD n;
CHAR buffer[BUFSIZE];
for (;;) {
BOOL bSuccess = ReadFile(stdout_h, buffer, BUFSIZE, &n, NULL);
if(!bSuccess || n == 0)
break;
std::string message;
for(DWORD c=0;c<n;c++)
message+=buffer[c];
async_print(message);
}
});
stdout_thread.detach();
unsigned long exit_code;
WaitForSingleObject(process, INFINITE);
GetExitCodeProcess(process, &exit_code);
async_executes_mutex.lock();
for(auto it=async_executes.begin();it!=async_executes.end();it++) {
if(it->first==process) {
async_executes.erase(it);
break;
}
}
stdin_buffer.clear();
CloseHandle(process);
CloseHandle(stdin_h);
CloseHandle(stdout_h);
CloseHandle(stderr_h);
async_executes_mutex.unlock();
if(callback)
callback(exit_code);
});
async_execute_thread.detach();
}
void Terminal::kill_last_async_execute(bool force) {
async_executes_mutex.lock();
if(async_executes.size()>0) {
TerminateProcess(async_executes.back().first, 2);
}
async_executes_mutex.unlock();
}
void Terminal::kill_async_executes(bool force) {
async_executes_mutex.lock();
for(auto &async_execute: async_executes) {
TerminateProcess(async_execute.first, 2);
}
async_executes_mutex.unlock();
}
int Terminal::print(const std::string &message, bool bold){
INFO("Terminal: PrintMessage");
if(bold)
get_buffer()->insert_with_tag(get_buffer()->end(), message, bold_tag);
else
get_buffer()->insert(get_buffer()->end(), message);
auto iter=get_buffer()->end();
if(iter.backward_char()) {
auto mark=get_buffer()->create_mark(iter);
scroll_to(mark, 0.0, 1.0, 1.0);
get_buffer()->delete_mark(mark);
}
return get_buffer()->end().get_line();
}
void Terminal::print(int line_nr, const std::string &message){
INFO("Terminal: PrintMessage at line " << line_nr);
auto iter=get_buffer()->get_iter_at_line(line_nr);
while(!iter.ends_line())
iter++;
get_buffer()->insert(iter, message);
}
std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) {
std::shared_ptr<Terminal::InProgress> in_progress=std::shared_ptr<Terminal::InProgress>(new Terminal::InProgress(start_msg));
return in_progress;
}
void Terminal::async_print(const std::string &message, bool bold) {
async_print_strings_mutex.lock();
bool dispatch=true;
if(async_print_strings.size()>0)
dispatch=false;
async_print_strings.emplace_back(message, bold);
async_print_strings_mutex.unlock();
if(dispatch)
async_print_dispatcher();
}
void Terminal::async_print(int line_nr, const std::string &message) {
async_print_on_line_strings_mutex.lock();
bool dispatch=true;
if(async_print_on_line_strings.size()>0)
dispatch=false;
async_print_on_line_strings.emplace_back(line_nr, message);
async_print_on_line_strings_mutex.unlock();
if(dispatch)
async_print_on_line_dispatcher();
}
bool Terminal::on_key_press_event(GdkEventKey *event) {
async_executes_mutex.lock();
if(async_executes.size()>0) {
get_buffer()->place_cursor(get_buffer()->end());
auto unicode=gdk_keyval_to_unicode(event->keyval);
char chr=(char)unicode;
if(unicode>=32 && unicode<=126) {
stdin_buffer+=chr;
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
scroll_to(get_buffer()->get_insert());
}
else if(event->keyval==GDK_KEY_BackSpace) {
if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) {
auto iter=get_buffer()->end();
iter--;
stdin_buffer.pop_back();
get_buffer()->erase(iter, get_buffer()->end());
scroll_to(get_buffer()->get_insert());
}
}
else if(event->keyval==GDK_KEY_Return) {
stdin_buffer+='\n';
DWORD written;
WriteFile(async_executes.back().second, stdin_buffer.c_str(), stdin_buffer.size(), &written, NULL);
//TODO: is this line needed?
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
scroll_to(get_buffer()->get_insert());
stdin_buffer.clear();
}
}
async_executes_mutex.unlock();
return true;
}

4
src/tooltips.h

@ -14,13 +14,13 @@ public:
Gdk::Rectangle activation_rectangle;
std::unique_ptr<Gtk::Window> window;
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark;
Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark;
private:
void wrap_lines(Glib::RefPtr<Gtk::TextBuffer> text_buffer);
std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer;
std::unique_ptr<Gtk::TextView> tooltip_widget;
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark;
Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark;
Gtk::TextView& text_view;
int tooltip_width, tooltip_height;
};

15
src/window.cc

@ -3,7 +3,7 @@
#include "singletons.h"
#include "sourcefile.h"
#include "config.h"
#include "api.h"
//#include "api.h"
#include <boost/lexical_cast.hpp>
#include <iostream> //TODO: remove
@ -30,7 +30,7 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil
add(box);
generate_keybindings();
PluginApi(&this->notebook, &this->menu);
//PluginApi(&this->notebook, &this->menu);
create_menu();
box.pack_start(menu.get_widget(), Gtk::PACK_SHRINK);
@ -93,6 +93,9 @@ Window::Window() : box(Gtk::ORIENTATION_VERTICAL), notebook(directories), compil
directories.select(notebook.get_current_view()->file_path);
if(auto source_view=dynamic_cast<Source::ClangView*>(notebook.get_current_view()))
source_view->start_reparse();
Singleton::status()->set_text(notebook.get_current_view()->status);
}
});
@ -162,6 +165,7 @@ void Window::create_menu() {
auto undo_manager = notebook.get_current_view()->get_source_buffer()->get_undo_manager();
if (undo_manager->can_undo()) {
undo_manager->undo();
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert());
}
}
INFO("Done undo");
@ -172,6 +176,7 @@ void Window::create_menu() {
auto undo_manager = notebook.get_current_view()->get_source_buffer()->get_undo_manager();
if(undo_manager->can_redo()) {
undo_manager->redo();
notebook.get_current_view()->scroll_to(notebook.get_current_view()->get_buffer()->get_insert());
}
}
INFO("Done Redo");
@ -226,12 +231,10 @@ void Window::create_menu() {
if(executable_path!="") {
compiling=true;
Singleton::terminal()->print("Compiling and running "+executable_path.string()+"\n");
//TODO: Windows...
auto project_path=cmake.project_path;
Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this, executable_path, project_path](int exit_code){
compiling=false;
if(exit_code==EXIT_SUCCESS) {
//TODO: Windows...
auto executable_path_spaces_fixed=executable_path.string();
char last_char=0;
for(size_t c=0;c<executable_path_spaces_fixed.size();c++) {
@ -261,7 +264,6 @@ void Window::create_menu() {
if(cmake.project_path!="") {
compiling=true;
Singleton::terminal()->print("Compiling project "+cmake.project_path.string()+"\n");
//TODO: Windows...
Singleton::terminal()->async_execute(Singleton::Config::terminal()->make_command, cmake.project_path, [this](int exit_code){
compiling=false;
});
@ -280,6 +282,7 @@ void Window::create_menu() {
entry_box.hide();
});
auto entry_it=entry_box.entries.begin();
entry_it->set_placeholder_text("Command");
entry_box.buttons.emplace_back("Run command", [this, entry_it](){
entry_it->activate();
});
@ -493,7 +496,7 @@ void Window::save_file_dialog() {
return;
INFO("Save file dialog");
Gtk::FileChooserDialog dialog(*this, "Please choose a file", Gtk::FILE_CHOOSER_ACTION_SAVE);
gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), notebook.get_current_view()->file_path.c_str());
gtk_file_chooser_set_filename((GtkFileChooser*)dialog.gobj(), notebook.get_current_view()->file_path.string().c_str());
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ALWAYS);
dialog.add_button("Cancel", Gtk::RESPONSE_CANCEL);
dialog.add_button("Save", Gtk::RESPONSE_OK);

Loading…
Cancel
Save