Browse Source

Merge branch 'cppit-master' into ci

merge-requests/365/head
Jørgen Lien Sellæg 10 years ago
parent
commit
27dcd0452b
  1. 1
      .gitignore
  2. 7
      .gitmodules
  3. 7
      README.md
  4. 19
      docs/api.md
  5. 5
      docs/install.md
  6. 2
      libclangmm
  7. 67
      plugins/snippet.py
  8. 14
      src/CMakeLists.txt
  9. 2384
      src/Doxyfile.in
  10. 213
      src/api.cc
  11. 72
      src/api.h
  12. 15
      src/api_ext.cc
  13. 39
      src/cmake.cc
  14. 9
      src/cmake/Modules/FindPlantuml.cmake
  15. 14
      src/config.cc
  16. 3
      src/config.h
  17. 6
      src/debug_clang.cc
  18. 4
      src/dialogs.cc
  19. 2
      src/dialogs.h
  20. 2
      src/directories.cc
  21. 83
      src/files.h
  22. 52
      src/info.cc
  23. 21
      src/info.h
  24. 27
      src/juci.h
  25. 5
      src/menu.cc
  26. 54
      src/notebook.cc
  27. 3
      src/notebook.h
  28. 113
      src/project.cc
  29. 40
      src/project.h
  30. 20
      src/project_build.cc
  31. 20
      src/project_build.h
  32. 23
      src/selectiondialog.cc
  33. 347
      src/source.cc
  34. 52
      src/source.h
  35. 784
      src/source_clang.cc
  36. 39
      src/source_clang.h
  37. 126
      src/terminal.cc
  38. 23
      src/terminal.h
  39. 28
      src/tooltips.cc
  40. 258
      src/window.cc
  41. 5
      src/window.h

1
.gitignore vendored

@ -3,7 +3,6 @@
!*/
#Whitelist
!*.cc
!*.h

7
.gitmodules vendored

@ -1,8 +1,7 @@
[submodule "libclangmm"]
path = libclangmm
url = https://github.com/cppit/libclangmm
branch = v0.9.6
[submodule "tiny-process-library"]
path = tiny-process-library
url = https://github.com/eidheim/tiny-process-library
branch = v1.0.4
[submodule "libclangmm"]
path = libclangmm
url = https://github.com/cppit/libclangmm

7
README.md

@ -20,7 +20,7 @@ towards libclang with speed and ease of use in mind.
* Rename refactoring across files (C++)
* Highlighting of similar types (C++)
* Automated documentation search (C++)
* Go to methods and usages (C++)
* Go to declaration, implementation, methods and usages (C++)
* Spell checking depending on file context
* Run shell commands within JuCi++
* Regex search and replace
@ -49,4 +49,7 @@ See [enhancements](https://github.com/cppit/jucipp/labels/enhancement) for plann
* [tiny-process-library](http://github.com/eidheim/tiny-process-library/) (downloaded directly with git --recursive, no need to install)
## Installation
See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md).
See [installation guide](docs/install.md).
## Documentation
See [how to build the API doc](docs/api.md).

19
docs/api.md

@ -0,0 +1,19 @@
# juCi++ API doc
## Prerequisites:
* doxygen
* plantuml
* install via apt-get or download from http://plantuml.com/
* see also http://plantuml.com/starting.html
* if downloaded either copy the jar file to /usr/bin or set the environment variable PLANTUML_PATH to point to the path containing the jar file)
## How to build the API doc:
```sh
mkdir jucipp/build
cd jucipp/build
cmake ..
make doc
```
## Where is the generated API documentation
Open jucipp/build/src/html/index.html

5
docs/install.md

@ -46,8 +46,6 @@ sudo make install
```
##Arch Linux/Manjaro Linux
**Arch Linux's lldb package has an issue that is being worked on (see https://github.com/cppit/jucipp/issues/191), and for the time being you have to build juCi++ without debug support. If you have the lldb package installed, please remove this package before building juCi++.**
Package available in the Arch User Repository:
https://aur.archlinux.org/packages/jucipp-git/
@ -56,12 +54,11 @@ If you have the arch package [yaourt](https://archlinux.fr/yaourt-en) installed:
yaourt -S jucipp-git
```
Alternatively, follow the instructions below.
Install dependencies:
```sh
sudo pacman -S git cmake make clang gtksourceviewmm boost aspell aspell-en
sudo pacman -S git cmake pkg-config make clang lldb gtksourceviewmm boost aspell aspell-en
```
Get juCi++ source, compile and install:

2
libclangmm

@ -1 +1 @@
Subproject commit 0753e37f0fb7a420a009a06bd81660916bd1943e
Subproject commit df42c7658bca28f5ba375eb6885a7d12ca8d15ed

67
plugins/snippet.py

@ -1,67 +0,0 @@
#!/usr/bin/python
#snippet plugin
import juci_to_python_api as juci, inspect
def initPlugin():
juci.addMenuElement("Snippet")
juci.addSubMenuElement("SnippetMenu", #name of parent menu
"Insert snippet", #menu description
"insertSnippet()", #function to execute
inspect.getfile(inspect.currentframe()), #plugin path
"<control><alt>space")
snippets = {}
snippets["for"] = """\
for(int i=0; i<v.size(); i++) {
// std::cout << v[i] << std::endl;
// Write code here
}
"""
snippets["if"] = """\
if(true) {
// Write code here
}
"""
snippets["ifelse"] = """\
if(false) {
// Write code here
} else {
// Write code here
}
"""
snippets["while"] = """\
while(condition) {
// Write code here
}
"""
snippets["main"] = """\
int main(int argc, char *argv[]) {
//Do something
}
"""
snippets["hello"] = """\
#include <iostream>
int main(int argc, char *argv[]) {
std::cout << "Hello, world! << std::endl;
}
"""
def getSnippet(word):
try:
output = snippets[word]
except KeyError:
output = word
return output
def insertSnippet():
theWord=juci.getWord()
output=getSnippet(theWord)
juci.replaceWord(output)

14
src/CMakeLists.txt

@ -84,6 +84,8 @@ set(project_files
files.h
filesystem.cc
filesystem.h
info.h
info.cc
juci.cc
juci.h
menu.cc
@ -141,3 +143,15 @@ target_link_libraries(${project_name} ${global_libraries})
install(TARGETS ${project_name}
RUNTIME DESTINATION bin
)
# add a target to generate API documentation with Doxygen
find_package(Plantuml)
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen to ${CMAKE_CURRENT_BINARY_DIR}" VERBATIM
)
endif(DOXYGEN_FOUND)

2384
src/Doxyfile.in

File diff suppressed because it is too large Load Diff

213
src/api.cc

@ -1,213 +0,0 @@
#include "api.h"
#include "logging.h"
#include "singletons.h"
Menu* PluginApi::menu=nullptr;
Notebook* PluginApi::notebook=nullptr;
/////////////////////////////
//// API ServiceProvider ////
/////////////////////////////
PluginApi::PluginApi(Notebook* notebook, Menu* menu) {
DEBUG("Adding pointers for the API");
this->notebook = notebook;
this->menu = menu;
DEBUG("Initiating plugins(from plugins.py)..");
#ifndef __APPLE__
//InitPlugins(); //TODO: fix this
#endif
DEBUG("Plugins initiated..");
}
void PluginApi::ReplaceWord(std::string word) {
Glib::RefPtr<Gtk::TextBuffer> buffer = libjuci::BufferFromNotebook();
Gtk::TextIter word_start = libjuci::IterFromNotebook();
Gtk::TextIter word_end = libjuci::IterFromNotebook();
libjuci::IterToWordStart(word_start);
libjuci::IterToWordEnd(word_end);
if (word_start != word_end) {
buffer->erase(word_start, word_end);
Gtk::TextIter current = libjuci::IterFromNotebook();
buffer->insert(current, word);
}
}
void PluginApi::ReplaceLine(std::string line) {
WARNING("use of unimplemented method");
}
std::string PluginApi::GetWord() {
Glib::RefPtr<Gtk::TextBuffer> buffer = libjuci::BufferFromNotebook();
Gtk::TextIter word_start = libjuci::IterFromNotebook();
Gtk::TextIter word_end = libjuci::IterFromNotebook();
libjuci::IterToWordStart(word_start);
libjuci::IterToWordEnd(word_end);
if (word_start < word_end) {
std::string word = buffer->get_text(word_start, word_end);
return word;
}
return "";
}
void PluginApi::InitPlugins() {
libjuci::LoadPlugin(Singleton::config_dir() + "plugins.py");
}
void PluginApi::AddMenuElement(std::string plugin_name) {
DEBUG("Adding menu element for "+plugin_name);
AddMenuXml(plugin_name, "PluginMenu");
std::string plugin_action_name = plugin_name+"Menu";
menu->action_group->add(Gtk::Action::create(plugin_action_name, plugin_name));
}
void PluginApi::AddSubMenuElement(std::string parent_menu,
std::string menu_name,
std::string menu_func_name,
std::string plugin_path,
std::string menu_keybinding) {
AddSubMenuXml(menu_func_name, parent_menu);
menu->action_group->add(Gtk::Action::create(menu_func_name,
menu_name),
Gtk::AccelKey(menu_keybinding),
[=]() {
libjuci::LoadPluginFunction(menu_func_name, plugin_path);
});
}
void PluginApi::AddMenuXml(std::string plugin_name, std::string parent_menu) {
std::string temp_menu = menu->ui;
std::size_t plugin_menu_pos = temp_menu.find(parent_menu);
// +2 gets you outside of the tag:<'menu_name'>
plugin_menu_pos+=parent_menu.size() +2;
std::string menu_prefix = temp_menu.substr(0, plugin_menu_pos);
std::string menu_suffix = temp_menu.substr(plugin_menu_pos);
std::string menu_input =
" <menu action='"+plugin_name+"Menu'> "
" </menu> ";
menu->ui = menu_prefix + menu_input + menu_suffix;
}
void PluginApi::AddSubMenuXml(std::string plugin_name,
std::string parent_menu) {
std::string temp_menu = menu->ui;
std::size_t parent_menu_pos = temp_menu.find(parent_menu);
// +2 gets you outside of the tag:<'menu_name'>
parent_menu_pos+=parent_menu.size() +2;
std::string menu_prefix = temp_menu.substr(0, parent_menu_pos);
std::string menu_suffix = temp_menu.substr(parent_menu_pos);
std::string menu_input ="<menuitem action='"+plugin_name+"'/>";
menu->ui = menu_prefix + menu_input + menu_suffix;
}
///////////////////////
//// Api to python ////
///////////////////////
void libjuci::ReplaceWord(const std::string word) {
PluginApi::ReplaceWord(word);
}
void libjuci::ReplaceLine(const std::string line) {
std::cout << "unimplemented: " << __func__ << " called"
<< std::endl;
}
std::string libjuci::GetWord() {
return PluginApi::GetWord();
}
void libjuci::AddMenuElement(std::string plugin_name) {
PluginApi::AddMenuElement(plugin_name);
}
void libjuci::AddSubMenuElement(std::string parent_menu,
std::string menu_name,
std::string menu_func_name,
std::string plugin_path,
std::string menu_keybinding) {
PluginApi::AddSubMenuElement(parent_menu,
menu_name,
menu_func_name,
plugin_path,
menu_keybinding);
}
//////////////////////////////
//// Boost.Python methods ////
//////////////////////////////
namespace bp = boost::python;
bp::api::object libjuci::OpenPythonScript(const std::string path,
bp::api::object python_name_space) {
bp::str str_path(path);
return bp::exec_file(str_path, python_name_space);
}
void libjuci::LoadPlugin(const std::string& plugin_name) {
try {
Py_Initialize();
bp::api::object main_module = bp::import("__main__");
bp::api::object main_namespace =
main_module.attr("__dict__");
bp::api::object ignored2 =
OpenPythonScript(plugin_name, main_namespace);
}catch (bp::error_already_set const&) {
PyErr_Print();
}
}
void libjuci::LoadPluginFunction(const std::string &function_name,
const std::string &plugin_path) {
try {
Py_Initialize();
bp::api::object main_module = bp::import("__main__");
bp::api::object main_namespace = main_module.attr("__dict__");
bp::api::object ignored2 = OpenPythonScript(plugin_path, main_namespace);
bp::str func_name(function_name);
bp::api::object returnValue = bp::eval(func_name, main_namespace);
}catch (bp::error_already_set const&) {
PyErr_Print();
}
}
void libjuci::InitPlugin(const std::string& plugin_path) {
try {
Py_Initialize();
bp::api::object main_module = bp::import("__main__");
bp::api::object main_namespace = main_module.attr("__dict__");
bp::api::object ignored2 = OpenPythonScript(plugin_path, main_namespace);
bp::object returnValue = bp::eval("initPlugin()", main_namespace);
// do something with return_value
}catch (bp::error_already_set const&) {
PyErr_Print();
}
}
///////////////////////
//// Glib wrappers ////
///////////////////////
void libjuci::IterToWordStart(Gtk::TextIter &iter) {
if (!iter.starts_line()) {
while (!iter.starts_word()) {
iter.backward_char();
}
}
}
void libjuci::IterToWordEnd(Gtk::TextIter &iter) {
if (!iter.ends_line()) {
while (!iter.ends_word()) {
iter.forward_char();
}
}
}
Glib::RefPtr<Gtk::TextBuffer> libjuci::BufferFromNotebook() {
return Glib::RefPtr<Gtk::TextBuffer>(PluginApi::notebook->get_current_view()->get_buffer());
}
Gtk::TextIter libjuci::IterFromNotebook() {
return libjuci::BufferFromNotebook()->get_insert()->get_iter();
}

72
src/api.h

@ -1,72 +0,0 @@
#ifndef JUCI_API_H_
#define JUCI_API_H_
#include <boost/python.hpp>
#include <Python.h>
#include <string>
#include "notebook.h"
#include "menu.h"
////////////////////
//// Plugin Api ////
////////////////////
class PluginApi {
public:
PluginApi(Notebook* notebook, Menu* menu);
static Menu* menu;
static Notebook* notebook;
static void InitPlugins();
// for Python module:
static std::string GetWord();
// menu management
static void AddMenuElement(const std::string plugin_name);
static void AddSubMenuElement(const std::string parent_menu,
const std::string menu_name,
const std::string menu_func_name,
const std::string plugin_path,
const std::string menu_keybinding);
static void ReplaceWord(const std::string word);
static void ReplaceLine(const std::string line);
protected:
static void AddMenuXml(std::string plugin_name, std::string parent_menu);
static void AddSubMenuXml(std::string plugin_name, std::string parent_menu);
}; // PluginApi
namespace libjuci {
///////////////////////
//// Glib wrappers ////
///////////////////////
void IterToWordStart(Gtk::TextIter &iter);
void IterToWordEnd(Gtk::TextIter &iter);
Gtk::TextIter IterFromNotebook();
Glib::RefPtr<Gtk::TextBuffer> BufferFromNotebook();
///////////////////////
//// Api to python ////
///////////////////////
void ReplaceWord(const std::string word);
void ReplaceLine(const std::string line);
std::string GetWord();
void AddMenuElement(const std::string plugin_name);
void AddSubMenuElement(const std::string parent_menu,
const std::string menu_name,
const std::string menu_func_name,
const std::string plugin_path,
const std::string menu_keybinding);
void AddMenuXml(const std::string plugin_name,
const std::string parent_menu);
void AddSubMenuXml(const std::string plugin_name,
const std::string parent_menu);
//////////////////////////////
//// Boost.Python methods ////
//////////////////////////////
namespace bp = boost::python;
bp::api::object OpenPythonScript(const std::string path,
bp::api::object python_name_space);
void LoadPlugin(const std::string& plugin_name);
void LoadPluginFunction(const std::string &function_name,
const std::string &plugin_path);
void InitPlugin(const std::string& plugin_path);
} // namespace libjuci
#endif // JUCI_API_H_

15
src/api_ext.cc

@ -1,15 +0,0 @@
#include "api.h"
BOOST_PYTHON_MODULE(juci_to_python_api) {
using namespace boost::python;
// plugin inclusion
def("addMenuElement", &libjuci::AddMenuElement);
def("addSubMenuElement", &libjuci::AddSubMenuElement);
def("loadPlugin", &libjuci::LoadPlugin);
def("initPlugin", &libjuci::InitPlugin);
// text editing
def("replaceLine", &libjuci::ReplaceLine);
def("replaceWord", &libjuci::ReplaceWord);
def("getWord", &libjuci::GetWord);
} // module::juci_to_python_api

39
src/cmake.cc

