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) cmake_minimum_required (VERSION 2.8.4)
set(project_name juci) set(project_name juci)
set(module juci_to_python_api) #set(module juci_to_python_api)
#### TODO WINDOWS SUPPORT #### #set(lib_install_path "/usr/local/lib/python2.7/dist-packages/")
set(bin_install_path "/usr/local/bin")
set(lib_install_path "/usr/local/lib/python2.7/dist-packages/")
#####
project (${project_name}) 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++ # juCi++
###### a lightweight C++-IDE with support for C++11 and C++14. ###### a lightweight C++-IDE with support for C++11 and C++14.
## About ## About
A lot of IDEs struggle with good C++11/14 support. Therefore we Current IDEs struggle with C++ support due to the complexity of
created juCi++. juCi++ is based of the compiler and will *always* the programming language. juCI++, however, is designed especially
support new versions of C++. towards libclang with speed in mind.
## Features ## Features
* Syntax highlighing (even C++11/14) * Fast and responsive
* Superfast autocomletion (even external libraries) * Syntax highlighing (even C++11/14, and more than 100 other file types)
* Accurate refactoring across files * C++ warnings and errors on the fly
* Basic editor functionallity * Fast C++ autocomletion (even external libraries)
* Tooltips showing type information and doxygen documentation
* Refactoring across files
* Highlighting of similar types * 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 ## ## Dependencies ##
Please install these dependencies on your system.
* libboost-python-dev
* libboost-filesystem-dev * libboost-filesystem-dev
* libboost-log-dev * libboost-log-dev
* libboost-test-dev * libboost-test-dev
@ -24,19 +26,9 @@ Please install these dependencies on your system.
* libgtkmm-3.0-dev * libgtkmm-3.0-dev
* libgtksourceview2.0-dev * libgtksourceview2.0-dev
* libgtksourceviewmm-3.0-dev * libgtksourceviewmm-3.0-dev
* libpython-dev * libaspell-dev
* libclang-dev * libclang-dev
* [libclangmm](http://github.com/cppit/libclangmm/) * [libclangmm](http://github.com/cppit/libclangmm/)
* cmake
* make
* clang or gcc (compiler)
## Installation ## ## Installation ##
Quickstart: See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md).
```sh
$ cmake .
$ make
$ sudo make install
```
See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md) for more details.

94
docs/install.md