@ -3,14 +3,25 @@
#include "dialogs.h"
#include "config.h"
#include "terminal.h"
//Temporary fix for current Arch Linux boost linking problem
#ifdef __GNUC_PREREQ
#if __GNUC_PREREQ(5,1)
#include <regex>
#define REGEX_NS std
#endif
#endif
#ifndef REGEX_NS
#include <boost/regex.hpp>
#define REGEX_NS boost
#endif
CMake::CMake(const boost::filesystem::path &path) {
const auto find_cmake_project=[this](const boost::filesystem::path &cmake_path) {
for(auto &line: filesystem::read_lines(cmake_path)) {
const boost::regex project_regex("^ *project *\\(.*$", boost::regex::icase);
boost::smatch sm;
if(boost::regex_match(line, sm, project_regex))
const static REGEX_NS::regex project_regex("^ *project *\\(.*$", REGEX_NS::regex::icase);
REGEX_NS::smatch sm;
if(REGEX_NS::regex_match(line, sm, project_regex))
return true;
}
return false;
@ -206,9 +217,9 @@ void CMake::find_variables() {
end_line=file.size();
if(end_line>start_line) {
auto line=file.substr(start_line, end_line-start_line);
boost::smatch sm;
const boost::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$", boost::regex::icase);
if(boost::regex_match(line, sm, set_regex)) {
REGEX_NS::smatch sm;
const static REGEX_NS::regex set_regex("^ *set *\\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\\) *$", REGEX_NS::regex::icase);
if(REGEX_NS::regex_match(line, sm, set_regex)) {
auto data=sm[2].str();
while(data.size()>0 && data.back()==' ')
data.pop_back();
@ -216,8 +227,8 @@ void CMake::find_variables() {
variables[sm[1].str()]=data;
}
else {
const boost::regex project_regex("^ *project *\\( *([^ ]+).*\\) *$", boost::regex::icase);
if(boost::regex_match(line, sm, project_regex)) {
const static REGEX_NS::regex project_regex("^ *project *\\( *([^ ]+).*\\) *$", REGEX_NS::regex::icase);
if(REGEX_NS::regex_match(line, sm, project_regex)) {
auto data=sm[1].str();
parse_variable_parameters(data);
variables["CMAKE_PROJECT_NAME"]=data; //TODO: is this variable deprecated/non-standard?
@ -250,7 +261,8 @@ void CMake::parse_variable_parameters(std::string &data) {
pos--;
}
last_char=data[pos];
if(pos!=static_cast<size_t>(-1))
last_char=data[pos];
pos++;
}
for(auto &var: variables) {
@ -307,7 +319,8 @@ std::vector<std::string> CMake::get_function_parameters(std::string &data) {
parameter_pos=pos+1;
}
last_char=data[pos];
if(pos!=static_cast<size_t>(-1))
last_char=data[pos];
pos++;
}
parameters.emplace_back(data.substr(parameter_pos));
@ -324,6 +337,7 @@ std::vector<std::string> CMake::get_function_parameters(std::string &data) {
}
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > CMake::get_functions_parameters(const std::string &name) {
const REGEX_NS::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$", REGEX_NS::regex::icase);
if(!parsed)
parse();
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > functions;
@ -337,9 +351,8 @@ std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > CMak
end_line=file.size();
if(end_line>start_line) {
auto line=file.substr(start_line, end_line-start_line);
const boost::regex function_regex("^ *"+name+" *\\( *(.*)\\) *$", boost::regex::icase);
boost::smatch sm;
if(boost::regex_match(line, sm, function_regex)) {
REGEX_NS::smatch sm;
if(REGEX_NS::regex_match(line, sm, function_regex)) {
auto data=sm[1].str();
while(data.size()>0 && data.back()==' ')
data.pop_back();

9
src/cmake/Modules/FindPlantuml.cmake

@ -0,0 +1,9 @@
if(NOT DEFINED plantuml_FOUND)
find_file(PLANTUML_JARFILE
NAMES plantuml.jar
HINTS "$ENV{PLANTUML_PATH}" ENV PLANTUML_DIR
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
plantuml DEFAULT_MSG PLANTUML_JARFILE)
endif()

14
src/config.cc

@ -59,14 +59,11 @@ void Config::load() {
void Config::find_or_create_config_files() {
auto config_dir = home/"config";
auto config_json = config_dir/"config.json";
auto plugins_py = config_dir/"plugins.py";
boost::filesystem::create_directories(config_dir); // io exp captured by calling method
if (!boost::filesystem::exists(config_json))
filesystem::write(config_json, configjson); // vars configjson and pluginspy
if (!boost::filesystem::exists(plugins_py)) // live in files.h
filesystem::write(plugins_py, pluginspy);
filesystem::write(config_json, configjson);
auto juci_style_path = home/"styles";
boost::filesystem::create_directories(juci_style_path); // io exp captured by calling method
@ -189,6 +186,7 @@ void Config::get_source() {
source.font=source_json.get<std::string>("font");
source.cleanup_whitespace_characters=source_json.get<bool>("cleanup_whitespace_characters");
source.show_whitespace_characters=source_json.get<std::string>("show_whitespace_characters");
source.show_map = source_json.get<bool>("show_map");
source.map_font_size = source_json.get<std::string>("map_font_size");
@ -204,8 +202,12 @@ void Config::get_source() {
source.highlight_current_line = source_json.get<bool>("highlight_current_line");
source.show_line_numbers = source_json.get<bool>("show_line_numbers");
for (auto &i : source_json.get_child("clang_types"))
source.clang_types[i.first] = i.second.get_value<std::string>();
for (auto &i : source_json.get_child("clang_types")) {
try {
source.clang_types[std::stoi(i.first)] = i.second.get_value<std::string>();
}
catch(const std::exception &) {}
}
source.clang_format_style = source_json.get<std::string>("clang_format_style");

3
src/config.h

@ -56,6 +56,7 @@ public:
std::string spellcheck_language;
bool cleanup_whitespace_characters;
std::string show_whitespace_characters;
bool show_map;
std::string map_font_size;
@ -66,7 +67,7 @@ public:
bool wrap_lines;
bool highlight_current_line;
bool show_line_numbers;
std::unordered_map<std::string, std::string> clang_types;
std::unordered_map<int, std::string> clang_types;
std::string clang_format_style;
std::unordered_map<std::string, DocumentationSearch> documentation_searches;

6
src/debug_clang.cc

@ -98,7 +98,7 @@ void Debug::Clang::start(const std::string &command, const boost::filesystem::pa
}
lldb::SBError error;
process = std::unique_ptr<lldb::SBProcess>(new lldb::SBProcess(target.Launch(*listener, argv, (const char**)environ, nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error)));
process = std::unique_ptr<lldb::SBProcess>(new lldb::SBProcess(target.Launch(*listener, argv, const_cast<const char**>(environ), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error)));
if(error.Fail()) {
Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true);
if(callback)
@ -463,7 +463,9 @@ void Debug::Clang::remove_breakpoint(const boost::filesystem::path &file_path, i
auto file_spec=line_entry.GetFileSpec();
boost::filesystem::path breakpoint_path=file_spec.GetDirectory();
breakpoint_path/=file_spec.GetFilename();
if(breakpoint_path==file_path) {
boost::system::error_code ec;
breakpoint_path = boost::filesystem::canonical(breakpoint_path, ec);
if(!ec && breakpoint_path==file_path) {
if(!target.BreakpointDelete(breakpoint.GetID()))
Terminal::get().async_print("Error (debug): Could not delete breakpoint at: "+file_path.string()+":"+std::to_string(line_nr)+'\n', true);
return;

4
src/dialogs.cc

@ -28,6 +28,10 @@ Dialog::Message::Message(const std::string &text): Gtk::MessageDialog(text, fals
g_main_context_iteration(NULL, false);
}
bool Dialog::Message::on_delete_event(GdkEventAny *event) {
return true;
}
std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::string &title,
const std::vector<std::pair<std::string, Gtk::ResponseType>> &buttons,
Gtk::FileChooserAction gtk_options) {

2
src/dialogs.h

@ -16,6 +16,8 @@ public:
class Message : public Gtk::MessageDialog {
public:
Message(const std::string &text);
protected:
bool on_delete_event(GdkEventAny *event) override;
};
private:

2
src/directories.cc

@ -365,7 +365,7 @@ Directories::Directories() : Gtk::TreeView(), stop_update_thread(false) {
menu_item_delete.signal_activate().connect([this] {
if(menu_popup_row_path.empty())
return;
Gtk::MessageDialog dialog((Gtk::Window&)(*get_toplevel()), "Delete!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
Gtk::MessageDialog dialog(*static_cast<Gtk::Window*>(get_toplevel()), "Delete!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
dialog.set_default_response(Gtk::RESPONSE_NO);
dialog.set_secondary_text("Are you sure you want to delete "+menu_popup_row_path.string()+"?");
int result = dialog.run();

83
src/files.h

@ -2,7 +2,7 @@
#define JUCI_FILES_H_
#include <string>
#define JUCI_VERSION "1.1.3"
#define JUCI_VERSION "1.1.3-3"
const std::string configjson =
"{\n"
@ -37,6 +37,8 @@ const std::string configjson =
#endif
" \"cleanup_whitespace_characters_comment\": \"Remove trailing whitespace characters on save, and add trailing newline if missing\",\n"
" \"cleanup_whitespace_characters\": false,\n"
" \"show_whitespace_characters_comment\": \"Determines what kind of whitespaces should be drawn. Use comma-separated list of: space, tab, newline, nbsp, leading, text, trailing or all\",\n"
" \"show_whitespace_characters\": \"\",\n"
" \"show_map\": true,\n"
" \"map_font_size\": \"1\",\n"
" \"spellcheck_language_comment\": \"Use \\\"\\\" to set language from your locale settings\",\n"
@ -92,6 +94,7 @@ const std::string configjson =
" \"source_center_cursor\": \"<primary>l\",\n"
" \"source_find_documentation\": \"<primary><shift>d\",\n"
" \"source_goto_declaration\": \"<primary>d\",\n"
" \"source_goto_implementation\": \"<primary>i\",\n"
" \"source_goto_usage\": \"<primary>u\",\n"
" \"source_goto_method\": \"<primary>m\",\n"
" \"source_rename\": \"<primary>r\",\n"
@ -326,82 +329,4 @@ const std::string juci_dark_blue_style =
"\n"
"</style-scheme>\n";
const std::string pluginspy =
"#!/usr/bin/python \n"
"import juci_to_python_api as juci \n"
"import glob \n"
"\n"
"def loadplugins(): \n"
" plugin_files = glob.glob(\"../plugins/*.py\") \n"
" for current_file in plugin_files: \n"
" juci.initPlugin(current_file) \n"
"loadplugins() \n";
const std::string snippetpy =
"#!/usr/bin/python \n"
"#snippet plugin \n"
"import juci_to_python_api as juci, inspect \n"
" \n"
"def initPlugin(): \n"
" juci.addMenuElement(\"Snippet\") \n"
" juci.addSubMenuElement(\"SnippetMenu\", #name of parent menu \n"
" \"Insert snippet\", #menu description \n"
" \"insertSnippet()\", #function to execute \n"
" inspect.getfile(inspect.currentframe()), #plugin path \n"
" \"<control><alt>space\") \n"
"snippets = {} \n"
" \n"
"snippets[\"for\"] = \"\"\"\\\n"
"for(int i=0; i<v.size(); i++) { \n"
" // std::cout << v[i] << std::endl; \n"
" // Write code here \n"
"} \n"
"\"\"\" \n"
" \n"
"snippets[\"if\"] = \"\"\"\\\n"
"if(true) { \n"
" // Write code here \n"
"} \n"
"\"\"\" \n"
" \n"
"snippets[\"ifelse\"] = \"\"\"\\\n"
"if(false) { \n"
" // Write code here \n"
"} else { \n"
" // Write code here \n"
"} \n"
"\"\"\" \n"
" \n"
"snippets[\"while\"] = \"\"\"\\\n"
"while(condition) { \n"
" // Write code here \n"
"} \n"
"\"\"\" \n"
" \n"
"snippets[\"main\"] = \"\"\"\\\n"
"int main(int argc, char *argv[]) { \n"
" //Do something \n"
"} \n"
"\"\"\" \n"
" \n"
"snippets[\"hello\"] = \"\"\"\\\n"
"#include <iostream> \n"
" \n"
"int main(int argc, char *argv[]) { \n"
" std::cout << \"Hello, world! << std::endl; \n"
"} \n"
"\"\"\" \n"
" \n"
"def getSnippet(word): \n"
" try: \n"
" output = snippets[word] \n"
" except KeyError: \n"
" output = word \n"
" return output \n"
" \n"
"def insertSnippet(): \n"
" theWord=juci.getWord() \n"
" output=getSnippet(theWord) \n"
" juci.replaceWord(output) \n";
#endif // JUCI_FILES_H_

52
src/info.cc

@ -0,0 +1,52 @@
#include "info.h"
namespace sigc {
#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
template <typename Functor>
struct functor_trait<Functor, false> {
typedef decltype (::sigc::mem_fun(std::declval<Functor&>(),
&Functor::operator())) _intermediate;
typedef typename _intermediate::result_type result_type;
typedef Functor functor_type;
};
#else
SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
#endif
}
Info::Info() {
set_hexpand(false);
set_halign(Gtk::Align::ALIGN_END);
auto content_area=dynamic_cast<Gtk::Container*>(get_content_area());
label.set_max_width_chars(40);
label.set_line_wrap(true);
content_area->add(label);
auto provider = Gtk::CssProvider::create();
provider->load_from_data("* {border-radius: 5px;}");
get_style_context()->add_provider(provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
//Workaround from https://bugzilla.gnome.org/show_bug.cgi?id=710888
//Issue described at the same issue report
//TODO: remove later
auto revealer = gtk_widget_get_template_child (GTK_WIDGET (gobj()), GTK_TYPE_INFO_BAR, "revealer");
if (revealer) {
gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 0);
}
}
void Info::print(const std::string &text) {
timeout_connection.disconnect();
//Timeout based on https://en.wikipedia.org/wiki/Words_per_minute
//(average_words_per_minute*average_letters_per_word)/60 => (228*4.5)/60 = 17.1
double timeout=1000.0*std::max(3.0, 1.0+text.size()/17.1);
timeout_connection=Glib::signal_timeout().connect([this]() {
hide();
return false;
}, timeout);
label.set_text(text);
show();
}

21
src/info.h

@ -0,0 +1,21 @@
#ifndef JUCI_INFO_H_
#define JUCI_INFO_H_
#include <gtkmm.h>
class Info : public Gtk::InfoBar {
Info();
public:
static Info &get() {
static Info instance;
return instance;
}
void print(const std::string &text);
private:
Gtk::Label label;
sigc::connection timeout_connection;
};
#endif // JUCI_INFO_H_

27
src/juci.h

@ -1,3 +1,30 @@
/**
\mainpage
juCi++ is a lightweight C++ IDE written in C++
(<a href="https://github.com/cppit/jucipp">Github page</a>).
\section sec_overview Overview
The application entry point is the class Application.
\section sec_dependencies Dependencies
juCi++ is based on boost, gtkmm and libclang (among others).
\startuml
left to right direction
component [juCi++] #LightGreen
component [libclangmm] #Cyan
component [tiny-process-library] #Cyan
[juCi++] --> [boost-filesystem] : use
[juCi++] --> [boost-regex] : use
[juCi++] --> [gtkmm-3.0] : use
[juCi++] --> [gtksourceviewmm-3.0] : use
[juCi++] --> [aspell] : use
[juCi++] --> [lbclang] : use
[juCi++] --> [lbdb] : use
[juCi++] --> [libclangmm] : use
[juCi++] --> [tiny-process-library] : use
\enduml
*/
#ifndef JUCI_JUCI_H_
#define JUCI_JUCI_H_

5
src/menu.cc

@ -213,6 +213,11 @@ Menu::Menu() {
+accels["source_goto_declaration"]+ //For Ubuntu...
" </item>"
" <item>"
" <attribute name='label' translatable='yes'>_Go to Implementation</attribute>"
" <attribute name='action'>app.source_goto_implementation</attribute>"
+accels["source_goto_implementation"]+ //For Ubuntu...
" </item>"
" <item>"
" <attribute name='label' translatable='yes'>_Go to Usage</attribute>"
" <attribute name='action'>app.source_goto_usage</attribute>"
+accels["source_goto_usage"]+ //For Ubuntu...

54
src/notebook.cc

@ -199,54 +199,10 @@ bool Notebook::save(int page) {
if(page>=size())
return false;
auto view=get_view(page);
if (view->file_path != "" && view->get_buffer()->get_modified()) {
//Remove trailing whitespace characters on save, and add trailing newline if missing
if(Config::get().source.cleanup_whitespace_characters) {
auto buffer=view->get_buffer();
buffer->begin_user_action();
for(int line=0;line<buffer->get_line_count();line++) {
auto iter=buffer->get_iter_at_line(line);
auto end_iter=iter;
while(!end_iter.ends_line())
end_iter.forward_char();
if(iter==end_iter)
continue;
iter=end_iter;
while(!iter.starts_line() && (*iter==' ' || *iter=='\t' || iter.ends_line()))
iter.backward_char();
if(*iter!=' ' && *iter!='\t')
iter.forward_char();
if(iter==end_iter)
continue;
buffer->erase(iter, end_iter);
}
auto iter=buffer->end();
if(!iter.starts_line())
buffer->insert(buffer->end(), "\n");
buffer->end_user_action();
}
if(filesystem::write(view->file_path, view->get_buffer())) {
if(auto clang_view=dynamic_cast<Source::ClangView*>(view)) {
if(clang_view->language->get_id()=="chdr" || clang_view->language->get_id()=="cpphdr") {
for(auto a_view: source_views) {
if(auto a_clang_view=dynamic_cast<Source::ClangView*>(a_view)) {
if(clang_view!=a_clang_view)
a_clang_view->soft_reparse_needed=true;
}
}
}
}
view->get_buffer()->set_modified(false);
Project::on_save(page);
return true;
}
Terminal::get().print("Error: could not save file " +view->file_path.string()+"\n", true);
}
return false;
if(!view->save(source_views))
return false;
Project::on_save(page);
return true;
}
bool Notebook::save_current() {
@ -308,7 +264,7 @@ boost::filesystem::path Notebook::get_current_folder() {
}
bool Notebook::save_modified_dialog(int page) {
Gtk::MessageDialog dialog((Gtk::Window&)(*get_toplevel()), "Save file!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
Gtk::MessageDialog dialog(*static_cast<Gtk::Window*>(get_toplevel()), "Save file!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
dialog.set_default_response(Gtk::RESPONSE_YES);
dialog.set_secondary_text("Do you want to save: " + get_view(page)->file_path.string()+" ?");
int result = dialog.run();

3
src/notebook.h

@ -37,11 +37,12 @@ public:
void configure(int view_nr);
boost::filesystem::path get_current_folder();
std::vector<Source::View*> source_views; //Is NOT freed in destructor, this is intended for quick program exit.
Gtk::Label info;
Gtk::Label status;
private:
bool save_modified_dialog(int page);
std::vector<Source::View*> source_views; //Is NOT freed in destructor, this is intended for quick program exit.
std::vector<std::unique_ptr<Gtk::Widget> > source_maps;
std::vector<std::unique_ptr<Gtk::ScrolledWindow> > scrolled_windows;
std::vector<std::unique_ptr<Gtk::HBox> > hboxes;

113
src/project.cc

@ -9,6 +9,7 @@
#ifdef JUCI_ENABLE_DEBUG
#include "debug_clang.h"
#endif
#include "info.h"
boost::filesystem::path Project::debug_last_stop_file_path;
std::unordered_map<std::string, std::string> Project::run_arguments;
@ -16,7 +17,7 @@ std::unordered_map<std::string, std::string> Project::debug_run_arguments;
std::atomic<bool> Project::compiling(false);
std::atomic<bool> Project::debugging(false);
std::pair<boost::filesystem::path, std::pair<int, int> > Project::debug_stop;
std::unique_ptr<Project::Language> Project::current_language;
std::unique_ptr<Project::Base> Project::current;
Gtk::Label &Project::debug_status_label() {
static Gtk::Label label;
@ -47,11 +48,11 @@ void Project::on_save(int page) {
cmake_path=filesystem::find_file_in_path_parents("CMakeLists.txt", view->file_path.parent_path());
if(!cmake_path.empty()) {
auto build=get_build(cmake_path);
if(dynamic_cast<CMake*>(build.get())) {
build->update_default_build(true);
if(boost::filesystem::exists(build->get_debug_build_path()))
build->update_debug_build(true);
auto build=Build::create(cmake_path);
if(dynamic_cast<CMakeBuild*>(build.get())) {
build->update_default(true);
if(boost::filesystem::exists(build->get_debug_path()))
build->update_debug(true);
for(int c=0;c<Notebook::get().size();c++) {
auto source_view=Notebook::get().get_view(c);
@ -105,35 +106,58 @@ void Project::debug_update_stop() {
Notebook::get().get_current_view()->get_buffer()->place_cursor(Notebook::get().get_current_view()->get_buffer()->get_insert()->get_iter());
}
std::unique_ptr<Project::Language> Project::get_language() {
std::unique_ptr<Project::Base> Project::create() {
std::unique_ptr<Project::Build> build;
if(Notebook::get().get_current_page()!=-1) {
auto view=Notebook::get().get_current_view();
build=get_build(view->file_path);
build=Build::create(view->file_path);
if(view->language) {
auto language_id=view->language->get_id();
if(language_id=="markdown")
return std::unique_ptr<Project::Language>(new Project::Markdown(std::move(build)));
return std::unique_ptr<Project::Base>(new Project::Markdown(std::move(build)));
if(language_id=="python")
return std::unique_ptr<Project::Language>(new Project::Python(std::move(build)));
return std::unique_ptr<Project::Base>(new Project::Python(std::move(build)));
if(language_id=="js")
return std::unique_ptr<Project::Language>(new Project::JavaScript(std::move(build)));
return std::unique_ptr<Project::Base>(new Project::JavaScript(std::move(build)));
if(language_id=="html")
return std::unique_ptr<Project::Language>(new Project::HTML(std::move(build)));
return std::unique_ptr<Project::Base>(new Project::HTML(std::move(build)));
}
}
else
build=get_build(Directories::get().path);
build=Build::create(Directories::get().path);
if(dynamic_cast<CMake*>(build.get()))
return std::unique_ptr<Project::Language>(new Project::Clang(std::move(build)));
if(dynamic_cast<CMakeBuild*>(build.get()))
return std::unique_ptr<Project::Base>(new Project::Clang(std::move(build)));
else
return std::unique_ptr<Project::Language>(new Project::Language(std::move(build)));
return std::unique_ptr<Project::Base>(new Project::Base(std::move(build)));
}
std::pair<std::string, std::string> Project::Base::get_run_arguments() {
Info::get().print("Could not find a supported project");
return {"", ""};
}
void Project::Base::compile() {
Info::get().print("Could not find a supported project");
}
void Project::Base::compile_and_run() {
Info::get().print("Could not find a supported project");
}
std::pair<std::string, std::string> Project::Base::debug_get_run_arguments() {
Info::get().print("Could not find a supported project");
return {"", ""};
}
void Project::Base::debug_start() {
Info::get().print("Could not find a supported project");
}
std::pair<std::string, std::string> Project::Clang::get_run_arguments() {
if(build->get_default_build_path().empty() || !build->update_default_build())
auto build_path=build->get_default_path();
if(build_path.empty())
return {"", ""};
auto project_path=build->project_path.string();
@ -146,25 +170,21 @@ std::pair<std::string, std::string> Project::Clang::get_run_arguments() {
auto executable=build->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string();
if(executable!="") {
auto project_path=build->project_path;
auto build_path=build->get_default_build_path();
if(!build_path.empty()) {
size_t pos=executable.find(project_path.string());
if(pos!=std::string::npos)
executable.replace(pos, project_path.string().size(), build_path.string());
}
size_t pos=executable.find(project_path);
if(pos!=std::string::npos)
executable.replace(pos, project_path.size(), build_path.string());
arguments=filesystem::escape_argument(executable);
}
else
arguments=filesystem::escape_argument(build->get_default_build_path());
arguments=filesystem::escape_argument(build->get_default_path());
}
return {project_path, arguments};
}
void Project::Clang::compile() {
auto default_build_path=build->get_default_build_path();
if(default_build_path.empty() || !build->update_default_build())
auto default_build_path=build->get_default_path();
if(default_build_path.empty() || !build->update_default())
return;
if(Config::get().project.clear_terminal_on_compile)
@ -178,8 +198,8 @@ void Project::Clang::compile() {
}
void Project::Clang::compile_and_run() {
auto default_build_path=build->get_default_build_path();
if(default_build_path.empty() || !build->update_default_build())
auto default_build_path=build->get_default_path();
if(default_build_path.empty() || !build->update_default())
return;
auto project_path=build->project_path;
@ -219,7 +239,8 @@ void Project::Clang::compile_and_run() {
#ifdef JUCI_ENABLE_DEBUG
std::pair<std::string, std::string> Project::Clang::debug_get_run_arguments() {
if(build->get_default_build_path().empty() || !build->update_default_build())
auto build_path=build->get_debug_path();
if(build_path.empty())
return {"", ""};
auto project_path=build->project_path.string();
@ -232,25 +253,21 @@ std::pair<std::string, std::string> Project::Clang::debug_get_run_arguments() {
auto executable=build->get_executable(Notebook::get().get_current_page()!=-1?Notebook::get().get_current_view()->file_path:"").string();
if(executable!="") {
auto project_path=build->project_path;
auto build_path=build->get_debug_build_path();
if(!build_path.empty()) {
size_t pos=executable.find(project_path.string());
if(pos!=std::string::npos)
executable.replace(pos, project_path.string().size(), build_path.string());
}
size_t pos=executable.find(project_path);
if(pos!=std::string::npos)
executable.replace(pos, project_path.size(), build_path.string());
arguments=filesystem::escape_argument(executable);
}
else
arguments=filesystem::escape_argument(build->get_debug_build_path());
arguments=filesystem::escape_argument(build->get_debug_path());
}
return {project_path, arguments};
}
void Project::Clang::debug_start() {
auto debug_build_path=build->get_debug_build_path();
if(debug_build_path.empty() || !build->update_debug_build())
auto debug_build_path=build->get_debug_path();
if(debug_build_path.empty() || !build->update_debug())
return;
auto project_path=build->project_path;
@ -380,12 +397,8 @@ void Project::Clang::debug_backtrace() {
Debug::Clang::get().select_frame(frame.index);
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(frame.line_nr-1, frame.line_index-1));
while(g_main_context_pending(NULL))
g_main_context_iteration(NULL, false);
if(Notebook::get().get_current_page()!=-1 && Notebook::get().get_current_view()==view)
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
view->place_cursor_at_line_index(frame.line_nr-1, frame.line_index-1);
view->scroll_to_cursor_delayed(view, true, true);
}
}
};
@ -420,12 +433,8 @@ void Project::Clang::debug_show_variables() {
Debug::Clang::get().select_frame(variable.frame_index, variable.thread_index_id);
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(variable.line_nr-1, variable.line_index-1));
while(g_main_context_pending(NULL))
g_main_context_iteration(NULL, false);
if(Notebook::get().get_current_page()!=-1 && Notebook::get().get_current_view()==view)
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
view->place_cursor_at_line_index(variable.line_nr-1, variable.line_index-1);
view->scroll_to_cursor_delayed(view, true, true);
}
}
};

40
src/project.h

@ -25,20 +25,20 @@ namespace Project {
void debug_update_stop();
void debug_update_status(const std::string &debug_status);
class Language {
class Base {
public:
Language(std::unique_ptr<Build> &&build): build(std::move(build)) {}
virtual ~Language() {}
Base(std::unique_ptr<Build> &&build): build(std::move(build)) {}
virtual ~Base() {}
std::unique_ptr<Build> build;
virtual std::pair<std::string, std::string> get_run_arguments() {return {"", ""};}
virtual void compile() {}
virtual void compile_and_run() {}
virtual std::pair<std::string, std::string> get_run_arguments();
virtual void compile();
virtual void compile_and_run();
virtual std::pair<std::string, std::string> debug_get_run_arguments() {return {"", ""};}
virtual std::pair<std::string, std::string> debug_get_run_arguments();
Tooltips debug_variable_tooltips;
virtual void debug_start() {}
virtual void debug_start();
virtual void debug_continue() {}
virtual void debug_stop() {}
virtual void debug_kill() {}
@ -55,11 +55,11 @@ namespace Project {
virtual void debug_delete() {}
};
class Clang : public Language {
class Clang : public Base {
private:
Dispatcher dispatcher;
public:
Clang(std::unique_ptr<Build> &&build) : Language(std::move(build)) {}
Clang(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
~Clang() { dispatcher.disconnect(); }
std::pair<std::string, std::string> get_run_arguments() override;
@ -87,38 +87,38 @@ namespace Project {
#endif
};
class Markdown : public Language {
class Markdown : public Base {
public:
Markdown(std::unique_ptr<Build> &&build) : Language(std::move(build)) {}
Markdown(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
~Markdown();
boost::filesystem::path last_temp_path;
void compile_and_run() override;
};
class Python : public Language {
class Python : public Base {
public:
Python(std::unique_ptr<Build> &&build) : Language(std::move(build)) {}
Python(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run() override;
};
class JavaScript : public Language {
class JavaScript : public Base {
public:
JavaScript(std::unique_ptr<Build> &&build) : Language(std::move(build)) {}
JavaScript(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run() override;
};
class HTML : public Language {
class HTML : public Base {
public:
HTML(std::unique_ptr<Build> &&build) : Language(std::move(build)) {}
HTML(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run() override;
};
std::unique_ptr<Language> get_language();
extern std::unique_ptr<Language> current_language;
std::unique_ptr<Base> create();
extern std::unique_ptr<Base> current;
};
#endif // JUCI_PROJECT_H_

20
src/project_build.cc

@ -1,15 +1,15 @@
#include "project_build.h"
#include "config.h"
std::unique_ptr<Project::Build> Project::get_build(const boost::filesystem::path &path) {
std::unique_ptr<Project::Build> cmake(new CMake(path));
std::unique_ptr<Project::Build> Project::Build::create(const boost::filesystem::path &path) {
std::unique_ptr<Project::Build> cmake(new CMakeBuild(path));
if(!cmake->project_path.empty())
return cmake;
else
return std::unique_ptr<Project::Build>(new Project::Build());
}
boost::filesystem::path Project::Build::get_default_build_path() {
boost::filesystem::path Project::Build::get_default_path() {
if(project_path.empty())
return boost::filesystem::path();
@ -32,7 +32,7 @@ boost::filesystem::path Project::Build::get_default_build_path() {
return default_build_path;
}
boost::filesystem::path Project::Build::get_debug_build_path() {
boost::filesystem::path Project::Build::get_debug_path() {
if(project_path.empty())
return boost::filesystem::path();
@ -66,18 +66,18 @@ boost::filesystem::path Project::Build::get_debug_build_path() {
return debug_build_path;
}
Project::CMake::CMake(const boost::filesystem::path &path) : Project::Build(), cmake(path) {
Project::CMakeBuild::CMakeBuild(const boost::filesystem::path &path) : Project::Build(), cmake(path) {
project_path=cmake.project_path;
}
bool Project::CMake::update_default_build(bool force) {
return cmake.update_default_build(get_default_build_path(), force);
bool Project::CMakeBuild::update_default(bool force) {
return cmake.update_default_build(get_default_path(), force);
}
bool Project::CMake::update_debug_build(bool force) {
return cmake.update_debug_build(get_debug_build_path(), force);
bool Project::CMakeBuild::update_debug(bool force) {
return cmake.update_debug_build(get_debug_path(), force);
}
boost::filesystem::path Project::CMake::get_executable(const boost::filesystem::path &path) {
boost::filesystem::path Project::CMakeBuild::get_executable(const boost::filesystem::path &path) {
return cmake.get_executable(path);
}

20
src/project_build.h

@ -12,26 +12,26 @@ namespace Project {
boost::filesystem::path project_path;
boost::filesystem::path get_default_build_path();
virtual bool update_default_build(bool force=false) {return false;}
boost::filesystem::path get_debug_build_path();
virtual bool update_debug_build(bool force=false) {return false;}
boost::filesystem::path get_default_path();
virtual bool update_default(bool force=false) {return false;}
boost::filesystem::path get_debug_path();
virtual bool update_debug(bool force=false) {return false;}
virtual boost::filesystem::path get_executable(const boost::filesystem::path &path) {return boost::filesystem::path();}
static std::unique_ptr<Build> create(const boost::filesystem::path &path);
};
class CMake : public Build {
class CMakeBuild : public Build {
::CMake cmake;
public:
CMake(const boost::filesystem::path &path);
CMakeBuild(const boost::filesystem::path &path);
bool update_default_build(bool force=false) override;
bool update_debug_build(bool force=false) override;
bool update_default(bool force=false) override;
bool update_debug(bool force=false) override;
boost::filesystem::path get_executable(const boost::filesystem::path &path) override;
};
std::unique_ptr<Build> get_build(const boost::filesystem::path &path);
}
#endif // JUCI_PROJECT_BUILD_H_

23
src/selectiondialog.cc

@ -49,6 +49,12 @@ list_view_text(use_markup), start_mark(start_mark), show_search_entry(show_searc
window=std::unique_ptr<Gtk::Window>(new Gtk::Window(Gtk::WindowType::WINDOW_POPUP));
else
window=std::unique_ptr<Gtk::Dialog>(new Gtk::Dialog());
auto g_application=g_application_get_default();
auto gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
window->set_transient_for(*application->get_active_window());
list_view_text.set_search_entry(search_entry);
window->set_default_size(0, 0);
@ -75,10 +81,9 @@ list_view_text(use_markup), start_mark(start_mark), show_search_entry(show_searc
if(!show_search_entry)
window->add(scrolled_window);
else {
auto dialog=(Gtk::Dialog*)window.get();
auto dialog=static_cast<Gtk::Dialog*>(window.get());
dialog->get_vbox()->pack_start(search_entry, false, false);
dialog->get_vbox()->pack_start(scrolled_window, true, true);
dialog->set_transient_for((Gtk::Window&)(*text_view.get_toplevel()));
}
}
@ -126,10 +131,12 @@ void SelectionDialogBase::hide() {
}
void SelectionDialogBase::move() {
Gdk::Rectangle rectangle;
text_view.get_iter_location(start_mark->get_iter(), rectangle);
int buffer_x=rectangle.get_x();
int buffer_y=rectangle.get_y()+rectangle.get_height();
Gdk::Rectangle iter_rect;
text_view.get_iter_location(start_mark->get_iter(), iter_rect);
Gdk::Rectangle visible_rect;
text_view.get_visible_rect(visible_rect);
int buffer_x=std::max(iter_rect.get_x(), visible_rect.get_x());
int buffer_y=iter_rect.get_y()+iter_rect.get_height();
int window_x, window_y;
text_view.buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, buffer_x, buffer_y, window_x, window_y);
int root_x, root_y;
@ -153,7 +160,7 @@ void SelectionDialogBase::resize() {
else
scrolled_window.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC);
int window_height=std::min(row_height*(int)list_view_text.get_model()->children().size(), row_height*10);
int window_height=std::min(row_height*static_cast<int>(list_view_text.get_model()->children().size()), row_height*10);
if(show_search_entry)
window_height+=search_entry.get_height();
window->resize(row_width+1, window_height);
@ -201,7 +208,7 @@ SelectionDialog::SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr<Gtk::Tex
search_entry.signal_event().connect([this](GdkEvent* event) {
if(event->type==GDK_KEY_PRESS) {
auto key=(GdkEventKey*)event;
auto key=reinterpret_cast<GdkEventKey*>(event);
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) {

347
src/source.cc

@ -1,11 +1,16 @@
#include "source.h"
#include "config.h"
#include <boost/property_tree/json_parser.hpp>
#include <algorithm>
#include <gtksourceview/gtksource.h>
#include <iostream>
#include "filesystem.h"
#include "terminal.h"
#include "info.h"
#include <gtksourceview/gtksource.h>
#include <boost/property_tree/json_parser.hpp>
#include <boost/spirit/home/qi/char.hpp>
#include <boost/spirit/home/qi/operator.hpp>
#include <boost/spirit/home/qi/string.hpp>
#include <iostream>
#include <numeric>
#include <set>
namespace sigc {
#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
@ -79,10 +84,15 @@ std::string Source::FixIt::string(Glib::RefPtr<Gtk::TextBuffer> buffer) {
//////////////
//// View ////
//////////////
const REGEX_NS::regex Source::View::bracket_regex("^([ \\t]*).*\\{ *$");
const REGEX_NS::regex Source::View::no_bracket_statement_regex("^([ \\t]*)(if|for|else if|while) *\\(.*[^;}] *$");
const REGEX_NS::regex Source::View::no_bracket_no_para_statement_regex("^([ \\t]*)(else) *$");
AspellConfig* Source::View::spellcheck_config=NULL;
Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language): file_path(file_path), language(language) {
get_source_buffer()->begin_not_undoable_action();
last_read_time=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
if(language) {
if(filesystem::read_non_utf8(file_path, get_buffer())==-1)
Terminal::get().print("Warning: "+file_path.string()+" is not a valid UTF-8 file. Saving might corrupt the file.\n");
@ -252,10 +262,10 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::L
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()->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();
get_buffer()->end_user_action();
delayed_tooltips_connection.disconnect();
};
spellcheck_suggestions_dialog->show();
@ -271,10 +281,6 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::L
set_tooltip_and_dialog_events();
bracket_regex=boost::regex("^([ \\t]*).*\\{ *$");
no_bracket_statement_regex=boost::regex("^([ \\t]*)(if|for|else if|while) *\\(.*[^;}] *$");
no_bracket_no_para_statement_regex=boost::regex("^([ \\t]*)(else) *$");
if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" ||
language->get_id()=="cpp" || language->get_id()=="objc" || language->get_id()=="java" ||
language->get_id()=="js" || language->get_id()=="ts" || language->get_id()=="proto" ||
@ -321,25 +327,16 @@ Source::View::View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::L
auto exit_status=Terminal::get().process(stdin_stream, stdout_stream, command, this->file_path.parent_path());
if(exit_status==0) {
get_source_buffer()->begin_user_action();
get_buffer()->begin_user_action();
auto iter=get_buffer()->get_insert()->get_iter();
auto cursor_line_nr=iter.get_line();
auto cursor_line_offset=iter.get_line_offset();
get_buffer()->set_text(stdout_stream.str());
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
cursor_line_nr=std::min(cursor_line_nr, get_buffer()->get_line_count()-1);
if(cursor_line_nr>=0) {
iter=get_buffer()->get_iter_at_line(cursor_line_nr);
for(int c=0;c<cursor_line_offset;c++) {
if(iter.ends_line())
break;
iter.forward_char();
}
get_buffer()->place_cursor(iter);
scroll_to_cursor_delayed(this, true, false);
}
place_cursor_at_line_offset(cursor_line_nr, cursor_line_offset);
scroll_to_cursor_delayed(this, true, false);
}
};
}
@ -374,6 +371,70 @@ void Source::View::set_tab_char_and_size(char tab_char, unsigned tab_size) {
tab+=tab_char;
}
Gsv::DrawSpacesFlags Source::View::parse_show_whitespace_characters(const std::string &text) {
namespace qi = boost::spirit::qi;
qi::symbols<char, Gsv::DrawSpacesFlags> options;
options.add
("space", Gsv::DRAW_SPACES_SPACE)
("tab", Gsv::DRAW_SPACES_TAB)
("newline", Gsv::DRAW_SPACES_NEWLINE)
("nbsp", Gsv::DRAW_SPACES_NBSP)
("leading", Gsv::DRAW_SPACES_LEADING)
("text", Gsv::DRAW_SPACES_TEXT)
("trailing", Gsv::DRAW_SPACES_TRAILING)
("all", Gsv::DRAW_SPACES_ALL);
std::set<Gsv::DrawSpacesFlags> out;
// parse comma-separated list of options
qi::phrase_parse(text.begin(), text.end(), options % ',', qi::space, out);
return out.count(Gsv::DRAW_SPACES_ALL)>0 ?
Gsv::DRAW_SPACES_ALL :
static_cast<Gsv::DrawSpacesFlags>(std::accumulate(out.begin(), out.end(), 0));
}
bool Source::View::save(const std::vector<Source::View*> &views) {
if(file_path.empty() || !get_buffer()->get_modified())
return false;
//Remove trailing whitespace characters on save, and add trailing newline if missing
if(Config::get().source.cleanup_whitespace_characters) {
auto buffer=get_buffer();
buffer->begin_user_action();
for(int line=0;line<buffer->get_line_count();line++) {
auto iter=buffer->get_iter_at_line(line);
auto end_iter=iter;
while(!end_iter.ends_line())
end_iter.forward_char();
if(iter==end_iter)
continue;
iter=end_iter;
while(!iter.starts_line() && (*iter==' ' || *iter=='\t' || iter.ends_line()))
iter.backward_char();
if(*iter!=' ' && *iter!='\t')
iter.forward_char();
if(iter==end_iter)
continue;
buffer->erase(iter, end_iter);
}
auto iter=buffer->end();
if(!iter.starts_line())
buffer->insert(buffer->end(), "\n");
buffer->end_user_action();
}
if(filesystem::write(file_path, get_buffer())) {
last_read_time=std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
get_buffer()->set_modified(false);
return true;
}
else {
Terminal::get().print("Error: could not save file "+file_path.string()+"\n", true);
return false;
}
}
void Source::View::configure() {
//TODO: Move this to notebook? Might take up too much memory doing this for every tab.
auto style_scheme_manager=Gsv::StyleSchemeManager::get_default();
@ -388,6 +449,8 @@ void Source::View::configure() {
Terminal::get().print("Error: Could not find gtksourceview style: "+Config::get().source.style+'\n', true);
}
set_draw_spaces(parse_show_whitespace_characters(Config::get().source.show_whitespace_characters));
if(Config::get().source.wrap_lines)
set_wrap_mode(Gtk::WrapMode::WRAP_CHAR);
else
@ -561,7 +624,7 @@ void Source::View::set_tooltip_and_dialog_events() {
}
void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data) {
auto view=(Source::View*)data;
auto view=static_cast<Source::View*>(data);
if(view->update_search_occurrences)
view->update_search_occurrences(gtk_source_search_context_get_occurrences_count(view->search_context));
}
@ -700,13 +763,13 @@ void Source::View::paste() {
paste_line=false;
}
}
if(paste_line_tabs==(size_t)-1)
if(paste_line_tabs==static_cast<size_t>(-1))
paste_line_tabs=0;
start_line=0;
end_line=0;
paste_line=false;
first_paste_line=true;
get_source_buffer()->begin_user_action();
get_buffer()->begin_user_action();
for(size_t c=0;c<text.size();c++) {
if(text[c]=='\n') {
end_line=c;
@ -744,7 +807,7 @@ void Source::View::paste() {
}
}
get_buffer()->place_cursor(get_buffer()->get_insert()->get_iter());
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
scroll_to_cursor_delayed(this, false, false);
}
else {
@ -756,19 +819,42 @@ void Source::View::paste() {
Gtk::TextIter Source::View::get_iter_for_dialog() {
auto iter=get_buffer()->get_insert()->get_iter();
if(iter.get_line_offset()>=80)
iter=get_buffer()->get_iter_at_line(iter.get_line());
Gdk::Rectangle visible_rect;
get_visible_rect(visible_rect);
Gdk::Rectangle iter_rect;
get_iter_location(iter, iter_rect);
iter_rect.set_width(1);
if(!visible_rect.intersects(iter_rect)) {
get_iter_at_location(iter, 0, visible_rect.get_y()+visible_rect.get_height()/3);
if(iter.get_line_offset()>=80) {
get_iter_at_location(iter, visible_rect.get_x(), iter_rect.get_y());
get_iter_location(iter, iter_rect);
}
if(!visible_rect.intersects(iter_rect))
get_iter_at_location(iter, visible_rect.get_x(), visible_rect.get_y()+visible_rect.get_height()/3);
return iter;
}
void Source::View::place_cursor_at_line_offset(int line, int offset) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_buffer()->get_iter_at_line(line);
while(!iter.ends_line())
iter.forward_char();
offset=std::min(offset, iter.get_line_offset());
get_buffer()->place_cursor(get_buffer()->get_iter_at_line_offset(line, offset));
}
void Source::View::place_cursor_at_line_index(int line, int index) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_buffer()->get_iter_at_line(line);
while(!iter.ends_line())
iter.forward_char();
index=std::min(index, iter.get_line_index());
get_buffer()->place_cursor(get_buffer()->get_iter_at_line_index(line, index));
}
void Source::View::set_status(const std::string &status) {
this->status=status;
if(on_update_status)
@ -870,7 +956,7 @@ std::string Source::View::get_line(const Gtk::TextIter &iter) {
auto line_start_it = get_buffer()->get_iter_at_line(iter.get_line());
auto line_end_it = iter;
while(!line_end_it.ends_line() && line_end_it.forward_char()) {}
std::string line(get_source_buffer()->get_text(line_start_it, line_end_it));
std::string line(get_buffer()->get_text(line_start_it, line_end_it));
return line;
}
std::string Source::View::get_line(Glib::RefPtr<Gtk::TextBuffer::Mark> mark) {
@ -884,8 +970,8 @@ std::string Source::View::get_line() {
}
std::string Source::View::get_line_before(const Gtk::TextIter &iter) {
auto line_it = get_source_buffer()->get_iter_at_line(iter.get_line());
std::string line(get_source_buffer()->get_text(line_it, iter));
auto line_it = get_buffer()->get_iter_at_line(iter.get_line());
std::string line(get_buffer()->get_text(line_it, iter));
return line;
}
std::string Source::View::get_line_before(Glib::RefPtr<Gtk::TextBuffer::Mark> mark) {
@ -902,7 +988,7 @@ Gtk::TextIter Source::View::get_tabs_end_iter(Glib::RefPtr<Gtk::TextBuffer::Mark
return get_tabs_end_iter(mark->get_iter());
}
Gtk::TextIter Source::View::get_tabs_end_iter(int line_nr) {
auto sentence_iter = get_source_buffer()->get_iter_at_line(line_nr);
auto sentence_iter = get_buffer()->get_iter_at_line(line_nr);
while((*sentence_iter==' ' || *sentence_iter=='\t') && !sentence_iter.ends_line() && sentence_iter.forward_char()) {}
return sentence_iter;
}
@ -1045,6 +1131,23 @@ bool Source::View::find_left_bracket_backward(Gtk::TextIter iter, Gtk::TextIter
return false;
}
std::string Source::View::get_token(Gtk::TextIter iter) {
auto start=iter;
auto end=iter;
while((*iter>='A' && *iter<='Z') || (*iter>='a' && *iter<='z') || (*iter>='0' && *iter<='9') || *iter=='_') {
start=iter;
if(!iter.backward_char())
break;
}
while((*end>='A' && *end<='Z') || (*end>='a' && *end<='z') || (*end>='0' && *end<='9') || *end=='_') {
if(!end.forward_char())
break;
}
return get_buffer()->get_text(start, end);
}
bool Source::View::on_key_press_event(GdkEventKey* key) {
if(spellcheck_suggestions_dialog && spellcheck_suggestions_dialog->shown) {
if(spellcheck_suggestions_dialog->on_key_press(key))
@ -1075,7 +1178,7 @@ bool Source::View::on_key_press_event(GdkEventKey* key) {
//Basic indentation
bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
get_source_buffer()->begin_user_action();
get_buffer()->begin_user_action();
auto iter=get_buffer()->get_insert()->get_iter();
//Indent as in next or previous line
if(key->keyval==GDK_KEY_Return && !get_buffer()->get_has_selection() && !iter.starts_line()) {
@ -1103,15 +1206,15 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
auto next_line_tabs_end_iter=get_tabs_end_iter(line_nr+1);
auto next_line_tabs=get_line_before(next_line_tabs_end_iter);
if(iter.ends_line() && next_line_tabs.size()>line_tabs.size()) {
get_source_buffer()->insert_at_cursor("\n"+next_line_tabs);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
get_buffer()->insert_at_cursor("\n"+next_line_tabs);
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
}
get_source_buffer()->insert_at_cursor("\n"+line_tabs);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
get_buffer()->insert_at_cursor("\n"+line_tabs);
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
//Indent right when clicking tab, no matter where in the line the cursor is. Also works on selected text.
@ -1135,34 +1238,34 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
tabs=next_line_tabs;
if(tabs.size()>=tab_size) {
get_buffer()->insert_at_cursor(tabs);
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return true;
}
}
Gtk::TextIter selection_start, selection_end;
get_source_buffer()->get_selection_bounds(selection_start, selection_end);
get_buffer()->get_selection_bounds(selection_start, selection_end);
int line_start=selection_start.get_line();
int line_end=selection_end.get_line();
for(int line=line_start;line<=line_end;line++) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line);
Gtk::TextIter line_it = get_buffer()->get_iter_at_line(line);
if(!get_buffer()->get_has_selection() || line_it!=selection_end)
get_source_buffer()->insert(line_it, tab);
get_buffer()->insert(line_it, tab);
}
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return true;
}
//Indent left when clicking shift-tab, no matter where in the line the cursor is. Also works on selected text.
else if((key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) && (key->state&GDK_SHIFT_MASK)>0) {
Gtk::TextIter selection_start, selection_end;
get_source_buffer()->get_selection_bounds(selection_start, selection_end);
get_buffer()->get_selection_bounds(selection_start, selection_end);
int line_start=selection_start.get_line();
int line_end=selection_end.get_line();
unsigned indent_left_steps=tab_size;
std::vector<bool> ignore_line;
for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
auto line_it = get_source_buffer()->get_iter_at_line(line_nr);
auto line_it = get_buffer()->get_iter_at_line(line_nr);
if(!get_buffer()->get_has_selection() || line_it!=selection_end) {
auto tabs_end_iter=get_tabs_end_iter(line_nr);
if(tabs_end_iter.starts_line() && tabs_end_iter.ends_line())
@ -1175,7 +1278,7 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
ignore_line.push_back(false);
}
else {
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return true;
}
}
@ -1183,15 +1286,15 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
}
for(int line_nr=line_start;line_nr<=line_end;line_nr++) {
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(line_nr);
Gtk::TextIter line_it = get_buffer()->get_iter_at_line(line_nr);
Gtk::TextIter line_plus_it=line_it;
if(!get_buffer()->get_has_selection() || line_it!=selection_end) {
line_plus_it.forward_chars(indent_left_steps);
if(!ignore_line.at(line_nr-line_start))
get_source_buffer()->erase(line_it, line_plus_it);
get_buffer()->erase(line_it, line_plus_it);
}
}
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return true;
}
//"Smart" backspace key
@ -1256,7 +1359,7 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
get_buffer()->place_cursor(end_line_iter);
}
scroll_to(get_buffer()->get_insert());
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return true;
}
else if(key->keyval==GDK_KEY_Home && (key->state&GDK_CONTROL_MASK)==0) {
@ -1279,7 +1382,7 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
get_buffer()->place_cursor(start_line_iter);
}
scroll_to(get_buffer()->get_insert());
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return true;
}
@ -1296,7 +1399,8 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
get_buffer()->erase(selection_start, selection_end);
}
get_buffer()->insert_at_cursor(Glib::ustring(1, unicode));
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
scroll_to(get_buffer()->get_insert());
//Trick to make the cursor visible right after insertion:
set_cursor_visible(false);
@ -1306,13 +1410,13 @@ bool Source::View::on_key_press_event_basic(GdkEventKey* key) {
}
auto stop=Gsv::View::on_key_press_event(key);
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return stop;
}
//Bracket language indentation
bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
get_source_buffer()->begin_user_action();
get_buffer()->begin_user_action();
auto iter=get_buffer()->get_insert()->get_iter();
//Indent depending on if/else/etc and brackets
if(key->keyval==GDK_KEY_Return && !iter.starts_line()) {
@ -1337,7 +1441,7 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
auto start_sentence_tabs_end_iter=get_tabs_end_iter(start_of_sentence_iter);
auto tabs=get_line_before(start_sentence_tabs_end_iter);
boost::smatch sm;
REGEX_NS::smatch sm;
if(iter.backward_char() && *iter=='{') {
auto found_iter=iter;
bool found_right_bracket=find_right_bracket_forward(iter, found_iter);
@ -1350,30 +1454,43 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
has_bracket=true;
}
if(*get_buffer()->get_insert()->get_iter()=='}') {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs);
auto insert_it = get_source_buffer()->get_insert()->get_iter();
get_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs);
auto insert_it = get_buffer()->get_insert()->get_iter();
if(insert_it.backward_chars(tabs.size()+1)) {
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
scroll_to(get_buffer()->get_insert());
get_buffer()->place_cursor(insert_it);
}
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return true;
}
else if(!has_bracket) {
//Insert new lines with bracket end
get_source_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs+"}");
auto insert_it = get_source_buffer()->get_insert()->get_iter();
if(insert_it.backward_chars(tabs.size()+2)) {
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->place_cursor(insert_it);
bool add_semicolon=false;
if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" ||
language->get_id()=="c" || language->get_id()=="cpp")) {
auto token=get_token(start_of_sentence_iter);
if(token.empty()) {
auto iter=start_of_sentence_iter;
while(!iter.starts_line() && iter.backward_char()) {}
if(iter.backward_char() && find_start_of_closed_expression(iter, iter))
token=get_token(iter);
}
if(token=="class" || token=="struct")
add_semicolon=true;
}
get_source_buffer()->end_user_action();
get_buffer()->insert_at_cursor("\n"+tabs+tab+"\n"+tabs+(add_semicolon?"};":"}"));
auto insert_it = get_buffer()->get_insert()->get_iter();
if(insert_it.backward_chars(tabs.size()+(add_semicolon?3:2))) {
scroll_to(get_buffer()->get_insert());
get_buffer()->place_cursor(insert_it);
}
get_buffer()->end_user_action();
return true;
}
else {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
get_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_buffer()->get_insert());
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return true;
}
}
@ -1389,35 +1506,35 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
iter.forward_char();
}
}
else if(boost::regex_match(line, sm, no_bracket_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
else if(REGEX_NS::regex_match(line, sm, no_bracket_statement_regex)) {
get_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
else if(boost::regex_match(line, sm, no_bracket_no_para_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
else if(REGEX_NS::regex_match(line, sm, no_bracket_no_para_statement_regex)) {
get_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
//Indenting after for instance if(...)\n...;\n
else if(iter.backward_char() && *iter==';') {
boost::smatch sm2;
size_t line_nr=get_source_buffer()->get_insert()->get_iter().get_line();
REGEX_NS::smatch sm2;
size_t line_nr=get_buffer()->get_insert()->get_iter().get_line();
if(line_nr>0 && tabs.size()>=tab_size) {
std::string previous_line=get_line(line_nr-1);
if(!boost::regex_match(previous_line, sm2, bracket_regex)) {
if(boost::regex_match(previous_line, sm2, no_bracket_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
if(!REGEX_NS::regex_match(previous_line, sm2, bracket_regex)) {
if(REGEX_NS::regex_match(previous_line, sm2, no_bracket_statement_regex)) {
get_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
else if(boost::regex_match(previous_line, sm2, no_bracket_no_para_statement_regex)) {
get_source_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
else if(REGEX_NS::regex_match(previous_line, sm2, no_bracket_no_para_statement_regex)) {
get_buffer()->insert_at_cursor("\n"+sm2[1].str());
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
}
@ -1431,7 +1548,7 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
left_bracket_iter.forward_char();
Gtk::TextIter start_of_left_bracket_sentence_iter;
if(find_start_of_closed_expression(left_bracket_iter, start_of_left_bracket_sentence_iter)) {
boost::smatch sm;
REGEX_NS::smatch sm;
auto tabs_end_iter=get_tabs_end_iter(start_of_left_bracket_sentence_iter);
auto tabs_start_of_sentence=get_line_before(tabs_end_iter);
if(tabs.size()==(tabs_start_of_sentence.size()+tab_size)) {
@ -1442,17 +1559,17 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
get_buffer()->erase(start_line_iter, start_line_plus_tab_size);
}
else {
get_source_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
get_buffer()->insert_at_cursor("\n"+tabs+tab);
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
}
}
}
get_source_buffer()->insert_at_cursor("\n"+tabs);
scroll_to(get_source_buffer()->get_insert());
get_source_buffer()->end_user_action();
get_buffer()->insert_at_cursor("\n"+tabs);
scroll_to(get_buffer()->get_insert());
get_buffer()->end_user_action();
return true;
}
}
@ -1462,19 +1579,19 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
if(line.size()>=tab_size) {
for(auto c: line) {
if(c!=tab_char) {
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
get_buffer()->insert_at_cursor("}");
get_buffer()->end_user_action();
return true;
}
}
Gtk::TextIter insert_it = get_source_buffer()->get_insert()->get_iter();
Gtk::TextIter line_it = get_source_buffer()->get_iter_at_line(insert_it.get_line());
Gtk::TextIter insert_it = get_buffer()->get_insert()->get_iter();
Gtk::TextIter line_it = get_buffer()->get_iter_at_line(insert_it.get_line());
Gtk::TextIter line_plus_it=line_it;
line_plus_it.forward_chars(tab_size);
get_source_buffer()->erase(line_it, line_plus_it);
get_buffer()->erase(line_it, line_plus_it);
}
get_source_buffer()->insert_at_cursor("}");
get_source_buffer()->end_user_action();
get_buffer()->insert_at_cursor("}");
get_buffer()->end_user_action();
return true;
}
//Indent left when writing { on a new line after for instance if(...)\n...
@ -1485,12 +1602,12 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
size_t line_nr=iter.get_line();
if(line_nr>0 && tabs.size()>=tab_size && iter==tabs_end_iter) {
std::string previous_line=get_line(line_nr-1);
boost::smatch sm;
if(!boost::regex_match(previous_line, sm, bracket_regex)) {
REGEX_NS::smatch sm;
if(!REGEX_NS::regex_match(previous_line, sm, bracket_regex)) {
auto start_iter=iter;
start_iter.backward_chars(tab_size);
if(boost::regex_match(previous_line, sm, no_bracket_statement_regex) ||
boost::regex_match(previous_line, sm, no_bracket_no_para_statement_regex)) {
if(REGEX_NS::regex_match(previous_line, sm, no_bracket_statement_regex) ||
REGEX_NS::regex_match(previous_line, sm, no_bracket_no_para_statement_regex)) {
if((tabs.size()-tab_size)==sm[1].str().size()) {
get_buffer()->erase(start_iter, iter);
get_buffer()->insert_at_cursor("{");
@ -1503,7 +1620,7 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) {
}
}
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
return on_key_press_event_basic(key);
}
@ -1528,6 +1645,14 @@ bool Source::View::on_button_press_event(GdkEventButton *event) {
return Gsv::View::on_button_press_event(event);
}
bool Source::View::on_focus_in_event(GdkEventFocus* focus_event){
boost::system::error_code ec;
auto last_write_time=boost::filesystem::last_write_time(file_path, ec);
if(!ec && last_write_time>last_read_time)
Info::get().print("Caution: " + file_path.filename().string() + " was altered outside of juCi++");
return Gsv::View::on_focus_in_event(focus_event);
};
std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
std::unordered_map<char, size_t> tab_chars;
std::unordered_map<unsigned, size_t> tab_sizes;
@ -1682,7 +1807,7 @@ std::pair<char, unsigned> Source::View::find_tab_char_and_size() {
}
bool Source::View::is_word_iter(const Gtk::TextIter& iter) {
return ((*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==39 || *iter>=128);
return ((*iter>='A' && *iter<='Z') || (*iter>='a' && *iter<='z') || *iter=='\'' || *iter>=128);
}
std::pair<Gtk::TextIter, Gtk::TextIter> Source::View::spellcheck_get_word(Gtk::TextIter iter) {

52
src/source.h

@ -7,7 +7,18 @@
#include <string>
#include <unordered_map>
#include <vector>
//Temporary fix for current Arch Linux boost linking problem
#ifdef __GNUC_PREREQ
#if __GNUC_PREREQ(5,1)
#include <regex>
#define REGEX_NS std
#endif
#endif
#ifndef REGEX_NS
#include <boost/regex.hpp>
#define REGEX_NS boost
#endif
#include "selectiondialog.h"
#include "tooltips.h"
@ -15,27 +26,11 @@
namespace Source {
Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path);
class Token {
public:
Token(): type(-1) {}
Token(Glib::RefPtr<Gsv::Language> language, int type, const std::string &spelling, const std::string &usr):
language(language), type(type), spelling(spelling), usr(usr) {}
operator bool() const {return (type>=0 && spelling.size()>0 && usr.size()>0);}
bool operator==(const Token &o) const {return (type==o.type &&
spelling==o.spelling &&
usr==o.usr);}
bool operator!=(const Token &o) const {return !(*this==o);}
Glib::RefPtr<Gsv::Language> language;
int type;
std::string spelling;
std::string usr;
};
class Offset {
public:
Offset() {}
Offset(unsigned line, unsigned index, const boost::filesystem::path &file_path=""): line(line), index(index), file_path(file_path) {}
operator bool() { return !file_path.empty(); }
bool operator==(const Offset &o) {return (line==o.line && index==o.index);}
unsigned line;
@ -61,6 +56,7 @@ namespace Source {
View(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language);
~View();
virtual bool save(const std::vector<Source::View*> &views);
virtual void configure();
void search_highlight(const std::string &text, bool case_sensitive, bool regex);
@ -78,11 +74,12 @@ namespace Source {
std::function<void()> auto_indent;
std::function<Offset()> get_declaration_location;
std::function<std::vector<std::pair<Offset, std::string> >(const Token &token)> get_usages;
std::function<Offset(const std::vector<Source::View*> &views)> get_implementation_location;
std::function<std::vector<std::pair<Offset, std::string> >(const std::vector<Source::View*> &views)> get_usages;
std::function<void()> goto_method;
std::function<Token()> get_token;
std::function<std::vector<std::string>()> get_token_data;
std::function<size_t(const Token &token, const std::string &text)> rename_similar_tokens;
std::function<std::string()> get_token_spelling;
std::function<std::vector<std::pair<boost::filesystem::path, size_t> >(const std::vector<Source::View*> &views, const std::string &text)> rename_similar_tokens;
std::function<void()> goto_next_diagnostic;
std::function<void()> apply_fix_its;
@ -92,6 +89,9 @@ namespace Source {
sigc::connection delayed_tooltips_connection;
std::function<void(View* view, bool center, bool show_tooltips)> scroll_to_cursor_delayed=[](View* view, bool center, bool show_tooltips) {};
void place_cursor_at_line_offset(int line, int offset);
void place_cursor_at_line_index(int line, int index);
std::function<void(View* view, const std::string &status_text)> on_update_status;
std::function<void(View* view, const std::string &info_text)> on_update_info;
void set_status(const std::string &status);
@ -111,6 +111,7 @@ namespace Source {
virtual void soft_reparse() {soft_reparse_needed=false;}
virtual bool full_reparse() {full_reparse_needed=false; return true;}
protected:
std::time_t last_read_time;
bool parsed=false;
Tooltips diagnostic_tooltips;
Tooltips type_tooltips;
@ -137,15 +138,18 @@ namespace Source {
bool find_right_bracket_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter);
bool find_left_bracket_backward(Gtk::TextIter iter, Gtk::TextIter &found_iter);
boost::regex bracket_regex;
boost::regex no_bracket_statement_regex;
boost::regex no_bracket_no_para_statement_regex;
std::string get_token(Gtk::TextIter iter);
const static REGEX_NS::regex bracket_regex;
const static REGEX_NS::regex no_bracket_statement_regex;
const static REGEX_NS::regex no_bracket_no_para_statement_regex;
bool on_key_press_event(GdkEventKey* key) override;
bool on_key_press_event_basic(GdkEventKey* key);
bool on_key_press_event_bracket_language(GdkEventKey* key);
bool is_bracket_language=false;
bool on_button_press_event(GdkEventButton *event) override;
bool on_focus_in_event(GdkEventFocus* focus_event) override;
std::pair<char, unsigned> find_tab_char_and_size();
unsigned tab_size;
@ -157,6 +161,8 @@ namespace Source {
guint previous_non_modifier_keyval=0;
guint last_keyval=0;
private:
Gsv::DrawSpacesFlags parse_show_whitespace_characters(const std::string &text);
GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings;
static void search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data);

784
src/source_clang.cc

@ -5,6 +5,8 @@
#ifdef JUCI_ENABLE_DEBUG
#include "debug_clang.h"
#endif
#include "info.h"
#include "dialogs.h"
namespace sigc {
#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
@ -42,6 +44,21 @@ Source::View(file_path, language) {
});
}
bool Source::ClangViewParse::save(const std::vector<Source::View*> &views) {
if(!Source::View::save(views))
return false;
if(language->get_id()=="chdr" || language->get_id()=="cpphdr") {
for(auto &view: views) {
if(auto clang_view=dynamic_cast<Source::ClangView*>(view)) {
if(this!=clang_view)
clang_view->soft_reparse_needed=true;
}
}
}
return true;
}
void Source::ClangViewParse::configure() {
Source::View::configure();
@ -173,9 +190,11 @@ void Source::ClangViewParse::soft_reparse() {
}
std::vector<std::string> Source::ClangViewParse::get_compilation_commands() {
auto build=Project::get_build(file_path);
auto default_build_path=build->get_default_build_path();
build->update_default_build();
auto build=Project::Build::create(file_path);
if(build->project_path.empty())
Info::get().print(file_path.filename().string()+": could not find a supported build system");
auto default_build_path=build->get_default_path();
build->update_default();
clang::CompilationDatabase db(default_build_path.string());
clang::CompileCommands commands(file_path.string(), db);
std::vector<clang::CompileCommand> cmds = commands.get_commands();
@ -187,9 +206,9 @@ std::vector<std::string> Source::ClangViewParse::get_compilation_commands() {
}
}
auto clang_version_string=clang::to_string(clang_getClangVersion());
const boost::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$");
boost::smatch sm;
if(boost::regex_match(clang_version_string, sm, clang_version_regex)) {
const static REGEX_NS::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$");
REGEX_NS::smatch sm;
if(REGEX_NS::regex_match(clang_version_string, sm, clang_version_regex)) {
auto clang_version=sm[1].str();
arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include");
#ifdef __APPLE__
@ -215,38 +234,37 @@ std::vector<std::string> Source::ClangViewParse::get_compilation_commands() {
}
void Source::ClangViewParse::update_syntax() {
std::vector<TokenRange> ranges;
std::vector<std::pair<std::pair<clang::Offset, clang::Offset>, int> > ranges;
for (auto &token : *clang_tokens) {
//if(token.get_kind()==0) // PunctuationToken
//ranges.emplace_back(token.offsets, (int) token.get_cursor().get_kind());
if(token.get_kind()==1) // KeywordToken
//if(token.get_kind()==clang::TokenKind::Token_Punctuation)
//ranges.emplace_back(token.offsets, static_cast<int>(token.get_cursor().get_kind()));
auto token_kind=token.get_kind();
if(token_kind==clang::TokenKind::Token_Keyword)
ranges.emplace_back(token.offsets, 702);
else if(token.get_kind()==2) {// IdentifierToken
auto kind=(int)token.get_cursor().get_kind();
if(kind==101 || kind==102)
kind=(int)token.get_cursor().get_referenced().get_kind();
if(kind!=500)
ranges.emplace_back(token.offsets, kind);
else if(token_kind==clang::TokenKind::Token_Identifier) {
auto cursor_kind=token.get_cursor().get_kind();
if(cursor_kind==clang::CursorKind::DeclRefExpr || cursor_kind==clang::CursorKind::MemberRefExpr)
cursor_kind=token.get_cursor().get_referenced().get_kind();
if(cursor_kind!=clang::CursorKind::PreprocessingDirective)
ranges.emplace_back(token.offsets, static_cast<int>(cursor_kind));
}
else if(token.get_kind()==3) { // LiteralToken
ranges.emplace_back(token.offsets, 109);
}
else if(token.get_kind()==4) // CommentToken
else if(token_kind==clang::TokenKind::Token_Literal)
ranges.emplace_back(token.offsets, static_cast<int>(clang::CursorKind::StringLiteral));
else if(token_kind==clang::TokenKind::Token_Comment)
ranges.emplace_back(token.offsets, 705);
}
if (ranges.empty() || ranges.size() == 0) {
if (ranges.empty())
return;
}
auto buffer = get_source_buffer();
auto buffer = get_buffer();
for(auto &tag: last_syntax_tags)
buffer->remove_tag_by_name(tag, buffer->begin(), buffer->end());
last_syntax_tags.clear();
for (auto &range : ranges) {
auto type_it=Config::get().source.clang_types.find(std::to_string(range.kind));
auto type_it=Config::get().source.clang_types.find(range.second);
if(type_it!=Config::get().source.clang_types.end()) {
last_syntax_tags.emplace(type_it->second);
Gtk::TextIter begin_iter = buffer->get_iter_at_line_index(range.offsets.first.line-1, range.offsets.first.index-1);
Gtk::TextIter end_iter = buffer->get_iter_at_line_index(range.offsets.second.line-1, range.offsets.second.index-1);
Gtk::TextIter begin_iter = buffer->get_iter_at_line_index(range.first.first.line-1, range.first.first.index-1);
Gtk::TextIter end_iter = buffer->get_iter_at_line_index(range.first.second.line-1, range.first.second.index-1);
buffer->apply_tag_by_name(type_it->second, begin_iter, end_iter);
}
}
@ -404,8 +422,8 @@ void Source::ClangViewParse::show_type_tooltips(const Gdk::Rectangle &rectangle)
type_tooltips.clear();
for(auto &token: *tokens) {
auto cursor=token.get_cursor();
if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) {
if(static_cast<unsigned>(token.get_cursor().get_kind())==103) //These cursors are buggy
if(token.get_kind()==clang::TokenKind::Token_Identifier && cursor.has_type()) {
if(token.get_cursor().get_kind()==clang::CursorKind::CallExpr) //These cursors are buggy
continue;
auto start=get_buffer()->get_iter_at_line_index(token.offsets.first.line-1, token.offsets.first.index-1);
auto end=get_buffer()->get_iter_at_line_index(token.offsets.second.line-1, token.offsets.second.index-1);
@ -536,7 +554,7 @@ void Source::ClangViewAutocomplete::autocomplete_dialog_setup() {
autocomplete_dialog=std::unique_ptr<CompletionDialog>(new CompletionDialog(*this, get_buffer()->create_mark(start_iter)));
autocomplete_dialog_rows.clear();
autocomplete_dialog->on_hide=[this](){
get_source_buffer()->end_user_action();
get_buffer()->end_user_action();
autocomplete_tooltips.hide();
autocomplete_tooltips.clear();
parsed=false;
@ -544,7 +562,9 @@ void Source::ClangViewAutocomplete::autocomplete_dialog_setup() {
};
autocomplete_dialog->on_select=[this](const std::string& selected, bool hide_window) {
auto row = autocomplete_dialog_rows.at(selected).first;
//erase existing variable or function before insert iter
get_buffer()->erase(autocomplete_dialog->start_mark->get_iter(), get_buffer()->get_insert()->get_iter());
//do not insert template argument or function parameters if they already exist
auto iter=get_buffer()->get_insert()->get_iter();
if(*iter=='<' || *iter=='(') {
auto bracket_pos=row.find(*iter);
@ -552,7 +572,13 @@ void Source::ClangViewAutocomplete::autocomplete_dialog_setup() {
row=row.substr(0, bracket_pos);
}
}
//Fixes for the most commonly used stream manipulators
static auto manipulators_map=autocomplete_manipulators_map();
auto it=manipulators_map.find(row);
if(it!=manipulators_map.end())
row=it->second;
get_buffer()->insert(autocomplete_dialog->start_mark->get_iter(), row);
//if selection is finalized, select text inside template arguments or function parameters
if(hide_window) {
auto para_pos=row.find('(');
auto angle_pos=row.find('<');
@ -626,10 +652,10 @@ void Source::ClangViewAutocomplete::autocomplete_check() {
get_source_buffer()->iter_has_context_class(iter, "comment")))
return;
std::string line=" "+get_line_before();
const boost::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$");
const boost::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$");
boost::smatch sm;
if(boost::regex_match(line, sm, in_specified_namespace)) {
const static REGEX_NS::regex in_specified_namespace("^(.*[a-zA-Z0-9_\\)\\]\\>])(->|\\.|::)([a-zA-Z0-9_]*)$");
const static REGEX_NS::regex within_namespace("^(.*)([^a-zA-Z0-9_]+)([a-zA-Z0-9_]{3,})$");
REGEX_NS::smatch sm;
if(REGEX_NS::regex_match(line, sm, in_specified_namespace)) {
{
std::unique_lock<std::mutex> lock(prefix_mutex);
prefix=sm[3].str();
@ -637,7 +663,7 @@ void Source::ClangViewAutocomplete::autocomplete_check() {
if(prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9')
autocomplete();
}
else if(boost::regex_match(line, sm, within_namespace)) {
else if(REGEX_NS::regex_match(line, sm, within_namespace)) {
{
std::unique_lock<std::mutex> lock(prefix_mutex);
prefix=sm[3].str();
@ -719,7 +745,7 @@ void Source::ClangViewAutocomplete::autocomplete() {
set_status("");
autocomplete_state=AutocompleteState::IDLE;
if (!autocomplete_dialog_rows.empty()) {
get_source_buffer()->begin_user_action();
get_buffer()->begin_user_action();
autocomplete_dialog->show();
}
else
@ -774,6 +800,18 @@ std::vector<Source::ClangViewAutocomplete::AutoCompleteData> Source::ClangViewAu
return suggestions;
}
std::unordered_map<std::string, std::string> Source::ClangViewAutocomplete::autocomplete_manipulators_map() {
std::unordered_map<std::string, std::string> map;
//TODO: feel free to add more
map["endl(basic_ostream<_CharT, _Traits> &__os)"]="endl";
map["flush(basic_ostream<_CharT, _Traits> &__os)"]="flush";
map["hex(std::ios_base &__str)"]="hex"; //clang++ headers
map["hex(std::ios_base &__base)"]="hex"; //g++ headers
map["dec(std::ios_base &__str)"]="dec";
map["dec(std::ios_base &__base)"]="dec";
return map;
}
void Source::ClangViewAutocomplete::async_delete() {
parsing_in_progress->cancel("canceled, freeing resources in the background");
parse_state=ParseState::STOP;
@ -823,72 +861,105 @@ bool Source::ClangViewAutocomplete::full_reparse() {
////////////////////////////
Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language):
Source::ClangViewAutocomplete(file_path, language) {
similar_tokens_tag=get_buffer()->create_tag();
similar_tokens_tag->property_weight()=1000; //TODO: replace with Pango::WEIGHT_ULTRAHEAVY in 2016 or so (when Ubuntu 14 is history)
similar_identifiers_tag=get_buffer()->create_tag();
similar_identifiers_tag->property_weight()=1000; //TODO: replace with Pango::WEIGHT_ULTRAHEAVY in 2016 or so (when Ubuntu 14 is history)
get_buffer()->signal_changed().connect([this]() {
if(!renaming && last_tagged_token) {
for(auto &mark: similar_token_marks) {
get_buffer()->remove_tag(similar_tokens_tag, mark.first->get_iter(), mark.second->get_iter());
if(!renaming && last_tagged_identifier) {
for(auto &mark: similar_identifiers_marks) {
get_buffer()->remove_tag(similar_identifiers_tag, mark.first->get_iter(), mark.second->get_iter());
get_buffer()->delete_mark(mark.first);
get_buffer()->delete_mark(mark.second);
}
similar_token_marks.clear();
last_tagged_token=Token();
similar_identifiers_marks.clear();
last_tagged_identifier=Identifier();
}
});
get_token=[this]() -> Token {
if(parsed) {
auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index();
for(auto &token: *clang_tokens) {
auto cursor=token.get_cursor();
if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) {
if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) {
if(static_cast<unsigned>(token.get_cursor().get_kind())==103) //These cursors are buggy
continue;
auto referenced=cursor.get_referenced();
if(referenced)
return Token(this->language, static_cast<int>(referenced.get_kind()), token.get_spelling(), referenced.get_usr());
}
}
}
get_token_spelling=[this]() {
if(!parsed) {
Info::get().print("Buffer is parsing");
return std::string();
}
return Token();
return get_identifier().spelling;
};
rename_similar_tokens=[this](const Token &token, const std::string &text) {
size_t number=0;
if(parsed && token.language &&
(token.language->get_id()=="chdr" || token.language->get_id()=="cpphdr" || token.language->get_id()=="c" || token.language->get_id()=="cpp" || token.language->get_id()=="objc")) {
auto offsets=clang_tokens->get_similar_token_offsets(static_cast<clang::CursorKind>(token.type), token.spelling, token.usr);
std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > marks;
for(auto &offset: offsets) {
marks.emplace_back(get_buffer()->create_mark(get_buffer()->get_iter_at_line_index(offset.first.line-1, offset.first.index-1)), get_buffer()->create_mark(get_buffer()->get_iter_at_line_index(offset.second.line-1, offset.second.index-1)));
number++;
rename_similar_tokens=[this](const std::vector<Source::View*> &views, const std::string &text) {
std::vector<std::pair<boost::filesystem::path, size_t> > renamed;
if(!parsed) {
Info::get().print("Buffer is parsing");
return renamed;
}
auto identifier=get_identifier();
if(identifier) {
wait_parsing(views);
//If rename constructor or destructor, set token to class
if(identifier.kind==clang::CursorKind::Constructor || identifier.kind==clang::CursorKind::Destructor) {
auto parent_cursor=identifier.cursor.get_semantic_parent();
identifier=Identifier(parent_cursor.get_kind(), identifier.spelling, parent_cursor.get_usr(), parent_cursor);
}
get_source_buffer()->begin_user_action();
for(auto &mark: marks) {
renaming=true;
get_buffer()->erase(mark.first->get_iter(), mark.second->get_iter());
get_buffer()->insert(mark.first->get_iter(), text);
get_buffer()->delete_mark(mark.first);
get_buffer()->delete_mark(mark.second);
std::vector<Source::View*> renamed_views;
for(auto &view: views) {
if(auto clang_view=dynamic_cast<Source::ClangView*>(view)) {
//If rename class, also rename constructors and destructor
std::set<Identifier> identifiers;
identifiers.emplace(identifier);
if(identifier.cursor.get_kind()==clang::CursorKind::ClassDecl) {
for(auto &token: *clang_view->clang_tokens) {
auto cursor=token.get_cursor();
auto cursor_kind=cursor.get_kind();
auto parent_cursor=cursor.get_semantic_parent();
if(token.get_kind()==clang::TokenKind::Token_Identifier &&
(cursor_kind==clang::CursorKind::Constructor || cursor_kind==clang::CursorKind::Destructor) &&
parent_cursor.get_usr()==identifier.cursor.get_usr() && cursor.has_type()) {
identifiers.emplace(cursor.get_kind(), token.get_spelling(), cursor.get_usr());
}
}
}
std::vector<std::pair<clang::Offset, clang::Offset> > offsets;
for(auto &identifier: identifiers) {
auto token_offsets=clang_view->clang_tokens->get_similar_token_offsets(identifier.kind, identifier.spelling, identifier.usr);
for(auto &token_offset: token_offsets)
offsets.emplace_back(token_offset);
}
std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > marks;
for(auto &offset: offsets) {
marks.emplace_back(clang_view->get_buffer()->create_mark(clang_view->get_buffer()->get_iter_at_line_index(offset.first.line-1, offset.first.index-1)),
clang_view->get_buffer()->create_mark(clang_view->get_buffer()->get_iter_at_line_index(offset.second.line-1, offset.second.index-1)));
}
if(!marks.empty()) {
clang_view->renaming=true;
clang_view->get_buffer()->begin_user_action();
for(auto &mark: marks) {
clang_view->get_buffer()->erase(mark.first->get_iter(), mark.second->get_iter());
clang_view->get_buffer()->insert(mark.first->get_iter(), text);
clang_view->get_buffer()->delete_mark(mark.first);
clang_view->get_buffer()->delete_mark(mark.second);
}
clang_view->get_buffer()->end_user_action();
clang_view->renaming=false;
clang_view->save(views);
renamed_views.emplace_back(clang_view);
renamed.emplace_back(clang_view->file_path, marks.size());
}
}
}
get_source_buffer()->end_user_action();
renaming=false;
for(auto &view: renamed_views)
view->soft_reparse_needed=false;
}
return number;
return renamed;
};
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark){
if(mark->get_name()=="insert") {
delayed_tag_similar_tokens_connection.disconnect();
delayed_tag_similar_tokens_connection=Glib::signal_timeout().connect([this]() {
auto token=get_token();
tag_similar_tokens(token);
delayed_tag_similar_identifiers_connection.disconnect();
delayed_tag_similar_identifiers_connection=Glib::signal_timeout().connect([this]() {
auto identifier=get_identifier();
tag_similar_identifiers(identifier);
return false;
}, 100);
}
@ -896,66 +967,119 @@ Source::ClangViewAutocomplete(file_path, language) {
get_declaration_location=[this](){
Offset location;
if(parsed) {
auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index();
for(auto &token: *clang_tokens) {
auto cursor=token.get_cursor();
if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) {
if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) {
auto referenced=cursor.get_referenced();
if(referenced) {
location.file_path=referenced.get_source_location().get_path();
auto clang_offset=referenced.get_source_location().get_offset();
location.line=clang_offset.line;
location.index=clang_offset.index;
break;
if(!parsed) {
Info::get().print("Buffer is parsing");
return location;
}
auto iter=get_buffer()->get_insert()->get_iter();
auto line=static_cast<unsigned>(iter.get_line());
auto index=static_cast<unsigned>(iter.get_line_index());
for(auto &token: *clang_tokens) {
auto cursor=token.get_cursor();
if(token.get_kind()==clang::TokenKind::Token_Identifier && cursor.has_type()) {
if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) {
auto referenced=cursor.get_referenced();
if(referenced) {
location.file_path=referenced.get_source_location().get_path();
auto clang_offset=referenced.get_source_location().get_offset();
location.line=clang_offset.line;
location.index=clang_offset.index;
break;
}
}
}
}
return location;
};
get_implementation_location=[this](const std::vector<Source::View*> &views){
Offset location;
if(!parsed) {
Info::get().print("Buffer is parsing");
return location;
}
auto identifier=get_identifier();
if(identifier) {
wait_parsing(views);
for(auto &view: views) {
if(auto clang_view=dynamic_cast<Source::ClangView*>(view)) {
if(clang_view->language && clang_view->language->get_id()!="chdr" && clang_view->language->get_id()!="cpphdr") {
for(auto token_it=clang_view->clang_tokens->rbegin();token_it!=clang_view->clang_tokens->rend();++token_it) {
auto cursor=token_it->get_cursor();
auto kind=cursor.get_kind();
if((kind==clang::CursorKind::FunctionDecl || kind==clang::CursorKind::CXXMethod ||
kind==clang::CursorKind::Constructor || kind==clang::CursorKind::Destructor) &&
token_it->get_kind()==clang::TokenKind::Token_Identifier && cursor.has_type()) {
auto referenced=cursor.get_referenced();
if(referenced && identifier.kind==referenced.get_kind() &&
identifier.spelling==token_it->get_spelling() && identifier.usr==referenced.get_usr()) {
location.file_path=cursor.get_source_location().get_path();
auto clang_offset=cursor.get_source_location().get_offset();
location.line=clang_offset.line;
location.index=clang_offset.index;
return location;
}
}
}
}
}
}
Info::get().print("Could not find implementation");
}
return location;
};
get_usages=[this](const Token &token) {
get_usages=[this](const std::vector<Source::View*> &views) {
std::vector<std::pair<Offset, std::string> > usages;
if(parsed && token.language &&
(token.language->get_id()=="chdr" || token.language->get_id()=="cpphdr" || token.language->get_id()=="c" || token.language->get_id()=="cpp" || token.language->get_id()=="objc")) {
auto offsets=clang_tokens->get_similar_token_offsets(static_cast<clang::CursorKind>(token.type), token.spelling, token.usr);
for(auto &offset: offsets) {
size_t whitespaces_removed=0;
auto start_iter=get_buffer()->get_iter_at_line(offset.first.line-1);
while(!start_iter.ends_line() && (*start_iter==' ' || *start_iter=='\t')) {
start_iter.forward_char();
whitespaces_removed++;
}
auto end_iter=start_iter;
while(!end_iter.ends_line())
end_iter.forward_char();
std::string line=Glib::Markup::escape_text(get_buffer()->get_text(start_iter, end_iter));
//markup token as bold
size_t token_start_pos=offset.first.index-1-whitespaces_removed;
size_t token_end_pos=offset.second.index-1-whitespaces_removed;
size_t pos=0;
while((pos=line.find('&', pos))!=std::string::npos) {
size_t pos2=line.find(';', pos+2);
if(token_start_pos>pos) {
token_start_pos+=pos2-pos;
token_end_pos+=pos2-pos;
if(!parsed) {
Info::get().print("Buffer is parsing");
return usages;
}
auto identifier=get_identifier();
if(identifier) {
wait_parsing(views);
std::vector<Source::View*> views_reordered;
views_reordered.emplace_back(this);
for(auto &view: views) {
if(view!=this)
views_reordered.emplace_back(view);
}
for(auto &view: views_reordered) {
if(auto clang_view=dynamic_cast<Source::ClangView*>(view)) {
auto offsets=clang_view->clang_tokens->get_similar_token_offsets(identifier.kind, identifier.spelling, identifier.usr);
for(auto &offset: offsets) {
size_t whitespaces_removed=0;
auto start_iter=clang_view->get_buffer()->get_iter_at_line(offset.first.line-1);
while(!start_iter.ends_line() && (*start_iter==' ' || *start_iter=='\t')) {
start_iter.forward_char();
whitespaces_removed++;
}
auto end_iter=start_iter;
while(!end_iter.ends_line())
end_iter.forward_char();
std::string line=Glib::Markup::escape_text(clang_view->get_buffer()->get_text(start_iter, end_iter));
//markup token as bold
size_t token_start_pos=offset.first.index-1-whitespaces_removed;
size_t token_end_pos=offset.second.index-1-whitespaces_removed;
size_t pos=0;
while((pos=line.find('&', pos))!=std::string::npos) {
size_t pos2=line.find(';', pos+2);
if(token_start_pos>pos) {
token_start_pos+=pos2-pos;
token_end_pos+=pos2-pos;
}
else if(token_end_pos>pos)
token_end_pos+=pos2-pos;
else
break;
pos=pos2+1;
}
line.insert(token_end_pos, "</b>");
line.insert(token_start_pos, "<b>");
usages.emplace_back(Offset(offset.first.line-1, offset.first.index-1, clang_view->file_path), line);
}
else if(token_end_pos>pos)
token_end_pos+=pos2-pos;
else
break;
pos=pos2+1;
}
line.insert(token_end_pos, "</b>");
line.insert(token_start_pos, "<b>");
usages.emplace_back(Offset(offset.first.line-1, offset.first.index-1, this->file_path), line);
}
}
@ -963,54 +1087,56 @@ Source::ClangViewAutocomplete(file_path, language) {
};
goto_method=[this](){
if(parsed) {
auto iter=get_iter_for_dialog();
selection_dialog=std::unique_ptr<SelectionDialog>(new SelectionDialog(*this, get_buffer()->create_mark(iter), true, true));
auto rows=std::make_shared<std::unordered_map<std::string, clang::Offset> >();
auto methods=clang_tokens->get_cxx_methods();
if(methods.size()==0)
return;
for(auto &method: methods) {
std::string row=std::to_string(method.second.line)+": "+Glib::Markup::escape_text(method.first);
//Add bold method token
size_t token_end_pos=row.find('(');
if(token_end_pos==0 || token_end_pos==std::string::npos)
continue;
if(token_end_pos>8 && row.substr(token_end_pos-4, 4)=="&gt;") {
token_end_pos-=8;
size_t angle_bracket_count=1;
do {
if(row.substr(token_end_pos-4, 4)=="&gt;") {
angle_bracket_count++;
token_end_pos-=4;
}
else if(row.substr(token_end_pos-4, 4)=="&lt;") {
angle_bracket_count--;
token_end_pos-=4;
}
else
token_end_pos--;
} while(angle_bracket_count>0 && token_end_pos>4);
}
auto pos=token_end_pos;
if(!parsed) {
Info::get().print("Buffer is parsing");
return;
}
auto iter=get_iter_for_dialog();
selection_dialog=std::unique_ptr<SelectionDialog>(new SelectionDialog(*this, get_buffer()->create_mark(iter), true, true));
auto rows=std::make_shared<std::unordered_map<std::string, clang::Offset> >();
auto methods=clang_tokens->get_cxx_methods();
if(methods.size()==0)
return;
for(auto &method: methods) {
std::string row=std::to_string(method.second.line)+": "+Glib::Markup::escape_text(method.first);
//Add bold method token
size_t token_end_pos=row.find('(');
if(token_end_pos==0 || token_end_pos==std::string::npos)
continue;
if(token_end_pos>8 && row.substr(token_end_pos-4, 4)=="&gt;") {
token_end_pos-=8;
size_t angle_bracket_count=1;
do {
pos--;
} while(((row[pos]>='a' && row[pos]<='z') ||
(row[pos]>='A' && row[pos]<='Z') ||
(row[pos]>='0' && row[pos]<='9') || row[pos]=='_' || row[pos]=='~') && pos>0);
row.insert(token_end_pos, "</b>");
row.insert(pos+1, "<b>");
(*rows)[row]=method.second;
selection_dialog->add_row(row);
if(row.substr(token_end_pos-4, 4)=="&gt;") {
angle_bracket_count++;
token_end_pos-=4;
}
else if(row.substr(token_end_pos-4, 4)=="&lt;") {
angle_bracket_count--;
token_end_pos-=4;
}
else
token_end_pos--;
} while(angle_bracket_count>0 && token_end_pos>4);
}
selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) {
auto offset=rows->at(selected);
get_buffer()->place_cursor(get_buffer()->get_iter_at_line_index(offset.line-1, offset.index-1));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
delayed_tooltips_connection.disconnect();
};
selection_dialog->show();
auto pos=token_end_pos;
do {
pos--;
} while(((row[pos]>='a' && row[pos]<='z') ||
(row[pos]>='A' && row[pos]<='Z') ||
(row[pos]>='0' && row[pos]<='9') || row[pos]=='_' || row[pos]=='~') && pos>0);
row.insert(token_end_pos, "</b>");
row.insert(pos+1, "<b>");
(*rows)[row]=method.second;
selection_dialog->add_row(row);
}
selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) {
auto offset=rows->at(selected);
get_buffer()->place_cursor(get_buffer()->get_iter_at_line_index(offset.line-1, offset.index-1));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
delayed_tooltips_connection.disconnect();
};
selection_dialog->show();
};
get_token_data=[this]() {
@ -1026,85 +1152,87 @@ Source::ClangViewAutocomplete(file_path, language) {
};
std::vector<std::string> data;
if(parsed) {
auto iter=get_buffer()->get_insert()->get_iter();
auto line=(unsigned)iter.get_line();
auto index=(unsigned)iter.get_line_index();
for(auto &token: *clang_tokens) {
auto cursor=token.get_cursor();
if(token.get_kind()==clang::Token_Identifier && cursor.has_type()) {
if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) {
auto referenced=cursor.get_referenced();
if(referenced) {
auto usr=referenced.get_usr();
data.emplace_back("clang");
//namespace
size_t pos1, pos2=0;
while((pos1=usr.find('@', pos2))!=std::string::npos && pos1+1<usr.size() && usr[pos1+1]=='N') {
pos1+=3;
pos2=find_non_word_char(usr, pos1);
if(pos2!=std::string::npos) {
auto ns=usr.substr(pos1, pos2-pos1);
if(ns=="__1")
break;
data.emplace_back(ns);
}
else
break;
}
if(data.size()==1)
data.emplace_back("");
//template arguments
size_t template_pos=usr.find('$');
bool found_type_or_function=false;
//type
pos2=0;
while(((pos1=usr.find("T@", pos2))!=std::string::npos || (pos1=usr.find("S@", pos2))!=std::string::npos) && pos1<template_pos) {
found_type_or_function=true;
pos1+=2;
pos2=find_non_word_char(usr, pos1);
if(pos2!=std::string::npos)
data.emplace_back(usr.substr(pos1, pos2-pos1));
else {
data.emplace_back(usr.substr(pos1));
if(!parsed) {
Info::get().print("Buffer is parsing");
return data;
}
auto iter=get_buffer()->get_insert()->get_iter();
auto line=static_cast<unsigned>(iter.get_line());
auto index=static_cast<unsigned>(iter.get_line_index());
for(auto &token: *clang_tokens) {
auto cursor=token.get_cursor();
if(token.get_kind()==clang::TokenKind::Token_Identifier && cursor.has_type()) {
if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) {
auto referenced=cursor.get_referenced();
if(referenced) {
auto usr=referenced.get_usr();
data.emplace_back("clang");
//namespace
size_t pos1, pos2=0;
while((pos1=usr.find('@', pos2))!=std::string::npos && pos1+1<usr.size() && usr[pos1+1]=='N') {
pos1+=3;
pos2=find_non_word_char(usr, pos1);
if(pos2!=std::string::npos) {
auto ns=usr.substr(pos1, pos2-pos1);
if(ns=="__1")
break;
}
data.emplace_back(ns);
}
//function/constant//variable
pos1=usr.find("F@");
if(pos1==std::string::npos)
pos1=usr.find("C@");
if(pos1==std::string::npos)
pos1=usr.find("I@");
if(pos1!=std::string::npos) {
pos1+=2;
pos2=find_non_word_char(usr, pos1);
}
if(pos1!=std::string::npos) {
found_type_or_function=true;
if(pos2!=std::string::npos)
data.emplace_back(usr.substr(pos1, pos2-pos1));
else
data.emplace_back(usr.substr(pos1));
else
break;
}
if(data.size()==1)
data.emplace_back("");
//template arguments
size_t template_pos=usr.find('$');
bool found_type_or_function=false;
//type
pos2=0;
while(((pos1=usr.find("T@", pos2))!=std::string::npos || (pos1=usr.find("S@", pos2))!=std::string::npos) && pos1<template_pos) {
found_type_or_function=true;
pos1+=2;
pos2=find_non_word_char(usr, pos1);
if(pos2!=std::string::npos)
data.emplace_back(usr.substr(pos1, pos2-pos1));
else {
data.emplace_back(usr.substr(pos1));
break;
}
}
//Sometimes a method is at end without a identifier it seems:
if(!found_type_or_function && (pos1=usr.rfind('@'))!=std::string::npos) {
pos1++;
pos2=find_non_word_char(usr, pos1);
if(pos2!=std::string::npos)
data.emplace_back(usr.substr(pos1, pos2-pos1));
else
data.emplace_back(usr.substr(pos1));
}
//function/constant//variable
pos1=usr.find("F@");
if(pos1==std::string::npos)
pos1=usr.find("C@");
if(pos1==std::string::npos)
pos1=usr.find("I@");
if(pos1!=std::string::npos) {
pos1+=2;
pos2=find_non_word_char(usr, pos1);
}
if(pos1!=std::string::npos) {
found_type_or_function=true;
if(pos2!=std::string::npos)
data.emplace_back(usr.substr(pos1, pos2-pos1));
else
data.emplace_back(usr.substr(pos1));
}
break;
//Sometimes a method is at end without a identifier it seems:
if(!found_type_or_function && (pos1=usr.rfind('@'))!=std::string::npos) {
pos1++;
pos2=find_non_word_char(usr, pos1);
if(pos2!=std::string::npos)
data.emplace_back(usr.substr(pos1, pos2-pos1));
else
data.emplace_back(usr.substr(pos1));
}
break;
}
}
}
@ -1113,82 +1241,138 @@ Source::ClangViewAutocomplete(file_path, language) {
};
goto_next_diagnostic=[this]() {
if(parsed) {
auto insert_offset=get_buffer()->get_insert()->get_iter().get_offset();
for(auto offset: diagnostic_offsets) {
if(offset>insert_offset) {
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return;
}
}
if(diagnostic_offsets.size()>0) {
auto iter=get_buffer()->get_iter_at_offset(*diagnostic_offsets.begin());
get_buffer()->place_cursor(iter);
if(!parsed) {
Info::get().print("Buffer is parsing");
return;
}
auto insert_offset=get_buffer()->get_insert()->get_iter().get_offset();
for(auto offset: diagnostic_offsets) {
if(offset>insert_offset) {
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return;
}
}
if(diagnostic_offsets.size()>0) {
auto iter=get_buffer()->get_iter_at_offset(*diagnostic_offsets.begin());
get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}
};
apply_fix_its=[this]() {
if(!parsed) {
Info::get().print("Buffer is parsing");
return;
}
std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > fix_it_marks;
if(parsed) {
for(auto &fix_it: fix_its) {
auto start_iter=get_buffer()->get_iter_at_line_index(fix_it.offsets.first.line-1, fix_it.offsets.first.index-1);
auto end_iter=get_buffer()->get_iter_at_line_index(fix_it.offsets.second.line-1, fix_it.offsets.second.index-1);
fix_it_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter));
for(auto &fix_it: fix_its) {
auto start_iter=get_buffer()->get_iter_at_line_index(fix_it.offsets.first.line-1, fix_it.offsets.first.index-1);
auto end_iter=get_buffer()->get_iter_at_line_index(fix_it.offsets.second.line-1, fix_it.offsets.second.index-1);
fix_it_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter));
}
size_t c=0;
get_buffer()->begin_user_action();
for(auto &fix_it: fix_its) {
if(fix_it.type==FixIt::Type::INSERT) {
get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source);
}
size_t c=0;
get_source_buffer()->begin_user_action();
for(auto &fix_it: fix_its) {
if(fix_it.type==FixIt::Type::INSERT) {
get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source);
}
if(fix_it.type==FixIt::Type::REPLACE) {
get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter());
get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source);
}
if(fix_it.type==FixIt::Type::ERASE) {
get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter());
}
c++;
if(fix_it.type==FixIt::Type::REPLACE) {
get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter());
get_buffer()->insert(fix_it_marks[c].first->get_iter(), fix_it.source);
}
for(auto &mark_pair: fix_it_marks) {
get_buffer()->delete_mark(mark_pair.first);
get_buffer()->delete_mark(mark_pair.second);
if(fix_it.type==FixIt::Type::ERASE) {
get_buffer()->erase(fix_it_marks[c].first->get_iter(), fix_it_marks[c].second->get_iter());
}
get_source_buffer()->end_user_action();
c++;
}
for(auto &mark_pair: fix_it_marks) {
get_buffer()->delete_mark(mark_pair.first);
get_buffer()->delete_mark(mark_pair.second);
}
get_buffer()->end_user_action();
};
}
void Source::ClangViewRefactor::tag_similar_tokens(const Token &token) {
Source::ClangViewRefactor::Identifier Source::ClangViewRefactor::get_identifier() {
if(!parsed)
return Identifier();
auto iter=get_buffer()->get_insert()->get_iter();
auto line=static_cast<unsigned>(iter.get_line());
auto index=static_cast<unsigned>(iter.get_line_index());
for(auto &token: *clang_tokens) {
auto cursor=token.get_cursor();
if(token.get_kind()==clang::TokenKind::Token_Identifier && cursor.has_type()) {
if(line==token.offsets.first.line-1 && index>=token.offsets.first.index-1 && index <=token.offsets.second.index-1) {
if(token.get_cursor().get_kind()==clang::CursorKind::CallExpr) //These cursors are buggy
continue;
auto referenced=cursor.get_referenced();
if(referenced)
return Identifier(referenced.get_kind(), token.get_spelling(), referenced.get_usr(), referenced);
}
}
}
return Identifier();
}
void Source::ClangViewRefactor::wait_parsing(const std::vector<Source::View*> &views) {
std::unique_ptr<Dialog::Message> message;
std::vector<Source::ClangView*> clang_views;
for(auto &view: views) {
if(auto clang_view=dynamic_cast<Source::ClangView*>(view)) {
if(!clang_view->parsed) {
clang_views.emplace_back(clang_view);
if(!message)
message=std::unique_ptr<Dialog::Message>(new Dialog::Message("Please wait while all buffers finish parsing"));
}
}
}
if(message) {
for(;;) {
while(g_main_context_pending(NULL))
g_main_context_iteration(NULL, false);
bool all_parsed=true;
for(auto &clang_view: clang_views) {
if(!clang_view->parsed) {
all_parsed=false;
break;
}
}
if(all_parsed)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
message->hide();
}
}
void Source::ClangViewRefactor::tag_similar_identifiers(const Identifier &identifier) {
if(parsed) {
if(token && last_tagged_token!=token) {
for(auto &mark: similar_token_marks) {
get_buffer()->remove_tag(similar_tokens_tag, mark.first->get_iter(), mark.second->get_iter());
if(identifier && last_tagged_identifier!=identifier) {
for(auto &mark: similar_identifiers_marks) {
get_buffer()->remove_tag(similar_identifiers_tag, mark.first->get_iter(), mark.second->get_iter());
get_buffer()->delete_mark(mark.first);
get_buffer()->delete_mark(mark.second);
}
similar_token_marks.clear();
auto offsets=clang_tokens->get_similar_token_offsets(static_cast<clang::CursorKind>(token.type), token.spelling, token.usr);
similar_identifiers_marks.clear();
auto offsets=clang_tokens->get_similar_token_offsets(identifier.kind, identifier.spelling, identifier.usr);
for(auto &offset: offsets) {
auto start_iter=get_buffer()->get_iter_at_line_index(offset.first.line-1, offset.first.index-1);
auto end_iter=get_buffer()->get_iter_at_line_index(offset.second.line-1, offset.second.index-1);
get_buffer()->apply_tag(similar_tokens_tag, start_iter, end_iter);
similar_token_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter));
get_buffer()->apply_tag(similar_identifiers_tag, start_iter, end_iter);
similar_identifiers_marks.emplace_back(get_buffer()->create_mark(start_iter), get_buffer()->create_mark(end_iter));
}
last_tagged_token=token;
last_tagged_identifier=identifier;
}
}
if(!token && last_tagged_token) {
for(auto &mark: similar_token_marks) {
get_buffer()->remove_tag(similar_tokens_tag, mark.first->get_iter(), mark.second->get_iter());
if(!identifier && last_tagged_identifier) {
for(auto &mark: similar_identifiers_marks) {
get_buffer()->remove_tag(similar_identifiers_tag, mark.first->get_iter(), mark.second->get_iter());
get_buffer()->delete_mark(mark.first);
get_buffer()->delete_mark(mark.second);
}
similar_token_marks.clear();
last_tagged_token=Token();
similar_identifiers_marks.clear();
last_tagged_identifier=Identifier();
}
}
@ -1202,7 +1386,7 @@ Source::ClangView::ClangView(const boost::filesystem::path &file_path, Glib::Ref
void Source::ClangView::async_delete() {
dispatcher.disconnect();
delayed_reparse_connection.disconnect();
delayed_tag_similar_tokens_connection.disconnect();
delayed_tag_similar_identifiers_connection.disconnect();
ClangViewAutocomplete::async_delete();
}

39
src/source_clang.h

@ -15,17 +15,11 @@ namespace Source {
protected:
enum class ParseState {PROCESSING, RESTARTING, STOP};
enum class ParseProcessState {IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING};
public:
class TokenRange {
public:
TokenRange(std::pair<clang::Offset, clang::Offset> offsets, int kind):
offsets(offsets), kind(kind) {}
std::pair<clang::Offset, clang::Offset> offsets;
int kind;
};
public:
ClangViewParse(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language);
bool save(const std::vector<Source::View*> &views) override;
void configure() override;
void soft_reparse() override;
@ -89,6 +83,7 @@ namespace Source {
Tooltips autocomplete_tooltips;
std::string prefix;
std::mutex prefix_mutex;
static std::unordered_map<std::string, std::string> autocomplete_manipulators_map();
Glib::Dispatcher do_delete_object;
std::thread delete_thread;
@ -97,15 +92,33 @@ namespace Source {
};
class ClangViewRefactor : public ClangViewAutocomplete {
class Identifier {
public:
Identifier(clang::CursorKind kind, const std::string &spelling, const std::string &usr, const clang::Cursor &cursor=clang::Cursor()) :
kind(kind), spelling(spelling), usr(usr), cursor(cursor) {}
Identifier() : kind(static_cast<clang::CursorKind>(0)) {}
operator bool() const { return static_cast<int>(kind)!=0; }
bool operator==(const Identifier &rhs) const { return (kind==rhs.kind && spelling==rhs.spelling && usr==rhs.usr); }
bool operator!=(const Identifier &rhs) const { return !(*this==rhs); }
bool operator<(const Identifier &rhs) const { return usr<rhs.usr; }
clang::CursorKind kind;
std::string spelling;
std::string usr;
clang::Cursor cursor;
};
public:
ClangViewRefactor(const boost::filesystem::path &file_path, Glib::RefPtr<Gsv::Language> language);
protected:
sigc::connection delayed_tag_similar_tokens_connection;
sigc::connection delayed_tag_similar_identifiers_connection;
private:
std::list<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > similar_token_marks;
void tag_similar_tokens(const Token &token);
Glib::RefPtr<Gtk::TextTag> similar_tokens_tag;
Token last_tagged_token;
Identifier get_identifier();
void wait_parsing(const std::vector<Source::View*> &views);
std::list<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark> > > similar_identifiers_marks;
void tag_similar_identifiers(const Identifier &identifier);
Glib::RefPtr<Gtk::TextTag> similar_identifiers_tag;
Identifier last_tagged_identifier;
bool renaming=false;
};

126
src/terminal.cc

@ -1,7 +1,9 @@
#include "terminal.h"
#include <iostream>
#include "config.h"
#include "project.h"
#include "info.h"
#include "notebook.h"
#include <iostream>
Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) {
start(start_msg);
@ -38,9 +40,17 @@ void Terminal::InProgress::cancel(const std::string& msg) {
Terminal::get().async_print(line_nr-1, msg);
}
const REGEX_NS::regex Terminal::link_regex("^([A-Z]:)?([^:]+):([0-9]+):([0-9]+)$");
Terminal::Terminal() {
bold_tag=get_buffer()->create_tag();
bold_tag->property_weight()=PANGO_WEIGHT_BOLD;
link_tag=get_buffer()->create_tag();
link_tag->property_underline()=Pango::Underline::UNDERLINE_SINGLE;
link_mouse_cursor=Gdk::Cursor::create(Gdk::CursorType::HAND1);
default_mouse_cursor=Gdk::Cursor::create(Gdk::CursorType::XTERM);
}
int Terminal::process(const std::string &command, const boost::filesystem::path &path, bool use_pipes) {
@ -138,7 +148,9 @@ void Terminal::async_process(const std::string &command, const boost::filesystem
void Terminal::kill_last_async_process(bool force) {
std::unique_lock<std::mutex> lock(processes_mutex);
if(processes.size()>0)
if(processes.empty())
Info::get().print("No running processes");
else
processes.back()->kill(force);
}
@ -148,6 +160,55 @@ void Terminal::kill_async_processes(bool force) {
process->kill(force);
}
bool Terminal::on_motion_notify_event(GdkEventMotion *motion_event) {
Gtk::TextIter iter;
int location_x, location_y;
window_to_buffer_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, motion_event->x, motion_event->y, location_x, location_y);
get_iter_at_location(iter, location_x, location_y);
if(iter.has_tag(link_tag))
get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->set_cursor(link_mouse_cursor);
else
get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->set_cursor(default_mouse_cursor);
return Gtk::TextView::on_motion_notify_event(motion_event);
}
void Terminal::apply_link_tags(Gtk::TextIter start_iter, Gtk::TextIter end_iter) {
auto iter=start_iter;
int offset=0;
size_t colons=0;
Gtk::TextIter start_path_iter;
bool possible_path=false;
//Search for path with line and index
//Simple implementation. Not sure if it is work the effort to make it work 100% on all platforms.
do {
if(iter.starts_line()) {
offset=0;
colons=0;
start_path_iter=iter;
possible_path=true;
}
if(possible_path) {
if(*iter==' ' || *iter=='\t' || iter.ends_line())
possible_path=false;
else {
++offset;
if(*iter==':') {
#ifdef _WIN32
if(offset!=2)
#endif
++colons;
if(colons==3 && possible_path) {
REGEX_NS::smatch sm;
if(REGEX_NS::regex_match(get_buffer()->get_text(start_path_iter, iter).raw(), sm, link_regex))
get_buffer()->apply_tag(link_tag, start_path_iter, iter);
possible_path=false;
}
}
}
}
} while(iter.forward_char() && iter!=end_iter);
}
size_t Terminal::print(const std::string &message, bool bold){
#ifdef _WIN32
//Remove color codes
@ -185,10 +246,16 @@ size_t Terminal::print(const std::string &message, bool bold){
umessage.replace(iter, next_char_iter, "?");
}
auto start_mark=get_buffer()->create_mark(get_buffer()->get_iter_at_line(get_buffer()->get_insert()->get_iter().get_line()));
if(bold)
get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag);
else
get_buffer()->insert(get_buffer()->end(), umessage);
auto start_iter=start_mark->get_iter();
get_buffer()->delete_mark(start_mark);
auto end_iter=get_buffer()->get_insert()->get_iter();
apply_link_tags(start_iter, end_iter);
if(get_buffer()->get_line_count()>Config::get().terminal.history_size) {
int lines=get_buffer()->get_line_count()-Config::get().terminal.history_size;
@ -240,6 +307,10 @@ void Terminal::async_print(size_t line_nr, const std::string &message) {
}
void Terminal::configure() {
#if GTKMM_MAJOR_VERSION>3 || (GTKMM_MAJOR_VERSION==3 && GTKMM_MINOR_VERSION>=12)
link_tag->property_foreground_rgba()=get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_LINK);
#endif
if(Config::get().terminal.font.size()>0) {
override_font(Pango::FontDescription(Config::get().terminal.font));
}
@ -267,16 +338,61 @@ void Terminal::clear() {
get_buffer()->set_text("");
}
bool Terminal::on_button_press_event(GdkEventButton* button_event) {
//open clicked link in terminal
if(button_event->type==GDK_BUTTON_PRESS && button_event->button==GDK_BUTTON_PRIMARY) {
Gtk::TextIter iter;
int location_x, location_y;
window_to_buffer_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, button_event->x, button_event->y, location_x, location_y);
get_iter_at_location(iter, location_x, location_y);
auto start_iter=iter;
auto end_iter=iter;
if(iter.has_tag(link_tag) &&
start_iter.backward_to_tag_toggle(link_tag) && end_iter.forward_to_tag_toggle(link_tag)) {
std::string path_str=get_buffer()->get_text(start_iter, end_iter);
REGEX_NS::smatch sm;
if(REGEX_NS::regex_match(path_str, sm, link_regex)) {
auto path_str=sm[1].str()+sm[2].str();
auto path=boost::filesystem::path(path_str);
boost::system::error_code ec;
if(path.is_relative()) {
if(Project::current)
path=boost::filesystem::canonical(Project::current->build->get_default_path()/path_str, ec);
else
return Gtk::TextView::on_button_press_event(button_event);
}
else
path=boost::filesystem::canonical(path_str, ec);
if(!ec && boost::filesystem::is_regular_file(path)) {
Notebook::get().open(path);
if(Notebook::get().get_current_page()!=-1) {
auto view=Notebook::get().get_current_view();
try {
int line = std::stoi(sm[3].str())-1;
int index = std::stoi(sm[4].str())-1;
view->place_cursor_at_line_index(line, index);
view->scroll_to_cursor_delayed(view, true, true);
return true;
}
catch(const std::exception &) {}
}
}
}
}
}
return Gtk::TextView::on_button_press_event(button_event);
}
bool Terminal::on_key_press_event(GdkEventKey *event) {
std::unique_lock<std::mutex> lock(processes_mutex);
bool debug_is_running=false;
#ifdef JUCI_ENABLE_DEBUG
debug_is_running=Project::current_language?Project::current_language->debug_is_running():false;
debug_is_running=Project::current?Project::current->debug_is_running():false;
#endif
if(processes.size()>0 || debug_is_running) {
get_buffer()->place_cursor(get_buffer()->end());
auto unicode=gdk_keyval_to_unicode(event->keyval);
char chr=(char)unicode;
char chr=static_cast<char>(unicode);
if(unicode>=32 && unicode<=126) {
stdin_buffer+=chr;
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
@ -293,7 +409,7 @@ bool Terminal::on_key_press_event(GdkEventKey *event) {
stdin_buffer+='\n';
if(debug_is_running) {
#ifdef JUCI_ENABLE_DEBUG
Project::current_language->debug_write(stdin_buffer);
Project::current->debug_write(stdin_buffer);
#endif
}
else

23
src/terminal.h

@ -11,6 +11,17 @@
#include "process.hpp"
#include "dispatcher.h"
#include <unordered_set>
//Temporary fix for current Arch Linux boost linking problem
#ifdef __GNUC_PREREQ
#if __GNUC_PREREQ(5,1)
#include <regex>
#define REGEX_NS std
#endif
#endif
#ifndef REGEX_NS
#include <boost/regex.hpp>
#define REGEX_NS boost
#endif
class Terminal : public Gtk::TextView {
public:
@ -53,17 +64,23 @@ public:
void clear();
protected:
bool on_key_press_event(GdkEventKey *event);
bool on_motion_notify_event (GdkEventMotion* motion_event) override;
bool on_button_press_event(GdkEventButton* button_event) override;
bool on_key_press_event(GdkEventKey *event) override;
private:
Dispatcher dispatcher;
Glib::RefPtr<Gtk::TextTag> bold_tag;
Glib::RefPtr<Gtk::TextTag> link_tag;
Glib::RefPtr<Gdk::Cursor> link_mouse_cursor;
Glib::RefPtr<Gdk::Cursor> default_mouse_cursor;
size_t deleted_lines=0;
const static REGEX_NS::regex link_regex;
void apply_link_tags(Gtk::TextIter start_iter, Gtk::TextIter end_iter);
std::vector<std::shared_ptr<Process> > processes;
std::mutex processes_mutex;
std::string stdin_buffer;
size_t deleted_lines=0;
std::unordered_set<InProgress*> in_progresses;
std::mutex in_progresses_mutex;
};

28
src/tooltips.cc

@ -1,8 +1,5 @@
#include "tooltips.h"
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
namespace sigc {
#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
template <typename Functor>
@ -51,6 +48,11 @@ void Tooltip::adjust(bool disregard_drawn) {
//init window
window=std::unique_ptr<Gtk::Window>(new Gtk::Window(Gtk::WindowType::WINDOW_POPUP));
auto g_application=g_application_get_default();
auto gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
window->set_transient_for(*application->get_active_window());
window->set_events(Gdk::POINTER_MOTION_MASK);
window->signal_motion_notify_event().connect([this](GdkEventMotion* event){
window->hide();
@ -74,18 +76,30 @@ void Tooltip::adjust(bool disregard_drawn) {
tooltip_height+=2;
}
//Adjust if tooltip is left of text_view
Gdk::Rectangle visible_rect;
text_view.get_visible_rect(visible_rect);
int visible_x, visible_y;
text_view.buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, visible_rect.get_x(), visible_rect.get_y(), visible_x, visible_y);
auto activation_rectangle_x=std::max(activation_rectangle.get_x(), visible_x);
int root_x, root_y;
text_view.get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(activation_rectangle.get_x(), activation_rectangle.get_y(), root_x, root_y);
text_view.get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(activation_rectangle_x, activation_rectangle.get_y(), root_x, root_y);
Gdk::Rectangle rectangle;
rectangle.set_x(root_x);
rectangle.set_y(root_y-tooltip_height);
rectangle.set_y(std::max(0, root_y-tooltip_height));
rectangle.set_width(tooltip_width);
rectangle.set_height(tooltip_height);
if(!disregard_drawn) {
if(Tooltips::drawn_tooltips_rectangle.get_width()!=0) {
if(rectangle.intersects(Tooltips::drawn_tooltips_rectangle))
rectangle.set_y(Tooltips::drawn_tooltips_rectangle.get_y()-tooltip_height);
if(rectangle.intersects(Tooltips::drawn_tooltips_rectangle)) {
int new_y=Tooltips::drawn_tooltips_rectangle.get_y()-tooltip_height;
if(new_y>=0)
rectangle.set_y(new_y);
else
rectangle.set_x(Tooltips::drawn_tooltips_rectangle.get_x()+Tooltips::drawn_tooltips_rectangle.get_width()+2);
}
Tooltips::drawn_tooltips_rectangle.join(rectangle);
}
else

258
src/window.cc

@ -7,6 +7,7 @@
#include "filesystem.h"
#include "project.h"
#include "entrybox.h"
#include "info.h"
namespace sigc {
#ifndef SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
@ -31,10 +32,6 @@ Window::Window() : notebook(Notebook::get()) {
configure();
set_default_size(Config::get().window.default_size.first, Config::get().window.default_size.second);
//PluginApi(&this->notebook, &this->menu);
add(vpaned);
directories_scrolled_window.add(Directories::get());
directory_and_notebook_panes.pack1(directories_scrolled_window, Gtk::SHRINK);
notebook_vbox.pack_start(notebook);
@ -59,7 +56,25 @@ Window::Window() : notebook(Notebook::get()) {
terminal_vbox.pack_end(info_and_status_hbox, Gtk::PACK_SHRINK);
vpaned.pack2(terminal_vbox, true, true);
#if GTKMM_MAJOR_VERSION>3 || (GTKMM_MAJOR_VERSION==3 && GTKMM_MINOR_VERSION>=14)
overlay_vbox.set_hexpand(false);
overlay_vbox.set_halign(Gtk::Align::ALIGN_START);
overlay_hbox.set_hexpand(false);
overlay_hbox.set_halign(Gtk::Align::ALIGN_END);
overlay_vbox.pack_start(Info::get(), Gtk::PACK_SHRINK, 20);
overlay_hbox.pack_end(overlay_vbox, Gtk::PACK_SHRINK, 20);
overlay.add(vpaned);
overlay.add_overlay(overlay_hbox);
#if GTKMM_MAJOR_VERSION>3 || (GTKMM_MAJOR_VERSION==3 && GTKMM_MINOR_VERSION>=18)
overlay.set_overlay_pass_through(overlay_hbox, true);
#endif
add(overlay);
#else
add(vpaned);
#endif
show_all_children();
Info::get().hide();
Directories::get().on_row_activated=[this](const boost::filesystem::path &path) {
notebook.open(path);
@ -421,81 +436,81 @@ void Window::set_menu_actions() {
if(notebook.get_current_page()!=-1) {
if(notebook.get_current_view()->get_declaration_location) {
auto location=notebook.get_current_view()->get_declaration_location();
if(!location.file_path.empty()) {
if(location) {
boost::filesystem::path declaration_file;
boost::system::error_code ec;
declaration_file=boost::filesystem::canonical(location.file_path, ec);
if(ec)
declaration_file=location.file_path;
return;
notebook.open(declaration_file);
auto view=notebook.get_current_view();
auto line=static_cast<int>(location.line)-1;
auto index=static_cast<int>(location.index)-1;
view->place_cursor_at_line_index(line, index);
view->scroll_to_cursor_delayed(view, true, false);
}
}
}
});
menu.add_action("source_goto_implementation", [this]() {
if(notebook.get_current_page()!=-1) {
auto view=notebook.get_current_view();
if(view->get_implementation_location) {
auto location=view->get_implementation_location(notebook.source_views);
if(location) {
boost::filesystem::path implementation_path;
boost::system::error_code ec;
implementation_path=boost::filesystem::canonical(location.file_path, ec);
if(ec)
return;
notebook.open(implementation_path);
auto view=notebook.get_current_view();
line=std::min(line, view->get_buffer()->get_line_count()-1);
if(line>=0) {
auto iter=view->get_buffer()->get_iter_at_line(line);
while(!iter.ends_line())
iter.forward_char();
auto end_line_index=iter.get_line_index();
index=std::min(index, end_line_index);
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(line, index));
view->scroll_to_cursor_delayed(view, true, false);
}
auto line=static_cast<int>(location.line)-1;
auto index=static_cast<int>(location.index)-1;
view->place_cursor_at_line_index(line, index);
view->scroll_to_cursor_delayed(view, true, false);
return;
}
}
}
});
menu.add_action("source_goto_usage", [this]() {
if(notebook.get_current_page()!=-1) {
auto current_view=notebook.get_current_view();
if(current_view->get_token && current_view->get_usages) {
auto token=current_view->get_token();
if(token) {
auto iter=current_view->get_iter_for_dialog();
current_view->selection_dialog=std::unique_ptr<SelectionDialog>(new SelectionDialog(*current_view, current_view->get_buffer()->create_mark(iter), true, true));
auto view=notebook.get_current_view();
if(view->get_usages) {
auto usages=view->get_usages(notebook.source_views);
if(!usages.empty()) {
auto iter=view->get_iter_for_dialog();
view->selection_dialog=std::unique_ptr<SelectionDialog>(new SelectionDialog(*view, view->get_buffer()->create_mark(iter), true, true));
auto rows=std::make_shared<std::unordered_map<std::string, Source::Offset> >();
//First add usages in current file
auto usages=current_view->get_usages(token);
for(auto &usage: usages) {
auto iter=current_view->get_buffer()->get_iter_at_line_index(usage.first.line, usage.first.index);
auto row=std::to_string(iter.get_line()+1)+':'+std::to_string(iter.get_line_offset()+1)+' '+usage.second;
std::string row;
//add file name if usage is not in current tab
if(view->file_path!=usage.first.file_path)
row=usage.first.file_path.filename().string()+":";
row+=std::to_string(usage.first.line+1)+": "+usage.second;
(*rows)[row]=usage.first;
current_view->selection_dialog->add_row(row);
}
//Then the remaining opened files
for(int page=0;page<notebook.size();page++) {
auto view=notebook.get_view(page);
if(view!=current_view) {
if(view->get_usages) {
auto usages=view->get_usages(token);
for(auto &usage: usages) {
auto iter=view->get_buffer()->get_iter_at_line_index(usage.first.line, usage.first.index);
auto row=usage.first.file_path.filename().string()+":"+std::to_string(iter.get_line()+1)+':'+std::to_string(iter.get_line_offset()+1)+' '+usage.second;
(*rows)[row]=usage.first;
current_view->selection_dialog->add_row(row);
}
}
}
view->selection_dialog->add_row(row);
}
if(rows->size()==0)
return;
current_view->selection_dialog->on_select=[this, rows](const std::string& selected, bool hide_window) {
view->selection_dialog->on_select=[this, rows](const std::string &selected, bool hide_window) {
auto offset=rows->at(selected);
boost::filesystem::path declaration_file;
boost::system::error_code ec;
declaration_file=boost::filesystem::canonical(offset.file_path, ec);
if(ec)
declaration_file=offset.file_path;
return;
notebook.open(declaration_file);
auto view=notebook.get_current_view();
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(offset.line, offset.index));
view->place_cursor_at_line_index(offset.line, offset.index);
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
view->delayed_tooltips_connection.disconnect();
};
current_view->selection_dialog->show();
view->selection_dialog->show();
}
}
}
@ -527,8 +542,8 @@ void Window::set_menu_actions() {
});
menu.add_action("project_set_run_arguments", [this]() {
auto project_language=Project::get_language();
auto run_arguments=std::make_shared<std::pair<std::string, std::string> >(project_language->get_run_arguments());
auto project=Project::create();
auto run_arguments=std::make_shared<std::pair<std::string, std::string> >(project->get_run_arguments());
if(run_arguments->second.empty())
return;
@ -551,26 +566,30 @@ void Window::set_menu_actions() {
EntryBox::get().show();
});
menu.add_action("compile_and_run", [this]() {
if(Project::compiling || Project::debugging)
if(Project::compiling || Project::debugging) {
Info::get().print("Compile or debug in progress");
return;
}
Project::current_language=Project::get_language();
Project::current=Project::create();
if(Config::get().project.save_on_compile_or_run)
Project::save_files(Project::current_language->build->project_path);
Project::save_files(Project::current->build->project_path);
Project::current_language->compile_and_run();
Project::current->compile_and_run();
});
menu.add_action("compile", [this]() {
if(Project::compiling || Project::debugging)
if(Project::compiling || Project::debugging) {
Info::get().print("Compile or debug in progress");
return;
}
Project::current_language=Project::get_language();
Project::current=Project::create();
if(Config::get().project.save_on_compile_or_run)
Project::save_files(Project::current_language->build->project_path);
Project::save_files(Project::current->build->project_path);
Project::current_language->compile();
Project::current->compile();
});
menu.add_action("run_command", [this]() {
@ -610,8 +629,8 @@ void Window::set_menu_actions() {
#ifdef JUCI_ENABLE_DEBUG
menu.add_action("debug_set_run_arguments", [this]() {
auto project_language=Project::get_language();
auto run_arguments=std::make_shared<std::pair<std::string, std::string> >(project_language->debug_get_run_arguments());
auto project=Project::create();
auto run_arguments=std::make_shared<std::pair<std::string, std::string> >(project->debug_get_run_arguments());
if(run_arguments->second.empty())
return;
@ -634,54 +653,56 @@ void Window::set_menu_actions() {
EntryBox::get().show();
});
menu.add_action("debug_start_continue", [this](){
if(Project::compiling)
if(Project::compiling) {
Info::get().print("Compile in progress");
return;
}
else if(Project::debugging) {
Project::current_language->debug_continue();
Project::current->debug_continue();
return;
}
Project::current_language=Project::get_language();
Project::current=Project::create();
if(Config::get().project.save_on_compile_or_run)
Project::save_files(Project::current_language->build->project_path);
Project::save_files(Project::current->build->project_path);
Project::current_language->debug_start();
Project::current->debug_start();
});
menu.add_action("debug_stop", [this]() {
if(Project::current_language)
Project::current_language->debug_stop();
if(Project::current)
Project::current->debug_stop();
});
menu.add_action("debug_kill", [this]() {
if(Project::current_language)
Project::current_language->debug_kill();
if(Project::current)
Project::current->debug_kill();
});
menu.add_action("debug_step_over", [this]() {
if(Project::current_language)
Project::current_language->debug_step_over();
if(Project::current)
Project::current->debug_step_over();
});
menu.add_action("debug_step_into", [this]() {
if(Project::current_language)
Project::current_language->debug_step_into();
if(Project::current)
Project::current->debug_step_into();
});
menu.add_action("debug_step_out", [this]() {
if(Project::current_language)
Project::current_language->debug_step_out();
if(Project::current)
Project::current->debug_step_out();
});
menu.add_action("debug_backtrace", [this]() {
if(Project::current_language)
Project::current_language->debug_backtrace();
if(Project::current)
Project::current->debug_backtrace();
});
menu.add_action("debug_show_variables", [this]() {
if(Project::current_language)
Project::current_language->debug_show_variables();
if(Project::current)
Project::current->debug_show_variables();
});
menu.add_action("debug_run_command", [this]() {
EntryBox::get().clear();
EntryBox::get().entries.emplace_back(last_run_debug_command, [this](const std::string& content){
if(content!="") {
if(Project::current_language)
Project::current_language->debug_run_command(content);
if(Project::current)
Project::current->debug_run_command(content);
last_run_debug_command=content;
}
EntryBox::get().hide();
@ -703,13 +724,13 @@ void Window::set_menu_actions() {
auto end_iter=start_iter;
while(!end_iter.ends_line() && end_iter.forward_char()) {}
view->get_source_buffer()->remove_source_marks(start_iter, end_iter, "debug_breakpoint");
if(Project::current_language && Project::debugging)
Project::current_language->debug_remove_breakpoint(view->file_path, line_nr+1, view->get_buffer()->get_line_count()+1);
if(Project::current && Project::debugging)
Project::current->debug_remove_breakpoint(view->file_path, line_nr+1, view->get_buffer()->get_line_count()+1);
}
else {
view->get_source_buffer()->create_source_mark("debug_breakpoint", view->get_buffer()->get_insert()->get_iter());
if(Project::current_language && Project::debugging)
Project::current_language->debug_add_breakpoint(view->file_path, line_nr+1);
if(Project::current && Project::debugging)
Project::current->debug_add_breakpoint(view->file_path, line_nr+1);
}
}
});
@ -720,19 +741,10 @@ void Window::set_menu_actions() {
if(notebook.get_current_page()!=-1) {
auto view=notebook.get_current_view();
int line_nr=Project::debug_stop.second.first-1;
int line_index=Project::debug_stop.second.second-1;
if(line_nr<view->get_buffer()->get_line_count()) {
auto iter=view->get_buffer()->get_iter_at_line(line_nr);
auto end_line_iter=iter;
while(!iter.ends_line() && iter.forward_char()) {}
auto line=view->get_buffer()->get_text(iter, end_line_iter);
if(static_cast<size_t>(line_index)>=line.bytes())
line_index=0;
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line_index(line_nr, line_index));
view->scroll_to_cursor_delayed(view, true, true);
}
int line=Project::debug_stop.second.first-1;
int index=Project::debug_stop.second.second-1;
view->place_cursor_at_line_index(line, index);
view->scroll_to_cursor_delayed(view, true, true);
Project::debug_update_stop();
}
}
@ -776,6 +788,7 @@ void Window::activate_menu_items(bool activate) {
menu.actions["source_indentation_auto_indent_buffer"]->set_enabled(activate ? static_cast<bool>(notebook.get_current_view()->auto_indent) : false);
menu.actions["source_find_documentation"]->set_enabled(activate ? static_cast<bool>(notebook.get_current_view()->get_token_data) : false);
menu.actions["source_goto_declaration"]->set_enabled(activate ? static_cast<bool>(notebook.get_current_view()->get_declaration_location) : false);
menu.actions["source_goto_implementation"]->set_enabled(activate ? static_cast<bool>(notebook.get_current_view()->get_implementation_location) : false);
menu.actions["source_goto_usage"]->set_enabled(activate ? static_cast<bool>(notebook.get_current_view()->get_usages) : false);
menu.actions["source_goto_method"]->set_enabled(activate ? static_cast<bool>(notebook.get_current_view()->goto_method) : false);
menu.actions["source_rename"]->set_enabled(activate ? static_cast<bool>(notebook.get_current_view()->rename_similar_tokens) : false);
@ -851,8 +864,8 @@ bool Window::on_delete_event(GdkEventAny *event) {
}
Terminal::get().kill_async_processes();
#ifdef JUCI_ENABLE_DEBUG
if(Project::current_language)
Project::current_language->debug_delete();
if(Project::current)
Project::current->debug_delete();
#endif
return false;
}
@ -1033,13 +1046,8 @@ void Window::goto_line_entry() {
if(notebook.get_current_page()!=-1) {
auto view=notebook.get_current_view();
try {
auto line = stoi(content);
if(line>0 && line<=view->get_buffer()->get_line_count()) {
line--;
view->get_buffer()->place_cursor(view->get_buffer()->get_iter_at_line(line));
view->scroll_to_cursor_delayed(view, true, false);
}
view->place_cursor_at_line_index(stoi(content)-1, 0);
view->scroll_to_cursor_delayed(view, true, false);
}
catch(const std::exception &e) {}
EntryBox::get().hide();
@ -1057,33 +1065,29 @@ void Window::goto_line_entry() {
void Window::rename_token_entry() {
EntryBox::get().clear();
if(notebook.get_current_page()!=-1) {
if(notebook.get_current_view()->get_token) {
auto token=std::make_shared<Source::Token>(notebook.get_current_view()->get_token());
if(*token) {
auto view=notebook.get_current_view();
if(view->get_token_spelling && view->rename_similar_tokens) {
auto spelling=std::make_shared<std::string>(view->get_token_spelling());
if(!spelling->empty()) {
EntryBox::get().labels.emplace_back();
auto label_it=EntryBox::get().labels.begin();
label_it->update=[label_it](int state, const std::string& message){
label_it->set_text("Warning: only opened and parsed tabs will have its content renamed, and modified files will be saved");
label_it->set_text("Warning: only opened files will be refactored, and altered files will be saved");
};
label_it->update(0, "");
EntryBox::get().entries.emplace_back(token->spelling, [this, token](const std::string& content){
if(notebook.get_current_page()!=-1 && content!=token->spelling) {
std::vector<int> modified_pages;
for(int c=0;c<notebook.size();c++) {
auto view=notebook.get_view(c);
if(view->rename_similar_tokens) {
auto number=view->rename_similar_tokens(*token, content);
if(number>0) {
Terminal::get().print("Replaced "+std::to_string(number)+" occurrences in file "+view->file_path.string()+"\n");
notebook.save(c);
modified_pages.emplace_back(c);
}
}
}
for(auto &page: modified_pages)
notebook.get_view(page)->soft_reparse_needed=false;
EntryBox::get().hide();
auto iter=std::make_shared<Gtk::TextIter>(view->get_buffer()->get_insert()->get_iter());
EntryBox::get().entries.emplace_back(*spelling, [this, view, spelling, iter](const std::string& content){
//TODO: gtk needs a way to check if iter is valid without dumping g_error message
//iter->get_buffer() will print such a message, but no segfault will occur
if(notebook.get_current_page()!=-1 && notebook.get_current_view()==view &&
content!=*spelling && iter->get_buffer() && view->get_buffer()->get_insert()->get_iter()==*iter) {
auto renamed_pairs=view->rename_similar_tokens(notebook.source_views, content);
for(auto &renamed: renamed_pairs)
Terminal::get().print("Replaced "+std::to_string(renamed.second)+" occurrence"+(renamed.second>1?"s":"")+" in file "+renamed.first.string()+"\n");
}
else
Info::get().print("Operation canceled");
EntryBox::get().hide();
});
auto entry_it=EntryBox::get().entries.begin();
entry_it->set_placeholder_text("New name");

5
src/window.h

@ -27,6 +27,11 @@ private:
Gtk::ScrolledWindow directories_scrolled_window;
Gtk::ScrolledWindow terminal_scrolled_window;
Gtk::HBox info_and_status_hbox;
Gtk::VBox overlay_vbox;
Gtk::HBox overlay_hbox;
#if GTKMM_MAJOR_VERSION>3 || (GTKMM_MAJOR_VERSION==3 && GTKMM_MINOR_VERSION>=14)
Gtk::Overlay overlay;
#endif
Gtk::AboutDialog about;
Glib::RefPtr<Gtk::CssProvider> css_provider;

Loading…
Cancel
Save