@ -1,22 +1,90 @@
# juCi++ # juCi++
## Installation guide ## ## Installation guide ##
Before installation, please install libclangmm see [installation guide](http://github.com/cppit/libclangmm/blob/master/docs/install.md) for installation. Before installation, please install libclangmm, see [installation guide](http://github.com/cppit/libclangmm/blob/master/docs/install.md).
## Debian
First dependencies: ## Debian/Ubuntu
```sh ```sh
$ sudo apt-get install libboost-python-dev libboost-filesystem-dev libboost-log-dev libboost-test-dev 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
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++
``` ```
Install the project:
```sh ```sh
$ git clone http://github.com/cppit/jucipp.git juci git clone http://github.com/cppit/jucipp.git
$ cd juci cd jucipp
$ cmake . cmake .
$ make make
$ sudo make install 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 ## Run
```sh ```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(CMAKE_MACOSX_RPATH 1)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig") set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig")
endif() endif()
INCLUDE(FindPkgConfig) INCLUDE(FindPkgConfig)
set(validation true) set(validation true)
@ -21,7 +22,7 @@ function(install_help APPLE UNIX WINDOWS)
endif(APPLE) endif(APPLE)
endif(UNIX) endif(UNIX)
if(WINDOWS) if(WINDOWS)
message("choco install ${WINDOWS}") #message("choco install ${WINDOWS}") #Removed this for the time being
endif(WINDOWS) endif(WINDOWS)
endfunction(install_help) endfunction(install_help)
@ -38,10 +39,16 @@ validate(${LCL_FOUND} "clangmm" "clangmm" "clangmm")
find_package(LibClang) find_package(LibClang)
validate(${LIBCLANG_FOUND} "clang" "libclang-dev" "llvm") validate(${LIBCLANG_FOUND} "clang" "libclang-dev" "llvm")
find_package(PythonLibs 2.7) #TODO: till clang is fixed on MSYS2 ((lib)clang.dll.a is missing):
validate(${PYTHONLIBS_FOUND} "python" "libpython-dev" "python") 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") 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 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) pkg_check_modules(GTKSVMM gtksourceviewmm-3.0)
validate(${GTKSVMM_FOUND} "gtksvmm" "libgtksvmm-dev" "gtkmmsv") validate(${GTKSVMM_FOUND} "gtksvmm" "libgtksvmm-dev" "gtkmmsv")
if(${validation}) find_package(ASPELL REQUIRED)
add_executable(${project_name}
juci.h set(source_files juci.h
juci.cc juci.cc
menu.h menu.h
menu.cc menu.cc
@ -66,8 +73,8 @@ if(${validation})
sourcefile.cc sourcefile.cc
window.cc window.cc
window.h window.h
api.h # api.h
api.cc # api.cc
notebook.cc notebook.cc
notebook.h notebook.h
entrybox.h entrybox.h
@ -75,7 +82,6 @@ if(${validation})
directories.h directories.h
directories.cc directories.cc
terminal.h terminal.h
terminal.cc
tooltips.h tooltips.h
tooltips.cc tooltips.cc
singletons.h singletons.h
@ -83,31 +89,42 @@ if(${validation})
cmake.h cmake.h
cmake.cc) cmake.cc)
add_library(${module} SHARED if(MSYS)
api list(APPEND source_files terminal_win.cc)
api_ext) 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( include_directories(
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS} # ${PYTHON_INCLUDE_DIRS}
${GTKMM_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS}
${GTKSVMM_INCLUDE_DIRS} ${GTKSVMM_INCLUDE_DIRS}
${LCL_INCLUDE_DIRS} ${LCL_INCLUDE_DIRS}
${LIBCLANG_INCLUDE_DIRS}) ${LIBCLANG_INCLUDE_DIRS}
${ASPELL_INCLUDE_DIR})
link_directories( link_directories(
${GTKMM_LIBRARY_DIRS} ${GTKMM_LIBRARY_DIRS}
${GTKSVMM_LIBRARY_DIRS} ${GTKSVMM_LIBRARY_DIRS}
${Boost_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS}
${PYTHON_INCLUDE_DIRS} # ${PYTHON_INCLUDE_DIRS}
${LCL_LIBRARY_DIRS} ${LCL_LIBRARY_DIRS}
${LIBCLANG_LIBRARY_DIRS}) ${LIBCLANG_LIBRARY_DIRS})
set_target_properties(${module} # set_target_properties(${module}
PROPERTIES PREFIX "" # PROPERTIES PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib/") # 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} target_link_libraries(${project_name}
${LIBCLANG_LIBRARIES} ${LIBCLANG_LIBRARIES}
@ -115,11 +132,14 @@ if(${validation})
${GTKMM_LIBRARIES} ${GTKMM_LIBRARIES}
${GTKSVMM_LIBRARIES} ${GTKSVMM_LIBRARIES}
${Boost_LIBRARIES} ${Boost_LIBRARIES}
${PYTHON_LIBRARIES}) ${ASPELL_LIBRARIES}
# ${PYTHON_LIBRARIES}
)
install(TARGETS ${project_name} ${module} # install(TARGETS ${project_name} ${module}
RUNTIME DESTINATION ${bin_install_path} install(TARGETS ${project_name}
LIBRARY DESTINATION ${lib_install_path} RUNTIME DESTINATION bin
# LIBRARY DESTINATION ${lib_install_path}
) )
endif(${validation}) 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) { bool CMake::create_compile_commands(const boost::filesystem::path &path) {
Singleton::terminal()->print("Creating "+path.string()+"/compile_commands.json\n"); Singleton::terminal()->print("Creating "+path.string()+"/compile_commands.json\n");
//TODO: Windows... if(Singleton::terminal()->execute(Singleton::Config::terminal()->cmake_command+" . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path)==EXIT_SUCCESS) {
if(Singleton::terminal()->execute("cmake . -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 true;
}
return false; return false;
} }

3
src/cmake/Modules/FindLibClang.cmake

@ -14,7 +14,8 @@
# Known LLVM release numbers. # Known LLVM release numbers.
# most recent versions come first # 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.6
3.5.1 3.5.1
3.5.0 #Arch Linux 3.5.0 #Arch Linux

6
src/cmake/Modules/FindLibClangmm.cmake

@ -5,12 +5,10 @@
find_package(PkgConfig) find_package(PkgConfig)
find_path(LCL_INCLUDE_DIR clangmm.h find_path(LCL_INCLUDE_DIR clangmm.h
HINTS "/usr/local/include/libclangmm/" PATH_SUFFIXES libclangmm
) )
find_library(LCL_LIBRARY NAMES clangmm find_library(LCL_LIBRARY NAMES clangmm)
HINTS "/usr/local/lib/"
)
set(LCL_LIBRARIES ${LCL_LIBRARY} ) set(LCL_LIBRARIES ${LCL_LIBRARY} )
set(LCL_INCLUDE_DIRS ${LCL_INCLUDE_DIR} ) 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::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()->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() { void MainConfig::find_or_create_config_files() {
@ -45,12 +46,16 @@ void MainConfig::GenerateSource() {
auto source_cfg = Singleton::Config::source(); auto source_cfg = Singleton::Config::source();
auto source_json = cfg.get_child("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_char = source_json.get<char>("default_tab_char");
source_cfg->default_tab_size = source_json.get<unsigned>("default_tab_size"); 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->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->wrap_lines = source_json.get<bool>("wrap_lines");
source_cfg->show_line_numbers = source_json.get_value<bool>("show_line_numbers");
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")) for (auto &i : source_json.get_child("clang_types"))
source_cfg->clang_types[i.first] = i.second.get_value<std::string>(); 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 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) { 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_max_length(length);
set_text(content); set_text(content);
selected_history=0;
signal_activate().connect([this](){ signal_activate().connect([this](){
if(this->on_activate) if(this->on_activate) {
this->on_activate(get_text()); 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 <list>
#include <functional> #include <functional>
#include "gtkmm.h" #include "gtkmm.h"
#include <unordered_map>
#include <string>
#include <vector>
class EntryBox : public Gtk::Box { class EntryBox : public Gtk::Box {
public: public:
@ -11,6 +14,8 @@ public:
public: public:
Entry(const std::string& content="", std::function<void(const std::string& content)> on_activate=nullptr, unsigned length=50); 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; std::function<void(const std::string& content)> on_activate;
private:
size_t selected_history;
}; };
class Button : public Gtk::Button { class Button : public Gtk::Button {
public: public:
@ -39,6 +44,9 @@ public:
std::list<Button> buttons; std::list<Button> buttons;
std::list<ToggleButton> toggle_buttons; std::list<ToggleButton> toggle_buttons;
std::list<Label> labels; std::list<Label> labels;
private:
static std::unordered_map<std::string, std::vector<std::string> > entry_histories;
}; };
#endif // JUCI_ENTRYBOX_H_ #endif // JUCI_ENTRYBOX_H_

17
src/files.h

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

2
src/notebook.cc

@ -67,7 +67,7 @@ void Notebook::open(const boost::filesystem::path &file_path) {
else else
Singleton::terminal()->print("Error: could not find project path for "+file_path.string()+"\n"); 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 else
source_views.emplace_back(new Source::GenericView(file_path, language)); 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() { void SelectionDialogBase::show() {
shown=true;
move(); move();
window->show_all(); window->show_all();
} }
@ -72,8 +73,9 @@ void SelectionDialogBase::hide() {
window->hide(); window->hide();
if(tooltips) if(tooltips)
tooltips->hide(); tooltips->hide();
if(on_hide) if(on_hide && shown)
on_hide(); on_hide();
shown=false;
} }
void SelectionDialogBase::update_tooltips() { 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() { void SelectionDialog::show() {
SelectionDialogBase::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) {} CompletionDialog::CompletionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark) : SelectionDialogBase(text_view, start_mark, false) {}
void CompletionDialog::show() { void CompletionDialog::show() {

5
src/selectiondialog.h

@ -31,11 +31,14 @@ protected:
std::unique_ptr<Tooltips> tooltips; std::unique_ptr<Tooltips> tooltips;
std::unordered_map<std::string, std::string> tooltip_texts; std::unordered_map<std::string, std::string> tooltip_texts;
std::string last_row; std::string last_row;
private:
bool shown=false;
}; };
class SelectionDialog : public SelectionDialogBase { class SelectionDialog : public SelectionDialogBase {
public: 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(); void show();
}; };

506
src/source.cc

@ -7,8 +7,8 @@
#include "singletons.h" #include "singletons.h"
#include <gtksourceview/gtksource.h> #include <gtksourceview/gtksource.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <iostream>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove using namespace std; //TODO: remove
namespace sigc { namespace sigc {
@ -36,6 +36,8 @@ Glib::RefPtr<Gsv::Language> Source::guess_language(const boost::filesystem::path
////////////// //////////////
//// View //// //// View ////
////////////// //////////////
AspellConfig* Source::View::spellcheck_config=NULL;
Source::View::View(const boost::filesystem::path &file_path): file_path(file_path) { Source::View::View(const boost::filesystem::path &file_path): file_path(file_path) {
set_smart_home_end(Gsv::SMART_HOME_END_BEFORE); set_smart_home_end(Gsv::SMART_HOME_END_BEFORE);
get_source_buffer()->begin_not_undoable_action(); 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'); 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_highlight_current_line() = Singleton::Config::source()->highlight_current_line;
property_show_line_numbers() = Singleton::Config::source()->show_line_numbers; property_show_line_numbers() = Singleton::Config::source()->show_line_numbers;
if(Singleton::Config::source()->font.size()>0) if(Singleton::Config::source()->font.size()>0)
override_font(Pango::FontDescription(Singleton::Config::source()->font)); 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_char=Singleton::Config::source()->default_tab_char;
tab_size=Singleton::Config::source()->default_tab_size; tab_size=Singleton::Config::source()->default_tab_size;
if(Singleton::Config::source()->auto_tab_char_and_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; tab+=tab_char;
tabs_regex=std::regex(std::string("^(")+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) { 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() { Source::View::~View() {
g_clear_object(&search_context); g_clear_object(&search_context);
g_clear_object(&search_settings); g_clear_object(&search_settings);
delete_aspell_speller(spellcheck_checker);
} }
void Source::View::search_highlight(const std::string &text, bool case_sensitive, bool regex) { 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 //Basic indentation
bool Source::View::on_key_press_event(GdkEventKey* key) { 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(); get_source_buffer()->begin_user_action();
//Indent as in next or previous line //Indent as in next or previous line
if(key->keyval==GDK_KEY_Return && key->state==0 && !get_buffer()->get_has_selection()) { 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; 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() { std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
const std::regex indent_regex("^([ \t]+).*$"); const std::regex indent_regex("^([ \t]+).*$");
auto size=get_buffer()->get_line_count(); 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}; 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 //// //// GenericView ////
///////////////////// /////////////////////
@ -450,6 +735,7 @@ Source::GenericView::GenericView(const boost::filesystem::path &file_path, Glib:
get_source_buffer()->set_language(language); get_source_buffer()->set_language(language);
Singleton::terminal()->print("Language for file "+file_path.string()+" set to "+language->get_name()+".\n"); Singleton::terminal()->print("Language for file "+file_path.string()+" set to "+language->get_name()+".\n");
} }
spellcheck_all=true;
auto completion=get_completion(); auto completion=get_completion();
auto completion_words=Gsv::CompletionWords::create("", Glib::RefPtr<Gdk::Pixbuf>()); auto completion_words=Gsv::CompletionWords::create("", Glib::RefPtr<Gdk::Pixbuf>());
completion_words->register_provider(get_buffer()); completion_words->register_provider(get_buffer());
@ -490,58 +776,6 @@ Source::View(file_path), project_path(project_path) {
} }
INFO("Tagtable filled"); 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()); parsing_in_progress=Singleton::terminal()->print_in_progress("Parsing "+file_path.string());
//GTK-calls must happen in main thread, so the parse_thread //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: //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_syntax();
update_diagnostics(); update_diagnostics();
update_types(); update_types();
clang_readable=true; source_readable=true;
set_status(""); set_status("");
parsing_mutex.unlock(); parsing_mutex.unlock();
INFO("Syntax updated"); INFO("Syntax updated");
@ -579,8 +813,6 @@ Source::View(file_path), project_path(project_path) {
diagnostic_tooltips.hide(); 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+"*).*\\{ *$"); 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_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) *$"); 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(); type_tooltips.hide();
diagnostic_tooltips.hide(); diagnostic_tooltips.hide();
get_buffer()->remove_all_tags(get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_all_tags(get_buffer()->begin(), get_buffer()->end());
clang_readable=false; source_readable=false;
parse_thread_go=true; parse_thread_go=true;
parse_thread_mapped=false; parse_thread_mapped=false;
parse_thread_stop=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() { void Source::ClangViewParse::start_reparse() {
parse_thread_mapped=false; parse_thread_mapped=false;
clang_readable=false; source_readable=false;
delayed_reparse_connection.disconnect(); delayed_reparse_connection.disconnect();
delayed_reparse_connection=Glib::signal_timeout().connect([this]() { delayed_reparse_connection=Glib::signal_timeout().connect([this]() {
clang_readable=false; source_readable=false;
parse_thread_go=true; parse_thread_go=true;
set_status("parsing..."); set_status("parsing...");
return false; 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++) if(file_path.extension()==".h") //TODO: temporary fix for .h-files (parse as c++)
arguments.emplace_back("-xc++"); 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; 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 //Clang indentation
//TODO: replace indentation methods with a better implementation or maybe use libclang //TODO: replace indentation methods with a better implementation or maybe use libclang
bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
@ -874,7 +1083,9 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) {
return true; 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()+"}"); 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(); auto insert_it = get_source_buffer()->get_insert()->get_iter();
for(size_t c=0;c<sm[1].str().size()+2;c++) 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(); get_source_buffer()->end_user_action();
return true; 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 { else {
get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab); get_source_buffer()->insert_at_cursor("\n"+sm[1].str()+tab);
scroll_to(get_source_buffer()->get_insert()); scroll_to(get_source_buffer()->get_insert());
@ -986,6 +1207,14 @@ Source::ClangViewParse(file_path, project_path), autocomplete_cancel_starting(fa
return false; return false;
}, 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) { 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); 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() { void Source::ClangViewAutocomplete::start_autocomplete() {
if(!has_focus()) if(!has_focus())
return; 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> >(); 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()]; auto ustr=get_buffer()->get_text();
buffer=get_buffer()->get_text(get_buffer()->begin(), get_buffer()->get_insert()->get_iter()); auto iter=get_buffer()->get_insert()->get_iter();
auto iter = get_source_buffer()->get_insert()->get_iter();
auto line_nr=iter.get_line()+1; auto line_nr=iter.get_line()+1;
auto column_nr=iter.get_line_offset()+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()=='_') { auto pos=iter.get_offset()-1;
buffer.pop_back(); 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--; column_nr--;
pos--;
} }
buffer+="\n"; (*buffer_map)[this->file_path.string()]=std::move(ustr); //TODO: does this work?
set_status("autocomplete..."); set_status("autocomplete...");
if(autocomplete_thread.joinable()) if(autocomplete_thread.joinable())
autocomplete_thread.join(); autocomplete_thread.join();
@ -1190,7 +1411,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
}); });
get_token=[this]() -> std::string { get_token=[this]() -> std::string {
if(clang_readable) { if(source_readable) {
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line(); auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index(); auto index=(unsigned)iter.get_line_index();
@ -1208,7 +1429,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
}; };
get_token_name=[this]() -> std::string { get_token_name=[this]() -> std::string {
if(clang_readable) { if(source_readable) {
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line(); auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index(); 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){ tag_similar_tokens=[this](const std::string &usr){
if(clang_readable) { if(source_readable) {
if(usr.size()>0 && last_similar_tokens_tagged!=usr) { if(usr.size()>0 && last_similar_tokens_tagged!=usr) {
get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(similar_tokens_tag, get_buffer()->begin(), get_buffer()->end());
auto offsets=clang_tokens->get_similar_token_offsets(usr); 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) { rename_similar_tokens=[this](const std::string &usr, const std::string &text) {
size_t number=0; size_t number=0;
if(clang_readable) { if(source_readable) {
auto offsets=clang_tokens->get_similar_token_offsets(usr); auto offsets=clang_tokens->get_similar_token_offsets(usr);
std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > marks; std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > marks;
for(auto &offset: offsets) { 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){ get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){
if(mark->get_name()=="insert") { 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(); auto usr=get_token();
tag_similar_tokens(usr); tag_similar_tokens(usr);
return false;
}, 100);
} }
}); });
get_declaration_location=[this](){ get_declaration_location=[this](){
std::pair<std::string, clang::Offset> location; std::pair<std::string, clang::Offset> location;
if(clang_readable) { if(source_readable) {
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line(); auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index(); auto index=(unsigned)iter.get_line_index();
@ -1293,7 +1518,7 @@ Source::ClangViewAutocomplete(file_path, project_path) {
}; };
goto_method=[this](){ 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()))); 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 rows=std::make_shared<std::unordered_map<std::string, clang::Offset> >();
auto methods=clang_tokens->get_cxx_methods(); 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](){ do_delete_object.connect([this](){
if(delete_thread.joinable()) if(delete_thread.joinable())
delete_thread.join(); delete_thread.join();

40
src/source.h

@ -15,6 +15,7 @@
#include "selectiondialog.h" #include "selectiondialog.h"
#include <set> #include <set>
#include <regex> #include <regex>
#include <aspell.h>
namespace Source { namespace Source {
Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path); Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path);
@ -23,9 +24,11 @@ namespace Source {
public: public:
std::string style; std::string style;
std::string font; std::string font;
std::string spellcheck_language;
bool auto_tab_char_and_size; bool auto_tab_char_and_size;
char default_tab_char; char default_tab_char;
unsigned default_tab_size; unsigned default_tab_size;
bool wrap_lines;
bool highlight_current_line; bool highlight_current_line;
bool show_line_numbers; bool show_line_numbers;
std::unordered_map<std::string, std::string> clang_types; 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::function<void(View* view, const std::string &status)> on_update_status;
std::string status; std::string status;
protected: 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); void set_status(const std::string &status);
std::string get_line(size_t line_number); std::string get_line(size_t line_number);
std::string get_line_before_insert(); std::string get_line_before_insert();
bool on_key_press_event(GdkEventKey* key); bool on_key_press_event(GdkEventKey* key);
bool on_button_press_event(GdkEventButton *event);
std::pair<char, unsigned> find_tab_char_and_size(); std::pair<char, unsigned> find_tab_char_and_size();
unsigned tab_size; unsigned tab_size;
char tab_char; char tab_char;
std::string tab; std::string tab;
std::regex tabs_regex; std::regex tabs_regex;
bool spellcheck_all=false;
private: private:
GtkSourceSearchContext *search_context; GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings; GtkSourceSearchSettings *search_settings;
static void search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data); 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 { class GenericView : public View {
public: public:
@ -101,17 +125,14 @@ namespace Source {
public: public:
ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path); ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
boost::filesystem::path project_path; boost::filesystem::path project_path;
void start_reparse();
protected: protected:
void init_parse(); void init_parse();
void start_reparse();
bool on_key_press_event(GdkEventKey* key); bool on_key_press_event(GdkEventKey* key);
bool on_focus_out_event(GdkEventFocus* event);
std::unique_ptr<clang::TranslationUnit> clang_tu; std::unique_ptr<clang::TranslationUnit> clang_tu;
std::mutex parsing_mutex; std::mutex parsing_mutex;
std::unique_ptr<clang::Tokens> clang_tokens; std::unique_ptr<clang::Tokens> clang_tokens;
bool clang_readable;
sigc::connection delayed_reparse_connection; sigc::connection delayed_reparse_connection;
sigc::connection delayed_tooltips_connection;
std::shared_ptr<Terminal::InProgress> parsing_in_progress; std::shared_ptr<Terminal::InProgress> parsing_in_progress;
std::thread parse_thread; std::thread parse_thread;
@ -129,12 +150,7 @@ namespace Source {
std::set<std::string> last_syntax_tags; std::set<std::string> last_syntax_tags;
void update_diagnostics(); void update_diagnostics();
void update_types(); 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; static clang::Index clang_index;
std::vector<std::string> get_compilation_commands(); 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); ClangViewAutocomplete(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path);
protected: protected:
bool on_key_press_event(GdkEventKey* key); bool on_key_press_event(GdkEventKey* key);
bool on_focus_out_event(GdkEventFocus* event);
std::thread autocomplete_thread; std::thread autocomplete_thread;
private: private:
void start_autocomplete(); void start_autocomplete();
@ -174,13 +189,14 @@ namespace Source {
private: private:
Glib::RefPtr<Gtk::TextTag> similar_tokens_tag; Glib::RefPtr<Gtk::TextTag> similar_tokens_tag;
std::string last_similar_tokens_tagged; std::string last_similar_tokens_tagged;
sigc::connection delayed_tag_similar_tokens_connection;
std::unique_ptr<SelectionDialog> selection_dialog; std::unique_ptr<SelectionDialog> selection_dialog;
bool renaming=false; bool renaming=false;
}; };
class ClangView : public ClangViewRefactor { class ClangView : public ClangViewRefactor {
public: 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(); void async_delete();
bool restart_parse(); bool restart_parse();
private: private:

17
src/sourcefile.cc

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

132
src/terminal.cc

@ -8,57 +8,74 @@
#include <iostream> //TODO: remove #include <iostream> //TODO: remove
using namespace std; //TODO: remove using namespace std; //TODO: remove
//TODO: Windows...
//A working implementation of popen3, with all pipes getting closed properly. //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 //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; pid_t pid;
int p_stdin[2], p_stdout[2], p_stderr[2]; int stdin_p[2], stdout_p[2], stderr_p[2];
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0) { if(stdin_fd!=nullptr && pipe(stdin_p)!=0) {
close(p_stdin[0]); close(stdin_p[0]);
close(p_stdout[0]); close(stdin_p[1]);
close(p_stderr[0]); return -1;
close(p_stdin[1]); }
close(p_stdout[1]); if(stdout_fd!=nullptr && pipe(stdout_p)!=0) {
close(p_stderr[1]); 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; return -1;
} }
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
close(p_stdin[0]); if(stdin_fd!=nullptr) close(stdin_p[0]);
close(p_stdout[0]); if(stdin_fd!=nullptr) close(stdin_p[1]);
close(p_stderr[0]); if(stdout_fd!=nullptr) close(stdout_p[0]);
close(p_stdin[1]); if(stdout_fd!=nullptr) close(stdout_p[1]);
close(p_stdout[1]); if(stderr_fd!=nullptr) close(stderr_p[0]);
close(p_stderr[1]); if(stderr_fd!=nullptr) close(stderr_p[1]);
return pid; return pid;
} }
else if (pid == 0) { else if (pid == 0) {
close(p_stdin[1]); if(stdin_fd!=nullptr) close(stdin_p[1]);
close(p_stdout[0]); if(stdout_fd!=nullptr) close(stdout_p[0]);
close(p_stderr[0]); if(stderr_fd!=nullptr) close(stderr_p[0]);
dup2(p_stdin[0], 0); if(stdin_fd!=nullptr) dup2(stdin_p[0], 0);
dup2(p_stdout[1], 1); if(stdout_fd!=nullptr) dup2(stdout_p[1], 1);
dup2(p_stderr[1], 2); if(stderr_fd!=nullptr) dup2(stderr_p[1], 2);
setpgid(0, 0); 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: 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 //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"); perror("execl");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
close(p_stdin[0]); if(stdin_fd!=nullptr) close(stdin_p[0]);
close(p_stdout[1]); if(stdout_fd!=nullptr) close(stdout_p[1]);
close(p_stderr[1]); if(stderr_fd!=nullptr) close(stderr_p[1]);
stdin = p_stdin[1]; if(stdin_fd!=nullptr) *stdin_fd = stdin_p[1];
stdout = p_stdout[0]; if(stdout_fd!=nullptr) *stdout_fd = stdout_p[0];
stderr = p_stderr[0]; if(stderr_fd!=nullptr) *stderr_fd = stderr_p[0];
return pid; return pid;
} }
@ -137,26 +154,18 @@ Terminal::Terminal() {
} }
int Terminal::execute(const std::string &command, const boost::filesystem::path &path) { int Terminal::execute(const std::string &command, const boost::filesystem::path &path) {
std::string cd_path_and_command; int stdin_fd, stdout_fd, stderr_fd;
if(path!="") { auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd);
//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);
if (pid<=0) { if (pid<=0) {
async_print("Error: Failed to run command: " + command + "\n"); async_print("Error: Failed to run command: " + command + "\n");
return -1; return -1;
} }
else { else {
std::thread stderr_thread([this, stderr](){ std::thread stderr_thread([this, stderr_fd](){
char buffer[1024]; char buffer[1024];
ssize_t n; ssize_t n;
while ((n=read(stderr, buffer, 1024)) > 0) { while ((n=read(stderr_fd, buffer, 1024)) > 0) {
std::string message; std::string message;
for(ssize_t c=0;c<n;c++) for(ssize_t c=0;c<n;c++)
message+=buffer[c]; message+=buffer[c];
@ -164,11 +173,11 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
} }
}); });
stderr_thread.detach(); stderr_thread.detach();
std::thread stdout_thread([this, stdout](){ std::thread stdout_thread([this, stdout_fd](){
char buffer[1024]; char buffer[1024];
ssize_t n; ssize_t n;
INFO("read"); INFO("read");
while ((n=read(stdout, buffer, 1024)) > 0) { while ((n=read(stdout_fd, buffer, 1024)) > 0) {
std::string message; std::string message;
for(ssize_t c=0;c<n;c++) for(ssize_t c=0;c<n;c++)
message+=buffer[c]; message+=buffer[c];
@ -179,9 +188,9 @@ int Terminal::execute(const std::string &command, const boost::filesystem::path
int exit_code; int exit_code;
waitpid(pid, &exit_code, 0); waitpid(pid, &exit_code, 0);
close(stdin); close(stdin_fd);
close(stdout); close(stdout_fd);
close(stderr); close(stderr_fd);
return exit_code; 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) { 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::thread async_execute_thread([this, command, path, callback](){
std::string cd_path_and_command; int stdin_fd, stdout_fd, stderr_fd;
if(path!="") {
//TODO: Windows...
cd_path_and_command="cd \""+path.string()+"\" && "+command;
}
else
cd_path_and_command=command;
int stdin, stdout, stderr;
async_executes_mutex.lock(); async_executes_mutex.lock();
stdin_buffer.clear(); stdin_buffer.clear();
auto pid=popen3(cd_path_and_command.c_str(), stdin, stdout, stderr); auto pid=popen3(command, path.string(), &stdin_fd, &stdout_fd, &stderr_fd);
async_executes.emplace_back(pid, stdin); async_executes.emplace_back(pid, stdin_fd);
async_executes_mutex.unlock(); async_executes_mutex.unlock();
if (pid<=0) { if (pid<=0) {
@ -211,10 +211,10 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
callback(-1); callback(-1);
} }
else { else {
std::thread stderr_thread([this, stderr](){ std::thread stderr_thread([this, stderr_fd](){
char buffer[1024]; char buffer[1024];
ssize_t n; ssize_t n;
while ((n=read(stderr, buffer, 1024)) > 0) { while ((n=read(stderr_fd, buffer, 1024)) > 0) {
std::string message; std::string message;
for(ssize_t c=0;c<n;c++) for(ssize_t c=0;c<n;c++)
message+=buffer[c]; message+=buffer[c];
@ -222,11 +222,11 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
} }
}); });
stderr_thread.detach(); stderr_thread.detach();
std::thread stdout_thread([this, stdout](){ std::thread stdout_thread([this, stdout_fd](){
char buffer[1024]; char buffer[1024];
ssize_t n; ssize_t n;
INFO("read"); INFO("read");
while ((n=read(stdout, buffer, 1024)) > 0) { while ((n=read(stdout_fd, buffer, 1024)) > 0) {
std::string message; std::string message;
for(ssize_t c=0;c<n;c++) for(ssize_t c=0;c<n;c++)
message+=buffer[c]; message+=buffer[c];
@ -245,9 +245,9 @@ void Terminal::async_execute(const std::string &command, const boost::filesystem
} }
} }
stdin_buffer.clear(); stdin_buffer.clear();
close(stdin); close(stdin_fd);
close(stdout); close(stdout_fd);
close(stderr); close(stderr_fd);
async_executes_mutex.unlock(); async_executes_mutex.unlock();
if(callback) if(callback)

6
src/terminal.h

@ -13,6 +13,7 @@ class Terminal : public Gtk::TextView {
public: public:
class Config { class Config {
public: public:
std::string cmake_command;
std::string make_command; std::string make_command;
}; };
@ -53,8 +54,11 @@ private:
Glib::RefPtr<Gtk::TextTag> bold_tag; Glib::RefPtr<Gtk::TextTag> bold_tag;
std::mutex async_executes_mutex; 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; std::list<std::pair<pid_t, int> > async_executes;
#endif
std::string stdin_buffer; 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; Gdk::Rectangle activation_rectangle;
std::unique_ptr<Gtk::Window> window; std::unique_ptr<Gtk::Window> window;
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark;
Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark;
private: private:
void wrap_lines(Glib::RefPtr<Gtk::TextBuffer> text_buffer); void wrap_lines(Glib::RefPtr<Gtk::TextBuffer> text_buffer);
std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer; std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer;
std::unique_ptr<Gtk::TextView> tooltip_widget; 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; Gtk::TextView& text_view;
int tooltip_width, tooltip_height; int tooltip_width, tooltip_height;
}; };

15
src/window.cc

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

Loading…
Cancel
Save