Browse Source

Formatted code with custom clang-format

merge-requests/389/head
eidheim 8 years ago
parent
commit
bf984231d4
  1. 9
      .clang-format
  2. 27
      README.md
  3. 28
      src/autocomplete.cc
  4. 8
      src/autocomplete.h
  5. 354
      src/cmake.cc
  6. 16
      src/cmake.h
  7. 151
      src/compile_commands.cc
  8. 10
      src/compile_commands.h
  9. 166
      src/config.cc
  10. 46
      src/config.h
  11. 202
      src/ctags.cc
  12. 11
      src/ctags.h
  13. 429
      src/debug_lldb.cc
  14. 36
      src/debug_lldb.h
  15. 60
      src/dialogs.cc
  16. 13
      src/dialogs.h
  17. 21
      src/dialogs_unix.cc
  18. 100
      src/dialogs_win.cc
  19. 577
      src/directories.cc
  20. 49
      src/directories.h
  21. 8
      src/dispatcher.cc
  22. 11
      src/dispatcher.h
  23. 38
      src/documentation_cppreference.cc
  24. 2
      src/documentation_cppreference.h
  25. 62
      src/entrybox.cc
  26. 30
      src/entrybox.h
  27. 28
      src/files.h
  28. 151
      src/filesystem.cc
  29. 16
      src/filesystem.h
  30. 218
      src/git.cc
  31. 67
      src/git.h
  32. 22
      src/info.cc
  33. 5
      src/info.h
  34. 72
      src/juci.cc
  35. 3
      src/juci.h
  36. 26
      src/menu.cc
  37. 16
      src/menu.h
  38. 96
      src/meson.cc
  39. 10
      src/meson.h
  40. 542
      src/notebook.cc
  41. 65
      src/notebook.h
  42. 795
      src/project.cc
  43. 79
      src/project.h
  44. 108
      src/project_build.cc
  45. 50
      src/project_build.h
  46. 300
      src/selection_dialog.cc
  47. 63
      src/selection_dialog.h
  48. 2683
      src/source.cc
  49. 131
      src/source.h
  50. 248
      src/source_base.cc
  51. 43
      src/source_base.h
  52. 1646
      src/source_clang.cc
  53. 68
      src/source_clang.h
  54. 232
      src/source_diff.cc
  55. 38
      src/source_diff.h
  56. 1160
      src/source_language_protocol.cc
  57. 28
      src/source_language_protocol.h
  58. 398
      src/source_spellcheck.cc
  59. 29
      src/source_spellcheck.h
  60. 329
      src/terminal.cc
  61. 41
      src/terminal.h
  62. 152
      src/tooltips.cc
  63. 41
      src/tooltips.h
  64. 22
      src/usages_clang.cc
  65. 1214
      src/window.cc
  66. 11
      src/window.h
  67. 104
      tests/cmake_build_test.cc
  68. 34
      tests/compile_commands_test.cc
  69. 70
      tests/filesystem_test.cc
  70. 68
      tests/git_test.cc
  71. 160
      tests/lldb_test.cc
  72. 2
      tests/lldb_test_files/main.cpp
  73. 48
      tests/meson_build_test.cc
  74. 38
      tests/process_test.cc
  75. 162
      tests/source_clang_test.cc
  76. 4
      tests/source_clang_test_files/main.cpp
  77. 180
      tests/source_test.cc
  78. 2
      tests/stubs/dialogs.cc
  79. 20
      tests/stubs/selection_dialog.cc
  80. 6
      tests/stubs/tooltips.cc
  81. 44
      tests/terminal_test.cc
  82. 26
      tests/usages_clang_test.cc
  83. 4
      tests/usages_clang_test_files/main.cpp
  84. 4
      tests/usages_clang_test_files/test.hpp

9
.clang-format

@ -0,0 +1,9 @@
IndentWidth: 2
AccessModifierOffset: -2
UseTab: Never
ColumnLimit: 0
MaxEmptyLinesToKeep: 2
SpaceBeforeParens: Never
BreakBeforeBraces: Custom
BraceWrapping: {BeforeElse: true, BeforeCatch: true}
NamespaceIndentation: All

27
README.md

@ -77,3 +77,30 @@ See [installation guide](docs/install.md).
## Documentation ## Documentation
See [how to build the API doc](docs/api.md). See [how to build the API doc](docs/api.md).
## Coding style
Due to poor lambda support in clang-format, a custom clang-format is used with the following patch applied:
```diff
diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp
index bb8efd61a3..e80a487055 100644
--- a/lib/Format/ContinuationIndenter.cpp
+++ b/lib/Format/ContinuationIndenter.cpp
@@ -276,6 +276,8 @@ LineState ContinuationIndenter::getInitialState(unsigned FirstIndent,
}
bool ContinuationIndenter::canBreak(const LineState &State) {
+ if(Style.ColumnLimit==0)
+ return true;
const FormatToken &Current = *State.NextToken;
const FormatToken &Previous = *Current.Previous;
assert(&Previous == Current.Previous);
@@ -325,6 +327,8 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
}
bool ContinuationIndenter::mustBreak(const LineState &State) {
+ if(Style.ColumnLimit==0)
+ return false;
const FormatToken &Current = *State.NextToken;
const FormatToken &Previous = *Current.Previous;
if (Current.MustBreakBefore || Current.is(TT_InlineASMColon))
```

28
src/autocomplete.cc

@ -130,10 +130,10 @@ void Autocomplete::stop() {
} }
void Autocomplete::setup_dialog() { void Autocomplete::setup_dialog() {
CompletionDialog::get()->on_show=[this] { CompletionDialog::get()->on_show = [this] {
on_show(); on_show();
}; };
CompletionDialog::get()->on_hide = [this]() { CompletionDialog::get()->on_hide = [this]() {
view->get_buffer()->end_user_action(); view->get_buffer()->end_user_action();
tooltips.hide(); tooltips.hide();
@ -141,39 +141,39 @@ void Autocomplete::setup_dialog() {
on_hide(); on_hide();
reparse(); reparse();
}; };
CompletionDialog::get()->on_changed = [this](unsigned int index, const std::string &text) { CompletionDialog::get()->on_changed = [this](unsigned int index, const std::string &text) {
if(index >= rows.size()) { if(index >= rows.size()) {
tooltips.hide(); tooltips.hide();
return; return;
} }
on_changed(index, text); on_changed(index, text);
auto tooltip = get_tooltip(index); auto tooltip = get_tooltip(index);
if(tooltip.empty()) if(tooltip.empty())
tooltips.hide(); tooltips.hide();
else { else {
tooltips.clear(); tooltips.clear();
auto create_tooltip_buffer = [ this, tooltip = std::move(tooltip) ]() { auto create_tooltip_buffer = [this, tooltip = std::move(tooltip)]() {
auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table()); auto tooltip_buffer = Gtk::TextBuffer::create(view->get_buffer()->get_tag_table());
tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), tooltip); tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), tooltip);
return tooltip_buffer; return tooltip_buffer;
}; };
auto iter = CompletionDialog::get()->start_mark->get_iter(); auto iter = CompletionDialog::get()->start_mark->get_iter();
tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter)); tooltips.emplace_back(create_tooltip_buffer, view, view->get_buffer()->create_mark(iter), view->get_buffer()->create_mark(iter));
tooltips.show(true); tooltips.show(true);
} }
}; };
CompletionDialog::get()->on_select=[this](unsigned int index, const std::string &text, bool hide_window) { CompletionDialog::get()->on_select = [this](unsigned int index, const std::string &text, bool hide_window) {
if(index>=rows.size()) if(index >= rows.size())
return; return;
on_select(index, text, hide_window); on_select(index, text, hide_window);
}; };
} }

8
src/autocomplete.h

@ -40,19 +40,19 @@ public:
/// The handler is not run in the main loop. /// The handler is not run in the main loop.
std::function<void(std::string &buffer, int line_number, int column)> add_rows = [](std::string &, int, int) {}; std::function<void(std::string &buffer, int line_number, int column)> add_rows = [](std::string &, int, int) {};
std::function<void()> on_show = [] {}; std::function<void()> on_show = [] {};
std::function<void()> on_hide = [] {}; std::function<void()> on_hide = [] {};
std::function<void(unsigned int, const std::string &)> on_changed = [](unsigned int index, const std::string &text) {}; std::function<void(unsigned int, const std::string &)> on_changed = [](unsigned int index, const std::string &text) {};
std::function<void(unsigned int, const std::string &, bool)> on_select = [](unsigned int index, const std::string &text, bool hide_window) {}; std::function<void(unsigned int, const std::string &, bool)> on_select = [](unsigned int index, const std::string &text, bool hide_window) {};
std::function<std::string(unsigned int)> get_tooltip = [](unsigned int index) {return std::string();}; std::function<std::string(unsigned int)> get_tooltip = [](unsigned int index) { return std::string(); };
Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word); Autocomplete(Gtk::TextView *view, bool &interactive_completion, guint &last_keyval, bool strip_word);
void run(); void run();
void stop(); void stop();
private: private:
void setup_dialog(); void setup_dialog();
}; };

354
src/cmake.cc

@ -1,14 +1,14 @@
#include "cmake.h" #include "cmake.h"
#include "filesystem.h" #include "compile_commands.h"
#include "dialogs.h"
#include "config.h" #include "config.h"
#include "dialogs.h"
#include "filesystem.h"
#include "terminal.h" #include "terminal.h"
#include <regex> #include <regex>
#include "compile_commands.h"
CMake::CMake(const boost::filesystem::path &path) { CMake::CMake(const boost::filesystem::path &path) {
const auto find_cmake_project=[](const boost::filesystem::path &cmake_path) { const auto find_cmake_project = [](const boost::filesystem::path &cmake_path) {
for(auto &line: filesystem::read_lines(cmake_path)) { for(auto &line : filesystem::read_lines(cmake_path)) {
const static std::regex project_regex(R"(^ *project *\(.*\r?$)", std::regex::icase); const static std::regex project_regex(R"(^ *project *\(.*\r?$)", std::regex::icase);
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, project_regex)) if(std::regex_match(line, sm, project_regex))
@ -16,53 +16,53 @@ CMake::CMake(const boost::filesystem::path &path) {
} }
return false; return false;
}; };
auto search_path=boost::filesystem::is_directory(path)?path:path.parent_path(); auto search_path = boost::filesystem::is_directory(path) ? path : path.parent_path();
while(true) { while(true) {
auto search_cmake_path=search_path/"CMakeLists.txt"; auto search_cmake_path = search_path / "CMakeLists.txt";
if(boost::filesystem::exists(search_cmake_path)) { if(boost::filesystem::exists(search_cmake_path)) {
paths.emplace(paths.begin(), search_cmake_path); paths.emplace(paths.begin(), search_cmake_path);
if(find_cmake_project(search_cmake_path)) { if(find_cmake_project(search_cmake_path)) {
project_path=search_path; project_path = search_path;
break; break;
} }
} }
if(search_path==search_path.root_directory()) if(search_path == search_path.root_directory())
break; break;
search_path=search_path.parent_path(); search_path = search_path.parent_path();
} }
} }
bool CMake::update_default_build(const boost::filesystem::path &default_build_path, bool force) { bool CMake::update_default_build(const boost::filesystem::path &default_build_path, bool force) {
if(project_path.empty() || !boost::filesystem::exists(project_path/"CMakeLists.txt") || default_build_path.empty()) if(project_path.empty() || !boost::filesystem::exists(project_path / "CMakeLists.txt") || default_build_path.empty())
return false; return false;
if(!boost::filesystem::exists(default_build_path)) { if(!boost::filesystem::exists(default_build_path)) {
boost::system::error_code ec; boost::system::error_code ec;
boost::filesystem::create_directories(default_build_path, ec); boost::filesystem::create_directories(default_build_path, ec);
if(ec) { if(ec) {
Terminal::get().print("Error: could not create "+default_build_path.string()+": "+ec.message()+"\n", true); Terminal::get().print("Error: could not create " + default_build_path.string() + ": " + ec.message() + "\n", true);
return false; return false;
} }
} }
if(!force && boost::filesystem::exists(default_build_path/"compile_commands.json")) if(!force && boost::filesystem::exists(default_build_path / "compile_commands.json"))
return true; return true;
auto compile_commands_path=default_build_path/"compile_commands.json"; auto compile_commands_path = default_build_path / "compile_commands.json";
Dialog::Message message("Creating/updating default build"); Dialog::Message message("Creating/updating default build");
auto exit_status=Terminal::get().process(Config::get().project.cmake.command+' '+ auto exit_status = Terminal::get().process(Config::get().project.cmake.command + ' ' +
filesystem::escape_argument(project_path.string())+" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", default_build_path); filesystem::escape_argument(project_path.string()) + " -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", default_build_path);
message.hide(); message.hide();
if(exit_status==EXIT_SUCCESS) { if(exit_status == EXIT_SUCCESS) {
#ifdef _WIN32 //Temporary fix to MSYS2's libclang #ifdef _WIN32 //Temporary fix to MSYS2's libclang
auto compile_commands_file=filesystem::read(compile_commands_path); auto compile_commands_file = filesystem::read(compile_commands_path);
auto replace_drive = [&compile_commands_file](const std::string& param) { auto replace_drive = [&compile_commands_file](const std::string &param) {
size_t pos=0; size_t pos = 0;
auto param_size = param.length(); auto param_size = param.length();
while((pos=compile_commands_file.find(param+"/", pos))!=std::string::npos) { while((pos = compile_commands_file.find(param + "/", pos)) != std::string::npos) {
if(pos+param_size+1<compile_commands_file.size()) if(pos + param_size + 1 < compile_commands_file.size())
compile_commands_file.replace(pos, param_size+2, param+compile_commands_file[pos+param_size+1]+":"); compile_commands_file.replace(pos, param_size + 2, param + compile_commands_file[pos + param_size + 1] + ":");
else else
break; break;
} }
@ -77,26 +77,26 @@ bool CMake::update_default_build(const boost::filesystem::path &default_build_pa
} }
bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) { bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) {
if(project_path.empty() || !boost::filesystem::exists(project_path/"CMakeLists.txt") || debug_build_path.empty()) if(project_path.empty() || !boost::filesystem::exists(project_path / "CMakeLists.txt") || debug_build_path.empty())
return false; return false;
if(!boost::filesystem::exists(debug_build_path)) { if(!boost::filesystem::exists(debug_build_path)) {
boost::system::error_code ec; boost::system::error_code ec;
boost::filesystem::create_directories(debug_build_path, ec); boost::filesystem::create_directories(debug_build_path, ec);
if(ec) { if(ec) {
Terminal::get().print("Error: could not create "+debug_build_path.string()+": "+ec.message()+"\n", true); Terminal::get().print("Error: could not create " + debug_build_path.string() + ": " + ec.message() + "\n", true);
return false; return false;
} }
} }
if(!force && boost::filesystem::exists(debug_build_path/"CMakeCache.txt")) if(!force && boost::filesystem::exists(debug_build_path / "CMakeCache.txt"))
return true; return true;
Dialog::Message message("Creating/updating debug build"); Dialog::Message message("Creating/updating debug build");
auto exit_status=Terminal::get().process(Config::get().project.cmake.command+' '+ auto exit_status = Terminal::get().process(Config::get().project.cmake.command + ' ' +
filesystem::escape_argument(project_path.string())+" -DCMAKE_BUILD_TYPE=Debug", debug_build_path); filesystem::escape_argument(project_path.string()) + " -DCMAKE_BUILD_TYPE=Debug", debug_build_path);
message.hide(); message.hide();
if(exit_status==EXIT_SUCCESS) if(exit_status == EXIT_SUCCESS)
return true; return true;
return false; return false;
} }
@ -105,53 +105,53 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui
// CMake does not store in compile_commands.json if an object is part of an executable or not. // CMake does not store in compile_commands.json if an object is part of an executable or not.
// Therefore, executables are first attempted found in the cmake files. These executables // Therefore, executables are first attempted found in the cmake files. These executables
// are then used to identify if a file in compile_commands.json is part of an executable or not // are then used to identify if a file in compile_commands.json is part of an executable or not
auto parameters = get_functions_parameters("add_executable"); auto parameters = get_functions_parameters("add_executable");
std::vector<boost::filesystem::path> cmake_executables; std::vector<boost::filesystem::path> cmake_executables;
for(auto &parameter: parameters) { for(auto &parameter : parameters) {
if(parameter.second.size()>1 && parameter.second[0].size()>0 && parameter.second[0].compare(0, 2, "${")!=0) { if(parameter.second.size() > 1 && parameter.second[0].size() > 0 && parameter.second[0].compare(0, 2, "${") != 0) {
auto executable=(parameter.first.parent_path()/parameter.second[0]).string(); auto executable = (parameter.first.parent_path() / parameter.second[0]).string();
auto project_path_str=project_path.string(); auto project_path_str = project_path.string();
size_t pos=executable.find(project_path_str); size_t pos = executable.find(project_path_str);
if(pos!=std::string::npos) if(pos != std::string::npos)
executable.replace(pos, project_path_str.size(), build_path.string()); executable.replace(pos, project_path_str.size(), build_path.string());
cmake_executables.emplace_back(executable); cmake_executables.emplace_back(executable);
} }
} }
CompileCommands compile_commands(build_path); CompileCommands compile_commands(build_path);
std::vector<std::pair<boost::filesystem::path, boost::filesystem::path>> command_files_and_maybe_executables; std::vector<std::pair<boost::filesystem::path, boost::filesystem::path>> command_files_and_maybe_executables;
for(auto &command: compile_commands.commands) { for(auto &command : compile_commands.commands) {
auto command_file=filesystem::get_normal_path(command.file); auto command_file = filesystem::get_normal_path(command.file);
auto values=command.parameter_values("-o"); auto values = command.parameter_values("-o");
if(!values.empty()) { if(!values.empty()) {
size_t pos; size_t pos;
values[0].erase(0, 11); values[0].erase(0, 11);
if((pos=values[0].find(".dir"))!=std::string::npos) { if((pos = values[0].find(".dir")) != std::string::npos) {
auto executable=command.directory/values[0].substr(0, pos); auto executable = command.directory / values[0].substr(0, pos);
command_files_and_maybe_executables.emplace_back(command_file, executable); command_files_and_maybe_executables.emplace_back(command_file, executable);
} }
} }
} }
size_t best_match_size=-1; size_t best_match_size = -1;
boost::filesystem::path best_match_executable; boost::filesystem::path best_match_executable;
for(auto &cmake_executable: cmake_executables) { for(auto &cmake_executable : cmake_executables) {
for(auto &command_file_and_maybe_executable: command_files_and_maybe_executables) { for(auto &command_file_and_maybe_executable : command_files_and_maybe_executables) {
auto &command_file=command_file_and_maybe_executable.first; auto &command_file = command_file_and_maybe_executable.first;
auto &maybe_executable=command_file_and_maybe_executable.second; auto &maybe_executable = command_file_and_maybe_executable.second;
if(cmake_executable==maybe_executable) { if(cmake_executable == maybe_executable) {
if(command_file==file_path) if(command_file == file_path)
return maybe_executable; return maybe_executable;
auto command_file_directory=command_file.parent_path(); auto command_file_directory = command_file.parent_path();
if(filesystem::file_in_path(file_path, command_file_directory)) { if(filesystem::file_in_path(file_path, command_file_directory)) {
auto size=static_cast<size_t>(std::distance(command_file_directory.begin(), command_file_directory.end())); auto size = static_cast<size_t>(std::distance(command_file_directory.begin(), command_file_directory.end()));
if(best_match_size==static_cast<size_t>(-1) || best_match_size<size) { if(best_match_size == static_cast<size_t>(-1) || best_match_size < size) {
best_match_size=size; best_match_size = size;
best_match_executable=maybe_executable; best_match_executable = maybe_executable;
} }
} }
} }
@ -159,18 +159,18 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui
} }
if(!best_match_executable.empty()) if(!best_match_executable.empty())
return best_match_executable; return best_match_executable;
for(auto &command_file_and_maybe_executable: command_files_and_maybe_executables) { for(auto &command_file_and_maybe_executable : command_files_and_maybe_executables) {
auto &command_file=command_file_and_maybe_executable.first; auto &command_file = command_file_and_maybe_executable.first;
auto &maybe_executable=command_file_and_maybe_executable.second; auto &maybe_executable = command_file_and_maybe_executable.second;
if(command_file==file_path) if(command_file == file_path)
return maybe_executable; return maybe_executable;
auto command_file_directory=command_file.parent_path(); auto command_file_directory = command_file.parent_path();
if(filesystem::file_in_path(file_path, command_file_directory)) { if(filesystem::file_in_path(file_path, command_file_directory)) {
auto size=static_cast<size_t>(std::distance(command_file_directory.begin(), command_file_directory.end())); auto size = static_cast<size_t>(std::distance(command_file_directory.begin(), command_file_directory.end()));
if(best_match_size==static_cast<size_t>(-1) || best_match_size<size) { if(best_match_size == static_cast<size_t>(-1) || best_match_size < size) {
best_match_size=size; best_match_size = size;
best_match_executable=maybe_executable; best_match_executable = maybe_executable;
} }
} }
} }
@ -178,33 +178,33 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui
} }
void CMake::read_files() { void CMake::read_files() {
for(auto &path: paths) for(auto &path : paths)
files.emplace_back(filesystem::read(path)); files.emplace_back(filesystem::read(path));
} }
void CMake::remove_tabs() { void CMake::remove_tabs() {
for(auto &file: files) { for(auto &file : files) {
for(auto &chr: file) { for(auto &chr : file) {
if(chr=='\t') if(chr == '\t')
chr=' '; chr = ' ';
} }
} }
} }
void CMake::remove_comments() { void CMake::remove_comments() {
for(auto &file: files) { for(auto &file : files) {
size_t pos=0; size_t pos = 0;
size_t comment_start; size_t comment_start;
bool inside_comment=false; bool inside_comment = false;
while(pos<file.size()) { while(pos < file.size()) {
if(!inside_comment && file[pos]=='#') { if(!inside_comment && file[pos] == '#') {
comment_start=pos; comment_start = pos;
inside_comment=true; inside_comment = true;
} }
if(inside_comment && file[pos]=='\n') { if(inside_comment && file[pos] == '\n') {
file.erase(comment_start, pos-comment_start); file.erase(comment_start, pos - comment_start);
pos-=pos-comment_start; pos -= pos - comment_start;
inside_comment=false; inside_comment = false;
} }
pos++; pos++;
} }
@ -214,69 +214,69 @@ void CMake::remove_comments() {
} }
void CMake::remove_newlines_inside_parentheses() { void CMake::remove_newlines_inside_parentheses() {
for(auto &file: files) { for(auto &file : files) {
size_t pos=0; size_t pos = 0;
bool inside_para=false; bool inside_para = false;
bool inside_quote=false; bool inside_quote = false;
char last_char=0; char last_char = 0;
while(pos<file.size()) { while(pos < file.size()) {
if(!inside_quote && file[pos]=='"' && last_char!='\\') if(!inside_quote && file[pos] == '"' && last_char != '\\')
inside_quote=true; inside_quote = true;
else if(inside_quote && file[pos]=='"' && last_char!='\\') else if(inside_quote && file[pos] == '"' && last_char != '\\')
inside_quote=false; inside_quote = false;
else if(!inside_quote && file[pos]=='(') else if(!inside_quote && file[pos] == '(')
inside_para=true; inside_para = true;
else if(!inside_quote && file[pos]==')') else if(!inside_quote && file[pos] == ')')
inside_para=false; inside_para = false;
else if(inside_para && file[pos]=='\n') else if(inside_para && file[pos] == '\n')
file.replace(pos, 1, 1, ' '); file.replace(pos, 1, 1, ' ');
last_char=file[pos]; last_char = file[pos];
pos++; pos++;
} }
} }
} }
void CMake::parse_variable_parameters(std::string &data) { void CMake::parse_variable_parameters(std::string &data) {
size_t pos=0; size_t pos = 0;
bool inside_quote=false; bool inside_quote = false;
char last_char=0; char last_char = 0;
while(pos<data.size()) { while(pos < data.size()) {
if(!inside_quote && data[pos]=='"' && last_char!='\\') { if(!inside_quote && data[pos] == '"' && last_char != '\\') {
inside_quote=true; inside_quote = true;
data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test} data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
pos--; pos--;
} }
else if(inside_quote && data[pos]=='"' && last_char!='\\') { else if(inside_quote && data[pos] == '"' && last_char != '\\') {
inside_quote=false; inside_quote = false;
data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test} data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test}
pos--; pos--;
} }
else if(!inside_quote && data[pos]==' ' && pos+1<data.size() && data[pos+1]==' ') { else if(!inside_quote && data[pos] == ' ' && pos + 1 < data.size() && data[pos + 1] == ' ') {
data.erase(pos, 1); data.erase(pos, 1);
pos--; pos--;
} }
if(pos!=static_cast<size_t>(-1)) if(pos != static_cast<size_t>(-1))
last_char=data[pos]; last_char = data[pos];
pos++; pos++;
} }
for(auto &var: variables) { for(auto &var : variables) {
auto pos=data.find("${"+var.first+'}'); auto pos = data.find("${" + var.first + '}');
while(pos!=std::string::npos) { while(pos != std::string::npos) {
data.replace(pos, var.first.size()+3, var.second); data.replace(pos, var.first.size() + 3, var.second);
pos=data.find("${"+var.first+'}'); pos = data.find("${" + var.first + '}');
} }
} }
//Remove variables we do not know: //Remove variables we do not know:
pos=data.find("${"); pos = data.find("${");
auto pos_end=data.find('}', pos+2); auto pos_end = data.find('}', pos + 2);
while(pos!=std::string::npos && pos_end!=std::string::npos) { while(pos != std::string::npos && pos_end != std::string::npos) {
data.erase(pos, pos_end-pos+1); data.erase(pos, pos_end - pos + 1);
pos=data.find("${"); pos = data.find("${");
pos_end=data.find('}', pos+2); pos_end = data.find('}', pos + 2);
} }
} }
@ -285,93 +285,93 @@ void CMake::parse() {
remove_tabs(); remove_tabs();
remove_comments(); remove_comments();
remove_newlines_inside_parentheses(); remove_newlines_inside_parentheses();
parsed=true; parsed = true;
} }
std::vector<std::string> CMake::get_function_parameters(std::string &data) { std::vector<std::string> CMake::get_function_parameters(std::string &data) {
std::vector<std::string> parameters; std::vector<std::string> parameters;
size_t pos=0; size_t pos = 0;
size_t parameter_pos=0; size_t parameter_pos = 0;
bool inside_quote=false; bool inside_quote = false;
char last_char=0; char last_char = 0;
while(pos<data.size()) { while(pos < data.size()) {
if(!inside_quote && data[pos]=='"' && last_char!='\\') { if(!inside_quote && data[pos] == '"' && last_char != '\\') {
inside_quote=true; inside_quote = true;
data.erase(pos, 1); data.erase(pos, 1);
pos--; pos--;
} }
else if(inside_quote && data[pos]=='"' && last_char!='\\') { else if(inside_quote && data[pos] == '"' && last_char != '\\') {
inside_quote=false; inside_quote = false;
data.erase(pos, 1); data.erase(pos, 1);
pos--; pos--;
} }
else if(!inside_quote && pos+1<data.size() && data[pos]==' ' && data[pos+1]==' ') { else if(!inside_quote && pos + 1 < data.size() && data[pos] == ' ' && data[pos + 1] == ' ') {
data.erase(pos, 1); data.erase(pos, 1);
pos--; pos--;
} }
else if(!inside_quote && data[pos]==' ') { else if(!inside_quote && data[pos] == ' ') {
parameters.emplace_back(data.substr(parameter_pos, pos-parameter_pos)); parameters.emplace_back(data.substr(parameter_pos, pos - parameter_pos));
if(pos+1<data.size()) if(pos + 1 < data.size())
parameter_pos=pos+1; parameter_pos = pos + 1;
} }
if(pos!=static_cast<size_t>(-1)) if(pos != static_cast<size_t>(-1))
last_char=data[pos]; last_char = data[pos];
pos++; pos++;
} }
parameters.emplace_back(data.substr(parameter_pos)); parameters.emplace_back(data.substr(parameter_pos));
for(auto &var: variables) { for(auto &var : variables) {
for(auto &parameter: parameters) { for(auto &parameter : parameters) {
auto pos=parameter.find("${"+var.first+'}'); auto pos = parameter.find("${" + var.first + '}');
while(pos!=std::string::npos) { while(pos != std::string::npos) {
parameter.replace(pos, var.first.size()+3, var.second); parameter.replace(pos, var.first.size() + 3, var.second);
pos=parameter.find("${"+var.first+'}'); pos = parameter.find("${" + var.first + '}');
} }
} }
} }
return parameters; return parameters;
} }
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > CMake::get_functions_parameters(const std::string &name) { std::vector<std::pair<boost::filesystem::path, std::vector<std::string>>> CMake::get_functions_parameters(const std::string &name) {
const std::regex function_regex("^ *"+name+R"( *\( *(.*)\) *\r?$)", std::regex::icase); const std::regex function_regex("^ *" + name + R"( *\( *(.*)\) *\r?$)", std::regex::icase);
variables.clear(); variables.clear();
if(!parsed) if(!parsed)
parse(); parse();
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > functions; std::vector<std::pair<boost::filesystem::path, std::vector<std::string>>> functions;
for(size_t c=0;c<files.size();++c) { for(size_t c = 0; c < files.size(); ++c) {
size_t pos=0; size_t pos = 0;
while(pos<files[c].size()) { while(pos < files[c].size()) {
auto start_line=pos; auto start_line = pos;
auto end_line=files[c].find('\n', start_line); auto end_line = files[c].find('\n', start_line);
if(end_line==std::string::npos) if(end_line == std::string::npos)
end_line=files[c].size(); end_line = files[c].size();
if(end_line>start_line) { if(end_line > start_line) {
auto line=files[c].substr(start_line, end_line-start_line); auto line = files[c].substr(start_line, end_line - start_line);
std::smatch sm; std::smatch sm;
const static std::regex set_regex(R"(^ *set *\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\) *\r?$)", std::regex::icase); const static std::regex set_regex(R"(^ *set *\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\) *\r?$)", std::regex::icase);
const static std::regex project_regex(R"(^ *project *\( *([^ ]+).*\) *\r?$)", std::regex::icase); const static std::regex project_regex(R"(^ *project *\( *([^ ]+).*\) *\r?$)", std::regex::icase);
if(std::regex_match(line, sm, set_regex)) { if(std::regex_match(line, sm, set_regex)) {
auto data=sm[2].str(); auto data = sm[2].str();
while(data.size()>0 && data.back()==' ') while(data.size() > 0 && data.back() == ' ')
data.pop_back(); data.pop_back();
parse_variable_parameters(data); parse_variable_parameters(data);
variables[sm[1].str()]=data; variables[sm[1].str()] = data;
} }
else if(std::regex_match(line, sm, project_regex)) { else if(std::regex_match(line, sm, project_regex)) {
auto data=sm[1].str(); auto data = sm[1].str();
parse_variable_parameters(data); parse_variable_parameters(data);
variables["CMAKE_PROJECT_NAME"]=data; //TODO: is this variable deprecated/non-standard? variables["CMAKE_PROJECT_NAME"] = data; //TODO: is this variable deprecated/non-standard?
variables["PROJECT_NAME"]=data; variables["PROJECT_NAME"] = data;
} }
if(std::regex_match(line, sm, function_regex)) { if(std::regex_match(line, sm, function_regex)) {
auto data=sm[1].str(); auto data = sm[1].str();
while(data.size()>0 && data.back()==' ') while(data.size() > 0 && data.back() == ' ')
data.pop_back(); data.pop_back();
auto parameters=get_function_parameters(data); auto parameters = get_function_parameters(data);
functions.emplace_back(paths[c], parameters); functions.emplace_back(paths[c], parameters);
} }
} }
pos=end_line+1; pos = end_line + 1;
} }
} }
return functions; return functions;

16
src/cmake.h

@ -1,19 +1,19 @@
#pragma once #pragma once
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <vector>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector>
class CMake { class CMake {
public: public:
CMake(const boost::filesystem::path &path); CMake(const boost::filesystem::path &path);
boost::filesystem::path project_path; boost::filesystem::path project_path;
bool update_default_build(const boost::filesystem::path &default_build_path, bool force=false); bool update_default_build(const boost::filesystem::path &default_build_path, bool force = false);
bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force=false); bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force = false);
boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
private: private:
std::vector<boost::filesystem::path> paths; std::vector<boost::filesystem::path> paths;
std::vector<std::string> files; std::vector<std::string> files;
@ -25,6 +25,6 @@ private:
void parse_variable_parameters(std::string &data); void parse_variable_parameters(std::string &data);
void parse(); void parse();
std::vector<std::string> get_function_parameters(std::string &data); std::vector<std::string> get_function_parameters(std::string &data);
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > get_functions_parameters(const std::string &name); std::vector<std::pair<boost::filesystem::path, std::vector<std::string>>> get_functions_parameters(const std::string &name);
bool parsed=false; bool parsed = false;
}; };

151
src/compile_commands.cc

@ -5,101 +5,102 @@
std::vector<std::string> CompileCommands::Command::parameter_values(const std::string &parameter_name) const { std::vector<std::string> CompileCommands::Command::parameter_values(const std::string &parameter_name) const {
std::vector<std::string> parameter_values; std::vector<std::string> parameter_values;
bool found_argument=false; bool found_argument = false;
for(auto &parameter: parameters) { for(auto &parameter : parameters) {
if(found_argument) { if(found_argument) {
parameter_values.emplace_back(parameter); parameter_values.emplace_back(parameter);
found_argument=false; found_argument = false;
} }
else if(parameter==parameter_name) else if(parameter == parameter_name)
found_argument=true; found_argument = true;
} }
return parameter_values; return parameter_values;
} }
CompileCommands::CompileCommands(const boost::filesystem::path &build_path) { CompileCommands::CompileCommands(const boost::filesystem::path &build_path) {
try { try {
boost::property_tree::ptree root_pt; boost::property_tree::ptree root_pt;
boost::property_tree::json_parser::read_json((build_path/"compile_commands.json").string(), root_pt); boost::property_tree::json_parser::read_json((build_path / "compile_commands.json").string(), root_pt);
auto commands_pt=root_pt.get_child(""); auto commands_pt = root_pt.get_child("");
for(auto &command: commands_pt) { for(auto &command : commands_pt) {
boost::filesystem::path directory=command.second.get<std::string>("directory"); boost::filesystem::path directory = command.second.get<std::string>("directory");
auto parameters_str=command.second.get<std::string>("command"); auto parameters_str = command.second.get<std::string>("command");
boost::filesystem::path file=command.second.get<std::string>("file"); boost::filesystem::path file = command.second.get<std::string>("file");
std::vector<std::string> parameters; std::vector<std::string> parameters;
bool backslash=false; bool backslash = false;
bool single_quote=false; bool single_quote = false;
bool double_quote=false; bool double_quote = false;
size_t parameter_start_pos=std::string::npos; size_t parameter_start_pos = std::string::npos;
size_t parameter_size=0; size_t parameter_size = 0;
auto add_parameter=[&parameters, &parameters_str, &parameter_start_pos, &parameter_size] { auto add_parameter = [&parameters, &parameters_str, &parameter_start_pos, &parameter_size] {
auto parameter=parameters_str.substr(parameter_start_pos, parameter_size); auto parameter = parameters_str.substr(parameter_start_pos, parameter_size);
// Remove escaping // Remove escaping
for(size_t c=0;c<parameter.size()-1;++c) { for(size_t c = 0; c < parameter.size() - 1; ++c) {
if(parameter[c]=='\\') if(parameter[c] == '\\')
parameter.replace(c, 2, std::string()+parameter[c+1]); parameter.replace(c, 2, std::string() + parameter[c + 1]);
} }
parameters.emplace_back(parameter); parameters.emplace_back(parameter);
}; };
for(size_t c=0;c<parameters_str.size();++c) { for(size_t c = 0; c < parameters_str.size(); ++c) {
if(backslash) if(backslash)
backslash=false; backslash = false;
else if(parameters_str[c]=='\\') else if(parameters_str[c] == '\\')
backslash=true; backslash = true;
else if((parameters_str[c]==' ' || parameters_str[c]=='\t') && !backslash && !single_quote && !double_quote) { else if((parameters_str[c] == ' ' || parameters_str[c] == '\t') && !backslash && !single_quote && !double_quote) {
if(parameter_start_pos!=std::string::npos) { if(parameter_start_pos != std::string::npos) {
add_parameter(); add_parameter();
parameter_start_pos=std::string::npos; parameter_start_pos = std::string::npos;
parameter_size=0; parameter_size = 0;
} }
continue; continue;
} }
else if(parameters_str[c]=='\'' && !backslash && !double_quote) { else if(parameters_str[c] == '\'' && !backslash && !double_quote) {
single_quote=!single_quote; single_quote = !single_quote;
continue; continue;
} }
else if(parameters_str[c]=='\"' && !backslash && !single_quote) { else if(parameters_str[c] == '\"' && !backslash && !single_quote) {
double_quote=!double_quote; double_quote = !double_quote;
continue; continue;
} }
if(parameter_start_pos==std::string::npos) if(parameter_start_pos == std::string::npos)
parameter_start_pos=c; parameter_start_pos = c;
++parameter_size; ++parameter_size;
} }
if(parameter_start_pos!=std::string::npos) if(parameter_start_pos != std::string::npos)
add_parameter(); add_parameter();
commands.emplace_back(Command{directory, parameters, boost::filesystem::absolute(file, build_path)}); commands.emplace_back(Command{directory, parameters, boost::filesystem::absolute(file, build_path)});
} }
} }
catch(...) {} catch(...) {
}
} }
std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {
std::string default_std_argument="-std=c++1y"; std::string default_std_argument = "-std=c++1y";
std::vector<std::string> arguments; std::vector<std::string> arguments;
if(!build_path.empty()) { if(!build_path.empty()) {
clangmm::CompilationDatabase db(build_path.string()); clangmm::CompilationDatabase db(build_path.string());
if(db) { if(db) {
clangmm::CompileCommands commands(file_path.string(), db); clangmm::CompileCommands commands(file_path.string(), db);
auto cmds = commands.get_commands(); auto cmds = commands.get_commands();
for (auto &cmd : cmds) { for(auto &cmd : cmds) {
auto cmd_arguments = cmd.get_arguments(); auto cmd_arguments = cmd.get_arguments();
bool ignore_next=false; bool ignore_next = false;
for (size_t c = 1; c < cmd_arguments.size(); c++) { for(size_t c = 1; c < cmd_arguments.size(); c++) {
if(ignore_next) { if(ignore_next) {
ignore_next=false; ignore_next = false;
continue; continue;
} }
else if(cmd_arguments[c]=="-o" || cmd_arguments[c]=="-c" || else if(cmd_arguments[c] == "-o" || cmd_arguments[c] == "-c" ||
cmd_arguments[c]=="-x") { // Remove language arguments since some tools add languages not understood by clang cmd_arguments[c] == "-x") { // Remove language arguments since some tools add languages not understood by clang
ignore_next=true; ignore_next = true;
continue; continue;
} }
arguments.emplace_back(cmd_arguments[c]); arguments.emplace_back(cmd_arguments[c]);
@ -111,44 +112,44 @@ std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem:
} }
else else
arguments.emplace_back(default_std_argument); arguments.emplace_back(default_std_argument);
auto clang_version_string=clangmm::to_string(clang_getClangVersion()); auto clang_version_string = clangmm::to_string(clang_getClangVersion());
const static std::regex clang_version_regex(R"(^[A-Za-z ]+([0-9.]+).*$)"); const static std::regex clang_version_regex(R"(^[A-Za-z ]+([0-9.]+).*$)");
std::smatch sm; std::smatch sm;
if(std::regex_match(clang_version_string, sm, clang_version_regex)) { if(std::regex_match(clang_version_string, sm, clang_version_regex)) {
auto clang_version=sm[1].str(); auto clang_version = sm[1].str();
arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include"); arguments.emplace_back("-I/usr/lib/clang/" + clang_version + "/include");
arguments.emplace_back("-I/usr/lib64/clang/"+clang_version+"/include"); // For Fedora arguments.emplace_back("-I/usr/lib64/clang/" + clang_version + "/include"); // For Fedora
#if defined(__APPLE__) && CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR<32 // TODO: remove during 2018 if llvm3.7 is no longer in homebrew (CINDEX_VERSION_MINOR=32 equals clang-3.8 I think) #if defined(__APPLE__) && CINDEX_VERSION_MAJOR == 0 && CINDEX_VERSION_MINOR < 32 // TODO: remove during 2018 if llvm3.7 is no longer in homebrew (CINDEX_VERSION_MINOR=32 equals clang-3.8 I think)
arguments.emplace_back("-I/usr/local/Cellar/llvm/"+clang_version+"/lib/clang/"+clang_version+"/include"); arguments.emplace_back("-I/usr/local/Cellar/llvm/" + clang_version + "/lib/clang/" + clang_version + "/include");
arguments.emplace_back("-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1"); arguments.emplace_back("-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1");
arguments.emplace_back("-I/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1"); //Added for OS X 10.11 arguments.emplace_back("-I/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1"); //Added for OS X 10.11
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
auto env_msystem_prefix=std::getenv("MSYSTEM_PREFIX"); auto env_msystem_prefix = std::getenv("MSYSTEM_PREFIX");
if(env_msystem_prefix!=nullptr) if(env_msystem_prefix != nullptr)
arguments.emplace_back("-I"+(boost::filesystem::path(env_msystem_prefix)/"lib/clang"/clang_version/"include").string()); arguments.emplace_back("-I" + (boost::filesystem::path(env_msystem_prefix) / "lib/clang" / clang_version / "include").string());
#endif #endif
} }
arguments.emplace_back("-fretain-comments-from-system-headers"); arguments.emplace_back("-fretain-comments-from-system-headers");
auto extension=file_path.extension().string(); auto extension = file_path.extension().string();
bool is_header=CompileCommands::is_header(file_path) || extension.empty(); // Include std C++ headers that are without extensions bool is_header = CompileCommands::is_header(file_path) || extension.empty(); // Include std C++ headers that are without extensions
if(is_header) { if(is_header) {
arguments.emplace_back("-Wno-pragma-once-outside-header"); arguments.emplace_back("-Wno-pragma-once-outside-header");
arguments.emplace_back("-Wno-pragma-system-header-outside-header"); arguments.emplace_back("-Wno-pragma-system-header-outside-header");
arguments.emplace_back("-Wno-include-next-outside-header"); arguments.emplace_back("-Wno-include-next-outside-header");
} }
if(extension==".cu" || extension==".cuh") { if(extension == ".cu" || extension == ".cuh") {
arguments.emplace_back("-xcuda"); arguments.emplace_back("-xcuda");
arguments.emplace_back("-D__CUDACC__"); arguments.emplace_back("-D__CUDACC__");
arguments.emplace_back("-include"); arguments.emplace_back("-include");
arguments.emplace_back("cuda_runtime.h"); arguments.emplace_back("cuda_runtime.h");
arguments.emplace_back("-ferror-limit=1000"); // CUDA headers redeclares some std functions arguments.emplace_back("-ferror-limit=1000"); // CUDA headers redeclares some std functions
} }
else if(extension==".cl") { else if(extension == ".cl") {
arguments.emplace_back("-xcl"); arguments.emplace_back("-xcl");
arguments.emplace_back("-cl-std=CL2.0"); arguments.emplace_back("-cl-std=CL2.0");
arguments.emplace_back("-Xclang"); arguments.emplace_back("-Xclang");
@ -157,7 +158,7 @@ std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem:
} }
else if(is_header) else if(is_header)
arguments.emplace_back("-xc++"); arguments.emplace_back("-xc++");
if(!build_path.empty()) { if(!build_path.empty()) {
arguments.emplace_back("-working-directory"); arguments.emplace_back("-working-directory");
arguments.emplace_back(build_path.string()); arguments.emplace_back(build_path.string());
@ -168,9 +169,9 @@ std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem:
bool CompileCommands::is_header(const boost::filesystem::path &path) { bool CompileCommands::is_header(const boost::filesystem::path &path) {
auto ext = path.extension(); auto ext = path.extension();
if(ext == ".h" || // c headers if(ext == ".h" || // c headers
ext == ".hh" || ext == ".hp" || ext == ".hpp" || ext == ".h++" || ext == ".tcc" || // c++ headers ext == ".hh" || ext == ".hp" || ext == ".hpp" || ext == ".h++" || ext == ".tcc" || // c++ headers
ext == ".cuh") // CUDA headers ext == ".cuh") // CUDA headers
return true; return true;
else else
return false; return false;
@ -178,10 +179,10 @@ bool CompileCommands::is_header(const boost::filesystem::path &path) {
bool CompileCommands::is_source(const boost::filesystem::path &path) { bool CompileCommands::is_source(const boost::filesystem::path &path) {
auto ext = path.extension(); auto ext = path.extension();
if(ext == ".c" || // c sources if(ext == ".c" || // c sources
ext == ".cpp" || ext == ".cxx" || ext == ".cc" || ext == ".C" || ext == ".c++" || // c++ sources ext == ".cpp" || ext == ".cxx" || ext == ".cc" || ext == ".C" || ext == ".c++" || // c++ sources
ext == ".cu" || // CUDA sources ext == ".cu" || // CUDA sources
ext == ".cl") // OpenCL sources ext == ".cl") // OpenCL sources
return true; return true;
else else
return false; return false;

10
src/compile_commands.h

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <vector>
#include <string> #include <string>
#include <vector>
class CompileCommands { class CompileCommands {
public: public:
@ -10,16 +10,16 @@ public:
boost::filesystem::path directory; boost::filesystem::path directory;
std::vector<std::string> parameters; std::vector<std::string> parameters;
boost::filesystem::path file; boost::filesystem::path file;
std::vector<std::string> parameter_values(const std::string &parameter_name) const; std::vector<std::string> parameter_values(const std::string &parameter_name) const;
}; };
CompileCommands(const boost::filesystem::path &build_path); CompileCommands(const boost::filesystem::path &build_path);
std::vector<Command> commands; std::vector<Command> commands;
/// Return arguments for the given file using libclangmm /// Return arguments for the given file using libclangmm
static std::vector<std::string> get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); static std::vector<std::string> get_arguments(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
static bool is_header(const boost::filesystem::path &path); static bool is_header(const boost::filesystem::path &path);
static bool is_source(const boost::filesystem::path &path); static bool is_source(const boost::filesystem::path &path);
}; };

166
src/config.cc

@ -1,20 +1,20 @@
#include "config.h" #include "config.h"
#include <exception>
#include "files.h" #include "files.h"
#include <iostream>
#include "filesystem.h" #include "filesystem.h"
#include "terminal.h" #include "terminal.h"
#include <algorithm> #include <algorithm>
#include <exception>
#include <iostream>
Config::Config() { Config::Config() {
home_path=filesystem::get_home_path(); home_path = filesystem::get_home_path();
if(home_path.empty()) if(home_path.empty())
throw std::runtime_error("Could not find home path"); throw std::runtime_error("Could not find home path");
home_juci_path=home_path/".juci"; home_juci_path = home_path / ".juci";
} }
void Config::load() { void Config::load() {
auto config_json = (home_juci_path/"config"/"config.json").string(); // This causes some redundant copies, but assures windows support auto config_json = (home_juci_path / "config" / "config.json").string(); // This causes some redundant copies, but assures windows support
boost::property_tree::ptree cfg; boost::property_tree::ptree cfg;
try { try {
find_or_create_config_files(); find_or_create_config_files();
@ -23,8 +23,8 @@ void Config::load() {
read(cfg); read(cfg);
} }
catch(const std::exception &e) { catch(const std::exception &e) {
dispatcher.post([config_json, e_what=std::string(e.what())] { dispatcher.post([config_json, e_what = std::string(e.what())] {
::Terminal::get().print("Error: could not parse "+config_json+": "+e_what+"\n", true); ::Terminal::get().print("Error: could not parse " + config_json + ": " + e_what + "\n", true);
}); });
std::stringstream ss; std::stringstream ss;
ss << default_config_file; ss << default_config_file;
@ -34,67 +34,67 @@ void Config::load() {
} }
void Config::find_or_create_config_files() { void Config::find_or_create_config_files() {
auto config_dir = home_juci_path/"config"; auto config_dir = home_juci_path / "config";
auto config_json = config_dir/"config.json"; auto config_json = config_dir / "config.json";
boost::filesystem::create_directories(config_dir); // io exp captured by calling method boost::filesystem::create_directories(config_dir); // io exp captured by calling method
if (!boost::filesystem::exists(config_json)) if(!boost::filesystem::exists(config_json))
filesystem::write(config_json, default_config_file); filesystem::write(config_json, default_config_file);
auto juci_style_path = home_juci_path/"styles"; auto juci_style_path = home_juci_path / "styles";
boost::filesystem::create_directories(juci_style_path); // io exp captured by calling method boost::filesystem::create_directories(juci_style_path); // io exp captured by calling method
juci_style_path/="juci-light.xml"; juci_style_path /= "juci-light.xml";
if(!boost::filesystem::exists(juci_style_path)) if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_light_style); filesystem::write(juci_style_path, juci_light_style);
juci_style_path=juci_style_path.parent_path(); juci_style_path = juci_style_path.parent_path();
juci_style_path/="juci-dark.xml"; juci_style_path /= "juci-dark.xml";
if(!boost::filesystem::exists(juci_style_path)) if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_dark_style); filesystem::write(juci_style_path, juci_dark_style);
juci_style_path=juci_style_path.parent_path(); juci_style_path = juci_style_path.parent_path();
juci_style_path/="juci-dark-blue.xml"; juci_style_path /= "juci-dark-blue.xml";
if(!boost::filesystem::exists(juci_style_path)) if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_dark_blue_style); filesystem::write(juci_style_path, juci_dark_blue_style);
} }
void Config::update(boost::property_tree::ptree &cfg) { void Config::update(boost::property_tree::ptree &cfg) {
boost::property_tree::ptree default_cfg; boost::property_tree::ptree default_cfg;
bool cfg_ok=true; bool cfg_ok = true;
if(cfg.get<std::string>("version")!=JUCI_VERSION) { if(cfg.get<std::string>("version") != JUCI_VERSION) {
std::stringstream ss; std::stringstream ss;
ss << default_config_file; ss << default_config_file;
boost::property_tree::read_json(ss, default_cfg); boost::property_tree::read_json(ss, default_cfg);
cfg_ok=false; cfg_ok = false;
auto it_version=cfg.find("version"); auto it_version = cfg.find("version");
if(it_version!=cfg.not_found()) { if(it_version != cfg.not_found()) {
make_version_dependent_corrections(cfg, default_cfg, it_version->second.data()); make_version_dependent_corrections(cfg, default_cfg, it_version->second.data());
it_version->second.data()=JUCI_VERSION; it_version->second.data() = JUCI_VERSION;
} }
auto style_path=home_juci_path/"styles"; auto style_path = home_juci_path / "styles";
filesystem::write(style_path/"juci-light.xml", juci_light_style); filesystem::write(style_path / "juci-light.xml", juci_light_style);
filesystem::write(style_path/"juci-dark.xml", juci_dark_style); filesystem::write(style_path / "juci-dark.xml", juci_dark_style);
filesystem::write(style_path/"juci-dark-blue.xml", juci_dark_blue_style); filesystem::write(style_path / "juci-dark-blue.xml", juci_dark_blue_style);
} }
else else
return; return;
cfg_ok&=add_missing_nodes(cfg, default_cfg); cfg_ok &= add_missing_nodes(cfg, default_cfg);
cfg_ok&=remove_deprecated_nodes(cfg, default_cfg); cfg_ok &= remove_deprecated_nodes(cfg, default_cfg);
if(!cfg_ok) if(!cfg_ok)
boost::property_tree::write_json((home_juci_path/"config"/"config.json").string(), cfg); boost::property_tree::write_json((home_juci_path / "config" / "config.json").string(), cfg);
} }
void Config::make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version) { void Config::make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version) {
auto &keybindings_cfg=cfg.get_child("keybindings"); auto &keybindings_cfg = cfg.get_child("keybindings");
try { try {
if(version<="1.2.4") { if(version <= "1.2.4") {
auto it_file_print=keybindings_cfg.find("print"); auto it_file_print = keybindings_cfg.find("print");
if(it_file_print!=keybindings_cfg.not_found() && it_file_print->second.data()=="<primary>p") { if(it_file_print != keybindings_cfg.not_found() && it_file_print->second.data() == "<primary>p") {
dispatcher.post([] { dispatcher.post([] {
::Terminal::get().print("Preference change: keybindings.print set to \"\"\n"); ::Terminal::get().print("Preference change: keybindings.print set to \"\"\n");
}); });
it_file_print->second.data()=""; it_file_print->second.data() = "";
} }
} }
} }
@ -104,37 +104,37 @@ void Config::make_version_dependent_corrections(boost::property_tree::ptree &cfg
} }
bool Config::add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) { bool Config::add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) {
if(parent_path.size()>0) if(parent_path.size() > 0)
parent_path+="."; parent_path += ".";
bool unchanged=true; bool unchanged = true;
for(auto &node: default_cfg) { for(auto &node : default_cfg) {
auto path=parent_path+node.first; auto path = parent_path + node.first;
try { try {
cfg.get<std::string>(path); cfg.get<std::string>(path);
} }
catch(const std::exception &e) { catch(const std::exception &e) {
cfg.add(path, node.second.data()); cfg.add(path, node.second.data());
unchanged=false; unchanged = false;
} }
unchanged&=add_missing_nodes(cfg, node.second, path); unchanged &= add_missing_nodes(cfg, node.second, path);
} }
return unchanged; return unchanged;
} }
bool Config::remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) { bool Config::remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) {
if(parent_path.size()>0) if(parent_path.size() > 0)
parent_path+="."; parent_path += ".";
bool unchanged=true; bool unchanged = true;
for(auto it=cfg.begin();it!=cfg.end();) { for(auto it = cfg.begin(); it != cfg.end();) {
auto path=parent_path+it->first; auto path = parent_path + it->first;
try { try {
default_cfg.get<std::string>(path); default_cfg.get<std::string>(path);
unchanged&=remove_deprecated_nodes(it->second, default_cfg, path); unchanged &= remove_deprecated_nodes(it->second, default_cfg, path);
++it; ++it;
} }
catch(const std::exception &e) { catch(const std::exception &e) {
it=cfg.erase(it); it = cfg.erase(it);
unchanged=false; unchanged = false;
} }
} }
return unchanged; return unchanged;
@ -142,21 +142,21 @@ bool Config::remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boo
void Config::read(const boost::property_tree::ptree &cfg) { void Config::read(const boost::property_tree::ptree &cfg) {
auto keybindings_pt = cfg.get_child("keybindings"); auto keybindings_pt = cfg.get_child("keybindings");
for (auto &i : keybindings_pt) { for(auto &i : keybindings_pt) {
menu.keys[i.first] = i.second.get_value<std::string>(); menu.keys[i.first] = i.second.get_value<std::string>();
} }
auto source_json = cfg.get_child("source"); auto source_json = cfg.get_child("source");
source.style=source_json.get<std::string>("style"); source.style = source_json.get<std::string>("style");
source.font=source_json.get<std::string>("font"); source.font = source_json.get<std::string>("font");
source.cleanup_whitespace_characters=source_json.get<bool>("cleanup_whitespace_characters"); 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_whitespace_characters = source_json.get<std::string>("show_whitespace_characters");
source.format_style_on_save=source_json.get<bool>("format_style_on_save"); source.format_style_on_save = source_json.get<bool>("format_style_on_save");
source.format_style_on_save_if_style_file_found=source_json.get<bool>("format_style_on_save_if_style_file_found"); source.format_style_on_save_if_style_file_found = source_json.get<bool>("format_style_on_save_if_style_file_found");
source.smart_brackets=source_json.get<bool>("smart_brackets"); source.smart_brackets = source_json.get<bool>("smart_brackets");
source.smart_inserts=source_json.get<bool>("smart_inserts"); source.smart_inserts = source_json.get<bool>("smart_inserts");
if(source.smart_inserts) if(source.smart_inserts)
source.smart_brackets=true; source.smart_brackets = true;
source.show_map = source_json.get<bool>("show_map"); source.show_map = source_json.get<bool>("show_map");
source.map_font_size = source_json.get<std::string>("map_font_size"); source.map_font_size = source_json.get<std::string>("map_font_size");
source.show_git_diff = source_json.get<bool>("show_git_diff"); source.show_git_diff = source_json.get<bool>("show_git_diff");
@ -175,30 +175,30 @@ void Config::read(const boost::property_tree::ptree &cfg) {
source.auto_reload_changed_files = source_json.get<bool>("auto_reload_changed_files"); source.auto_reload_changed_files = source_json.get<bool>("auto_reload_changed_files");
source.clang_format_style = source_json.get<std::string>("clang_format_style"); source.clang_format_style = source_json.get<std::string>("clang_format_style");
source.clang_usages_threads = static_cast<unsigned>(source_json.get<int>("clang_usages_threads")); source.clang_usages_threads = static_cast<unsigned>(source_json.get<int>("clang_usages_threads"));
auto pt_doc_search=cfg.get_child("documentation_searches"); auto pt_doc_search = cfg.get_child("documentation_searches");
for(auto &pt_doc_search_lang: pt_doc_search) { for(auto &pt_doc_search_lang : pt_doc_search) {
source.documentation_searches[pt_doc_search_lang.first].separator=pt_doc_search_lang.second.get<std::string>("separator"); source.documentation_searches[pt_doc_search_lang.first].separator = pt_doc_search_lang.second.get<std::string>("separator");
auto &queries=source.documentation_searches.find(pt_doc_search_lang.first)->second.queries; auto &queries = source.documentation_searches.find(pt_doc_search_lang.first)->second.queries;
for(auto &i: pt_doc_search_lang.second.get_child("queries")) { for(auto &i : pt_doc_search_lang.second.get_child("queries")) {
queries[i.first]=i.second.get_value<std::string>(); queries[i.first] = i.second.get_value<std::string>();
} }
} }
window.theme_name=cfg.get<std::string>("gtk_theme.name"); window.theme_name = cfg.get<std::string>("gtk_theme.name");
window.theme_variant=cfg.get<std::string>("gtk_theme.variant"); window.theme_variant = cfg.get<std::string>("gtk_theme.variant");
window.version = cfg.get<std::string>("version"); window.version = cfg.get<std::string>("version");
project.default_build_path=cfg.get<std::string>("project.default_build_path"); project.default_build_path = cfg.get<std::string>("project.default_build_path");
project.debug_build_path=cfg.get<std::string>("project.debug_build_path"); project.debug_build_path = cfg.get<std::string>("project.debug_build_path");
project.cmake.command=cfg.get<std::string>("project.cmake.command"); project.cmake.command = cfg.get<std::string>("project.cmake.command");
project.cmake.compile_command=cfg.get<std::string>("project.cmake.compile_command"); project.cmake.compile_command = cfg.get<std::string>("project.cmake.compile_command");
project.meson.command=cfg.get<std::string>("project.meson.command"); project.meson.command = cfg.get<std::string>("project.meson.command");
project.meson.compile_command=cfg.get<std::string>("project.meson.compile_command"); project.meson.compile_command = cfg.get<std::string>("project.meson.compile_command");
project.save_on_compile_or_run=cfg.get<bool>("project.save_on_compile_or_run"); project.save_on_compile_or_run = cfg.get<bool>("project.save_on_compile_or_run");
project.clear_terminal_on_compile=cfg.get<bool>("project.clear_terminal_on_compile"); project.clear_terminal_on_compile = cfg.get<bool>("project.clear_terminal_on_compile");
project.ctags_command=cfg.get<std::string>("project.ctags_command"); project.ctags_command = cfg.get<std::string>("project.ctags_command");
project.python_command=cfg.get<std::string>("project.python_command"); project.python_command = cfg.get<std::string>("project.python_command");
terminal.history_size=cfg.get<int>("terminal.history_size"); terminal.history_size = cfg.get<int>("terminal.history_size");
terminal.font=cfg.get<std::string>("terminal.font"); terminal.font = cfg.get<std::string>("terminal.font");
} }

46
src/config.h

@ -1,11 +1,11 @@
#pragma once #pragma once
#include <boost/property_tree/json_parser.hpp> #include "dispatcher.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <unordered_map> #include <boost/property_tree/json_parser.hpp>
#include <string> #include <string>
#include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "dispatcher.h"
class Config { class Config {
public: public:
@ -13,20 +13,20 @@ public:
public: public:
std::unordered_map<std::string, std::string> keys; std::unordered_map<std::string, std::string> keys;
}; };
class Window { class Window {
public: public:
std::string theme_name; std::string theme_name;
std::string theme_variant; std::string theme_variant;
std::string version; std::string version;
}; };
class Terminal { class Terminal {
public: public:
int history_size; int history_size;
std::string font; std::string font;
}; };
class Project { class Project {
public: public:
class CMake { class CMake {
@ -39,7 +39,7 @@ public:
std::string command; std::string command;
std::string compile_command; std::string compile_command;
}; };
std::string default_build_path; std::string default_build_path;
std::string debug_build_path; std::string debug_build_path;
CMake cmake; CMake cmake;
@ -49,7 +49,7 @@ public:
std::string ctags_command; std::string ctags_command;
std::string python_command; std::string python_command;
}; };
class Source { class Source {
public: public:
class DocumentationSearch { class DocumentationSearch {
@ -57,27 +57,27 @@ public:
std::string separator; std::string separator;
std::unordered_map<std::string, std::string> queries; std::unordered_map<std::string, std::string> queries;
}; };
std::string style; std::string style;
std::string font; std::string font;
std::string spellcheck_language; std::string spellcheck_language;
bool cleanup_whitespace_characters; bool cleanup_whitespace_characters;
std::string show_whitespace_characters; std::string show_whitespace_characters;
bool format_style_on_save; bool format_style_on_save;
bool format_style_on_save_if_style_file_found; bool format_style_on_save_if_style_file_found;
bool smart_brackets; bool smart_brackets;
bool smart_inserts; bool smart_inserts;
bool show_map; bool show_map;
std::string map_font_size; std::string map_font_size;
bool show_git_diff; bool show_git_diff;
bool show_background_pattern; bool show_background_pattern;
bool show_right_margin; bool show_right_margin;
unsigned right_margin_position; unsigned right_margin_position;
bool auto_tab_char_and_size; bool auto_tab_char_and_size;
char default_tab_char; char default_tab_char;
unsigned default_tab_size; unsigned default_tab_size;
@ -87,39 +87,41 @@ public:
bool show_line_numbers; bool show_line_numbers;
bool enable_multiple_cursors; bool enable_multiple_cursors;
bool auto_reload_changed_files; bool auto_reload_changed_files;
std::string clang_format_style; std::string clang_format_style;
unsigned clang_usages_threads; unsigned clang_usages_threads;
std::unordered_map<std::string, DocumentationSearch> documentation_searches; std::unordered_map<std::string, DocumentationSearch> documentation_searches;
}; };
private: private:
Config(); Config();
public: public:
static Config &get() { static Config &get() {
static Config singleton; static Config singleton;
return singleton; return singleton;
} }
void load(); void load();
Menu menu; Menu menu;
Window window; Window window;
Terminal terminal; Terminal terminal;
Project project; Project project;
Source source; Source source;
boost::filesystem::path home_path; boost::filesystem::path home_path;
boost::filesystem::path home_juci_path; boost::filesystem::path home_juci_path;
private: private:
/// Used to dispatch Terminal outputs after juCi++ GUI setup and configuration /// Used to dispatch Terminal outputs after juCi++ GUI setup and configuration
Dispatcher dispatcher; Dispatcher dispatcher;
void find_or_create_config_files(); void find_or_create_config_files();
void update(boost::property_tree::ptree &cfg); void update(boost::property_tree::ptree &cfg);
void make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version); void make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version);
bool add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path=""); bool add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path = "");
bool remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path=""); bool remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path = "");
void read(const boost::property_tree::ptree &cfg); void read(const boost::property_tree::ptree &cfg);
}; };

202
src/ctags.cc

@ -1,38 +1,38 @@
#include "ctags.h" #include "ctags.h"
#include "config.h" #include "config.h"
#include "terminal.h"
#include "project_build.h"
#include "filesystem.h" #include "filesystem.h"
#include "project_build.h"
#include "terminal.h"
#include <climits>
#include <iostream> #include <iostream>
#include <vector>
#include <regex> #include <regex>
#include <climits> #include <vector>
std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream> > Ctags::get_result(const boost::filesystem::path &path) { std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream>> Ctags::get_result(const boost::filesystem::path &path) {
auto build=Project::Build::create(path); auto build = Project::Build::create(path);
auto run_path=build->project_path; auto run_path = build->project_path;
std::string exclude; std::string exclude;
if(!run_path.empty()) { if(!run_path.empty()) {
auto relative_default_path=filesystem::get_relative_path(build->get_default_path(), run_path); auto relative_default_path = filesystem::get_relative_path(build->get_default_path(), run_path);
if(!relative_default_path.empty()) if(!relative_default_path.empty())
exclude+=" --exclude="+relative_default_path.string(); exclude += " --exclude=" + relative_default_path.string();
auto relative_debug_path=filesystem::get_relative_path(build->get_debug_path(), run_path); auto relative_debug_path = filesystem::get_relative_path(build->get_debug_path(), run_path);
if(!relative_debug_path.empty()) if(!relative_debug_path.empty())
exclude+=" --exclude="+relative_debug_path.string(); exclude += " --exclude=" + relative_debug_path.string();
} }
else { else {
boost::system::error_code ec; boost::system::error_code ec;
if(boost::filesystem::is_directory(path, ec) || ec) if(boost::filesystem::is_directory(path, ec) || ec)
run_path=path; run_path = path;
else else
run_path=path.parent_path(); run_path = path.parent_path();
} }
std::stringstream stdin_stream; std::stringstream stdin_stream;
//TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below //TODO: when debian stable gets newer g++ version that supports move on streams, remove unique_ptr below
auto stdout_stream=std::make_unique<std::stringstream>(); auto stdout_stream = std::make_unique<std::stringstream>();
auto command=Config::get().project.ctags_command+exclude+" --fields=ns --sort=foldcase -I \"override noexcept\" -f - -R *"; auto command = Config::get().project.ctags_command + exclude + " --fields=ns --sort=foldcase -I \"override noexcept\" -f - -R *";
Terminal::get().process(stdin_stream, *stdout_stream, command, run_path); Terminal::get().process(stdin_stream, *stdout_stream, command, run_path);
return {run_path, std::move(stdout_stream)}; return {run_path, std::move(stdout_stream)};
} }
@ -41,166 +41,166 @@ Ctags::Location Ctags::get_location(const std::string &line, bool markup) {
Location location; Location location;
#ifdef _WIN32 #ifdef _WIN32
auto line_fixed=line; auto line_fixed = line;
if(!line_fixed.empty() && line_fixed.back()=='\r') if(!line_fixed.empty() && line_fixed.back() == '\r')
line_fixed.pop_back(); line_fixed.pop_back();
#else #else
auto &line_fixed=line; auto &line_fixed = line;
#endif #endif
const static std::regex regex(R"(^([^\t]+)\t([^\t]+)\t(?:/\^)?([ \t]*)(.+?)(\$/)?;"\tline:([0-9]+)\t?[a-zA-Z]*:?(.*)$)"); const static std::regex regex(R"(^([^\t]+)\t([^\t]+)\t(?:/\^)?([ \t]*)(.+?)(\$/)?;"\tline:([0-9]+)\t?[a-zA-Z]*:?(.*)$)");
std::smatch sm; std::smatch sm;
if(std::regex_match(line_fixed, sm, regex)) { if(std::regex_match(line_fixed, sm, regex)) {
location.symbol=sm[1].str(); location.symbol = sm[1].str();
//fix location.symbol for operators //fix location.symbol for operators
if(9<location.symbol.size() && location.symbol[8]==' ' && location.symbol.compare(0, 8, "operator")==0) { if(9 < location.symbol.size() && location.symbol[8] == ' ' && location.symbol.compare(0, 8, "operator") == 0) {
auto &chr=location.symbol[9]; auto &chr = location.symbol[9];
if(!((chr>='a' && chr<='z') || (chr>='A' && chr<='Z') || (chr>='0' && chr<='9') || chr=='_')) if(!((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr == '_'))
location.symbol.erase(8, 1); location.symbol.erase(8, 1);
} }
location.file_path=sm[2].str(); location.file_path = sm[2].str();
location.source=sm[4].str(); location.source = sm[4].str();
try { try {
location.line=std::stoul(sm[6])-1; location.line = std::stoul(sm[6]) - 1;
} }
catch(const std::exception&) { catch(const std::exception &) {
location.line=0; location.line = 0;
} }
location.scope=sm[7].str(); location.scope = sm[7].str();
if(!sm[5].str().empty()) { if(!sm[5].str().empty()) {
location.index=sm[3].str().size(); location.index = sm[3].str().size();
size_t pos=location.source.find(location.symbol); size_t pos = location.source.find(location.symbol);
if(pos!=std::string::npos) if(pos != std::string::npos)
location.index+=pos; location.index += pos;
if(markup) { if(markup) {
location.source=Glib::Markup::escape_text(location.source); location.source = Glib::Markup::escape_text(location.source);
auto symbol=Glib::Markup::escape_text(location.symbol); auto symbol = Glib::Markup::escape_text(location.symbol);
pos=-1; pos = -1;
while((pos=location.source.find(symbol, pos+1))!=std::string::npos) { while((pos = location.source.find(symbol, pos + 1)) != std::string::npos) {
location.source.insert(pos+symbol.size(), "</b>"); location.source.insert(pos + symbol.size(), "</b>");
location.source.insert(pos, "<b>"); location.source.insert(pos, "<b>");
pos+=7+symbol.size(); pos += 7 + symbol.size();
} }
} }
} }
else { else {
location.index=0; location.index = 0;
location.source=location.symbol; location.source = location.symbol;
if(markup) if(markup)
location.source="<b>"+Glib::Markup::escape_text(location.source)+"</b>"; location.source = "<b>" + Glib::Markup::escape_text(location.source) + "</b>";
} }
} }
else else
std::cerr << "Warning (ctags): please report to the juCi++ project that the following line was not parsed:\n" << line << std::endl; std::cerr << "Warning (ctags): please report to the juCi++ project that the following line was not parsed:\n" << line << std::endl;
return location; return location;
} }
///Split up a type into its various significant parts ///Split up a type into its various significant parts
std::vector<std::string> Ctags::get_type_parts(const std::string &type) { std::vector<std::string> Ctags::get_type_parts(const std::string &type) {
std::vector<std::string> parts; std::vector<std::string> parts;
size_t text_start=-1; size_t text_start = -1;
for(size_t c=0;c<type.size();++c) { for(size_t c = 0; c < type.size(); ++c) {
auto &chr=type[c]; auto &chr = type[c];
if((chr>='0' && chr<='9') || (chr>='a' && chr<='z') || (chr>='A' && chr<='Z') || chr=='_' || chr=='~') { if((chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || chr == '_' || chr == '~') {
if(text_start==static_cast<size_t>(-1)) if(text_start == static_cast<size_t>(-1))
text_start=c; text_start = c;
} }
else { else {
if(text_start!=static_cast<size_t>(-1)) { if(text_start != static_cast<size_t>(-1)) {
parts.emplace_back(type.substr(text_start, c-text_start)); parts.emplace_back(type.substr(text_start, c - text_start));
text_start=-1; text_start = -1;
} }
if(chr=='*' || chr=='&') if(chr == '*' || chr == '&')
parts.emplace_back(std::string()+chr); parts.emplace_back(std::string() + chr);
} }
} }
return parts; return parts;
} }
std::vector<Ctags::Location> Ctags::get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type) { std::vector<Ctags::Location> Ctags::get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type) {
auto result=get_result(path); auto result = get_result(path);
result.second->seekg(0, std::ios::end); result.second->seekg(0, std::ios::end);
if(result.second->tellg()==0) if(result.second->tellg() == 0)
return std::vector<Location>(); return std::vector<Location>();
result.second->seekg(0, std::ios::beg); result.second->seekg(0, std::ios::beg);
//insert name into type //insert name into type
size_t c=0; size_t c = 0;
size_t bracket_count=0; size_t bracket_count = 0;
for(;c<type.size();++c) { for(; c < type.size(); ++c) {
if(type[c]=='<') if(type[c] == '<')
++bracket_count; ++bracket_count;
else if(type[c]=='>') else if(type[c] == '>')
--bracket_count; --bracket_count;
else if(bracket_count==0 && type[c]=='(') else if(bracket_count == 0 && type[c] == '(')
break; break;
} }
auto full_type=type; auto full_type = type;
full_type.insert(c, name); full_type.insert(c, name);
auto parts=get_type_parts(full_type); auto parts = get_type_parts(full_type);
std::string line; std::string line;
long best_score=LONG_MIN; long best_score = LONG_MIN;
std::vector<Location> best_locations; std::vector<Location> best_locations;
while(std::getline(*result.second, line)) { while(std::getline(*result.second, line)) {
if(line.size()>2048) if(line.size() > 2048)
continue; continue;
auto location=Ctags::get_location(line, false); auto location = Ctags::get_location(line, false);
if(!location.scope.empty()) { if(!location.scope.empty()) {
if(location.scope+"::"+location.symbol!=name) if(location.scope + "::" + location.symbol != name)
continue; continue;
} }
else if(location.symbol!=name) else if(location.symbol != name)
continue; continue;
location.file_path=result.first/location.file_path; location.file_path = result.first / location.file_path;
auto source_parts=get_type_parts(location.source); auto source_parts = get_type_parts(location.source);
//Find match score //Find match score
long score=0; long score = 0;
size_t source_index=0; size_t source_index = 0;
for(auto &part: parts) { for(auto &part : parts) {
bool found=false; bool found = false;
for(auto c=source_index;c<source_parts.size();++c) { for(auto c = source_index; c < source_parts.size(); ++c) {
if(part==source_parts[c]) { if(part == source_parts[c]) {
source_index=c+1; source_index = c + 1;
++score; ++score;
found=true; found = true;
break; break;
} }
} }
if(!found) if(!found)
--score; --score;
} }
size_t index=0; size_t index = 0;
for(auto &source_part: source_parts) { for(auto &source_part : source_parts) {
bool found=false; bool found = false;
for(auto c=index;c<parts.size();++c) { for(auto c = index; c < parts.size(); ++c) {
if(source_part==parts[c]) { if(source_part == parts[c]) {
index=c+1; index = c + 1;
++score; ++score;
found=true; found = true;
break; break;
} }
} }
if(!found) if(!found)
--score; --score;
} }
if(score>best_score) { if(score > best_score) {
best_score=score; best_score = score;
best_locations.clear(); best_locations.clear();
best_locations.emplace_back(location); best_locations.emplace_back(location);
} }
else if(score==best_score) else if(score == best_score)
best_locations.emplace_back(location); best_locations.emplace_back(location);
} }
return best_locations; return best_locations;
} }

11
src/ctags.h

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <string>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <sstream> #include <sstream>
#include <string>
#include <vector> #include <vector>
class Ctags { class Ctags {
@ -16,12 +16,13 @@ public:
std::string source; std::string source;
operator bool() const { return !file_path.empty(); } operator bool() const { return !file_path.empty(); }
}; };
static std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream> > get_result(const boost::filesystem::path &path); static std::pair<boost::filesystem::path, std::unique_ptr<std::stringstream>> get_result(const boost::filesystem::path &path);
static Location get_location(const std::string &line, bool markup); static Location get_location(const std::string &line, bool markup);
static std::vector<Location> get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type); static std::vector<Location> get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type);
private: private:
static std::vector<std::string> get_type_parts(const std::string &type); static std::vector<std::string> get_type_parts(const std::string &type);
}; };

429
src/debug_lldb.cc

@ -3,12 +3,12 @@
#ifdef __APPLE__ #ifdef __APPLE__
#include <cstdlib> #include <cstdlib>
#endif #endif
#include <boost/filesystem.hpp> #include "config.h"
#include <iostream>
#include "terminal.h"
#include "filesystem.h" #include "filesystem.h"
#include "process.hpp" #include "process.hpp"
#include "config.h" #include "terminal.h"
#include <boost/filesystem.hpp>
#include <iostream>
extern char **environ; extern char **environ;
@ -16,7 +16,7 @@ void log(const char *msg, void *) {
std::cout << "debugger log: " << msg << std::endl; std::cout << "debugger log: " << msg << std::endl;
} }
Debug::LLDB::LLDB(): state(lldb::StateType::eStateInvalid), buffer_size(131072) { Debug::LLDB::LLDB() : state(lldb::StateType::eStateInvalid), buffer_size(131072) {
if(!getenv("LLDB_DEBUGSERVER_PATH")) { if(!getenv("LLDB_DEBUGSERVER_PATH")) {
#ifdef __APPLE__ #ifdef __APPLE__
std::string debug_server_path("/usr/local/opt/llvm/bin/debugserver"); std::string debug_server_path("/usr/local/opt/llvm/bin/debugserver");
@ -35,211 +35,211 @@ Debug::LLDB::LLDB(): state(lldb::StateType::eStateInvalid), buffer_size(131072)
} }
} }
std::tuple<std::vector<std::string>, std::string, std::vector<std::string> > Debug::LLDB::parse_run_arguments(const std::string &command) { std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> Debug::LLDB::parse_run_arguments(const std::string &command) {
std::vector<std::string> environment; std::vector<std::string> environment;
std::string executable; std::string executable;
std::vector<std::string> arguments; std::vector<std::string> arguments;
size_t start_pos=std::string::npos; size_t start_pos = std::string::npos;
bool quote=false; bool quote = false;
bool double_quote=false; bool double_quote = false;
size_t backslash_count=0; size_t backslash_count = 0;
for(size_t c=0;c<=command.size();c++) { for(size_t c = 0; c <= command.size(); c++) {
if(c==command.size() || (!quote && !double_quote && backslash_count%2==0 && command[c]==' ')) { if(c == command.size() || (!quote && !double_quote && backslash_count % 2 == 0 && command[c] == ' ')) {
if(c>0 && start_pos!=std::string::npos) { if(c > 0 && start_pos != std::string::npos) {
auto argument=command.substr(start_pos, c-start_pos); auto argument = command.substr(start_pos, c - start_pos);
if(executable.empty()) { if(executable.empty()) {
//Check for environment variable //Check for environment variable
bool env_arg=false; bool env_arg = false;
for(size_t c=0;c<argument.size();++c) { for(size_t c = 0; c < argument.size(); ++c) {
if((argument[c]>='a' && argument[c]<='z') || (argument[c]>='A' && argument[c]<='Z') || if((argument[c] >= 'a' && argument[c] <= 'z') || (argument[c] >= 'A' && argument[c] <= 'Z') ||
(argument[c]>='0' && argument[c]<='9') || argument[c]=='_') (argument[c] >= '0' && argument[c] <= '9') || argument[c] == '_')
continue; continue;
else if(argument[c]=='=' && c+1<argument.size()) { else if(argument[c] == '=' && c + 1 < argument.size()) {
environment.emplace_back(argument.substr(0, c+1)+filesystem::unescape_argument(argument.substr(c+1))); environment.emplace_back(argument.substr(0, c + 1) + filesystem::unescape_argument(argument.substr(c + 1)));
env_arg=true; env_arg = true;
break; break;
} }
else else
break; break;
} }
if(!env_arg) if(!env_arg)
executable=filesystem::unescape_argument(argument); executable = filesystem::unescape_argument(argument);
} }
else else
arguments.emplace_back(filesystem::unescape_argument(argument)); arguments.emplace_back(filesystem::unescape_argument(argument));
start_pos=std::string::npos; start_pos = std::string::npos;
} }
} }
else if(command[c]=='\'' && backslash_count%2==0 && !double_quote) else if(command[c] == '\'' && backslash_count % 2 == 0 && !double_quote)
quote=!quote; quote = !quote;
else if(command[c]=='"' && backslash_count%2==0 && !quote) else if(command[c] == '"' && backslash_count % 2 == 0 && !quote)
double_quote=!double_quote; double_quote = !double_quote;
else if(command[c]=='\\' && !quote && !double_quote) else if(command[c] == '\\' && !quote && !double_quote)
++backslash_count; ++backslash_count;
else else
backslash_count=0; backslash_count = 0;
if(c<command.size() && start_pos==std::string::npos && command[c]!=' ') if(c < command.size() && start_pos == std::string::npos && command[c] != ' ')
start_pos=c; start_pos = c;
} }
return std::make_tuple(environment, executable, arguments); return std::make_tuple(environment, executable, arguments);
} }
void Debug::LLDB::start(const std::string &command, const boost::filesystem::path &path, void Debug::LLDB::start(const std::string &command, const boost::filesystem::path &path,
const std::vector<std::pair<boost::filesystem::path, int> > &breakpoints, const std::vector<std::pair<boost::filesystem::path, int>> &breakpoints,
const std::vector<std::string> &startup_commands, const std::string &remote_host) { const std::vector<std::string> &startup_commands, const std::string &remote_host) {
if(!debugger) { if(!debugger) {
lldb::SBDebugger::Initialize(); lldb::SBDebugger::Initialize();
debugger=std::make_unique<lldb::SBDebugger>(lldb::SBDebugger::Create(true, log, nullptr)); debugger = std::make_unique<lldb::SBDebugger>(lldb::SBDebugger::Create(true, log, nullptr));
listener=std::make_unique<lldb::SBListener>("juCi++ lldb listener"); listener = std::make_unique<lldb::SBListener>("juCi++ lldb listener");
} }
//Create executable string and argument array //Create executable string and argument array
auto parsed_run_arguments=parse_run_arguments(command); auto parsed_run_arguments = parse_run_arguments(command);
auto &environment_from_arguments=std::get<0>(parsed_run_arguments); auto &environment_from_arguments = std::get<0>(parsed_run_arguments);
auto &executable=std::get<1>(parsed_run_arguments); auto &executable = std::get<1>(parsed_run_arguments);
#ifdef _WIN32 #ifdef _WIN32
if(remote_host.empty()) if(remote_host.empty())
executable+=".exe"; executable += ".exe";
#endif #endif
auto &arguments=std::get<2>(parsed_run_arguments); auto &arguments = std::get<2>(parsed_run_arguments);
std::vector<const char*> argv; std::vector<const char *> argv;
argv.reserve(arguments.size()); argv.reserve(arguments.size());
for(auto &argument : arguments) for(auto &argument : arguments)
argv.emplace_back(argument.c_str()); argv.emplace_back(argument.c_str());
argv.emplace_back(nullptr); argv.emplace_back(nullptr);
auto target=debugger->CreateTarget(executable.c_str()); auto target = debugger->CreateTarget(executable.c_str());
if(!target.IsValid()) { if(!target.IsValid()) {
Terminal::get().async_print("Error (debug): Could not create debug target to: "+executable+'\n', true); Terminal::get().async_print("Error (debug): Could not create debug target to: " + executable + '\n', true);
for(auto &handler: on_exit) for(auto &handler : on_exit)
handler(-1); handler(-1);
return; return;
} }
//Set breakpoints //Set breakpoints
for(auto &breakpoint: breakpoints) { for(auto &breakpoint : breakpoints) {
if(!(target.BreakpointCreateByLocation(breakpoint.first.string().c_str(), breakpoint.second)).IsValid()) { if(!(target.BreakpointCreateByLocation(breakpoint.first.string().c_str(), breakpoint.second)).IsValid()) {
Terminal::get().async_print("Error (debug): Could not create breakpoint at: "+breakpoint.first.string()+":"+std::to_string(breakpoint.second)+'\n', true); Terminal::get().async_print("Error (debug): Could not create breakpoint at: " + breakpoint.first.string() + ":" + std::to_string(breakpoint.second) + '\n', true);
for(auto &handler: on_exit) for(auto &handler : on_exit)
handler(-1); handler(-1);
return; return;
} }
} }
lldb::SBError error; lldb::SBError error;
if(!remote_host.empty()) { if(!remote_host.empty()) {
auto connect_string="connect://"+remote_host; auto connect_string = "connect://" + remote_host;
process = std::make_unique<lldb::SBProcess>(target.ConnectRemote(*listener, connect_string.c_str(), "gdb-remote", error)); process = std::make_unique<lldb::SBProcess>(target.ConnectRemote(*listener, connect_string.c_str(), "gdb-remote", error));
if(error.Fail()) { if(error.Fail()) {
Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
for(auto &handler: on_exit) for(auto &handler : on_exit)
handler(-1); handler(-1);
return; return;
} }
lldb::SBEvent event; lldb::SBEvent event;
while(true) { while(true) {
if(listener->GetNextEvent(event)) { if(listener->GetNextEvent(event)) {
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged)>0) { if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) {
auto state=process->GetStateFromEvent(event); auto state = process->GetStateFromEvent(event);
this->state=state; this->state = state;
if(state==lldb::StateType::eStateConnected) if(state == lldb::StateType::eStateConnected)
break; break;
} }
} }
} }
// Create environment array // Create environment array
std::vector<const char*> environment; std::vector<const char *> environment;
environment.reserve(environment_from_arguments.size()); environment.reserve(environment_from_arguments.size());
for(auto &e: environment_from_arguments) for(auto &e : environment_from_arguments)
environment.emplace_back(e.c_str()); environment.emplace_back(e.c_str());
environment.emplace_back(nullptr); environment.emplace_back(nullptr);
process->RemoteLaunch(argv.data(), environment.data(), nullptr, nullptr, nullptr, nullptr, lldb::eLaunchFlagNone, false, error); process->RemoteLaunch(argv.data(), environment.data(), nullptr, nullptr, nullptr, nullptr, lldb::eLaunchFlagNone, false, error);
if(!error.Fail()) if(!error.Fail())
process->Continue(); process->Continue();
} }
else { else {
// Create environment array // Create environment array
std::vector<const char*> environment; std::vector<const char *> environment;
environment.reserve(environment_from_arguments.size()); environment.reserve(environment_from_arguments.size());
for(auto &e: environment_from_arguments) for(auto &e : environment_from_arguments)
environment.emplace_back(e.c_str()); environment.emplace_back(e.c_str());
size_t environ_size=0; size_t environ_size = 0;
while(environ[environ_size]!=nullptr) while(environ[environ_size] != nullptr)
++environ_size; ++environ_size;
for(size_t c=0;c<environ_size;++c) for(size_t c = 0; c < environ_size; ++c)
environment.emplace_back(environ[c]); environment.emplace_back(environ[c]);
environment.emplace_back(nullptr); environment.emplace_back(nullptr);
process = std::make_unique<lldb::SBProcess>(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error)); process = std::make_unique<lldb::SBProcess>(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error));
} }
if(error.Fail()) { if(error.Fail()) {
Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
for(auto &handler: on_exit) for(auto &handler : on_exit)
handler(-1); handler(-1);
return; return;
} }
if(debug_thread.joinable()) if(debug_thread.joinable())
debug_thread.join(); debug_thread.join();
for(auto &handler: on_start) for(auto &handler : on_start)
handler(*process); handler(*process);
for(auto &command: startup_commands) { for(auto &command : startup_commands) {
lldb::SBCommandReturnObject command_return_object; lldb::SBCommandReturnObject command_return_object;
debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, false); debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, false);
} }
debug_thread=std::thread([this]() { debug_thread = std::thread([this]() {
lldb::SBEvent event; lldb::SBEvent event;
while(true) { while(true) {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(listener->GetNextEvent(event)) { if(listener->GetNextEvent(event)) {
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged)>0) { if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) {
auto state=process->GetStateFromEvent(event); auto state = process->GetStateFromEvent(event);
this->state=state; this->state = state;
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
for(uint32_t c=0;c<process->GetNumThreads();c++) { for(uint32_t c = 0; c < process->GetNumThreads(); c++) {
auto thread=process->GetThreadAtIndex(c); auto thread = process->GetThreadAtIndex(c);
if(thread.GetStopReason()>=2) { if(thread.GetStopReason() >= 2) {
process->SetSelectedThreadByIndexID(thread.GetIndexID()); process->SetSelectedThreadByIndexID(thread.GetIndexID());
break; break;
} }
} }
} }
lock.unlock(); lock.unlock();
for(auto &handler: on_event) for(auto &handler : on_event)
handler(event); handler(event);
lock.lock(); lock.lock();
if(state==lldb::StateType::eStateExited || state==lldb::StateType::eStateCrashed) { if(state == lldb::StateType::eStateExited || state == lldb::StateType::eStateCrashed) {
auto exit_status=state==lldb::StateType::eStateCrashed?-1:process->GetExitStatus(); auto exit_status = state == lldb::StateType::eStateCrashed ? -1 : process->GetExitStatus();
lock.unlock(); lock.unlock();
for(auto &handler: on_exit) for(auto &handler : on_exit)
handler(exit_status); handler(exit_status);
lock.lock(); lock.lock();
process.reset(); process.reset();
this->state=lldb::StateType::eStateInvalid; this->state = lldb::StateType::eStateInvalid;
return; return;
} }
} }
if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDOUT)>0) { if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDOUT) > 0) {
char buffer[buffer_size]; char buffer[buffer_size];
size_t n; size_t n;
while((n=process->GetSTDOUT(buffer, buffer_size))!=0) while((n = process->GetSTDOUT(buffer, buffer_size)) != 0)
Terminal::get().async_print(std::string(buffer, n)); Terminal::get().async_print(std::string(buffer, n));
} }
//TODO: for some reason stderr is redirected to stdout //TODO: for some reason stderr is redirected to stdout
if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDERR)>0) { if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDERR) > 0) {
char buffer[buffer_size]; char buffer[buffer_size];
size_t n; size_t n;
while((n=process->GetSTDERR(buffer, buffer_size))!=0) while((n = process->GetSTDERR(buffer, buffer_size)) != 0)
Terminal::get().async_print(std::string(buffer, n), true); Terminal::get().async_print(std::string(buffer, n), true);
} }
} }
@ -251,45 +251,45 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
void Debug::LLDB::continue_debug() { void Debug::LLDB::continue_debug() {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) if(state == lldb::StateType::eStateStopped)
process->Continue(); process->Continue();
} }
void Debug::LLDB::stop() { void Debug::LLDB::stop() {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateRunning) { if(state == lldb::StateType::eStateRunning) {
auto error=process->Stop(); auto error = process->Stop();
if(error.Fail()) if(error.Fail())
Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
} }
} }
void Debug::LLDB::kill() { void Debug::LLDB::kill() {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(process) { if(process) {
auto error=process->Kill(); auto error = process->Kill();
if(error.Fail()) if(error.Fail())
Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
} }
} }
void Debug::LLDB::step_over() { void Debug::LLDB::step_over() {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
process->GetSelectedThread().StepOver(); process->GetSelectedThread().StepOver();
} }
} }
void Debug::LLDB::step_into() { void Debug::LLDB::step_into() {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
process->GetSelectedThread().StepInto(); process->GetSelectedThread().StepInto();
} }
} }
void Debug::LLDB::step_out() { void Debug::LLDB::step_out() {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
process->GetSelectedThread().StepOut(); process->GetSelectedThread().StepOut();
} }
} }
@ -297,15 +297,15 @@ void Debug::LLDB::step_out() {
std::pair<std::string, std::string> Debug::LLDB::run_command(const std::string &command) { std::pair<std::string, std::string> Debug::LLDB::run_command(const std::string &command) {
std::pair<std::string, std::string> command_return; std::pair<std::string, std::string> command_return;
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped || state==lldb::StateType::eStateRunning) { if(state == lldb::StateType::eStateStopped || state == lldb::StateType::eStateRunning) {
lldb::SBCommandReturnObject command_return_object; lldb::SBCommandReturnObject command_return_object;
debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true); debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true);
auto output=command_return_object.GetOutput(); auto output = command_return_object.GetOutput();
if(output) if(output)
command_return.first=output; command_return.first = output;
auto error=command_return_object.GetError(); auto error = command_return_object.GetError();
if(error) if(error)
command_return.second=error; command_return.second = error;
} }
return command_return; return command_return;
} }
@ -313,32 +313,32 @@ std::pair<std::string, std::string> Debug::LLDB::run_command(const std::string &
std::vector<Debug::LLDB::Frame> Debug::LLDB::get_backtrace() { std::vector<Debug::LLDB::Frame> Debug::LLDB::get_backtrace() {
std::vector<Frame> backtrace; std::vector<Frame> backtrace;
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
auto thread=process->GetSelectedThread(); auto thread = process->GetSelectedThread();
for(uint32_t c_f=0;c_f<thread.GetNumFrames();c_f++) { for(uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) {
Frame backtrace_frame; Frame backtrace_frame;
auto frame=thread.GetFrameAtIndex(c_f); auto frame = thread.GetFrameAtIndex(c_f);
backtrace_frame.index=c_f; backtrace_frame.index = c_f;
if(frame.GetFunctionName()!=nullptr) if(frame.GetFunctionName() != nullptr)
backtrace_frame.function_name=frame.GetFunctionName(); backtrace_frame.function_name = frame.GetFunctionName();
auto module_filename=frame.GetModule().GetFileSpec().GetFilename(); auto module_filename = frame.GetModule().GetFileSpec().GetFilename();
if(module_filename!=nullptr) { if(module_filename != nullptr) {
backtrace_frame.module_filename=module_filename; backtrace_frame.module_filename = module_filename;
} }
auto line_entry=frame.GetLineEntry(); auto line_entry = frame.GetLineEntry();
if(line_entry.IsValid()) { if(line_entry.IsValid()) {
lldb::SBStream stream; lldb::SBStream stream;
line_entry.GetFileSpec().GetDescription(stream); line_entry.GetFileSpec().GetDescription(stream);
auto column=line_entry.GetColumn(); auto column = line_entry.GetColumn();
if(column==0) if(column == 0)
column=1; column = 1;
backtrace_frame.file_path=filesystem::get_normal_path(stream.GetData()); backtrace_frame.file_path = filesystem::get_normal_path(stream.GetData());
backtrace_frame.line_nr=line_entry.GetLine(); backtrace_frame.line_nr = line_entry.GetLine();
backtrace_frame.line_index=column; backtrace_frame.line_index = column;
} }
backtrace.emplace_back(backtrace_frame); backtrace.emplace_back(backtrace_frame);
} }
@ -349,48 +349,48 @@ std::vector<Debug::LLDB::Frame> Debug::LLDB::get_backtrace() {
std::vector<Debug::LLDB::Variable> Debug::LLDB::get_variables() { std::vector<Debug::LLDB::Variable> Debug::LLDB::get_variables() {
std::vector<Debug::LLDB::Variable> variables; std::vector<Debug::LLDB::Variable> variables;
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
for(uint32_t c_t=0;c_t<process->GetNumThreads();c_t++) { for(uint32_t c_t = 0; c_t < process->GetNumThreads(); c_t++) {
auto thread=process->GetThreadAtIndex(c_t); auto thread = process->GetThreadAtIndex(c_t);
for(uint32_t c_f=0;c_f<thread.GetNumFrames();c_f++) { for(uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) {
auto frame=thread.GetFrameAtIndex(c_f); auto frame = thread.GetFrameAtIndex(c_f);
auto values=frame.GetVariables(true, true, true, false); auto values = frame.GetVariables(true, true, true, false);
for(uint32_t value_index=0;value_index<values.GetSize();value_index++) { for(uint32_t value_index = 0; value_index < values.GetSize(); value_index++) {
lldb::SBStream stream; lldb::SBStream stream;
auto value=values.GetValueAtIndex(value_index); auto value = values.GetValueAtIndex(value_index);
Debug::LLDB::Variable variable; Debug::LLDB::Variable variable;
variable.thread_index_id=thread.GetIndexID(); variable.thread_index_id = thread.GetIndexID();
variable.frame_index=c_f; variable.frame_index = c_f;
if(value.GetName()!=nullptr) if(value.GetName() != nullptr)
variable.name=value.GetName(); variable.name = value.GetName();
value.GetDescription(stream); value.GetDescription(stream);
variable.value=stream.GetData(); variable.value = stream.GetData();
auto declaration=value.GetDeclaration(); auto declaration = value.GetDeclaration();
if(declaration.IsValid()) { if(declaration.IsValid()) {
variable.declaration_found=true; variable.declaration_found = true;
variable.line_nr=declaration.GetLine(); variable.line_nr = declaration.GetLine();
variable.line_index=declaration.GetColumn(); variable.line_index = declaration.GetColumn();
if(variable.line_index==0) if(variable.line_index == 0)
variable.line_index=1; variable.line_index = 1;
auto file_spec=declaration.GetFileSpec(); auto file_spec = declaration.GetFileSpec();
variable.file_path=filesystem::get_normal_path(file_spec.GetDirectory()); variable.file_path = filesystem::get_normal_path(file_spec.GetDirectory());
variable.file_path/=file_spec.GetFilename(); variable.file_path /= file_spec.GetFilename();
} }
else { else {
variable.declaration_found=false; variable.declaration_found = false;
auto line_entry=frame.GetLineEntry(); auto line_entry = frame.GetLineEntry();
if(line_entry.IsValid()) { if(line_entry.IsValid()) {
variable.line_nr=line_entry.GetLine(); variable.line_nr = line_entry.GetLine();
variable.line_index=line_entry.GetColumn(); variable.line_index = line_entry.GetColumn();
if(variable.line_index==0) if(variable.line_index == 0)
variable.line_index=1; variable.line_index = 1;
auto file_spec=line_entry.GetFileSpec(); auto file_spec = line_entry.GetFileSpec();
variable.file_path=filesystem::get_normal_path(file_spec.GetDirectory()); variable.file_path = filesystem::get_normal_path(file_spec.GetDirectory());
variable.file_path/=file_spec.GetFilename(); variable.file_path /= file_spec.GetFilename();
} }
} }
variables.emplace_back(variable); variables.emplace_back(variable);
@ -403,10 +403,11 @@ std::vector<Debug::LLDB::Variable> Debug::LLDB::get_variables() {
void Debug::LLDB::select_frame(uint32_t frame_index, uint32_t thread_index_id) { void Debug::LLDB::select_frame(uint32_t frame_index, uint32_t thread_index_id) {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
if(thread_index_id!=0) if(thread_index_id != 0)
process->SetSelectedThreadByIndexID(thread_index_id); process->SetSelectedThreadByIndexID(thread_index_id);
process->GetSelectedThread().SetSelectedFrame(frame_index);; process->GetSelectedThread().SetSelectedFrame(frame_index);
;
} }
} }
@ -419,26 +420,26 @@ void Debug::LLDB::cancel() {
std::string Debug::LLDB::get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { std::string Debug::LLDB::get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) {
std::string variable_value; std::string variable_value;
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
auto frame=process->GetSelectedThread().GetSelectedFrame(); auto frame = process->GetSelectedThread().GetSelectedFrame();
auto values=frame.GetVariables(true, true, true, false); auto values = frame.GetVariables(true, true, true, false);
//First try to find variable based on name, file and line number //First try to find variable based on name, file and line number
if(!file_path.empty()) { if(!file_path.empty()) {
for(uint32_t value_index=0;value_index<values.GetSize();value_index++) { for(uint32_t value_index = 0; value_index < values.GetSize(); value_index++) {
lldb::SBStream stream; lldb::SBStream stream;
auto value=values.GetValueAtIndex(value_index); auto value = values.GetValueAtIndex(value_index);
if(value.GetName()!=nullptr && value.GetName()==variable) { if(value.GetName() != nullptr && value.GetName() == variable) {
auto declaration=value.GetDeclaration(); auto declaration = value.GetDeclaration();
if(declaration.IsValid()) { if(declaration.IsValid()) {
if(declaration.GetLine()==line_nr && (declaration.GetColumn()==0 || declaration.GetColumn()==line_index)) { if(declaration.GetLine() == line_nr && (declaration.GetColumn() == 0 || declaration.GetColumn() == line_index)) {
auto file_spec=declaration.GetFileSpec(); auto file_spec = declaration.GetFileSpec();
auto value_decl_path=filesystem::get_normal_path(file_spec.GetDirectory()); auto value_decl_path = filesystem::get_normal_path(file_spec.GetDirectory());
value_decl_path/=file_spec.GetFilename(); value_decl_path /= file_spec.GetFilename();
if(value_decl_path==file_path) { if(value_decl_path == file_path) {
value.GetDescription(stream); value.GetDescription(stream);
variable_value=stream.GetData(); variable_value = stream.GetData();
break; break;
} }
} }
@ -448,11 +449,11 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil
} }
if(variable_value.empty()) { if(variable_value.empty()) {
//In case a variable is missing file and line number, only do check on name //In case a variable is missing file and line number, only do check on name
auto value=frame.GetValueForVariablePath(variable.c_str()); auto value = frame.GetValueForVariablePath(variable.c_str());
if(value.IsValid()) { if(value.IsValid()) {
lldb::SBStream stream; lldb::SBStream stream;
value.GetDescription(stream); value.GetDescription(stream);
variable_value=stream.GetData(); variable_value = stream.GetData();
} }
} }
} }
@ -462,19 +463,19 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil
std::string Debug::LLDB::get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { std::string Debug::LLDB::get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) {
std::string return_value; std::string return_value;
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
auto thread=process->GetSelectedThread(); auto thread = process->GetSelectedThread();
auto thread_return_value=thread.GetStopReturnValue(); auto thread_return_value = thread.GetStopReturnValue();
if(thread_return_value.IsValid()) { if(thread_return_value.IsValid()) {
auto line_entry=thread.GetSelectedFrame().GetLineEntry(); auto line_entry = thread.GetSelectedFrame().GetLineEntry();
if(line_entry.IsValid()) { if(line_entry.IsValid()) {
lldb::SBStream stream; lldb::SBStream stream;
line_entry.GetFileSpec().GetDescription(stream); line_entry.GetFileSpec().GetDescription(stream);
if(filesystem::get_normal_path(stream.GetData())==file_path && line_entry.GetLine()==line_nr && if(filesystem::get_normal_path(stream.GetData()) == file_path && line_entry.GetLine() == line_nr &&
(line_entry.GetColumn()==0 || line_entry.GetColumn()==line_index)) { (line_entry.GetColumn() == 0 || line_entry.GetColumn() == line_index)) {
lldb::SBStream stream; lldb::SBStream stream;
thread_return_value.GetDescription(stream); thread_return_value.GetDescription(stream);
return_value=stream.GetData(); return_value = stream.GetData();
} }
} }
} }
@ -484,43 +485,43 @@ std::string Debug::LLDB::get_return_value(const boost::filesystem::path &file_pa
bool Debug::LLDB::is_invalid() { bool Debug::LLDB::is_invalid() {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
return state==lldb::StateType::eStateInvalid; return state == lldb::StateType::eStateInvalid;
} }
bool Debug::LLDB::is_stopped() { bool Debug::LLDB::is_stopped() {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
return state==lldb::StateType::eStateStopped; return state == lldb::StateType::eStateStopped;
} }
bool Debug::LLDB::is_running() { bool Debug::LLDB::is_running() {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
return state==lldb::StateType::eStateRunning; return state == lldb::StateType::eStateRunning;
} }
void Debug::LLDB::add_breakpoint(const boost::filesystem::path &file_path, int line_nr) { void Debug::LLDB::add_breakpoint(const boost::filesystem::path &file_path, int line_nr) {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::eStateStopped || state==lldb::eStateRunning) { if(state == lldb::eStateStopped || state == lldb::eStateRunning) {
if(!(process->GetTarget().BreakpointCreateByLocation(file_path.string().c_str(), line_nr)).IsValid()) if(!(process->GetTarget().BreakpointCreateByLocation(file_path.string().c_str(), line_nr)).IsValid())
Terminal::get().async_print("Error (debug): Could not create breakpoint at: "+file_path.string()+":"+std::to_string(line_nr)+'\n', true); Terminal::get().async_print("Error (debug): Could not create breakpoint at: " + file_path.string() + ":" + std::to_string(line_nr) + '\n', true);
} }
} }
void Debug::LLDB::remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) { void Debug::LLDB::remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::eStateStopped || state==lldb::eStateRunning) { if(state == lldb::eStateStopped || state == lldb::eStateRunning) {
auto target=process->GetTarget(); auto target = process->GetTarget();
for(int line_nr_try=line_nr;line_nr_try<line_count;line_nr_try++) { for(int line_nr_try = line_nr; line_nr_try < line_count; line_nr_try++) {
for(uint32_t b_index=0;b_index<target.GetNumBreakpoints();b_index++) { for(uint32_t b_index = 0; b_index < target.GetNumBreakpoints(); b_index++) {
auto breakpoint=target.GetBreakpointAtIndex(b_index); auto breakpoint = target.GetBreakpointAtIndex(b_index);
for(uint32_t l_index=0;l_index<breakpoint.GetNumLocations();l_index++) { for(uint32_t l_index = 0; l_index < breakpoint.GetNumLocations(); l_index++) {
auto line_entry=breakpoint.GetLocationAtIndex(l_index).GetAddress().GetLineEntry(); auto line_entry = breakpoint.GetLocationAtIndex(l_index).GetAddress().GetLineEntry();
if(line_entry.GetLine()==static_cast<uint32_t>(line_nr_try)) { if(line_entry.GetLine() == static_cast<uint32_t>(line_nr_try)) {
auto file_spec=line_entry.GetFileSpec(); auto file_spec = line_entry.GetFileSpec();
auto breakpoint_path=filesystem::get_normal_path(file_spec.GetDirectory()); auto breakpoint_path = filesystem::get_normal_path(file_spec.GetDirectory());
breakpoint_path/=file_spec.GetFilename(); breakpoint_path /= file_spec.GetFilename();
if(breakpoint_path==file_path) { if(breakpoint_path == file_path) {
if(!target.BreakpointDelete(breakpoint.GetID())) 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); Terminal::get().async_print("Error (debug): Could not delete breakpoint at: " + file_path.string() + ":" + std::to_string(line_nr) + '\n', true);
return; return;
} }
} }
@ -532,7 +533,7 @@ void Debug::LLDB::remove_breakpoint(const boost::filesystem::path &file_path, in
void Debug::LLDB::write(const std::string &buffer) { void Debug::LLDB::write(const std::string &buffer) {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateRunning) { if(state == lldb::StateType::eStateRunning) {
process->PutSTDIN(buffer.c_str(), buffer.size()); process->PutSTDIN(buffer.c_str(), buffer.size());
} }
} }

36
src/debug_lldb.h

@ -2,8 +2,8 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <list> #include <list>
#include <lldb/API/LLDB.h> #include <lldb/API/LLDB.h>
#include <thread>
#include <mutex> #include <mutex>
#include <thread>
#include <tuple> #include <tuple>
namespace Debug { namespace Debug {
@ -29,25 +29,27 @@ namespace Debug {
int line_nr; int line_nr;
int line_index; int line_index;
}; };
private: private:
LLDB(); LLDB();
public: public:
static LLDB &get() { static LLDB &get() {
static LLDB singleton; static LLDB singleton;
return singleton; return singleton;
} }
std::list<std::function<void(const lldb::SBProcess &)>> on_start; std::list<std::function<void(const lldb::SBProcess &)>> on_start;
/// The handlers are not run in the main loop. /// The handlers are not run in the main loop.
std::list<std::function<void(int exit_status)>> on_exit; std::list<std::function<void(int exit_status)>> on_exit;
/// The handlers are not run in the main loop. /// The handlers are not run in the main loop.
std::list<std::function<void(const lldb::SBEvent &)>> on_event; std::list<std::function<void(const lldb::SBEvent &)>> on_event;
std::mutex mutex; std::mutex mutex;
void start(const std::string &command, const boost::filesystem::path &path="", void start(const std::string &command, const boost::filesystem::path &path = "",
const std::vector<std::pair<boost::filesystem::path, int> > &breakpoints={}, const std::vector<std::pair<boost::filesystem::path, int>> &breakpoints = {},
const std::vector<std::string> &startup_commands={}, const std::string &remote_host=""); const std::vector<std::string> &startup_commands = {}, const std::string &remote_host = "");
void continue_debug(); //can't use continue as function name void continue_debug(); //can't use continue as function name
void stop(); void stop();
void kill(); void kill();
@ -57,32 +59,32 @@ namespace Debug {
std::pair<std::string, std::string> run_command(const std::string &command); std::pair<std::string, std::string> run_command(const std::string &command);
std::vector<Frame> get_backtrace(); std::vector<Frame> get_backtrace();
std::vector<Variable> get_variables(); std::vector<Variable> get_variables();
void select_frame(uint32_t frame_index, uint32_t thread_index_id=0); void select_frame(uint32_t frame_index, uint32_t thread_index_id = 0);
void cancel(); void cancel();
std::string get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index); std::string get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index);
std::string get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index); std::string get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index);
bool is_invalid(); bool is_invalid();
bool is_stopped(); bool is_stopped();
bool is_running(); bool is_running();
void add_breakpoint(const boost::filesystem::path &file_path, int line_nr); void add_breakpoint(const boost::filesystem::path &file_path, int line_nr);
void remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count); void remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count);
void write(const std::string &buffer); void write(const std::string &buffer);
private: private:
std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> parse_run_arguments(const std::string &command); std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> parse_run_arguments(const std::string &command);
std::unique_ptr<lldb::SBDebugger> debugger; std::unique_ptr<lldb::SBDebugger> debugger;
std::unique_ptr<lldb::SBListener> listener; std::unique_ptr<lldb::SBListener> listener;
std::unique_ptr<lldb::SBProcess> process; std::unique_ptr<lldb::SBProcess> process;
std::thread debug_thread; std::thread debug_thread;
lldb::StateType state; lldb::StateType state;
size_t buffer_size; size_t buffer_size;
}; };
} } // namespace Debug

60
src/dialogs.cc

@ -1,27 +1,27 @@
#include "dialogs.h" #include "dialogs.h"
#include <cmath> #include <cmath>
Dialog::Message::Message(const std::string &text): Gtk::Window(Gtk::WindowType::WINDOW_POPUP) { Dialog::Message::Message(const std::string &text) : Gtk::Window(Gtk::WindowType::WINDOW_POPUP) {
auto g_application=g_application_get_default(); auto g_application = g_application_get_default();
auto gio_application=Glib::wrap(g_application, true); auto gio_application = Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application); auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
set_transient_for(*application->get_active_window()); set_transient_for(*application->get_active_window());
set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
set_modal(true); set_modal(true);
set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_NOTIFICATION); set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_NOTIFICATION);
property_decorated()=false; property_decorated() = false;
set_skip_taskbar_hint(true); set_skip_taskbar_hint(true);
auto box=Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL)); auto box = Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL));
auto label=Gtk::manage(new Gtk::Label(text)); auto label = Gtk::manage(new Gtk::Label(text));
label->set_padding(10, 10); label->set_padding(10, 10);
box->pack_start(*label); box->pack_start(*label);
add(*box); add(*box);
show_all_children(); show_all_children();
show_now(); show_now();
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(false); Gtk::Main::iteration(false);
} }
@ -31,21 +31,23 @@ bool Dialog::Message::on_delete_event(GdkEventAny *event) {
} }
std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::string &title, std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::string &title,
const std::vector<std::pair<std::string, Gtk::ResponseType>> &buttons, const std::vector<std::pair<std::string, Gtk::ResponseType>> &buttons,
Gtk::FileChooserAction action) { Gtk::FileChooserAction action) {
// Workaround for crash on MacOS when filtering files in file/folder dialogs. // Workaround for crash on MacOS when filtering files in file/folder dialogs.
// See also https://github.com/cppit/jucipp/issues/259. // See also https://github.com/cppit/jucipp/issues/259.
// TODO 2018: check if this bug has been fixed // TODO 2018: check if this bug has been fixed
#ifdef __APPLE__ #ifdef __APPLE__
class FileChooserDialog : public Gtk::FileChooserDialog { class FileChooserDialog : public Gtk::FileChooserDialog {
Gtk::FileChooserAction action; Gtk::FileChooserAction action;
public: public:
FileChooserDialog(const Glib::ustring& title, Gtk::FileChooserAction action) : Gtk::FileChooserDialog(title, action), action(action) {} FileChooserDialog(const Glib::ustring &title, Gtk::FileChooserAction action) : Gtk::FileChooserDialog(title, action), action(action) {}
protected: protected:
bool on_key_press_event(GdkEventKey *key_event) override { bool on_key_press_event(GdkEventKey *key_event) override {
if(action==Gtk::FileChooserAction::FILE_CHOOSER_ACTION_OPEN || action==Gtk::FileChooserAction::FILE_CHOOSER_ACTION_SELECT_FOLDER) { if(action == Gtk::FileChooserAction::FILE_CHOOSER_ACTION_OPEN || action == Gtk::FileChooserAction::FILE_CHOOSER_ACTION_SELECT_FOLDER) {
auto unicode=gdk_keyval_to_unicode(key_event->keyval); auto unicode = gdk_keyval_to_unicode(key_event->keyval);
if(unicode>31 && unicode!=127) if(unicode > 31 && unicode != 127)
return true; return true;
} }
return Gtk::FileChooserDialog::on_key_press_event(key_event); return Gtk::FileChooserDialog::on_key_press_event(key_event);
@ -55,25 +57,25 @@ std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::s
#else #else
Gtk::FileChooserDialog dialog(title, action); Gtk::FileChooserDialog dialog(title, action);
#endif #endif
auto g_application=g_application_get_default(); auto g_application = g_application_get_default();
auto gio_application=Glib::wrap(g_application, true); auto gio_application = Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application); auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
dialog.set_transient_for(*application->get_active_window()); dialog.set_transient_for(*application->get_active_window());
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT); dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
if(title=="Save File As") if(title == "Save File As")
gtk_file_chooser_set_filename(reinterpret_cast<GtkFileChooser*>(dialog.gobj()), path.string().c_str()); gtk_file_chooser_set_filename(reinterpret_cast<GtkFileChooser *>(dialog.gobj()), path.string().c_str());
else if(!path.empty()) else if(!path.empty())
gtk_file_chooser_set_current_folder(reinterpret_cast<GtkFileChooser*>(dialog.gobj()), path.string().c_str()); gtk_file_chooser_set_current_folder(reinterpret_cast<GtkFileChooser *>(dialog.gobj()), path.string().c_str());
else { else {
boost::system::error_code ec; boost::system::error_code ec;
auto current_path=boost::filesystem::current_path(ec); auto current_path = boost::filesystem::current_path(ec);
if(!ec) if(!ec)
gtk_file_chooser_set_current_folder(reinterpret_cast<GtkFileChooser*>(dialog.gobj()), current_path.string().c_str()); gtk_file_chooser_set_current_folder(reinterpret_cast<GtkFileChooser *>(dialog.gobj()), current_path.string().c_str());
} }
for (auto &button : buttons) for(auto &button : buttons)
dialog.add_button(button.first, button.second); dialog.add_button(button.first, button.second);
return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : ""; return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : "";
} }

13
src/dialogs.h

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <string>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <vector>
#include <gtkmm.h> #include <gtkmm.h>
#include <string>
#include <vector>
class Dialog { class Dialog {
public: public:
@ -11,16 +11,17 @@ public:
static std::string new_file(const boost::filesystem::path &path); static std::string new_file(const boost::filesystem::path &path);
static std::string new_folder(const boost::filesystem::path &path); static std::string new_folder(const boost::filesystem::path &path);
static std::string save_file_as(const boost::filesystem::path &path); static std::string save_file_as(const boost::filesystem::path &path);
class Message : public Gtk::Window { class Message : public Gtk::Window {
public: public:
Message(const std::string &text); Message(const std::string &text);
protected: protected:
bool on_delete_event(GdkEventAny *event) override; bool on_delete_event(GdkEventAny *event) override;
}; };
private: private:
static std::string gtk_dialog(const boost::filesystem::path &path, const std::string &title, static std::string gtk_dialog(const boost::filesystem::path &path, const std::string &title,
const std::vector<std::pair<std::string, Gtk::ResponseType>> &buttons, const std::vector<std::pair<std::string, Gtk::ResponseType>> &buttons,
Gtk::FileChooserAction gtk_options); Gtk::FileChooserAction gtk_options);
}; };

21
src/dialogs_unix.cc

@ -2,31 +2,30 @@
std::string Dialog::open_folder(const boost::filesystem::path &path) { std::string Dialog::open_folder(const boost::filesystem::path &path) {
return gtk_dialog(path, "Open Folder", return gtk_dialog(path, "Open Folder",
{std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Open", Gtk::RESPONSE_OK)}, {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Open", Gtk::RESPONSE_OK)},
Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
} }
std::string Dialog::new_file(const boost::filesystem::path &path) { std::string Dialog::new_file(const boost::filesystem::path &path) {
return gtk_dialog(path, "New File", return gtk_dialog(path, "New File",
{std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)}, {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)},
Gtk::FILE_CHOOSER_ACTION_SAVE); Gtk::FILE_CHOOSER_ACTION_SAVE);
} }
std::string Dialog::new_folder(const boost::filesystem::path &path) { std::string Dialog::new_folder(const boost::filesystem::path &path) {
return gtk_dialog(path, "New Folder", return gtk_dialog(path, "New Folder",
{std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Create", Gtk::RESPONSE_OK)},
Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
} }
std::string Dialog::open_file(const boost::filesystem::path &path) { std::string Dialog::open_file(const boost::filesystem::path &path) {
return gtk_dialog(path, "Open File", return gtk_dialog(path, "Open File",
{std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Select", Gtk::RESPONSE_OK)}, {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Select", Gtk::RESPONSE_OK)},
Gtk::FILE_CHOOSER_ACTION_OPEN); Gtk::FILE_CHOOSER_ACTION_OPEN);
} }
std::string Dialog::save_file_as(const boost::filesystem::path &path) { std::string Dialog::save_file_as(const boost::filesystem::path &path) {
return gtk_dialog(path, "Save File As", return gtk_dialog(path, "Save File As",
{std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Save", Gtk::RESPONSE_OK)}, {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Save", Gtk::RESPONSE_OK)},
Gtk::FILE_CHOOSER_ACTION_SAVE); Gtk::FILE_CHOOSER_ACTION_SAVE);
} }

100
src/dialogs_win.cc

@ -1,136 +1,136 @@
#include "dialogs.h" #include "dialogs.h"
#include "singletons.h"
#include "juci.h" #include "juci.h"
#include "singletons.h"
#undef NTDDI_VERSION #undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_VISTA #define NTDDI_VERSION NTDDI_VISTA
#undef _WIN32_WINNT #undef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_VISTA #define _WIN32_WINNT _WIN32_WINNT_VISTA
#include <windows.h>
#include <shobjidl.h> #include <shobjidl.h>
#include <vector> #include <vector>
#include <windows.h>
#include <iostream> //TODO: remove #include <iostream> //TODO: remove
using namespace std; //TODO: remove using namespace std; //TODO: remove
class Win32Dialog { class Win32Dialog {
public: public:
Win32Dialog() {}; Win32Dialog(){};
~Win32Dialog() { ~Win32Dialog() {
if(dialog!=nullptr) if(dialog != nullptr)
dialog->Release(); dialog->Release();
} }
/** Returns the selected item's path as a string */ /** Returns the selected item's path as a string */
std::string open(const std::wstring &title, unsigned option=0) { std::string open(const std::wstring &title, unsigned option = 0) {
if(!init(CLSID_FileOpenDialog)) if(!init(CLSID_FileOpenDialog))
return ""; return "";
if(!set_title(title) || !add_option(option)) if(!set_title(title) || !add_option(option))
return ""; return "";
if(!set_folder()) if(!set_folder())
return ""; return "";
return show(); return show();
} }
std::string save(const std::wstring &title, const boost::filesystem::path &file_path="", unsigned option=0) { std::string save(const std::wstring &title, const boost::filesystem::path &file_path = "", unsigned option = 0) {
if(!init(CLSID_FileSaveDialog)) if(!init(CLSID_FileSaveDialog))
return ""; return "";
if(!set_title(title) || !add_option(option)) if(!set_title(title) || !add_option(option))
return ""; return "";
if(!set_folder()) if(!set_folder())
return ""; return "";
std::vector<COMDLG_FILTERSPEC> extensions; std::vector<COMDLG_FILTERSPEC> extensions;
if(!file_path.empty()) { if(!file_path.empty()) {
if(file_path.has_extension() && file_path.filename()!=file_path.extension()) { if(file_path.has_extension() && file_path.filename() != file_path.extension()) {
auto extension=(L"*"+file_path.extension().native()).c_str(); auto extension = (L"*" + file_path.extension().native()).c_str();
extensions.emplace_back(COMDLG_FILTERSPEC{extension, extension}); extensions.emplace_back(COMDLG_FILTERSPEC{extension, extension});
if(!set_default_file_extension(extension)) if(!set_default_file_extension(extension))
return ""; return "";
} }
} }
extensions.emplace_back(COMDLG_FILTERSPEC{L"All files", L"*.*"}); extensions.emplace_back(COMDLG_FILTERSPEC{L"All files", L"*.*"});
if(dialog->SetFileTypes(extensions.size(), extensions.data())!=S_OK) if(dialog->SetFileTypes(extensions.size(), extensions.data()) != S_OK)
return ""; return "";
return show(); return show();
} }
private: private:
IFileDialog *dialog=nullptr; IFileDialog *dialog = nullptr;
DWORD options; DWORD options;
bool init(CLSID type) { bool init(CLSID type) {
if(CoCreateInstance(type, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog))!=S_OK) if(CoCreateInstance(type, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog)) != S_OK)
return false; return false;
if(dialog->GetOptions(&options)!=S_OK) if(dialog->GetOptions(&options) != S_OK)
return false; return false;
return true; return true;
} }
/** available options are listed at https://msdn.microsoft.com/en-gb/library/windows/desktop/dn457282(v=vs.85).aspx */ /** available options are listed at https://msdn.microsoft.com/en-gb/library/windows/desktop/dn457282(v=vs.85).aspx */
bool add_option(unsigned option) { bool add_option(unsigned option) {
if(dialog->SetOptions(options | option)!=S_OK) if(dialog->SetOptions(options | option) != S_OK)
return false; return false;
return true; return true;
} }
bool set_title(const std::wstring &title) { bool set_title(const std::wstring &title) {
if(dialog->SetTitle(title.c_str())!=S_OK) if(dialog->SetTitle(title.c_str()) != S_OK)
return false; return false;
return true; return true;
} }
/** Sets the extensions the browser can find */ /** Sets the extensions the browser can find */
bool set_default_file_extension(const std::wstring &file_extension) { bool set_default_file_extension(const std::wstring &file_extension) {
if(dialog->SetDefaultExtension(file_extension.c_str())!=S_OK) if(dialog->SetDefaultExtension(file_extension.c_str()) != S_OK)
return false; return false;
return true; return true;
} }
/** Sets the directory to start browsing */ /** Sets the directory to start browsing */
bool set_folder() { bool set_folder() {
auto g_application=g_application_get_default(); //TODO: Post issue that Gio::Application::get_default should return pointer and not Glib::RefPtr auto g_application = g_application_get_default(); //TODO: Post issue that Gio::Application::get_default should return pointer and not Glib::RefPtr
auto gio_application=Glib::wrap(g_application, true); auto gio_application = Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Application>::cast_static(gio_application); auto application = Glib::RefPtr<Application>::cast_static(gio_application);
auto current_path=application->window->notebook.get_current_folder(); auto current_path = application->window->notebook.get_current_folder();
boost::system::error_code ec; boost::system::error_code ec;
if(current_path.empty()) if(current_path.empty())
current_path=boost::filesystem::current_path(ec); current_path = boost::filesystem::current_path(ec);
if(ec) if(ec)
return false; return false;
std::wstring path=current_path.native(); std::wstring path = current_path.native();
size_t pos=0; size_t pos = 0;
while((pos=path.find(L'/', pos))!=std::wstring::npos) {//TODO: issue bug report on boost::filesystem::path::native on MSYS2 while((pos = path.find(L'/', pos)) != std::wstring::npos) { //TODO: issue bug report on boost::filesystem::path::native on MSYS2
path.replace(pos, 1, L"\\"); path.replace(pos, 1, L"\\");
pos++; pos++;
} }
IShellItem *folder = nullptr; IShellItem *folder = nullptr;
if(SHCreateItemFromParsingName(path.c_str(), nullptr, IID_PPV_ARGS(&folder))!=S_OK) if(SHCreateItemFromParsingName(path.c_str(), nullptr, IID_PPV_ARGS(&folder)) != S_OK)
return false; return false;
if(dialog->SetFolder(folder)!=S_OK) if(dialog->SetFolder(folder) != S_OK)
return false; return false;
folder->Release(); folder->Release();
return true; return true;
} }
std::string show() { std::string show() {
if(dialog->Show(nullptr)!=S_OK) if(dialog->Show(nullptr) != S_OK)
return ""; return "";
IShellItem *result = nullptr; IShellItem *result = nullptr;
if(dialog->GetResult(&result)!=S_OK) if(dialog->GetResult(&result) != S_OK)
return ""; return "";
LPWSTR file_path = nullptr; LPWSTR file_path = nullptr;
auto hresult=result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); auto hresult = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
result->Release(); result->Release();
if(hresult!=S_OK) if(hresult != S_OK)
return ""; return "";
std::wstring file_path_wstring(file_path); std::wstring file_path_wstring(file_path);
std::string file_path_string(file_path_wstring.begin(), file_path_wstring.end()); std::string file_path_string(file_path_wstring.begin(), file_path_wstring.end());
@ -150,12 +150,12 @@ std::string Dialog::new_file() {
std::string Dialog::new_folder() { std::string Dialog::new_folder() {
//Win32 (IFileDialog) does not support create folder... //Win32 (IFileDialog) does not support create folder...
return gtk_dialog("New Folder", return gtk_dialog("New Folder",
{std::make_pair("Cancel", Gtk::RESPONSE_CANCEL),std::make_pair("Create", Gtk::RESPONSE_OK)}, {std::make_pair("Cancel", Gtk::RESPONSE_CANCEL), std::make_pair("Create", Gtk::RESPONSE_OK)},
Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
} }
std::string Dialog::open_file() { std::string Dialog::open_file() {
return Win32Dialog().open(L"Open File"); return Win32Dialog().open(L"Open File");
} }
std::string Dialog::save_file_as(const boost::filesystem::path &file_path) { std::string Dialog::save_file_as(const boost::filesystem::path &file_path) {

577
src/directories.cc

@ -1,38 +1,38 @@
#include "directories.h" #include "directories.h"
#include <algorithm> #include "entrybox.h"
#include "filesystem.h"
#include "notebook.h"
#include "source.h" #include "source.h"
#include "terminal.h" #include "terminal.h"
#include "notebook.h" #include <algorithm>
#include "filesystem.h"
#include "entrybox.h"
bool Directories::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const { bool Directories::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const {
return true; return true;
} }
bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) { bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) {
auto &directories=Directories::get(); auto &directories = Directories::get();
auto get_target_folder=[this, &directories](const TreeModel::Path &path) { auto get_target_folder = [this, &directories](const TreeModel::Path &path) {
if(path.size()==1) if(path.size() == 1)
return directories.path; return directories.path;
else { else {
auto it=get_iter(path); auto it = get_iter(path);
if(it) { if(it) {
auto prev_path=path; auto prev_path = path;
prev_path.up(); prev_path.up();
it=get_iter(prev_path); it = get_iter(prev_path);
if(it) if(it)
return it->get_value(directories.column_record.path); return it->get_value(directories.column_record.path);
} }
else { else {
auto prev_path=path; auto prev_path = path;
prev_path.up(); prev_path.up();
if(prev_path.size()==1) if(prev_path.size() == 1)
return directories.path; return directories.path;
else { else {
prev_path.up(); prev_path.up();
it=get_iter(prev_path); it = get_iter(prev_path);
if(it) if(it)
return it->get_value(directories.column_record.path); return it->get_value(directories.column_record.path);
} }
@ -40,122 +40,122 @@ bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &pat
} }
return boost::filesystem::path(); return boost::filesystem::path();
}; };
auto it=directories.get_selection()->get_selected(); auto it = directories.get_selection()->get_selected();
if(it) { if(it) {
auto source_path=it->get_value(directories.column_record.path); auto source_path = it->get_value(directories.column_record.path);
if(source_path.empty()) if(source_path.empty())
return false; return false;
auto target_path=get_target_folder(path); auto target_path = get_target_folder(path);
target_path/=source_path.filename(); target_path /= source_path.filename();
if(source_path==target_path) if(source_path == target_path)
return false; return false;
if(boost::filesystem::exists(target_path)) { if(boost::filesystem::exists(target_path)) {
Terminal::get().print("Error: could not move file: "+target_path.string()+" already exists\n", true); Terminal::get().print("Error: could not move file: " + target_path.string() + " already exists\n", true);
return false; return false;
} }
bool is_directory=boost::filesystem::is_directory(source_path); bool is_directory = boost::filesystem::is_directory(source_path);
if(is_directory) if(is_directory)
Directories::get().remove_path(source_path); Directories::get().remove_path(source_path);
boost::system::error_code ec; boost::system::error_code ec;
boost::filesystem::rename(source_path, target_path, ec); boost::filesystem::rename(source_path, target_path, ec);
if(ec) { if(ec) {
Terminal::get().print("Error: could not move file: "+ec.message()+'\n', true); Terminal::get().print("Error: could not move file: " + ec.message() + '\n', true);
return false; return false;
} }
for(size_t c=0;c<Notebook::get().size();c++) { for(size_t c = 0; c < Notebook::get().size(); c++) {
auto view=Notebook::get().get_view(c); auto view = Notebook::get().get_view(c);
if(is_directory) { if(is_directory) {
if(filesystem::file_in_path(view->file_path, source_path)) { if(filesystem::file_in_path(view->file_path, source_path)) {
auto file_it=view->file_path.begin(); auto file_it = view->file_path.begin();
for(auto source_it=source_path.begin();source_it!=source_path.end();source_it++) for(auto source_it = source_path.begin(); source_it != source_path.end(); source_it++)
file_it++; file_it++;
auto new_file_path=target_path; auto new_file_path = target_path;
for(;file_it!=view->file_path.end();file_it++) for(; file_it != view->file_path.end(); file_it++)
new_file_path/=*file_it; new_file_path /= *file_it;
view->rename(new_file_path); view->rename(new_file_path);
} }
} }
else if(view->file_path==source_path) { else if(view->file_path == source_path) {
view->rename(target_path); view->rename(target_path);
break; break;
} }
} }
Directories::get().update(); Directories::get().update();
Directories::get().on_save_file(target_path); Directories::get().on_save_file(target_path);
directories.select(target_path); directories.select(target_path);
} }
EntryBox::get().hide(); EntryBox::get().hide();
return false; return false;
} }
bool Directories::TreeStore::drag_data_delete_vfunc (const Gtk::TreeModel::Path &path) { bool Directories::TreeStore::drag_data_delete_vfunc(const Gtk::TreeModel::Path &path) {
return false; return false;
} }
Directories::Directories() : Gtk::ListViewText(1) { Directories::Directories() : Gtk::ListViewText(1) {
set_enable_tree_lines(true); set_enable_tree_lines(true);
tree_store = TreeStore::create(); tree_store = TreeStore::create();
tree_store->set_column_types(column_record); tree_store->set_column_types(column_record);
set_model(tree_store); set_model(tree_store);
get_column(0)->set_title(""); get_column(0)->set_title("");
auto renderer=dynamic_cast<Gtk::CellRendererText*>(get_column(0)->get_first_cell()); auto renderer = dynamic_cast<Gtk::CellRendererText *>(get_column(0)->get_first_cell());
get_column(0)->set_cell_data_func(*renderer, [this] (Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &iter) { get_column(0)->set_cell_data_func(*renderer, [this](Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &iter) {
if(auto renderer_text=dynamic_cast<Gtk::CellRendererText*>(renderer)) if(auto renderer_text = dynamic_cast<Gtk::CellRendererText *>(renderer))
renderer_text->property_markup()=iter->get_value(column_record.markup); renderer_text->property_markup() = iter->get_value(column_record.markup);
}); });
get_style_context()->add_class("juci_directories"); get_style_context()->add_class("juci_directories");
tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING); tree_store->set_sort_column(column_record.id, Gtk::SortType::SORT_ASCENDING);
set_enable_search(true); //TODO: why does this not work in OS X? set_enable_search(true); //TODO: why does this not work in OS X?
set_search_column(column_record.name); set_search_column(column_record.name);
signal_row_activated().connect([this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column){ signal_row_activated().connect([this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) {
auto iter = tree_store->get_iter(path); auto iter = tree_store->get_iter(path);
if (iter) { if(iter) {
auto filesystem_path=iter->get_value(column_record.path); auto filesystem_path = iter->get_value(column_record.path);
if(filesystem_path!="") { if(filesystem_path != "") {
if (boost::filesystem::is_directory(boost::filesystem::path(filesystem_path))) if(boost::filesystem::is_directory(boost::filesystem::path(filesystem_path)))
row_expanded(path) ? collapse_row(path) : expand_row(path, false); row_expanded(path) ? collapse_row(path) : expand_row(path, false);
else else
Notebook::get().open(filesystem_path); Notebook::get().open(filesystem_path);
} }
} }
}); });
signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path){ signal_test_expand_row().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) {
if(iter->children().begin()->get_value(column_record.path)=="") if(iter->children().begin()->get_value(column_record.path) == "")
add_or_update_path(iter->get_value(column_record.path), *iter, true); add_or_update_path(iter->get_value(column_record.path), *iter, true);
return false; return false;
}); });
signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path){ signal_row_collapsed().connect([this](const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) {
this->remove_path(iter->get_value(column_record.path)); this->remove_path(iter->get_value(column_record.path));
}); });
enable_model_drag_source(); enable_model_drag_source();
enable_model_drag_dest(); enable_model_drag_dest();
auto new_file_label = "New File"; auto new_file_label = "New File";
auto new_file_function = [this] { auto new_file_function = [this] {
if(menu_popup_row_path.empty()) if(menu_popup_row_path.empty())
return; return;
EntryBox::get().clear(); EntryBox::get().clear();
EntryBox::get().entries.emplace_back("", [this, source_path=menu_popup_row_path](const std::string &content) { EntryBox::get().entries.emplace_back("", [this, source_path = menu_popup_row_path](const std::string &content) {
bool is_directory=boost::filesystem::is_directory(source_path); bool is_directory = boost::filesystem::is_directory(source_path);
auto target_path = (is_directory ? source_path : source_path.parent_path())/content; auto target_path = (is_directory ? source_path : source_path.parent_path()) / content;
if(!boost::filesystem::exists(target_path)) { if(!boost::filesystem::exists(target_path)) {
if(filesystem::write(target_path, "")) { if(filesystem::write(target_path, "")) {
update(); update();
@ -163,41 +163,41 @@ Directories::Directories() : Gtk::ListViewText(1) {
on_save_file(target_path); on_save_file(target_path);
} }
else { else {
Terminal::get().print("Error: could not create "+target_path.string()+'\n', true); Terminal::get().print("Error: could not create " + target_path.string() + '\n', true);
return; return;
} }
} }
else { else {
Terminal::get().print("Error: could not create "+target_path.string()+": already exists\n", true); Terminal::get().print("Error: could not create " + target_path.string() + ": already exists\n", true);
return; return;
} }
EntryBox::get().hide(); EntryBox::get().hide();
}); });
auto entry_it=EntryBox::get().entries.begin(); auto entry_it = EntryBox::get().entries.begin();
entry_it->set_placeholder_text("Filename"); entry_it->set_placeholder_text("Filename");
EntryBox::get().buttons.emplace_back("Create New File", [entry_it](){ EntryBox::get().buttons.emplace_back("Create New File", [entry_it]() {
entry_it->activate(); entry_it->activate();
}); });
EntryBox::get().show(); EntryBox::get().show();
}; };
menu_item_new_file.set_label(new_file_label); menu_item_new_file.set_label(new_file_label);
menu_item_new_file.signal_activate().connect(new_file_function); menu_item_new_file.signal_activate().connect(new_file_function);
menu.append(menu_item_new_file); menu.append(menu_item_new_file);
menu_root_item_new_file.set_label(new_file_label); menu_root_item_new_file.set_label(new_file_label);
menu_root_item_new_file.signal_activate().connect(new_file_function); menu_root_item_new_file.signal_activate().connect(new_file_function);
menu_root.append(menu_root_item_new_file); menu_root.append(menu_root_item_new_file);
auto new_folder_label = "New Folder"; auto new_folder_label = "New Folder";
auto new_folder_function = [this] { auto new_folder_function = [this] {
if(menu_popup_row_path.empty()) if(menu_popup_row_path.empty())
return; return;
EntryBox::get().clear(); EntryBox::get().clear();
EntryBox::get().entries.emplace_back("", [this, source_path=menu_popup_row_path](const std::string &content) { EntryBox::get().entries.emplace_back("", [this, source_path = menu_popup_row_path](const std::string &content) {
bool is_directory=boost::filesystem::is_directory(source_path); bool is_directory = boost::filesystem::is_directory(source_path);
auto target_path = (is_directory ? source_path : source_path.parent_path())/content; auto target_path = (is_directory ? source_path : source_path.parent_path()) / content;
if(!boost::filesystem::exists(target_path)) { if(!boost::filesystem::exists(target_path)) {
boost::system::error_code ec; boost::system::error_code ec;
boost::filesystem::create_directory(target_path, ec); boost::filesystem::create_directory(target_path, ec);
@ -206,154 +206,154 @@ Directories::Directories() : Gtk::ListViewText(1) {
select(target_path); select(target_path);
} }
else { else {
Terminal::get().print("Error: could not create "+target_path.string()+": "+ec.message(), true); Terminal::get().print("Error: could not create " + target_path.string() + ": " + ec.message(), true);
return; return;
} }
} }
else { else {
Terminal::get().print("Error: could not create "+target_path.string()+": already exists\n", true); Terminal::get().print("Error: could not create " + target_path.string() + ": already exists\n", true);
return; return;
} }
EntryBox::get().hide(); EntryBox::get().hide();
}); });
auto entry_it=EntryBox::get().entries.begin(); auto entry_it = EntryBox::get().entries.begin();
entry_it->set_placeholder_text("Folder name"); entry_it->set_placeholder_text("Folder name");
EntryBox::get().buttons.emplace_back("Create New Folder", [entry_it](){ EntryBox::get().buttons.emplace_back("Create New Folder", [entry_it]() {
entry_it->activate(); entry_it->activate();
}); });
EntryBox::get().show(); EntryBox::get().show();
}; };
menu_item_new_folder.set_label(new_folder_label); menu_item_new_folder.set_label(new_folder_label);
menu_item_new_folder.signal_activate().connect(new_folder_function); menu_item_new_folder.signal_activate().connect(new_folder_function);
menu.append(menu_item_new_folder); menu.append(menu_item_new_folder);
menu_root_item_new_folder.set_label(new_folder_label); menu_root_item_new_folder.set_label(new_folder_label);
menu_root_item_new_folder.signal_activate().connect(new_folder_function); menu_root_item_new_folder.signal_activate().connect(new_folder_function);
menu_root.append(menu_root_item_new_folder); menu_root.append(menu_root_item_new_folder);
menu.append(menu_item_separator); menu.append(menu_item_separator);
menu_item_rename.set_label("Rename"); menu_item_rename.set_label("Rename");
menu_item_rename.signal_activate().connect([this] { menu_item_rename.signal_activate().connect([this] {
if(menu_popup_row_path.empty()) if(menu_popup_row_path.empty())
return; return;
EntryBox::get().clear(); EntryBox::get().clear();
EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), [this, source_path=menu_popup_row_path](const std::string &content){ EntryBox::get().entries.emplace_back(menu_popup_row_path.filename().string(), [this, source_path = menu_popup_row_path](const std::string &content) {
bool is_directory=boost::filesystem::is_directory(source_path); bool is_directory = boost::filesystem::is_directory(source_path);
auto target_path=source_path.parent_path()/content; auto target_path = source_path.parent_path() / content;
if(boost::filesystem::exists(target_path)) { if(boost::filesystem::exists(target_path)) {
Terminal::get().print("Error: could not rename to "+target_path.string()+": already exists\n", true); Terminal::get().print("Error: could not rename to " + target_path.string() + ": already exists\n", true);
return; return;
} }
if(is_directory) if(is_directory)
this->remove_path(source_path); this->remove_path(source_path);
boost::system::error_code ec; boost::system::error_code ec;
boost::filesystem::rename(source_path, target_path, ec); boost::filesystem::rename(source_path, target_path, ec);
if(ec) { if(ec) {
Terminal::get().print("Error: could not rename "+source_path.string()+": "+ec.message()+'\n', true); Terminal::get().print("Error: could not rename " + source_path.string() + ": " + ec.message() + '\n', true);
return; return;
} }
update(); update();
on_save_file(target_path); on_save_file(target_path);
select(target_path); select(target_path);
for(size_t c=0;c<Notebook::get().size();c++) { for(size_t c = 0; c < Notebook::get().size(); c++) {
auto view=Notebook::get().get_view(c); auto view = Notebook::get().get_view(c);
if(is_directory) { if(is_directory) {
if(filesystem::file_in_path(view->file_path, source_path)) { if(filesystem::file_in_path(view->file_path, source_path)) {
auto file_it=view->file_path.begin(); auto file_it = view->file_path.begin();
for(auto source_it=source_path.begin();source_it!=source_path.end();source_it++) for(auto source_it = source_path.begin(); source_it != source_path.end(); source_it++)
file_it++; file_it++;
auto new_file_path=target_path; auto new_file_path = target_path;
for(;file_it!=view->file_path.end();file_it++) for(; file_it != view->file_path.end(); file_it++)
new_file_path/=*file_it; new_file_path /= *file_it;
view->rename(new_file_path); view->rename(new_file_path);
} }
} }
else if(view->file_path==source_path) { else if(view->file_path == source_path) {
view->rename(target_path); view->rename(target_path);
std::string old_language_id; std::string old_language_id;
if(view->language) if(view->language)
old_language_id=view->language->get_id(); old_language_id = view->language->get_id();
view->language=Source::guess_language(target_path); view->language = Source::guess_language(target_path);
std::string new_language_id; std::string new_language_id;
if(view->language) if(view->language)
new_language_id=view->language->get_id(); new_language_id = view->language->get_id();
if(new_language_id!=old_language_id) if(new_language_id != old_language_id)
Terminal::get().print("Warning: language for "+target_path.string()+" has changed. Please reopen the file\n"); Terminal::get().print("Warning: language for " + target_path.string() + " has changed. Please reopen the file\n");
} }
} }
EntryBox::get().hide(); EntryBox::get().hide();
}); });
auto entry_it=EntryBox::get().entries.begin(); auto entry_it = EntryBox::get().entries.begin();
entry_it->set_placeholder_text("Filename"); entry_it->set_placeholder_text("Filename");
EntryBox::get().buttons.emplace_back("Rename file", [entry_it](){ EntryBox::get().buttons.emplace_back("Rename file", [entry_it]() {
entry_it->activate(); entry_it->activate();
}); });
EntryBox::get().show(); EntryBox::get().show();
auto end_pos=Glib::ustring(menu_popup_row_path.filename().string()).rfind('.'); auto end_pos = Glib::ustring(menu_popup_row_path.filename().string()).rfind('.');
if(end_pos!=Glib::ustring::npos) if(end_pos != Glib::ustring::npos)
entry_it->select_region(0, end_pos); entry_it->select_region(0, end_pos);
}); });
menu.append(menu_item_rename); menu.append(menu_item_rename);
menu_item_delete.set_label("Delete"); menu_item_delete.set_label("Delete");
menu_item_delete.signal_activate().connect([this] { menu_item_delete.signal_activate().connect([this] {
if(menu_popup_row_path.empty()) if(menu_popup_row_path.empty())
return; return;
Gtk::MessageDialog dialog(*static_cast<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_default_response(Gtk::RESPONSE_NO);
dialog.set_secondary_text("Are you sure you want to delete "+menu_popup_row_path.string()+"?"); dialog.set_secondary_text("Are you sure you want to delete " + menu_popup_row_path.string() + "?");
int result = dialog.run(); int result = dialog.run();
if(result==Gtk::RESPONSE_YES) { if(result == Gtk::RESPONSE_YES) {
bool is_directory=boost::filesystem::is_directory(menu_popup_row_path); bool is_directory = boost::filesystem::is_directory(menu_popup_row_path);
boost::system::error_code ec; boost::system::error_code ec;
boost::filesystem::remove_all(menu_popup_row_path, ec); boost::filesystem::remove_all(menu_popup_row_path, ec);
if(ec) if(ec)
Terminal::get().print("Error: could not delete "+menu_popup_row_path.string()+": "+ec.message()+"\n", true); Terminal::get().print("Error: could not delete " + menu_popup_row_path.string() + ": " + ec.message() + "\n", true);
else { else {
update(); update();
for(size_t c=0;c<Notebook::get().size();c++) { for(size_t c = 0; c < Notebook::get().size(); c++) {
auto view=Notebook::get().get_view(c); auto view = Notebook::get().get_view(c);
if(is_directory) { if(is_directory) {
if(filesystem::file_in_path(view->file_path, menu_popup_row_path)) if(filesystem::file_in_path(view->file_path, menu_popup_row_path))
view->get_buffer()->set_modified(); view->get_buffer()->set_modified();
} }
else if(view->file_path==menu_popup_row_path) else if(view->file_path == menu_popup_row_path)
view->get_buffer()->set_modified(); view->get_buffer()->set_modified();
} }
} }
} }
}); });
menu.append(menu_item_delete); menu.append(menu_item_delete);
menu.show_all(); menu.show_all();
menu.accelerate(*this); menu.accelerate(*this);
menu_root.show_all(); menu_root.show_all();
menu_root.accelerate(*this); menu_root.accelerate(*this);
set_headers_clickable(); set_headers_clickable();
forall([this](Gtk::Widget &widget) { forall([this](Gtk::Widget &widget) {
if(widget.get_name()=="GtkButton") { if(widget.get_name() == "GtkButton") {
widget.signal_button_press_event().connect([this](GdkEventButton *event) { widget.signal_button_press_event().connect([this](GdkEventButton *event) {
if(event->type==GDK_BUTTON_PRESS && event->button==GDK_BUTTON_SECONDARY && !path.empty()) { if(event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY && !path.empty()) {
menu_popup_row_path=this->path; menu_popup_row_path = this->path;
menu_root.popup(event->button, event->time); menu_root.popup(event->button, event->time);
} }
return true; return true;
@ -370,40 +370,40 @@ void Directories::open(const boost::filesystem::path &dir_path) {
boost::system::error_code ec; boost::system::error_code ec;
if(dir_path.empty() || !boost::filesystem::exists(dir_path, ec) || ec) if(dir_path.empty() || !boost::filesystem::exists(dir_path, ec) || ec)
return; return;
tree_store->clear(); tree_store->clear();
path=filesystem::get_normal_path(dir_path); path = filesystem::get_normal_path(dir_path);
//TODO: report that set_title does not handle '_' correctly? //TODO: report that set_title does not handle '_' correctly?
auto title=path.filename().string(); auto title = path.filename().string();
size_t pos=0; size_t pos = 0;
while((pos=title.find('_', pos))!=std::string::npos) { while((pos = title.find('_', pos)) != std::string::npos) {
title.replace(pos, 1, "__"); title.replace(pos, 1, "__");
pos+=2; pos += 2;
} }
get_column(0)->set_title(title); get_column(0)->set_title(title);
for(auto &directory: directories) { for(auto &directory : directories) {
if(directory.second.repository) if(directory.second.repository)
directory.second.repository->clear_saved_status(); directory.second.repository->clear_saved_status();
} }
directories.clear(); directories.clear();
add_or_update_path(path, Gtk::TreeModel::Row(), true); add_or_update_path(path, Gtk::TreeModel::Row(), true);
} }
void Directories::update() { void Directories::update() {
std::vector<std::pair<std::string, Gtk::TreeModel::Row> > saved_directories; std::vector<std::pair<std::string, Gtk::TreeModel::Row>> saved_directories;
for(auto &directory: directories) for(auto &directory : directories)
saved_directories.emplace_back(directory.first, directory.second.row); saved_directories.emplace_back(directory.first, directory.second.row);
for(auto &directory: saved_directories) for(auto &directory : saved_directories)
add_or_update_path(directory.first, directory.second, false); add_or_update_path(directory.first, directory.second, false);
} }
void Directories::on_save_file(const boost::filesystem::path &file_path) { void Directories::on_save_file(const boost::filesystem::path &file_path) {
auto it=directories.find(file_path.parent_path().string()); auto it = directories.find(file_path.parent_path().string());
if(it!=directories.end()) { if(it != directories.end()) {
if(it->second.repository) if(it->second.repository)
it->second.repository->clear_saved_status(); it->second.repository->clear_saved_status();
colorize_path(it->first, true); colorize_path(it->first, true);
@ -411,32 +411,32 @@ void Directories::on_save_file(const boost::filesystem::path &file_path) {
} }
void Directories::select(const boost::filesystem::path &select_path) { void Directories::select(const boost::filesystem::path &select_path) {
if(path=="") if(path == "")
return; return;
if(!filesystem::file_in_path(select_path, path)) if(!filesystem::file_in_path(select_path, path))
return; return;
//return if the select_path is already selected //return if the select_path is already selected
auto iter=get_selection()->get_selected(); auto iter = get_selection()->get_selected();
if(iter) { if(iter) {
if(iter->get_value(column_record.path)==select_path) if(iter->get_value(column_record.path) == select_path)
return; return;
} }
std::list<boost::filesystem::path> paths; std::list<boost::filesystem::path> paths;
boost::filesystem::path parent_path; boost::filesystem::path parent_path;
if(boost::filesystem::is_directory(select_path)) if(boost::filesystem::is_directory(select_path))
parent_path=select_path; parent_path = select_path;
else else
parent_path=select_path.parent_path(); parent_path = select_path.parent_path();
//check if select_path is already expanded //check if select_path is already expanded
if(directories.find(parent_path.string())!=directories.end()) { if(directories.find(parent_path.string()) != directories.end()) {
//set cursor at select_path and return //set cursor at select_path and return
tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter){ tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter) {
if(iter->get_value(column_record.path)==select_path) { if(iter->get_value(column_record.path) == select_path) {
auto tree_path=Gtk::TreePath(iter); auto tree_path = Gtk::TreePath(iter);
expand_to_path(tree_path); expand_to_path(tree_path);
set_cursor(tree_path); set_cursor(tree_path);
return true; return true;
@ -445,28 +445,28 @@ void Directories::select(const boost::filesystem::path &select_path) {
}); });
return; return;
} }
paths.emplace_front(parent_path); paths.emplace_front(parent_path);
while(parent_path!=path) { while(parent_path != path) {
parent_path=parent_path.parent_path(); parent_path = parent_path.parent_path();
paths.emplace_front(parent_path); paths.emplace_front(parent_path);
} }
//expand to select_path //expand to select_path
for(auto &a_path: paths) { for(auto &a_path : paths) {
tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator &iter){ tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator &iter) {
if(iter->get_value(column_record.path)==a_path) { if(iter->get_value(column_record.path) == a_path) {
add_or_update_path(a_path, *iter, true); add_or_update_path(a_path, *iter, true);
return true; return true;
} }
return false; return false;
}); });
} }
//set cursor at select_path //set cursor at select_path
tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter){ tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter) {
if(iter->get_value(column_record.path)==select_path) { if(iter->get_value(column_record.path) == select_path) {
auto tree_path=Gtk::TreePath(iter); auto tree_path = Gtk::TreePath(iter);
expand_to_path(tree_path); expand_to_path(tree_path);
set_cursor(tree_path); set_cursor(tree_path);
return true; return true;
@ -475,18 +475,18 @@ void Directories::select(const boost::filesystem::path &select_path) {
}); });
} }
bool Directories::on_button_press_event(GdkEventButton* event) { bool Directories::on_button_press_event(GdkEventButton *event) {
if(event->type==GDK_BUTTON_PRESS && event->button==GDK_BUTTON_SECONDARY) { if(event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) {
EntryBox::get().hide(); EntryBox::get().hide();
Gtk::TreeModel::Path path; Gtk::TreeModel::Path path;
if(get_path_at_pos(static_cast<int>(event->x), static_cast<int>(event->y), path)) { if(get_path_at_pos(static_cast<int>(event->x), static_cast<int>(event->y), path)) {
menu_popup_row_path=get_model()->get_iter(path)->get_value(column_record.path); menu_popup_row_path = get_model()->get_iter(path)->get_value(column_record.path);
if(menu_popup_row_path.empty()) { if(menu_popup_row_path.empty()) {
auto parent=get_model()->get_iter(path)->parent(); auto parent = get_model()->get_iter(path)->parent();
if(parent) if(parent)
menu_popup_row_path=parent->get_value(column_record.path); menu_popup_row_path = parent->get_value(column_record.path);
else { else {
menu_popup_row_path=this->path; menu_popup_row_path = this->path;
menu_root.popup(event->button, event->time); menu_root.popup(event->button, event->time);
return true; return true;
} }
@ -495,88 +495,89 @@ bool Directories::on_button_press_event(GdkEventButton* event) {
return true; return true;
} }
else if(!this->path.empty()) { else if(!this->path.empty()) {
menu_popup_row_path=this->path; menu_popup_row_path = this->path;
menu_root.popup(event->button, event->time); menu_root.popup(event->button, event->time);
return true; return true;
} }
} }
return Gtk::TreeView::on_button_press_event(event); return Gtk::TreeView::on_button_press_event(event);
} }
void Directories::add_or_update_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, bool include_parent_paths) { void Directories::add_or_update_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, bool include_parent_paths) {
auto path_it=directories.find(dir_path.string()); auto path_it = directories.find(dir_path.string());
if(!boost::filesystem::exists(dir_path)) { if(!boost::filesystem::exists(dir_path)) {
if(path_it!=directories.end()) if(path_it != directories.end())
directories.erase(path_it); directories.erase(path_it);
return; return;
} }
if(path_it==directories.end()) { if(path_it == directories.end()) {
auto g_file=Gio::File::create_for_path(dir_path.string()); auto g_file = Gio::File::create_for_path(dir_path.string());
auto monitor=g_file->monitor_directory(Gio::FileMonitorFlags::FILE_MONITOR_WATCH_MOVES); auto monitor = g_file->monitor_directory(Gio::FileMonitorFlags::FILE_MONITOR_WATCH_MOVES);
auto path_and_row=std::make_shared<std::pair<boost::filesystem::path, Gtk::TreeModel::Row> >(dir_path, row); auto path_and_row = std::make_shared<std::pair<boost::filesystem::path, Gtk::TreeModel::Row>>(dir_path, row);
auto connection=std::make_shared<sigc::connection>(); auto connection = std::make_shared<sigc::connection>();
std::shared_ptr<Git::Repository> repository; std::shared_ptr<Git::Repository> repository;
try { try {
repository=Git::get_repository(dir_path); repository = Git::get_repository(dir_path);
} }
catch(const std::exception &) {} catch(const std::exception &) {
}
monitor->signal_changed().connect([this, connection, path_and_row, repository] (const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File>&, monitor->signal_changed().connect([this, connection, path_and_row, repository](const Glib::RefPtr<Gio::File> &file,
Gio::FileMonitorEvent monitor_event) { const Glib::RefPtr<Gio::File> &,
if(monitor_event!=Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { Gio::FileMonitorEvent monitor_event) {
if(monitor_event != Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
if(repository) if(repository)
repository->clear_saved_status(); repository->clear_saved_status();
connection->disconnect(); connection->disconnect();
*connection=Glib::signal_timeout().connect([path_and_row, this]() { *connection = Glib::signal_timeout().connect([path_and_row, this]() {
if(directories.find(path_and_row->first.string())!=directories.end()) if(directories.find(path_and_row->first.string()) != directories.end())
add_or_update_path(path_and_row->first, path_and_row->second, true); add_or_update_path(path_and_row->first, path_and_row->second, true);
return false; return false;
}, 500); }, 500);
} }
}); });
std::shared_ptr<sigc::connection> repository_connection(new sigc::connection(), [](sigc::connection *connection) { std::shared_ptr<sigc::connection> repository_connection(new sigc::connection(), [](sigc::connection *connection) {
connection->disconnect(); connection->disconnect();
delete connection; delete connection;
}); });
if(repository) { if(repository) {
auto connection=std::make_shared<sigc::connection>(); auto connection = std::make_shared<sigc::connection>();
*repository_connection=repository->monitor->signal_changed().connect([this, connection, path_and_row](const Glib::RefPtr<Gio::File> &file, *repository_connection = repository->monitor->signal_changed().connect([this, connection, path_and_row](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File>&, const Glib::RefPtr<Gio::File> &,
Gio::FileMonitorEvent monitor_event) { Gio::FileMonitorEvent monitor_event) {
if(monitor_event!=Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { if(monitor_event != Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
connection->disconnect(); connection->disconnect();
*connection=Glib::signal_timeout().connect([this, path_and_row] { *connection = Glib::signal_timeout().connect([this, path_and_row] {
if(directories.find(path_and_row->first.string())!=directories.end()) if(directories.find(path_and_row->first.string()) != directories.end())
colorize_path(path_and_row->first, false); colorize_path(path_and_row->first, false);
return false; return false;
}, 500); }, 500);
} }
}); });
} }
directories[dir_path.string()]={row, monitor, repository, repository_connection}; directories[dir_path.string()] = {row, monitor, repository, repository_connection};
} }
Gtk::TreeNodeChildren children(row?row.children():tree_store->children()); Gtk::TreeNodeChildren children(row ? row.children() : tree_store->children());
if(children) { if(children) {
if(children.begin()->get_value(column_record.path)=="") if(children.begin()->get_value(column_record.path) == "")
tree_store->erase(children.begin()); tree_store->erase(children.begin());
} }
std::unordered_set<std::string> not_deleted; std::unordered_set<std::string> not_deleted;
boost::filesystem::directory_iterator end_it; boost::filesystem::directory_iterator end_it;
for(boost::filesystem::directory_iterator it(dir_path);it!=end_it;it++) { for(boost::filesystem::directory_iterator it(dir_path); it != end_it; it++) {
auto filename=it->path().filename().string(); auto filename = it->path().filename().string();
bool already_added=false; bool already_added = false;
if(children) { if(children) {
for(auto &child: children) { for(auto &child : children) {
if(child->get_value(column_record.name)==filename) { if(child->get_value(column_record.name) == filename) {
not_deleted.emplace(filename); not_deleted.emplace(filename);
already_added=true; already_added = true;
break; break;
} }
} }
@ -587,59 +588,59 @@ void Directories::add_or_update_path(const boost::filesystem::path &dir_path, co
child->set_value(column_record.name, filename); child->set_value(column_record.name, filename);
child->set_value(column_record.markup, Glib::Markup::escape_text(filename)); child->set_value(column_record.markup, Glib::Markup::escape_text(filename));
child->set_value(column_record.path, it->path()); child->set_value(column_record.path, it->path());
if (boost::filesystem::is_directory(it->path())) { if(boost::filesystem::is_directory(it->path())) {
child->set_value(column_record.id, '1'+filename); child->set_value(column_record.id, '1' + filename);
auto grandchild=tree_store->append(child->children()); auto grandchild = tree_store->append(child->children());
grandchild->set_value(column_record.name, std::string("(empty)")); grandchild->set_value(column_record.name, std::string("(empty)"));
grandchild->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); grandchild->set_value(column_record.markup, Glib::Markup::escape_text("(empty)"));
grandchild->set_value(column_record.type, PathType::UNKNOWN); grandchild->set_value(column_record.type, PathType::UNKNOWN);
} }
else { else {
child->set_value(column_record.id, '2'+filename); child->set_value(column_record.id, '2' + filename);
auto language=Source::guess_language(it->path().filename()); auto language = Source::guess_language(it->path().filename());
if(!language) if(!language)
child->set_value(column_record.type, PathType::UNKNOWN); child->set_value(column_record.type, PathType::UNKNOWN);
} }
} }
} }
if(children) { if(children) {
for(auto it=children.begin();it!=children.end();) { for(auto it = children.begin(); it != children.end();) {
if(not_deleted.count(it->get_value(column_record.name))==0) { if(not_deleted.count(it->get_value(column_record.name)) == 0) {
it=tree_store->erase(it); it = tree_store->erase(it);
} }
else else
it++; it++;
} }
} }
if(!children) { if(!children) {
auto child=tree_store->append(children); auto child = tree_store->append(children);
child->set_value(column_record.name, std::string("(empty)")); child->set_value(column_record.name, std::string("(empty)"));
child->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); child->set_value(column_record.markup, Glib::Markup::escape_text("(empty)"));
child->set_value(column_record.type, PathType::UNKNOWN); child->set_value(column_record.type, PathType::UNKNOWN);
} }
colorize_path(dir_path, include_parent_paths); colorize_path(dir_path, include_parent_paths);
} }
void Directories::remove_path(const boost::filesystem::path &dir_path) { void Directories::remove_path(const boost::filesystem::path &dir_path) {
auto it=directories.find(dir_path.string()); auto it = directories.find(dir_path.string());
if(it==directories.end()) if(it == directories.end())
return; return;
auto children=it->second.row->children(); auto children = it->second.row->children();
for(auto it=directories.begin();it!=directories.end();) { for(auto it = directories.begin(); it != directories.end();) {
if(filesystem::file_in_path(it->first, dir_path)) if(filesystem::file_in_path(it->first, dir_path))
it=directories.erase(it); it = directories.erase(it);
else else
it++; it++;
} }
if(children) { if(children) {
while(children) { while(children) {
tree_store->erase(children.begin()); tree_store->erase(children.begin());
} }
auto child=tree_store->append(children); auto child = tree_store->append(children);
child->set_value(column_record.name, std::string("(empty)")); child->set_value(column_record.name, std::string("(empty)"));
child->set_value(column_record.markup, Glib::Markup::escape_text("(empty)")); child->set_value(column_record.markup, Glib::Markup::escape_text("(empty)"));
child->set_value(column_record.type, PathType::UNKNOWN); child->set_value(column_record.type, PathType::UNKNOWN);
@ -647,82 +648,82 @@ void Directories::remove_path(const boost::filesystem::path &dir_path) {
} }
void Directories::colorize_path(boost::filesystem::path dir_path_, bool include_parent_paths) { void Directories::colorize_path(boost::filesystem::path dir_path_, bool include_parent_paths) {
auto dir_path=std::make_shared<boost::filesystem::path>(std::move(dir_path_)); auto dir_path = std::make_shared<boost::filesystem::path>(std::move(dir_path_));
auto it=directories.find(dir_path->string()); auto it = directories.find(dir_path->string());
if(it==directories.end()) if(it == directories.end())
return; return;
if(it!=directories.end() && it->second.repository) { if(it != directories.end() && it->second.repository) {
auto repository=it->second.repository; auto repository = it->second.repository;
std::thread git_status_thread([this, dir_path, repository, include_parent_paths] { std::thread git_status_thread([this, dir_path, repository, include_parent_paths] {
Git::Repository::Status status; Git::Repository::Status status;
try { try {
status=repository->get_status(); status = repository->get_status();
} }
catch(const std::exception &e) { catch(const std::exception &e) {
Terminal::get().async_print(std::string("Error (git): ")+e.what()+'\n', true); Terminal::get().async_print(std::string("Error (git): ") + e.what() + '\n', true);
} }
dispatcher.post([this, dir_path, include_parent_paths, status=std::move(status)] { dispatcher.post([this, dir_path, include_parent_paths, status = std::move(status)] {
auto it=directories.find(dir_path->string()); auto it = directories.find(dir_path->string());
if(it==directories.end()) if(it == directories.end())
return; return;
auto normal_color=get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL); auto normal_color = get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL);
Gdk::RGBA gray; Gdk::RGBA gray;
gray.set_rgba(0.5, 0.5, 0.5); gray.set_rgba(0.5, 0.5, 0.5);
Gdk::RGBA yellow; Gdk::RGBA yellow;
yellow.set_rgba(1.0, 1.0, 0.2); yellow.set_rgba(1.0, 1.0, 0.2);
double factor=0.5; double factor = 0.5;
yellow.set_red(normal_color.get_red()+factor*(yellow.get_red()-normal_color.get_red())); yellow.set_red(normal_color.get_red() + factor * (yellow.get_red() - normal_color.get_red()));
yellow.set_green(normal_color.get_green()+factor*(yellow.get_green()-normal_color.get_green())); yellow.set_green(normal_color.get_green() + factor * (yellow.get_green() - normal_color.get_green()));
yellow.set_blue(normal_color.get_blue()+factor*(yellow.get_blue()-normal_color.get_blue())); yellow.set_blue(normal_color.get_blue() + factor * (yellow.get_blue() - normal_color.get_blue()));
Gdk::RGBA green; Gdk::RGBA green;
green.set_rgba(0.0, 1.0, 0.0); green.set_rgba(0.0, 1.0, 0.0);
factor=0.4; factor = 0.4;
green.set_red(normal_color.get_red()+factor*(green.get_red()-normal_color.get_red())); green.set_red(normal_color.get_red() + factor * (green.get_red() - normal_color.get_red()));
green.set_green(normal_color.get_green()+factor*(green.get_green()-normal_color.get_green())); green.set_green(normal_color.get_green() + factor * (green.get_green() - normal_color.get_green()));
green.set_blue(normal_color.get_blue()+factor*(green.get_blue()-normal_color.get_blue())); green.set_blue(normal_color.get_blue() + factor * (green.get_blue() - normal_color.get_blue()));
do { do {
Gtk::TreeNodeChildren children(it->second.row?it->second.row.children():tree_store->children()); Gtk::TreeNodeChildren children(it->second.row ? it->second.row.children() : tree_store->children());
if(!children) if(!children)
return; return;
for(auto &child: children) { for(auto &child : children) {
auto name=Glib::Markup::escape_text(child.get_value(column_record.name)); auto name = Glib::Markup::escape_text(child.get_value(column_record.name));
auto path=child.get_value(column_record.path); auto path = child.get_value(column_record.path);
Gdk::RGBA *color; Gdk::RGBA *color;
if(status.modified.find(path.generic_string())!=status.modified.end()) if(status.modified.find(path.generic_string()) != status.modified.end())
color=&yellow; color = &yellow;
else if(status.added.find(path.generic_string())!=status.added.end()) else if(status.added.find(path.generic_string()) != status.added.end())
color=&green; color = &green;
else else
color=&normal_color; color = &normal_color;
std::stringstream ss; std::stringstream ss;
ss << '#' << std::setfill('0') << std::hex; ss << '#' << std::setfill('0') << std::hex;
ss << std::setw(2) << std::hex << (color->get_red_u()>>8); ss << std::setw(2) << std::hex << (color->get_red_u() >> 8);
ss << std::setw(2) << std::hex << (color->get_green_u()>>8); ss << std::setw(2) << std::hex << (color->get_green_u() >> 8);
ss << std::setw(2) << std::hex << (color->get_blue_u()>>8); ss << std::setw(2) << std::hex << (color->get_blue_u() >> 8);
child.set_value(column_record.markup, "<span foreground=\""+ss.str()+"\">"+name+"</span>"); child.set_value(column_record.markup, "<span foreground=\"" + ss.str() + "\">" + name + "</span>");
auto type=child.get_value(column_record.type); auto type = child.get_value(column_record.type);
if(type==PathType::UNKNOWN) if(type == PathType::UNKNOWN)
child.set_value(column_record.markup, "<i>"+child.get_value(column_record.markup)+"</i>"); child.set_value(column_record.markup, "<i>" + child.get_value(column_record.markup) + "</i>");
} }
if(!include_parent_paths) if(!include_parent_paths)
break; break;
auto path=boost::filesystem::path(it->first); auto path = boost::filesystem::path(it->first);
if(boost::filesystem::exists(path/".git")) if(boost::filesystem::exists(path / ".git"))
break; break;
if(path==path.root_directory()) if(path == path.root_directory())
break; break;
auto parent_path=boost::filesystem::path(it->first).parent_path(); auto parent_path = boost::filesystem::path(it->first).parent_path();
it=directories.find(parent_path.string()); it = directories.find(parent_path.string());
} while(it!=directories.end()); } while(it != directories.end());
}); });
}); });
git_status_thread.detach(); git_status_thread.detach();

49
src/directories.h

@ -1,15 +1,15 @@
#pragma once #pragma once
#include "boost/filesystem.hpp"
#include "dispatcher.h"
#include "git.h"
#include <atomic>
#include <gtkmm.h> #include <gtkmm.h>
#include <vector> #include <mutex>
#include <string> #include <string>
#include "boost/filesystem.hpp"
#include <thread> #include <thread>
#include <mutex>
#include <atomic>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include "git.h" #include <vector>
#include "dispatcher.h"
class Directories : public Gtk::ListViewText { class Directories : public Gtk::ListViewText {
class DirectoryData { class DirectoryData {
@ -19,17 +19,17 @@ class Directories : public Gtk::ListViewText {
std::shared_ptr<Git::Repository> repository; std::shared_ptr<Git::Repository> repository;
std::shared_ptr<sigc::connection> connection; std::shared_ptr<sigc::connection> connection;
}; };
enum class PathType {KNOWN, UNKNOWN}; enum class PathType { KNOWN, UNKNOWN };
class TreeStore : public Gtk::TreeStore { class TreeStore : public Gtk::TreeStore {
protected: protected:
TreeStore()=default; TreeStore() = default;
bool row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const override; bool row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const override;
bool drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) override; bool drag_data_received_vfunc(const TreeModel::Path &path, const Gtk::SelectionData &selection_data) override;
bool drag_data_delete_vfunc (const Gtk::TreeModel::Path &path) override; bool drag_data_delete_vfunc(const Gtk::TreeModel::Path &path) override;
public: public:
class ColumnRecord : public Gtk::TreeModel::ColumnRecord { class ColumnRecord : public Gtk::TreeModel::ColumnRecord {
public: public:
@ -46,40 +46,41 @@ class Directories : public Gtk::ListViewText {
Gtk::TreeModelColumn<boost::filesystem::path> path; Gtk::TreeModelColumn<boost::filesystem::path> path;
Gtk::TreeModelColumn<PathType> type; Gtk::TreeModelColumn<PathType> type;
}; };
static Glib::RefPtr<TreeStore> create() {return Glib::RefPtr<TreeStore>(new TreeStore());} static Glib::RefPtr<TreeStore> create() { return Glib::RefPtr<TreeStore>(new TreeStore()); }
}; };
Directories(); Directories();
public: public:
static Directories &get() { static Directories &get() {
static Directories singleton; static Directories singleton;
return singleton; return singleton;
} }
~Directories() override; ~Directories() override;
void open(const boost::filesystem::path &dir_path=""); void open(const boost::filesystem::path &dir_path = "");
void update(); void update();
void on_save_file(const boost::filesystem::path &file_path); void on_save_file(const boost::filesystem::path &file_path);
void select(const boost::filesystem::path &path); void select(const boost::filesystem::path &path);
boost::filesystem::path path; boost::filesystem::path path;
protected: protected:
bool on_button_press_event(GdkEventButton *event) override; bool on_button_press_event(GdkEventButton *event) override;
private: private:
void add_or_update_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, bool include_parent_paths); void add_or_update_path(const boost::filesystem::path &dir_path, const Gtk::TreeModel::Row &row, bool include_parent_paths);
void remove_path(const boost::filesystem::path &dir_path); void remove_path(const boost::filesystem::path &dir_path);
void colorize_path(boost::filesystem::path dir_path_, bool include_parent_paths); void colorize_path(boost::filesystem::path dir_path_, bool include_parent_paths);
Glib::RefPtr<Gtk::TreeStore> tree_store; Glib::RefPtr<Gtk::TreeStore> tree_store;
TreeStore::ColumnRecord column_record; TreeStore::ColumnRecord column_record;
std::unordered_map<std::string, DirectoryData> directories; std::unordered_map<std::string, DirectoryData> directories;
Dispatcher dispatcher; Dispatcher dispatcher;
Gtk::Menu menu; Gtk::Menu menu;
Gtk::MenuItem menu_item_new_file; Gtk::MenuItem menu_item_new_file;
Gtk::MenuItem menu_item_new_folder; Gtk::MenuItem menu_item_new_folder;

8
src/dispatcher.cc

@ -2,21 +2,21 @@
#include <vector> #include <vector>
Dispatcher::Dispatcher() { Dispatcher::Dispatcher() {
connection=dispatcher.connect([this] { connection = dispatcher.connect([this] {
std::vector<std::list<std::function<void()>>::iterator> its; std::vector<std::list<std::function<void()>>::iterator> its;
{ {
std::unique_lock<std::mutex> lock(functions_mutex); std::unique_lock<std::mutex> lock(functions_mutex);
if(functions.empty()) if(functions.empty())
return; return;
its.reserve(functions.size()); its.reserve(functions.size());
for(auto it=functions.begin();it!=functions.end();++it) for(auto it = functions.begin(); it != functions.end(); ++it)
its.emplace_back(it); its.emplace_back(it);
} }
for(auto &it: its) for(auto &it : its)
(*it)(); (*it)();
{ {
std::unique_lock<std::mutex> lock(functions_mutex); std::unique_lock<std::mutex> lock(functions_mutex);
for(auto &it: its) for(auto &it : its)
functions.erase(it); functions.erase(it);
} }
}); });

11
src/dispatcher.h

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <gtkmm.h>
#include <mutex>
#include <functional> #include <functional>
#include <gtkmm.h>
#include <list> #include <list>
#include <mutex>
class Dispatcher { class Dispatcher {
private: private:
@ -10,11 +10,12 @@ private:
std::mutex functions_mutex; std::mutex functions_mutex;
Glib::Dispatcher dispatcher; Glib::Dispatcher dispatcher;
sigc::connection connection; sigc::connection connection;
public: public:
Dispatcher(); Dispatcher();
~Dispatcher(); ~Dispatcher();
template<typename T> template <typename T>
void post(T &&function) { void post(T &&function) {
{ {
std::unique_lock<std::mutex> lock(functions_mutex); std::unique_lock<std::mutex> lock(functions_mutex);
@ -22,6 +23,6 @@ public:
} }
dispatcher(); dispatcher();
} }
void disconnect(); void disconnect();
}; };

38
src/documentation_cppreference.cc

@ -12173,34 +12173,34 @@ std::experimental::filesystem::is_socket cpp/experimental/fs/is_socket
std::experimental::filesystem::is_symlink cpp/experimental/fs/is_symlink std::experimental::filesystem::is_symlink cpp/experimental/fs/is_symlink
std::experimental::filesystem::status_known cpp/experimental/fs/status_known"} std::experimental::filesystem::status_known cpp/experimental/fs/status_known"}
)"; )";
class SymbolToUrl { class SymbolToUrl {
public: public:
SymbolToUrl(const std::string &symbol_urls) { SymbolToUrl(const std::string &symbol_urls) {
size_t symbol_start=0; size_t symbol_start = 0;
size_t symbol_end=std::string::npos; size_t symbol_end = std::string::npos;
size_t url_start=std::string::npos; size_t url_start = std::string::npos;
for(size_t c=0;c<symbol_urls.size();++c) { for(size_t c = 0; c < symbol_urls.size(); ++c) {
auto &chr=symbol_urls[c]; auto &chr = symbol_urls[c];
if(chr=='\t') { if(chr == '\t') {
symbol_end=c; symbol_end = c;
url_start=c+1; url_start = c + 1;
} }
else if(chr=='\n') { else if(chr == '\n') {
if(symbol_end!=std::string::npos && url_start!=std::string::npos) if(symbol_end != std::string::npos && url_start != std::string::npos)
map.emplace(symbol_urls.substr(symbol_start, symbol_end-symbol_start), symbol_urls.substr(url_start, c-url_start)); map.emplace(symbol_urls.substr(symbol_start, symbol_end - symbol_start), symbol_urls.substr(url_start, c - url_start));
symbol_start=c+1; symbol_start = c + 1;
symbol_end=std::string::npos; symbol_end = std::string::npos;
url_start=std::string::npos; url_start = std::string::npos;
} }
} }
} }
std::unordered_map<std::string, std::string> map; std::unordered_map<std::string, std::string> map;
}; };
static SymbolToUrl symbol_to_url(symbol_urls); static SymbolToUrl symbol_to_url(symbol_urls);
auto it=symbol_to_url.map.find(symbol); auto it = symbol_to_url.map.find(symbol);
if(it==symbol_to_url.map.end()) if(it == symbol_to_url.map.end())
return std::string(); return std::string();
return "http://en.cppreference.com/w/"+it->second; return "http://en.cppreference.com/w/" + it->second;
} }

2
src/documentation_cppreference.h

@ -6,4 +6,4 @@ namespace Documentation {
public: public:
static std::string get_url(const std::string &symbol) noexcept; static std::string get_url(const std::string &symbol) noexcept;
}; };
} } // namespace Documentation

62
src/entrybox.cc

@ -1,37 +1,37 @@
#include "entrybox.h" #include "entrybox.h"
std::unordered_map<std::string, std::vector<std::string> > EntryBox::entry_histories; std::unordered_map<std::string, std::vector<std::string>> EntryBox::entry_histories;
EntryBox::Entry::Entry(const std::string& content, std::function<void(const std::string& content)> on_activate_, unsigned width_chars) : Gtk::Entry(), on_activate(std::move(on_activate_)) { EntryBox::Entry::Entry(const std::string &content, std::function<void(const std::string &content)> on_activate_, unsigned width_chars) : Gtk::Entry(), on_activate(std::move(on_activate_)) {
set_max_length(0); set_max_length(0);
set_width_chars(width_chars); set_width_chars(width_chars);
set_text(content); set_text(content);
selected_history=0; selected_history = 0;
signal_activate().connect([this](){ signal_activate().connect([this]() {
if(this->on_activate) { if(this->on_activate) {
auto &history=EntryBox::entry_histories[get_placeholder_text()]; auto &history = EntryBox::entry_histories[get_placeholder_text()];
auto text=get_text(); auto text = get_text();
if(history.size()==0 || (history.size()>0 && *history.begin()!=text)) if(history.size() == 0 || (history.size() > 0 && *history.begin() != text))
history.emplace(history.begin(), text); history.emplace(history.begin(), text);
selected_history=0; selected_history = 0;
this->on_activate(text); this->on_activate(text);
} }
}); });
signal_key_press_event().connect([this](GdkEventKey* key){ signal_key_press_event().connect([this](GdkEventKey *key) {
if(key->keyval==GDK_KEY_Up || key->keyval==GDK_KEY_KP_Up) { if(key->keyval == GDK_KEY_Up || key->keyval == GDK_KEY_KP_Up) {
auto &history=entry_histories[get_placeholder_text()]; auto &history = entry_histories[get_placeholder_text()];
if(history.size()>0) { if(history.size() > 0) {
selected_history++; selected_history++;
if(selected_history>=history.size()) if(selected_history >= history.size())
selected_history=history.size()-1; selected_history = history.size() - 1;
set_text(history[selected_history]); set_text(history[selected_history]);
set_position(-1); set_position(-1);
} }
} }
if(key->keyval==GDK_KEY_Down || key->keyval==GDK_KEY_KP_Down) { if(key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_KP_Down) {
auto &history=entry_histories[get_placeholder_text()]; auto &history = entry_histories[get_placeholder_text()];
if(history.size()>0) { if(history.size() > 0) {
if(selected_history!=0) if(selected_history != 0)
selected_history--; selected_history--;
set_text(history[selected_history]); set_text(history[selected_history]);
set_position(-1); set_position(-1);
@ -41,25 +41,25 @@ EntryBox::Entry::Entry(const std::string& content, std::function<void(const std:
}); });
} }
EntryBox::Button::Button(const std::string& label, std::function<void()> on_activate_) : Gtk::Button(label), on_activate(std::move(on_activate_)) { EntryBox::Button::Button(const std::string &label, std::function<void()> on_activate_) : Gtk::Button(label), on_activate(std::move(on_activate_)) {
set_focus_on_click(false); set_focus_on_click(false);
signal_clicked().connect([this](){ signal_clicked().connect([this]() {
if(this->on_activate) if(this->on_activate)
this->on_activate(); this->on_activate();
}); });
} }
EntryBox::ToggleButton::ToggleButton(const std::string& label, std::function<void()> on_activate_) : Gtk::ToggleButton(label), on_activate(std::move(on_activate_)) { EntryBox::ToggleButton::ToggleButton(const std::string &label, std::function<void()> on_activate_) : Gtk::ToggleButton(label), on_activate(std::move(on_activate_)) {
set_focus_on_click(false); set_focus_on_click(false);
signal_clicked().connect([this](){ signal_clicked().connect([this]() {
if(this->on_activate) if(this->on_activate)
this->on_activate(); this->on_activate();
}); });
} }
EntryBox::Label::Label(std::function<void(int state, const std::string& message)> update_) : Gtk::Label(), update(std::move(update_)) { EntryBox::Label::Label(std::function<void(int state, const std::string &message)> update_) : Gtk::Label(), update(std::move(update_)) {
if(this->update) if(this->update)
this->update(-1, ""); this->update(-1, "");
} }
EntryBox::EntryBox() : Gtk::Box(Gtk::ORIENTATION_VERTICAL), upper_box(Gtk::ORIENTATION_HORIZONTAL), lower_box(Gtk::ORIENTATION_HORIZONTAL) { EntryBox::EntryBox() : Gtk::Box(Gtk::ORIENTATION_VERTICAL), upper_box(Gtk::ORIENTATION_HORIZONTAL), lower_box(Gtk::ORIENTATION_HORIZONTAL) {
@ -77,20 +77,20 @@ void EntryBox::clear() {
} }
void EntryBox::show() { void EntryBox::show() {
std::vector<Gtk::Widget*> focus_chain; std::vector<Gtk::Widget *> focus_chain;
for(auto& entry: entries) { for(auto &entry : entries) {
lower_box.pack_start(entry, Gtk::PACK_SHRINK); lower_box.pack_start(entry, Gtk::PACK_SHRINK);
focus_chain.emplace_back(&entry); focus_chain.emplace_back(&entry);
} }
for(auto& button: buttons) for(auto &button : buttons)
lower_box.pack_start(button, Gtk::PACK_SHRINK); lower_box.pack_start(button, Gtk::PACK_SHRINK);
for(auto& toggle_button: toggle_buttons) for(auto &toggle_button : toggle_buttons)
lower_box.pack_start(toggle_button, Gtk::PACK_SHRINK); lower_box.pack_start(toggle_button, Gtk::PACK_SHRINK);
for(auto& label: labels) for(auto &label : labels)
upper_box.pack_start(label, Gtk::PACK_SHRINK); upper_box.pack_start(label, Gtk::PACK_SHRINK);
lower_box.set_focus_chain(focus_chain); lower_box.set_focus_chain(focus_chain);
show_all(); show_all();
if(entries.size()>0) { if(entries.size() > 0) {
entries.begin()->grab_focus(); entries.begin()->grab_focus();
entries.begin()->select_region(0, entries.begin()->get_text_length()); entries.begin()->select_region(0, entries.begin()->get_text_length());
} }

30
src/entrybox.h

@ -1,54 +1,56 @@
#pragma once #pragma once
#include <list>
#include <functional>
#include "gtkmm.h" #include "gtkmm.h"
#include <unordered_map> #include <functional>
#include <list>
#include <string> #include <string>
#include <unordered_map>
#include <vector> #include <vector>
class EntryBox : public Gtk::Box { class EntryBox : public Gtk::Box {
public: public:
class Entry : public Gtk::Entry { class Entry : public Gtk::Entry {
public: public:
Entry(const std::string& content="", std::function<void(const std::string& content)> on_activate_=nullptr, unsigned width_chars=-1); Entry(const std::string &content = "", std::function<void(const std::string &content)> on_activate_ = nullptr, unsigned width_chars = -1);
std::function<void(const std::string& content)> on_activate; std::function<void(const std::string &content)> on_activate;
private: private:
size_t selected_history; size_t selected_history;
}; };
class Button : public Gtk::Button { class Button : public Gtk::Button {
public: public:
Button(const std::string& label, std::function<void()> on_activate_=nullptr); Button(const std::string &label, std::function<void()> on_activate_ = nullptr);
std::function<void()> on_activate; std::function<void()> on_activate;
}; };
class ToggleButton : public Gtk::ToggleButton { class ToggleButton : public Gtk::ToggleButton {
public: public:
ToggleButton(const std::string& label, std::function<void()> on_activate_=nullptr); ToggleButton(const std::string &label, std::function<void()> on_activate_ = nullptr);
std::function<void()> on_activate; std::function<void()> on_activate;
}; };
class Label : public Gtk::Label { class Label : public Gtk::Label {
public: public:
Label(std::function<void(int state, const std::string& message)> update_=nullptr); Label(std::function<void(int state, const std::string &message)> update_ = nullptr);
std::function<void(int state, const std::string& message)> update; std::function<void(int state, const std::string &message)> update;
}; };
private: private:
EntryBox(); EntryBox();
public: public:
static EntryBox &get() { static EntryBox &get() {
static EntryBox singleton; static EntryBox singleton;
return singleton; return singleton;
} }
Gtk::Box upper_box; Gtk::Box upper_box;
Gtk::Box lower_box; Gtk::Box lower_box;
void clear(); void clear();
void hide() {clear();} void hide() { clear(); }
void show(); void show();
std::list<Entry> entries; std::list<Entry> entries;
std::list<Button> buttons; std::list<Button> buttons;
std::list<ToggleButton> toggle_buttons; std::list<ToggleButton> toggle_buttons;
std::list<Label> labels; std::list<Label> labels;
private: private:
static std::unordered_map<std::string, std::vector<std::string> > entry_histories; static std::unordered_map<std::string, std::vector<std::string>> entry_histories;
}; };

28
src/files.h

@ -5,7 +5,7 @@
/// version number (JUCI_VERSION) in ../CMakeLists.txt to automatically apply /// version number (JUCI_VERSION) in ../CMakeLists.txt to automatically apply
/// the changes to user's ~/.juci/config/config.json files /// the changes to user's ~/.juci/config/config.json files
const std::string default_config_file = R"RAW({ const std::string default_config_file = R"RAW({
"version": ")RAW"+std::string(JUCI_VERSION)+R"RAW(", "version": ")RAW" + std::string(JUCI_VERSION) + R"RAW(",
"gtk_theme": { "gtk_theme": {
"name_comment": "Use \"\" for default theme, At least these two exist on all systems: Adwaita, Raleigh", "name_comment": "Use \"\" for default theme, At least these two exist on all systems: Adwaita, Raleigh",
"name": "", "name": "",
@ -17,18 +17,18 @@ const std::string default_config_file = R"RAW({
"style": "juci-light", "style": "juci-light",
"font_comment": "Use \"\" for default font, and for instance \"Monospace 12\" to also set size",)RAW" "font_comment": "Use \"\" for default font, and for instance \"Monospace 12\" to also set size",)RAW"
#ifdef __APPLE__ #ifdef __APPLE__
R"RAW( R"RAW(
"font": "Menlo",)RAW" "font": "Menlo",)RAW"
#else #else
#ifdef _WIN32 #ifdef _WIN32
R"RAW( R"RAW(
"font": "Consolas",)RAW" "font": "Consolas",)RAW"
#else #else
R"RAW( R"RAW(
"font": "Monospace",)RAW" "font": "Monospace",)RAW"
#endif #endif
#endif #endif
R"RAW( R"RAW(
"cleanup_whitespace_characters_comment": "Remove trailing whitespace characters on save, and add trailing newline if missing", "cleanup_whitespace_characters_comment": "Remove trailing whitespace characters on save, and add trailing newline if missing",
"cleanup_whitespace_characters": false, "cleanup_whitespace_characters": false,
"show_whitespace_characters_comment": "Determines what kind of whitespaces should be drawn. Use comma-separated list of: space, tab, newline, nbsp, leading, text, trailing or all", "show_whitespace_characters_comment": "Determines what kind of whitespaces should be drawn. Use comma-separated list of: space, tab, newline, nbsp, leading, text, trailing or all",
@ -133,25 +133,25 @@ R"RAW(
"debug_toggle_breakpoint": "<primary>b", "debug_toggle_breakpoint": "<primary>b",
"debug_goto_stop": "<primary><shift>l",)RAW" "debug_goto_stop": "<primary><shift>l",)RAW"
#ifdef __linux #ifdef __linux
R"RAW( R"RAW(
"window_next_tab": "<primary>Tab", "window_next_tab": "<primary>Tab",
"window_previous_tab": "<primary><shift>Tab",)RAW" "window_previous_tab": "<primary><shift>Tab",)RAW"
#else #else
R"RAW( R"RAW(
"window_next_tab": "<primary><alt>Right", "window_next_tab": "<primary><alt>Right",
"window_previous_tab": "<primary><alt>Left",)RAW" "window_previous_tab": "<primary><alt>Left",)RAW"
#endif #endif
R"RAW( R"RAW(
"window_close_tab": "<primary>w", "window_close_tab": "<primary>w",
"window_toggle_split": "",)RAW" "window_toggle_split": "",)RAW"
#ifdef __APPLE__ #ifdef __APPLE__
R"RAW( R"RAW(
"window_toggle_full_screen": "<primary><control>f",)RAW" "window_toggle_full_screen": "<primary><control>f",)RAW"
#else #else
R"RAW( R"RAW(
"window_toggle_full_screen": "F11",)RAW" "window_toggle_full_screen": "F11",)RAW"
#endif #endif
R"RAW( R"RAW(
"window_toggle_tabs": "", "window_toggle_tabs": "",
"window_clear_terminal": "" "window_clear_terminal": ""
}, },
@ -162,13 +162,13 @@ R"RAW(
"debug_build_path": "<default_build_path>/debug", "debug_build_path": "<default_build_path>/debug",
"cmake": {)RAW" "cmake": {)RAW"
#ifdef _WIN32 #ifdef _WIN32
R"RAW( R"RAW(
"command": "cmake -G\"MSYS Makefiles\"",)RAW" "command": "cmake -G\"MSYS Makefiles\"",)RAW"
#else #else
R"RAW( R"RAW(
"command": "cmake",)RAW" "command": "cmake",)RAW"
#endif #endif
R"RAW( R"RAW(
"compile_command": "cmake --build ." "compile_command": "cmake --build ."
}, },
"meson": { "meson": {

151
src/filesystem.cc

@ -1,7 +1,7 @@
#include <algorithm>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <algorithm>
#include "filesystem.h" #include "filesystem.h"
@ -20,8 +20,10 @@ std::string filesystem::read(const std::string &path) {
std::vector<std::string> filesystem::read_lines(const std::string &path) { std::vector<std::string> filesystem::read_lines(const std::string &path) {
std::vector<std::string> res; std::vector<std::string> res;
std::ifstream input(path, std::ofstream::binary); std::ifstream input(path, std::ofstream::binary);
if (input) { if(input) {
do { res.emplace_back(); } while(getline(input, res.back())); do {
res.emplace_back();
} while(getline(input, res.back()));
} }
input.close(); input.close();
return res; return res;
@ -39,9 +41,9 @@ bool filesystem::write(const std::string &path, const std::string &new_content)
} }
std::string filesystem::escape_argument(const std::string &argument) { std::string filesystem::escape_argument(const std::string &argument) {
auto escaped=argument; auto escaped = argument;
for(size_t pos=0;pos<escaped.size();++pos) { for(size_t pos = 0; pos < escaped.size(); ++pos) {
if(escaped[pos]==' ' || escaped[pos]=='(' || escaped[pos]==')' || escaped[pos]=='\'' || escaped[pos]=='"') { if(escaped[pos] == ' ' || escaped[pos] == '(' || escaped[pos] == ')' || escaped[pos] == '\'' || escaped[pos] == '"') {
escaped.insert(pos, "\\"); escaped.insert(pos, "\\");
++pos; ++pos;
} }
@ -50,40 +52,40 @@ std::string filesystem::escape_argument(const std::string &argument) {
} }
std::string filesystem::unescape_argument(const std::string &argument) { std::string filesystem::unescape_argument(const std::string &argument) {
auto unescaped=argument; auto unescaped = argument;
if(unescaped.size()>=2) { if(unescaped.size() >= 2) {
if((unescaped[0]=='\'' && unescaped[unescaped.size()-1]=='\'') || if((unescaped[0] == '\'' && unescaped[unescaped.size() - 1] == '\'') ||
(unescaped[0]=='"' && unescaped[unescaped.size()-1]=='"')) { (unescaped[0] == '"' && unescaped[unescaped.size() - 1] == '"')) {
char quotation_mark=unescaped[0]; char quotation_mark = unescaped[0];
unescaped=unescaped.substr(1, unescaped.size()-2); unescaped = unescaped.substr(1, unescaped.size() - 2);
size_t backslash_count=0; size_t backslash_count = 0;
for(size_t pos=0;pos<unescaped.size();++pos) { for(size_t pos = 0; pos < unescaped.size(); ++pos) {
if(backslash_count%2==1 && (unescaped[pos]=='\\' || unescaped[pos]==quotation_mark)) { if(backslash_count % 2 == 1 && (unescaped[pos] == '\\' || unescaped[pos] == quotation_mark)) {
unescaped.erase(pos-1, 1); unescaped.erase(pos - 1, 1);
--pos; --pos;
backslash_count=0; backslash_count = 0;
} }
else if(unescaped[pos]=='\\') else if(unescaped[pos] == '\\')
++backslash_count; ++backslash_count;
else else
backslash_count=0; backslash_count = 0;
} }
return unescaped; return unescaped;
} }
} }
size_t backslash_count=0; size_t backslash_count = 0;
for(size_t pos=0;pos<unescaped.size();++pos) { for(size_t pos = 0; pos < unescaped.size(); ++pos) {
if(backslash_count%2==1 && (unescaped[pos]=='\\' || unescaped[pos]==' ' || unescaped[pos]=='(' || unescaped[pos]==')' || unescaped[pos]=='\'' || unescaped[pos]=='"')) { if(backslash_count % 2 == 1 && (unescaped[pos] == '\\' || unescaped[pos] == ' ' || unescaped[pos] == '(' || unescaped[pos] == ')' || unescaped[pos] == '\'' || unescaped[pos] == '"')) {
unescaped.erase(pos-1, 1); unescaped.erase(pos - 1, 1);
--pos; --pos;
backslash_count=0; backslash_count = 0;
} }
else if(unescaped[pos]=='\\') else if(unescaped[pos] == '\\')
++backslash_count; ++backslash_count;
else else
backslash_count=0; backslash_count = 0;
} }
return unescaped; return unescaped;
} }
@ -91,10 +93,10 @@ std::string filesystem::unescape_argument(const std::string &argument) {
boost::filesystem::path filesystem::get_home_path() noexcept { boost::filesystem::path filesystem::get_home_path() noexcept {
std::vector<std::string> environment_variables = {"HOME", "AppData"}; std::vector<std::string> environment_variables = {"HOME", "AppData"};
char *ptr = nullptr; char *ptr = nullptr;
for (auto &variable : environment_variables) { for(auto &variable : environment_variables) {
ptr=std::getenv(variable.c_str()); ptr = std::getenv(variable.c_str());
boost::system::error_code ec; boost::system::error_code ec;
if (ptr!=nullptr && boost::filesystem::exists(ptr, ec)) if(ptr != nullptr && boost::filesystem::exists(ptr, ec))
return ptr; return ptr;
} }
return boost::filesystem::path(); return boost::filesystem::path();
@ -104,72 +106,72 @@ boost::filesystem::path filesystem::get_short_path(const boost::filesystem::path
#ifdef _WIN32 #ifdef _WIN32
return path; return path;
#else #else
static auto home_path=get_home_path(); static auto home_path = get_home_path();
if(!home_path.empty()) { if(!home_path.empty()) {
auto relative_path=filesystem::get_relative_path(path, home_path); auto relative_path = filesystem::get_relative_path(path, home_path);
if(!relative_path.empty()) if(!relative_path.empty())
return "~"/relative_path; return "~" / relative_path;
} }
return path; return path;
#endif #endif
} }
bool filesystem::file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path) { bool filesystem::file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path) {
if(std::distance(file_path.begin(), file_path.end())<std::distance(path.begin(), path.end())) if(std::distance(file_path.begin(), file_path.end()) < std::distance(path.begin(), path.end()))
return false; return false;
return std::equal(path.begin(), path.end(), file_path.begin()); return std::equal(path.begin(), path.end(), file_path.begin());
} }
boost::filesystem::path filesystem::find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path) { boost::filesystem::path filesystem::find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path) {
auto current_path=path; auto current_path = path;
while(true) { while(true) {
auto test_path=current_path/file_name; auto test_path = current_path / file_name;
if(boost::filesystem::exists(test_path)) if(boost::filesystem::exists(test_path))
return test_path; return test_path;
if(current_path==current_path.root_directory()) if(current_path == current_path.root_directory())
return boost::filesystem::path(); return boost::filesystem::path();
current_path=current_path.parent_path(); current_path = current_path.parent_path();
} }
} }
boost::filesystem::path filesystem::get_normal_path(const boost::filesystem::path &path) noexcept { boost::filesystem::path filesystem::get_normal_path(const boost::filesystem::path &path) noexcept {
boost::filesystem::path normal_path; boost::filesystem::path normal_path;
for(auto &e: path) { for(auto &e : path) {
if(e==".") if(e == ".")
continue; continue;
else if(e=="..") { else if(e == "..") {
auto parent_path=normal_path.parent_path(); auto parent_path = normal_path.parent_path();
if(!parent_path.empty()) if(!parent_path.empty())
normal_path=parent_path; normal_path = parent_path;
else else
normal_path/=e; normal_path /= e;
} }
else if(e.empty()) else if(e.empty())
continue; continue;
else else
normal_path/=e; normal_path /= e;
} }
return normal_path; return normal_path;
} }
boost::filesystem::path filesystem::get_relative_path(const boost::filesystem::path &path, const boost::filesystem::path &base) noexcept { boost::filesystem::path filesystem::get_relative_path(const boost::filesystem::path &path, const boost::filesystem::path &base) noexcept {
boost::filesystem::path relative_path; boost::filesystem::path relative_path;
if(std::distance(path.begin(), path.end())<std::distance(base.begin(), base.end())) if(std::distance(path.begin(), path.end()) < std::distance(base.begin(), base.end()))
return boost::filesystem::path(); return boost::filesystem::path();
auto base_it=base.begin(); auto base_it = base.begin();
auto path_it=path.begin(); auto path_it = path.begin();
while(path_it!=path.end() && base_it!=base.end()) { while(path_it != path.end() && base_it != base.end()) {
if(*path_it!=*base_it) if(*path_it != *base_it)
return boost::filesystem::path(); return boost::filesystem::path();
++path_it; ++path_it;
++base_it; ++base_it;
} }
for(;path_it!=path.end();++path_it) for(; path_it != path.end(); ++path_it)
relative_path/=*path_it; relative_path /= *path_it;
return relative_path; return relative_path;
} }
@ -179,39 +181,40 @@ boost::filesystem::path filesystem::get_executable(const boost::filesystem::path
return executable_name; return executable_name;
#endif #endif
static std::vector<boost::filesystem::path> bin_paths={"/usr/bin", "/usr/local/bin"}; static std::vector<boost::filesystem::path> bin_paths = {"/usr/bin", "/usr/local/bin"};
try { try {
for(auto &path: bin_paths) { for(auto &path : bin_paths) {
if(boost::filesystem::exists(path/executable_name)) if(boost::filesystem::exists(path / executable_name))
return executable_name; return executable_name;
} }
auto &executable_name_str = executable_name.string(); auto &executable_name_str = executable_name.string();
for(auto &path: bin_paths) { for(auto &path : bin_paths) {
boost::filesystem::path executable; boost::filesystem::path executable;
for(boost::filesystem::directory_iterator it(path), end; it != end; ++it) { for(boost::filesystem::directory_iterator it(path), end; it != end; ++it) {
auto it_path = it->path(); auto it_path = it->path();
auto it_path_filename_str = it_path.filename().string(); auto it_path_filename_str = it_path.filename().string();
if(!it_path_filename_str.empty() && it_path_filename_str.compare(0, executable_name_str.size(), executable_name_str)==0) { if(!it_path_filename_str.empty() && it_path_filename_str.compare(0, executable_name_str.size(), executable_name_str) == 0) {
if(it_path > executable && if(it_path > executable &&
((it_path_filename_str.size() > executable_name_str.size() && ((it_path_filename_str.size() > executable_name_str.size() &&
it_path_filename_str[executable_name_str.size()]>='0' && it_path_filename_str[executable_name_str.size()] >= '0' &&
it_path_filename_str[executable_name_str.size()]<='9') || it_path_filename_str[executable_name_str.size()] <= '9') ||
(it_path_filename_str.size() > executable_name_str.size()+1 && (it_path_filename_str.size() > executable_name_str.size() + 1 &&
it_path_filename_str[executable_name_str.size()]=='-' && it_path_filename_str[executable_name_str.size()] == '-' &&
it_path_filename_str[executable_name_str.size()+1]>='0' && it_path_filename_str[executable_name_str.size() + 1] >= '0' &&
it_path_filename_str[executable_name_str.size()+1]<='9')) && it_path_filename_str[executable_name_str.size() + 1] <= '9')) &&
!boost::filesystem::is_directory(it_path)) !boost::filesystem::is_directory(it_path))
executable=it_path; executable = it_path;
} }
} }
if(!executable.empty()) if(!executable.empty())
return executable; return executable;
} }
} }
catch(...) {} catch(...) {
}
return executable_name; return executable_name;
} }
@ -236,9 +239,9 @@ const std::vector<boost::filesystem::path> &filesystem::get_executable_search_pa
} }
boost::filesystem::path filesystem::find_executable(const std::string &executable_name) { boost::filesystem::path filesystem::find_executable(const std::string &executable_name) {
for(auto &path: get_executable_search_paths()) { for(auto &path : get_executable_search_paths()) {
boost::system::error_code ec; boost::system::error_code ec;
auto executable_path=path/executable_name; auto executable_path = path / executable_name;
if(boost::filesystem::exists(executable_path, ec)) if(boost::filesystem::exists(executable_path, ec))
return executable_path; return executable_path;
} }

16
src/filesystem.h

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <vector>
#include <string>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <string>
#include <vector>
class filesystem { class filesystem {
public: public:
@ -18,23 +18,23 @@ public:
static std::string escape_argument(const std::string &argument); static std::string escape_argument(const std::string &argument);
static std::string unescape_argument(const std::string &argument); static std::string unescape_argument(const std::string &argument);
static boost::filesystem::path get_home_path() noexcept; static boost::filesystem::path get_home_path() noexcept;
static boost::filesystem::path get_short_path(const boost::filesystem::path &path) noexcept; static boost::filesystem::path get_short_path(const boost::filesystem::path &path) noexcept;
static bool file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path); static bool file_in_path(const boost::filesystem::path &file_path, const boost::filesystem::path &path);
static boost::filesystem::path find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path); static boost::filesystem::path find_file_in_path_parents(const std::string &file_name, const boost::filesystem::path &path);
/// Return path with dot, dot-dot and directory separator elements removed /// Return path with dot, dot-dot and directory separator elements removed
static boost::filesystem::path get_normal_path(const boost::filesystem::path &path) noexcept; static boost::filesystem::path get_normal_path(const boost::filesystem::path &path) noexcept;
/// Returns empty path on failure /// Returns empty path on failure
static boost::filesystem::path get_relative_path(const boost::filesystem::path &path, const boost::filesystem::path &base) noexcept; static boost::filesystem::path get_relative_path(const boost::filesystem::path &path, const boost::filesystem::path &base) noexcept;
/// Return executable with latest version in filename on systems that is lacking executable_name symbolic link /// Return executable with latest version in filename on systems that is lacking executable_name symbolic link
static boost::filesystem::path get_executable(const boost::filesystem::path &executable_name) noexcept; static boost::filesystem::path get_executable(const boost::filesystem::path &executable_name) noexcept;
static const std::vector<boost::filesystem::path> &get_executable_search_paths(); static const std::vector<boost::filesystem::path> &get_executable_search_paths();
static boost::filesystem::path find_executable(const std::string &executable_name); static boost::filesystem::path find_executable(const std::string &executable_name);
}; };

218
src/git.cc

@ -2,44 +2,45 @@
#include <cstring> #include <cstring>
#include <unordered_map> #include <unordered_map>
bool Git::initialized=false; bool Git::initialized = false;
std::mutex Git::mutex; std::mutex Git::mutex;
std::string Git::Error::message() noexcept { std::string Git::Error::message() noexcept {
const git_error *last_error = giterr_last(); const git_error *last_error = giterr_last();
if(last_error==nullptr) if(last_error == nullptr)
return std::string(); return std::string();
else else
return last_error->message; return last_error->message;
} }
Git::Repository::Diff::Diff(const boost::filesystem::path &path, git_repository *repository) : repository(repository) { Git::Repository::Diff::Diff(const boost::filesystem::path &path, git_repository *repository) : repository(repository) {
blob=std::shared_ptr<git_blob>(nullptr, [](git_blob *blob) { blob = std::shared_ptr<git_blob>(nullptr, [](git_blob *blob) {
if(blob) git_blob_free(blob); if(blob)
git_blob_free(blob);
}); });
Error error; Error error;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
auto spec="HEAD:"+path.generic_string(); auto spec = "HEAD:" + path.generic_string();
error.code = git_revparse_single(reinterpret_cast<git_object**>(&blob), repository, spec.c_str()); error.code = git_revparse_single(reinterpret_cast<git_object **>(&blob), repository, spec.c_str());
if(error) if(error)
throw std::runtime_error(error.message()); throw std::runtime_error(error.message());
git_diff_init_options(&options, GIT_DIFF_OPTIONS_VERSION); git_diff_init_options(&options, GIT_DIFF_OPTIONS_VERSION);
options.context_lines=0; options.context_lines = 0;
} }
//Based on https://github.com/atom/git-diff/blob/master/lib/git-diff-view.coffee //Based on https://github.com/atom/git-diff/blob/master/lib/git-diff-view.coffee
int Git::Repository::Diff::hunk_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) noexcept { int Git::Repository::Diff::hunk_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) noexcept {
auto lines=static_cast<Lines*>(payload); auto lines = static_cast<Lines *>(payload);
auto start=hunk->new_start-1; auto start = hunk->new_start - 1;
auto end=hunk->new_start+hunk->new_lines-1; auto end = hunk->new_start + hunk->new_lines - 1;
if(hunk->old_lines==0 && hunk->new_lines>0) if(hunk->old_lines == 0 && hunk->new_lines > 0)
lines->added.emplace_back(start, end); lines->added.emplace_back(start, end);
else if(hunk->new_lines==0 && hunk->old_lines>0) else if(hunk->new_lines == 0 && hunk->old_lines > 0)
lines->removed.emplace_back(start); lines->removed.emplace_back(start);
else else
lines->modified.emplace_back(start, end); lines->modified.emplace_back(start, end);
return 0; return 0;
} }
@ -47,7 +48,7 @@ Git::Repository::Diff::Lines Git::Repository::Diff::get_lines(const std::string
Lines lines; Lines lines;
Error error; Error error;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
error.code=git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, hunk_cb, nullptr, &lines); error.code = git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, hunk_cb, nullptr, &lines);
if(error) if(error)
throw std::runtime_error(error.message()); throw std::runtime_error(error.message());
return lines; return lines;
@ -58,10 +59,9 @@ std::vector<Git::Repository::Diff::Hunk> Git::Repository::Diff::get_hunks(const
Error error; Error error;
git_diff_options options; git_diff_options options;
git_diff_init_options(&options, GIT_DIFF_OPTIONS_VERSION); git_diff_init_options(&options, GIT_DIFF_OPTIONS_VERSION);
options.context_lines=0; options.context_lines = 0;
error.code=git_diff_buffers(old_buffer.c_str(), old_buffer.size(), nullptr, new_buffer.c_str(), new_buffer.size(), nullptr, &options, nullptr, nullptr, error.code = git_diff_buffers(old_buffer.c_str(), old_buffer.size(), nullptr, new_buffer.c_str(), new_buffer.size(), nullptr, &options, nullptr, nullptr, [](const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) {
[](const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) { auto hunks = static_cast<std::vector<Git::Repository::Diff::Hunk> *>(payload);
auto hunks=static_cast<std::vector<Git::Repository::Diff::Hunk>*>(payload);
hunks->emplace_back(hunk->old_start, hunk->old_lines, hunk->new_start, hunk->new_lines); hunks->emplace_back(hunk->old_start, hunk->old_lines, hunk->new_start, hunk->new_lines);
return 0; return 0;
}, nullptr, &hunks); }, nullptr, &hunks);
@ -71,24 +71,24 @@ std::vector<Git::Repository::Diff::Hunk> Git::Repository::Diff::get_hunks(const
} }
int Git::Repository::Diff::line_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) noexcept { int Git::Repository::Diff::line_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) noexcept {
auto details=static_cast<std::pair<std::string, int> *>(payload); auto details = static_cast<std::pair<std::string, int> *>(payload);
auto line_nr=details->second; auto line_nr = details->second;
auto start=hunk->new_start-1; auto start = hunk->new_start - 1;
auto end=hunk->new_start+hunk->new_lines-1; auto end = hunk->new_start + hunk->new_lines - 1;
if(line_nr==start || (line_nr>=start && line_nr<end)) { if(line_nr == start || (line_nr >= start && line_nr < end)) {
if(details->first.empty()) if(details->first.empty())
details->first+=std::string(hunk->header, hunk->header_len); details->first += std::string(hunk->header, hunk->header_len);
details->first+=line->origin+std::string(line->content, line->content_len); details->first += line->origin + std::string(line->content, line->content_len);
} }
return 0; return 0;
} }
std::string Git::Repository::Diff::get_details(const std::string &buffer, int line_nr) { std::string Git::Repository::Diff::get_details(const std::string &buffer, int line_nr) {
std::pair<std::string, int> details; std::pair<std::string, int> details;
details.second=line_nr; details.second = line_nr;
Error error; Error error;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
error.code=git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, nullptr, line_cb, &details); error.code = git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, nullptr, line_cb, &details);
if(error) if(error)
throw std::runtime_error(error.message()); throw std::runtime_error(error.message());
return details.first; return details.first;
@ -103,20 +103,20 @@ Git::Repository::Repository(const boost::filesystem::path &path) {
if(error) if(error)
throw std::runtime_error(error.message()); throw std::runtime_error(error.message());
} }
repository=std::unique_ptr<git_repository, std::function<void(git_repository *)> >(repository_ptr, [](git_repository *ptr) { repository = std::unique_ptr<git_repository, std::function<void(git_repository *)>>(repository_ptr, [](git_repository *ptr) {
git_repository_free(ptr); git_repository_free(ptr);
}); });
work_path=get_work_path(); work_path = get_work_path();
if(work_path.empty()) if(work_path.empty())
throw std::runtime_error("Could not find work path"); throw std::runtime_error("Could not find work path");
auto git_directory=Gio::File::create_for_path(get_path().string()); auto git_directory = Gio::File::create_for_path(get_path().string());
monitor=git_directory->monitor_directory(Gio::FileMonitorFlags::FILE_MONITOR_WATCH_MOVES); monitor = git_directory->monitor_directory(Gio::FileMonitorFlags::FILE_MONITOR_WATCH_MOVES);
monitor_changed_connection=monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file, monitor_changed_connection = monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File>&, const Glib::RefPtr<Gio::File> &,
Gio::FileMonitorEvent monitor_event) { Gio::FileMonitorEvent monitor_event) {
if(monitor_event!=Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { if(monitor_event != Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
this->clear_saved_status(); this->clear_saved_status();
} }
}, false); }, false);
@ -128,45 +128,55 @@ Git::Repository::~Repository() {
std::string Git::Repository::status_string(STATUS status) noexcept { std::string Git::Repository::status_string(STATUS status) noexcept {
switch(status) { switch(status) {
case STATUS::CURRENT: return "current"; case STATUS::CURRENT:
case STATUS::NEW: return "new"; return "current";
case STATUS::MODIFIED: return "modified"; case STATUS::NEW:
case STATUS::DELETED: return "deleted"; return "new";
case STATUS::RENAMED: return "renamed"; case STATUS::MODIFIED:
case STATUS::TYPECHANGE: return "typechange"; return "modified";
case STATUS::UNREADABLE: return "unreadable"; case STATUS::DELETED:
case STATUS::IGNORED: return "ignored"; return "deleted";
case STATUS::CONFLICTED: return "conflicted"; case STATUS::RENAMED:
default: return ""; return "renamed";
case STATUS::TYPECHANGE:
return "typechange";
case STATUS::UNREADABLE:
return "unreadable";
case STATUS::IGNORED:
return "ignored";
case STATUS::CONFLICTED:
return "conflicted";
default:
return "";
} }
} }
int Git::Repository::status_callback(const char *path, unsigned int status_flags, void *data) noexcept { int Git::Repository::status_callback(const char *path, unsigned int status_flags, void *data) noexcept {
auto callback=static_cast<std::function<void(const char *path, STATUS status)>*>(data); auto callback = static_cast<std::function<void(const char *path, STATUS status)> *>(data);
STATUS status; STATUS status;
if((status_flags&(GIT_STATUS_INDEX_NEW|GIT_STATUS_WT_NEW))>0) if((status_flags & (GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW)) > 0)
status=STATUS::NEW; status = STATUS::NEW;
else if((status_flags&(GIT_STATUS_INDEX_MODIFIED|GIT_STATUS_WT_MODIFIED))>0) else if((status_flags & (GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED)) > 0)
status=STATUS::MODIFIED; status = STATUS::MODIFIED;
else if((status_flags&(GIT_STATUS_INDEX_DELETED|GIT_STATUS_WT_DELETED))>0) else if((status_flags & (GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED)) > 0)
status=STATUS::DELETED; status = STATUS::DELETED;
else if((status_flags&(GIT_STATUS_INDEX_RENAMED|GIT_STATUS_WT_RENAMED))>0) else if((status_flags & (GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED)) > 0)
status=STATUS::RENAMED; status = STATUS::RENAMED;
else if((status_flags&(GIT_STATUS_INDEX_TYPECHANGE|GIT_STATUS_WT_TYPECHANGE))>0) else if((status_flags & (GIT_STATUS_INDEX_TYPECHANGE | GIT_STATUS_WT_TYPECHANGE)) > 0)
status=STATUS::TYPECHANGE; status = STATUS::TYPECHANGE;
else if((status_flags&(GIT_STATUS_WT_UNREADABLE))>0) else if((status_flags & (GIT_STATUS_WT_UNREADABLE)) > 0)
status=STATUS::UNREADABLE; status = STATUS::UNREADABLE;
else if((status_flags&(GIT_STATUS_IGNORED))>0) else if((status_flags & (GIT_STATUS_IGNORED)) > 0)
status=STATUS::IGNORED; status = STATUS::IGNORED;
else if((status_flags&(GIT_STATUS_CONFLICTED))>0) else if((status_flags & (GIT_STATUS_CONFLICTED)) > 0)
status=STATUS::CONFLICTED; status = STATUS::CONFLICTED;
else else
status=STATUS::CURRENT; status = STATUS::CURRENT;
if(*callback) if(*callback)
(*callback)(path, status); (*callback)(path, status);
return 0; return 0;
} }
@ -176,22 +186,22 @@ Git::Repository::Status Git::Repository::get_status() {
if(has_saved_status) if(has_saved_status)
return saved_status; return saved_status;
} }
Status status; Status status;
bool first=true; bool first = true;
std::unique_lock<std::mutex> status_saved_lock(saved_status_mutex, std::defer_lock); std::unique_lock<std::mutex> status_saved_lock(saved_status_mutex, std::defer_lock);
std::function<void(const char *path, STATUS status)> callback=[this, &status, &first, &status_saved_lock](const char *path_cstr, STATUS status_enum) { std::function<void(const char *path, STATUS status)> callback = [this, &status, &first, &status_saved_lock](const char *path_cstr, STATUS status_enum) {
if(first) { if(first) {
status_saved_lock.lock(); status_saved_lock.lock();
first=false; first = false;
} }
boost::filesystem::path rel_path(path_cstr); boost::filesystem::path rel_path(path_cstr);
do { do {
if(status_enum==STATUS::MODIFIED) if(status_enum == STATUS::MODIFIED)
status.modified.emplace((work_path/rel_path).generic_string()); status.modified.emplace((work_path / rel_path).generic_string());
if(status_enum==STATUS::NEW) if(status_enum == STATUS::NEW)
status.added.emplace((work_path/rel_path).generic_string()); status.added.emplace((work_path / rel_path).generic_string());
rel_path=rel_path.parent_path(); rel_path = rel_path.parent_path();
} while(!rel_path.empty()); } while(!rel_path.empty());
}; };
Error error; Error error;
@ -199,8 +209,8 @@ Git::Repository::Status Git::Repository::get_status() {
error.code = git_status_foreach(repository.get(), status_callback, &callback); error.code = git_status_foreach(repository.get(), status_callback, &callback);
if(error) if(error)
throw std::runtime_error(error.message()); throw std::runtime_error(error.message());
saved_status=status; saved_status = status;
has_saved_status=true; has_saved_status = true;
if(status_saved_lock) if(status_saved_lock)
status_saved_lock.unlock(); status_saved_lock.unlock();
return status; return status;
@ -210,7 +220,7 @@ void Git::Repository::clear_saved_status() {
std::unique_lock<std::mutex> lock(saved_status_mutex); std::unique_lock<std::mutex> lock(saved_status_mutex);
saved_status.added.clear(); saved_status.added.clear();
saved_status.modified.clear(); saved_status.modified.clear();
has_saved_status=false; has_saved_status = false;
} }
boost::filesystem::path Git::Repository::get_work_path() noexcept { boost::filesystem::path Git::Repository::get_work_path() noexcept {
@ -233,7 +243,7 @@ boost::filesystem::path Git::Repository::get_root_path(const boost::filesystem::
if(error) if(error)
throw std::runtime_error(error.message()); throw std::runtime_error(error.message());
} }
auto root_path=Git::path(root.ptr, root.size); auto root_path = Git::path(root.ptr, root.size);
git_buf_free(&root); git_buf_free(&root);
return root_path; return root_path;
} }
@ -245,17 +255,17 @@ Git::Repository::Diff Git::Repository::get_diff(const boost::filesystem::path &p
std::string Git::Repository::get_branch() noexcept { std::string Git::Repository::get_branch() noexcept {
std::string branch; std::string branch;
git_reference *reference; git_reference *reference;
if(git_repository_head(&reference, repository.get())==0) { if(git_repository_head(&reference, repository.get()) == 0) {
if(auto reference_name_cstr=git_reference_name(reference)) { if(auto reference_name_cstr = git_reference_name(reference)) {
std::string reference_name(reference_name_cstr); std::string reference_name(reference_name_cstr);
size_t pos; size_t pos;
if((pos=reference_name.rfind('/'))!=std::string::npos) { if((pos = reference_name.rfind('/')) != std::string::npos) {
if(pos+1<reference_name.size()) if(pos + 1 < reference_name.size())
branch=reference_name.substr(pos+1); branch = reference_name.substr(pos + 1);
} }
else if((pos=reference_name.rfind('\\'))!=std::string::npos) { else if((pos = reference_name.rfind('\\')) != std::string::npos) {
if(pos+1<reference_name.size()) if(pos + 1 < reference_name.size())
branch=reference_name.substr(pos+1); branch = reference_name.substr(pos + 1);
} }
} }
git_reference_free(reference); git_reference_free(reference);
@ -267,33 +277,33 @@ void Git::initialize() noexcept {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
if(!initialized) { if(!initialized) {
git_libgit2_init(); git_libgit2_init();
initialized=true; initialized = true;
} }
} }
std::shared_ptr<Git::Repository> Git::get_repository(const boost::filesystem::path &path) { std::shared_ptr<Git::Repository> Git::get_repository(const boost::filesystem::path &path) {
initialize(); initialize();
static std::unordered_map<std::string, std::weak_ptr<Git::Repository> > cache; static std::unordered_map<std::string, std::weak_ptr<Git::Repository>> cache;
static std::mutex mutex; static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
auto root_path=Repository::get_root_path(path).generic_string(); auto root_path = Repository::get_root_path(path).generic_string();
auto it=cache.find(root_path); auto it = cache.find(root_path);
if(it==cache.end()) if(it == cache.end())
it=cache.emplace(root_path, std::weak_ptr<Git::Repository>()).first; it = cache.emplace(root_path, std::weak_ptr<Git::Repository>()).first;
auto instance=it->second.lock(); auto instance = it->second.lock();
if(!instance) if(!instance)
it->second=instance=std::shared_ptr<Repository>(new Repository(root_path)); it->second = instance = std::shared_ptr<Repository>(new Repository(root_path));
return instance; return instance;
} }
boost::filesystem::path Git::path(const char *cpath, size_t cpath_length) noexcept { boost::filesystem::path Git::path(const char *cpath, size_t cpath_length) noexcept {
if(cpath==nullptr) if(cpath == nullptr)
return boost::filesystem::path(); return boost::filesystem::path();
if(cpath_length==static_cast<size_t>(-1)) if(cpath_length == static_cast<size_t>(-1))
cpath_length=strlen(cpath); cpath_length = strlen(cpath);
if(cpath_length>0 && (cpath[cpath_length-1]=='/' || cpath[cpath_length-1]=='\\')) if(cpath_length > 0 && (cpath[cpath_length - 1] == '/' || cpath[cpath_length - 1] == '\\'))
return std::string(cpath, cpath_length-1); return std::string(cpath, cpath_length - 1);
else else
return std::string(cpath, cpath_length); return std::string(cpath, cpath_length);
} }

67
src/git.h

@ -1,104 +1,111 @@
#pragma once #pragma once
#include <boost/filesystem.hpp>
#include <giomm.h>
#include <git2.h> #include <git2.h>
#include <mutex>
#include <memory>
#include <iostream> #include <iostream>
#include <memory>
#include <mutex>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include <giomm.h>
#include <boost/filesystem.hpp>
class Git { class Git {
friend class Repository; friend class Repository;
public: public:
class Error { class Error {
friend class Git; friend class Git;
std::string message() noexcept; std::string message() noexcept;
public: public:
int code=0; int code = 0;
operator bool() noexcept {return code<0;} operator bool() noexcept { return code < 0; }
}; };
class Repository { class Repository {
public: public:
class Diff { class Diff {
public: public:
class Lines { class Lines {
public: public:
std::vector<std::pair<int, int> > added; std::vector<std::pair<int, int>> added;
std::vector<std::pair<int, int> > modified; std::vector<std::pair<int, int>> modified;
std::vector<int> removed; std::vector<int> removed;
}; };
class Hunk { class Hunk {
public: public:
Hunk(int old_start, int old_size, int new_start, int new_size): old_lines(old_start, old_size), new_lines(new_start, new_size) {} Hunk(int old_start, int old_size, int new_start, int new_size) : old_lines(old_start, old_size), new_lines(new_start, new_size) {}
/// Start and size /// Start and size
std::pair<int, int> old_lines; std::pair<int, int> old_lines;
/// Start and size /// Start and size
std::pair<int, int> new_lines; std::pair<int, int> new_lines;
}; };
private: private:
friend class Repository; friend class Repository;
Diff(const boost::filesystem::path &path, git_repository *repository); Diff(const boost::filesystem::path &path, git_repository *repository);
git_repository *repository=nullptr; git_repository *repository = nullptr;
std::shared_ptr<git_blob> blob=nullptr; std::shared_ptr<git_blob> blob = nullptr;
git_diff_options options; git_diff_options options;
static int hunk_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) noexcept; static int hunk_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) noexcept;
static int line_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) noexcept; static int line_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) noexcept;
public: public:
Lines get_lines(const std::string &buffer); Lines get_lines(const std::string &buffer);
static std::vector<Hunk> get_hunks(const std::string &old_buffer, const std::string &new_buffer); static std::vector<Hunk> get_hunks(const std::string &old_buffer, const std::string &new_buffer);
std::string get_details(const std::string &buffer, int line_nr); std::string get_details(const std::string &buffer, int line_nr);
}; };
enum class STATUS {CURRENT, NEW, MODIFIED, DELETED, RENAMED, TYPECHANGE, UNREADABLE, IGNORED, CONFLICTED}; enum class STATUS { CURRENT, NEW, MODIFIED, DELETED, RENAMED, TYPECHANGE, UNREADABLE, IGNORED, CONFLICTED };
class Status { class Status {
public: public:
std::unordered_set<std::string> added; std::unordered_set<std::string> added;
std::unordered_set<std::string> modified; std::unordered_set<std::string> modified;
}; };
private: private:
friend class Git; friend class Git;
Repository(const boost::filesystem::path &path); Repository(const boost::filesystem::path &path);
static int status_callback(const char *path, unsigned int status_flags, void *data) noexcept; static int status_callback(const char *path, unsigned int status_flags, void *data) noexcept;
std::unique_ptr<git_repository, std::function<void(git_repository *)> > repository; std::unique_ptr<git_repository, std::function<void(git_repository *)>> repository;
boost::filesystem::path work_path; boost::filesystem::path work_path;
sigc::connection monitor_changed_connection; sigc::connection monitor_changed_connection;
Status saved_status; Status saved_status;
bool has_saved_status=false; bool has_saved_status = false;
std::mutex saved_status_mutex; std::mutex saved_status_mutex;
public: public:
~Repository(); ~Repository();
static std::string status_string(STATUS status) noexcept; static std::string status_string(STATUS status) noexcept;
Status get_status(); Status get_status();
void clear_saved_status(); void clear_saved_status();
boost::filesystem::path get_work_path() noexcept; boost::filesystem::path get_work_path() noexcept;
boost::filesystem::path get_path() noexcept; boost::filesystem::path get_path() noexcept;
static boost::filesystem::path get_root_path(const boost::filesystem::path &path); static boost::filesystem::path get_root_path(const boost::filesystem::path &path);
Diff get_diff(const boost::filesystem::path &path); Diff get_diff(const boost::filesystem::path &path);
std::string get_branch() noexcept; std::string get_branch() noexcept;
Glib::RefPtr<Gio::FileMonitor> monitor; Glib::RefPtr<Gio::FileMonitor> monitor;
}; };
private: private:
static bool initialized; static bool initialized;
///Mutex for thread safe operations ///Mutex for thread safe operations
static std::mutex mutex; static std::mutex mutex;
///Call initialize in public static methods ///Call initialize in public static methods
static void initialize() noexcept; static void initialize() noexcept;
static boost::filesystem::path path(const char *cpath, size_t cpath_length=static_cast<size_t>(-1)) noexcept; static boost::filesystem::path path(const char *cpath, size_t cpath_length = static_cast<size_t>(-1)) noexcept;
public: public:
static std::shared_ptr<Repository> get_repository(const boost::filesystem::path &path); static std::shared_ptr<Repository> get_repository(const boost::filesystem::path &path);
}; };

22
src/info.cc

@ -3,21 +3,21 @@
Info::Info() { Info::Info() {
set_hexpand(false); set_hexpand(false);
set_halign(Gtk::Align::ALIGN_END); set_halign(Gtk::Align::ALIGN_END);
auto content_area=dynamic_cast<Gtk::Container*>(get_content_area()); auto content_area = dynamic_cast<Gtk::Container *>(get_content_area());
label.set_max_width_chars(40); label.set_max_width_chars(40);
label.set_line_wrap(true); label.set_line_wrap(true);
content_area->add(label); content_area->add(label);
get_style_context()->add_class("juci_info"); get_style_context()->add_class("juci_info");
//Workaround from https://bugzilla.gnome.org/show_bug.cgi?id=710888 //Workaround from https://bugzilla.gnome.org/show_bug.cgi?id=710888
//Issue described at the same issue report //Issue described at the same issue report
//TODO: remove later //TODO: remove later
auto revealer = gtk_widget_get_template_child (GTK_WIDGET (gobj()), GTK_TYPE_INFO_BAR, "revealer"); auto revealer = gtk_widget_get_template_child(GTK_WIDGET(gobj()), GTK_TYPE_INFO_BAR, "revealer");
if (revealer) { if(revealer) {
gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_NONE); gtk_revealer_set_transition_type(GTK_REVEALER(revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 0); gtk_revealer_set_transition_duration(GTK_REVEALER(revealer), 0);
} }
} }
@ -25,12 +25,12 @@ void Info::print(const std::string &text) {
timeout_connection.disconnect(); timeout_connection.disconnect();
//Timeout based on https://en.wikipedia.org/wiki/Words_per_minute //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 //(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); double timeout = 1000.0 * std::max(3.0, 1.0 + text.size() / 17.1);
timeout_connection=Glib::signal_timeout().connect([this]() { timeout_connection = Glib::signal_timeout().connect([this]() {
hide(); hide();
return false; return false;
}, timeout); }, timeout);
label.set_text(text); label.set_text(text);
show(); show();
} }

5
src/info.h

@ -3,14 +3,15 @@
class Info : public Gtk::InfoBar { class Info : public Gtk::InfoBar {
Info(); Info();
public: public:
static Info &get() { static Info &get() {
static Info instance; static Info instance;
return instance; return instance;
} }
void print(const std::string &text); void print(const std::string &text);
private: private:
Gtk::Label label; Gtk::Label label;
sigc::connection timeout_connection; sigc::connection timeout_connection;

72
src/juci.cc

@ -1,10 +1,10 @@
#include "juci.h" #include "juci.h"
#include "window.h" #include "config.h"
#include "notebook.h"
#include "directories.h" #include "directories.h"
#include "menu.h" #include "menu.h"
#include "config.h" #include "notebook.h"
#include "terminal.h" #include "terminal.h"
#include "window.h"
#ifndef _WIN32 #ifndef _WIN32
#include <csignal> #include <csignal>
#endif #endif
@ -17,15 +17,15 @@ int Application::on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine>
int argc; int argc;
char **argv = cmd->get_arguments(argc); char **argv = cmd->get_arguments(argc);
ctx.parse(argc, argv); ctx.parse(argc, argv);
if(argc>=2) { if(argc >= 2) {
boost::system::error_code current_path_ec; boost::system::error_code current_path_ec;
auto current_path=boost::filesystem::current_path(current_path_ec); auto current_path = boost::filesystem::current_path(current_path_ec);
if(current_path_ec) if(current_path_ec)
errors.emplace_back("Error: could not find current path\n"); errors.emplace_back("Error: could not find current path\n");
for(int c=1;c<argc;c++) { for(int c = 1; c < argc; c++) {
boost::filesystem::path path(argv[c]); boost::filesystem::path path(argv[c]);
if(path.is_relative() && !current_path_ec) if(path.is_relative() && !current_path_ec)
path=current_path/path; path = current_path / path;
if(boost::filesystem::exists(path)) { if(boost::filesystem::exists(path)) {
if(boost::filesystem::is_regular_file(path)) if(boost::filesystem::is_regular_file(path))
files.emplace_back(path, 0); files.emplace_back(path, 0);
@ -37,7 +37,7 @@ int Application::on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine>
if(path.is_absolute() && boost::filesystem::is_directory(path.parent_path())) if(path.is_absolute() && boost::filesystem::is_directory(path.parent_path()))
files.emplace_back(path, 0); files.emplace_back(path, 0);
else else
errors.emplace_back("Error: could not create "+path.string()+".\n"); errors.emplace_back("Error: could not create " + path.string() + ".\n");
} }
} }
} }
@ -49,71 +49,71 @@ void Application::on_activate() {
std::vector<std::pair<int, int>> file_offsets; std::vector<std::pair<int, int>> file_offsets;
std::string current_file; std::string current_file;
Window::get().load_session(directories, files, file_offsets, current_file, directories.empty() && files.empty()); Window::get().load_session(directories, files, file_offsets, current_file, directories.empty() && files.empty());
Window::get().add_widgets(); Window::get().add_widgets();
add_window(Window::get()); add_window(Window::get());
Window::get().show(); Window::get().show();
bool first_directory=true; bool first_directory = true;
for(auto &directory: directories) { for(auto &directory : directories) {
if(first_directory) { if(first_directory) {
Directories::get().open(directory); Directories::get().open(directory);
first_directory=false; first_directory = false;
} }
else { else {
std::string files_in_directory; std::string files_in_directory;
for(auto it=files.begin();it!=files.end();) { for(auto it = files.begin(); it != files.end();) {
if(it->first.generic_string().compare(0, directory.generic_string().size()+1, directory.generic_string()+'/')==0) { if(it->first.generic_string().compare(0, directory.generic_string().size() + 1, directory.generic_string() + '/') == 0) {
files_in_directory+=" "+it->first.string(); files_in_directory += " " + it->first.string();
it=files.erase(it); it = files.erase(it);
} }
else else
it++; it++;
} }
std::thread another_juci_app([directory, files_in_directory](){ std::thread another_juci_app([directory, files_in_directory]() {
Terminal::get().async_print("Executing: juci "+directory.string()+files_in_directory+"\n"); Terminal::get().async_print("Executing: juci " + directory.string() + files_in_directory + "\n");
Terminal::get().process("juci "+directory.string()+files_in_directory, "", false); Terminal::get().process("juci " + directory.string() + files_in_directory, "", false);
}); });
another_juci_app.detach(); another_juci_app.detach();
} }
} }
for(size_t i=0;i<files.size();++i) { for(size_t i = 0; i < files.size(); ++i) {
Notebook::get().open(files[i].first, files[i].second); Notebook::get().open(files[i].first, files[i].second);
if(i<file_offsets.size()) { if(i < file_offsets.size()) {
if(auto view=Notebook::get().get_current_view()) { if(auto view = Notebook::get().get_current_view()) {
view->place_cursor_at_line_offset(file_offsets[i].first, file_offsets[i].second); view->place_cursor_at_line_offset(file_offsets[i].first, file_offsets[i].second);
view->hide_tooltips(); view->hide_tooltips();
} }
} }
} }
for(auto &error: errors) for(auto &error : errors)
Terminal::get().print(error, true); Terminal::get().print(error, true);
if(!current_file.empty()) { if(!current_file.empty()) {
Notebook::get().open(current_file); Notebook::get().open(current_file);
if(auto view=Notebook::get().get_current_view()) { if(auto view = Notebook::get().get_current_view()) {
auto iter=view->get_buffer()->get_insert()->get_iter(); auto iter = view->get_buffer()->get_insert()->get_iter();
// To update cursor history // To update cursor history
view->place_cursor_at_line_offset(iter.get_line(), iter.get_line_offset()); view->place_cursor_at_line_offset(iter.get_line(), iter.get_line_offset());
view->hide_tooltips(); view->hide_tooltips();
} }
} }
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(false); Gtk::Main::iteration(false);
for(auto view: Notebook::get().get_views()) for(auto view : Notebook::get().get_views())
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }
void Application::on_startup() { void Application::on_startup() {
Gtk::Application::on_startup(); Gtk::Application::on_startup();
Menu::get().build(); Menu::get().build();
if (!Menu::get().juci_menu || !Menu::get().window_menu) { if(!Menu::get().juci_menu || !Menu::get().window_menu) {
std::cerr << "Menu not found." << std::endl; std::cerr << "Menu not found." << std::endl;
} }
else { else {
@ -124,9 +124,9 @@ void Application::on_startup() {
Application::Application() : Gtk::Application("no.sout.juci", Gio::APPLICATION_NON_UNIQUE | Gio::APPLICATION_HANDLES_COMMAND_LINE) { Application::Application() : Gtk::Application("no.sout.juci", Gio::APPLICATION_NON_UNIQUE | Gio::APPLICATION_HANDLES_COMMAND_LINE) {
Glib::set_application_name("juCi++"); Glib::set_application_name("juCi++");
//Gtk::MessageDialog without buttons caused text to be selected, this prevents that //Gtk::MessageDialog without buttons caused text to be selected, this prevents that
Gtk::Settings::get_default()->property_gtk_label_select_on_focus()=false; Gtk::Settings::get_default()->property_gtk_label_select_on_focus() = false;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {

3
src/juci.h

@ -25,8 +25,8 @@
[juCi++] --> [tiny-process-library] : use [juCi++] --> [tiny-process-library] : use
\enduml \enduml
*/ */
#include <gtkmm.h>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <gtkmm.h>
class Application : public Gtk::Application { class Application : public Gtk::Application {
public: public:
@ -34,6 +34,7 @@ public:
int on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine> &cmd) override; int on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine> &cmd) override;
void on_activate() override; void on_activate() override;
void on_startup() override; void on_startup() override;
private: private:
std::vector<boost::filesystem::path> directories; std::vector<boost::filesystem::path> directories;
std::vector<std::pair<boost::filesystem::path, size_t>> files; std::vector<std::pair<boost::filesystem::path, size_t>> files;

26
src/menu.cc

@ -1,9 +1,9 @@
#include "menu.h" #include "menu.h"
#include "config.h" #include "config.h"
#include <string>
#include <iostream> #include <iostream>
#include <string>
const Glib::ustring menu_xml= R"RAW(<interface> const Glib::ustring menu_xml = R"RAW(<interface>
<menu id='right-click-line-menu'> <menu id='right-click-line-menu'>
<section> <section>
<item> <item>
@ -478,21 +478,21 @@ const Glib::ustring menu_xml= R"RAW(<interface>
)RAW"; )RAW";
void Menu::add_action(const std::string &name, const std::function<void()> &action) { void Menu::add_action(const std::string &name, const std::function<void()> &action) {
auto g_application=g_application_get_default(); auto g_application = g_application_get_default();
auto gio_application=Glib::wrap(g_application, true); auto gio_application = Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application); auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
actions[name]=application->add_action(name, action); actions[name] = application->add_action(name, action);
} }
void Menu::set_keys() { void Menu::set_keys() {
auto g_application=g_application_get_default(); auto g_application = g_application_get_default();
auto gio_application=Glib::wrap(g_application, true); auto gio_application = Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application); auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
for(auto &key: Config::get().menu.keys) { for(auto &key : Config::get().menu.keys) {
if(key.second.size()>0 && actions.find(key.first)!=actions.end()) if(key.second.size() > 0 && actions.find(key.first) != actions.end())
application->set_accel_for_action("app."+key.first, key.second); application->set_accel_for_action("app." + key.first, key.second);
} }
} }
@ -510,7 +510,7 @@ void Menu::build() {
ptr = Glib::RefPtr<Gio::Menu>::cast_dynamic(object); ptr = Glib::RefPtr<Gio::Menu>::cast_dynamic(object);
right_click_selected_menu = std::make_unique<Gtk::Menu>(ptr); right_click_selected_menu = std::make_unique<Gtk::Menu>(ptr);
} }
catch (const Glib::Error &ex) { catch(const Glib::Error &ex) {
std::cerr << "building menu failed: " << ex.what(); std::cerr << "building menu failed: " << ex.what();
} }
} }

16
src/menu.h

@ -1,28 +1,30 @@
#pragma once #pragma once
#include <string>
#include <unordered_map>
#include <functional> #include <functional>
#include <gtkmm.h> #include <gtkmm.h>
#include <string>
#include <unordered_map>
class Menu { class Menu {
Menu() = default; Menu() = default;
public: public:
static Menu &get() { static Menu &get() {
static Menu singleton; static Menu singleton;
return singleton; return singleton;
} }
void add_action(const std::string &name, const std::function<void()> &action); void add_action(const std::string &name, const std::function<void()> &action);
std::unordered_map<std::string, Glib::RefPtr<Gio::SimpleAction> > actions; std::unordered_map<std::string, Glib::RefPtr<Gio::SimpleAction>> actions;
void set_keys(); void set_keys();
void build(); void build();
Glib::RefPtr<Gio::Menu> juci_menu; Glib::RefPtr<Gio::Menu> juci_menu;
Glib::RefPtr<Gio::Menu> window_menu; Glib::RefPtr<Gio::Menu> window_menu;
std::unique_ptr<Gtk::Menu> right_click_line_menu; std::unique_ptr<Gtk::Menu> right_click_line_menu;
std::unique_ptr<Gtk::Menu> right_click_selected_menu; std::unique_ptr<Gtk::Menu> right_click_selected_menu;
std::function<void()> toggle_menu_items = []{}; std::function<void()> toggle_menu_items = [] {};
private: private:
Glib::RefPtr<Gtk::Builder> builder; Glib::RefPtr<Gtk::Builder> builder;
}; };

96
src/meson.cc

@ -1,14 +1,14 @@
#include "meson.h" #include "meson.h"
#include "filesystem.h"
#include "compile_commands.h" #include "compile_commands.h"
#include <regex>
#include "terminal.h"
#include "dialogs.h"
#include "config.h" #include "config.h"
#include "dialogs.h"
#include "filesystem.h"
#include "terminal.h"
#include <regex>
Meson::Meson(const boost::filesystem::path &path) { Meson::Meson(const boost::filesystem::path &path) {
const auto find_project=[](const boost::filesystem::path &file_path) { const auto find_project = [](const boost::filesystem::path &file_path) {
for(auto &line: filesystem::read_lines(file_path)) { for(auto &line : filesystem::read_lines(file_path)) {
const static std::regex project_regex(R"(^ *project *\(.*\r?$)", std::regex::icase); const static std::regex project_regex(R"(^ *project *\(.*\r?$)", std::regex::icase);
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, project_regex)) if(std::regex_match(line, sm, project_regex))
@ -16,102 +16,104 @@ Meson::Meson(const boost::filesystem::path &path) {
} }
return false; return false;
}; };
auto search_path=boost::filesystem::is_directory(path)?path:path.parent_path(); auto search_path = boost::filesystem::is_directory(path) ? path : path.parent_path();
while(true) { while(true) {
auto search_file=search_path/"meson.build"; auto search_file = search_path / "meson.build";
if(boost::filesystem::exists(search_file)) { if(boost::filesystem::exists(search_file)) {
if(find_project(search_file)) { if(find_project(search_file)) {
project_path=search_path; project_path = search_path;
break; break;
} }
} }
if(search_path==search_path.root_directory()) if(search_path == search_path.root_directory())
break; break;
search_path=search_path.parent_path(); search_path = search_path.parent_path();
} }
} }
bool Meson::update_default_build(const boost::filesystem::path &default_build_path, bool force) { bool Meson::update_default_build(const boost::filesystem::path &default_build_path, bool force) {
if(project_path.empty() || !boost::filesystem::exists(project_path/"meson.build") || default_build_path.empty()) if(project_path.empty() || !boost::filesystem::exists(project_path / "meson.build") || default_build_path.empty())
return false; return false;
if(!boost::filesystem::exists(default_build_path)) { if(!boost::filesystem::exists(default_build_path)) {
boost::system::error_code ec; boost::system::error_code ec;
boost::filesystem::create_directories(default_build_path, ec); boost::filesystem::create_directories(default_build_path, ec);
if(ec) { if(ec) {
Terminal::get().print("Error: could not create "+default_build_path.string()+": "+ec.message()+"\n", true); Terminal::get().print("Error: could not create " + default_build_path.string() + ": " + ec.message() + "\n", true);
return false; return false;
} }
} }
auto compile_commands_path=default_build_path/"compile_commands.json"; auto compile_commands_path = default_build_path / "compile_commands.json";
bool compile_commands_exists=boost::filesystem::exists(compile_commands_path); bool compile_commands_exists = boost::filesystem::exists(compile_commands_path);
if(!force && compile_commands_exists) if(!force && compile_commands_exists)
return true; return true;
Dialog::Message message("Creating/updating default build"); Dialog::Message message("Creating/updating default build");
auto exit_status=Terminal::get().process(Config::get().project.meson.command+' '+(compile_commands_exists?"--internal regenerate ":"")+ auto exit_status = Terminal::get().process(Config::get().project.meson.command + ' ' +
filesystem::escape_argument(project_path.string()), default_build_path); (compile_commands_exists ? "--internal regenerate " : "") +
filesystem::escape_argument(project_path.string()), default_build_path);
message.hide(); message.hide();
if(exit_status==EXIT_SUCCESS) if(exit_status == EXIT_SUCCESS)
return true; return true;
return false; return false;
} }
bool Meson::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) { bool Meson::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) {
if(project_path.empty() || !boost::filesystem::exists(project_path/"meson.build") || debug_build_path.empty()) if(project_path.empty() || !boost::filesystem::exists(project_path / "meson.build") || debug_build_path.empty())
return false; return false;
if(!boost::filesystem::exists(debug_build_path)) { if(!boost::filesystem::exists(debug_build_path)) {
boost::system::error_code ec; boost::system::error_code ec;
boost::filesystem::create_directories(debug_build_path, ec); boost::filesystem::create_directories(debug_build_path, ec);
if(ec) { if(ec) {
Terminal::get().print("Error: could not create "+debug_build_path.string()+": "+ec.message()+"\n", true); Terminal::get().print("Error: could not create " + debug_build_path.string() + ": " + ec.message() + "\n", true);
return false; return false;
} }
} }
bool compile_commands_exists=boost::filesystem::exists(debug_build_path/"compile_commands.json"); bool compile_commands_exists = boost::filesystem::exists(debug_build_path / "compile_commands.json");
if(!force && compile_commands_exists) if(!force && compile_commands_exists)
return true; return true;
Dialog::Message message("Creating/updating debug build"); Dialog::Message message("Creating/updating debug build");
auto exit_status=Terminal::get().process(Config::get().project.meson.command+' '+(compile_commands_exists?"--internal regenerate ":"")+ auto exit_status = Terminal::get().process(Config::get().project.meson.command + ' ' +
"--buildtype debug "+filesystem::escape_argument(project_path.string()), debug_build_path); (compile_commands_exists ? "--internal regenerate " : "") +
"--buildtype debug " + filesystem::escape_argument(project_path.string()), debug_build_path);
message.hide(); message.hide();
if(exit_status==EXIT_SUCCESS) if(exit_status == EXIT_SUCCESS)
return true; return true;
return false; return false;
} }
boost::filesystem::path Meson::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { boost::filesystem::path Meson::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {
CompileCommands compile_commands(build_path); CompileCommands compile_commands(build_path);
size_t best_match_size=-1; size_t best_match_size = -1;
boost::filesystem::path best_match_executable; boost::filesystem::path best_match_executable;
for(auto &command: compile_commands.commands) { for(auto &command : compile_commands.commands) {
auto command_file=filesystem::get_normal_path(command.file); auto command_file = filesystem::get_normal_path(command.file);
auto values=command.parameter_values("-o"); auto values = command.parameter_values("-o");
if(!values.empty()) { if(!values.empty()) {
size_t pos; size_t pos;
if((pos=values[0].find('@'))!=std::string::npos) { if((pos = values[0].find('@')) != std::string::npos) {
if(pos+1<values[0].size() && values[0].compare(pos+1, 3, "exe")==0) { if(pos + 1 < values[0].size() && values[0].compare(pos + 1, 3, "exe") == 0) {
auto executable=build_path/values[0].substr(0, pos); auto executable = build_path / values[0].substr(0, pos);
if(command_file==file_path) if(command_file == file_path)
return executable; return executable;
auto command_file_directory=command_file.parent_path(); auto command_file_directory = command_file.parent_path();
if(filesystem::file_in_path(file_path, command_file_directory)) { if(filesystem::file_in_path(file_path, command_file_directory)) {
auto size=static_cast<size_t>(std::distance(command_file_directory.begin(), command_file_directory.end())); auto size = static_cast<size_t>(std::distance(command_file_directory.begin(), command_file_directory.end()));
if(best_match_size==static_cast<size_t>(-1) || best_match_size<size) { if(best_match_size == static_cast<size_t>(-1) || best_match_size < size) {
best_match_size=size; best_match_size = size;
best_match_executable=executable; best_match_executable = executable;
} }
} }
} }
} }
} }
} }
return best_match_executable; return best_match_executable;
} }

10
src/meson.h

@ -5,11 +5,11 @@
class Meson { class Meson {
public: public:
Meson(const boost::filesystem::path &path); Meson(const boost::filesystem::path &path);
boost::filesystem::path project_path; boost::filesystem::path project_path;
bool update_default_build(const boost::filesystem::path &default_build_path, bool force=false); bool update_default_build(const boost::filesystem::path &default_build_path, bool force = false);
bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force=false); bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force = false);
boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
}; };

542
src/notebook.cc

@ -1,63 +1,63 @@
#include "notebook.h" #include "notebook.h"
#include "config.h" #include "config.h"
#include "directories.h" #include "directories.h"
#include <fstream>
#include <regex>
#include "project.h"
#include "filesystem.h" #include "filesystem.h"
#include "gtksourceview-3.0/gtksourceview/gtksourcemap.h"
#include "project.h"
#include "selection_dialog.h" #include "selection_dialog.h"
#include "source_clang.h" #include "source_clang.h"
#include "source_language_protocol.h" #include "source_language_protocol.h"
#include "gtksourceview-3.0/gtksourceview/gtksourcemap.h" #include <fstream>
#include <regex>
Notebook::TabLabel::TabLabel(const std::function<void()> &on_close) { Notebook::TabLabel::TabLabel(const std::function<void()> &on_close) {
set_can_focus(false); set_can_focus(false);
auto button=Gtk::manage(new Gtk::Button()); auto button = Gtk::manage(new Gtk::Button());
auto hbox=Gtk::manage(new Gtk::Box()); auto hbox = Gtk::manage(new Gtk::Box());
hbox->set_can_focus(false); hbox->set_can_focus(false);
label.set_can_focus(false); label.set_can_focus(false);
button->set_image_from_icon_name("window-close-symbolic", Gtk::ICON_SIZE_MENU); button->set_image_from_icon_name("window-close-symbolic", Gtk::ICON_SIZE_MENU);
button->set_can_focus(false); button->set_can_focus(false);
button->set_relief(Gtk::ReliefStyle::RELIEF_NONE); button->set_relief(Gtk::ReliefStyle::RELIEF_NONE);
hbox->pack_start(label, Gtk::PACK_SHRINK); hbox->pack_start(label, Gtk::PACK_SHRINK);
hbox->pack_end(*button, Gtk::PACK_SHRINK); hbox->pack_end(*button, Gtk::PACK_SHRINK);
add(*hbox); add(*hbox);
button->signal_clicked().connect(on_close); button->signal_clicked().connect(on_close);
signal_button_press_event().connect([on_close](GdkEventButton *event) { signal_button_press_event().connect([on_close](GdkEventButton *event) {
if(event->button==GDK_BUTTON_MIDDLE) { if(event->button == GDK_BUTTON_MIDDLE) {
on_close(); on_close();
return true; return true;
} }
return false; return false;
}); });
show_all(); show_all();
} }
Notebook::Notebook() : Gtk::Paned(), notebooks(2) { Notebook::Notebook() : Gtk::Paned(), notebooks(2) {
for(auto &notebook: notebooks) { for(auto &notebook : notebooks) {
notebook.get_style_context()->add_class("juci_notebook"); notebook.get_style_context()->add_class("juci_notebook");
notebook.set_scrollable(); notebook.set_scrollable();
notebook.set_group_name("source_notebooks"); notebook.set_group_name("source_notebooks");
notebook.signal_switch_page().connect([this](Gtk::Widget *widget, guint) { notebook.signal_switch_page().connect([this](Gtk::Widget *widget, guint) {
auto hbox=dynamic_cast<Gtk::Box*>(widget); auto hbox = dynamic_cast<Gtk::Box *>(widget);
for(size_t c=0;c<hboxes.size();++c) { for(size_t c = 0; c < hboxes.size(); ++c) {
if(hboxes[c].get()==hbox) { if(hboxes[c].get() == hbox) {
focus_view(source_views[c]); focus_view(source_views[c]);
set_current_view(source_views[c]); set_current_view(source_views[c]);
break; break;
} }
} }
last_index=-1; last_index = -1;
}); });
notebook.signal_page_added().connect([this](Gtk::Widget* widget, guint) { notebook.signal_page_added().connect([this](Gtk::Widget *widget, guint) {
auto hbox=dynamic_cast<Gtk::Box*>(widget); auto hbox = dynamic_cast<Gtk::Box *>(widget);
for(size_t c=0;c<hboxes.size();++c) { for(size_t c = 0; c < hboxes.size(); ++c) {
if(hboxes[c].get()==hbox) { if(hboxes[c].get() == hbox) {
focus_view(source_views[c]); focus_view(source_views[c]);
set_current_view(source_views[c]); set_current_view(source_views[c]);
break; break;
@ -72,94 +72,94 @@ size_t Notebook::size() {
return source_views.size(); return source_views.size();
} }
Source::View* Notebook::get_view(size_t index) { Source::View *Notebook::get_view(size_t index) {
if(index>=size()) if(index >= size())
return nullptr; return nullptr;
return source_views[index]; return source_views[index];
} }
Source::View* Notebook::get_current_view() { Source::View *Notebook::get_current_view() {
if(intermediate_view) { if(intermediate_view) {
for(auto view: source_views) { for(auto view : source_views) {
if(view==intermediate_view) if(view == intermediate_view)
return view; return view;
} }
} }
for(auto view: source_views) { for(auto view : source_views) {
if(view==current_view) if(view == current_view)
return view; return view;
} }
//In case there exist a tab that has not yet received focus again in a different notebook //In case there exist a tab that has not yet received focus again in a different notebook
for(int notebook_index=0;notebook_index<2;++notebook_index) { for(int notebook_index = 0; notebook_index < 2; ++notebook_index) {
auto page=notebooks[notebook_index].get_current_page(); auto page = notebooks[notebook_index].get_current_page();
if(page>=0) if(page >= 0)
return get_view(notebook_index, page); return get_view(notebook_index, page);
} }
return nullptr; return nullptr;
} }
std::vector<Source::View*> &Notebook::get_views() { std::vector<Source::View *> &Notebook::get_views() {
return source_views; return source_views;
} }
void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_index) { void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_index) {
auto file_path=filesystem::get_normal_path(file_path_); auto file_path = filesystem::get_normal_path(file_path_);
if(notebook_index==1 && !split) if(notebook_index == 1 && !split)
toggle_split(); toggle_split();
// Use canonical path to follow symbolic links // Use canonical path to follow symbolic links
boost::system::error_code ec; boost::system::error_code ec;
auto canonical_file_path=boost::filesystem::canonical(file_path, ec); auto canonical_file_path = boost::filesystem::canonical(file_path, ec);
if(ec) if(ec)
canonical_file_path=file_path; canonical_file_path = file_path;
for(size_t c=0;c<size();c++) { for(size_t c = 0; c < size(); c++) {
if(canonical_file_path==source_views[c]->canonical_file_path) { if(canonical_file_path == source_views[c]->canonical_file_path) {
auto notebook_page=get_notebook_page(c); auto notebook_page = get_notebook_page(c);
notebooks[notebook_page.first].set_current_page(notebook_page.second); notebooks[notebook_page.first].set_current_page(notebook_page.second);
focus_view(source_views[c]); focus_view(source_views[c]);
return; return;
} }
} }
if(boost::filesystem::exists(file_path)) { if(boost::filesystem::exists(file_path)) {
std::ifstream can_read(file_path.string()); std::ifstream can_read(file_path.string());
if(!can_read) { if(!can_read) {
Terminal::get().print("Error: could not open "+file_path.string()+"\n", true); Terminal::get().print("Error: could not open " + file_path.string() + "\n", true);
return; return;
} }
can_read.close(); can_read.close();
} }
auto last_view=get_current_view(); auto last_view = get_current_view();
auto language=Source::guess_language(file_path); auto language = Source::guess_language(file_path);
std::string language_protocol_language_id; std::string language_protocol_language_id;
if(language) { if(language) {
language_protocol_language_id=language->get_id(); language_protocol_language_id = language->get_id();
if(language_protocol_language_id=="js") { if(language_protocol_language_id == "js") {
if(file_path.extension()==".ts") if(file_path.extension() == ".ts")
language_protocol_language_id="typescript"; language_protocol_language_id = "typescript";
else else
language_protocol_language_id="javascript"; language_protocol_language_id = "javascript";
} }
} }
if(language && (language->get_id()=="chdr" || language->get_id()=="cpphdr" || language->get_id()=="c" || language->get_id()=="cpp" || language->get_id()=="objc")) if(language && (language->get_id() == "chdr" || language->get_id() == "cpphdr" || language->get_id() == "c" || language->get_id() == "cpp" || language->get_id() == "objc"))
source_views.emplace_back(new Source::ClangView(file_path, language)); source_views.emplace_back(new Source::ClangView(file_path, language));
else if(language && !language_protocol_language_id.empty() && !filesystem::find_executable(language_protocol_language_id+"-language-server").empty()) else if(language && !language_protocol_language_id.empty() && !filesystem::find_executable(language_protocol_language_id + "-language-server").empty())
source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id)); source_views.emplace_back(new Source::LanguageProtocolView(file_path, language, language_protocol_language_id));
else else
source_views.emplace_back(new Source::GenericView(file_path, language)); source_views.emplace_back(new Source::GenericView(file_path, language));
auto source_view=source_views.back(); auto source_view = source_views.back();
source_view->configure(); source_view->configure();
source_view->scroll_to_cursor_delayed=[this](Source::BaseView* view, bool center, bool show_tooltips) { source_view->scroll_to_cursor_delayed = [this](Source::BaseView *view, bool center, bool show_tooltips) {
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(false); Gtk::Main::iteration(false);
if(get_current_view()==view) { if(get_current_view() == view) {
if(center) if(center)
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5); view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
else else
@ -168,90 +168,90 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
view->hide_tooltips(); view->hide_tooltips();
} }
}; };
source_view->update_status_location=[this](Source::BaseView* view) { source_view->update_status_location = [this](Source::BaseView *view) {
if(get_current_view()==view) { if(get_current_view() == view) {
auto iter=view->get_buffer()->get_insert()->get_iter(); auto iter = view->get_buffer()->get_insert()->get_iter();
status_location.set_text(" "+std::to_string(iter.get_line()+1)+":"+std::to_string(iter.get_line_offset()+1)); status_location.set_text(" " + std::to_string(iter.get_line() + 1) + ":" + std::to_string(iter.get_line_offset() + 1));
} }
}; };
source_view->update_status_file_path=[this](Source::BaseView* view) { source_view->update_status_file_path = [this](Source::BaseView *view) {
if(get_current_view()==view) if(get_current_view() == view)
status_file_path.set_text(' '+filesystem::get_short_path(view->file_path).string()); status_file_path.set_text(' ' + filesystem::get_short_path(view->file_path).string());
}; };
source_view->update_status_branch=[this](Source::BaseView* view) { source_view->update_status_branch = [this](Source::BaseView *view) {
if(get_current_view()==view) { if(get_current_view() == view) {
if(!view->status_branch.empty()) if(!view->status_branch.empty())
status_branch.set_text(" ("+view->status_branch+")"); status_branch.set_text(" (" + view->status_branch + ")");
else else
status_branch.set_text(""); status_branch.set_text("");
} }
}; };
source_view->update_status_diagnostics=[this](Source::BaseView* view) { source_view->update_status_diagnostics = [this](Source::BaseView *view) {
if(get_current_view()==view) { if(get_current_view() == view) {
std::string diagnostic_info; std::string diagnostic_info;
auto num_warnings=std::get<0>(view->status_diagnostics); auto num_warnings = std::get<0>(view->status_diagnostics);
auto num_errors=std::get<1>(view->status_diagnostics); auto num_errors = std::get<1>(view->status_diagnostics);
auto num_fix_its=std::get<2>(view->status_diagnostics); auto num_fix_its = std::get<2>(view->status_diagnostics);
if(num_warnings>0 || num_errors>0 || num_fix_its>0) { if(num_warnings > 0 || num_errors > 0 || num_fix_its > 0) {
auto normal_color=get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL); auto normal_color = get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL);
Gdk::RGBA yellow; Gdk::RGBA yellow;
yellow.set_rgba(1.0, 1.0, 0.2); yellow.set_rgba(1.0, 1.0, 0.2);
double factor=0.5; double factor = 0.5;
yellow.set_red(normal_color.get_red()+factor*(yellow.get_red()-normal_color.get_red())); yellow.set_red(normal_color.get_red() + factor * (yellow.get_red() - normal_color.get_red()));
yellow.set_green(normal_color.get_green()+factor*(yellow.get_green()-normal_color.get_green())); yellow.set_green(normal_color.get_green() + factor * (yellow.get_green() - normal_color.get_green()));
yellow.set_blue(normal_color.get_blue()+factor*(yellow.get_blue()-normal_color.get_blue())); yellow.set_blue(normal_color.get_blue() + factor * (yellow.get_blue() - normal_color.get_blue()));
Gdk::RGBA red; Gdk::RGBA red;
red.set_rgba(1.0, 0.0, 0.0); red.set_rgba(1.0, 0.0, 0.0);
factor=0.5; factor = 0.5;
red.set_red(normal_color.get_red()+factor*(red.get_red()-normal_color.get_red())); red.set_red(normal_color.get_red() + factor * (red.get_red() - normal_color.get_red()));
red.set_green(normal_color.get_green()+factor*(red.get_green()-normal_color.get_green())); red.set_green(normal_color.get_green() + factor * (red.get_green() - normal_color.get_green()));
red.set_blue(normal_color.get_blue()+factor*(red.get_blue()-normal_color.get_blue())); red.set_blue(normal_color.get_blue() + factor * (red.get_blue() - normal_color.get_blue()));
Gdk::RGBA green; Gdk::RGBA green;
green.set_rgba(0.0, 1.0, 0.0); green.set_rgba(0.0, 1.0, 0.0);
factor=0.4; factor = 0.4;
green.set_red(normal_color.get_red()+factor*(green.get_red()-normal_color.get_red())); green.set_red(normal_color.get_red() + factor * (green.get_red() - normal_color.get_red()));
green.set_green(normal_color.get_green()+factor*(green.get_green()-normal_color.get_green())); green.set_green(normal_color.get_green() + factor * (green.get_green() - normal_color.get_green()));
green.set_blue(normal_color.get_blue()+factor*(green.get_blue()-normal_color.get_blue())); green.set_blue(normal_color.get_blue() + factor * (green.get_blue() - normal_color.get_blue()));
std::stringstream yellow_ss, red_ss, green_ss; std::stringstream yellow_ss, red_ss, green_ss;
yellow_ss << std::hex << std::setfill('0') << std::setw(2) << (int)(yellow.get_red_u()>>8) << std::setw(2) << (int)(yellow.get_green_u()>>8) << std::setw(2) << (int)(yellow.get_blue_u()>>8); yellow_ss << std::hex << std::setfill('0') << std::setw(2) << (int)(yellow.get_red_u() >> 8) << std::setw(2) << (int)(yellow.get_green_u() >> 8) << std::setw(2) << (int)(yellow.get_blue_u() >> 8);
red_ss << std::hex << std::setfill('0') << std::setw(2) << (int)(red.get_red_u()>>8) << std::setw(2) << (int)(red.get_green_u()>>8) << std::setw(2) << (int)(red.get_blue_u()>>8); red_ss << std::hex << std::setfill('0') << std::setw(2) << (int)(red.get_red_u() >> 8) << std::setw(2) << (int)(red.get_green_u() >> 8) << std::setw(2) << (int)(red.get_blue_u() >> 8);
green_ss << std::hex << std::setfill('0') << std::setw(2) << (int)(green.get_red_u()>>8) << std::setw(2) << (int)(green.get_green_u()>>8) << std::setw(2) << (int)(green.get_blue_u()>>8); green_ss << std::hex << std::setfill('0') << std::setw(2) << (int)(green.get_red_u() >> 8) << std::setw(2) << (int)(green.get_green_u() >> 8) << std::setw(2) << (int)(green.get_blue_u() >> 8);
if(num_warnings>0) { if(num_warnings > 0) {
diagnostic_info+="<span color='#"+yellow_ss.str()+"'>"; diagnostic_info += "<span color='#" + yellow_ss.str() + "'>";
diagnostic_info+=std::to_string(num_warnings)+" warning"; diagnostic_info += std::to_string(num_warnings) + " warning";
if(num_warnings>1) if(num_warnings > 1)
diagnostic_info+='s'; diagnostic_info += 's';
diagnostic_info+="</span>"; diagnostic_info += "</span>";
} }
if(num_errors>0) { if(num_errors > 0) {
if(num_warnings>0) if(num_warnings > 0)
diagnostic_info+=", "; diagnostic_info += ", ";
diagnostic_info+="<span color='#"+red_ss.str()+"'>"; diagnostic_info += "<span color='#" + red_ss.str() + "'>";
diagnostic_info+=std::to_string(num_errors)+" error"; diagnostic_info += std::to_string(num_errors) + " error";
if(num_errors>1) if(num_errors > 1)
diagnostic_info+='s'; diagnostic_info += 's';
diagnostic_info+="</span>"; diagnostic_info += "</span>";
} }
if(num_fix_its>0) { if(num_fix_its > 0) {
if(num_warnings>0 || num_errors>0) if(num_warnings > 0 || num_errors > 0)
diagnostic_info+=", "; diagnostic_info += ", ";
diagnostic_info+="<span color='#"+green_ss.str()+"'>"; diagnostic_info += "<span color='#" + green_ss.str() + "'>";
diagnostic_info+=std::to_string(num_fix_its)+" fix it"; diagnostic_info += std::to_string(num_fix_its) + " fix it";
if(num_fix_its>1) if(num_fix_its > 1)
diagnostic_info+='s'; diagnostic_info += 's';
diagnostic_info+="</span>"; diagnostic_info += "</span>";
} }
} }
status_diagnostics.set_markup(diagnostic_info); status_diagnostics.set_markup(diagnostic_info);
} }
}; };
source_view->update_status_state=[this](Source::BaseView* view) { source_view->update_status_state = [this](Source::BaseView *view) {
if(get_current_view()==view) if(get_current_view() == view)
status_state.set_text(view->status_state+" "); status_state.set_text(view->status_state + " ");
}; };
scrolled_windows.emplace_back(new Gtk::ScrolledWindow()); scrolled_windows.emplace_back(new Gtk::ScrolledWindow());
hboxes.emplace_back(new Gtk::Box()); hboxes.emplace_back(new Gtk::Box());
scrolled_windows.back()->add(*source_view); scrolled_windows.back()->add(*source_view);
@ -260,23 +260,23 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
source_maps.emplace_back(Glib::wrap(gtk_source_map_new())); source_maps.emplace_back(Glib::wrap(gtk_source_map_new()));
gtk_source_map_set_view(GTK_SOURCE_MAP(source_maps.back()->gobj()), source_view->gobj()); gtk_source_map_set_view(GTK_SOURCE_MAP(source_maps.back()->gobj()), source_view->gobj());
configure(source_views.size()-1); configure(source_views.size() - 1);
//Set up tab label //Set up tab label
tab_labels.emplace_back(new TabLabel([this, source_view]() { tab_labels.emplace_back(new TabLabel([this, source_view]() {
auto index=get_index(source_view); auto index = get_index(source_view);
if(index!=static_cast<size_t>(-1)) if(index != static_cast<size_t>(-1))
close(index); close(index);
})); }));
source_view->update_tab_label=[this](Source::BaseView *view) { source_view->update_tab_label = [this](Source::BaseView *view) {
std::string title=view->file_path.filename().string(); std::string title = view->file_path.filename().string();
if(view->get_buffer()->get_modified()) if(view->get_buffer()->get_modified())
title+='*'; title += '*';
else else
title+=' '; title += ' ';
for(size_t c=0;c<size();++c) { for(size_t c = 0; c < size(); ++c) {
if(source_views[c]==view) { if(source_views[c] == view) {
auto &tab_label=tab_labels.at(c); auto &tab_label = tab_labels.at(c);
tab_label->label.set_text(title); tab_label->label.set_text(title);
tab_label->set_tooltip_text(filesystem::get_short_path(view->file_path).string()); tab_label->set_tooltip_text(filesystem::get_short_path(view->file_path).string());
return; return;
@ -284,44 +284,44 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
} }
}; };
source_view->update_tab_label(source_view); source_view->update_tab_label(source_view);
//Add star on tab label when the page is not saved: //Add star on tab label when the page is not saved:
source_view->get_buffer()->signal_modified_changed().connect([source_view]() { source_view->get_buffer()->signal_modified_changed().connect([source_view]() {
if(source_view->update_tab_label) if(source_view->update_tab_label)
source_view->update_tab_label(source_view); source_view->update_tab_label(source_view);
}); });
//Cursor history //Cursor history
auto update_cursor_locations=[this, source_view](const Gtk::TextBuffer::iterator &iter) { auto update_cursor_locations = [this, source_view](const Gtk::TextBuffer::iterator &iter) {
bool mark_moved=false; bool mark_moved = false;
if(current_cursor_location!=static_cast<size_t>(-1)) { if(current_cursor_location != static_cast<size_t>(-1)) {
auto &cursor_location=cursor_locations.at(current_cursor_location); auto &cursor_location = cursor_locations.at(current_cursor_location);
if(cursor_location.view==source_view && abs(cursor_location.mark->get_iter().get_line()-iter.get_line())<=2) { if(cursor_location.view == source_view && abs(cursor_location.mark->get_iter().get_line() - iter.get_line()) <= 2) {
source_view->get_buffer()->move_mark(cursor_location.mark, iter); source_view->get_buffer()->move_mark(cursor_location.mark, iter);
mark_moved=true; mark_moved = true;
} }
} }
if(!mark_moved) { if(!mark_moved) {
if(current_cursor_location!=static_cast<size_t>(-1)) { if(current_cursor_location != static_cast<size_t>(-1)) {
for(auto it=cursor_locations.begin()+current_cursor_location+1;it!=cursor_locations.end();) { for(auto it = cursor_locations.begin() + current_cursor_location + 1; it != cursor_locations.end();) {
it->view->get_buffer()->delete_mark(it->mark); it->view->get_buffer()->delete_mark(it->mark);
it=cursor_locations.erase(it); it = cursor_locations.erase(it);
} }
} }
cursor_locations.emplace_back(source_view, source_view->get_buffer()->create_mark(iter)); cursor_locations.emplace_back(source_view, source_view->get_buffer()->create_mark(iter));
current_cursor_location=cursor_locations.size()-1; current_cursor_location = cursor_locations.size() - 1;
} }
// Combine adjacent cursor histories that are similar // Combine adjacent cursor histories that are similar
if(!cursor_locations.empty()) { if(!cursor_locations.empty()) {
size_t cursor_locations_index=1; size_t cursor_locations_index = 1;
auto last_it=cursor_locations.begin(); auto last_it = cursor_locations.begin();
for(auto it=cursor_locations.begin()+1;it!=cursor_locations.end();) { for(auto it = cursor_locations.begin() + 1; it != cursor_locations.end();) {
if(last_it->view==it->view && abs(last_it->mark->get_iter().get_line()-it->mark->get_iter().get_line())<=2) { if(last_it->view == it->view && abs(last_it->mark->get_iter().get_line() - it->mark->get_iter().get_line()) <= 2) {
last_it->view->get_buffer()->delete_mark(last_it->mark); last_it->view->get_buffer()->delete_mark(last_it->mark);
last_it->mark=it->mark; last_it->mark = it->mark;
it=cursor_locations.erase(it); it = cursor_locations.erase(it);
if(current_cursor_location!=static_cast<size_t>(-1) && current_cursor_location>cursor_locations_index) if(current_cursor_location != static_cast<size_t>(-1) && current_cursor_location > cursor_locations_index)
--current_cursor_location; --current_cursor_location;
} }
else { else {
@ -331,22 +331,22 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
} }
} }
} }
// Remove start of cache if cache limit is exceeded // Remove start of cache if cache limit is exceeded
while(cursor_locations.size()>10) { while(cursor_locations.size() > 10) {
cursor_locations.begin()->view->get_buffer()->delete_mark(cursor_locations.begin()->mark); cursor_locations.begin()->view->get_buffer()->delete_mark(cursor_locations.begin()->mark);
cursor_locations.erase(cursor_locations.begin()); cursor_locations.erase(cursor_locations.begin());
if(current_cursor_location!=static_cast<size_t>(-1)) if(current_cursor_location != static_cast<size_t>(-1))
--current_cursor_location; --current_cursor_location;
} }
if(current_cursor_location>=cursor_locations.size()) if(current_cursor_location >= cursor_locations.size())
current_cursor_location=cursor_locations.size()-1; current_cursor_location = cursor_locations.size() - 1;
}; };
source_view->get_buffer()->signal_mark_set().connect([this, update_cursor_locations](const Gtk::TextBuffer::iterator &iter, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) { source_view->get_buffer()->signal_mark_set().connect([this, update_cursor_locations](const Gtk::TextBuffer::iterator &iter, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) {
if(mark->get_name()=="insert") { if(mark->get_name() == "insert") {
if(disable_next_update_cursor_locations) { if(disable_next_update_cursor_locations) {
disable_next_update_cursor_locations=false; disable_next_update_cursor_locations = false;
return; return;
} }
update_cursor_locations(iter); update_cursor_locations(iter);
@ -355,75 +355,75 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
source_view->get_buffer()->signal_changed().connect([source_view, update_cursor_locations] { source_view->get_buffer()->signal_changed().connect([source_view, update_cursor_locations] {
update_cursor_locations(source_view->get_buffer()->get_insert()->get_iter()); update_cursor_locations(source_view->get_buffer()->get_insert()->get_iter());
}); });
#ifdef JUCI_ENABLE_DEBUG #ifdef JUCI_ENABLE_DEBUG
if(dynamic_cast<Source::ClangView*>(source_view) || (source_view->language && source_view->language->get_id()=="rust")) { if(dynamic_cast<Source::ClangView *>(source_view) || (source_view->language && source_view->language->get_id() == "rust")) {
source_view->toggle_breakpoint=[source_view](int line_nr) { source_view->toggle_breakpoint = [source_view](int line_nr) {
if(source_view->get_source_buffer()->get_source_marks_at_line(line_nr, "debug_breakpoint").size()>0) { if(source_view->get_source_buffer()->get_source_marks_at_line(line_nr, "debug_breakpoint").size() > 0) {
auto start_iter=source_view->get_buffer()->get_iter_at_line(line_nr); auto start_iter = source_view->get_buffer()->get_iter_at_line(line_nr);
auto end_iter=source_view->get_iter_at_line_end(line_nr); auto end_iter = source_view->get_iter_at_line_end(line_nr);
source_view->get_source_buffer()->remove_source_marks(start_iter, end_iter, "debug_breakpoint"); source_view->get_source_buffer()->remove_source_marks(start_iter, end_iter, "debug_breakpoint");
source_view->get_source_buffer()->remove_source_marks(start_iter, end_iter, "debug_breakpoint_and_stop"); source_view->get_source_buffer()->remove_source_marks(start_iter, end_iter, "debug_breakpoint_and_stop");
if(Project::current && Project::debugging) if(Project::current && Project::debugging)
Project::current->debug_remove_breakpoint(source_view->file_path, line_nr+1, source_view->get_buffer()->get_line_count()+1); Project::current->debug_remove_breakpoint(source_view->file_path, line_nr + 1, source_view->get_buffer()->get_line_count() + 1);
} }
else { else {
auto iter=source_view->get_buffer()->get_iter_at_line(line_nr); auto iter = source_view->get_buffer()->get_iter_at_line(line_nr);
source_view->get_source_buffer()->create_source_mark("debug_breakpoint", iter); source_view->get_source_buffer()->create_source_mark("debug_breakpoint", iter);
if(source_view->get_source_buffer()->get_source_marks_at_line(line_nr, "debug_stop").size()>0) if(source_view->get_source_buffer()->get_source_marks_at_line(line_nr, "debug_stop").size() > 0)
source_view->get_source_buffer()->create_source_mark("debug_breakpoint_and_stop", iter); source_view->get_source_buffer()->create_source_mark("debug_breakpoint_and_stop", iter);
if(Project::current && Project::debugging) if(Project::current && Project::debugging)
Project::current->debug_add_breakpoint(source_view->file_path, line_nr+1); Project::current->debug_add_breakpoint(source_view->file_path, line_nr + 1);
} }
}; };
} }
#endif #endif
source_view->signal_focus_in_event().connect([this, source_view](GdkEventFocus *) { source_view->signal_focus_in_event().connect([this, source_view](GdkEventFocus *) {
set_current_view(source_view); set_current_view(source_view);
return false; return false;
}); });
if(notebook_index==static_cast<size_t>(-1)) { if(notebook_index == static_cast<size_t>(-1)) {
if(!split) if(!split)
notebook_index=0; notebook_index = 0;
else if(notebooks[0].get_n_pages()==0) else if(notebooks[0].get_n_pages() == 0)
notebook_index=0; notebook_index = 0;
else if(notebooks[1].get_n_pages()==0) else if(notebooks[1].get_n_pages() == 0)
notebook_index=1; notebook_index = 1;
else if(last_view) else if(last_view)
notebook_index=get_notebook_page(get_index(last_view)).first; notebook_index = get_notebook_page(get_index(last_view)).first;
} }
auto &notebook=notebooks[notebook_index]; auto &notebook = notebooks[notebook_index];
notebook.append_page(*hboxes.back(), *tab_labels.back()); notebook.append_page(*hboxes.back(), *tab_labels.back());
notebook.set_tab_reorderable(*hboxes.back(), true); notebook.set_tab_reorderable(*hboxes.back(), true);
notebook.set_tab_detachable(*hboxes.back(), true); notebook.set_tab_detachable(*hboxes.back(), true);
show_all_children(); show_all_children();
notebook.set_current_page(notebook.get_n_pages()-1); notebook.set_current_page(notebook.get_n_pages() - 1);
last_index=-1; last_index = -1;
if(last_view) { if(last_view) {
auto index=get_index(last_view); auto index = get_index(last_view);
auto notebook_page=get_notebook_page(index); auto notebook_page = get_notebook_page(index);
if(notebook_page.first==notebook_index) if(notebook_page.first == notebook_index)
last_index=index; last_index = index;
} }
set_focus_child(*source_views.back()); set_focus_child(*source_views.back());
focus_view(source_view); focus_view(source_view);
} }
void Notebook::configure(size_t index) { void Notebook::configure(size_t index) {
auto source_font_description=Pango::FontDescription(Config::get().source.font); auto source_font_description = Pango::FontDescription(Config::get().source.font);
auto source_map_font_desc=Pango::FontDescription(static_cast<std::string>(source_font_description.get_family())+" "+Config::get().source.map_font_size); auto source_map_font_desc = Pango::FontDescription(static_cast<std::string>(source_font_description.get_family()) + " " + Config::get().source.map_font_size);
source_maps.at(index)->override_font(source_map_font_desc); source_maps.at(index)->override_font(source_map_font_desc);
if(Config::get().source.show_map) { if(Config::get().source.show_map) {
if(hboxes.at(index)->get_children().size()==1) if(hboxes.at(index)->get_children().size() == 1)
hboxes.at(index)->pack_end(*source_maps.at(index), Gtk::PACK_SHRINK); hboxes.at(index)->pack_end(*source_maps.at(index), Gtk::PACK_SHRINK);
} }
else if(hboxes.at(index)->get_children().size()==2) else if(hboxes.at(index)->get_children().size() == 2)
hboxes.at(index)->remove(*source_maps.at(index)); hboxes.at(index)->remove(*source_maps.at(index));
} }
@ -435,77 +435,77 @@ bool Notebook::save(size_t index) {
} }
bool Notebook::save_current() { bool Notebook::save_current() {
if(auto view=get_current_view()) if(auto view = get_current_view())
return save(get_index(view)); return save(get_index(view));
return false; return false;
} }
bool Notebook::close(size_t index) { bool Notebook::close(size_t index) {
if(auto view=get_view(index)) { if(auto view = get_view(index)) {
if(view->get_buffer()->get_modified()){ if(view->get_buffer()->get_modified()) {
if(!save_modified_dialog(index)) if(!save_modified_dialog(index))
return false; return false;
} }
if(view==get_current_view()) { if(view == get_current_view()) {
bool focused=false; bool focused = false;
if(last_index!=static_cast<size_t>(-1)) { if(last_index != static_cast<size_t>(-1)) {
auto notebook_page=get_notebook_page(last_index); auto notebook_page = get_notebook_page(last_index);
if(notebook_page.first==get_notebook_page(get_index(view)).first) { if(notebook_page.first == get_notebook_page(get_index(view)).first) {
focus_view(source_views[last_index]); focus_view(source_views[last_index]);
notebooks[notebook_page.first].set_current_page(notebook_page.second); notebooks[notebook_page.first].set_current_page(notebook_page.second);
last_index=-1; last_index = -1;
focused=true; focused = true;
} }
} }
if(!focused) { if(!focused) {
auto notebook_page=get_notebook_page(get_index(view)); auto notebook_page = get_notebook_page(get_index(view));
if(notebook_page.second>0) if(notebook_page.second > 0)
focus_view(get_view(notebook_page.first, notebook_page.second-1)); focus_view(get_view(notebook_page.first, notebook_page.second - 1));
else { else {
size_t notebook_index=notebook_page.first==0?1:0; size_t notebook_index = notebook_page.first == 0 ? 1 : 0;
if(notebooks[notebook_index].get_n_pages()>0) if(notebooks[notebook_index].get_n_pages() > 0)
focus_view(get_view(notebook_index, notebooks[notebook_index].get_current_page())); focus_view(get_view(notebook_index, notebooks[notebook_index].get_current_page()));
else else
set_current_view(nullptr); set_current_view(nullptr);
} }
} }
} }
else if(index==last_index) else if(index == last_index)
last_index=-1; last_index = -1;
else if(index<last_index && last_index!=static_cast<size_t>(-1)) else if(index < last_index && last_index != static_cast<size_t>(-1))
last_index--; last_index--;
auto notebook_page=get_notebook_page(index); auto notebook_page = get_notebook_page(index);
notebooks[notebook_page.first].remove_page(notebook_page.second); notebooks[notebook_page.first].remove_page(notebook_page.second);
source_maps.erase(source_maps.begin()+index); source_maps.erase(source_maps.begin() + index);
if(on_close_page) if(on_close_page)
on_close_page(view); on_close_page(view);
delete_cursor_locations(view); delete_cursor_locations(view);
SelectionDialog::get()=nullptr; SelectionDialog::get() = nullptr;
CompletionDialog::get()=nullptr; CompletionDialog::get() = nullptr;
if(auto clang_view=dynamic_cast<Source::ClangView*>(view)) if(auto clang_view = dynamic_cast<Source::ClangView *>(view))
clang_view->async_delete(); clang_view->async_delete();
else else
delete view; delete view;
source_views.erase(source_views.begin()+index); source_views.erase(source_views.begin() + index);
scrolled_windows.erase(scrolled_windows.begin()+index); scrolled_windows.erase(scrolled_windows.begin() + index);
hboxes.erase(hboxes.begin()+index); hboxes.erase(hboxes.begin() + index);
tab_labels.erase(tab_labels.begin()+index); tab_labels.erase(tab_labels.begin() + index);
} }
return true; return true;
} }
void Notebook::delete_cursor_locations(Source::View *view) { void Notebook::delete_cursor_locations(Source::View *view) {
size_t cursor_locations_index=0; size_t cursor_locations_index = 0;
for(auto it=cursor_locations.begin();it!=cursor_locations.end();) { for(auto it = cursor_locations.begin(); it != cursor_locations.end();) {
if(it->view==view) { if(it->view == view) {
view->get_buffer()->delete_mark(it->mark); view->get_buffer()->delete_mark(it->mark);
it=cursor_locations.erase(it); it = cursor_locations.erase(it);
if(current_cursor_location!=static_cast<size_t>(-1) && current_cursor_location>cursor_locations_index) if(current_cursor_location != static_cast<size_t>(-1) && current_cursor_location > cursor_locations_index)
--current_cursor_location; --current_cursor_location;
} }
else { else {
@ -513,8 +513,8 @@ void Notebook::delete_cursor_locations(Source::View *view) {
++cursor_locations_index; ++cursor_locations_index;
} }
} }
if(current_cursor_location>=cursor_locations.size()) if(current_cursor_location >= cursor_locations.size())
current_cursor_location=cursor_locations.size()-1; current_cursor_location = cursor_locations.size() - 1;
} }
bool Notebook::close_current() { bool Notebook::close_current() {
@ -522,10 +522,10 @@ bool Notebook::close_current() {
} }
void Notebook::next() { void Notebook::next() {
if(auto view=get_current_view()) { if(auto view = get_current_view()) {
auto notebook_page=get_notebook_page(get_index(view)); auto notebook_page = get_notebook_page(get_index(view));
int page=notebook_page.second+1; int page = notebook_page.second + 1;
if(page>=notebooks[notebook_page.first].get_n_pages()) if(page >= notebooks[notebook_page.first].get_n_pages())
notebooks[notebook_page.first].set_current_page(0); notebooks[notebook_page.first].set_current_page(0);
else else
notebooks[notebook_page.first].set_current_page(page); notebooks[notebook_page.first].set_current_page(page);
@ -533,11 +533,11 @@ void Notebook::next() {
} }
void Notebook::previous() { void Notebook::previous() {
if(auto view=get_current_view()) { if(auto view = get_current_view()) {
auto notebook_page=get_notebook_page(get_index(view)); auto notebook_page = get_notebook_page(get_index(view));
int page=notebook_page.second-1; int page = notebook_page.second - 1;
if(page<0) if(page < 0)
notebooks[notebook_page.first].set_current_page(notebooks[notebook_page.first].get_n_pages()-1); notebooks[notebook_page.first].set_current_page(notebooks[notebook_page.first].get_n_pages() - 1);
else else
notebooks[notebook_page.first].set_current_page(page); notebooks[notebook_page.first].set_current_page(page);
} }
@ -546,24 +546,24 @@ void Notebook::previous() {
void Notebook::toggle_split() { void Notebook::toggle_split() {
if(!split) { if(!split) {
pack2(notebooks[1], true, true); pack2(notebooks[1], true, true);
set_position(get_width()/2); set_position(get_width() / 2);
show_all(); show_all();
//Make sure the position is correct //Make sure the position is correct
//TODO: report bug to gtk if it is not fixed in gtk3.22 //TODO: report bug to gtk if it is not fixed in gtk3.22
Glib::signal_timeout().connect([this] { Glib::signal_timeout().connect([this] {
set_position(get_width()/2); set_position(get_width() / 2);
return false; return false;
}, 200); }, 200);
} }
else { else {
for(size_t c=size()-1;c!=static_cast<size_t>(-1);--c) { for(size_t c = size() - 1; c != static_cast<size_t>(-1); --c) {
auto notebook_index=get_notebook_page(c).first; auto notebook_index = get_notebook_page(c).first;
if(notebook_index==1 && !close(c)) if(notebook_index == 1 && !close(c))
return; return;
} }
remove(notebooks[1]); remove(notebooks[1]);
} }
split=!split; split = !split;
} }
void Notebook::toggle_tabs() { void Notebook::toggle_tabs() {
//Show / Hide tabs for each notebook. //Show / Hide tabs for each notebook.
@ -574,7 +574,7 @@ void Notebook::toggle_tabs() {
boost::filesystem::path Notebook::get_current_folder() { boost::filesystem::path Notebook::get_current_folder() {
if(!Directories::get().path.empty()) if(!Directories::get().path.empty())
return Directories::get().path; return Directories::get().path;
else if(auto view=get_current_view()) else if(auto view = get_current_view())
return view->file_path.parent_path(); return view->file_path.parent_path();
else else
return boost::filesystem::path(); return boost::filesystem::path();
@ -582,9 +582,9 @@ boost::filesystem::path Notebook::get_current_folder() {
std::vector<std::pair<size_t, Source::View *>> Notebook::get_notebook_views() { std::vector<std::pair<size_t, Source::View *>> Notebook::get_notebook_views() {
std::vector<std::pair<size_t, Source::View *>> notebook_views; std::vector<std::pair<size_t, Source::View *>> notebook_views;
for(size_t notebook_index=0;notebook_index<notebooks.size();++notebook_index) { for(size_t notebook_index = 0; notebook_index < notebooks.size(); ++notebook_index) {
for(int page=0;page<notebooks[notebook_index].get_n_pages();++page) { for(int page = 0; page < notebooks[notebook_index].get_n_pages(); ++page) {
if(auto view=get_view(notebook_index, page)) if(auto view = get_view(notebook_index, page))
notebook_views.emplace_back(notebook_index, view); notebook_views.emplace_back(notebook_index, view);
} }
} }
@ -613,60 +613,60 @@ void Notebook::clear_status() {
} }
size_t Notebook::get_index(Source::View *view) { size_t Notebook::get_index(Source::View *view) {
for(size_t c=0;c<size();++c) { for(size_t c = 0; c < size(); ++c) {
if(source_views[c]==view) if(source_views[c] == view)
return c; return c;
} }
return -1; return -1;
} }
Source::View *Notebook::get_view(size_t notebook_index, int page) { Source::View *Notebook::get_view(size_t notebook_index, int page) {
if(notebook_index==static_cast<size_t>(-1) || notebook_index>=notebooks.size() || if(notebook_index == static_cast<size_t>(-1) || notebook_index >= notebooks.size() ||
page<0 || page>=notebooks[notebook_index].get_n_pages()) page < 0 || page >= notebooks[notebook_index].get_n_pages())
return nullptr; return nullptr;
auto hbox=dynamic_cast<Gtk::Box*>(notebooks[notebook_index].get_nth_page(page)); auto hbox = dynamic_cast<Gtk::Box *>(notebooks[notebook_index].get_nth_page(page));
auto scrolled_window=dynamic_cast<Gtk::ScrolledWindow*>(hbox->get_children()[0]); auto scrolled_window = dynamic_cast<Gtk::ScrolledWindow *>(hbox->get_children()[0]);
return dynamic_cast<Source::View*>(scrolled_window->get_children()[0]); return dynamic_cast<Source::View *>(scrolled_window->get_children()[0]);
} }
void Notebook::focus_view(Source::View *view) { void Notebook::focus_view(Source::View *view) {
intermediate_view=view; intermediate_view = view;
view->grab_focus(); view->grab_focus();
} }
std::pair<size_t, int> Notebook::get_notebook_page(size_t index) { std::pair<size_t, int> Notebook::get_notebook_page(size_t index) {
if(index>=hboxes.size()) if(index >= hboxes.size())
return {-1, -1}; return {-1, -1};
for(size_t c=0;c<notebooks.size();++c) { for(size_t c = 0; c < notebooks.size(); ++c) {
auto page_num=notebooks[c].page_num(*hboxes[index]); auto page_num = notebooks[c].page_num(*hboxes[index]);
if(page_num>=0) if(page_num >= 0)
return {c, page_num}; return {c, page_num};
} }
return {-1, -1}; return {-1, -1};
} }
void Notebook::set_current_view(Source::View *view) { void Notebook::set_current_view(Source::View *view) {
intermediate_view=nullptr; intermediate_view = nullptr;
if(current_view!=view) { if(current_view != view) {
if(auto view=get_current_view()) { if(auto view = get_current_view()) {
view->hide_tooltips(); view->hide_tooltips();
view->hide_dialogs(); view->hide_dialogs();
} }
current_view=view; current_view = view;
if(view && on_change_page) if(view && on_change_page)
on_change_page(view); on_change_page(view);
} }
} }
bool Notebook::save_modified_dialog(size_t index) { bool Notebook::save_modified_dialog(size_t index) {
Gtk::MessageDialog dialog(*static_cast<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_default_response(Gtk::RESPONSE_YES);
dialog.set_secondary_text("Do you want to save: " + get_view(index)->file_path.string()+" ?"); dialog.set_secondary_text("Do you want to save: " + get_view(index)->file_path.string() + " ?");
int result = dialog.run(); int result = dialog.run();
if(result==Gtk::RESPONSE_YES) { if(result == Gtk::RESPONSE_YES) {
return save(index); return save(index);
} }
else if(result==Gtk::RESPONSE_NO) { else if(result == Gtk::RESPONSE_NO) {
return true; return true;
} }
else { else {

65
src/notebook.h

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <iostream>
#include "gtkmm.h" #include "gtkmm.h"
#include "source.h" #include "source.h"
#include <type_traits> #include <iostream>
#include <map> #include <map>
#include <sigc++/sigc++.h> #include <sigc++/sigc++.h>
#include <type_traits>
class Notebook : public Gtk::Paned { class Notebook : public Gtk::Paned {
class TabLabel : public Gtk::EventBox { class TabLabel : public Gtk::EventBox {
@ -12,28 +12,29 @@ class Notebook : public Gtk::Paned {
TabLabel(const std::function<void()> &on_close); TabLabel(const std::function<void()> &on_close);
Gtk::Label label; Gtk::Label label;
}; };
class CursorLocation { class CursorLocation {
public: public:
CursorLocation(Source::View *view, Glib::RefPtr<Gtk::TextBuffer::Mark> mark_) : view(view), mark(std::move(mark_)) {} CursorLocation(Source::View *view, Glib::RefPtr<Gtk::TextBuffer::Mark> mark_) : view(view), mark(std::move(mark_)) {}
Source::View *view; Source::View *view;
Glib::RefPtr<Gtk::TextBuffer::Mark> mark; Glib::RefPtr<Gtk::TextBuffer::Mark> mark;
}; };
private: private:
Notebook(); Notebook();
public: public:
static Notebook &get() { static Notebook &get() {
static Notebook singleton; static Notebook singleton;
return singleton; return singleton;
} }
size_t size(); size_t size();
Source::View* get_view(size_t index); Source::View *get_view(size_t index);
Source::View* get_current_view(); Source::View *get_current_view();
std::vector<Source::View*> &get_views(); std::vector<Source::View *> &get_views();
void open(const boost::filesystem::path &file_path, size_t notebook_index=-1); void open(const boost::filesystem::path &file_path, size_t notebook_index = -1);
void configure(size_t index); void configure(size_t index);
bool save(size_t index); bool save(size_t index);
bool save_current(); bool save_current();
@ -42,10 +43,10 @@ public:
void next(); void next();
void previous(); void previous();
void toggle_split(); void toggle_split();
/// Hide/Show tabs. /// Hide/Show tabs.
void toggle_tabs(); void toggle_tabs();
boost::filesystem::path get_current_folder(); boost::filesystem::path get_current_folder();
std::vector<std::pair<size_t, Source::View*>> get_notebook_views(); std::vector<std::pair<size_t, Source::View *>> get_notebook_views();
Gtk::Label status_location; Gtk::Label status_location;
Gtk::Label status_file_path; Gtk::Label status_file_path;
@ -54,35 +55,35 @@ public:
Gtk::Label status_state; Gtk::Label status_state;
void update_status(Source::BaseView *view); void update_status(Source::BaseView *view);
void clear_status(); void clear_status();
std::function<void(Source::View*)> on_change_page; std::function<void(Source::View *)> on_change_page;
std::function<void(Source::View*)> on_close_page; std::function<void(Source::View *)> on_close_page;
/// Cursor history /// Cursor history
std::vector<CursorLocation> cursor_locations; std::vector<CursorLocation> cursor_locations;
size_t current_cursor_location=-1; size_t current_cursor_location = -1;
bool disable_next_update_cursor_locations=false; bool disable_next_update_cursor_locations = false;
void delete_cursor_locations(Source::View *view); void delete_cursor_locations(Source::View *view);
private: private:
size_t get_index(Source::View *view); size_t get_index(Source::View *view);
Source::View *get_view(size_t notebook_index, int page); Source::View *get_view(size_t notebook_index, int page);
void focus_view(Source::View *view); void focus_view(Source::View *view);
std::pair<size_t, int> get_notebook_page(size_t index); std::pair<size_t, int> get_notebook_page(size_t index);
std::vector<Gtk::Notebook> notebooks; std::vector<Gtk::Notebook> notebooks;
std::vector<Source::View*> source_views; //Is NOT freed in destructor, this is intended for quick program exit. 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::Widget>> source_maps;
std::vector<std::unique_ptr<Gtk::ScrolledWindow> > scrolled_windows; std::vector<std::unique_ptr<Gtk::ScrolledWindow>> scrolled_windows;
std::vector<std::unique_ptr<Gtk::Box> > hboxes; std::vector<std::unique_ptr<Gtk::Box>> hboxes;
std::vector<std::unique_ptr<TabLabel> > tab_labels; std::vector<std::unique_ptr<TabLabel>> tab_labels;
bool split=false; bool split = false;
size_t last_index=-1; size_t last_index = -1;
void set_current_view(Source::View *view); void set_current_view(Source::View *view);
Source::View* current_view=nullptr; Source::View *current_view = nullptr;
Source::View* intermediate_view=nullptr; Source::View *intermediate_view = nullptr;
bool save_modified_dialog(size_t index); bool save_modified_dialog(size_t index);
}; };

795
src/project.cc

File diff suppressed because it is too large Load Diff

79
src/project.h

@ -1,12 +1,12 @@
#pragma once #pragma once
#include <gtkmm.h>
#include <boost/filesystem.hpp>
#include <atomic>
#include <unordered_map>
#include "tooltips.h"
#include "dispatcher.h" #include "dispatcher.h"
#include <iostream>
#include "project_build.h" #include "project_build.h"
#include "tooltips.h"
#include <atomic>
#include <boost/filesystem.hpp>
#include <gtkmm.h>
#include <iostream>
#include <unordered_map>
namespace Project { namespace Project {
class DebugRunArguments { class DebugRunArguments {
@ -15,47 +15,48 @@ namespace Project {
bool remote_enabled; bool remote_enabled;
std::string remote_host_port; std::string remote_host_port;
}; };
class DebugOptions : public Gtk::Popover { class DebugOptions : public Gtk::Popover {
public: public:
DebugOptions() : Gtk::Popover(), vbox(Gtk::Orientation::ORIENTATION_VERTICAL) { add(vbox); } DebugOptions() : Gtk::Popover(), vbox(Gtk::Orientation::ORIENTATION_VERTICAL) { add(vbox); }
Gtk::Box vbox; Gtk::Box vbox;
}; };
Gtk::Label &debug_status_label(); Gtk::Label &debug_status_label();
void save_files(const boost::filesystem::path &path); void save_files(const boost::filesystem::path &path);
void on_save(size_t index); void on_save(size_t index);
extern boost::filesystem::path debug_last_stop_file_path; extern boost::filesystem::path debug_last_stop_file_path;
extern std::unordered_map<std::string, std::string> run_arguments; extern std::unordered_map<std::string, std::string> run_arguments;
extern std::unordered_map<std::string, DebugRunArguments> debug_run_arguments; extern std::unordered_map<std::string, DebugRunArguments> debug_run_arguments;
extern std::atomic<bool> compiling; extern std::atomic<bool> compiling;
extern std::atomic<bool> debugging; extern std::atomic<bool> debugging;
extern std::pair<boost::filesystem::path, std::pair<int, int> > debug_stop; extern std::pair<boost::filesystem::path, std::pair<int, int>> debug_stop;
extern std::string debug_status; extern std::string debug_status;
void debug_update_status(const std::string &new_debug_status); void debug_update_status(const std::string &new_debug_status);
void debug_activate_menu_items(); void debug_activate_menu_items();
void debug_update_stop(); void debug_update_stop();
class Base : public std::enable_shared_from_this<Base> { class Base : public std::enable_shared_from_this<Base> {
protected: protected:
static std::unique_ptr<DebugOptions> debug_options; static std::unique_ptr<DebugOptions> debug_options;
public: public:
Base() = default; Base() = default;
Base(std::unique_ptr<Build> &&build): build(std::move(build)) {} Base(std::unique_ptr<Build> &&build) : build(std::move(build)) {}
virtual ~Base() = default; virtual ~Base() = default;
std::unique_ptr<Build> build; std::unique_ptr<Build> build;
Dispatcher dispatcher; Dispatcher dispatcher;
virtual std::pair<std::string, std::string> get_run_arguments(); virtual std::pair<std::string, std::string> get_run_arguments();
virtual void compile(); virtual void compile();
virtual void compile_and_run(); virtual void compile_and_run();
virtual void recreate_build(); virtual void recreate_build();
virtual void show_symbols(); virtual void show_symbols();
virtual std::pair<std::string, std::string> debug_get_run_arguments(); virtual std::pair<std::string, std::string> debug_get_run_arguments();
virtual Project::DebugOptions *debug_get_options() { return nullptr; } virtual Project::DebugOptions *debug_get_options() { return nullptr; }
Tooltips debug_variable_tooltips; Tooltips debug_variable_tooltips;
@ -75,11 +76,11 @@ namespace Project {
virtual void debug_write(const std::string &buffer) {} virtual void debug_write(const std::string &buffer) {}
virtual void debug_cancel() {} virtual void debug_cancel() {}
}; };
class LLDB : public virtual Base { class LLDB : public virtual Base {
public: public:
~LLDB() override { dispatcher.disconnect(); } ~LLDB() override { dispatcher.disconnect(); }
#ifdef JUCI_ENABLE_DEBUG #ifdef JUCI_ENABLE_DEBUG
std::pair<std::string, std::string> debug_get_run_arguments() override; std::pair<std::string, std::string> debug_get_run_arguments() override;
Project::DebugOptions *debug_get_options() override; Project::DebugOptions *debug_get_options() override;
@ -100,68 +101,68 @@ namespace Project {
void debug_cancel() override; void debug_cancel() override;
#endif #endif
}; };
class LanguageProtocol : public virtual Base { class LanguageProtocol : public virtual Base {
public: public:
virtual std::string get_language_id()=0; virtual std::string get_language_id() = 0;
void show_symbols() override; void show_symbols() override;
}; };
class Clang : public LLDB { class Clang : public LLDB {
public: public:
Clang(std::unique_ptr<Build> &&build) : Base(std::move(build)) {} Clang(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
std::pair<std::string, std::string> get_run_arguments() override; std::pair<std::string, std::string> get_run_arguments() override;
void compile() override; void compile() override;
void compile_and_run() override; void compile_and_run() override;
void recreate_build() override; void recreate_build() override;
}; };
class Markdown : public Base { class Markdown : public Base {
public: public:
Markdown(std::unique_ptr<Build> &&build) : Base(std::move(build)) {} Markdown(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
~Markdown() override; ~Markdown() override;
boost::filesystem::path last_temp_path; boost::filesystem::path last_temp_path;
void compile_and_run() override; void compile_and_run() override;
}; };
class Python : public LanguageProtocol { class Python : public LanguageProtocol {
public: public:
Python(std::unique_ptr<Build> &&build) : Base(std::move(build)) {} Python(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run() override; void compile_and_run() override;
std::string get_language_id() override { return "python"; } std::string get_language_id() override { return "python"; }
}; };
class JavaScript : public LanguageProtocol { class JavaScript : public LanguageProtocol {
public: public:
JavaScript(std::unique_ptr<Build> &&build) : Base(std::move(build)) {} JavaScript(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run() override; void compile_and_run() override;
std::string get_language_id() override { return "javascript"; } std::string get_language_id() override { return "javascript"; }
}; };
class HTML : public Base { class HTML : public Base {
public: public:
HTML(std::unique_ptr<Build> &&build) : Base(std::move(build)) {} HTML(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run() override; void compile_and_run() override;
}; };
class Rust : public LLDB, public LanguageProtocol { class Rust : public LLDB, public LanguageProtocol {
public: public:
Rust(std::unique_ptr<Build> &&build) : Base(std::move(build)) {} Rust(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
std::pair<std::string, std::string> get_run_arguments() override; std::pair<std::string, std::string> get_run_arguments() override;
void compile() override; void compile() override;
void compile_and_run() override; void compile_and_run() override;
std::string get_language_id() override { return "rust"; } std::string get_language_id() override { return "rust"; }
}; };
std::shared_ptr<Base> create(); std::shared_ptr<Base> create();
extern std::shared_ptr<Base> current; extern std::shared_ptr<Base> current;
}; }; // namespace Project

108
src/project_build.cc

@ -3,102 +3,102 @@
#include "filesystem.h" #include "filesystem.h"
std::unique_ptr<Project::Build> Project::Build::create(const boost::filesystem::path &path) { std::unique_ptr<Project::Build> Project::Build::create(const boost::filesystem::path &path) {
auto search_path=boost::filesystem::is_directory(path)?path:path.parent_path(); auto search_path = boost::filesystem::is_directory(path) ? path : path.parent_path();
while(true) { while(true) {
if(boost::filesystem::exists(search_path/"CMakeLists.txt")) { if(boost::filesystem::exists(search_path / "CMakeLists.txt")) {
std::unique_ptr<Project::Build> build(new CMakeBuild(path)); std::unique_ptr<Project::Build> build(new CMakeBuild(path));
if(!build->project_path.empty()) if(!build->project_path.empty())
return build; return build;
else else
return std::make_unique<Project::Build>(); return std::make_unique<Project::Build>();
} }
if(boost::filesystem::exists(search_path/"meson.build")) { if(boost::filesystem::exists(search_path / "meson.build")) {
std::unique_ptr<Project::Build> build(new MesonBuild(path)); std::unique_ptr<Project::Build> build(new MesonBuild(path));
if(!build->project_path.empty()) if(!build->project_path.empty())
return build; return build;
} }
if(boost::filesystem::exists(search_path/"Cargo.toml")) { if(boost::filesystem::exists(search_path / "Cargo.toml")) {
std::unique_ptr<Project::Build> build(new CargoBuild()); std::unique_ptr<Project::Build> build(new CargoBuild());
build->project_path=search_path; build->project_path = search_path;
return build; return build;
} }
if(boost::filesystem::exists(search_path/"package.json")) { if(boost::filesystem::exists(search_path / "package.json")) {
std::unique_ptr<Project::Build> build(new NpmBuild()); std::unique_ptr<Project::Build> build(new NpmBuild());
build->project_path=search_path; build->project_path = search_path;
return build; return build;
} }
if(search_path==search_path.root_directory()) if(search_path == search_path.root_directory())
break; break;
search_path=search_path.parent_path(); search_path = search_path.parent_path();
} }
return std::make_unique<Project::Build>(); return std::make_unique<Project::Build>();
} }
boost::filesystem::path Project::Build::get_default_path() { boost::filesystem::path Project::Build::get_default_path() {
if(project_path.empty()) if(project_path.empty())
return boost::filesystem::path(); return boost::filesystem::path();
boost::filesystem::path default_build_path=Config::get().project.default_build_path; boost::filesystem::path default_build_path = Config::get().project.default_build_path;
const std::string path_variable_project_directory_name="<project_directory_name>"; const std::string path_variable_project_directory_name = "<project_directory_name>";
size_t pos=0; size_t pos = 0;
auto default_build_path_string=default_build_path.string(); auto default_build_path_string = default_build_path.string();
auto path_filename_string=project_path.filename().string(); auto path_filename_string = project_path.filename().string();
while((pos=default_build_path_string.find(path_variable_project_directory_name, pos))!=std::string::npos) { while((pos = default_build_path_string.find(path_variable_project_directory_name, pos)) != std::string::npos) {
default_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string); default_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string);
pos+=path_filename_string.size(); pos += path_filename_string.size();
} }
if(pos!=0) if(pos != 0)
default_build_path=default_build_path_string; default_build_path = default_build_path_string;
if(default_build_path.is_relative()) if(default_build_path.is_relative())
default_build_path=project_path/default_build_path; default_build_path = project_path / default_build_path;
return filesystem::get_normal_path(default_build_path); return filesystem::get_normal_path(default_build_path);
} }
boost::filesystem::path Project::Build::get_debug_path() { boost::filesystem::path Project::Build::get_debug_path() {
if(project_path.empty()) if(project_path.empty())
return boost::filesystem::path(); return boost::filesystem::path();
boost::filesystem::path debug_build_path=Config::get().project.debug_build_path; boost::filesystem::path debug_build_path = Config::get().project.debug_build_path;
const std::string path_variable_project_directory_name="<project_directory_name>"; const std::string path_variable_project_directory_name = "<project_directory_name>";
size_t pos=0; size_t pos = 0;
auto debug_build_path_string=debug_build_path.string(); auto debug_build_path_string = debug_build_path.string();
auto path_filename_string=project_path.filename().string(); auto path_filename_string = project_path.filename().string();
while((pos=debug_build_path_string.find(path_variable_project_directory_name, pos))!=std::string::npos) { while((pos = debug_build_path_string.find(path_variable_project_directory_name, pos)) != std::string::npos) {
debug_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string); debug_build_path_string.replace(pos, path_variable_project_directory_name.size(), path_filename_string);
pos+=path_filename_string.size(); pos += path_filename_string.size();
} }
if(pos!=0) if(pos != 0)
debug_build_path=debug_build_path_string; debug_build_path = debug_build_path_string;
const std::string path_variable_default_build_path="<default_build_path>"; const std::string path_variable_default_build_path = "<default_build_path>";
pos=0; pos = 0;
debug_build_path_string=debug_build_path.string(); debug_build_path_string = debug_build_path.string();
auto default_build_path=Config::get().project.default_build_path; auto default_build_path = Config::get().project.default_build_path;
while((pos=debug_build_path_string.find(path_variable_default_build_path, pos))!=std::string::npos) { while((pos = debug_build_path_string.find(path_variable_default_build_path, pos)) != std::string::npos) {
debug_build_path_string.replace(pos, path_variable_default_build_path.size(), default_build_path); debug_build_path_string.replace(pos, path_variable_default_build_path.size(), default_build_path);
pos+=default_build_path.size(); pos += default_build_path.size();
} }
if(pos!=0) if(pos != 0)
debug_build_path=debug_build_path_string; debug_build_path = debug_build_path_string;
if(debug_build_path.is_relative()) if(debug_build_path.is_relative())
debug_build_path=project_path/debug_build_path; debug_build_path = project_path / debug_build_path;
return filesystem::get_normal_path(debug_build_path); return filesystem::get_normal_path(debug_build_path);
} }
Project::CMakeBuild::CMakeBuild(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; project_path = cmake.project_path;
} }
bool Project::CMakeBuild::update_default(bool force) { bool Project::CMakeBuild::update_default(bool force) {
@ -118,7 +118,7 @@ boost::filesystem::path Project::CMakeBuild::get_executable(const boost::filesys
} }
Project::MesonBuild::MesonBuild(const boost::filesystem::path &path) : Project::Build(), meson(path) { Project::MesonBuild::MesonBuild(const boost::filesystem::path &path) : Project::Build(), meson(path) {
project_path=meson.project_path; project_path = meson.project_path;
} }
bool Project::MesonBuild::update_default(bool force) { bool Project::MesonBuild::update_default(bool force) {

50
src/project_build.h

@ -1,60 +1,62 @@
#pragma once #pragma once
#include <boost/filesystem.hpp>
#include "cmake.h" #include "cmake.h"
#include "meson.h" #include "meson.h"
#include <boost/filesystem.hpp>
namespace Project { namespace Project {
class Build { class Build {
public: public:
virtual ~Build() {} virtual ~Build() {}
boost::filesystem::path project_path; boost::filesystem::path project_path;
virtual boost::filesystem::path get_default_path(); virtual boost::filesystem::path get_default_path();
virtual bool update_default(bool force=false) {return false;} virtual bool update_default(bool force = false) { return false; }
virtual boost::filesystem::path get_debug_path(); virtual boost::filesystem::path get_debug_path();
virtual bool update_debug(bool force=false) {return false;} virtual bool update_debug(bool force = false) { return false; }
virtual std::string get_compile_command() { return std::string(); } virtual std::string get_compile_command() { return std::string(); }
virtual boost::filesystem::path get_executable(const boost::filesystem::path &path) {return boost::filesystem::path();} 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); static std::unique_ptr<Build> create(const boost::filesystem::path &path);
}; };
class CMakeBuild : public Build { class CMakeBuild : public Build {
::CMake cmake; ::CMake cmake;
public: public:
CMakeBuild(const boost::filesystem::path &path); CMakeBuild(const boost::filesystem::path &path);
bool update_default(bool force=false) override; bool update_default(bool force = false) override;
bool update_debug(bool force=false) override; bool update_debug(bool force = false) override;
std::string get_compile_command() override; std::string get_compile_command() override;
boost::filesystem::path get_executable(const boost::filesystem::path &path) override; boost::filesystem::path get_executable(const boost::filesystem::path &path) override;
}; };
class MesonBuild : public Build { class MesonBuild : public Build {
Meson meson; Meson meson;
public: public:
MesonBuild(const boost::filesystem::path &path); MesonBuild(const boost::filesystem::path &path);
bool update_default(bool force=false) override; bool update_default(bool force = false) override;
bool update_debug(bool force=false) override; bool update_debug(bool force = false) override;
std::string get_compile_command() override; std::string get_compile_command() override;
boost::filesystem::path get_executable(const boost::filesystem::path &path) override; boost::filesystem::path get_executable(const boost::filesystem::path &path) override;
}; };
class CargoBuild : public Build { class CargoBuild : public Build {
public: public:
boost::filesystem::path get_default_path() override { return project_path/"target"/"debug"; } boost::filesystem::path get_default_path() override { return project_path / "target" / "debug"; }
bool update_default(bool force=false) override { return true; } bool update_default(bool force = false) override { return true; }
boost::filesystem::path get_debug_path() override { return get_default_path(); } boost::filesystem::path get_debug_path() override { return get_default_path(); }
bool update_debug(bool force=false) override { return true; } bool update_debug(bool force = false) override { return true; }
std::string get_compile_command() override { return "cargo build"; } std::string get_compile_command() override { return "cargo build"; }
boost::filesystem::path get_executable(const boost::filesystem::path &path) override { return get_debug_path()/project_path.filename(); } boost::filesystem::path get_executable(const boost::filesystem::path &path) override { return get_debug_path() / project_path.filename(); }
}; };
class NpmBuild : public Build {}; class NpmBuild : public Build {};
} } // namespace Project

300
src/selection_dialog.cc

@ -9,7 +9,7 @@ SelectionDialogBase::ListViewText::ListViewText(bool use_markup) : Gtk::TreeView
get_column(0)->add_attribute(cell_renderer.property_markup(), column_record.text); get_column(0)->add_attribute(cell_renderer.property_markup(), column_record.text);
else else
get_column(0)->add_attribute(cell_renderer.property_text(), column_record.text); get_column(0)->add_attribute(cell_renderer.property_text(), column_record.text);
get_selection()->set_mode(Gtk::SelectionMode::SELECTION_BROWSE); get_selection()->set_mode(Gtk::SelectionMode::SELECTION_BROWSE);
set_enable_search(true); set_enable_search(true);
set_headers_visible(false); set_headers_visible(false);
@ -19,43 +19,43 @@ SelectionDialogBase::ListViewText::ListViewText(bool use_markup) : Gtk::TreeView
set_rules_hint(true); set_rules_hint(true);
} }
void SelectionDialogBase::ListViewText::append(const std::string& value) { void SelectionDialogBase::ListViewText::append(const std::string &value) {
auto new_row=list_store->append(); auto new_row = list_store->append();
new_row->set_value(column_record.text, value); new_row->set_value(column_record.text, value);
new_row->set_value(column_record.index, size++); new_row->set_value(column_record.index, size++);
} }
void SelectionDialogBase::ListViewText::erase_rows() { void SelectionDialogBase::ListViewText::erase_rows() {
list_store->clear(); list_store->clear();
size=0; size = 0;
} }
void SelectionDialogBase::ListViewText::clear() { void SelectionDialogBase::ListViewText::clear() {
unset_model(); unset_model();
list_store.reset(); list_store.reset();
size=0; size = 0;
} }
SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup): SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup)
start_mark(start_mark), text_view(text_view), window(Gtk::WindowType::WINDOW_POPUP), vbox(Gtk::Orientation::ORIENTATION_VERTICAL), list_view_text(use_markup), show_search_entry(show_search_entry) { : start_mark(start_mark), text_view(text_view), window(Gtk::WindowType::WINDOW_POPUP), vbox(Gtk::Orientation::ORIENTATION_VERTICAL), list_view_text(use_markup), show_search_entry(show_search_entry) {
auto g_application=g_application_get_default(); auto g_application = g_application_get_default();
auto gio_application=Glib::wrap(g_application, true); auto gio_application = Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application); auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
window.set_transient_for(*application->get_active_window()); window.set_transient_for(*application->get_active_window());
window.set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_COMBO); window.set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_COMBO);
search_entry.signal_changed().connect([this] { search_entry.signal_changed().connect([this] {
if(on_search_entry_changed) if(on_search_entry_changed)
on_search_entry_changed(search_entry.get_text()); on_search_entry_changed(search_entry.get_text());
}, false); }, false);
list_view_text.set_search_entry(search_entry); list_view_text.set_search_entry(search_entry);
window.set_default_size(0, 0); window.set_default_size(0, 0);
window.property_decorated()=false; window.property_decorated() = false;
window.set_skip_taskbar_hint(true); window.set_skip_taskbar_hint(true);
scrolled_window.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC); scrolled_window.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC);
scrolled_window.add(list_view_text); scrolled_window.add(list_view_text);
@ -64,61 +64,61 @@ SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const Glib::R
vbox.pack_start(scrolled_window, true, true); vbox.pack_start(scrolled_window, true, true);
window.add(vbox); window.add(vbox);
list_view_text.signal_realize().connect([this](){ list_view_text.signal_realize().connect([this]() {
auto g_application=g_application_get_default(); auto g_application = g_application_get_default();
auto gio_application=Glib::wrap(g_application, true); auto gio_application = Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application); auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
auto application_window=application->get_active_window(); auto application_window = application->get_active_window();
// Calculate window width and height // Calculate window width and height
int row_width=0, padding_height=0, window_height=0; int row_width = 0, padding_height = 0, window_height = 0;
Gdk::Rectangle rect; Gdk::Rectangle rect;
auto children=list_view_text.get_model()->children(); auto children = list_view_text.get_model()->children();
size_t c=0; size_t c = 0;
for(auto it=children.begin();it!=children.end() && c<10;++it) { for(auto it = children.begin(); it != children.end() && c < 10; ++it) {
list_view_text.get_cell_area(list_view_text.get_model()->get_path(it), *(list_view_text.get_column(0)), rect); list_view_text.get_cell_area(list_view_text.get_model()->get_path(it), *(list_view_text.get_column(0)), rect);
if(c==0) { if(c == 0) {
row_width=rect.get_width()+rect.get_x()*2; row_width = rect.get_width() + rect.get_x() * 2;
padding_height=rect.get_y()*2; padding_height = rect.get_y() * 2;
} }
window_height+=rect.get_height()+padding_height; window_height += rect.get_height() + padding_height;
++c; ++c;
} }
if(this->text_view && row_width>this->text_view->get_width()*2/3) if(this->text_view && row_width > this->text_view->get_width() * 2 / 3)
row_width=this->text_view->get_width()*2/3; row_width = this->text_view->get_width() * 2 / 3;
else if(row_width>application_window->get_width()/2) else if(row_width > application_window->get_width() / 2)
row_width=application_window->get_width()/2; row_width = application_window->get_width() / 2;
else else
scrolled_window.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC); scrolled_window.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC);
if(this->show_search_entry) if(this->show_search_entry)
window_height+=search_entry.get_height(); window_height += search_entry.get_height();
int window_width=row_width+1; int window_width = row_width + 1;
window.resize(window_width, window_height); window.resize(window_width, window_height);
if(this->text_view) { if(this->text_view) {
Gdk::Rectangle iter_rect; Gdk::Rectangle iter_rect;
this->text_view->get_iter_location(this->start_mark->get_iter(), iter_rect); this->text_view->get_iter_location(this->start_mark->get_iter(), iter_rect);
Gdk::Rectangle visible_rect; Gdk::Rectangle visible_rect;
this->text_view->get_visible_rect(visible_rect); this->text_view->get_visible_rect(visible_rect);
int buffer_x=std::max(iter_rect.get_x(), visible_rect.get_x()); 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 buffer_y = iter_rect.get_y() + iter_rect.get_height();
int window_x, window_y; int window_x, window_y;
this->text_view->buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, buffer_x, buffer_y, window_x, window_y); this->text_view->buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, buffer_x, buffer_y, window_x, window_y);
int root_x, root_y; int root_x, root_y;
this->text_view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(window_x, window_y, root_x, root_y); this->text_view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(window_x, window_y, root_x, root_y);
window.move(root_x, root_y+1); //TODO: replace 1 with some margin window.move(root_x, root_y + 1); //TODO: replace 1 with some margin
} }
else { else {
int root_x, root_y; int root_x, root_y;
application_window->get_position(root_x, root_y); application_window->get_position(root_x, root_y);
root_x+=application_window->get_width()/2-window_width/2; root_x += application_window->get_width() / 2 - window_width / 2;
root_y+=application_window->get_height()/2-window_height/2; root_y += application_window->get_height() / 2 - window_height / 2;
window.move(root_x, root_y); window.move(root_x, root_y);
} }
}); });
list_view_text.signal_cursor_changed().connect([this] { list_view_text.signal_cursor_changed().connect([this] {
cursor_changed(); cursor_changed();
}); });
@ -132,21 +132,21 @@ SelectionDialogBase::~SelectionDialogBase() {
void SelectionDialogBase::cursor_changed() { void SelectionDialogBase::cursor_changed() {
if(!is_visible()) if(!is_visible())
return; return;
auto it=list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
auto index=static_cast<unsigned int>(-1); auto index = static_cast<unsigned int>(-1);
if(it) if(it)
index=it->get_value(list_view_text.column_record.index); index = it->get_value(list_view_text.column_record.index);
if(last_index==index) if(last_index == index)
return; return;
if(on_changed) { if(on_changed) {
std::string text; std::string text;
if(it) if(it)
text=it->get_value(list_view_text.column_record.text); text = it->get_value(list_view_text.column_record.text);
on_changed(index, text); on_changed(index, text);
} }
last_index=index; last_index = index;
} }
void SelectionDialogBase::add_row(const std::string& row) { void SelectionDialogBase::add_row(const std::string &row) {
list_view_text.append(row); list_view_text.append(row);
} }
@ -158,13 +158,13 @@ void SelectionDialogBase::show() {
window.show_all(); window.show_all();
if(text_view) if(text_view)
text_view->grab_focus(); text_view->grab_focus();
if(list_view_text.get_model()->children().size()>0) { if(list_view_text.get_model()->children().size() > 0) {
if(!list_view_text.get_selection()->get_selected()) { if(!list_view_text.get_selection()->get_selected()) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin())); list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
cursor_changed(); cursor_changed();
} }
else if(list_view_text.get_model()->children().begin()!=list_view_text.get_selection()->get_selected()) { else if(list_view_text.get_model()->children().begin() != list_view_text.get_selection()->get_selected()) {
while(Gtk::Main::events_pending()) while(Gtk::Main::events_pending())
Gtk::Main::iteration(false); Gtk::Main::iteration(false);
if(is_visible()) if(is_visible())
@ -176,9 +176,9 @@ void SelectionDialogBase::show() {
} }
void SelectionDialogBase::set_cursor_at_last_row() { void SelectionDialogBase::set_cursor_at_last_row() {
auto children=list_view_text.get_model()->children(); auto children = list_view_text.get_model()->children();
if(children.size()>0) { if(children.size() > 0) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(children[children.size()-1])); list_view_text.set_cursor(list_view_text.get_model()->get_path(children[children.size() - 1]));
cursor_changed(); cursor_changed();
} }
} }
@ -190,70 +190,71 @@ void SelectionDialogBase::hide() {
if(on_hide) if(on_hide)
on_hide(); on_hide();
list_view_text.clear(); list_view_text.clear();
last_index=static_cast<unsigned int>(-1); last_index = static_cast<unsigned int>(-1);
} }
std::unique_ptr<SelectionDialog> SelectionDialog::instance; std::unique_ptr<SelectionDialog> SelectionDialog::instance;
SelectionDialog::SelectionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup) : SelectionDialogBase(text_view, start_mark, show_search_entry, use_markup) { SelectionDialog::SelectionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup)
auto search_key=std::make_shared<std::string>(); : SelectionDialogBase(text_view, start_mark, show_search_entry, use_markup) {
auto filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model()); auto search_key = std::make_shared<std::string>();
auto filter_model = Gtk::TreeModelFilter::create(list_view_text.get_model());
filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator& iter){
filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator &iter) {
std::string row_lc; std::string row_lc;
iter->get_value(0, row_lc); iter->get_value(0, row_lc);
auto search_key_lc=*search_key; auto search_key_lc = *search_key;
std::transform(row_lc.begin(), row_lc.end(), row_lc.begin(), ::tolower); std::transform(row_lc.begin(), row_lc.end(), row_lc.begin(), ::tolower);
std::transform(search_key_lc.begin(), search_key_lc.end(), search_key_lc.begin(), ::tolower); std::transform(search_key_lc.begin(), search_key_lc.end(), search_key_lc.begin(), ::tolower);
if(list_view_text.use_markup) { if(list_view_text.use_markup) {
size_t pos=0; size_t pos = 0;
while((pos=row_lc.find('<', pos))!=std::string::npos) { while((pos = row_lc.find('<', pos)) != std::string::npos) {
auto pos2=row_lc.find('>', pos+1); auto pos2 = row_lc.find('>', pos + 1);
row_lc.erase(pos, pos2-pos+1); row_lc.erase(pos, pos2 - pos + 1);
} }
search_key_lc=Glib::Markup::escape_text(search_key_lc); search_key_lc = Glib::Markup::escape_text(search_key_lc);
} }
if(row_lc.find(search_key_lc)!=std::string::npos) if(row_lc.find(search_key_lc) != std::string::npos)
return true; return true;
return false; return false;
}); });
list_view_text.set_model(filter_model); list_view_text.set_model(filter_model);
list_view_text.set_search_equal_func([](const Glib::RefPtr<Gtk::TreeModel>& model, int column, const Glib::ustring& key, const Gtk::TreeModel::iterator& iter) { list_view_text.set_search_equal_func([](const Glib::RefPtr<Gtk::TreeModel> &model, int column, const Glib::ustring &key, const Gtk::TreeModel::iterator &iter) {
return false; return false;
}); });
search_entry.signal_changed().connect([this, search_key, filter_model](){ search_entry.signal_changed().connect([this, search_key, filter_model]() {
*search_key=search_entry.get_text(); *search_key = search_entry.get_text();
filter_model->refilter(); filter_model->refilter();
list_view_text.set_search_entry(search_entry); //TODO:Report the need of this to GTK's git (bug) list_view_text.set_search_entry(search_entry); //TODO:Report the need of this to GTK's git (bug)
if(search_key->empty()) { if(search_key->empty()) {
if(list_view_text.get_model()->children().size()>0) if(list_view_text.get_model()->children().size() > 0)
list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin())); list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
} }
}); });
auto activate=[this](){ auto activate = [this]() {
auto it=list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
if(on_select && it) { if(on_select && it) {
auto index=it->get_value(list_view_text.column_record.index); auto index = it->get_value(list_view_text.column_record.index);
auto text=it->get_value(list_view_text.column_record.text); auto text = it->get_value(list_view_text.column_record.text);
on_select(index, text, true); on_select(index, text, true);
} }
hide(); hide();
}; };
search_entry.signal_activate().connect([activate](){ search_entry.signal_activate().connect([activate]() {
activate(); activate();
}); });
list_view_text.signal_row_activated().connect([activate](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { list_view_text.signal_row_activated().connect([activate](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *) {
activate(); activate();
}); });
} }
bool SelectionDialog::on_key_press(GdkEventKey* key) { bool SelectionDialog::on_key_press(GdkEventKey *key) {
if((key->keyval==GDK_KEY_Down || key->keyval==GDK_KEY_KP_Down) && list_view_text.get_model()->children().size()>0) { if((key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_KP_Down) && list_view_text.get_model()->children().size() > 0) {
auto it=list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
if(it) { if(it) {
it++; it++;
if(it) if(it)
@ -261,8 +262,8 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) {
} }
return true; return true;
} }
else if((key->keyval==GDK_KEY_Up || key->keyval==GDK_KEY_KP_Up) && list_view_text.get_model()->children().size()>0) { else if((key->keyval == GDK_KEY_Up || key->keyval == GDK_KEY_KP_Up) && list_view_text.get_model()->children().size() > 0) {
auto it=list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
if(it) { if(it) {
it--; it--;
if(it) if(it)
@ -270,43 +271,43 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) {
} }
return true; return true;
} }
else if(key->keyval==GDK_KEY_Return || key-> keyval==GDK_KEY_KP_Enter || key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) { else if(key->keyval == GDK_KEY_Return || key->keyval == GDK_KEY_KP_Enter || key->keyval == GDK_KEY_ISO_Left_Tab || key->keyval == GDK_KEY_Tab) {
auto it=list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
auto column=list_view_text.get_column(0); auto column = list_view_text.get_column(0);
list_view_text.row_activated(list_view_text.get_model()->get_path(it), *column); list_view_text.row_activated(list_view_text.get_model()->get_path(it), *column);
return true; return true;
} }
else if(key->keyval==GDK_KEY_Escape) { else if(key->keyval == GDK_KEY_Escape) {
hide(); hide();
return true; return true;
} }
else if(key->keyval==GDK_KEY_Left || key->keyval==GDK_KEY_KP_Left || key->keyval==GDK_KEY_Right || key->keyval==GDK_KEY_KP_Right) { else if(key->keyval == GDK_KEY_Left || key->keyval == GDK_KEY_KP_Left || key->keyval == GDK_KEY_Right || key->keyval == GDK_KEY_KP_Right) {
hide(); hide();
return false; return false;
} }
else if(show_search_entry) { else if(show_search_entry) {
#ifdef __APPLE__ //OS X bug most likely: Gtk::Entry will not work if window is of type POPUP #ifdef __APPLE__ //OS X bug most likely: Gtk::Entry will not work if window is of type POPUP
int search_entry_length=search_entry.get_text_length(); int search_entry_length = search_entry.get_text_length();
if(key->keyval==GDK_KEY_dead_tilde) { if(key->keyval == GDK_KEY_dead_tilde) {
search_entry.insert_text("~", 1, search_entry_length); search_entry.insert_text("~", 1, search_entry_length);
return true; return true;
} }
else if(key->keyval==GDK_KEY_dead_circumflex) { else if(key->keyval == GDK_KEY_dead_circumflex) {
search_entry.insert_text("^", 1, search_entry_length); search_entry.insert_text("^", 1, search_entry_length);
return true; return true;
} }
else if(key->is_modifier) else if(key->is_modifier)
return true; return true;
else if(key->keyval==GDK_KEY_BackSpace) { else if(key->keyval == GDK_KEY_BackSpace) {
auto length=search_entry.get_text_length(); auto length = search_entry.get_text_length();
if(length>0) if(length > 0)
search_entry.delete_text(length-1, length); search_entry.delete_text(length - 1, length);
return true; return true;
} }
else { else {
gunichar unicode=gdk_keyval_to_unicode(key->keyval); gunichar unicode = gdk_keyval_to_unicode(key->keyval);
if(unicode>=32 && unicode!=126) { if(unicode >= 32 && unicode != 126) {
auto ustr=Glib::ustring(1, unicode); auto ustr = Glib::ustring(1, unicode);
search_entry.insert_text(ustr, ustr.bytes(), search_entry_length); search_entry.insert_text(ustr, ustr.bytes(), search_entry_length);
return true; return true;
} }
@ -323,75 +324,75 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) {
std::unique_ptr<CompletionDialog> CompletionDialog::instance; std::unique_ptr<CompletionDialog> CompletionDialog::instance;
CompletionDialog::CompletionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark) : SelectionDialogBase(text_view, start_mark, false, false) { CompletionDialog::CompletionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark) : SelectionDialogBase(text_view, start_mark, false, false) {
show_offset=text_view->get_buffer()->get_insert()->get_iter().get_offset(); show_offset = text_view->get_buffer()->get_insert()->get_iter().get_offset();
auto search_key=std::make_shared<std::string>(); auto search_key = std::make_shared<std::string>();
auto filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model()); auto filter_model = Gtk::TreeModelFilter::create(list_view_text.get_model());
if(show_offset==start_mark->get_iter().get_offset()) { if(show_offset == start_mark->get_iter().get_offset()) {
filter_model->set_visible_func([search_key](const Gtk::TreeModel::const_iterator& iter){ filter_model->set_visible_func([search_key](const Gtk::TreeModel::const_iterator &iter) {
std::string row_lc; std::string row_lc;
iter->get_value(0, row_lc); iter->get_value(0, row_lc);
auto search_key_lc=*search_key; auto search_key_lc = *search_key;
std::transform(row_lc.begin(), row_lc.end(), row_lc.begin(), ::tolower); std::transform(row_lc.begin(), row_lc.end(), row_lc.begin(), ::tolower);
std::transform(search_key_lc.begin(), search_key_lc.end(), search_key_lc.begin(), ::tolower); std::transform(search_key_lc.begin(), search_key_lc.end(), search_key_lc.begin(), ::tolower);
if(row_lc.find(search_key_lc)!=std::string::npos) if(row_lc.find(search_key_lc) != std::string::npos)
return true; return true;
return false; return false;
}); });
} }
else { else {
filter_model->set_visible_func([search_key](const Gtk::TreeModel::const_iterator& iter){ filter_model->set_visible_func([search_key](const Gtk::TreeModel::const_iterator &iter) {
std::string row; std::string row;
iter->get_value(0, row); iter->get_value(0, row);
if(row.find(*search_key)==0) if(row.find(*search_key) == 0)
return true; return true;
return false; return false;
}); });
} }
list_view_text.set_model(filter_model); list_view_text.set_model(filter_model);
search_entry.signal_changed().connect([this, search_key, filter_model](){ search_entry.signal_changed().connect([this, search_key, filter_model]() {
*search_key=search_entry.get_text(); *search_key = search_entry.get_text();
filter_model->refilter(); filter_model->refilter();
list_view_text.set_search_entry(search_entry); //TODO:Report the need of this to GTK's git (bug) list_view_text.set_search_entry(search_entry); //TODO:Report the need of this to GTK's git (bug)
}); });
list_view_text.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { list_view_text.signal_row_activated().connect([this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *) {
select(); select();
}); });
auto text=text_view->get_buffer()->get_text(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter()); auto text = text_view->get_buffer()->get_text(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter());
if(text.size()>0) { if(text.size() > 0) {
search_entry.set_text(text); search_entry.set_text(text);
list_view_text.set_search_entry(search_entry); list_view_text.set_search_entry(search_entry);
} }
} }
void CompletionDialog::select(bool hide_window) { void CompletionDialog::select(bool hide_window) {
row_in_entry=true; row_in_entry = true;
auto it=list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
if(on_select && it) { if(on_select && it) {
auto index=it->get_value(list_view_text.column_record.index); auto index = it->get_value(list_view_text.column_record.index);
auto text=it->get_value(list_view_text.column_record.text); auto text = it->get_value(list_view_text.column_record.text);
on_select(index, text, hide_window); on_select(index, text, hide_window);
} }
if(hide_window) if(hide_window)
hide(); hide();
} }
bool CompletionDialog::on_key_release(GdkEventKey* key) { bool CompletionDialog::on_key_release(GdkEventKey *key) {
if(key->keyval==GDK_KEY_Down || key->keyval==GDK_KEY_KP_Down || key->keyval==GDK_KEY_Up || key->keyval==GDK_KEY_KP_Up) if(key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_KP_Down || key->keyval == GDK_KEY_Up || key->keyval == GDK_KEY_KP_Up)
return false; return false;
if(show_offset>text_view->get_buffer()->get_insert()->get_iter().get_offset()) { if(show_offset > text_view->get_buffer()->get_insert()->get_iter().get_offset()) {
hide(); hide();
} }
else { else {
auto text=text_view->get_buffer()->get_text(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter()); auto text = text_view->get_buffer()->get_text(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter());
search_entry.set_text(text); search_entry.set_text(text);
list_view_text.set_search_entry(search_entry); list_view_text.set_search_entry(search_entry);
if(text=="") { if(text == "") {
if(list_view_text.get_model()->children().size()>0) if(list_view_text.get_model()->children().size() > 0)
list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin())); list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
} }
cursor_changed(); cursor_changed();
@ -399,23 +400,24 @@ bool CompletionDialog::on_key_release(GdkEventKey* key) {
return false; return false;
} }
bool CompletionDialog::on_key_press(GdkEventKey* key) { bool CompletionDialog::on_key_press(GdkEventKey *key) {
if((key->keyval>=GDK_KEY_0 && key->keyval<=GDK_KEY_9) || if((key->keyval >= GDK_KEY_0 && key->keyval <= GDK_KEY_9) ||
(key->keyval>=GDK_KEY_A && key->keyval<=GDK_KEY_Z) || (key->keyval >= GDK_KEY_A && key->keyval <= GDK_KEY_Z) ||
(key->keyval>=GDK_KEY_a && key->keyval<=GDK_KEY_z) || (key->keyval >= GDK_KEY_a && key->keyval <= GDK_KEY_z) ||
key->keyval==GDK_KEY_underscore || key->keyval==GDK_KEY_BackSpace) { key->keyval == GDK_KEY_underscore || key->keyval == GDK_KEY_BackSpace) {
if(row_in_entry) { if(row_in_entry) {
text_view->get_buffer()->erase(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter()); text_view->get_buffer()->erase(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter());
row_in_entry=false; row_in_entry = false;
if(key->keyval==GDK_KEY_BackSpace) if(key->keyval == GDK_KEY_BackSpace)
return true; return true;
} }
return false; return false;
} }
if(key->keyval==GDK_KEY_Shift_L || key->keyval==GDK_KEY_Shift_R || key->keyval==GDK_KEY_Alt_L || key->keyval==GDK_KEY_Alt_R || key->keyval==GDK_KEY_Control_L || key->keyval==GDK_KEY_Control_R || key->keyval==GDK_KEY_Meta_L || key->keyval==GDK_KEY_Meta_R) if(key->keyval == GDK_KEY_Shift_L || key->keyval == GDK_KEY_Shift_R || key->keyval == GDK_KEY_Alt_L || key->keyval == GDK_KEY_Alt_R ||
key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R || key->keyval == GDK_KEY_Meta_L || key->keyval == GDK_KEY_Meta_R)
return false; return false;
if((key->keyval==GDK_KEY_Down || key->keyval==GDK_KEY_KP_Down) && list_view_text.get_model()->children().size()>0) { if((key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_KP_Down) && list_view_text.get_model()->children().size() > 0) {
auto it=list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
if(it) { if(it) {
it++; it++;
if(it) { if(it) {
@ -428,8 +430,8 @@ bool CompletionDialog::on_key_press(GdkEventKey* key) {
select(false); select(false);
return true; return true;
} }
if((key->keyval==GDK_KEY_Up || key->keyval==GDK_KEY_KP_Up) && list_view_text.get_model()->children().size()>0) { if((key->keyval == GDK_KEY_Up || key->keyval == GDK_KEY_KP_Up) && list_view_text.get_model()->children().size() > 0) {
auto it=list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
if(it) { if(it) {
it--; it--;
if(it) { if(it) {
@ -438,7 +440,7 @@ bool CompletionDialog::on_key_press(GdkEventKey* key) {
} }
} }
else { else {
auto last_it=list_view_text.get_model()->children().end(); auto last_it = list_view_text.get_model()->children().end();
last_it--; last_it--;
if(last_it) if(last_it)
list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it)); list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it));
@ -446,12 +448,12 @@ bool CompletionDialog::on_key_press(GdkEventKey* key) {
select(false); select(false);
return true; return true;
} }
if(key->keyval==GDK_KEY_Return || key->keyval==GDK_KEY_KP_Enter || key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) { if(key->keyval == GDK_KEY_Return || key->keyval == GDK_KEY_KP_Enter || key->keyval == GDK_KEY_ISO_Left_Tab || key->keyval == GDK_KEY_Tab) {
select(); select();
return true; return true;
} }
hide(); hide();
if(key->keyval==GDK_KEY_Escape) if(key->keyval == GDK_KEY_Escape)
return true; return true;
return false; return false;
} }

63
src/selection_dialog.h

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "gtkmm.h" #include "gtkmm.h"
#include <unordered_map>
#include <functional> #include <functional>
#include <unordered_map>
class SelectionDialogBase { class SelectionDialogBase {
class ListViewText : public Gtk::TreeView { class ListViewText : public Gtk::TreeView {
@ -14,47 +14,49 @@ class SelectionDialogBase {
Gtk::TreeModelColumn<std::string> text; Gtk::TreeModelColumn<std::string> text;
Gtk::TreeModelColumn<unsigned int> index; Gtk::TreeModelColumn<unsigned int> index;
}; };
public: public:
bool use_markup; bool use_markup;
ColumnRecord column_record; ColumnRecord column_record;
ListViewText(bool use_markup); ListViewText(bool use_markup);
void append(const std::string& value); void append(const std::string &value);
void erase_rows(); void erase_rows();
void clear(); void clear();
private: private:
Glib::RefPtr<Gtk::ListStore> list_store; Glib::RefPtr<Gtk::ListStore> list_store;
Gtk::CellRendererText cell_renderer; Gtk::CellRendererText cell_renderer;
unsigned int size=0; unsigned int size = 0;
}; };
class SearchEntry : public Gtk::Entry { class SearchEntry : public Gtk::Entry {
public: public:
SearchEntry() : Gtk::Entry() {} SearchEntry() : Gtk::Entry() {}
bool on_key_press_event(GdkEventKey *event) override { return Gtk::Entry::on_key_press_event(event); }; bool on_key_press_event(GdkEventKey *event) override { return Gtk::Entry::on_key_press_event(event); };
}; };
public: public:
SelectionDialogBase(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup); SelectionDialogBase(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup);
virtual ~SelectionDialogBase(); virtual ~SelectionDialogBase();
void add_row(const std::string& row); void add_row(const std::string &row);
void erase_rows(); void erase_rows();
void set_cursor_at_last_row(); void set_cursor_at_last_row();
void show(); void show();
void hide(); void hide();
bool is_visible() {return window.is_visible();} bool is_visible() { return window.is_visible(); }
void get_position(int &root_x, int &root_y) {window.get_position(root_x, root_y);} void get_position(int &root_x, int &root_y) { window.get_position(root_x, root_y); }
std::function<void()> on_show; std::function<void()> on_show;
std::function<void()> on_hide; std::function<void()> on_hide;
std::function<void(unsigned int index, const std::string &text, bool hide_window)> on_select; std::function<void(unsigned int index, const std::string &text, bool hide_window)> on_select;
std::function<void(unsigned int index, const std::string &text)> on_changed; std::function<void(unsigned int index, const std::string &text)> on_changed;
std::function<void(const std::string &text)> on_search_entry_changed; std::function<void(const std::string &text)> on_search_entry_changed;
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark; Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark;
protected: protected:
void cursor_changed(); void cursor_changed();
Gtk::TextView *text_view; Gtk::TextView *text_view;
Gtk::Window window; Gtk::Window window;
Gtk::Box vbox; Gtk::Box vbox;
@ -62,39 +64,42 @@ protected:
ListViewText list_view_text; ListViewText list_view_text;
SearchEntry search_entry; SearchEntry search_entry;
bool show_search_entry; bool show_search_entry;
unsigned int last_index=static_cast<unsigned int>(-1); unsigned int last_index = static_cast<unsigned int>(-1);
}; };
class SelectionDialog : public SelectionDialogBase { class SelectionDialog : public SelectionDialogBase {
SelectionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup); SelectionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup);
static std::unique_ptr<SelectionDialog> instance; static std::unique_ptr<SelectionDialog> instance;
public: public:
bool on_key_press(GdkEventKey* key); bool on_key_press(GdkEventKey *key);
static void create(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry=true, bool use_markup=false) { static void create(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry = true, bool use_markup = false) {
instance=std::unique_ptr<SelectionDialog>(new SelectionDialog(text_view, start_mark, show_search_entry, use_markup)); instance = std::unique_ptr<SelectionDialog>(new SelectionDialog(text_view, start_mark, show_search_entry, use_markup));
} }
static void create(bool show_search_entry=true, bool use_markup=false) { static void create(bool show_search_entry = true, bool use_markup = false) {
instance=std::unique_ptr<SelectionDialog>(new SelectionDialog(nullptr, Glib::RefPtr<Gtk::TextBuffer::Mark>(), show_search_entry, use_markup)); instance = std::unique_ptr<SelectionDialog>(new SelectionDialog(nullptr, Glib::RefPtr<Gtk::TextBuffer::Mark>(), show_search_entry, use_markup));
} }
static std::unique_ptr<SelectionDialog> &get() {return instance;} static std::unique_ptr<SelectionDialog> &get() { return instance; }
}; };
class CompletionDialog : public SelectionDialogBase { class CompletionDialog : public SelectionDialogBase {
CompletionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark); CompletionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark);
static std::unique_ptr<CompletionDialog> instance; static std::unique_ptr<CompletionDialog> instance;
public: public:
bool on_key_release(GdkEventKey* key); bool on_key_release(GdkEventKey *key);
bool on_key_press(GdkEventKey* key); bool on_key_press(GdkEventKey *key);
static void create(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark) { static void create(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark) {
instance=std::unique_ptr<CompletionDialog>(new CompletionDialog(text_view, start_mark)); instance = std::unique_ptr<CompletionDialog>(new CompletionDialog(text_view, start_mark));
} }
static std::unique_ptr<CompletionDialog> &get() {return instance;} static std::unique_ptr<CompletionDialog> &get() { return instance; }
private: private:
void select(bool hide_window=true); void select(bool hide_window = true);
int show_offset; int show_offset;
bool row_in_entry=false; bool row_in_entry = false;
}; };

2683
src/source.cc

File diff suppressed because it is too large Load Diff

131
src/source.h

@ -1,14 +1,14 @@
#pragma once #pragma once
#include "source_spellcheck.h"
#include "source_diff.h" #include "source_diff.h"
#include "source_spellcheck.h"
#include "tooltips.h" #include "tooltips.h"
#include <boost/property_tree/xml_parser.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <set>
#include <string> #include <string>
#include <tuple>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <set>
#include <tuple>
namespace Source { namespace Source {
/// Workaround for buggy Gsv::LanguageManager::get_default() /// Workaround for buggy Gsv::LanguageManager::get_default()
@ -21,29 +21,29 @@ namespace Source {
public: public:
static Glib::RefPtr<Gsv::StyleSchemeManager> get_default(); static Glib::RefPtr<Gsv::StyleSchemeManager> get_default();
}; };
Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path); Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path);
class Offset { class Offset {
public: public:
Offset() = default; Offset() = default;
Offset(unsigned line, unsigned index, boost::filesystem::path file_path_=""): line(line), index(index), file_path(std::move(file_path_)) {} Offset(unsigned line, unsigned index, boost::filesystem::path file_path_ = "") : line(line), index(index), file_path(std::move(file_path_)) {}
operator bool() { return !file_path.empty(); } operator bool() { return !file_path.empty(); }
bool operator==(const Offset &o) {return (line==o.line && index==o.index);} bool operator==(const Offset &o) { return (line == o.line && index == o.index); }
unsigned line; unsigned line;
unsigned index; unsigned index;
boost::filesystem::path file_path; boost::filesystem::path file_path;
}; };
class FixIt { class FixIt {
public: public:
enum class Type {INSERT, REPLACE, ERASE}; enum class Type { INSERT, REPLACE, ERASE };
FixIt(std::string source_, std::pair<Offset, Offset> offsets_); FixIt(std::string source_, std::pair<Offset, Offset> offsets_);
std::string string(const Glib::RefPtr<Gtk::TextBuffer> &buffer); std::string string(const Glib::RefPtr<Gtk::TextBuffer> &buffer);
Type type; Type type;
std::string source; std::string source;
std::pair<Offset, Offset> offsets; std::pair<Offset, Offset> offsets;
@ -51,16 +51,16 @@ namespace Source {
class View : public SpellCheckView, public DiffView { class View : public SpellCheckView, public DiffView {
public: public:
static std::set<View*> non_deleted_views; static std::set<View *> non_deleted_views;
static std::set<View*> views; static std::set<View *> views;
View(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language, bool is_generic_view=false); View(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language, bool is_generic_view = false);
~View() override; ~View() override;
bool save() override; bool save() override;
void configure() override; void configure() override;
void search_highlight(const std::string &text, bool case_sensitive, bool regex); void search_highlight(const std::string &text, bool case_sensitive, bool regex);
std::function<void(int number)> update_search_occurrences; std::function<void(int number)> update_search_occurrences;
void search_forward(); void search_forward();
@ -68,18 +68,18 @@ namespace Source {
void replace_forward(const std::string &replacement); void replace_forward(const std::string &replacement);
void replace_backward(const std::string &replacement); void replace_backward(const std::string &replacement);
void replace_all(const std::string &replacement); void replace_all(const std::string &replacement);
void paste(); void paste();
std::function<void()> non_interactive_completion; std::function<void()> non_interactive_completion;
std::function<void(bool)> format_style; std::function<void(bool)> format_style;
std::function<Offset()> get_declaration_location; std::function<Offset()> get_declaration_location;
std::function<Offset()> get_type_declaration_location; std::function<Offset()> get_type_declaration_location;
std::function<std::vector<Offset>()> get_implementation_locations; std::function<std::vector<Offset>()> get_implementation_locations;
std::function<std::vector<Offset>()> get_declaration_or_implementation_locations; std::function<std::vector<Offset>()> get_declaration_or_implementation_locations;
std::function<std::vector<std::pair<Offset, std::string> >()> get_usages; std::function<std::vector<std::pair<Offset, std::string>>()> get_usages;
std::function<std::string()> get_method; std::function<std::string()> get_method;
std::function<std::vector<std::pair<Offset, std::string> >()> get_methods; std::function<std::vector<std::pair<Offset, std::string>>()> get_methods;
std::function<std::vector<std::string>()> get_token_data; std::function<std::vector<std::string>()> get_token_data;
std::function<std::string()> get_token_spelling; std::function<std::string()> get_token_spelling;
std::function<void(const std::string &text)> rename_similar_tokens; std::function<void(const std::string &text)> rename_similar_tokens;
@ -88,17 +88,18 @@ namespace Source {
std::function<void()> toggle_comments; std::function<void()> toggle_comments;
std::function<std::tuple<Source::Offset, std::string, size_t>()> get_documentation_template; std::function<std::tuple<Source::Offset, std::string, size_t>()> get_documentation_template;
std::function<void(int)> toggle_breakpoint; std::function<void(int)> toggle_breakpoint;
void hide_tooltips() override; void hide_tooltips() override;
void hide_dialogs() override; void hide_dialogs() override;
void set_tab_char_and_size(char tab_char, unsigned tab_size); void set_tab_char_and_size(char tab_char, unsigned tab_size);
std::pair<char, unsigned> get_tab_char_and_size() {return {tab_char, tab_size};} std::pair<char, unsigned> get_tab_char_and_size() { return {tab_char, tab_size}; }
bool soft_reparse_needed=false; bool soft_reparse_needed = false;
bool full_reparse_needed=false; bool full_reparse_needed = false;
virtual void soft_reparse(bool delayed=false) {soft_reparse_needed=false;} virtual void soft_reparse(bool delayed = false) { soft_reparse_needed = false; }
virtual void full_reparse() {full_reparse_needed=false;} virtual void full_reparse() { full_reparse_needed = false; }
protected: protected:
std::atomic<bool> parsed; std::atomic<bool> parsed;
Tooltips diagnostic_tooltips; Tooltips diagnostic_tooltips;
@ -110,9 +111,9 @@ namespace Source {
void add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, std::string spelling, bool error); void add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, std::string spelling, bool error);
void clear_diagnostic_tooltips(); void clear_diagnostic_tooltips();
virtual void show_type_tooltips(const Gdk::Rectangle &rectangle) {} virtual void show_type_tooltips(const Gdk::Rectangle &rectangle) {}
gdouble on_motion_last_x=0.0; gdouble on_motion_last_x = 0.0;
gdouble on_motion_last_y=0.0; gdouble on_motion_last_y = 0.0;
Gtk::TextIter find_non_whitespace_code_iter_backward(Gtk::TextIter iter); Gtk::TextIter find_non_whitespace_code_iter_backward(Gtk::TextIter iter);
/// If closing bracket is found, continues until the open bracket. /// If closing bracket is found, continues until the open bracket.
/// Returns if open bracket is found that has no corresponding closing bracket. /// Returns if open bracket is found that has no corresponding closing bracket.
@ -122,61 +123,63 @@ namespace Source {
bool find_close_symbol_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter, unsigned int positive_char, unsigned int negative_char); bool find_close_symbol_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter, unsigned int positive_char, unsigned int negative_char);
long symbol_count(Gtk::TextIter iter, unsigned int positive_char, unsigned int negative_char); long symbol_count(Gtk::TextIter iter, unsigned int positive_char, unsigned int negative_char);
bool is_templated_function(Gtk::TextIter iter, Gtk::TextIter &parenthesis_end_iter); bool is_templated_function(Gtk::TextIter iter, Gtk::TextIter &parenthesis_end_iter);
std::string get_token(Gtk::TextIter iter); std::string get_token(Gtk::TextIter iter);
void cleanup_whitespace_characters_on_return(const Gtk::TextIter &iter); void cleanup_whitespace_characters_on_return(const Gtk::TextIter &iter);
bool on_key_press_event(GdkEventKey* key) override; bool on_key_press_event(GdkEventKey *key) override;
bool on_key_press_event_basic(GdkEventKey* key); bool on_key_press_event_basic(GdkEventKey *key);
bool on_key_press_event_bracket_language(GdkEventKey* key); bool on_key_press_event_bracket_language(GdkEventKey *key);
bool on_key_press_event_smart_brackets(GdkEventKey* key); bool on_key_press_event_smart_brackets(GdkEventKey *key);
bool on_key_press_event_smart_inserts(GdkEventKey* key); bool on_key_press_event_smart_inserts(GdkEventKey *key);
bool on_button_press_event(GdkEventButton *event) override; bool on_button_press_event(GdkEventButton *event) override;
bool on_motion_notify_event(GdkEventMotion *motion_event) override; bool on_motion_notify_event(GdkEventMotion *motion_event) override;
std::pair<char, unsigned> find_tab_char_and_size(); std::pair<char, unsigned> find_tab_char_and_size();
unsigned tab_size; unsigned tab_size;
char tab_char; char tab_char;
std::string tab; std::string tab;
bool interactive_completion=true; bool interactive_completion = true;
private: private:
void setup_tooltip_and_dialog_events(); void setup_tooltip_and_dialog_events();
void setup_format_style(bool is_generic_view); void setup_format_style(bool is_generic_view);
void cleanup_whitespace_characters(); void cleanup_whitespace_characters();
Gsv::DrawSpacesFlags parse_show_whitespace_characters(const std::string &text); Gsv::DrawSpacesFlags parse_show_whitespace_characters(const std::string &text);
GtkSourceSearchContext *search_context; GtkSourceSearchContext *search_context;
GtkSourceSearchSettings *search_settings; GtkSourceSearchSettings *search_settings;
static void search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data); static void search_occurrences_updated(GtkWidget *widget, GParamSpec *property, gpointer data);
sigc::connection renderer_activate_connection; sigc::connection renderer_activate_connection;
bool is_bracket_language=false; bool is_bracket_language = false;
bool use_fixed_continuation_indenting=true; bool use_fixed_continuation_indenting = true;
bool is_cpp=false; bool is_cpp = false;
guint previous_non_modifier_keyval=0; guint previous_non_modifier_keyval = 0;
bool multiple_cursors_signals_set=false; bool multiple_cursors_signals_set = false;
bool multiple_cursors_use=false; bool multiple_cursors_use = false;
std::vector<std::pair<Glib::RefPtr<Gtk::TextBuffer::Mark>, int>> multiple_cursors_extra_cursors; std::vector<std::pair<Glib::RefPtr<Gtk::TextBuffer::Mark>, int>> multiple_cursors_extra_cursors;
Glib::RefPtr<Gtk::TextBuffer::Mark> multiple_cursors_last_insert; Glib::RefPtr<Gtk::TextBuffer::Mark> multiple_cursors_last_insert;
int multiple_cursors_erase_backward_length; int multiple_cursors_erase_backward_length;
int multiple_cursors_erase_forward_length; int multiple_cursors_erase_forward_length;
bool on_key_press_event_multiple_cursors(GdkEventKey* key); bool on_key_press_event_multiple_cursors(GdkEventKey *key);
}; };
class GenericView : public View { class GenericView : public View {
private: private:
class CompletionBuffer : public Gtk::TextBuffer { class CompletionBuffer : public Gtk::TextBuffer {
public: public:
static Glib::RefPtr<CompletionBuffer> create() {return Glib::RefPtr<CompletionBuffer>(new CompletionBuffer());} static Glib::RefPtr<CompletionBuffer> create() { return Glib::RefPtr<CompletionBuffer>(new CompletionBuffer()); }
}; };
public: public:
GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language); GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
void parse_language_file(Glib::RefPtr<CompletionBuffer> &completion_buffer, bool &has_context_class, const boost::property_tree::ptree &pt); void parse_language_file(Glib::RefPtr<CompletionBuffer> &completion_buffer, bool &has_context_class, const boost::property_tree::ptree &pt);
}; };
} } // namespace Source

248
src/source_base.cc

@ -1,20 +1,20 @@
#include "source_base.h" #include "source_base.h"
#include "config.h"
#include "git.h"
#include "info.h" #include "info.h"
#include "terminal.h" #include "terminal.h"
#include "git.h"
#include "config.h"
#include <fstream> #include <fstream>
Source::BaseView::BaseView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language): Gsv::View(), file_path(file_path), language(language), status_diagnostics(0, 0, 0) { Source::BaseView::BaseView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language) : Gsv::View(), file_path(file_path), language(language), status_diagnostics(0, 0, 0) {
load(true); load(true);
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(0)); get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(0));
signal_focus_in_event().connect([this](GdkEventFocus *event) { signal_focus_in_event().connect([this](GdkEventFocus *event) {
if(this->last_write_time!=static_cast<std::time_t>(-1)) if(this->last_write_time != static_cast<std::time_t>(-1))
check_last_write_time(); check_last_write_time();
return false; return false;
}); });
monitor_file(); monitor_file();
} }
@ -25,14 +25,14 @@ Source::BaseView::~BaseView() {
bool Source::BaseView::load(bool not_undoable_action) { bool Source::BaseView::load(bool not_undoable_action) {
boost::system::error_code ec; boost::system::error_code ec;
last_write_time=boost::filesystem::last_write_time(file_path, ec); last_write_time = boost::filesystem::last_write_time(file_path, ec);
if(ec) if(ec)
last_write_time=static_cast<std::time_t>(-1); last_write_time = static_cast<std::time_t>(-1);
disable_spellcheck=true; disable_spellcheck = true;
if(not_undoable_action) if(not_undoable_action)
get_source_buffer()->begin_not_undoable_action(); get_source_buffer()->begin_not_undoable_action();
class Guard { class Guard {
public: public:
Source::BaseView *view; Source::BaseView *view;
@ -40,31 +40,31 @@ bool Source::BaseView::load(bool not_undoable_action) {
~Guard() { ~Guard() {
if(not_undoable_action) if(not_undoable_action)
view->get_source_buffer()->end_not_undoable_action(); view->get_source_buffer()->end_not_undoable_action();
view->disable_spellcheck=false; view->disable_spellcheck = false;
} }
}; };
Guard guard{this, not_undoable_action}; Guard guard{this, not_undoable_action};
if(language) { if(language) {
std::ifstream input(file_path.string(), std::ofstream::binary); std::ifstream input(file_path.string(), std::ofstream::binary);
if(input) { if(input) {
std::stringstream ss; std::stringstream ss;
ss << input.rdbuf(); ss << input.rdbuf();
Glib::ustring ustr=ss.str(); Glib::ustring ustr = ss.str();
bool valid=true; bool valid = true;
Glib::ustring::iterator iter; Glib::ustring::iterator iter;
while(!ustr.validate(iter)) { while(!ustr.validate(iter)) {
auto next_char_iter=iter; auto next_char_iter = iter;
next_char_iter++; next_char_iter++;
ustr.replace(iter, next_char_iter, "?"); ustr.replace(iter, next_char_iter, "?");
valid=false; valid = false;
} }
if(!valid) if(!valid)
Terminal::get().print("Warning: "+file_path.string()+" is not a valid UTF-8 file. Saving might corrupt the file.\n"); Terminal::get().print("Warning: " + file_path.string() + " is not a valid UTF-8 file. Saving might corrupt the file.\n");
if(get_buffer()->size()==0) if(get_buffer()->size() == 0)
get_buffer()->insert_at_cursor(ustr); get_buffer()->insert_at_cursor(ustr);
else else
replace_text(ustr.raw()); replace_text(ustr.raw());
@ -77,31 +77,31 @@ bool Source::BaseView::load(bool not_undoable_action) {
if(input) { if(input) {
std::stringstream ss; std::stringstream ss;
ss << input.rdbuf(); ss << input.rdbuf();
Glib::ustring ustr=ss.str(); Glib::ustring ustr = ss.str();
if(ustr.validate()) { if(ustr.validate()) {
if(get_buffer()->size()==0) if(get_buffer()->size() == 0)
get_buffer()->insert_at_cursor(ustr); get_buffer()->insert_at_cursor(ustr);
else else
replace_text(ustr.raw()); replace_text(ustr.raw());
} }
else { else {
Terminal::get().print("Error: "+file_path.string()+" is not a valid UTF-8 file.\n", true); Terminal::get().print("Error: " + file_path.string() + " is not a valid UTF-8 file.\n", true);
return false; return false;
} }
} }
else else
return false; return false;
} }
get_buffer()->set_modified(false); get_buffer()->set_modified(false);
return true; return true;
} }
void Source::BaseView::replace_text(const std::string &new_text) { void Source::BaseView::replace_text(const std::string &new_text) {
get_buffer()->begin_user_action(); get_buffer()->begin_user_action();
if(get_buffer()->size()==0) { if(get_buffer()->size() == 0) {
get_buffer()->insert_at_cursor(new_text); get_buffer()->insert_at_cursor(new_text);
get_buffer()->end_user_action(); get_buffer()->end_user_action();
return; return;
@ -111,48 +111,48 @@ void Source::BaseView::replace_text(const std::string &new_text) {
get_buffer()->end_user_action(); get_buffer()->end_user_action();
return; return;
} }
auto iter=get_buffer()->get_insert()->get_iter(); auto iter = get_buffer()->get_insert()->get_iter();
int cursor_line_nr=iter.get_line(); int cursor_line_nr = iter.get_line();
int cursor_line_offset=iter.ends_line() ? std::numeric_limits<int>::max() : iter.get_line_offset(); int cursor_line_offset = iter.ends_line() ? std::numeric_limits<int>::max() : iter.get_line_offset();
std::vector<std::pair<const char*, const char*>> new_lines; std::vector<std::pair<const char *, const char *>> new_lines;
const char* line_start=new_text.c_str(); const char *line_start = new_text.c_str();
for(const char &chr: new_text) { for(const char &chr : new_text) {
if(chr=='\n') { if(chr == '\n') {
new_lines.emplace_back(line_start, &chr+1); new_lines.emplace_back(line_start, &chr + 1);
line_start = &chr+1; line_start = &chr + 1;
} }
} }
if(new_text.empty() || new_text.back()!='\n') if(new_text.empty() || new_text.back() != '\n')
new_lines.emplace_back(line_start, &new_text[new_text.size()]); new_lines.emplace_back(line_start, &new_text[new_text.size()]);
try { try {
auto hunks = Git::Repository::Diff::get_hunks(get_buffer()->get_text().raw(), new_text); auto hunks = Git::Repository::Diff::get_hunks(get_buffer()->get_text().raw(), new_text);
for(auto it=hunks.rbegin();it!=hunks.rend();++it) { for(auto it = hunks.rbegin(); it != hunks.rend(); ++it) {
bool place_cursor=false; bool place_cursor = false;
Gtk::TextIter start; Gtk::TextIter start;
if(it->old_lines.second!=0) { if(it->old_lines.second != 0) {
start=get_buffer()->get_iter_at_line(it->old_lines.first-1); start = get_buffer()->get_iter_at_line(it->old_lines.first - 1);
auto end=get_buffer()->get_iter_at_line(it->old_lines.first-1+it->old_lines.second); auto end = get_buffer()->get_iter_at_line(it->old_lines.first - 1 + it->old_lines.second);
if(cursor_line_nr>=start.get_line() && cursor_line_nr<end.get_line()) { if(cursor_line_nr >= start.get_line() && cursor_line_nr < end.get_line()) {
if(it->new_lines.second!=0) { if(it->new_lines.second != 0) {
place_cursor = true; place_cursor = true;
int line_diff=cursor_line_nr-start.get_line(); int line_diff = cursor_line_nr - start.get_line();
cursor_line_nr+=static_cast<int>(0.5+(static_cast<float>(line_diff)/it->old_lines.second)*it->new_lines.second)-line_diff; cursor_line_nr += static_cast<int>(0.5 + (static_cast<float>(line_diff) / it->old_lines.second) * it->new_lines.second) - line_diff;
} }
} }
get_buffer()->erase(start, end); get_buffer()->erase(start, end);
start=get_buffer()->get_iter_at_line(it->old_lines.first-1); start = get_buffer()->get_iter_at_line(it->old_lines.first - 1);
} }
else else
start=get_buffer()->get_iter_at_line(it->old_lines.first); start = get_buffer()->get_iter_at_line(it->old_lines.first);
if(it->new_lines.second!=0) { if(it->new_lines.second != 0) {
get_buffer()->insert(start, new_lines[it->new_lines.first-1].first, new_lines[it->new_lines.first-1+it->new_lines.second-1].second); get_buffer()->insert(start, new_lines[it->new_lines.first - 1].first, new_lines[it->new_lines.first - 1 + it->new_lines.second - 1].second);
if(place_cursor) if(place_cursor)
place_cursor_at_line_offset(cursor_line_nr, cursor_line_offset); place_cursor_at_line_offset(cursor_line_nr, cursor_line_offset);
} }
@ -161,13 +161,13 @@ void Source::BaseView::replace_text(const std::string &new_text) {
catch(...) { catch(...) {
Terminal::get().print("Error: Could not replace text in buffer\n", true); Terminal::get().print("Error: Could not replace text in buffer\n", true);
} }
get_buffer()->end_user_action(); get_buffer()->end_user_action();
} }
void Source::BaseView::rename(const boost::filesystem::path &path) { void Source::BaseView::rename(const boost::filesystem::path &path) {
file_path=path; file_path = path;
if(update_status_file_path) if(update_status_file_path)
update_status_file_path(this); update_status_file_path(this);
if(update_tab_label) if(update_tab_label)
@ -180,10 +180,10 @@ void Source::BaseView::monitor_file() {
public: public:
static void f(BaseView *view, std::time_t last_write_time_) { static void f(BaseView *view, std::time_t last_write_time_) {
view->delayed_monitor_changed_connection.disconnect(); view->delayed_monitor_changed_connection.disconnect();
view->delayed_monitor_changed_connection=Glib::signal_timeout().connect([view, last_write_time_]() { view->delayed_monitor_changed_connection = Glib::signal_timeout().connect([view, last_write_time_]() {
boost::system::error_code ec; boost::system::error_code ec;
auto last_write_time=boost::filesystem::last_write_time(view->file_path, ec); auto last_write_time = boost::filesystem::last_write_time(view->file_path, ec);
if(last_write_time!=last_write_time_) if(last_write_time != last_write_time_)
view->check_last_write_time(last_write_time); view->check_last_write_time(last_write_time);
Recursive::f(view, last_write_time); Recursive::f(view, last_write_time);
return false; return false;
@ -191,18 +191,18 @@ void Source::BaseView::monitor_file() {
} }
}; };
delayed_monitor_changed_connection.disconnect(); delayed_monitor_changed_connection.disconnect();
if(this->last_write_time!=static_cast<std::time_t>(-1)) if(this->last_write_time != static_cast<std::time_t>(-1))
Recursive::f(this, last_write_time); Recursive::f(this, last_write_time);
#else #else
if(this->last_write_time!=static_cast<std::time_t>(-1)) { if(this->last_write_time != static_cast<std::time_t>(-1)) {
monitor=Gio::File::create_for_path(file_path.string())->monitor_file(Gio::FileMonitorFlags::FILE_MONITOR_NONE); monitor = Gio::File::create_for_path(file_path.string())->monitor_file(Gio::FileMonitorFlags::FILE_MONITOR_NONE);
monitor_changed_connection.disconnect(); monitor_changed_connection.disconnect();
monitor_changed_connection=monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file, monitor_changed_connection = monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File>&, const Glib::RefPtr<Gio::File> &,
Gio::FileMonitorEvent monitor_event) { Gio::FileMonitorEvent monitor_event) {
if(monitor_event!=Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { if(monitor_event != Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
delayed_monitor_changed_connection.disconnect(); delayed_monitor_changed_connection.disconnect();
delayed_monitor_changed_connection=Glib::signal_timeout().connect([this]() { delayed_monitor_changed_connection = Glib::signal_timeout().connect([this]() {
check_last_write_time(); check_last_write_time();
return false; return false;
}, 500); }, 500);
@ -213,21 +213,21 @@ void Source::BaseView::monitor_file() {
} }
void Source::BaseView::check_last_write_time(std::time_t last_write_time_) { void Source::BaseView::check_last_write_time(std::time_t last_write_time_) {
if(this->last_write_time==static_cast<std::time_t>(-1)) if(this->last_write_time == static_cast<std::time_t>(-1))
return; return;
if(Config::get().source.auto_reload_changed_files && !get_buffer()->get_modified()) { if(Config::get().source.auto_reload_changed_files && !get_buffer()->get_modified()) {
boost::system::error_code ec; boost::system::error_code ec;
auto last_write_time=last_write_time_!=static_cast<std::time_t>(-1) ? last_write_time_ : boost::filesystem::last_write_time(file_path, ec); auto last_write_time = last_write_time_ != static_cast<std::time_t>(-1) ? last_write_time_ : boost::filesystem::last_write_time(file_path, ec);
if(!ec && last_write_time!=this->last_write_time) { if(!ec && last_write_time != this->last_write_time) {
if(load()) if(load())
return; return;
} }
} }
else if(has_focus()) { else if(has_focus()) {
boost::system::error_code ec; boost::system::error_code ec;
auto last_write_time=last_write_time_!=static_cast<std::time_t>(-1) ? last_write_time_ : boost::filesystem::last_write_time(file_path, ec); auto last_write_time = last_write_time_ != static_cast<std::time_t>(-1) ? last_write_time_ : boost::filesystem::last_write_time(file_path, ec);
if(!ec && last_write_time!=this->last_write_time) if(!ec && last_write_time != this->last_write_time)
Info::get().print("Caution: " + file_path.filename().string() + " was changed outside of juCi++"); Info::get().print("Caution: " + file_path.filename().string() + " was changed outside of juCi++");
} }
} }
@ -237,57 +237,58 @@ Gtk::TextIter Source::BaseView::get_iter_at_line_pos(int line, int pos) {
} }
Gtk::TextIter Source::BaseView::get_iter_at_line_offset(int line, int offset) { Gtk::TextIter Source::BaseView::get_iter_at_line_offset(int line, int offset) {
line=std::min(line, get_buffer()->get_line_count()-1); line = std::min(line, get_buffer()->get_line_count() - 1);
if(line<0) if(line < 0)
line=0; line = 0;
auto iter=get_iter_at_line_end(line); auto iter = get_iter_at_line_end(line);
offset=std::min(offset, iter.get_line_offset()); offset = std::min(offset, iter.get_line_offset());
if(offset<0) if(offset < 0)
offset=0; offset = 0;
return get_buffer()->get_iter_at_line_offset(line, offset); return get_buffer()->get_iter_at_line_offset(line, offset);
} }
Gtk::TextIter Source::BaseView::get_iter_at_line_index(int line, int index) { Gtk::TextIter Source::BaseView::get_iter_at_line_index(int line, int index) {
line=std::min(line, get_buffer()->get_line_count()-1); line = std::min(line, get_buffer()->get_line_count() - 1);
if(line<0) if(line < 0)
line=0; line = 0;
auto iter=get_iter_at_line_end(line); auto iter = get_iter_at_line_end(line);
index=std::min(index, iter.get_line_index()); index = std::min(index, iter.get_line_index());
if(index<0) if(index < 0)
index=0; index = 0;
return get_buffer()->get_iter_at_line_index(line, index); return get_buffer()->get_iter_at_line_index(line, index);
} }
Gtk::TextIter Source::BaseView::get_iter_at_line_end(int line_nr) { Gtk::TextIter Source::BaseView::get_iter_at_line_end(int line_nr) {
if(line_nr>=get_buffer()->get_line_count()) if(line_nr >= get_buffer()->get_line_count())
return get_buffer()->end(); return get_buffer()->end();
else if(line_nr+1<get_buffer()->get_line_count()) { else if(line_nr + 1 < get_buffer()->get_line_count()) {
auto iter=get_buffer()->get_iter_at_line(line_nr+1); auto iter = get_buffer()->get_iter_at_line(line_nr + 1);
iter.backward_char(); iter.backward_char();
if(!iter.ends_line()) // for CR+LF if(!iter.ends_line()) // for CR+LF
iter.backward_char(); iter.backward_char();
return iter; return iter;
} }
else { else {
auto iter=get_buffer()->get_iter_at_line(line_nr); auto iter = get_buffer()->get_iter_at_line(line_nr);
while(!iter.ends_line() && iter.forward_char()) {} while(!iter.ends_line() && iter.forward_char()) {
}
return iter; return iter;
} }
} }
Gtk::TextIter Source::BaseView::get_iter_for_dialog() { Gtk::TextIter Source::BaseView::get_iter_for_dialog() {
auto iter=get_buffer()->get_insert()->get_iter(); auto iter = get_buffer()->get_insert()->get_iter();
Gdk::Rectangle visible_rect; Gdk::Rectangle visible_rect;
get_visible_rect(visible_rect); get_visible_rect(visible_rect);
Gdk::Rectangle iter_rect; Gdk::Rectangle iter_rect;
get_iter_location(iter, iter_rect); get_iter_location(iter, iter_rect);
iter_rect.set_width(1); iter_rect.set_width(1);
if(iter.get_line_offset()>=80) { if(iter.get_line_offset() >= 80) {
get_iter_at_location(iter, visible_rect.get_x(), iter_rect.get_y()); get_iter_at_location(iter, visible_rect.get_x(), iter_rect.get_y());
get_iter_location(iter, iter_rect); get_iter_location(iter, iter_rect);
} }
if(!visible_rect.intersects(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); get_iter_at_location(iter, visible_rect.get_x(), visible_rect.get_y() + visible_rect.get_height() / 3);
return iter; return iter;
} }
@ -304,28 +305,30 @@ void Source::BaseView::place_cursor_at_line_index(int line, int index) {
} }
Gtk::TextIter Source::BaseView::get_smart_home_iter(const Gtk::TextIter &iter) { Gtk::TextIter Source::BaseView::get_smart_home_iter(const Gtk::TextIter &iter) {
auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line()); auto start_line_iter = get_buffer()->get_iter_at_line(iter.get_line());
auto start_sentence_iter=start_line_iter; auto start_sentence_iter = start_line_iter;
while(!start_sentence_iter.ends_line() && while(!start_sentence_iter.ends_line() &&
(*start_sentence_iter==' ' || *start_sentence_iter=='\t') && (*start_sentence_iter == ' ' || *start_sentence_iter == '\t') &&
start_sentence_iter.forward_char()) {} start_sentence_iter.forward_char()) {
}
if(iter>start_sentence_iter || iter==start_line_iter)
if(iter > start_sentence_iter || iter == start_line_iter)
return start_sentence_iter; return start_sentence_iter;
else else
return start_line_iter; return start_line_iter;
} }
Gtk::TextIter Source::BaseView::get_smart_end_iter(const Gtk::TextIter &iter) { Gtk::TextIter Source::BaseView::get_smart_end_iter(const Gtk::TextIter &iter) {
auto end_line_iter=get_iter_at_line_end(iter.get_line()); auto end_line_iter = get_iter_at_line_end(iter.get_line());
auto end_sentence_iter=end_line_iter; auto end_sentence_iter = end_line_iter;
while(!end_sentence_iter.starts_line() && while(!end_sentence_iter.starts_line() &&
(*end_sentence_iter==' ' || *end_sentence_iter=='\t' || end_sentence_iter.ends_line()) && (*end_sentence_iter == ' ' || *end_sentence_iter == '\t' || end_sentence_iter.ends_line()) &&
end_sentence_iter.backward_char()) {} end_sentence_iter.backward_char()) {
if(!end_sentence_iter.ends_line() && *end_sentence_iter!=' ' && *end_sentence_iter!='\t') }
if(!end_sentence_iter.ends_line() && *end_sentence_iter != ' ' && *end_sentence_iter != '\t')
end_sentence_iter.forward_char(); end_sentence_iter.forward_char();
if(iter==end_line_iter) if(iter == end_line_iter)
return end_sentence_iter; return end_sentence_iter;
else else
return end_line_iter; return end_line_iter;
@ -367,7 +370,8 @@ Gtk::TextIter Source::BaseView::get_tabs_end_iter(const Glib::RefPtr<Gtk::TextBu
} }
Gtk::TextIter Source::BaseView::get_tabs_end_iter(int line_nr) { Gtk::TextIter Source::BaseView::get_tabs_end_iter(int line_nr) {
auto sentence_iter = get_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()) {} while((*sentence_iter == ' ' || *sentence_iter == '\t') && !sentence_iter.ends_line() && sentence_iter.forward_char()) {
}
return sentence_iter; return sentence_iter;
} }
Gtk::TextIter Source::BaseView::get_tabs_end_iter() { Gtk::TextIter Source::BaseView::get_tabs_end_iter() {
@ -375,18 +379,18 @@ Gtk::TextIter Source::BaseView::get_tabs_end_iter() {
} }
void Source::BaseView::place_cursor_at_next_diagnostic() { void Source::BaseView::place_cursor_at_next_diagnostic() {
auto insert_offset=get_buffer()->get_insert()->get_iter().get_offset(); auto insert_offset = get_buffer()->get_insert()->get_iter().get_offset();
for(auto offset: diagnostic_offsets) { for(auto offset : diagnostic_offsets) {
if(offset>insert_offset) { if(offset > insert_offset) {
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset)); get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return; return;
} }
} }
if(diagnostic_offsets.size()==0) if(diagnostic_offsets.size() == 0)
Info::get().print("No diagnostics found in current buffer"); Info::get().print("No diagnostics found in current buffer");
else { else {
auto iter=get_buffer()->get_iter_at_offset(*diagnostic_offsets.begin()); auto iter = get_buffer()->get_iter_at_offset(*diagnostic_offsets.begin());
get_buffer()->place_cursor(iter); get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
} }

43
src/source_base.h

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <boost/filesystem.hpp>
#include <gtksourceviewmm.h> #include <gtksourceviewmm.h>
#include <mutex> #include <mutex>
#include <set> #include <set>
#include <boost/filesystem.hpp>
namespace Source { namespace Source {
class BaseView : public Gsv::View { class BaseView : public Gsv::View {
@ -11,55 +11,55 @@ namespace Source {
BaseView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language); BaseView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
~BaseView() override; ~BaseView() override;
boost::filesystem::path file_path; boost::filesystem::path file_path;
Glib::RefPtr<Gsv::Language> language; Glib::RefPtr<Gsv::Language> language;
bool load(bool not_undoable_action=false); bool load(bool not_undoable_action = false);
/// Set new text more optimally and without unnecessary scrolling /// Set new text more optimally and without unnecessary scrolling
void replace_text(const std::string &new_text); void replace_text(const std::string &new_text);
virtual void rename(const boost::filesystem::path &path); virtual void rename(const boost::filesystem::path &path);
virtual bool save() = 0; virtual bool save() = 0;
Glib::RefPtr<Gio::FileMonitor> monitor; Glib::RefPtr<Gio::FileMonitor> monitor;
sigc::connection monitor_changed_connection; sigc::connection monitor_changed_connection;
sigc::connection delayed_monitor_changed_connection; sigc::connection delayed_monitor_changed_connection;
virtual void configure() = 0; virtual void configure() = 0;
virtual void hide_tooltips() = 0; virtual void hide_tooltips() = 0;
virtual void hide_dialogs() = 0; virtual void hide_dialogs() = 0;
std::function<void(BaseView* view, bool center, bool show_tooltips)> scroll_to_cursor_delayed=[](BaseView* view, bool center, bool show_tooltips) {}; std::function<void(BaseView *view, bool center, bool show_tooltips)> scroll_to_cursor_delayed = [](BaseView *view, bool center, bool show_tooltips) {};
/// Safely returns iter given line and an offset using either byte index or character offset. Defaults to using byte index. /// Safely returns iter given line and an offset using either byte index or character offset. Defaults to using byte index.
virtual Gtk::TextIter get_iter_at_line_pos(int line, int pos); virtual Gtk::TextIter get_iter_at_line_pos(int line, int pos);
/// Safely returns iter given line and character offset /// Safely returns iter given line and character offset
Gtk::TextIter get_iter_at_line_offset(int line, int offset); Gtk::TextIter get_iter_at_line_offset(int line, int offset);
/// Safely returns iter given line and byte index /// Safely returns iter given line and byte index
Gtk::TextIter get_iter_at_line_index(int line, int index); Gtk::TextIter get_iter_at_line_index(int line, int index);
Gtk::TextIter get_iter_at_line_end(int line_nr); Gtk::TextIter get_iter_at_line_end(int line_nr);
Gtk::TextIter get_iter_for_dialog(); Gtk::TextIter get_iter_for_dialog();
/// Safely places cursor at line using get_iter_at_line_pos. /// Safely places cursor at line using get_iter_at_line_pos.
void place_cursor_at_line_pos(int line, int pos); void place_cursor_at_line_pos(int line, int pos);
/// Safely places cursor at line offset /// Safely places cursor at line offset
void place_cursor_at_line_offset(int line, int offset); void place_cursor_at_line_offset(int line, int offset);
/// Safely places cursor at line index /// Safely places cursor at line index
void place_cursor_at_line_index(int line, int index); void place_cursor_at_line_index(int line, int index);
protected: protected:
std::time_t last_write_time; std::time_t last_write_time;
void monitor_file(); void monitor_file();
void check_last_write_time(std::time_t last_write_time_=static_cast<std::time_t>(-1)); void check_last_write_time(std::time_t last_write_time_ = static_cast<std::time_t>(-1));
/// Move iter to line start. Depending on iter position, before or after indentation. /// Move iter to line start. Depending on iter position, before or after indentation.
/// Works with wrapped lines. /// Works with wrapped lines.
Gtk::TextIter get_smart_home_iter(const Gtk::TextIter &iter); Gtk::TextIter get_smart_home_iter(const Gtk::TextIter &iter);
/// Move iter to line end. Depending on iter position, before or after indentation. /// Move iter to line end. Depending on iter position, before or after indentation.
/// Works with wrapped lines. /// Works with wrapped lines.
/// Note that smart end goes FIRST to end of line to avoid hiding empty chars after expressions. /// Note that smart end goes FIRST to end of line to avoid hiding empty chars after expressions.
Gtk::TextIter get_smart_end_iter(const Gtk::TextIter &iter); Gtk::TextIter get_smart_end_iter(const Gtk::TextIter &iter);
std::string get_line(const Gtk::TextIter &iter); std::string get_line(const Gtk::TextIter &iter);
std::string get_line(const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark); std::string get_line(const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark);
std::string get_line(int line_nr); std::string get_line(int line_nr);
@ -71,9 +71,10 @@ namespace Source {
Gtk::TextIter get_tabs_end_iter(const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark); Gtk::TextIter get_tabs_end_iter(const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark);
Gtk::TextIter get_tabs_end_iter(int line_nr); Gtk::TextIter get_tabs_end_iter(int line_nr);
Gtk::TextIter get_tabs_end_iter(); Gtk::TextIter get_tabs_end_iter();
std::set<int> diagnostic_offsets; std::set<int> diagnostic_offsets;
void place_cursor_at_next_diagnostic(); void place_cursor_at_next_diagnostic();
public: public:
std::function<void(BaseView *view)> update_tab_label; std::function<void(BaseView *view)> update_tab_label;
std::function<void(BaseView *view)> update_status_location; std::function<void(BaseView *view)> update_status_location;
@ -84,7 +85,7 @@ namespace Source {
std::string status_state; std::string status_state;
std::function<void(BaseView *view)> update_status_branch; std::function<void(BaseView *view)> update_status_branch;
std::string status_branch; std::string status_branch;
bool disable_spellcheck=false; bool disable_spellcheck = false;
}; };
} } // namespace Source

1646
src/source_clang.cc

File diff suppressed because it is too large Load Diff

68
src/source_clang.h

@ -1,28 +1,29 @@
#pragma once #pragma once
#include <thread> #include "autocomplete.h"
#include "clangmm.h"
#include "dispatcher.h"
#include "source.h"
#include "terminal.h"
#include <atomic> #include <atomic>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <set> #include <set>
#include "clangmm.h" #include <thread>
#include "source.h"
#include "terminal.h"
#include "dispatcher.h"
#include "autocomplete.h"
namespace Source { namespace Source {
class ClangViewParse : public View { class ClangViewParse : public View {
protected: protected:
enum class ParseState {PROCESSING, RESTARTING, STOP}; enum class ParseState { PROCESSING, RESTARTING, STOP };
enum class ParseProcessState {IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING}; enum class ParseProcessState { IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING };
public: public:
ClangViewParse(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language); ClangViewParse(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
bool save() override; bool save() override;
void configure() override; void configure() override;
void soft_reparse(bool delayed=false) override; void soft_reparse(bool delayed = false) override;
protected: protected:
Dispatcher dispatcher; Dispatcher dispatcher;
void parse_initialize(); void parse_initialize();
@ -30,38 +31,41 @@ namespace Source {
std::unique_ptr<clangmm::Tokens> clang_tokens; std::unique_ptr<clangmm::Tokens> clang_tokens;
std::vector<std::pair<clangmm::Offset, clangmm::Offset>> clang_tokens_offsets; std::vector<std::pair<clangmm::Offset, clangmm::Offset>> clang_tokens_offsets;
sigc::connection delayed_reparse_connection; sigc::connection delayed_reparse_connection;
void show_type_tooltips(const Gdk::Rectangle &rectangle) override; void show_type_tooltips(const Gdk::Rectangle &rectangle) override;
std::vector<FixIt> fix_its; std::vector<FixIt> fix_its;
std::thread parse_thread; std::thread parse_thread;
std::mutex parse_mutex; std::mutex parse_mutex;
std::atomic<ParseState> parse_state; std::atomic<ParseState> parse_state;
std::atomic<ParseProcessState> parse_process_state; std::atomic<ParseProcessState> parse_process_state;
CXCompletionString selected_completion_string=nullptr; CXCompletionString selected_completion_string = nullptr;
private: private:
Glib::ustring parse_thread_buffer; Glib::ustring parse_thread_buffer;
static const std::map<int, std::string> &clang_types(); static const std::map<int, std::string> &clang_types();
void update_syntax(); void update_syntax();
std::map<int, Glib::RefPtr<Gtk::TextTag>> syntax_tags; std::map<int, Glib::RefPtr<Gtk::TextTag>> syntax_tags;
void update_diagnostics(); void update_diagnostics();
std::vector<clangmm::Diagnostic> clang_diagnostics; std::vector<clangmm::Diagnostic> clang_diagnostics;
static clangmm::Index clang_index; static clangmm::Index clang_index;
}; };
class ClangViewAutocomplete : public virtual ClangViewParse { class ClangViewAutocomplete : public virtual ClangViewParse {
public: public:
ClangViewAutocomplete(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language); ClangViewAutocomplete(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
protected: protected:
Autocomplete autocomplete; Autocomplete autocomplete;
std::unique_ptr<clangmm::CodeCompleteResults> code_complete_results; std::unique_ptr<clangmm::CodeCompleteResults> code_complete_results;
std::vector<CXCompletionString> completion_strings; std::vector<CXCompletionString> completion_strings;
sigc::connection delayed_show_arguments_connection; sigc::connection delayed_show_arguments_connection;
private: private:
bool is_possible_parameter(); bool is_possible_parameter();
bool show_arguments; bool show_arguments;
@ -74,37 +78,39 @@ namespace Source {
Identifier(std::string spelling_, const clangmm::Cursor &cursor) Identifier(std::string spelling_, const clangmm::Cursor &cursor)
: kind(cursor.get_kind()), spelling(std::move(spelling_)), usr_extended(cursor.get_usr_extended()), cursor(cursor) {} : kind(cursor.get_kind()), spelling(std::move(spelling_)), usr_extended(cursor.get_usr_extended()), cursor(cursor) {}
Identifier() : kind(static_cast<clangmm::Cursor::Kind>(0)) {} Identifier() : kind(static_cast<clangmm::Cursor::Kind>(0)) {}
operator bool() const { return static_cast<int>(kind)!=0; } operator bool() const { return static_cast<int>(kind) != 0; }
bool operator==(const Identifier &rhs) const { return spelling==rhs.spelling && usr_extended==rhs.usr_extended; } bool operator==(const Identifier &rhs) const { return spelling == rhs.spelling && usr_extended == rhs.usr_extended; }
bool operator!=(const Identifier &rhs) const { return !(*this==rhs); } bool operator!=(const Identifier &rhs) const { return !(*this == rhs); }
bool operator<(const Identifier &rhs) const { return spelling<rhs.spelling || (spelling==rhs.spelling && usr_extended<rhs.usr_extended); } bool operator<(const Identifier &rhs) const { return spelling < rhs.spelling || (spelling == rhs.spelling && usr_extended < rhs.usr_extended); }
clangmm::Cursor::Kind kind; clangmm::Cursor::Kind kind;
std::string spelling; std::string spelling;
std::string usr_extended; std::string usr_extended;
clangmm::Cursor cursor; clangmm::Cursor cursor;
}; };
public: public:
ClangViewRefactor(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language); ClangViewRefactor(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
private: private:
Identifier get_identifier(); Identifier get_identifier();
void wait_parsing(); void wait_parsing();
void tag_similar_identifiers(const Identifier &identifier); void tag_similar_identifiers(const Identifier &identifier);
Identifier last_tagged_identifier; Identifier last_tagged_identifier;
}; };
class ClangView : public ClangViewAutocomplete, public ClangViewRefactor { class ClangView : public ClangViewAutocomplete, public ClangViewRefactor {
public: public:
ClangView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language); ClangView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
void full_reparse() override; void full_reparse() override;
void async_delete(); void async_delete();
private: private:
Glib::Dispatcher do_delete_object; Glib::Dispatcher do_delete_object;
std::thread delete_thread; std::thread delete_thread;
std::thread full_reparse_thread; std::thread full_reparse_thread;
bool full_reparse_running=false; bool full_reparse_running = false;
}; };
} } // namespace Source

232
src/source_diff.cc

@ -1,8 +1,8 @@
#include "source_diff.h" #include "source_diff.h"
#include "config.h" #include "config.h"
#include "terminal.h"
#include "filesystem.h" #include "filesystem.h"
#include "info.h" #include "info.h"
#include "terminal.h"
#include <boost/version.hpp> #include <boost/version.hpp>
Source::DiffView::Renderer::Renderer() : Gsv::GutterRenderer() { Source::DiffView::Renderer::Renderer() : Gsv::GutterRenderer() {
@ -24,27 +24,27 @@ void Source::DiffView::Renderer::draw_vfunc(const Cairo::RefPtr<Cairo::Context>
} }
if(start.has_tag(tag_removed_below) || end.has_tag(tag_removed_below)) { if(start.has_tag(tag_removed_below) || end.has_tag(tag_removed_below)) {
cr->set_source_rgba(1.0, 0.0, 0.0, 0.5); cr->set_source_rgba(1.0, 0.0, 0.0, 0.5);
cr->rectangle(cell_area.get_x()-4, cell_area.get_y()+cell_area.get_height()-2, 8, 2); cr->rectangle(cell_area.get_x() - 4, cell_area.get_y() + cell_area.get_height() - 2, 8, 2);
cr->fill(); cr->fill();
} }
if(start.has_tag(tag_removed_above) || end.has_tag(tag_removed_above)) { if(start.has_tag(tag_removed_above) || end.has_tag(tag_removed_above)) {
cr->set_source_rgba(1.0, 0.0, 0.0, 0.5); cr->set_source_rgba(1.0, 0.0, 0.0, 0.5);
cr->rectangle(cell_area.get_x()-4, cell_area.get_y(), 8, 2); cr->rectangle(cell_area.get_x() - 4, cell_area.get_y(), 8, 2);
cr->fill(); cr->fill();
} }
} }
Source::DiffView::DiffView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language) : BaseView(file_path, language), renderer(new Renderer()) { Source::DiffView::DiffView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language) : BaseView(file_path, language), renderer(new Renderer()) {
boost::system::error_code ec; boost::system::error_code ec;
canonical_file_path=boost::filesystem::canonical(file_path, ec); canonical_file_path = boost::filesystem::canonical(file_path, ec);
if(ec) if(ec)
canonical_file_path=file_path; canonical_file_path = file_path;
renderer->tag_added=get_buffer()->create_tag("git_added"); renderer->tag_added = get_buffer()->create_tag("git_added");
renderer->tag_modified=get_buffer()->create_tag("git_modified"); renderer->tag_modified = get_buffer()->create_tag("git_modified");
renderer->tag_removed=get_buffer()->create_tag("git_removed"); renderer->tag_removed = get_buffer()->create_tag("git_removed");
renderer->tag_removed_below=get_buffer()->create_tag(); renderer->tag_removed_below = get_buffer()->create_tag();
renderer->tag_removed_above=get_buffer()->create_tag(); renderer->tag_removed_above = get_buffer()->create_tag();
} }
Source::DiffView::~DiffView() { Source::DiffView::~DiffView() {
@ -56,8 +56,8 @@ Source::DiffView::~DiffView() {
monitor_changed_connection.disconnect(); monitor_changed_connection.disconnect();
delayed_buffer_changed_connection.disconnect(); delayed_buffer_changed_connection.disconnect();
delayed_monitor_changed_connection.disconnect(); delayed_monitor_changed_connection.disconnect();
parse_stop=true; parse_stop = true;
if(parse_thread.joinable()) if(parse_thread.joinable())
parse_thread.join(); parse_thread.join();
} }
@ -75,37 +75,37 @@ void Source::DiffView::configure() {
monitor_changed_connection.disconnect(); monitor_changed_connection.disconnect();
delayed_buffer_changed_connection.disconnect(); delayed_buffer_changed_connection.disconnect();
delayed_monitor_changed_connection.disconnect(); delayed_monitor_changed_connection.disconnect();
parse_stop=true; parse_stop = true;
if(parse_thread.joinable()) if(parse_thread.joinable())
parse_thread.join(); parse_thread.join();
repository=nullptr; repository = nullptr;
diff=nullptr; diff = nullptr;
return; return;
} }
else else
return; return;
try { try {
repository=Git::get_repository(this->file_path.parent_path()); repository = Git::get_repository(this->file_path.parent_path());
} }
catch(const std::exception &) { catch(const std::exception &) {
return; return;
} }
get_gutter(Gtk::TextWindowType::TEXT_WINDOW_LEFT)->insert(renderer.get(), -40); get_gutter(Gtk::TextWindowType::TEXT_WINDOW_LEFT)->insert(renderer.get(), -40);
parse_state=ParseState::STARTING; parse_state = ParseState::STARTING;
parse_stop=false; parse_stop = false;
monitor_changed=false; monitor_changed = false;
buffer_insert_connection=get_buffer()->signal_insert().connect([this](const Gtk::TextBuffer::iterator &iter ,const Glib::ustring &text, int) { buffer_insert_connection = get_buffer()->signal_insert().connect([this](const Gtk::TextBuffer::iterator &iter, const Glib::ustring &text, int) {
//Do not perform git diff if no newline is added and line is already marked as added //Do not perform git diff if no newline is added and line is already marked as added
if(!iter.starts_line() && iter.has_tag(renderer->tag_added)) { if(!iter.starts_line() && iter.has_tag(renderer->tag_added)) {
bool newline=false; bool newline = false;
for(auto &c: text.raw()) { for(auto &c : text.raw()) {
if(c=='\n') { if(c == '\n') {
newline=true; newline = true;
break; break;
} }
} }
@ -113,92 +113,92 @@ void Source::DiffView::configure() {
return; return;
} }
//Remove tag_removed_above/below if newline is inserted //Remove tag_removed_above/below if newline is inserted
else if(!text.empty() && text[0]=='\n' && iter.has_tag(renderer->tag_removed)) { else if(!text.empty() && text[0] == '\n' && iter.has_tag(renderer->tag_removed)) {
auto start_iter=get_buffer()->get_iter_at_line(iter.get_line()); auto start_iter = get_buffer()->get_iter_at_line(iter.get_line());
auto end_iter=get_iter_at_line_end(iter.get_line()); auto end_iter = get_iter_at_line_end(iter.get_line());
end_iter.forward_char(); end_iter.forward_char();
get_buffer()->remove_tag(renderer->tag_removed_above, start_iter, end_iter); get_buffer()->remove_tag(renderer->tag_removed_above, start_iter, end_iter);
get_buffer()->remove_tag(renderer->tag_removed_below, start_iter, end_iter); get_buffer()->remove_tag(renderer->tag_removed_below, start_iter, end_iter);
} }
parse_state=ParseState::IDLE; parse_state = ParseState::IDLE;
delayed_buffer_changed_connection.disconnect(); delayed_buffer_changed_connection.disconnect();
delayed_buffer_changed_connection=Glib::signal_timeout().connect([this]() { delayed_buffer_changed_connection = Glib::signal_timeout().connect([this]() {
parse_state=ParseState::STARTING; parse_state = ParseState::STARTING;
return false; return false;
}, 250); }, 250);
}, false); }, false);
buffer_erase_connection=get_buffer()->signal_erase().connect([this](const Gtk::TextBuffer::iterator &start_iter, const Gtk::TextBuffer::iterator &end_iter) { buffer_erase_connection = get_buffer()->signal_erase().connect([this](const Gtk::TextBuffer::iterator &start_iter, const Gtk::TextBuffer::iterator &end_iter) {
//Do not perform git diff if start_iter and end_iter is at the same line in addition to the line is tagged added //Do not perform git diff if start_iter and end_iter is at the same line in addition to the line is tagged added
if(start_iter.get_line()==end_iter.get_line() && start_iter.has_tag(renderer->tag_added)) if(start_iter.get_line() == end_iter.get_line() && start_iter.has_tag(renderer->tag_added))
return; return;
parse_state=ParseState::IDLE; parse_state = ParseState::IDLE;
delayed_buffer_changed_connection.disconnect(); delayed_buffer_changed_connection.disconnect();
delayed_buffer_changed_connection=Glib::signal_timeout().connect([this]() { delayed_buffer_changed_connection = Glib::signal_timeout().connect([this]() {
parse_state=ParseState::STARTING; parse_state = ParseState::STARTING;
return false; return false;
}, 250); }, 250);
}, false); }, false);
monitor_changed_connection=repository->monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file, monitor_changed_connection = repository->monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File>&, const Glib::RefPtr<Gio::File> &,
Gio::FileMonitorEvent monitor_event) { Gio::FileMonitorEvent monitor_event) {
if(monitor_event!=Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { if(monitor_event != Gio::FileMonitorEvent::FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
delayed_monitor_changed_connection.disconnect(); delayed_monitor_changed_connection.disconnect();
delayed_monitor_changed_connection=Glib::signal_timeout().connect([this]() { delayed_monitor_changed_connection = Glib::signal_timeout().connect([this]() {
monitor_changed=true; monitor_changed = true;
parse_state=ParseState::STARTING; parse_state = ParseState::STARTING;
std::unique_lock<std::mutex> lock(parse_mutex); std::unique_lock<std::mutex> lock(parse_mutex);
diff=nullptr; diff = nullptr;
return false; return false;
}, 500); }, 500);
} }
}); });
parse_thread=std::thread([this]() { parse_thread = std::thread([this]() {
std::string status_branch; std::string status_branch;
try { try {
diff=get_diff(); diff = get_diff();
status_branch=repository->get_branch(); status_branch = repository->get_branch();
} }
catch(const std::exception &) { catch(const std::exception &) {
status_branch=""; status_branch = "";
} }
dispatcher.post([this, status_branch=std::move(status_branch)] { dispatcher.post([this, status_branch = std::move(status_branch)] {
this->status_branch=status_branch; this->status_branch = status_branch;
if(update_status_branch) if(update_status_branch)
update_status_branch(this); update_status_branch(this);
}); });
try { try {
while(true) { while(true) {
while(!parse_stop && parse_state!=ParseState::STARTING && parse_state!=ParseState::PROCESSING) while(!parse_stop && parse_state != ParseState::STARTING && parse_state != ParseState::PROCESSING)
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
if(parse_stop) if(parse_stop)
break; break;
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock); std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock);
auto expected=ParseState::STARTING; auto expected = ParseState::STARTING;
if(parse_state.compare_exchange_strong(expected, ParseState::PREPROCESSING)) { if(parse_state.compare_exchange_strong(expected, ParseState::PREPROCESSING)) {
dispatcher.post([this] { dispatcher.post([this] {
auto expected=ParseState::PREPROCESSING; auto expected = ParseState::PREPROCESSING;
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock); std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock);
if(parse_lock.try_lock()) { if(parse_lock.try_lock()) {
if(parse_state.compare_exchange_strong(expected, ParseState::PROCESSING)) if(parse_state.compare_exchange_strong(expected, ParseState::PROCESSING))
parse_buffer=get_buffer()->get_text(); parse_buffer = get_buffer()->get_text();
parse_lock.unlock(); parse_lock.unlock();
} }
else else
parse_state.compare_exchange_strong(expected, ParseState::STARTING); parse_state.compare_exchange_strong(expected, ParseState::STARTING);
}); });
} }
else if (parse_state==ParseState::PROCESSING && parse_lock.try_lock()) { else if(parse_state == ParseState::PROCESSING && parse_lock.try_lock()) {
bool expected_monitor_changed=true; bool expected_monitor_changed = true;
if(monitor_changed.compare_exchange_strong(expected_monitor_changed, false)) { if(monitor_changed.compare_exchange_strong(expected_monitor_changed, false)) {
try { try {
diff=get_diff(); diff = get_diff();
dispatcher.post([this, status_branch=repository->get_branch()] { dispatcher.post([this, status_branch = repository->get_branch()] {
this->status_branch=status_branch; this->status_branch = status_branch;
if(update_status_branch) if(update_status_branch)
update_status_branch(this); update_status_branch(this);
}); });
@ -211,26 +211,26 @@ void Source::DiffView::configure() {
get_buffer()->remove_tag(renderer->tag_removed_below, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed_below, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed_above, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed_above, get_buffer()->begin(), get_buffer()->end());
renderer->queue_draw(); renderer->queue_draw();
this->status_branch=""; this->status_branch = "";
if(update_status_branch) if(update_status_branch)
update_status_branch(this); update_status_branch(this);
}); });
} }
} }
if(diff) if(diff)
lines=diff->get_lines(parse_buffer.raw()); lines = diff->get_lines(parse_buffer.raw());
else { else {
lines.added.clear(); lines.added.clear();
lines.modified.clear(); lines.modified.clear();
lines.removed.clear(); lines.removed.clear();
} }
auto expected=ParseState::PROCESSING; auto expected = ParseState::PROCESSING;
if(parse_state.compare_exchange_strong(expected, ParseState::POSTPROCESSING)) { if(parse_state.compare_exchange_strong(expected, ParseState::POSTPROCESSING)) {
parse_lock.unlock(); parse_lock.unlock();
dispatcher.post([this] { dispatcher.post([this] {
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock); std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock);
if(parse_lock.try_lock()) { if(parse_lock.try_lock()) {
auto expected=ParseState::POSTPROCESSING; auto expected = ParseState::POSTPROCESSING;
if(parse_state.compare_exchange_strong(expected, ParseState::IDLE)) if(parse_state.compare_exchange_strong(expected, ParseState::IDLE))
update_lines(); update_lines();
} }
@ -240,14 +240,14 @@ void Source::DiffView::configure() {
} }
} }
catch(const std::exception &e) { catch(const std::exception &e) {
dispatcher.post([this, e_what=e.what()] { dispatcher.post([this, e_what = e.what()] {
get_buffer()->remove_tag(renderer->tag_added, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_added, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_modified, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_modified, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed_below, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed_below, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed_above, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed_above, get_buffer()->begin(), get_buffer()->end());
renderer->queue_draw(); renderer->queue_draw();
Terminal::get().print(std::string("Error (git): ")+e_what+'\n', true); Terminal::get().print(std::string("Error (git): ") + e_what + '\n', true);
}); });
} }
}); });
@ -255,35 +255,35 @@ void Source::DiffView::configure() {
void Source::DiffView::rename(const boost::filesystem::path &path) { void Source::DiffView::rename(const boost::filesystem::path &path) {
Source::BaseView::rename(path); Source::BaseView::rename(path);
std::lock_guard<std::mutex> lock(canonical_file_path_mutex); std::lock_guard<std::mutex> lock(canonical_file_path_mutex);
boost::system::error_code ec; boost::system::error_code ec;
canonical_file_path=boost::filesystem::canonical(path, ec); canonical_file_path = boost::filesystem::canonical(path, ec);
if(ec) if(ec)
canonical_file_path=path; canonical_file_path = path;
} }
void Source::DiffView::git_goto_next_diff() { void Source::DiffView::git_goto_next_diff() {
auto iter=get_buffer()->get_insert()->get_iter(); auto iter = get_buffer()->get_insert()->get_iter();
auto insert_iter=iter; auto insert_iter = iter;
bool wrapped=false; bool wrapped = false;
iter.forward_char(); iter.forward_char();
for(;;) { for(;;) {
auto toggled_tags=iter.get_toggled_tags(); auto toggled_tags = iter.get_toggled_tags();
for(auto &toggled_tag: toggled_tags) { for(auto &toggled_tag : toggled_tags) {
if(toggled_tag->property_name()=="git_added" || if(toggled_tag->property_name() == "git_added" ||
toggled_tag->property_name()=="git_modified" || toggled_tag->property_name() == "git_modified" ||
toggled_tag->property_name()=="git_removed") { toggled_tag->property_name() == "git_removed") {
get_buffer()->place_cursor(iter); get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return; return;
} }
} }
if(wrapped && (iter==insert_iter || iter==get_buffer()->end())) if(wrapped && (iter == insert_iter || iter == get_buffer()->end()))
break; break;
if(!wrapped && iter==get_buffer()->end()) { if(!wrapped && iter == get_buffer()->end()) {
iter=get_buffer()->begin(); iter = get_buffer()->begin();
wrapped=true; wrapped = true;
} }
else else
iter.forward_char(); iter.forward_char();
@ -294,13 +294,13 @@ void Source::DiffView::git_goto_next_diff() {
std::string Source::DiffView::git_get_diff_details() { std::string Source::DiffView::git_get_diff_details() {
std::string details; std::string details;
if(diff) { if(diff) {
auto line_nr=get_buffer()->get_insert()->get_iter().get_line(); auto line_nr = get_buffer()->get_insert()->get_iter().get_line();
auto iter=get_buffer()->get_iter_at_line(line_nr); auto iter = get_buffer()->get_iter_at_line(line_nr);
if(iter.has_tag(renderer->tag_removed_above)) if(iter.has_tag(renderer->tag_removed_above))
--line_nr; --line_nr;
std::unique_lock<std::mutex> lock(parse_mutex); std::unique_lock<std::mutex> lock(parse_mutex);
parse_buffer=get_buffer()->get_text(); parse_buffer = get_buffer()->get_text();
details=diff->get_details(parse_buffer.raw(), line_nr); details = diff->get_details(parse_buffer.raw(), line_nr);
} }
if(details.empty()) if(details.empty())
Info::get().print("No changes found at current line"); Info::get().print("No changes found at current line");
@ -309,11 +309,11 @@ std::string Source::DiffView::git_get_diff_details() {
///Return repository diff instance. Throws exception on error ///Return repository diff instance. Throws exception on error
std::unique_ptr<Git::Repository::Diff> Source::DiffView::get_diff() { std::unique_ptr<Git::Repository::Diff> Source::DiffView::get_diff() {
auto work_path=filesystem::get_normal_path(repository->get_work_path()); auto work_path = filesystem::get_normal_path(repository->get_work_path());
boost::filesystem::path relative_path; boost::filesystem::path relative_path;
{ {
std::unique_lock<std::mutex> lock(canonical_file_path_mutex); std::unique_lock<std::mutex> lock(canonical_file_path_mutex);
relative_path=filesystem::get_relative_path(canonical_file_path, work_path); relative_path = filesystem::get_relative_path(canonical_file_path, work_path);
if(relative_path.empty()) if(relative_path.empty())
throw std::runtime_error("not a relative path"); throw std::runtime_error("not a relative path");
} }
@ -326,40 +326,40 @@ void Source::DiffView::update_lines() {
get_buffer()->remove_tag(renderer->tag_removed, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed_below, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed_below, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed_above, get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(renderer->tag_removed_above, get_buffer()->begin(), get_buffer()->end());
for(auto &added: lines.added) { for(auto &added : lines.added) {
auto start_iter=get_buffer()->get_iter_at_line(added.first); auto start_iter = get_buffer()->get_iter_at_line(added.first);
auto end_iter=get_iter_at_line_end(added.second-1); auto end_iter = get_iter_at_line_end(added.second - 1);
end_iter.forward_char(); end_iter.forward_char();
get_buffer()->apply_tag(renderer->tag_added, start_iter, end_iter); get_buffer()->apply_tag(renderer->tag_added, start_iter, end_iter);
} }
for(auto &modified: lines.modified) { for(auto &modified : lines.modified) {
auto start_iter=get_buffer()->get_iter_at_line(modified.first); auto start_iter = get_buffer()->get_iter_at_line(modified.first);
auto end_iter=get_iter_at_line_end(modified.second-1); auto end_iter = get_iter_at_line_end(modified.second - 1);
end_iter.forward_char(); end_iter.forward_char();
get_buffer()->apply_tag(renderer->tag_modified, start_iter, end_iter); get_buffer()->apply_tag(renderer->tag_modified, start_iter, end_iter);
} }
for(auto &line_nr: lines.removed) { for(auto &line_nr : lines.removed) {
Gtk::TextIter removed_start, removed_end; Gtk::TextIter removed_start, removed_end;
if(line_nr>=0) { if(line_nr >= 0) {
auto start_iter=get_buffer()->get_iter_at_line(line_nr); auto start_iter = get_buffer()->get_iter_at_line(line_nr);
removed_start=start_iter; removed_start = start_iter;
auto end_iter=get_iter_at_line_end(line_nr); auto end_iter = get_iter_at_line_end(line_nr);
end_iter.forward_char(); end_iter.forward_char();
removed_end=end_iter; removed_end = end_iter;
get_buffer()->apply_tag(renderer->tag_removed_below, start_iter, end_iter); get_buffer()->apply_tag(renderer->tag_removed_below, start_iter, end_iter);
} }
if(line_nr+1<get_buffer()->get_line_count()) { if(line_nr + 1 < get_buffer()->get_line_count()) {
auto start_iter=get_buffer()->get_iter_at_line(line_nr+1); auto start_iter = get_buffer()->get_iter_at_line(line_nr + 1);
if(line_nr<0) if(line_nr < 0)
removed_start=start_iter; removed_start = start_iter;
auto end_iter=get_iter_at_line_end(line_nr+1); auto end_iter = get_iter_at_line_end(line_nr + 1);
end_iter.forward_char(); end_iter.forward_char();
removed_end=end_iter; removed_end = end_iter;
get_buffer()->apply_tag(renderer->tag_removed_above, start_iter, end_iter); get_buffer()->apply_tag(renderer->tag_removed_above, start_iter, end_iter);
} }
get_buffer()->apply_tag(renderer->tag_removed, removed_start, removed_end); get_buffer()->apply_tag(renderer->tag_removed, removed_start, removed_end);
} }
renderer->queue_draw(); renderer->queue_draw();
} }

38
src/source_diff.h

@ -1,56 +1,58 @@
#pragma once #pragma once
#include "dispatcher.h"
#include "git.h"
#include "source_base.h" #include "source_base.h"
#include <atomic>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "dispatcher.h"
#include <set>
#include <map> #include <map>
#include <thread>
#include <atomic>
#include <mutex> #include <mutex>
#include "git.h" #include <set>
#include <thread>
namespace Source { namespace Source {
class DiffView : virtual public Source::BaseView { class DiffView : virtual public Source::BaseView {
enum class ParseState {IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING}; enum class ParseState { IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING };
class Renderer : public Gsv::GutterRenderer { class Renderer : public Gsv::GutterRenderer {
public: public:
Renderer(); Renderer();
Glib::RefPtr<Gtk::TextTag> tag_added; Glib::RefPtr<Gtk::TextTag> tag_added;
Glib::RefPtr<Gtk::TextTag> tag_modified; Glib::RefPtr<Gtk::TextTag> tag_modified;
Glib::RefPtr<Gtk::TextTag> tag_removed; Glib::RefPtr<Gtk::TextTag> tag_removed;
Glib::RefPtr<Gtk::TextTag> tag_removed_below; Glib::RefPtr<Gtk::TextTag> tag_removed_below;
Glib::RefPtr<Gtk::TextTag> tag_removed_above; Glib::RefPtr<Gtk::TextTag> tag_removed_above;
protected: protected:
void draw_vfunc(const Cairo::RefPtr<Cairo::Context> &cr, const Gdk::Rectangle &background_area, void draw_vfunc(const Cairo::RefPtr<Cairo::Context> &cr, const Gdk::Rectangle &background_area,
const Gdk::Rectangle &cell_area, Gtk::TextIter &start, Gtk::TextIter &end, const Gdk::Rectangle &cell_area, Gtk::TextIter &start, Gtk::TextIter &end,
Gsv::GutterRendererState p6) override; Gsv::GutterRendererState p6) override;
}; };
public: public:
DiffView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language); DiffView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
~DiffView() override; ~DiffView() override;
void configure() override; void configure() override;
void rename(const boost::filesystem::path &path) override; void rename(const boost::filesystem::path &path) override;
void git_goto_next_diff(); void git_goto_next_diff();
std::string git_get_diff_details(); std::string git_get_diff_details();
/// Use canonical path to follow symbolic links /// Use canonical path to follow symbolic links
boost::filesystem::path canonical_file_path; boost::filesystem::path canonical_file_path;
private: private:
std::mutex canonical_file_path_mutex; std::mutex canonical_file_path_mutex;
std::unique_ptr<Renderer> renderer; std::unique_ptr<Renderer> renderer;
Dispatcher dispatcher; Dispatcher dispatcher;
std::shared_ptr<Git::Repository> repository; std::shared_ptr<Git::Repository> repository;
std::unique_ptr<Git::Repository::Diff> diff; std::unique_ptr<Git::Repository::Diff> diff;
std::unique_ptr<Git::Repository::Diff> get_diff(); std::unique_ptr<Git::Repository::Diff> get_diff();
std::thread parse_thread; std::thread parse_thread;
std::atomic<ParseState> parse_state; std::atomic<ParseState> parse_state;
std::mutex parse_mutex; std::mutex parse_mutex;
@ -62,8 +64,8 @@ namespace Source {
sigc::connection delayed_buffer_changed_connection; sigc::connection delayed_buffer_changed_connection;
sigc::connection delayed_monitor_changed_connection; sigc::connection delayed_monitor_changed_connection;
std::atomic<bool> monitor_changed; std::atomic<bool> monitor_changed;
Git::Repository::Diff::Lines lines; Git::Repository::Diff::Lines lines;
void update_lines(); void update_lines();
}; };
} } // namespace Source

1160
src/source_language_protocol.cc

File diff suppressed because it is too large Load Diff

28
src/source_language_protocol.h

@ -4,10 +4,10 @@
#include "source.h" #include "source.h"
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <list> #include <list>
#include <mutex>
#include <sstream>
#include <map> #include <map>
#include <mutex>
#include <set> #include <set>
#include <sstream>
namespace Source { namespace Source {
class LanguageProtocolView; class LanguageProtocolView;
@ -48,7 +48,7 @@ namespace LanguageProtocol {
std::set<Source::LanguageProtocolView *> views; std::set<Source::LanguageProtocolView *> views;
std::mutex views_mutex; std::mutex views_mutex;
std::mutex initialize_mutex; std::mutex initialize_mutex;
std::unique_ptr<TinyProcessLib::Process> process; std::unique_ptr<TinyProcessLib::Process> process;
@ -61,7 +61,7 @@ namespace LanguageProtocol {
size_t message_id = 1; size_t message_id = 1;
std::map<size_t, std::pair<Source::LanguageProtocolView*, std::function<void(const boost::property_tree::ptree &, bool error)>>> handlers; std::map<size_t, std::pair<Source::LanguageProtocolView *, std::function<void(const boost::property_tree::ptree &, bool error)>>> handlers;
std::vector<std::thread> timeout_threads; std::vector<std::thread> timeout_threads;
std::mutex timeout_threads_mutex; std::mutex timeout_threads_mutex;
@ -73,7 +73,7 @@ namespace LanguageProtocol {
bool initialized = false; bool initialized = false;
Capabilities initialize(Source::LanguageProtocolView *view); Capabilities initialize(Source::LanguageProtocolView *view);
void close(Source::LanguageProtocolView *view); void close(Source::LanguageProtocolView *view);
void parse_server_message(); void parse_server_message();
void write_request(Source::LanguageProtocolView *view, const std::string &method, const std::string &params, std::function<void(const boost::property_tree::ptree &, bool)> &&function = nullptr); void write_request(Source::LanguageProtocolView *view, const std::string &method, const std::string &params, std::function<void(const boost::property_tree::ptree &, bool)> &&function = nullptr);
void write_notification(const std::string &method, const std::string &params); void write_notification(const std::string &method, const std::string &params);
@ -87,11 +87,11 @@ namespace Source {
LanguageProtocolView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language, std::string language_id_); LanguageProtocolView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language, std::string language_id_);
~LanguageProtocolView() override; ~LanguageProtocolView() override;
std::string uri; std::string uri;
bool save() override; bool save() override;
void update_diagnostics(std::vector<LanguageProtocol::Diagnostic> &&diagnostics); void update_diagnostics(std::vector<LanguageProtocol::Diagnostic> &&diagnostics);
Gtk::TextIter get_iter_at_line_pos(int line, int pos) override; Gtk::TextIter get_iter_at_line_pos(int line, int pos) override;
protected: protected:
@ -100,21 +100,21 @@ namespace Source {
private: private:
std::string language_id; std::string language_id;
LanguageProtocol::Capabilities capabilities; LanguageProtocol::Capabilities capabilities;
std::shared_ptr<LanguageProtocol::Client> client; std::shared_ptr<LanguageProtocol::Client> client;
size_t document_version = 1; size_t document_version = 1;
std::thread initialize_thread; std::thread initialize_thread;
Dispatcher dispatcher; Dispatcher dispatcher;
void setup_navigation_and_refactoring(); void setup_navigation_and_refactoring();
void escape_text(std::string &text); void escape_text(std::string &text);
void unescape_text(std::string &text); void unescape_text(std::string &text);
void tag_similar_symbols(); void tag_similar_symbols();
Offset get_declaration(const Gtk::TextIter &iter); Offset get_declaration(const Gtk::TextIter &iter);
Autocomplete autocomplete; Autocomplete autocomplete;
@ -123,11 +123,11 @@ namespace Source {
std::vector<std::string> autocomplete_insert; std::vector<std::string> autocomplete_insert;
std::list<std::pair<Glib::RefPtr<Gtk::TextBuffer::Mark>, Glib::RefPtr<Gtk::TextBuffer::Mark>>> autocomplete_marks; std::list<std::pair<Glib::RefPtr<Gtk::TextBuffer::Mark>, Glib::RefPtr<Gtk::TextBuffer::Mark>>> autocomplete_marks;
bool autocomplete_keep_marks = false; bool autocomplete_keep_marks = false;
boost::filesystem::path flow_coverage_executable; boost::filesystem::path flow_coverage_executable;
std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark>>> flow_coverage_marks; std::vector<std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark>>> flow_coverage_marks;
const std::string flow_coverage_message="Not covered by Flow"; const std::string flow_coverage_message = "Not covered by Flow";
size_t num_warnings=0, num_errors=0, num_fix_its=0, num_flow_coverage_warnings=0; size_t num_warnings = 0, num_errors = 0, num_fix_its = 0, num_flow_coverage_warnings = 0;
void add_flow_coverage_tooltips(bool called_in_thread); void add_flow_coverage_tooltips(bool called_in_thread);
}; };
} // namespace Source } // namespace Source

398
src/source_spellcheck.cc

@ -4,170 +4,171 @@
#include "selection_dialog.h" #include "selection_dialog.h"
#include <iostream> #include <iostream>
AspellConfig* Source::SpellCheckView::spellcheck_config=nullptr; AspellConfig *Source::SpellCheckView::spellcheck_config = nullptr;
Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language): BaseView(file_path, language) { Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language) : BaseView(file_path, language) {
if(spellcheck_config==nullptr) if(spellcheck_config == nullptr)
spellcheck_config=new_aspell_config(); spellcheck_config = new_aspell_config();
spellcheck_checker=nullptr; spellcheck_checker = nullptr;
spellcheck_error_tag=get_buffer()->create_tag("spellcheck_error"); spellcheck_error_tag = get_buffer()->create_tag("spellcheck_error");
spellcheck_error_tag->property_underline()=Pango::Underline::UNDERLINE_ERROR; spellcheck_error_tag->property_underline() = Pango::Underline::UNDERLINE_ERROR;
signal_key_press_event().connect([](GdkEventKey *event) { signal_key_press_event().connect([](GdkEventKey *event) {
if(SelectionDialog::get() && SelectionDialog::get()->is_visible()) { if(SelectionDialog::get() && SelectionDialog::get()->is_visible()) {
if(SelectionDialog::get()->on_key_press(event)) if(SelectionDialog::get()->on_key_press(event))
return true; return true;
} }
return false; return false;
}, false); }, false);
//The following signal is added in case SpellCheckView is not subclassed //The following signal is added in case SpellCheckView is not subclassed
signal_key_press_event().connect([this](GdkEventKey *event) { signal_key_press_event().connect([this](GdkEventKey *event) {
last_keyval=event->keyval; last_keyval = event->keyval;
return false; return false;
}); });
get_buffer()->signal_changed().connect([this]() { get_buffer()->signal_changed().connect([this]() {
if(spellcheck_checker==nullptr) if(spellcheck_checker == nullptr)
return; return;
delayed_spellcheck_suggestions_connection.disconnect(); delayed_spellcheck_suggestions_connection.disconnect();
auto iter=get_buffer()->get_insert()->get_iter(); auto iter = get_buffer()->get_insert()->get_iter();
if(!is_word_iter(iter) && !iter.starts_line()) if(!is_word_iter(iter) && !iter.starts_line())
iter.backward_char(); iter.backward_char();
if(disable_spellcheck) { if(disable_spellcheck) {
if(is_word_iter(iter)) { if(is_word_iter(iter)) {
auto word=get_word(iter); auto word = get_word(iter);
get_buffer()->remove_tag(spellcheck_error_tag, word.first, word.second); get_buffer()->remove_tag(spellcheck_error_tag, word.first, word.second);
} }
return; return;
} }
if(!is_code_iter(iter)) { if(!is_code_iter(iter)) {
if(last_keyval==GDK_KEY_Return || last_keyval==GDK_KEY_KP_Enter) { if(last_keyval == GDK_KEY_Return || last_keyval == GDK_KEY_KP_Enter) {
auto previous_line_iter=iter; auto previous_line_iter = iter;
while(previous_line_iter.backward_char() && !previous_line_iter.ends_line()) {} while(previous_line_iter.backward_char() && !previous_line_iter.ends_line()) {
}
if(previous_line_iter.backward_char()) { if(previous_line_iter.backward_char()) {
get_buffer()->remove_tag(spellcheck_error_tag, previous_line_iter, iter); get_buffer()->remove_tag(spellcheck_error_tag, previous_line_iter, iter);
if(!is_code_iter(previous_line_iter)) { if(!is_code_iter(previous_line_iter)) {
auto word=get_word(previous_line_iter); auto word = get_word(previous_line_iter);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
} }
auto word=get_word(iter); auto word = get_word(iter);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
} }
} }
else { else {
auto previous_iter=iter; auto previous_iter = iter;
//When for instance using space to split two words //When for instance using space to split two words
if(!iter.starts_line() && !iter.ends_line() && is_word_iter(iter) && if(!iter.starts_line() && !iter.ends_line() && is_word_iter(iter) &&
previous_iter.backward_char() && !previous_iter.starts_line() && !is_word_iter(previous_iter)) { previous_iter.backward_char() && !previous_iter.starts_line() && !is_word_iter(previous_iter)) {
auto first=previous_iter; auto first = previous_iter;
if(first.backward_char()) { if(first.backward_char()) {
get_buffer()->remove_tag(spellcheck_error_tag, first, iter); get_buffer()->remove_tag(spellcheck_error_tag, first, iter);
auto word=get_word(first); auto word = get_word(first);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
word=get_word(iter); word = get_word(iter);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
} }
} }
else { else {
auto word=get_word(iter); auto word = get_word(iter);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
} }
} }
} }
delayed_spellcheck_error_clear.disconnect(); delayed_spellcheck_error_clear.disconnect();
delayed_spellcheck_error_clear=Glib::signal_timeout().connect([this]() { delayed_spellcheck_error_clear = Glib::signal_timeout().connect([this]() {
auto iter=get_buffer()->begin(); auto iter = get_buffer()->begin();
Gtk::TextIter begin_no_spellcheck_iter; Gtk::TextIter begin_no_spellcheck_iter;
if(spellcheck_all) { if(spellcheck_all) {
bool spell_check=!get_source_buffer()->iter_has_context_class(iter, "no-spell-check"); bool spell_check = !get_source_buffer()->iter_has_context_class(iter, "no-spell-check");
if(!spell_check) if(!spell_check)
begin_no_spellcheck_iter=iter; begin_no_spellcheck_iter = iter;
while(iter!=get_buffer()->end()) { while(iter != get_buffer()->end()) {
if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter, "no-spell-check")) if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter, "no-spell-check"))
iter=get_buffer()->end(); iter = get_buffer()->end();
spell_check=!spell_check; spell_check = !spell_check;
if(!spell_check) if(!spell_check)
begin_no_spellcheck_iter=iter; begin_no_spellcheck_iter = iter;
else else
get_buffer()->remove_tag(spellcheck_error_tag, begin_no_spellcheck_iter, iter); get_buffer()->remove_tag(spellcheck_error_tag, begin_no_spellcheck_iter, iter);
} }
return false; return false;
} }
bool spell_check=get_source_buffer()->iter_has_context_class(iter, "string") || get_source_buffer()->iter_has_context_class(iter, "comment"); bool spell_check = get_source_buffer()->iter_has_context_class(iter, "string") || get_source_buffer()->iter_has_context_class(iter, "comment");
if(!spell_check) if(!spell_check)
begin_no_spellcheck_iter=iter; begin_no_spellcheck_iter = iter;
while(iter!=get_buffer()->end()) { while(iter != get_buffer()->end()) {
auto iter1=iter; auto iter1 = iter;
auto iter2=iter; auto iter2 = iter;
if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter1, "string")) if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter1, "string"))
iter1=get_buffer()->end(); iter1 = get_buffer()->end();
if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter2, "comment")) if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter2, "comment"))
iter2=get_buffer()->end(); iter2 = get_buffer()->end();
if(iter2<iter1) if(iter2 < iter1)
iter=iter2; iter = iter2;
else else
iter=iter1; iter = iter1;
spell_check=!spell_check; spell_check = !spell_check;
if(!spell_check) if(!spell_check)
begin_no_spellcheck_iter=iter; begin_no_spellcheck_iter = iter;
else else
get_buffer()->remove_tag(spellcheck_error_tag, begin_no_spellcheck_iter, iter); get_buffer()->remove_tag(spellcheck_error_tag, begin_no_spellcheck_iter, iter);
} }
return false; return false;
}, 1000); }, 1000);
}); });
// In case of for instance text paste or undo/redo // In case of for instance text paste or undo/redo
get_buffer()->signal_insert().connect([this](const Gtk::TextIter &start_iter, const Glib::ustring &inserted_string, int) { get_buffer()->signal_insert().connect([this](const Gtk::TextIter &start_iter, const Glib::ustring &inserted_string, int) {
if(spellcheck_checker==nullptr) if(spellcheck_checker == nullptr)
return; return;
if(!disable_spellcheck) if(!disable_spellcheck)
return; return;
auto iter=start_iter; auto iter = start_iter;
if(!is_word_iter(iter) && !iter.starts_line()) if(!is_word_iter(iter) && !iter.starts_line())
iter.backward_char(); iter.backward_char();
if(is_word_iter(iter)) { if(is_word_iter(iter)) {
auto word=get_word(iter); auto word = get_word(iter);
get_buffer()->remove_tag(spellcheck_error_tag, word.first, word.second); get_buffer()->remove_tag(spellcheck_error_tag, word.first, word.second);
} }
}, false); }, false);
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iter, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) { get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator &iter, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) {
if(spellcheck_checker==nullptr) if(spellcheck_checker == nullptr)
return; return;
if(mark->get_name()=="insert") { if(mark->get_name() == "insert") {
if(SelectionDialog::get()) if(SelectionDialog::get())
SelectionDialog::get()->hide(); SelectionDialog::get()->hide();
delayed_spellcheck_suggestions_connection.disconnect(); delayed_spellcheck_suggestions_connection.disconnect();
delayed_spellcheck_suggestions_connection=Glib::signal_timeout().connect([this]() { delayed_spellcheck_suggestions_connection = Glib::signal_timeout().connect([this]() {
if(get_buffer()->get_insert()->get_iter().has_tag(spellcheck_error_tag)) { if(get_buffer()->get_insert()->get_iter().has_tag(spellcheck_error_tag)) {
SelectionDialog::create(this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()), false); SelectionDialog::create(this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()), false);
auto word=get_word(get_buffer()->get_insert()->get_iter()); auto word = get_word(get_buffer()->get_insert()->get_iter());
if(*word.first=='\'' && word.second.get_offset()-word.first.get_offset()>=3) { if(*word.first == '\'' && word.second.get_offset() - word.first.get_offset() >= 3) {
auto before_end=word.second; auto before_end = word.second;
if(before_end.backward_char() && *before_end=='\'') { if(before_end.backward_char() && *before_end == '\'') {
word.first.forward_char(); word.first.forward_char();
word.second.backward_char(); word.second.backward_char();
} }
} }
auto suggestions=get_spellcheck_suggestions(word.first, word.second); auto suggestions = get_spellcheck_suggestions(word.first, word.second);
if(suggestions.size()==0) if(suggestions.size() == 0)
return false; return false;
for(auto &suggestion: suggestions) for(auto &suggestion : suggestions)
SelectionDialog::get()->add_row(suggestion); SelectionDialog::get()->add_row(suggestion);
SelectionDialog::get()->on_select=[this, word](unsigned int index, const std::string &text, bool hide_window) { SelectionDialog::get()->on_select = [this, word](unsigned int index, const std::string &text, bool hide_window) {
get_buffer()->begin_user_action(); get_buffer()->begin_user_action();
get_buffer()->erase(word.first, word.second); get_buffer()->erase(word.first, word.second);
get_buffer()->insert(get_buffer()->get_insert()->get_iter(), text); get_buffer()->insert(get_buffer()->get_insert()->get_iter(), text);
@ -180,38 +181,38 @@ Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path,
}, 500); }, 500);
} }
}); });
get_buffer()->signal_mark_set().connect([](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) { get_buffer()->signal_mark_set().connect([](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) {
if(mark->get_name()=="insert") { if(mark->get_name() == "insert") {
if(SelectionDialog::get()) if(SelectionDialog::get())
SelectionDialog::get()->hide(); SelectionDialog::get()->hide();
} }
}); });
signal_focus_out_event().connect([this](GdkEventFocus* event) { signal_focus_out_event().connect([this](GdkEventFocus *event) {
delayed_spellcheck_suggestions_connection.disconnect(); delayed_spellcheck_suggestions_connection.disconnect();
return false; return false;
}); });
signal_leave_notify_event().connect([this](GdkEventCrossing*) { signal_leave_notify_event().connect([this](GdkEventCrossing *) {
delayed_spellcheck_suggestions_connection.disconnect(); delayed_spellcheck_suggestions_connection.disconnect();
return false; return false;
}); });
signal_tag_added_connection=get_buffer()->get_tag_table()->signal_tag_added().connect([this](const Glib::RefPtr<Gtk::TextTag> &tag) { signal_tag_added_connection = get_buffer()->get_tag_table()->signal_tag_added().connect([this](const Glib::RefPtr<Gtk::TextTag> &tag) {
if(tag->property_name()=="gtksourceview:context-classes:comment") if(tag->property_name() == "gtksourceview:context-classes:comment")
comment_tag=tag; comment_tag = tag;
else if(tag->property_name()=="gtksourceview:context-classes:string") else if(tag->property_name() == "gtksourceview:context-classes:string")
string_tag=tag; string_tag = tag;
else if(tag->property_name()=="gtksourceview:context-classes:no-spell-check") else if(tag->property_name() == "gtksourceview:context-classes:no-spell-check")
no_spell_check_tag=tag; no_spell_check_tag = tag;
}); });
signal_tag_removed_connection=get_buffer()->get_tag_table()->signal_tag_removed().connect([this](const Glib::RefPtr<Gtk::TextTag> &tag) { signal_tag_removed_connection = get_buffer()->get_tag_table()->signal_tag_removed().connect([this](const Glib::RefPtr<Gtk::TextTag> &tag) {
if(tag->property_name()=="gtksourceview:context-classes:comment") if(tag->property_name() == "gtksourceview:context-classes:comment")
comment_tag.reset(); comment_tag.reset();
else if(tag->property_name()=="gtksourceview:context-classes:string") else if(tag->property_name() == "gtksourceview:context-classes:string")
string_tag.reset(); string_tag.reset();
else if(tag->property_name()=="gtksourceview:context-classes:no-spell-check") else if(tag->property_name() == "gtksourceview:context-classes:no-spell-check")
no_spell_check_tag.reset(); no_spell_check_tag.reset();
}); });
} }
@ -219,24 +220,24 @@ Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path,
Source::SpellCheckView::~SpellCheckView() { Source::SpellCheckView::~SpellCheckView() {
delayed_spellcheck_suggestions_connection.disconnect(); delayed_spellcheck_suggestions_connection.disconnect();
delayed_spellcheck_error_clear.disconnect(); delayed_spellcheck_error_clear.disconnect();
if(spellcheck_checker!=nullptr) if(spellcheck_checker != nullptr)
delete_aspell_speller(spellcheck_checker); delete_aspell_speller(spellcheck_checker);
signal_tag_added_connection.disconnect(); signal_tag_added_connection.disconnect();
signal_tag_removed_connection.disconnect(); signal_tag_removed_connection.disconnect();
} }
void Source::SpellCheckView::configure() { void Source::SpellCheckView::configure() {
if(Config::get().source.spellcheck_language.size()>0) { if(Config::get().source.spellcheck_language.size() > 0) {
aspell_config_replace(spellcheck_config, "lang", Config::get().source.spellcheck_language.c_str()); aspell_config_replace(spellcheck_config, "lang", Config::get().source.spellcheck_language.c_str());
aspell_config_replace(spellcheck_config, "encoding", "utf-8"); aspell_config_replace(spellcheck_config, "encoding", "utf-8");
} }
spellcheck_possible_err=new_aspell_speller(spellcheck_config); spellcheck_possible_err = new_aspell_speller(spellcheck_config);
if(spellcheck_checker!=nullptr) if(spellcheck_checker != nullptr)
delete_aspell_speller(spellcheck_checker); delete_aspell_speller(spellcheck_checker);
spellcheck_checker=nullptr; spellcheck_checker = nullptr;
if (aspell_error_number(spellcheck_possible_err) != 0) if(aspell_error_number(spellcheck_possible_err) != 0)
std::cerr << "Spell check error: " << aspell_error_message(spellcheck_possible_err) << std::endl; std::cerr << "Spell check error: " << aspell_error_message(spellcheck_possible_err) << std::endl;
else else
spellcheck_checker = to_aspell_speller(spellcheck_possible_err); spellcheck_checker = to_aspell_speller(spellcheck_possible_err);
@ -249,57 +250,57 @@ void Source::SpellCheckView::hide_dialogs() {
SelectionDialog::get()->hide(); SelectionDialog::get()->hide();
} }
void Source::SpellCheckView::spellcheck(const Gtk::TextIter& start, const Gtk::TextIter& end) { void Source::SpellCheckView::spellcheck(const Gtk::TextIter &start, const Gtk::TextIter &end) {
if(spellcheck_checker==nullptr) if(spellcheck_checker == nullptr)
return; return;
auto iter=start; auto iter = start;
while(iter && iter<end) { while(iter && iter < end) {
if(is_word_iter(iter)) { if(is_word_iter(iter)) {
auto word=get_word(iter); auto word = get_word(iter);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
iter=word.second; iter = word.second;
} }
iter.forward_char(); iter.forward_char();
} }
} }
void Source::SpellCheckView::spellcheck() { void Source::SpellCheckView::spellcheck() {
auto iter=get_buffer()->begin(); auto iter = get_buffer()->begin();
Gtk::TextIter begin_spellcheck_iter; Gtk::TextIter begin_spellcheck_iter;
if(spellcheck_all) { if(spellcheck_all) {
bool spell_check=!get_source_buffer()->iter_has_context_class(iter, "no-spell-check"); bool spell_check = !get_source_buffer()->iter_has_context_class(iter, "no-spell-check");
if(spell_check) if(spell_check)
begin_spellcheck_iter=iter; begin_spellcheck_iter = iter;
while(iter!=get_buffer()->end()) { while(iter != get_buffer()->end()) {
if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter, "no-spell-check")) if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter, "no-spell-check"))
iter=get_buffer()->end(); iter = get_buffer()->end();
spell_check=!spell_check; spell_check = !spell_check;
if(spell_check) if(spell_check)
begin_spellcheck_iter=iter; begin_spellcheck_iter = iter;
else else
spellcheck(begin_spellcheck_iter, iter); spellcheck(begin_spellcheck_iter, iter);
} }
} }
else { else {
bool spell_check=!is_code_iter(iter); bool spell_check = !is_code_iter(iter);
if(spell_check) if(spell_check)
begin_spellcheck_iter=iter; begin_spellcheck_iter = iter;
while(iter!=get_buffer()->end()) { while(iter != get_buffer()->end()) {
auto iter1=iter; auto iter1 = iter;
auto iter2=iter; auto iter2 = iter;
if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter1, "string")) if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter1, "string"))
iter1=get_buffer()->end(); iter1 = get_buffer()->end();
if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter2, "comment")) if(!get_source_buffer()->iter_forward_to_context_class_toggle(iter2, "comment"))
iter2=get_buffer()->end(); iter2 = get_buffer()->end();
if(iter2<iter1) if(iter2 < iter1)
iter=iter2; iter = iter2;
else else
iter=iter1; iter = iter1;
spell_check=!spell_check; spell_check = !spell_check;
if(spell_check) if(spell_check)
begin_spellcheck_iter=iter; begin_spellcheck_iter = iter;
else else
spellcheck(begin_spellcheck_iter, iter); spellcheck(begin_spellcheck_iter, iter);
} }
@ -311,32 +312,32 @@ void Source::SpellCheckView::remove_spellcheck_errors() {
} }
void Source::SpellCheckView::goto_next_spellcheck_error() { void Source::SpellCheckView::goto_next_spellcheck_error() {
auto iter=get_buffer()->get_insert()->get_iter(); auto iter = get_buffer()->get_insert()->get_iter();
auto insert_iter=iter; auto insert_iter = iter;
bool wrapped=false; bool wrapped = false;
iter.forward_char(); iter.forward_char();
while(!wrapped || iter<insert_iter) { while(!wrapped || iter < insert_iter) {
auto toggled_tags=iter.get_toggled_tags(); auto toggled_tags = iter.get_toggled_tags();
for(auto &toggled_tag: toggled_tags) { for(auto &toggled_tag : toggled_tags) {
if(toggled_tag==spellcheck_error_tag) { if(toggled_tag == spellcheck_error_tag) {
get_buffer()->place_cursor(iter); get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return; return;
} }
} }
iter.forward_char(); iter.forward_char();
if(!wrapped && iter==get_buffer()->end()) { if(!wrapped && iter == get_buffer()->end()) {
iter=get_buffer()->begin(); iter = get_buffer()->begin();
wrapped=true; wrapped = true;
} }
} }
Info::get().print("No spelling errors found in current buffer"); Info::get().print("No spelling errors found in current buffer");
} }
bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) { bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
if(*iter=='\'') { if(*iter == '\'') {
auto previous_iter=iter; auto previous_iter = iter;
if(!iter.starts_line() && previous_iter.backward_char() && *previous_iter=='\'') if(!iter.starts_line() && previous_iter.backward_char() && *previous_iter == '\'')
return false; return false;
} }
if(spellcheck_all) { if(spellcheck_all) {
@ -344,10 +345,10 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
return true; return true;
// workaround for gtksourceview bug // workaround for gtksourceview bug
if(iter.ends_line()) { if(iter.ends_line()) {
auto previous_iter=iter; auto previous_iter = iter;
if(previous_iter.backward_char()) { if(previous_iter.backward_char()) {
if(*previous_iter=='\'' || *previous_iter=='"') { if(*previous_iter == '\'' || *previous_iter == '"') {
auto next_iter=iter; auto next_iter = iter;
next_iter.forward_char(); next_iter.forward_char();
if(next_iter.begins_tag(no_spell_check_tag) || next_iter.is_end()) if(next_iter.begins_tag(no_spell_check_tag) || next_iter.is_end())
return true; return true;
@ -355,9 +356,9 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
} }
} }
// for example, mark first " as code iter in this case: r"" // for example, mark first " as code iter in this case: r""
if(*iter=='\'' || *iter=='"') { if(*iter == '\'' || *iter == '"') {
auto previous_iter=iter; auto previous_iter = iter;
if(previous_iter.backward_char() && *previous_iter!='\'' && *previous_iter!='\"' && previous_iter.ends_tag(no_spell_check_tag)) if(previous_iter.backward_char() && *previous_iter != '\'' && *previous_iter != '\"' && previous_iter.ends_tag(no_spell_check_tag))
return true; return true;
} }
return false; return false;
@ -367,14 +368,15 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
return false; return false;
//Exception at the end of /**/ //Exception at the end of /**/
else if(iter.ends_tag(comment_tag)) { else if(iter.ends_tag(comment_tag)) {
auto previous_iter=iter; auto previous_iter = iter;
if(previous_iter.backward_char() && *previous_iter=='/') { if(previous_iter.backward_char() && *previous_iter == '/') {
auto previous_previous_iter=previous_iter; auto previous_previous_iter = previous_iter;
if(previous_previous_iter.backward_char() && *previous_previous_iter=='*') { if(previous_previous_iter.backward_char() && *previous_previous_iter == '*') {
auto it=previous_iter; auto it = previous_iter;
while(!it.begins_tag(comment_tag) && it.backward_to_tag_toggle(comment_tag)) {} while(!it.begins_tag(comment_tag) && it.backward_to_tag_toggle(comment_tag)) {
auto next_iter=it; }
if(it.begins_tag(comment_tag) && next_iter.forward_char() && *it=='/' && *next_iter=='*' && previous_iter!=it) auto next_iter = it;
if(it.begins_tag(comment_tag) && next_iter.forward_char() && *it == '/' && *next_iter == '*' && previous_iter != it)
return true; return true;
} }
} }
@ -385,15 +387,16 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
if(iter.has_tag(string_tag)) { if(iter.has_tag(string_tag)) {
// When ending an open ''-string with ', the last '-iter is not correctly marked as end iter for string_tag // When ending an open ''-string with ', the last '-iter is not correctly marked as end iter for string_tag
// For instance 'test, when inserting ' at end, would lead to spellcheck error of test' // For instance 'test, when inserting ' at end, would lead to spellcheck error of test'
if(*iter=='\'') { if(*iter == '\'') {
long backslash_count=0; long backslash_count = 0;
auto it=iter; auto it = iter;
while(it.backward_char() && *it=='\\') while(it.backward_char() && *it == '\\')
++backslash_count; ++backslash_count;
if(backslash_count%2==0) { if(backslash_count % 2 == 0) {
auto it=iter; auto it = iter;
while(!it.begins_tag(string_tag) && it.backward_to_tag_toggle(string_tag)) {} while(!it.begins_tag(string_tag) && it.backward_to_tag_toggle(string_tag)) {
if(it.begins_tag(string_tag) && *it=='\'' && iter!=it) }
if(it.begins_tag(string_tag) && *it == '\'' && iter != it)
return true; return true;
} }
} }
@ -402,17 +405,18 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
} }
// If iter is at the end of string_tag, with exception of after " and ' // If iter is at the end of string_tag, with exception of after " and '
else if(iter.ends_tag(string_tag)) { else if(iter.ends_tag(string_tag)) {
auto previous_iter=iter; auto previous_iter = iter;
if(!iter.starts_line() && previous_iter.backward_char()) { if(!iter.starts_line() && previous_iter.backward_char()) {
if((*previous_iter=='"' || *previous_iter=='\'')) { if((*previous_iter == '"' || *previous_iter == '\'')) {
long backslash_count=0; long backslash_count = 0;
auto it=previous_iter; auto it = previous_iter;
while(it.backward_char() && *it=='\\') while(it.backward_char() && *it == '\\')
++backslash_count; ++backslash_count;
if(backslash_count%2==0) { if(backslash_count % 2 == 0) {
auto it=previous_iter; auto it = previous_iter;
while(!it.begins_tag(string_tag) && it.backward_to_tag_toggle(string_tag)) {} while(!it.begins_tag(string_tag) && it.backward_to_tag_toggle(string_tag)) {
if(it.begins_tag(string_tag) && *previous_iter==*it && previous_iter!=it) }
if(it.begins_tag(string_tag) && *previous_iter == *it && previous_iter != it)
return true; return true;
} }
} }
@ -423,19 +427,19 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
return true; return true;
} }
bool Source::SpellCheckView::is_word_iter(const Gtk::TextIter& iter) { bool Source::SpellCheckView::is_word_iter(const Gtk::TextIter &iter) {
auto previous_iter=iter; auto previous_iter = iter;
size_t backslash_count=0; size_t backslash_count = 0;
while(previous_iter.backward_char() && *previous_iter=='\\') while(previous_iter.backward_char() && *previous_iter == '\\')
++backslash_count; ++backslash_count;
if(backslash_count%2==1) if(backslash_count % 2 == 1)
return false; return false;
if(((*iter>='A' && *iter<='Z') || (*iter>='a' && *iter<='z') || *iter>=128)) if(((*iter >= 'A' && *iter <= 'Z') || (*iter >= 'a' && *iter <= 'z') || *iter >= 128))
return true; return true;
if(*iter=='\'') { if(*iter == '\'') {
if(is_code_iter(iter)) if(is_code_iter(iter))
return false; return false;
auto next_iter=iter; auto next_iter = iter;
if(next_iter.forward_char() && is_code_iter(next_iter) && if(next_iter.forward_char() && is_code_iter(next_iter) &&
!(comment_tag && iter.ends_tag(comment_tag))) // additional check for end of line comment !(comment_tag && iter.ends_tag(comment_tag))) // additional check for end of line comment
return false; return false;
@ -445,11 +449,11 @@ bool Source::SpellCheckView::is_word_iter(const Gtk::TextIter& iter) {
} }
std::pair<Gtk::TextIter, Gtk::TextIter> Source::SpellCheckView::get_word(Gtk::TextIter iter) { std::pair<Gtk::TextIter, Gtk::TextIter> Source::SpellCheckView::get_word(Gtk::TextIter iter) {
auto start=iter; auto start = iter;
auto end=iter; auto end = iter;
while(is_word_iter(iter)) { while(is_word_iter(iter)) {
start=iter; start = iter;
if(!iter.backward_char()) if(!iter.backward_char())
break; break;
} }
@ -457,42 +461,42 @@ std::pair<Gtk::TextIter, Gtk::TextIter> Source::SpellCheckView::get_word(Gtk::Te
if(!end.forward_char()) if(!end.forward_char())
break; break;
} }
return {start, end}; return {start, end};
} }
void Source::SpellCheckView::spellcheck_word(Gtk::TextIter start, Gtk::TextIter end) { void Source::SpellCheckView::spellcheck_word(Gtk::TextIter start, Gtk::TextIter end) {
if(*start=='\'' && end.get_offset()-start.get_offset()>=3) { if(*start == '\'' && end.get_offset() - start.get_offset() >= 3) {
auto before_end=end; auto before_end = end;
if(before_end.backward_char() && *before_end=='\'') { if(before_end.backward_char() && *before_end == '\'') {
get_buffer()->remove_tag(spellcheck_error_tag, start, end); get_buffer()->remove_tag(spellcheck_error_tag, start, end);
start.forward_char(); start.forward_char();
end.backward_char(); end.backward_char();
} }
} }
auto word=get_buffer()->get_text(start, end); auto word = get_buffer()->get_text(start, end);
if(word.size()>0) { if(word.size() > 0) {
auto correct = aspell_speller_check(spellcheck_checker, word.data(), word.bytes()); auto correct = aspell_speller_check(spellcheck_checker, word.data(), word.bytes());
if(correct==0) if(correct == 0)
get_buffer()->apply_tag(spellcheck_error_tag, start, end); get_buffer()->apply_tag(spellcheck_error_tag, start, end);
else else
get_buffer()->remove_tag(spellcheck_error_tag, start, end); get_buffer()->remove_tag(spellcheck_error_tag, start, end);
} }
} }
std::vector<std::string> Source::SpellCheckView::get_spellcheck_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end) { std::vector<std::string> Source::SpellCheckView::get_spellcheck_suggestions(const Gtk::TextIter &start, const Gtk::TextIter &end) {
auto word_with_error=get_buffer()->get_text(start, end); auto word_with_error = get_buffer()->get_text(start, end);
const AspellWordList *suggestions = aspell_speller_suggest(spellcheck_checker, word_with_error.data(), word_with_error.bytes()); const AspellWordList *suggestions = aspell_speller_suggest(spellcheck_checker, word_with_error.data(), word_with_error.bytes());
AspellStringEnumeration *elements = aspell_word_list_elements(suggestions); AspellStringEnumeration *elements = aspell_word_list_elements(suggestions);
std::vector<std::string> words; std::vector<std::string> words;
const char *word; const char *word;
while ((word = aspell_string_enumeration_next(elements))!= nullptr) { while((word = aspell_string_enumeration_next(elements)) != nullptr) {
words.emplace_back(word); words.emplace_back(word);
} }
delete_aspell_string_enumeration(elements); delete_aspell_string_enumeration(elements);
return words; return words;
} }

29
src/source_spellcheck.h

@ -7,38 +7,39 @@ namespace Source {
public: public:
SpellCheckView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language); SpellCheckView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
~SpellCheckView() override; ~SpellCheckView() override;
void configure() override; void configure() override;
void hide_dialogs() override; void hide_dialogs() override;
void spellcheck(); void spellcheck();
void remove_spellcheck_errors(); void remove_spellcheck_errors();
void goto_next_spellcheck_error(); void goto_next_spellcheck_error();
protected: protected:
bool is_code_iter(const Gtk::TextIter &iter); bool is_code_iter(const Gtk::TextIter &iter);
bool spellcheck_all=false; bool spellcheck_all = false;
guint last_keyval=0; guint last_keyval = 0;
Glib::RefPtr<Gtk::TextTag> comment_tag; Glib::RefPtr<Gtk::TextTag> comment_tag;
Glib::RefPtr<Gtk::TextTag> string_tag; Glib::RefPtr<Gtk::TextTag> string_tag;
Glib::RefPtr<Gtk::TextTag> no_spell_check_tag; Glib::RefPtr<Gtk::TextTag> no_spell_check_tag;
private: private:
Glib::RefPtr<Gtk::TextTag> spellcheck_error_tag; Glib::RefPtr<Gtk::TextTag> spellcheck_error_tag;
sigc::connection signal_tag_added_connection; sigc::connection signal_tag_added_connection;
sigc::connection signal_tag_removed_connection; sigc::connection signal_tag_removed_connection;
static AspellConfig* spellcheck_config; static AspellConfig *spellcheck_config;
AspellCanHaveError *spellcheck_possible_err; AspellCanHaveError *spellcheck_possible_err;
AspellSpeller *spellcheck_checker; AspellSpeller *spellcheck_checker;
bool is_word_iter(const Gtk::TextIter& iter); bool is_word_iter(const Gtk::TextIter &iter);
std::pair<Gtk::TextIter, Gtk::TextIter> get_word(Gtk::TextIter iter); std::pair<Gtk::TextIter, Gtk::TextIter> get_word(Gtk::TextIter iter);
void spellcheck_word(Gtk::TextIter start, Gtk::TextIter end); void spellcheck_word(Gtk::TextIter start, Gtk::TextIter end);
std::vector<std::string> get_spellcheck_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end); std::vector<std::string> get_spellcheck_suggestions(const Gtk::TextIter &start, const Gtk::TextIter &end);
sigc::connection delayed_spellcheck_suggestions_connection; sigc::connection delayed_spellcheck_suggestions_connection;
sigc::connection delayed_spellcheck_error_clear; sigc::connection delayed_spellcheck_error_clear;
void spellcheck(const Gtk::TextIter& start, const Gtk::TextIter& end); void spellcheck(const Gtk::TextIter &start, const Gtk::TextIter &end);
}; };
} } // namespace Source

329
src/terminal.cc

@ -1,79 +1,79 @@
#include "terminal.h" #include "terminal.h"
#include "config.h" #include "config.h"
#include "project.h" #include "filesystem.h"
#include "info.h" #include "info.h"
#include "notebook.h" #include "notebook.h"
#include "filesystem.h" #include "project.h"
#include <iostream> #include <iostream>
#include <regex> #include <regex>
#include <thread> #include <thread>
Terminal::Terminal() { Terminal::Terminal() {
set_editable(false); set_editable(false);
bold_tag=get_buffer()->create_tag(); bold_tag = get_buffer()->create_tag();
bold_tag->property_weight()=Pango::WEIGHT_ULTRAHEAVY; bold_tag->property_weight() = Pango::WEIGHT_ULTRAHEAVY;
link_tag=get_buffer()->create_tag(); link_tag = get_buffer()->create_tag();
link_tag->property_underline()=Pango::Underline::UNDERLINE_SINGLE; link_tag->property_underline() = Pango::Underline::UNDERLINE_SINGLE;
link_mouse_cursor=Gdk::Cursor::create(Gdk::CursorType::HAND1); link_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::HAND1);
default_mouse_cursor=Gdk::Cursor::create(Gdk::CursorType::XTERM); default_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::XTERM);
} }
int Terminal::process(const std::string &command, const boost::filesystem::path &path, bool use_pipes) { int Terminal::process(const std::string &command, const boost::filesystem::path &path, bool use_pipes) {
std::unique_ptr<TinyProcessLib::Process> process; std::unique_ptr<TinyProcessLib::Process> process;
if(use_pipes) if(use_pipes)
process=std::make_unique<TinyProcessLib::Process>(command, path.string(), [this](const char* bytes, size_t n) { process = std::make_unique<TinyProcessLib::Process>(command, path.string(), [this](const char *bytes, size_t n) {
async_print(std::string(bytes, n)); async_print(std::string(bytes, n));
}, [this](const char* bytes, size_t n) { }, [this](const char *bytes, size_t n) {
async_print(std::string(bytes, n), true); async_print(std::string(bytes, n), true);
}); });
else else
process=std::make_unique<TinyProcessLib::Process>(command, path.string()); process = std::make_unique<TinyProcessLib::Process>(command, path.string());
if(process->get_id()<=0) { if(process->get_id() <= 0) {
async_print("Error: failed to run command: " + command + "\n", true); async_print("Error: failed to run command: " + command + "\n", true);
return -1; return -1;
} }
return process->get_exit_status(); return process->get_exit_status();
} }
int Terminal::process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path, std::ostream *stderr_stream) { int Terminal::process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path, std::ostream *stderr_stream) {
TinyProcessLib::Process process(command, path.string(), [&stdout_stream](const char* bytes, size_t n) { TinyProcessLib::Process process(command, path.string(), [&stdout_stream](const char *bytes, size_t n) {
Glib::ustring umessage(std::string(bytes, n)); Glib::ustring umessage(std::string(bytes, n));
Glib::ustring::iterator iter; Glib::ustring::iterator iter;
while(!umessage.validate(iter)) { while(!umessage.validate(iter)) {
auto next_char_iter=iter; auto next_char_iter = iter;
next_char_iter++; next_char_iter++;
umessage.replace(iter, next_char_iter, "?"); umessage.replace(iter, next_char_iter, "?");
} }
stdout_stream.write(umessage.data(), n); stdout_stream.write(umessage.data(), n);
}, [this, stderr_stream](const char* bytes, size_t n) { }, [this, stderr_stream](const char *bytes, size_t n) {
if(stderr_stream) if(stderr_stream)
stderr_stream->write(bytes, n); stderr_stream->write(bytes, n);
else else
async_print(std::string(bytes, n), true); async_print(std::string(bytes, n), true);
}, true); }, true);
if(process.get_id()<=0) { if(process.get_id() <= 0) {
async_print("Error: failed to run command: " + command + "\n", true); async_print("Error: failed to run command: " + command + "\n", true);
return -1; return -1;
} }
char buffer[131072]; char buffer[131072];
for(;;) { for(;;) {
stdin_stream.readsome(buffer, 131072); stdin_stream.readsome(buffer, 131072);
auto read_n=stdin_stream.gcount(); auto read_n = stdin_stream.gcount();
if(read_n==0) if(read_n == 0)
break; break;
if(!process.write(buffer, read_n)) { if(!process.write(buffer, read_n)) {
break; break;
} }
} }
process.close_stdin(); process.close_stdin();
return process.get_exit_status(); return process.get_exit_status();
} }
@ -81,15 +81,15 @@ void Terminal::async_process(const std::string &command, const boost::filesystem
std::thread async_execute_thread([this, command, path, callback, quiet]() { std::thread async_execute_thread([this, command, path, callback, quiet]() {
std::unique_lock<std::mutex> processes_lock(processes_mutex); std::unique_lock<std::mutex> processes_lock(processes_mutex);
stdin_buffer.clear(); stdin_buffer.clear();
auto process=std::make_shared<TinyProcessLib::Process>(command, path.string(), [this, quiet](const char* bytes, size_t n) { auto process = std::make_shared<TinyProcessLib::Process>(command, path.string(), [this, quiet](const char *bytes, size_t n) {
if(!quiet) if(!quiet)
async_print(std::string(bytes, n)); async_print(std::string(bytes, n));
}, [this, quiet](const char* bytes, size_t n) { }, [this, quiet](const char *bytes, size_t n) {
if(!quiet) if(!quiet)
async_print(std::string(bytes, n), true); async_print(std::string(bytes, n), true);
}, true); }, true);
auto pid=process->get_id(); auto pid = process->get_id();
if (pid<=0) { if(pid <= 0) {
processes_lock.unlock(); processes_lock.unlock();
async_print("Error: failed to run command: " + command + "\n", true); async_print("Error: failed to run command: " + command + "\n", true);
if(callback) if(callback)
@ -100,19 +100,19 @@ void Terminal::async_process(const std::string &command, const boost::filesystem
processes.emplace_back(process); processes.emplace_back(process);
processes_lock.unlock(); processes_lock.unlock();
} }
auto exit_status=process->get_exit_status(); auto exit_status = process->get_exit_status();
processes_lock.lock(); processes_lock.lock();
for(auto it=processes.begin();it!=processes.end();it++) { for(auto it = processes.begin(); it != processes.end(); it++) {
if((*it)->get_id()==pid) { if((*it)->get_id() == pid) {
processes.erase(it); processes.erase(it);
break; break;
} }
} }
stdin_buffer.clear(); stdin_buffer.clear();
processes_lock.unlock(); processes_lock.unlock();
if(callback) if(callback)
callback(exit_status); callback(exit_status);
}); });
@ -129,7 +129,7 @@ void Terminal::kill_last_async_process(bool force) {
void Terminal::kill_async_processes(bool force) { void Terminal::kill_async_processes(bool force) {
std::unique_lock<std::mutex> lock(processes_mutex); std::unique_lock<std::mutex> lock(processes_mutex);
for(auto &process: processes) for(auto &process : processes)
process->kill(force); process->kill(force);
} }
@ -142,9 +142,9 @@ bool Terminal::on_motion_notify_event(GdkEventMotion *event) {
get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->set_cursor(link_mouse_cursor); get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->set_cursor(link_mouse_cursor);
else else
get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->set_cursor(default_mouse_cursor); get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->set_cursor(default_mouse_cursor);
// Workaround for drag-and-drop crash on MacOS // Workaround for drag-and-drop crash on MacOS
// TODO 2018: check if this bug has been fixed // TODO 2018: check if this bug has been fixed
#ifdef __APPLE__ #ifdef __APPLE__
if((event->state & GDK_BUTTON1_MASK) == 0) if((event->state & GDK_BUTTON1_MASK) == 0)
return Gtk::TextView::on_motion_notify_event(event); return Gtk::TextView::on_motion_notify_event(event);
@ -159,133 +159,133 @@ bool Terminal::on_motion_notify_event(GdkEventMotion *event) {
#else #else
return Gtk::TextView::on_motion_notify_event(event); return Gtk::TextView::on_motion_notify_event(event);
#endif #endif
return Gtk::TextView::on_motion_notify_event(event); return Gtk::TextView::on_motion_notify_event(event);
} }
std::tuple<size_t, size_t, std::string, std::string, std::string> Terminal::find_link(const std::string &line) { std::tuple<size_t, size_t, std::string, std::string, std::string> Terminal::find_link(const std::string &line) {
const static std::regex link_regex("^([A-Z]:)?([^:]+):([0-9]+):([0-9]+): .*$|" //compile warning/error/rename usages const static std::regex link_regex("^([A-Z]:)?([^:]+):([0-9]+):([0-9]+): .*$|" //compile warning/error/rename usages
"^Assertion failed: .*file ([A-Z]:)?([^:]+), line ([0-9]+)\\.$|" //clang assert() "^Assertion failed: .*file ([A-Z]:)?([^:]+), line ([0-9]+)\\.$|" //clang assert()
"^[^:]*: ([A-Z]:)?([^:]+):([0-9]+): .* Assertion .* failed\\.$|" //gcc assert() "^[^:]*: ([A-Z]:)?([^:]+):([0-9]+): .* Assertion .* failed\\.$|" //gcc assert()
"^ERROR:([A-Z]:)?([^:]+):([0-9]+):.*$"); //g_assert (glib.h) "^ERROR:([A-Z]:)?([^:]+):([0-9]+):.*$"); //g_assert (glib.h)
size_t start_position=-1, end_position=-1; size_t start_position = -1, end_position = -1;
std::string path, line_number, line_offset; std::string path, line_number, line_offset;
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, link_regex)) { if(std::regex_match(line, sm, link_regex)) {
for(size_t sub=1;sub<link_regex.mark_count();) { for(size_t sub = 1; sub < link_regex.mark_count();) {
size_t subs=sub==1?4:3; size_t subs = sub == 1 ? 4 : 3;
if(sm.length(sub+1)) { if(sm.length(sub + 1)) {
start_position=sm.position(sub+1)-sm.length(sub); start_position = sm.position(sub + 1) - sm.length(sub);
end_position=sm.position(sub+subs-1)+sm.length(sub+subs-1); end_position = sm.position(sub + subs - 1) + sm.length(sub + subs - 1);
if(sm.length(sub)) if(sm.length(sub))
path+=sm[sub].str(); path += sm[sub].str();
path+=sm[sub+1].str(); path += sm[sub + 1].str();
line_number=sm[sub+2].str(); line_number = sm[sub + 2].str();
line_offset=subs==4?sm[sub+3].str():"1"; line_offset = subs == 4 ? sm[sub + 3].str() : "1";
break; break;
} }
sub+=subs; sub += subs;
} }
} }
return std::make_tuple(start_position, end_position, path, line_number, line_offset); return std::make_tuple(start_position, end_position, path, line_number, line_offset);
} }
void Terminal::apply_link_tags(const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter) { void Terminal::apply_link_tags(const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter) {
auto iter=start_iter; auto iter = start_iter;
Gtk::TextIter line_start; Gtk::TextIter line_start;
bool line_start_set=false; bool line_start_set = false;
bool delimiter_found=false; bool delimiter_found = false;
bool dot_found=false; bool dot_found = false;
bool number_found=false; bool number_found = false;
do { do {
if(iter.starts_line()) { if(iter.starts_line()) {
line_start=iter; line_start = iter;
line_start_set=true; line_start_set = true;
delimiter_found=false; delimiter_found = false;
dot_found=false; dot_found = false;
number_found=false; number_found = false;
} }
if(line_start_set && (*iter=='\\' || *iter=='/')) if(line_start_set && (*iter == '\\' || *iter == '/'))
delimiter_found=true; delimiter_found = true;
else if(line_start_set && *iter=='.') else if(line_start_set && *iter == '.')
dot_found=true; dot_found = true;
else if(line_start_set && (*iter>='0' && *iter<='9')) else if(line_start_set && (*iter >= '0' && *iter <= '9'))
number_found=true; number_found = true;
else if(line_start_set && delimiter_found && dot_found && number_found && iter.ends_line()) { else if(line_start_set && delimiter_found && dot_found && number_found && iter.ends_line()) {
auto line=get_buffer()->get_text(line_start, iter); auto line = get_buffer()->get_text(line_start, iter);
//Convert to ascii for std::regex and Gtk::Iter::forward_chars //Convert to ascii for std::regex and Gtk::Iter::forward_chars
for(size_t c=0;c<line.size();++c) { for(size_t c = 0; c < line.size(); ++c) {
if(line[c]>127) if(line[c] > 127)
line.replace(c, 1, "a"); line.replace(c, 1, "a");
} }
auto link=find_link(line.raw()); auto link = find_link(line.raw());
if(std::get<0>(link)!=static_cast<size_t>(-1)) { if(std::get<0>(link) != static_cast<size_t>(-1)) {
auto link_start=line_start; auto link_start = line_start;
auto link_end=line_start; auto link_end = line_start;
link_start.forward_chars(std::get<0>(link)); link_start.forward_chars(std::get<0>(link));
link_end.forward_chars(std::get<1>(link)); link_end.forward_chars(std::get<1>(link));
get_buffer()->apply_tag(link_tag, link_start, link_end); get_buffer()->apply_tag(link_tag, link_start, link_end);
} }
line_start_set=false; line_start_set = false;
} }
} while(iter.forward_char() && iter!=end_iter); } while(iter.forward_char() && iter != end_iter);
} }
size_t Terminal::print(const std::string &message, bool bold){ size_t Terminal::print(const std::string &message, bool bold) {
#ifdef _WIN32 #ifdef _WIN32
//Remove color codes //Remove color codes
auto message_no_color=message; //copy here since operations on Glib::ustring is too slow auto message_no_color = message; //copy here since operations on Glib::ustring is too slow
size_t pos=0; size_t pos = 0;
while((pos=message_no_color.find('\e', pos))!=std::string::npos) { while((pos = message_no_color.find('\e', pos)) != std::string::npos) {
if((pos+2)>=message_no_color.size()) if((pos + 2) >= message_no_color.size())
break; break;
if(message_no_color[pos+1]=='[') { if(message_no_color[pos + 1] == '[') {
size_t end_pos=pos+2; size_t end_pos = pos + 2;
bool color_code_found=false; bool color_code_found = false;
while(end_pos<message_no_color.size()) { while(end_pos < message_no_color.size()) {
if((message_no_color[end_pos]>='0' && message_no_color[end_pos]<='9') || message_no_color[end_pos]==';') if((message_no_color[end_pos] >= '0' && message_no_color[end_pos] <= '9') || message_no_color[end_pos] == ';')
end_pos++; end_pos++;
else if(message_no_color[end_pos]=='m') { else if(message_no_color[end_pos] == 'm') {
color_code_found=true; color_code_found = true;
break; break;
} }
else else
break; break;
} }
if(color_code_found) if(color_code_found)
message_no_color.erase(pos, end_pos-pos+1); message_no_color.erase(pos, end_pos - pos + 1);
} }
} }
Glib::ustring umessage=message_no_color; Glib::ustring umessage = message_no_color;
#else #else
Glib::ustring umessage=message; Glib::ustring umessage = message;
#endif #endif
Glib::ustring::iterator iter; Glib::ustring::iterator iter;
while(!umessage.validate(iter)) { while(!umessage.validate(iter)) {
auto next_char_iter=iter; auto next_char_iter = iter;
next_char_iter++; next_char_iter++;
umessage.replace(iter, next_char_iter, "?"); umessage.replace(iter, next_char_iter, "?");
} }
auto start_mark=get_buffer()->create_mark(get_buffer()->get_iter_at_line(get_buffer()->end().get_line())); auto start_mark = get_buffer()->create_mark(get_buffer()->get_iter_at_line(get_buffer()->end().get_line()));
if(bold) if(bold)
get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag); get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag);
else else
get_buffer()->insert(get_buffer()->end(), umessage); get_buffer()->insert(get_buffer()->end(), umessage);
auto start_iter=start_mark->get_iter(); auto start_iter = start_mark->get_iter();
get_buffer()->delete_mark(start_mark); get_buffer()->delete_mark(start_mark);
auto end_iter=get_buffer()->get_insert()->get_iter(); auto end_iter = get_buffer()->get_insert()->get_iter();
apply_link_tags(start_iter, end_iter); apply_link_tags(start_iter, end_iter);
if(get_buffer()->get_line_count()>Config::get().terminal.history_size) { if(get_buffer()->get_line_count() > Config::get().terminal.history_size) {
int lines=get_buffer()->get_line_count()-Config::get().terminal.history_size; int lines = get_buffer()->get_line_count() - Config::get().terminal.history_size;
get_buffer()->erase(get_buffer()->begin(), get_buffer()->get_iter_at_line(lines)); get_buffer()->erase(get_buffer()->begin(), get_buffer()->get_iter_at_line(lines));
deleted_lines+=static_cast<size_t>(lines); deleted_lines += static_cast<size_t>(lines);
} }
return static_cast<size_t>(get_buffer()->end().get_line())+deleted_lines; return static_cast<size_t>(get_buffer()->end().get_line()) + deleted_lines;
} }
void Terminal::async_print(const std::string &message, bool bold) { void Terminal::async_print(const std::string &message, bool bold) {
@ -296,38 +296,39 @@ void Terminal::async_print(const std::string &message, bool bold) {
void Terminal::async_print(size_t line_nr, const std::string &message) { void Terminal::async_print(size_t line_nr, const std::string &message) {
dispatcher.post([this, line_nr, message] { dispatcher.post([this, line_nr, message] {
if(line_nr<deleted_lines) if(line_nr < deleted_lines)
return; return;
Glib::ustring umessage=message; Glib::ustring umessage = message;
Glib::ustring::iterator iter; Glib::ustring::iterator iter;
while(!umessage.validate(iter)) { while(!umessage.validate(iter)) {
auto next_char_iter=iter; auto next_char_iter = iter;
next_char_iter++; next_char_iter++;
umessage.replace(iter, next_char_iter, "?"); umessage.replace(iter, next_char_iter, "?");
} }
auto end_line_iter=get_buffer()->get_iter_at_line(static_cast<int>(line_nr-deleted_lines)); auto end_line_iter = get_buffer()->get_iter_at_line(static_cast<int>(line_nr - deleted_lines));
while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {} while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {
}
get_buffer()->insert(end_line_iter, umessage); get_buffer()->insert(end_line_iter, umessage);
}); });
} }
void Terminal::configure() { void Terminal::configure() {
link_tag->property_foreground_rgba()=get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_LINK); link_tag->property_foreground_rgba() = get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_LINK);
if(Config::get().terminal.font.size()>0) { if(Config::get().terminal.font.size() > 0) {
override_font(Pango::FontDescription(Config::get().terminal.font)); override_font(Pango::FontDescription(Config::get().terminal.font));
} }
else if(Config::get().source.font.size()>0) { else if(Config::get().source.font.size() > 0) {
Pango::FontDescription font_description(Config::get().source.font); Pango::FontDescription font_description(Config::get().source.font);
auto font_description_size=font_description.get_size(); auto font_description_size = font_description.get_size();
if(font_description_size==0) { if(font_description_size == 0) {
Pango::FontDescription default_font_description(Gtk::Settings::get_default()->property_gtk_font_name()); Pango::FontDescription default_font_description(Gtk::Settings::get_default()->property_gtk_font_name());
font_description_size=default_font_description.get_size(); font_description_size = default_font_description.get_size();
} }
if(font_description_size>0) if(font_description_size > 0)
font_description.set_size(font_description_size*0.95); font_description.set_size(font_description_size * 0.95);
override_font(font_description); override_font(font_description);
} }
} }
@ -336,57 +337,59 @@ void Terminal::clear() {
get_buffer()->set_text(""); get_buffer()->set_text("");
} }
bool Terminal::on_button_press_event(GdkEventButton* button_event) { bool Terminal::on_button_press_event(GdkEventButton *button_event) {
//open clicked link in terminal //open clicked link in terminal
if(button_event->type==GDK_BUTTON_PRESS && button_event->button==GDK_BUTTON_PRIMARY) { if(button_event->type == GDK_BUTTON_PRESS && button_event->button == GDK_BUTTON_PRIMARY) {
Gtk::TextIter iter; Gtk::TextIter iter;
int location_x, location_y; int location_x, location_y;
window_to_buffer_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, button_event->x, button_event->y, 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); get_iter_at_location(iter, location_x, location_y);
if(iter.has_tag(link_tag)) { if(iter.has_tag(link_tag)) {
auto start_iter=get_buffer()->get_iter_at_line(iter.get_line()); auto start_iter = get_buffer()->get_iter_at_line(iter.get_line());
auto end_iter=start_iter; auto end_iter = start_iter;
while(!end_iter.ends_line() && end_iter.forward_char()) {} while(!end_iter.ends_line() && end_iter.forward_char()) {
auto link=find_link(get_buffer()->get_text(start_iter, end_iter).raw()); }
if(std::get<0>(link)!=static_cast<size_t>(-1)) { auto link = find_link(get_buffer()->get_text(start_iter, end_iter).raw());
boost::filesystem::path path=std::get<2>(link); if(std::get<0>(link) != static_cast<size_t>(-1)) {
std::string line=std::get<3>(link); boost::filesystem::path path = std::get<2>(link);
std::string index=std::get<4>(link); std::string line = std::get<3>(link);
std::string index = std::get<4>(link);
if(!path.empty() && *path.begin()=="~") { // boost::filesystem does not recognize ~
if(!path.empty() && *path.begin() == "~") { // boost::filesystem does not recognize ~
boost::filesystem::path corrected_path; boost::filesystem::path corrected_path;
corrected_path=filesystem::get_home_path(); corrected_path = filesystem::get_home_path();
if(!corrected_path.empty()) { if(!corrected_path.empty()) {
auto it=path.begin(); auto it = path.begin();
++it; ++it;
for(;it!=path.end();++it) for(; it != path.end(); ++it)
corrected_path/=*it; corrected_path /= *it;
path=corrected_path; path = corrected_path;
} }
} }
if(path.is_relative()) { if(path.is_relative()) {
if(Project::current) { if(Project::current) {
auto absolute_path=Project::current->build->get_default_path()/path; auto absolute_path = Project::current->build->get_default_path() / path;
if(boost::filesystem::exists(absolute_path)) if(boost::filesystem::exists(absolute_path))
path=absolute_path; path = absolute_path;
else else
path=Project::current->build->get_debug_path()/path; path = Project::current->build->get_debug_path() / path;
} }
else else
return Gtk::TextView::on_button_press_event(button_event); return Gtk::TextView::on_button_press_event(button_event);
} }
if(boost::filesystem::is_regular_file(path)) { if(boost::filesystem::is_regular_file(path)) {
Notebook::get().open(path); Notebook::get().open(path);
if(auto view=Notebook::get().get_current_view()) { if(auto view = Notebook::get().get_current_view()) {
try { try {
int line_int = std::stoi(line)-1; int line_int = std::stoi(line) - 1;
int index_int = std::stoi(index)-1; int index_int = std::stoi(index) - 1;
view->place_cursor_at_line_index(line_int, index_int); view->place_cursor_at_line_index(line_int, index_int);
view->scroll_to_cursor_delayed(view, true, true); view->scroll_to_cursor_delayed(view, true, true);
return true; return true;
} }
catch(...) {} catch(...) {
}
} }
} }
} }
@ -397,29 +400,29 @@ bool Terminal::on_button_press_event(GdkEventButton* button_event) {
bool Terminal::on_key_press_event(GdkEventKey *event) { bool Terminal::on_key_press_event(GdkEventKey *event) {
std::unique_lock<std::mutex> lock(processes_mutex); std::unique_lock<std::mutex> lock(processes_mutex);
bool debug_is_running=false; bool debug_is_running = false;
#ifdef JUCI_ENABLE_DEBUG #ifdef JUCI_ENABLE_DEBUG
debug_is_running=Project::current?Project::current->debug_is_running():false; debug_is_running = Project::current ? Project::current->debug_is_running() : false;
#endif #endif
if(processes.size()>0 || debug_is_running) { if(processes.size() > 0 || debug_is_running) {
auto unicode=gdk_keyval_to_unicode(event->keyval); auto unicode = gdk_keyval_to_unicode(event->keyval);
if(unicode>=32 && unicode!=126 && unicode!=0) { if(unicode >= 32 && unicode != 126 && unicode != 0) {
get_buffer()->place_cursor(get_buffer()->end()); get_buffer()->place_cursor(get_buffer()->end());
stdin_buffer+=unicode; stdin_buffer += unicode;
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size() - 1));
} }
else if(event->keyval==GDK_KEY_BackSpace) { else if(event->keyval == GDK_KEY_BackSpace) {
get_buffer()->place_cursor(get_buffer()->end()); get_buffer()->place_cursor(get_buffer()->end());
if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) { if(stdin_buffer.size() > 0 && get_buffer()->get_char_count() > 0) {
auto iter=get_buffer()->end(); auto iter = get_buffer()->end();
iter--; iter--;
stdin_buffer.erase(stdin_buffer.size()-1); stdin_buffer.erase(stdin_buffer.size() - 1);
get_buffer()->erase(iter, get_buffer()->end()); get_buffer()->erase(iter, get_buffer()->end());
} }
} }
else if(event->keyval==GDK_KEY_Return || event->keyval==GDK_KEY_KP_Enter) { else if(event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
get_buffer()->place_cursor(get_buffer()->end()); get_buffer()->place_cursor(get_buffer()->end());
stdin_buffer+='\n'; stdin_buffer += '\n';
if(debug_is_running) { if(debug_is_running) {
#ifdef JUCI_ENABLE_DEBUG #ifdef JUCI_ENABLE_DEBUG
Project::current->debug_write(stdin_buffer); Project::current->debug_write(stdin_buffer);
@ -427,7 +430,7 @@ bool Terminal::on_key_press_event(GdkEventKey *event) {
} }
else else
processes.back()->write(stdin_buffer); processes.back()->write(stdin_buffer);
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1)); get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size() - 1));
stdin_buffer.clear(); stdin_buffer.clear();
} }
} }

41
src/terminal.h

@ -1,46 +1,49 @@
#pragma once #pragma once
#include <mutex> #include "dispatcher.h"
#include <functional>
#include "gtkmm.h" #include "gtkmm.h"
#include "process.hpp"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <functional>
#include <iostream> #include <iostream>
#include "process.hpp" #include <mutex>
#include "dispatcher.h"
#include <tuple> #include <tuple>
class Terminal : public Gtk::TextView { class Terminal : public Gtk::TextView {
Terminal(); Terminal();
public: public:
static Terminal &get() { static Terminal &get() {
static Terminal singleton; static Terminal singleton;
return singleton; return singleton;
} }
int process(const std::string &command, const boost::filesystem::path &path="", bool use_pipes=true); int process(const std::string &command, const boost::filesystem::path &path = "", bool use_pipes = true);
int process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path="", std::ostream *stderr_stream=nullptr); int process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path = "", std::ostream *stderr_stream = nullptr);
void async_process(const std::string &command, const boost::filesystem::path &path="", const std::function<void(int exit_status)> &callback=nullptr, bool quiet=false); void async_process(const std::string &command, const boost::filesystem::path &path = "", const std::function<void(int exit_status)> &callback = nullptr, bool quiet = false);
void kill_last_async_process(bool force=false); void kill_last_async_process(bool force = false);
void kill_async_processes(bool force=false); void kill_async_processes(bool force = false);
size_t print(const std::string &message, bool bold=false); size_t print(const std::string &message, bool bold = false);
void async_print(const std::string &message, bool bold=false); void async_print(const std::string &message, bool bold = false);
void async_print(size_t line_nr, const std::string &message); void async_print(size_t line_nr, const std::string &message);
void configure(); void configure();
void clear(); void clear();
protected: protected:
bool on_motion_notify_event (GdkEventMotion* motion_event) override; bool on_motion_notify_event(GdkEventMotion *motion_event) override;
bool on_button_press_event(GdkEventButton* button_event) override; bool on_button_press_event(GdkEventButton *button_event) override;
bool on_key_press_event(GdkEventKey *event) override; bool on_key_press_event(GdkEventKey *event) override;
private: private:
Dispatcher dispatcher; Dispatcher dispatcher;
Glib::RefPtr<Gtk::TextTag> bold_tag; Glib::RefPtr<Gtk::TextTag> bold_tag;
Glib::RefPtr<Gtk::TextTag> link_tag; Glib::RefPtr<Gtk::TextTag> link_tag;
Glib::RefPtr<Gdk::Cursor> link_mouse_cursor; Glib::RefPtr<Gdk::Cursor> link_mouse_cursor;
Glib::RefPtr<Gdk::Cursor> default_mouse_cursor; Glib::RefPtr<Gdk::Cursor> default_mouse_cursor;
size_t deleted_lines=0; size_t deleted_lines = 0;
std::tuple<size_t, size_t, std::string, std::string, std::string> find_link(const std::string &line); std::tuple<size_t, size_t, std::string, std::string, std::string> find_link(const std::string &line);
void apply_link_tags(const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter); void apply_link_tags(const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter);

152
src/tooltips.cc

@ -1,8 +1,8 @@
#include "tooltips.h" #include "tooltips.h"
#include "selection_dialog.h" #include "selection_dialog.h"
std::set<Tooltip*> Tooltips::shown_tooltips; std::set<Tooltip *> Tooltips::shown_tooltips;
Gdk::Rectangle Tooltips::drawn_tooltips_rectangle=Gdk::Rectangle(); Gdk::Rectangle Tooltips::drawn_tooltips_rectangle = Gdk::Rectangle();
Tooltip::Tooltip(std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer_, Gtk::TextView *text_view, Tooltip::Tooltip(std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer_, Gtk::TextView *text_view,
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark_, Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark_) Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark_, Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark_)
@ -18,11 +18,11 @@ Tooltip::~Tooltip() {
void Tooltip::update() { void Tooltip::update() {
if(text_view) { if(text_view) {
auto iter=start_mark->get_iter(); auto iter = start_mark->get_iter();
auto end_iter=end_mark->get_iter(); auto end_iter = end_mark->get_iter();
text_view->get_iter_location(iter, activation_rectangle); text_view->get_iter_location(iter, activation_rectangle);
if(iter.get_offset()<end_iter.get_offset()) { if(iter.get_offset() < end_iter.get_offset()) {
while(iter.forward_char() && iter!=end_iter) { while(iter.forward_char() && iter != end_iter) {
Gdk::Rectangle rectangle; Gdk::Rectangle rectangle;
text_view->get_iter_location(iter, rectangle); text_view->get_iter_location(iter, rectangle);
activation_rectangle.join(rectangle); activation_rectangle.join(rectangle);
@ -37,164 +37,164 @@ void Tooltip::update() {
void Tooltip::show(bool disregard_drawn, const std::function<void()> &on_motion) { void Tooltip::show(bool disregard_drawn, const std::function<void()> &on_motion) {
Tooltips::shown_tooltips.emplace(this); Tooltips::shown_tooltips.emplace(this);
if(!window) { if(!window) {
//init window //init window
window=std::make_unique<Gtk::Window>(Gtk::WindowType::WINDOW_POPUP); window = std::make_unique<Gtk::Window>(Gtk::WindowType::WINDOW_POPUP);
auto g_application=g_application_get_default(); auto g_application = g_application_get_default();
auto gio_application=Glib::wrap(g_application, true); auto gio_application = Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application); auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
window->set_transient_for(*application->get_active_window()); window->set_transient_for(*application->get_active_window());
window->set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_TOOLTIP); window->set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_TOOLTIP);
window->set_events(Gdk::POINTER_MOTION_MASK); window->set_events(Gdk::POINTER_MOTION_MASK);
window->property_decorated()=false; window->property_decorated() = false;
window->set_accept_focus(false); window->set_accept_focus(false);
window->set_skip_taskbar_hint(true); window->set_skip_taskbar_hint(true);
window->set_default_size(0, 0); window->set_default_size(0, 0);
window->signal_motion_notify_event().connect([on_motion](GdkEventMotion *event) { window->signal_motion_notify_event().connect([on_motion](GdkEventMotion *event) {
if(on_motion) if(on_motion)
on_motion(); on_motion();
return false; return false;
}); });
window->get_style_context()->add_class("juci_tooltip_window"); window->get_style_context()->add_class("juci_tooltip_window");
auto visual = window->get_screen()->get_rgba_visual(); auto visual = window->get_screen()->get_rgba_visual();
if(visual) if(visual)
gtk_widget_set_visual(reinterpret_cast<GtkWidget*>(window->gobj()), visual->gobj()); gtk_widget_set_visual(reinterpret_cast<GtkWidget *>(window->gobj()), visual->gobj());
auto box=Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL)); auto box = Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL));
box->get_style_context()->add_class("juci_tooltip_box"); box->get_style_context()->add_class("juci_tooltip_box");
window->add(*box); window->add(*box);
text_buffer=create_tooltip_buffer(); text_buffer = create_tooltip_buffer();
wrap_lines(); wrap_lines();
auto tooltip_text_view=Gtk::manage(new Gtk::TextView(text_buffer)); auto tooltip_text_view = Gtk::manage(new Gtk::TextView(text_buffer));
tooltip_text_view->get_style_context()->add_class("juci_tooltip_text_view"); tooltip_text_view->get_style_context()->add_class("juci_tooltip_text_view");
tooltip_text_view->set_editable(false); tooltip_text_view->set_editable(false);
#if GTK_VERSION_GE(3, 20) #if GTK_VERSION_GE(3, 20)
box->add(*tooltip_text_view); box->add(*tooltip_text_view);
#else #else
auto box2=Gtk::manage(new Gtk::Box()); auto box2 = Gtk::manage(new Gtk::Box());
box2->pack_start(*tooltip_text_view, true, true, 3); box2->pack_start(*tooltip_text_view, true, true, 3);
box->pack_start(*box2, true, true, 3); box->pack_start(*box2, true, true, 3);
#endif #endif
auto layout=Pango::Layout::create(tooltip_text_view->get_pango_context()); auto layout = Pango::Layout::create(tooltip_text_view->get_pango_context());
layout->set_text(text_buffer->get_text()); layout->set_text(text_buffer->get_text());
layout->get_pixel_size(size.first, size.second); layout->get_pixel_size(size.first, size.second);
size.first+=6; // 2xpadding size.first += 6; // 2xpadding
size.second+=8; // 2xpadding + 2 size.second += 8; // 2xpadding + 2
window->signal_realize().connect([this] { window->signal_realize().connect([this] {
if(!text_view) { if(!text_view) {
auto &dialog=SelectionDialog::get(); auto &dialog = SelectionDialog::get();
if(dialog && dialog->is_visible()) { if(dialog && dialog->is_visible()) {
int root_x, root_y; int root_x, root_y;
dialog->get_position(root_x, root_y); dialog->get_position(root_x, root_y);
root_x-=3; // -1xpadding root_x -= 3; // -1xpadding
rectangle.set_x(root_x); rectangle.set_x(root_x);
rectangle.set_y(root_y-size.second); rectangle.set_y(root_y - size.second);
if(rectangle.get_y()<0) if(rectangle.get_y() < 0)
rectangle.set_y(0); rectangle.set_y(0);
} }
} }
window->move(rectangle.get_x(), rectangle.get_y()); window->move(rectangle.get_x(), rectangle.get_y());
}); });
} }
int root_x=0, root_y=0; int root_x = 0, root_y = 0;
if(text_view) { if(text_view) {
//Adjust if tooltip is left of text_view //Adjust if tooltip is left of text_view
Gdk::Rectangle visible_rect; Gdk::Rectangle visible_rect;
text_view->get_visible_rect(visible_rect); text_view->get_visible_rect(visible_rect);
int visible_x, visible_y; 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); 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); auto activation_rectangle_x = std::max(activation_rectangle.get_x(), visible_x);
text_view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(activation_rectangle_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);
root_x-=3; // -1xpadding root_x -= 3; // -1xpadding
if(root_y<size.second) if(root_y < size.second)
root_x+=visible_rect.get_width()*0.1; root_x += visible_rect.get_width() * 0.1;
} }
rectangle.set_x(root_x); rectangle.set_x(root_x);
rectangle.set_y(std::max(0, root_y-size.second)); rectangle.set_y(std::max(0, root_y - size.second));
rectangle.set_width(size.first); rectangle.set_width(size.first);
rectangle.set_height(size.second); rectangle.set_height(size.second);
if(!disregard_drawn) { if(!disregard_drawn) {
if(Tooltips::drawn_tooltips_rectangle.get_width()!=0) { if(Tooltips::drawn_tooltips_rectangle.get_width() != 0) {
if(rectangle.intersects(Tooltips::drawn_tooltips_rectangle)) { if(rectangle.intersects(Tooltips::drawn_tooltips_rectangle)) {
int new_y=Tooltips::drawn_tooltips_rectangle.get_y()-size.second; int new_y = Tooltips::drawn_tooltips_rectangle.get_y() - size.second;
if(new_y>=0) if(new_y >= 0)
rectangle.set_y(new_y); rectangle.set_y(new_y);
else else
rectangle.set_x(Tooltips::drawn_tooltips_rectangle.get_x()+Tooltips::drawn_tooltips_rectangle.get_width()+2); rectangle.set_x(Tooltips::drawn_tooltips_rectangle.get_x() + Tooltips::drawn_tooltips_rectangle.get_width() + 2);
} }
Tooltips::drawn_tooltips_rectangle.join(rectangle); Tooltips::drawn_tooltips_rectangle.join(rectangle);
} }
else else
Tooltips::drawn_tooltips_rectangle=rectangle; Tooltips::drawn_tooltips_rectangle = rectangle;
} }
if(window->get_realized()) if(window->get_realized())
window->move(rectangle.get_x(), rectangle.get_y()); window->move(rectangle.get_x(), rectangle.get_y());
window->show_all(); window->show_all();
shown=true; shown = true;
} }
void Tooltip::hide(const std::pair<int, int> &last_mouse_pos, const std::pair<int, int> &mouse_pos) { void Tooltip::hide(const std::pair<int, int> &last_mouse_pos, const std::pair<int, int> &mouse_pos) {
// Keep tooltip if mouse is moving towards it // Keep tooltip if mouse is moving towards it
// Calculated using dot product between the mouse_pos vector and the corners of the tooltip window // Calculated using dot product between the mouse_pos vector and the corners of the tooltip window
if(text_view && window && shown && last_mouse_pos.first!=-1 && last_mouse_pos.second!=-1 && mouse_pos.first!=-1 && mouse_pos.second!=-1) { if(text_view && window && shown && last_mouse_pos.first != -1 && last_mouse_pos.second != -1 && mouse_pos.first != -1 && mouse_pos.second != -1) {
static int root_x, root_y; static int root_x, root_y;
text_view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(last_mouse_pos.first, last_mouse_pos.second, root_x, root_y); text_view->get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(last_mouse_pos.first, last_mouse_pos.second, root_x, root_y);
int diff_x=mouse_pos.first-last_mouse_pos.first; int diff_x = mouse_pos.first - last_mouse_pos.first;
int diff_y=mouse_pos.second-last_mouse_pos.second; int diff_y = mouse_pos.second - last_mouse_pos.second;
class Corner { class Corner {
public: public:
Corner(int x, int y): x(x-root_x), y(y-root_y) {} Corner(int x, int y) : x(x - root_x), y(y - root_y) {}
int x, y; int x, y;
}; };
std::vector<Corner> corners; std::vector<Corner> corners;
corners.emplace_back(rectangle.get_x(), rectangle.get_y()); corners.emplace_back(rectangle.get_x(), rectangle.get_y());
corners.emplace_back(rectangle.get_x()+rectangle.get_width(), rectangle.get_y()); corners.emplace_back(rectangle.get_x() + rectangle.get_width(), rectangle.get_y());
corners.emplace_back(rectangle.get_x(), rectangle.get_y()+rectangle.get_height()); corners.emplace_back(rectangle.get_x(), rectangle.get_y() + rectangle.get_height());
corners.emplace_back(rectangle.get_x()+rectangle.get_width(), rectangle.get_y()+rectangle.get_height()); corners.emplace_back(rectangle.get_x() + rectangle.get_width(), rectangle.get_y() + rectangle.get_height());
for(auto &corner: corners) { for(auto &corner : corners) {
if(diff_x*corner.x + diff_y*corner.y >= 0) if(diff_x * corner.x + diff_y * corner.y >= 0)
return; return;
} }
} }
Tooltips::shown_tooltips.erase(this); Tooltips::shown_tooltips.erase(this);
if(window) if(window)
window->hide(); window->hide();
shown=false; shown = false;
} }
void Tooltip::wrap_lines() { void Tooltip::wrap_lines() {
if(!text_buffer) if(!text_buffer)
return; return;
auto iter=text_buffer->begin(); auto iter = text_buffer->begin();
while(iter) { while(iter) {
auto last_space=text_buffer->end(); auto last_space = text_buffer->end();
bool end=false; bool end = false;
for(unsigned c=0;c<=80;c++) { for(unsigned c = 0; c <= 80; c++) {
if(!iter) { if(!iter) {
end=true; end = true;
break; break;
} }
if(*iter==' ') if(*iter == ' ')
last_space=iter; last_space = iter;
if(*iter=='\n') { if(*iter == '\n') {
end=true; end = true;
iter.forward_char(); iter.forward_char();
break; break;
} }
@ -203,17 +203,17 @@ void Tooltip::wrap_lines() {
if(!end) { if(!end) {
while(!last_space && iter) { //If no space (word longer than 80) while(!last_space && iter) { //If no space (word longer than 80)
iter.forward_char(); iter.forward_char();
if(iter && *iter==' ') if(iter && *iter == ' ')
last_space=iter; last_space = iter;
} }
if(iter && last_space) { if(iter && last_space) {
auto mark=text_buffer->create_mark(last_space); auto mark = text_buffer->create_mark(last_space);
auto last_space_p=last_space; auto last_space_p = last_space;
last_space.forward_char(); last_space.forward_char();
text_buffer->erase(last_space_p, last_space); text_buffer->erase(last_space_p, last_space);
text_buffer->insert(mark->get_iter(), "\n"); text_buffer->insert(mark->get_iter(), "\n");
iter=mark->get_iter(); iter = mark->get_iter();
iter.forward_char(); iter.forward_char();
text_buffer->delete_mark(mark); text_buffer->delete_mark(mark);
@ -222,7 +222,7 @@ void Tooltip::wrap_lines() {
} }
} }
void Tooltips::show(const Gdk::Rectangle& rectangle, bool disregard_drawn) { void Tooltips::show(const Gdk::Rectangle &rectangle, bool disregard_drawn) {
for(auto &tooltip : tooltip_list) { for(auto &tooltip : tooltip_list) {
tooltip.update(); tooltip.update();
if(rectangle.intersects(tooltip.activation_rectangle)) if(rectangle.intersects(tooltip.activation_rectangle))

41
src/tooltips.h

@ -1,55 +1,56 @@
#pragma once #pragma once
#include "gtkmm.h" #include "gtkmm.h"
#include <string>
#include <list>
#include <functional> #include <functional>
#include <list>
#include <set> #include <set>
#include <string>
class Tooltip { class Tooltip {
public: public:
Tooltip(std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer_, Gtk::TextView *text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark_, Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark_); Tooltip(std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer_, Gtk::TextView *text_view, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark_, Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark_);
Tooltip(std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer_) : Tooltip(std::move(create_tooltip_buffer_), nullptr, Glib::RefPtr<Gtk::TextBuffer::Mark>(), Glib::RefPtr<Gtk::TextBuffer::Mark>()) {} Tooltip(std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer_) : Tooltip(std::move(create_tooltip_buffer_), nullptr, Glib::RefPtr<Gtk::TextBuffer::Mark>(), Glib::RefPtr<Gtk::TextBuffer::Mark>()) {}
~Tooltip(); ~Tooltip();
void update(); void update();
void show(bool disregard_drawn=false, const std::function<void()> &on_motion=nullptr); void show(bool disregard_drawn = false, const std::function<void()> &on_motion = nullptr);
void hide(const std::pair<int, int> &last_mouse_pos = {-1, -1}, const std::pair<int, int> &mouse_pos = {-1, -1}); void hide(const std::pair<int, int> &last_mouse_pos = {-1, -1}, const std::pair<int, int> &mouse_pos = {-1, -1});
Gdk::Rectangle activation_rectangle; Gdk::Rectangle activation_rectangle;
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark; Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark;
Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark; Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark;
Glib::RefPtr<Gtk::TextBuffer> text_buffer; Glib::RefPtr<Gtk::TextBuffer> text_buffer;
private: private:
std::unique_ptr<Gtk::Window> window; std::unique_ptr<Gtk::Window> window;
void wrap_lines(); void wrap_lines();
std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer; std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer;
Gtk::TextView *text_view; Gtk::TextView *text_view;
std::pair<int, int> size; std::pair<int, int> size;
Gdk::Rectangle rectangle; Gdk::Rectangle rectangle;
bool shown=false; bool shown = false;
}; };
class Tooltips { class Tooltips {
public: public:
static std::set<Tooltip*> shown_tooltips; static std::set<Tooltip *> shown_tooltips;
static Gdk::Rectangle drawn_tooltips_rectangle; static Gdk::Rectangle drawn_tooltips_rectangle;
static void init() {drawn_tooltips_rectangle=Gdk::Rectangle();} static void init() { drawn_tooltips_rectangle = Gdk::Rectangle(); }
void show(const Gdk::Rectangle& rectangle, bool disregard_drawn=false); void show(const Gdk::Rectangle &rectangle, bool disregard_drawn = false);
void show(bool disregard_drawn=false); void show(bool disregard_drawn = false);
void hide(const std::pair<int, int> &last_mouse_pos = {-1, -1}, const std::pair<int, int> &mouse_pos = {-1, -1}); void hide(const std::pair<int, int> &last_mouse_pos = {-1, -1}, const std::pair<int, int> &mouse_pos = {-1, -1});
void clear() {tooltip_list.clear();}; void clear() { tooltip_list.clear(); };
template<typename... Ts> template <typename... Ts>
void emplace_back(Ts&&... params) { void emplace_back(Ts &&... params) {
tooltip_list.emplace_back(std::forward<Ts>(params)...); tooltip_list.emplace_back(std::forward<Ts>(params)...);
} }
std::function<void()> on_motion; std::function<void()> on_motion;
private: private:
std::list<Tooltip> tooltip_list; std::list<Tooltip> tooltip_list;
}; };

22
src/usages_clang.cc

@ -91,8 +91,7 @@ Usages::Clang::Cache::Cache(boost::filesystem::path project_path_, boost::filesy
} }
} }
} }
}, }, &visitor_data);
&visitor_data);
} }
std::vector<std::pair<clangmm::Offset, clangmm::Offset>> Usages::Clang::Cache::get_similar_token_offsets(clangmm::Cursor::Kind kind, const std::string &spelling, std::vector<std::pair<clangmm::Offset, clangmm::Offset>> Usages::Clang::Cache::get_similar_token_offsets(clangmm::Cursor::Kind kind, const std::string &spelling,
@ -142,9 +141,9 @@ std::vector<Usages::Clang::Usages> Usages::Clang::get_usages(const boost::filesy
auto paths = find_paths(project_path, build_path, debug_path); auto paths = find_paths(project_path, build_path, debug_path);
auto pair = parse_paths(spelling, paths); auto pair = parse_paths(spelling, paths);
PathSet all_cursors_paths; PathSet all_cursors_paths;
auto canonical=cursor.get_canonical(); auto canonical = cursor.get_canonical();
all_cursors_paths.emplace(canonical.get_source_location().get_path()); all_cursors_paths.emplace(canonical.get_source_location().get_path());
for(auto &cursor: canonical.get_all_overridden_cursors()) for(auto &cursor : canonical.get_all_overridden_cursors())
all_cursors_paths.emplace(cursor.get_source_location().get_path()); all_cursors_paths.emplace(cursor.get_source_location().get_path());
auto pair2 = find_potential_paths(all_cursors_paths, project_path, pair.first, pair.second); auto pair2 = find_potential_paths(all_cursors_paths, project_path, pair.first, pair.second);
auto &potential_paths = pair2.first; auto &potential_paths = pair2.first;
@ -243,7 +242,7 @@ std::vector<Usages::Clang::Usages> Usages::Clang::get_usages(const boost::filesy
buffer.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>()); buffer.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
auto arguments = CompileCommands::get_arguments(build_path, path); auto arguments = CompileCommands::get_arguments(build_path, path);
arguments.emplace_back("-w"); // Disable all warnings arguments.emplace_back("-w"); // Disable all warnings
for(auto it = arguments.begin(); it != arguments.end();) { // remove comments from system headers for(auto it = arguments.begin(); it != arguments.end();) { // remove comments from system headers
if(*it == "-fretain-comments-from-system-headers") if(*it == "-fretain-comments-from-system-headers")
it = arguments.erase(it); it = arguments.erase(it);
@ -321,8 +320,7 @@ void Usages::Clang::cache(const boost::filesystem::path &project_path, const boo
visitor_data->paths.emplace(path); visitor_data->paths.emplace(path);
return CXChildVisit_Continue; return CXChildVisit_Continue;
}, }, &visitor_data);
&visitor_data);
visitor_data.paths.erase(path); visitor_data.paths.erase(path);
@ -632,14 +630,14 @@ std::pair<Usages::Clang::PathSet, Usages::Clang::PathSet> Usages::Clang::find_po
PathSet potential_paths; PathSet potential_paths;
PathSet all_includes; PathSet all_includes;
bool first=true; bool first = true;
for(auto &path: paths) { for(auto &path : paths) {
if(filesystem::file_in_path(path, project_path)) { if(filesystem::file_in_path(path, project_path)) {
for(auto &path_with_spelling : paths_with_spelling) { for(auto &path_with_spelling : paths_with_spelling) {
auto path_all_includes = get_all_includes(path_with_spelling, paths_includes); auto path_all_includes = get_all_includes(path_with_spelling, paths_includes);
if((path_all_includes.find(path) != path_all_includes.end() || path_with_spelling == path)) { if((path_all_includes.find(path) != path_all_includes.end() || path_with_spelling == path)) {
potential_paths.emplace(path_with_spelling); potential_paths.emplace(path_with_spelling);
for(auto &include : path_all_includes) for(auto &include : path_all_includes)
all_includes.emplace(include); all_includes.emplace(include);
} }
@ -649,12 +647,12 @@ std::pair<Usages::Clang::PathSet, Usages::Clang::PathSet> Usages::Clang::find_po
if(first) { if(first) {
for(auto &path_with_spelling : paths_with_spelling) { for(auto &path_with_spelling : paths_with_spelling) {
potential_paths.emplace(path_with_spelling); potential_paths.emplace(path_with_spelling);
auto path_all_includes = get_all_includes(path_with_spelling, paths_includes); auto path_all_includes = get_all_includes(path_with_spelling, paths_includes);
for(auto &include : path_all_includes) for(auto &include : path_all_includes)
all_includes.emplace(include); all_includes.emplace(include);
} }
first=false; first = false;
} }
} }
} }

1214
src/window.cc

File diff suppressed because it is too large Load Diff

11
src/window.h

@ -1,10 +1,11 @@
#pragma once #pragma once
#include <gtkmm.h>
#include <atomic> #include <atomic>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <gtkmm.h>
class Window : public Gtk::ApplicationWindow { class Window : public Gtk::ApplicationWindow {
Window(); Window();
public: public:
static Window &get() { static Window &get() {
static Window singleton; static Window singleton;
@ -20,7 +21,7 @@ protected:
private: private:
Gtk::AboutDialog about; Gtk::AboutDialog about;
Glib::RefPtr<Gtk::CssProvider> css_provider_theme; Glib::RefPtr<Gtk::CssProvider> css_provider_theme;
Glib::RefPtr<Gtk::CssProvider> css_provider_tooltips; Glib::RefPtr<Gtk::CssProvider> css_provider_tooltips;
@ -34,7 +35,7 @@ private:
std::string last_replace; std::string last_replace;
std::string last_run_command; std::string last_run_command;
std::string last_run_debug_command; std::string last_run_debug_command;
bool case_sensitive_search=true; bool case_sensitive_search = true;
bool regex_search=false; bool regex_search = false;
bool search_entry_shown=false; bool search_entry_shown = false;
}; };

104
tests/cmake_build_test.cc

@ -1,74 +1,74 @@
#include <glib.h>
#include "cmake.h" #include "cmake.h"
#include "project_build.h"
#include "config.h" #include "config.h"
#include <boost/filesystem.hpp>
#include "process.hpp" #include "process.hpp"
#include "project_build.h"
#include <boost/filesystem.hpp>
#include <glib.h>
#include <iostream> #include <iostream>
using namespace std; using namespace std;
int main() { int main() {
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
{ {
auto project_path=boost::filesystem::canonical(tests_path/".."); auto project_path = boost::filesystem::canonical(tests_path / "..");
{ {
CMake cmake(project_path); CMake cmake(project_path);
TinyProcessLib::Process process("cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..", (project_path/"build").string(), [](const char *bytes, size_t n) {}); TinyProcessLib::Process process("cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..", (project_path / "build").string(), [](const char *bytes, size_t n) {});
g_assert(process.get_exit_status()==0); g_assert(process.get_exit_status() == 0);
g_assert(cmake.get_executable(project_path/"build", project_path)==""); g_assert(cmake.get_executable(project_path / "build", project_path) == "");
g_assert(cmake.get_executable(project_path/"build"/"non_existing_file.cc", project_path)==""); g_assert(cmake.get_executable(project_path / "build" / "non_existing_file.cc", project_path) == "");
} }
{ {
CMake cmake(project_path/"src"); CMake cmake(project_path / "src");
g_assert(cmake.get_executable(project_path/"build", project_path/"src")==project_path/"build"/"src"/"juci"); g_assert(cmake.get_executable(project_path / "build", project_path / "src") == project_path / "build" / "src" / "juci");
g_assert(cmake.get_executable(project_path/"build", project_path/"src"/"cmake.cc")==project_path/"build"/"src"/"juci"); g_assert(cmake.get_executable(project_path / "build", project_path / "src" / "cmake.cc") == project_path / "build" / "src" / "juci");
g_assert(cmake.get_executable(project_path/"build", project_path/"src"/"juci.cc")==project_path/"build"/"src"/"juci"); g_assert(cmake.get_executable(project_path / "build", project_path / "src" / "juci.cc") == project_path / "build" / "src" / "juci");
g_assert(cmake.get_executable(project_path/"build", project_path/"src"/"non_existing_file.cc")==project_path/"build"/"src"/"juci"); g_assert(cmake.get_executable(project_path / "build", project_path / "src" / "non_existing_file.cc") == project_path / "build" / "src" / "juci");
} }
{ {
CMake cmake(tests_path); CMake cmake(tests_path);
g_assert(cmake.project_path==project_path); g_assert(cmake.project_path == project_path);
auto functions_parameters=cmake.get_functions_parameters("project"); auto functions_parameters = cmake.get_functions_parameters("project");
g_assert(functions_parameters.at(0).second.at(0)=="juci"); g_assert(functions_parameters.at(0).second.at(0) == "juci");
g_assert(cmake.get_executable(project_path/"build", tests_path).parent_path()==project_path/"build"/"tests"); g_assert(cmake.get_executable(project_path / "build", tests_path).parent_path() == project_path / "build" / "tests");
g_assert(cmake.get_executable(project_path/"build", tests_path/"cmake_build_test.cc")==project_path/"build"/"tests"/"cmake_build_test"); g_assert(cmake.get_executable(project_path / "build", tests_path / "cmake_build_test.cc") == project_path / "build" / "tests" / "cmake_build_test");
g_assert(cmake.get_executable(project_path/"build", tests_path/"non_existing_file.cc").parent_path()==project_path/"build"/"tests"); g_assert(cmake.get_executable(project_path / "build", tests_path / "non_existing_file.cc").parent_path() == project_path / "build" / "tests");
} }
auto build=Project::Build::create(tests_path); auto build = Project::Build::create(tests_path);
g_assert(dynamic_cast<Project::CMakeBuild*>(build.get())); g_assert(dynamic_cast<Project::CMakeBuild *>(build.get()));
build=Project::Build::create(tests_path/"stubs"); build = Project::Build::create(tests_path / "stubs");
g_assert(dynamic_cast<Project::CMakeBuild*>(build.get())); g_assert(dynamic_cast<Project::CMakeBuild *>(build.get()));
g_assert(build->project_path==project_path); g_assert(build->project_path == project_path);
Config::get().project.default_build_path="./build"; Config::get().project.default_build_path = "./build";
g_assert(build->get_default_path()==project_path/"build"); g_assert(build->get_default_path() == project_path / "build");
Config::get().project.debug_build_path="<default_build_path>/debug"; Config::get().project.debug_build_path = "<default_build_path>/debug";
g_assert(build->get_debug_path()==project_path/"build/debug"); g_assert(build->get_debug_path() == project_path / "build/debug");
auto project_path_filename=project_path.filename(); auto project_path_filename = project_path.filename();
Config::get().project.debug_build_path="../debug_<project_directory_name>"; Config::get().project.debug_build_path = "../debug_<project_directory_name>";
g_assert(build->get_debug_path()==project_path.parent_path()/("debug_"+project_path_filename.string())); g_assert(build->get_debug_path() == project_path.parent_path() / ("debug_" + project_path_filename.string()));
Config::get().project.default_build_path="../build_<project_directory_name>"; Config::get().project.default_build_path = "../build_<project_directory_name>";
g_assert(build->get_default_path()==project_path.parent_path()/("build_"+project_path_filename.string())); g_assert(build->get_default_path() == project_path.parent_path() / ("build_" + project_path_filename.string()));
} }
{ {
auto project_path=tests_path/"source_clang_test_files"; auto project_path = tests_path / "source_clang_test_files";
CMake cmake(project_path); CMake cmake(project_path);
g_assert(cmake.project_path==project_path); g_assert(cmake.project_path == project_path);
g_assert(cmake.get_executable(project_path/"build", project_path/"main.cpp")==boost::filesystem::path(".")/"test"); g_assert(cmake.get_executable(project_path / "build", project_path / "main.cpp") == boost::filesystem::path(".") / "test");
g_assert(cmake.get_executable(project_path/"build", project_path/"non_existing_file.cpp")==boost::filesystem::path(".")/"test"); g_assert(cmake.get_executable(project_path / "build", project_path / "non_existing_file.cpp") == boost::filesystem::path(".") / "test");
g_assert(cmake.get_executable(project_path/"build", project_path)==boost::filesystem::path(".")/"test"); g_assert(cmake.get_executable(project_path / "build", project_path) == boost::filesystem::path(".") / "test");
} }
} }

34
tests/compile_commands_test.cc

@ -2,35 +2,35 @@
#include <glib.h> #include <glib.h>
int main() { int main() {
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
{ {
CompileCommands compile_commands(tests_path/"meson_test_files"/"build"); CompileCommands compile_commands(tests_path / "meson_test_files" / "build");
g_assert(compile_commands.commands.at(0).directory=="jucipp/tests/meson_test_files/build"); g_assert(compile_commands.commands.at(0).directory == "jucipp/tests/meson_test_files/build");
g_assert_cmpuint(compile_commands.commands.size(), ==, 5); g_assert_cmpuint(compile_commands.commands.size(), ==, 5);
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(0).c_str(), ==, "te's\"t"); g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(0).c_str(), ==, "te's\"t");
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(1).c_str(), ==, "te st"); g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(1).c_str(), ==, "te st");
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(2).c_str(), ==, "test"); g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(2).c_str(), ==, "test");
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(3).c_str(), ==, "te\\st"); g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(3).c_str(), ==, "te\\st");
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(4).c_str(), ==, "te\\\\st"); g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(4).c_str(), ==, "te\\\\st");
auto parameter_values=compile_commands.commands.at(0).parameter_values("-o"); auto parameter_values = compile_commands.commands.at(0).parameter_values("-o");
g_assert_cmpuint(parameter_values.size(), ==, 1); g_assert_cmpuint(parameter_values.size(), ==, 1);
g_assert_cmpstr(parameter_values.at(0).c_str(), ==, "hello_lib@sta/main.cpp.o"); g_assert_cmpstr(parameter_values.at(0).c_str(), ==, "hello_lib@sta/main.cpp.o");
g_assert(boost::filesystem::canonical(compile_commands.commands.at(0).file) == tests_path/"meson_test_files"/"main.cpp"); g_assert(boost::filesystem::canonical(compile_commands.commands.at(0).file) == tests_path / "meson_test_files" / "main.cpp");
} }
{ {
CompileCommands compile_commands(tests_path/"source_clang_test_files"/"build"); CompileCommands compile_commands(tests_path / "source_clang_test_files" / "build");
g_assert(compile_commands.commands.at(0).directory=="."); g_assert(compile_commands.commands.at(0).directory == ".");
g_assert_cmpuint(compile_commands.commands.size(), ==, 1); g_assert_cmpuint(compile_commands.commands.size(), ==, 1);
g_assert_cmpstr(compile_commands.commands.at(0).parameters.at(2).c_str(), ==, "-Wall"); g_assert_cmpstr(compile_commands.commands.at(0).parameters.at(2).c_str(), ==, "-Wall");
} }
} }

70
tests/filesystem_test.cc

@ -3,80 +3,80 @@
int main() { int main() {
{ {
auto home_path=filesystem::get_home_path(); auto home_path = filesystem::get_home_path();
g_assert(!home_path.empty()); g_assert(!home_path.empty());
} }
{ {
auto original="test () '\""; auto original = "test () '\"";
auto escaped=filesystem::escape_argument(original); auto escaped = filesystem::escape_argument(original);
g_assert_cmpstr(escaped.c_str(), ==, "test\\ \\(\\)\\ \\'\\\""); g_assert_cmpstr(escaped.c_str(), ==, "test\\ \\(\\)\\ \\'\\\"");
auto unescaped=filesystem::unescape_argument(escaped); auto unescaped = filesystem::unescape_argument(escaped);
g_assert_cmpstr(unescaped.c_str(), ==, original); g_assert_cmpstr(unescaped.c_str(), ==, original);
} }
{ {
auto unescaped=filesystem::unescape_argument("'test \\()\"\\''"); auto unescaped = filesystem::unescape_argument("'test \\()\"\\''");
g_assert_cmpstr(unescaped.c_str(), ==, "test \\()\"'"); g_assert_cmpstr(unescaped.c_str(), ==, "test \\()\"'");
} }
{ {
auto unescaped=filesystem::unescape_argument("\"test \\'()\\\"\""); auto unescaped = filesystem::unescape_argument("\"test \\'()\\\"\"");
g_assert_cmpstr(unescaped.c_str(), ==, "test \\'()\""); g_assert_cmpstr(unescaped.c_str(), ==, "test \\'()\"");
} }
{ {
auto unescaped=filesystem::unescape_argument("\\\\"); auto unescaped = filesystem::unescape_argument("\\\\");
g_assert_cmpstr(unescaped.c_str(), ==, "\\"); g_assert_cmpstr(unescaped.c_str(), ==, "\\");
} }
{ {
auto unescaped=filesystem::unescape_argument("\\\\\\ "); auto unescaped = filesystem::unescape_argument("\\\\\\ ");
g_assert_cmpstr(unescaped.c_str(), ==, "\\ "); g_assert_cmpstr(unescaped.c_str(), ==, "\\ ");
} }
{ {
auto unescaped=filesystem::unescape_argument("\\\\\\ \\ \\ \\\\"); auto unescaped = filesystem::unescape_argument("\\\\\\ \\ \\ \\\\");
g_assert_cmpstr(unescaped.c_str(), ==, "\\ \\"); g_assert_cmpstr(unescaped.c_str(), ==, "\\ \\");
} }
{ {
auto unescaped=filesystem::unescape_argument("c:\\a\\ b\\c"); auto unescaped = filesystem::unescape_argument("c:\\a\\ b\\c");
g_assert_cmpstr(unescaped.c_str(), ==, "c:\\a b\\c"); g_assert_cmpstr(unescaped.c_str(), ==, "c:\\a b\\c");
} }
{ {
auto unescaped=filesystem::unescape_argument("\"\\\\\\\"\""); auto unescaped = filesystem::unescape_argument("\"\\\\\\\"\"");
g_assert_cmpstr(unescaped.c_str(), ==, "\\\""); g_assert_cmpstr(unescaped.c_str(), ==, "\\\"");
} }
{ {
auto unescaped=filesystem::unescape_argument("\"\\\"\""); auto unescaped = filesystem::unescape_argument("\"\\\"\"");
g_assert_cmpstr(unescaped.c_str(), ==, "\""); g_assert_cmpstr(unescaped.c_str(), ==, "\"");
} }
{ {
auto unescaped=filesystem::unescape_argument("\"a\\b\""); auto unescaped = filesystem::unescape_argument("\"a\\b\"");
g_assert_cmpstr(unescaped.c_str(), ==, "a\\b"); g_assert_cmpstr(unescaped.c_str(), ==, "a\\b");
} }
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
{ {
g_assert(filesystem::file_in_path(tests_path/"filesystem_test.cc", tests_path)); g_assert(filesystem::file_in_path(tests_path / "filesystem_test.cc", tests_path));
g_assert(!filesystem::file_in_path(boost::filesystem::canonical(tests_path/".."/"CMakeLists.txt"), tests_path)); g_assert(!filesystem::file_in_path(boost::filesystem::canonical(tests_path / ".." / "CMakeLists.txt"), tests_path));
} }
auto license_file=boost::filesystem::canonical(tests_path/".."/"LICENSE"); auto license_file = boost::filesystem::canonical(tests_path / ".." / "LICENSE");
{ {
g_assert(filesystem::find_file_in_path_parents("LICENSE", tests_path)==license_file); g_assert(filesystem::find_file_in_path_parents("LICENSE", tests_path) == license_file);
} }
{ {
g_assert(filesystem::get_normal_path(tests_path/".."/"LICENSE")==license_file); g_assert(filesystem::get_normal_path(tests_path / ".." / "LICENSE") == license_file);
g_assert(filesystem::get_normal_path("/foo")=="/foo"); g_assert(filesystem::get_normal_path("/foo") == "/foo");
g_assert(filesystem::get_normal_path("/foo/")=="/foo"); g_assert(filesystem::get_normal_path("/foo/") == "/foo");
g_assert(filesystem::get_normal_path("/foo/.")=="/foo"); g_assert(filesystem::get_normal_path("/foo/.") == "/foo");
g_assert(filesystem::get_normal_path("/foo/./bar/..////")=="/foo"); g_assert(filesystem::get_normal_path("/foo/./bar/..////") == "/foo");
g_assert(filesystem::get_normal_path("/foo/.///bar/../")=="/foo"); g_assert(filesystem::get_normal_path("/foo/.///bar/../") == "/foo");
g_assert(filesystem::get_normal_path("../foo")=="../foo"); g_assert(filesystem::get_normal_path("../foo") == "../foo");
g_assert(filesystem::get_normal_path("../../foo")=="../../foo"); g_assert(filesystem::get_normal_path("../../foo") == "../../foo");
g_assert(filesystem::get_normal_path("../././foo")=="../foo"); g_assert(filesystem::get_normal_path("../././foo") == "../foo");
g_assert(filesystem::get_normal_path("/../foo")=="/../foo"); g_assert(filesystem::get_normal_path("/../foo") == "/../foo");
} }
{ {
boost::filesystem::path relative_path="filesystem_test.cc"; boost::filesystem::path relative_path = "filesystem_test.cc";
g_assert(filesystem::get_relative_path(tests_path/relative_path, tests_path)==relative_path); g_assert(filesystem::get_relative_path(tests_path / relative_path, tests_path) == relative_path);
} }
} }

68
tests/git_test.cc

@ -1,25 +1,25 @@
#include <glib.h>
#include <gtkmm.h>
#include "git.h" #include "git.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <glib.h>
#include <gtkmm.h>
int main() { int main() {
auto app=Gtk::Application::create(); auto app = Gtk::Application::create();
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
auto jucipp_path=tests_path.parent_path(); auto jucipp_path = tests_path.parent_path();
auto git_path=jucipp_path/".git"; auto git_path = jucipp_path / ".git";
try { try {
auto repository=Git::get_repository(tests_path); auto repository = Git::get_repository(tests_path);
g_assert(repository->get_path()==git_path); g_assert(repository->get_path() == git_path);
g_assert(repository->get_work_path()==jucipp_path); g_assert(repository->get_work_path() == jucipp_path);
auto status=repository->get_status(); auto status = repository->get_status();
auto diff=repository->get_diff((boost::filesystem::path("tests")/"git_test.cc")); auto diff = repository->get_diff((boost::filesystem::path("tests") / "git_test.cc"));
auto lines=diff.get_lines("#include added\n#include <glib.h>\n#include modified\n#include \"git.h\"\n"); auto lines = diff.get_lines("#include added\n#include \"git.h\"\n#include modified\n#include <glib.h>\n");
g_assert_cmpuint(lines.added.size(), ==, 1); g_assert_cmpuint(lines.added.size(), ==, 1);
g_assert_cmpuint(lines.modified.size(), ==, 1); g_assert_cmpuint(lines.modified.size(), ==, 1);
g_assert_cmpuint(lines.removed.size(), ==, 1); g_assert_cmpuint(lines.removed.size(), ==, 1);
@ -28,31 +28,31 @@ int main() {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
return 1; return 1;
} }
try { try {
g_assert(Git::Repository::get_root_path(tests_path)==git_path); g_assert(Git::Repository::get_root_path(tests_path) == git_path);
} }
catch(const std::exception &e) { catch(const std::exception &e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
return 1; return 1;
} }
{ {
std::string old_text("line 1\nline2\n\nline4\n\n"); std::string old_text("line 1\nline2\n\nline4\n\n");
std::string new_text("line2\n\nline41\nline5\n\nline 5\nline 6\n"); std::string new_text("line2\n\nline41\nline5\n\nline 5\nline 6\n");
auto hunks=Git::Repository::Diff::get_hunks(old_text, new_text); auto hunks = Git::Repository::Diff::get_hunks(old_text, new_text);
assert(hunks.size()==3); assert(hunks.size() == 3);
assert(hunks[0].old_lines.first==1); assert(hunks[0].old_lines.first == 1);
assert(hunks[0].old_lines.second==1); assert(hunks[0].old_lines.second == 1);
assert(hunks[0].new_lines.first==0); assert(hunks[0].new_lines.first == 0);
assert(hunks[0].new_lines.second==0); assert(hunks[0].new_lines.second == 0);
assert(hunks[1].old_lines.first==4); assert(hunks[1].old_lines.first == 4);
assert(hunks[1].old_lines.second==1); assert(hunks[1].old_lines.second == 1);
assert(hunks[1].new_lines.first==3); assert(hunks[1].new_lines.first == 3);
assert(hunks[1].new_lines.second==2); assert(hunks[1].new_lines.second == 2);
assert(hunks[2].old_lines.first==5); assert(hunks[2].old_lines.first == 5);
assert(hunks[2].old_lines.second==0); assert(hunks[2].old_lines.second == 0);
assert(hunks[2].new_lines.first==6); assert(hunks[2].new_lines.first == 6);
assert(hunks[2].new_lines.second==2); assert(hunks[2].new_lines.second == 2);
} }
} }

160
tests/lldb_test.cc

@ -1,140 +1,140 @@
#include <glib.h>
#include "debug_lldb.h" #include "debug_lldb.h"
#include <thread>
#include <boost/filesystem.hpp>
#include <atomic> #include <atomic>
#include <boost/filesystem.hpp>
#include <glib.h>
#include <thread>
int main() { int main() {
auto build_path=boost::filesystem::canonical(JUCI_BUILD_PATH); auto build_path = boost::filesystem::canonical(JUCI_BUILD_PATH);
auto exec_path=build_path/"tests"/"lldb_test_files"/"lldb_test_executable"; auto exec_path = build_path / "tests" / "lldb_test_files" / "lldb_test_executable";
g_assert(boost::filesystem::exists(exec_path)); g_assert(boost::filesystem::exists(exec_path));
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
auto source_path=tests_path/"lldb_test_files"/"main.cpp"; auto source_path = tests_path / "lldb_test_files" / "main.cpp";
g_assert(boost::filesystem::exists(source_path)); g_assert(boost::filesystem::exists(source_path));
{ {
auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("\"~/test/te st\""); auto parsed_run_arguments = Debug::LLDB::get().parse_run_arguments("\"~/test/te st\"");
assert(std::get<0>(parsed_run_arguments).size()==0); assert(std::get<0>(parsed_run_arguments).size() == 0);
assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); assert(std::get<1>(parsed_run_arguments) == "~/test/te st");
assert(std::get<2>(parsed_run_arguments).size()==0); assert(std::get<2>(parsed_run_arguments).size() == 0);
} }
{ {
auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("~/test/te\\ st"); auto parsed_run_arguments = Debug::LLDB::get().parse_run_arguments("~/test/te\\ st");
assert(std::get<0>(parsed_run_arguments).size()==0); assert(std::get<0>(parsed_run_arguments).size() == 0);
assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); assert(std::get<1>(parsed_run_arguments) == "~/test/te st");
assert(std::get<2>(parsed_run_arguments).size()==0); assert(std::get<2>(parsed_run_arguments).size() == 0);
} }
{ {
auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("~/test/te\\ st Arg1\\\\ arg2"); auto parsed_run_arguments = Debug::LLDB::get().parse_run_arguments("~/test/te\\ st Arg1\\\\ arg2");
assert(std::get<0>(parsed_run_arguments).size()==0); assert(std::get<0>(parsed_run_arguments).size() == 0);
assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); assert(std::get<1>(parsed_run_arguments) == "~/test/te st");
assert(std::get<2>(parsed_run_arguments).size()==2); assert(std::get<2>(parsed_run_arguments).size() == 2);
assert(std::get<2>(parsed_run_arguments)[0]=="Arg1\\"); assert(std::get<2>(parsed_run_arguments)[0] == "Arg1\\");
assert(std::get<2>(parsed_run_arguments)[1]=="arg2"); assert(std::get<2>(parsed_run_arguments)[1] == "arg2");
} }
{ {
auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("~/test/te\\ st Arg1\\\\\\ arg1"); auto parsed_run_arguments = Debug::LLDB::get().parse_run_arguments("~/test/te\\ st Arg1\\\\\\ arg1");
assert(std::get<0>(parsed_run_arguments).size()==0); assert(std::get<0>(parsed_run_arguments).size() == 0);
assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); assert(std::get<1>(parsed_run_arguments) == "~/test/te st");
assert(std::get<2>(parsed_run_arguments).size()==1); assert(std::get<2>(parsed_run_arguments).size() == 1);
assert(std::get<2>(parsed_run_arguments)[0]=="Arg1\\ arg1"); assert(std::get<2>(parsed_run_arguments)[0] == "Arg1\\ arg1");
} }
{ {
auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("\"~/test/te st\" Arg1 \"Ar g2\" Ar\\ g3"); auto parsed_run_arguments = Debug::LLDB::get().parse_run_arguments("\"~/test/te st\" Arg1 \"Ar g2\" Ar\\ g3");
assert(std::get<0>(parsed_run_arguments).size()==0); assert(std::get<0>(parsed_run_arguments).size() == 0);
assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); assert(std::get<1>(parsed_run_arguments) == "~/test/te st");
assert(std::get<2>(parsed_run_arguments).size()==3); assert(std::get<2>(parsed_run_arguments).size() == 3);
assert(std::get<2>(parsed_run_arguments)[0]=="Arg1"); assert(std::get<2>(parsed_run_arguments)[0] == "Arg1");
assert(std::get<2>(parsed_run_arguments)[1]=="Ar g2"); assert(std::get<2>(parsed_run_arguments)[1] == "Ar g2");
assert(std::get<2>(parsed_run_arguments)[2]=="Ar g3"); assert(std::get<2>(parsed_run_arguments)[2] == "Ar g3");
} }
{ {
auto parsed_run_arguments=Debug::LLDB::get().parse_run_arguments("ENV1=Test ENV2=Te\\ st ENV3=\"te ts\" ~/test/te\\ st Arg1 \"Ar g2\" Ar\\ g3"); auto parsed_run_arguments = Debug::LLDB::get().parse_run_arguments("ENV1=Test ENV2=Te\\ st ENV3=\"te ts\" ~/test/te\\ st Arg1 \"Ar g2\" Ar\\ g3");
assert(std::get<0>(parsed_run_arguments).size()==3); assert(std::get<0>(parsed_run_arguments).size() == 3);
assert(std::get<0>(parsed_run_arguments)[0]=="ENV1=Test"); assert(std::get<0>(parsed_run_arguments)[0] == "ENV1=Test");
assert(std::get<0>(parsed_run_arguments)[1]=="ENV2=Te st"); assert(std::get<0>(parsed_run_arguments)[1] == "ENV2=Te st");
assert(std::get<0>(parsed_run_arguments)[2]=="ENV3=te ts"); assert(std::get<0>(parsed_run_arguments)[2] == "ENV3=te ts");
assert(std::get<1>(parsed_run_arguments)=="~/test/te st"); assert(std::get<1>(parsed_run_arguments) == "~/test/te st");
assert(std::get<2>(parsed_run_arguments).size()==3); assert(std::get<2>(parsed_run_arguments).size() == 3);
assert(std::get<2>(parsed_run_arguments)[0]=="Arg1"); assert(std::get<2>(parsed_run_arguments)[0] == "Arg1");
assert(std::get<2>(parsed_run_arguments)[1]=="Ar g2"); assert(std::get<2>(parsed_run_arguments)[1] == "Ar g2");
assert(std::get<2>(parsed_run_arguments)[2]=="Ar g3"); assert(std::get<2>(parsed_run_arguments)[2] == "Ar g3");
} }
std::vector<std::pair<boost::filesystem::path, int> > breakpoints; std::vector<std::pair<boost::filesystem::path, int>> breakpoints;
breakpoints.emplace_back(source_path, 2); breakpoints.emplace_back(source_path, 2);
std::atomic<bool> exited(false); std::atomic<bool> exited(false);
int exit_status; int exit_status;
std::atomic<int> line_nr(0); std::atomic<int> line_nr(0);
Debug::LLDB::get().on_exit.emplace_back([&](int exit_status_) { Debug::LLDB::get().on_exit.emplace_back([&](int exit_status_) {
exit_status=exit_status_; exit_status = exit_status_;
exited=true; exited = true;
}); });
Debug::LLDB::get().on_event.emplace_back([&](const lldb::SBEvent &event) { Debug::LLDB::get().on_event.emplace_back([&](const lldb::SBEvent &event) {
std::unique_lock<std::mutex> lock(Debug::LLDB::get().mutex); std::unique_lock<std::mutex> lock(Debug::LLDB::get().mutex);
auto process=lldb::SBProcess::GetProcessFromEvent(event); auto process = lldb::SBProcess::GetProcessFromEvent(event);
auto state=lldb::SBProcess::GetStateFromEvent(event); auto state = lldb::SBProcess::GetStateFromEvent(event);
if(state==lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
auto line_entry=process.GetSelectedThread().GetSelectedFrame().GetLineEntry(); auto line_entry = process.GetSelectedThread().GetSelectedFrame().GetLineEntry();
if(line_entry.IsValid()) { if(line_entry.IsValid()) {
lldb::SBStream stream; lldb::SBStream stream;
line_entry.GetFileSpec().GetDescription(stream); line_entry.GetFileSpec().GetDescription(stream);
line_nr=line_entry.GetLine(); line_nr = line_entry.GetLine();
} }
} }
}); });
Debug::LLDB::get().start(exec_path.string(), "", breakpoints); Debug::LLDB::get().start(exec_path.string(), "", breakpoints);
for(;;) { for(;;) {
if(exited) { if(exited) {
g_assert_cmpint(exit_status, ==, 0); g_assert_cmpint(exit_status, ==, 0);
break; break;
} }
else if(line_nr>0) { else if(line_nr > 0) {
for(;;) { for(;;) {
if(Debug::LLDB::get().is_stopped()) if(Debug::LLDB::get().is_stopped())
break; break;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
g_assert_cmpint(line_nr, ==, 2); g_assert_cmpint(line_nr, ==, 2);
g_assert(Debug::LLDB::get().get_backtrace().size()>0); g_assert(Debug::LLDB::get().get_backtrace().size() > 0);
auto variables=Debug::LLDB::get().get_variables(); auto variables = Debug::LLDB::get().get_variables();
g_assert_cmpstr(variables.at(0).name.c_str(), ==, "an_int"); g_assert_cmpstr(variables.at(0).name.c_str(), ==, "an_int");
line_nr=0; line_nr = 0;
Debug::LLDB::get().step_over(); Debug::LLDB::get().step_over();
for(;;) { for(;;) {
if(line_nr>0 && Debug::LLDB::get().is_stopped()) if(line_nr > 0 && Debug::LLDB::get().is_stopped())
break; break;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
g_assert_cmpint(line_nr, ==, 3); g_assert_cmpint(line_nr, ==, 3);
g_assert(Debug::LLDB::get().get_backtrace().size()>0); g_assert(Debug::LLDB::get().get_backtrace().size() > 0);
variables=Debug::LLDB::get().get_variables(); variables = Debug::LLDB::get().get_variables();
g_assert_cmpstr(variables.at(0).name.c_str(), ==, "an_int"); g_assert_cmpstr(variables.at(0).name.c_str(), ==, "an_int");
auto value=Debug::LLDB::get().get_value("an_int", source_path, 2, 7); auto value = Debug::LLDB::get().get_value("an_int", source_path, 2, 7);
g_assert_cmpuint(value.size(), >, 16); g_assert_cmpuint(value.size(), >, 16);
auto value_substr=value.substr(0, 16); auto value_substr = value.substr(0, 16);
g_assert_cmpstr(value_substr.c_str(), ==, "(int) an_int = 1"); g_assert_cmpstr(value_substr.c_str(), ==, "(int) an_int = 1");
line_nr=0; line_nr = 0;
Debug::LLDB::get().continue_debug(); Debug::LLDB::get().continue_debug();
} }
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
Debug::LLDB::get().cancel(); Debug::LLDB::get().cancel();
} }

2
tests/lldb_test_files/main.cpp

@ -1,4 +1,4 @@
int main() { int main() {
int an_int=1; int an_int = 1;
an_int++; an_int++;
} }

48
tests/meson_build_test.cc

@ -1,34 +1,34 @@
#include "meson.h" #include "meson.h"
#include <glib.h>
#include "project.h" #include "project.h"
#include <glib.h>
int main() { int main() {
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
auto meson_test_files_path=boost::filesystem::canonical(tests_path/"meson_test_files"); auto meson_test_files_path = boost::filesystem::canonical(tests_path / "meson_test_files");
{ {
Meson meson(meson_test_files_path/"a_subdir"); Meson meson(meson_test_files_path / "a_subdir");
g_assert(meson.project_path==meson_test_files_path); g_assert(meson.project_path == meson_test_files_path);
} }
{ {
Meson meson(meson_test_files_path); Meson meson(meson_test_files_path);
g_assert(meson.project_path==meson_test_files_path); g_assert(meson.project_path == meson_test_files_path);
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"main.cpp")==meson_test_files_path/"build"/"hello"); g_assert(meson.get_executable(meson_test_files_path / "build", meson_test_files_path / "main.cpp") == meson_test_files_path / "build" / "hello");
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"another_file.cpp")==meson_test_files_path/"build"/"another_executable"); g_assert(meson.get_executable(meson_test_files_path / "build", meson_test_files_path / "another_file.cpp") == meson_test_files_path / "build" / "another_executable");
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"a_subdir"/"main.cpp")==meson_test_files_path/"build"/"a_subdir"/"hello2"); g_assert(meson.get_executable(meson_test_files_path / "build", meson_test_files_path / "a_subdir" / "main.cpp") == meson_test_files_path / "build" / "a_subdir" / "hello2");
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"non_existing_file.cpp")==meson_test_files_path/"build"/"hello"); g_assert(meson.get_executable(meson_test_files_path / "build", meson_test_files_path / "non_existing_file.cpp") == meson_test_files_path / "build" / "hello");
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path)==meson_test_files_path/"build"/"hello"); g_assert(meson.get_executable(meson_test_files_path / "build", meson_test_files_path) == meson_test_files_path / "build" / "hello");
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"a_subdir")==meson_test_files_path/"build"/"a_subdir"/"hello2"); g_assert(meson.get_executable(meson_test_files_path / "build", meson_test_files_path / "a_subdir") == meson_test_files_path / "build" / "a_subdir" / "hello2");
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"a_subdir"/"non_existing_file.cpp")==meson_test_files_path/"build"/"a_subdir"/"hello2"); g_assert(meson.get_executable(meson_test_files_path / "build", meson_test_files_path / "a_subdir" / "non_existing_file.cpp") == meson_test_files_path / "build" / "a_subdir" / "hello2");
} }
auto build=Project::Build::create(meson_test_files_path); auto build = Project::Build::create(meson_test_files_path);
g_assert(dynamic_cast<Project::MesonBuild*>(build.get())); g_assert(dynamic_cast<Project::MesonBuild *>(build.get()));
build=Project::Build::create(meson_test_files_path/"a_subdir"); build = Project::Build::create(meson_test_files_path / "a_subdir");
g_assert(dynamic_cast<Project::MesonBuild*>(build.get())); g_assert(dynamic_cast<Project::MesonBuild *>(build.get()));
} }

38
tests/process_test.cc

@ -1,50 +1,50 @@
#include <glib.h>
#include "process.hpp" #include "process.hpp"
#include <glib.h>
int main() { int main() {
auto output=std::make_shared<std::string>(); auto output = std::make_shared<std::string>();
auto error=std::make_shared<std::string>(); auto error = std::make_shared<std::string>();
{ {
TinyProcessLib::Process process("echo Test", "", [output](const char *bytes, size_t n) { TinyProcessLib::Process process("echo Test", "", [output](const char *bytes, size_t n) {
*output+=std::string(bytes, n); *output += std::string(bytes, n);
}); });
g_assert(process.get_exit_status()==0); g_assert(process.get_exit_status() == 0);
g_assert(output->substr(0, 4)=="Test"); g_assert(output->substr(0, 4) == "Test");
output->clear(); output->clear();
} }
{ {
TinyProcessLib::Process process("echo Test && ls an_incorrect_path", "", [output](const char *bytes, size_t n) { TinyProcessLib::Process process("echo Test && ls an_incorrect_path", "", [output](const char *bytes, size_t n) {
*output+=std::string(bytes, n); *output += std::string(bytes, n);
}, [error](const char *bytes, size_t n) { }, [error](const char *bytes, size_t n) {
*error+=std::string(bytes, n); *error += std::string(bytes, n);
}); });
g_assert(process.get_exit_status()>0); g_assert(process.get_exit_status() > 0);
g_assert(output->substr(0, 4)=="Test"); g_assert(output->substr(0, 4) == "Test");
g_assert(!error->empty()); g_assert(!error->empty());
output->clear(); output->clear();
error->clear(); error->clear();
} }
{ {
TinyProcessLib::Process process("bash", "", [output](const char *bytes, size_t n) { TinyProcessLib::Process process("bash", "", [output](const char *bytes, size_t n) {
*output+=std::string(bytes, n); *output += std::string(bytes, n);
}, nullptr, true); }, nullptr, true);
process.write("echo Test\n"); process.write("echo Test\n");
process.write("exit\n"); process.write("exit\n");
g_assert(process.get_exit_status()==0); g_assert(process.get_exit_status() == 0);
g_assert(output->substr(0, 4)=="Test"); g_assert(output->substr(0, 4) == "Test");
output->clear(); output->clear();
} }
{ {
TinyProcessLib::Process process("cat", "", [output](const char *bytes, size_t n) { TinyProcessLib::Process process("cat", "", [output](const char *bytes, size_t n) {
*output+=std::string(bytes, n); *output += std::string(bytes, n);
}, nullptr, true); }, nullptr, true);
process.write("Test\n"); process.write("Test\n");
process.close_stdin(); process.close_stdin();
g_assert(process.get_exit_status()==0); g_assert(process.get_exit_status() == 0);
g_assert(output->substr(0, 4)=="Test"); g_assert(output->substr(0, 4) == "Test");
output->clear(); output->clear();
} }
} }

162
tests/source_clang_test.cc

@ -1,9 +1,9 @@
#include <glib.h>
#include "source_clang.h"
#include "config.h" #include "config.h"
#include "filesystem.h" #include "filesystem.h"
#include "source_clang.h"
#include <glib.h>
std::string main_error=R"(int main() { std::string main_error = R"(int main() {
int number=2; int number=2;
number=3 number=3
} }
@ -20,122 +20,122 @@ void flush_events() {
} }
int main() { int main() {
auto app=Gtk::Application::create(); auto app = Gtk::Application::create();
Gsv::init(); Gsv::init();
#ifdef _WIN32 #ifdef _WIN32
g_assert_cmpstr(std::getenv("MSYSTEM_PREFIX"), !=, nullptr); g_assert_cmpstr(std::getenv("MSYSTEM_PREFIX"), !=, nullptr);
#endif #endif
Config::get().project.default_build_path="./build"; Config::get().project.default_build_path = "./build";
Source::ClangView *clang_view=new Source::ClangView(boost::filesystem::canonical(std::string(JUCI_TESTS_PATH)+"/source_clang_test_files/main.cpp"), Source::ClangView *clang_view = new Source::ClangView(boost::filesystem::canonical(std::string(JUCI_TESTS_PATH) + "/source_clang_test_files/main.cpp"),
Gsv::LanguageManager::get_default()->get_language("cpp")); Gsv::LanguageManager::get_default()->get_language("cpp"));
while(!clang_view->parsed) while(!clang_view->parsed)
flush_events(); flush_events();
g_assert_cmpuint(clang_view->clang_diagnostics.size(), ==, 0); g_assert_cmpuint(clang_view->clang_diagnostics.size(), ==, 0);
//test get_declaration and get_implementation //test get_declaration and get_implementation
clang_view->place_cursor_at_line_index(15, 7); clang_view->place_cursor_at_line_index(15, 7);
auto location=clang_view->get_declaration_location(); auto location = clang_view->get_declaration_location();
g_assert_cmpuint(location.line, ==, 6); g_assert_cmpuint(location.line, ==, 6);
clang_view->place_cursor_at_line_index(location.line, location.index); clang_view->place_cursor_at_line_index(location.line, location.index);
auto impl_locations=clang_view->get_implementation_locations(); auto impl_locations = clang_view->get_implementation_locations();
g_assert_cmpuint(impl_locations.size(), ==, 1); g_assert_cmpuint(impl_locations.size(), ==, 1);
g_assert_cmpuint(impl_locations[0].line, ==, 11); g_assert_cmpuint(impl_locations[0].line, ==, 11);
clang_view->place_cursor_at_line_index(location.line, location.index); clang_view->place_cursor_at_line_index(location.line, location.index);
location=clang_view->get_declaration_location(); location = clang_view->get_declaration_location();
g_assert_cmpuint(location.line, ==, 6); g_assert_cmpuint(location.line, ==, 6);
//test get_usages and get_methods //test get_usages and get_methods
auto locations=clang_view->get_usages(); auto locations = clang_view->get_usages();
g_assert_cmpuint(locations.size(), >, 0); g_assert_cmpuint(locations.size(), >, 0);
locations=clang_view->get_methods(); locations = clang_view->get_methods();
g_assert_cmpuint(locations.size(), >, 0); g_assert_cmpuint(locations.size(), >, 0);
//Test rename class (error if not constructor and destructor is renamed as well) //Test rename class (error if not constructor and destructor is renamed as well)
auto saved_main=clang_view->get_buffer()->get_text(); auto saved_main = clang_view->get_buffer()->get_text();
clang_view->place_cursor_at_line_index(0, 6); clang_view->place_cursor_at_line_index(0, 6);
auto token=clang_view->get_token(clang_view->get_buffer()->get_insert()->get_iter()); auto token = clang_view->get_token(clang_view->get_buffer()->get_insert()->get_iter());
g_assert_cmpstr(token.c_str(), ==, "TestClass"); g_assert_cmpstr(token.c_str(), ==, "TestClass");
location=clang_view->get_declaration_location(); location = clang_view->get_declaration_location();
g_assert_cmpuint(location.line, ==, 0); g_assert_cmpuint(location.line, ==, 0);
clang_view->rename_similar_tokens("RenamedTestClass"); clang_view->rename_similar_tokens("RenamedTestClass");
while(!clang_view->parsed) while(!clang_view->parsed)
flush_events(); flush_events();
auto iter=clang_view->get_buffer()->get_insert()->get_iter(); auto iter = clang_view->get_buffer()->get_insert()->get_iter();
iter.backward_char(); iter.backward_char();
token=clang_view->get_token(iter); token = clang_view->get_token(iter);
g_assert_cmpstr(token.c_str(), ==, "RenamedTestClass"); g_assert_cmpstr(token.c_str(), ==, "RenamedTestClass");
g_assert_cmpuint(clang_view->clang_diagnostics.size(), ==, 0); g_assert_cmpuint(clang_view->clang_diagnostics.size(), ==, 0);
clang_view->get_buffer()->set_text(saved_main); clang_view->get_buffer()->set_text(saved_main);
clang_view->save(); clang_view->save();
//test error //test error
clang_view->get_buffer()->set_text(main_error); clang_view->get_buffer()->set_text(main_error);
while(!clang_view->parsed) while(!clang_view->parsed)
flush_events(); flush_events();
g_assert_cmpuint(clang_view->clang_diagnostics.size(), >, 0); g_assert_cmpuint(clang_view->clang_diagnostics.size(), >, 0);
g_assert_cmpuint(clang_view->get_fix_its().size(), >, 0); g_assert_cmpuint(clang_view->get_fix_its().size(), >, 0);
// test remove_include_guard // test remove_include_guard
{ {
clang_view->language=Gsv::LanguageManager::get_default()->get_language("chdr"); clang_view->language = Gsv::LanguageManager::get_default()->get_language("chdr");
std::string source="#ifndef F\n#define F\n#endif // F"; std::string source = "#ifndef F\n#define F\n#endif // F";
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n "); g_assert_cmpstr(source.c_str(), ==, " \n \n ");
source="#ifndef F\n#define F\n#endif // F\n"; source = "#ifndef F\n#define F\n#endif // F\n";
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n \n"); g_assert_cmpstr(source.c_str(), ==, " \n \n \n");
source="/*test*/\n#ifndef F\n#define F\n#endif // F\n"; source = "/*test*/\n#ifndef F\n#define F\n#endif // F\n";
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==,"/*test*/\n \n \n \n"); g_assert_cmpstr(source.c_str(), ==, "/*test*/\n \n \n \n");
source="//test\n#ifndef F\n#define F\n#endif // F\n"; source = "//test\n#ifndef F\n#define F\n#endif // F\n";
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==,"//test\n \n \n \n"); g_assert_cmpstr(source.c_str(), ==, "//test\n \n \n \n");
source="#ifndef F /*test*/\n#define F\n#endif // F"; source = "#ifndef F /*test*/\n#define F\n#endif // F";
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n "); g_assert_cmpstr(source.c_str(), ==, " \n \n ");
source="#ifndef F //test\n#define F\n#endif // F"; source = "#ifndef F //test\n#define F\n#endif // F";
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n "); g_assert_cmpstr(source.c_str(), ==, " \n \n ");
source="#ifndef F\n//test\n#define F\n#endif // F\n"; source = "#ifndef F\n//test\n#define F\n#endif // F\n";
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n//test\n \n \n"); g_assert_cmpstr(source.c_str(), ==, " \n//test\n \n \n");
source="#if !defined(F)\n#define F\n#endif\n"; source = "#if !defined(F)\n#define F\n#endif\n";
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(source.c_str(),==," \n \n \n"); g_assert_cmpstr(source.c_str(), ==, " \n \n \n");
source="#ifndef F\ntest\n#define F\n#endif // F\n"; source = "#ifndef F\ntest\n#define F\n#endif // F\n";
auto old_source=source; auto old_source = source;
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(old_source.c_str(),==,source.c_str()); g_assert_cmpstr(old_source.c_str(), ==, source.c_str());
source="test\n#ifndef F\n#define F\n#endif // F\n"; source = "test\n#ifndef F\n#define F\n#endif // F\n";
old_source=source; old_source = source;
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(old_source.c_str(),==,source.c_str()); g_assert_cmpstr(old_source.c_str(), ==, source.c_str());
source="#ifndef F\n#define F\n#endif // F\ntest\n"; source = "#ifndef F\n#define F\n#endif // F\ntest\n";
old_source=source; old_source = source;
clangmm::remove_include_guard(source); clangmm::remove_include_guard(source);
g_assert_cmpstr(old_source.c_str(),==,source.c_str()); g_assert_cmpstr(old_source.c_str(), ==, source.c_str());
} }
// Test Implement method // Test Implement method
{ {
clang_view->get_buffer()->set_text(R"(#include <string> clang_view->get_buffer()->set_text(R"(#include <string>
#include <vector>
#include <map> #include <map>
#include <vector>
namespace N { namespace N {
class T { class T {
@ -161,50 +161,50 @@ namespace N {
while(!clang_view->parsed) while(!clang_view->parsed)
flush_events(); flush_events();
g_assert_cmpuint(clang_view->clang_diagnostics.size(), ==, 0); g_assert_cmpuint(clang_view->clang_diagnostics.size(), ==, 0);
auto buffer=clang_view->get_buffer(); auto buffer = clang_view->get_buffer();
buffer->place_cursor(buffer->get_iter_at_line_offset(6, 9)); buffer->place_cursor(buffer->get_iter_at_line_offset(6, 9));
auto method=clang_view->get_method(); auto method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "void N::T::f1() {}"); g_assert_cmpstr(method.c_str(), ==, "void N::T::f1() {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(7, 9)); buffer->place_cursor(buffer->get_iter_at_line_offset(7, 9));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "void N::T::f1(T &t) {}"); g_assert_cmpstr(method.c_str(), ==, "void N::T::f1(T &t) {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(8, 9)); buffer->place_cursor(buffer->get_iter_at_line_offset(8, 9));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "void N::T::f1(const T &t) {}"); g_assert_cmpstr(method.c_str(), ==, "void N::T::f1(const T &t) {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(9, 9)); buffer->place_cursor(buffer->get_iter_at_line_offset(9, 9));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "void N::T::f1(std::vector<T> ts) {}"); g_assert_cmpstr(method.c_str(), ==, "void N::T::f1(std::vector<T> ts) {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(10, 9)); buffer->place_cursor(buffer->get_iter_at_line_offset(10, 9));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "void N::T::f1(const std::vector<T> &ts) {}"); g_assert_cmpstr(method.c_str(), ==, "void N::T::f1(const std::vector<T> &ts) {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(11, 16)); buffer->place_cursor(buffer->get_iter_at_line_offset(11, 16));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "std::string N::T::f2() {}"); g_assert_cmpstr(method.c_str(), ==, "std::string N::T::f2() {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(12, 23)); buffer->place_cursor(buffer->get_iter_at_line_offset(12, 23));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "const std::string &N::T::f3() {}"); g_assert_cmpstr(method.c_str(), ==, "const std::string &N::T::f3() {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(13, 19)); buffer->place_cursor(buffer->get_iter_at_line_offset(13, 19));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "std::vector<N::T> N::T::f4() {}"); g_assert_cmpstr(method.c_str(), ==, "std::vector<N::T> N::T::f4() {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(14, 20)); buffer->place_cursor(buffer->get_iter_at_line_offset(14, 20));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "std::vector<N::T *> N::T::f5() {}"); g_assert_cmpstr(method.c_str(), ==, "std::vector<N::T *> N::T::f5() {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(15, 20)); buffer->place_cursor(buffer->get_iter_at_line_offset(15, 20));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "std::vector<N::T &> N::T::f6() {}"); g_assert_cmpstr(method.c_str(), ==, "std::vector<N::T &> N::T::f6() {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(16, 29)); buffer->place_cursor(buffer->get_iter_at_line_offset(16, 29));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "std::map<N::T, std::string> N::T::f7() {}"); g_assert_cmpstr(method.c_str(), ==, "std::map<N::T, std::string> N::T::f7() {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(20, 6)); buffer->place_cursor(buffer->get_iter_at_line_offset(20, 6));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "std::map<N::T, std::string> N::T::f8() {}"); g_assert_cmpstr(method.c_str(), ==, "std::map<N::T, std::string> N::T::f8() {}");
buffer->place_cursor(buffer->get_iter_at_line_offset(21, 9)); buffer->place_cursor(buffer->get_iter_at_line_offset(21, 9));
method=clang_view->get_method(); method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "void N::T::f9() const {}"); g_assert_cmpstr(method.c_str(), ==, "void N::T::f9() const {}");
} }
clang_view->async_delete(); clang_view->async_delete();
clang_view->delete_thread.join(); clang_view->delete_thread.join();
flush_events(); flush_events();

4
tests/source_clang_test_files/main.cpp

@ -1,8 +1,8 @@
class TestClass { class TestClass {
public: public:
TestClass(); TestClass();
template<class T> template <class T>
TestClass(T t) {++t;} TestClass(T t) { ++t; }
~TestClass() {} ~TestClass() {}
void function(); void function();
}; };

180
tests/source_test.cc

@ -1,14 +1,14 @@
#include <glib.h>
#include "source.h"
#include "filesystem.h" #include "filesystem.h"
#include "source.h"
#include <glib.h>
std::string hello_world=R"(#include <iostream> std::string hello_world = R"(#include <iostream>
int main() { int main() {
std::cout << "hello world\n"; std::cout << "hello world\n";
})"; })";
std::string hello_world_cleaned=R"(#include <iostream> std::string hello_world_cleaned = R"(#include <iostream>
int main() { int main() {
std::cout << "hello world\n"; std::cout << "hello world\n";
@ -21,157 +21,157 @@ int main() {
//make test //make test
int main() { int main() {
auto app=Gtk::Application::create(); auto app = Gtk::Application::create();
Gsv::init(); Gsv::init();
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
auto source_file=tests_path/"tmp"/"source_file.cpp"; auto source_file = tests_path / "tmp" / "source_file.cpp";
{ {
Source::View source_view(source_file, Glib::RefPtr<Gsv::Language>()); Source::View source_view(source_file, Glib::RefPtr<Gsv::Language>());
source_view.get_buffer()->set_text(hello_world); source_view.get_buffer()->set_text(hello_world);
g_assert(source_view.save()); g_assert(source_view.save());
} }
Source::View source_view(source_file, Glib::RefPtr<Gsv::Language>()); Source::View source_view(source_file, Glib::RefPtr<Gsv::Language>());
g_assert(source_view.get_buffer()->get_text()==hello_world); g_assert(source_view.get_buffer()->get_text() == hello_world);
source_view.cleanup_whitespace_characters(); source_view.cleanup_whitespace_characters();
g_assert(source_view.get_buffer()->get_text()==hello_world_cleaned); g_assert(source_view.get_buffer()->get_text() == hello_world_cleaned);
g_assert(boost::filesystem::remove(source_file)); g_assert(boost::filesystem::remove(source_file));
g_assert(!boost::filesystem::exists(source_file)); g_assert(!boost::filesystem::exists(source_file));
for(int c=0;c<2;++c) { for(int c = 0; c < 2; ++c) {
size_t found=0; size_t found = 0;
auto style_scheme_manager=Source::StyleSchemeManager::get_default(); auto style_scheme_manager = Source::StyleSchemeManager::get_default();
for(auto &search_path: style_scheme_manager->get_search_path()) { for(auto &search_path : style_scheme_manager->get_search_path()) {
if(search_path=="styles") // found added style if(search_path == "styles") // found added style
++found; ++found;
} }
g_assert(found==1); g_assert(found == 1);
} }
// replace_text tests // replace_text tests
{ {
auto buffer=source_view.get_buffer(); auto buffer = source_view.get_buffer();
{ {
auto text="line 1\nline 2\nline3"; auto text = "line 1\nline 2\nline3";
buffer->set_text(text); buffer->set_text(text);
buffer->place_cursor(buffer->begin()); buffer->place_cursor(buffer->begin());
source_view.replace_text(text); source_view.replace_text(text);
assert(buffer->get_text()==text); assert(buffer->get_text() == text);
assert(buffer->get_insert()->get_iter()==buffer->begin()); assert(buffer->get_insert()->get_iter() == buffer->begin());
buffer->place_cursor(buffer->end()); buffer->place_cursor(buffer->end());
source_view.replace_text(text); source_view.replace_text(text);
assert(buffer->get_text()==text); assert(buffer->get_text() == text);
assert(buffer->get_insert()->get_iter()==buffer->end()); assert(buffer->get_insert()->get_iter() == buffer->end());
source_view.place_cursor_at_line_offset(1, 0); source_view.place_cursor_at_line_offset(1, 0);
source_view.replace_text(text); source_view.replace_text(text);
assert(buffer->get_text()==text); assert(buffer->get_text() == text);
assert(buffer->get_insert()->get_iter().get_line()==1); assert(buffer->get_insert()->get_iter().get_line() == 1);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
} }
{ {
auto old_text="line 1\nline3"; auto old_text = "line 1\nline3";
auto new_text="line 1\nline 2\nline3"; auto new_text = "line 1\nline 2\nline3";
buffer->set_text(old_text); buffer->set_text(old_text);
source_view.place_cursor_at_line_offset(1, 0); source_view.place_cursor_at_line_offset(1, 0);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
assert(buffer->get_insert()->get_iter().get_line()==2); assert(buffer->get_insert()->get_iter().get_line() == 2);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
source_view.replace_text(old_text); source_view.replace_text(old_text);
assert(buffer->get_text()==old_text); assert(buffer->get_text() == old_text);
assert(buffer->get_insert()->get_iter().get_line()==1); assert(buffer->get_insert()->get_iter().get_line() == 1);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
source_view.place_cursor_at_line_offset(0, 0); source_view.place_cursor_at_line_offset(0, 0);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
assert(buffer->get_insert()->get_iter().get_line()==0); assert(buffer->get_insert()->get_iter().get_line() == 0);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
source_view.replace_text(old_text); source_view.replace_text(old_text);
assert(buffer->get_text()==old_text); assert(buffer->get_text() == old_text);
assert(buffer->get_insert()->get_iter().get_line()==0); assert(buffer->get_insert()->get_iter().get_line() == 0);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
source_view.place_cursor_at_line_offset(2, 0); source_view.place_cursor_at_line_offset(2, 0);
source_view.replace_text(old_text); source_view.replace_text(old_text);
assert(buffer->get_text()==old_text); assert(buffer->get_text() == old_text);
assert(buffer->get_insert()->get_iter().get_line()==1); assert(buffer->get_insert()->get_iter().get_line() == 1);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
} }
{ {
auto old_text="line 1\nline 3"; auto old_text = "line 1\nline 3";
auto new_text=""; auto new_text = "";
buffer->set_text(old_text); buffer->set_text(old_text);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
source_view.replace_text(old_text); source_view.replace_text(old_text);
assert(buffer->get_text()==old_text); assert(buffer->get_text() == old_text);
assert(buffer->get_insert()->get_iter().get_line()==1); assert(buffer->get_insert()->get_iter().get_line() == 1);
assert(buffer->get_insert()->get_iter().get_line_offset()==6); assert(buffer->get_insert()->get_iter().get_line_offset() == 6);
} }
{ {
auto old_text=""; auto old_text = "";
auto new_text=""; auto new_text = "";
buffer->set_text(old_text); buffer->set_text(old_text);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
} }
{ {
auto old_text="line 1\nline 3\n"; auto old_text = "line 1\nline 3\n";
auto new_text=""; auto new_text = "";
buffer->set_text(old_text); buffer->set_text(old_text);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
source_view.replace_text(old_text); source_view.replace_text(old_text);
assert(buffer->get_text()==old_text); assert(buffer->get_text() == old_text);
assert(buffer->get_insert()->get_iter().get_line()==2); assert(buffer->get_insert()->get_iter().get_line() == 2);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
} }
{ {
auto old_text="line 1\n\nline 3\nline 4\n\nline 5\n"; auto old_text = "line 1\n\nline 3\nline 4\n\nline 5\n";
auto new_text="line 1\n\nline 33\nline 44\n\nline 5\n"; auto new_text = "line 1\n\nline 33\nline 44\n\nline 5\n";
buffer->set_text(old_text); buffer->set_text(old_text);
source_view.place_cursor_at_line_offset(2, 0); source_view.place_cursor_at_line_offset(2, 0);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
assert(buffer->get_insert()->get_iter().get_line()==2); assert(buffer->get_insert()->get_iter().get_line() == 2);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
buffer->set_text(old_text); buffer->set_text(old_text);
source_view.place_cursor_at_line_offset(3, 0); source_view.place_cursor_at_line_offset(3, 0);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
assert(buffer->get_insert()->get_iter().get_line()==3); assert(buffer->get_insert()->get_iter().get_line() == 3);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
} }
{ {
auto old_text="line 1\n\nline 3\nline 4\n\nline 5\n"; auto old_text = "line 1\n\nline 3\nline 4\n\nline 5\n";
auto new_text="line 1\n\nline 33\nline 44\nline 45\nline 46\n\nline 5\n"; auto new_text = "line 1\n\nline 33\nline 44\nline 45\nline 46\n\nline 5\n";
buffer->set_text(old_text); buffer->set_text(old_text);
source_view.place_cursor_at_line_offset(2, 0); source_view.place_cursor_at_line_offset(2, 0);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
assert(buffer->get_insert()->get_iter().get_line()==2); assert(buffer->get_insert()->get_iter().get_line() == 2);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
buffer->set_text(old_text); buffer->set_text(old_text);
source_view.place_cursor_at_line_offset(3, 0); source_view.place_cursor_at_line_offset(3, 0);
source_view.replace_text(new_text); source_view.replace_text(new_text);
assert(buffer->get_text()==new_text); assert(buffer->get_text() == new_text);
assert(buffer->get_insert()->get_iter().get_line()==4); assert(buffer->get_insert()->get_iter().get_line() == 4);
assert(buffer->get_insert()->get_iter().get_line_offset()==0); assert(buffer->get_insert()->get_iter().get_line_offset() == 0);
} }
} }
} }

2
tests/stubs/dialogs.cc

@ -1,6 +1,6 @@
#include "dialogs.h" #include "dialogs.h"
Dialog::Message::Message(const std::string &text): Gtk::Window(Gtk::WindowType::WINDOW_POPUP) {} Dialog::Message::Message(const std::string &text) : Gtk::Window(Gtk::WindowType::WINDOW_POPUP) {}
bool Dialog::Message::on_delete_event(GdkEventAny *event) { bool Dialog::Message::on_delete_event(GdkEventAny *event) {
return true; return true;

20
tests/stubs/selection_dialog.cc

@ -2,29 +2,29 @@
SelectionDialogBase::ListViewText::ListViewText(bool use_markup) {} SelectionDialogBase::ListViewText::ListViewText(bool use_markup) {}
SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup): SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup)
text_view(text_view), list_view_text(use_markup) {} : text_view(text_view), list_view_text(use_markup) {}
void SelectionDialogBase::show() {} void SelectionDialogBase::show() {}
void SelectionDialogBase::hide() {} void SelectionDialogBase::hide() {}
void SelectionDialogBase::add_row(const std::string& row) {} void SelectionDialogBase::add_row(const std::string &row) {}
std::unique_ptr<SelectionDialog> SelectionDialog::instance; std::unique_ptr<SelectionDialog> SelectionDialog::instance;
SelectionDialog::SelectionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup) : SelectionDialog::SelectionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup)
SelectionDialogBase(text_view, start_mark, show_search_entry, use_markup) {} : SelectionDialogBase(text_view, start_mark, show_search_entry, use_markup) {}
SelectionDialogBase::~SelectionDialogBase() {} SelectionDialogBase::~SelectionDialogBase() {}
bool SelectionDialog::on_key_press(GdkEventKey* key) { return true; } bool SelectionDialog::on_key_press(GdkEventKey *key) { return true; }
std::unique_ptr<CompletionDialog> CompletionDialog::instance; std::unique_ptr<CompletionDialog> CompletionDialog::instance;
CompletionDialog::CompletionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark): CompletionDialog::CompletionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark)
SelectionDialogBase(text_view, start_mark, false, false) {} : SelectionDialogBase(text_view, start_mark, false, false) {}
bool CompletionDialog::on_key_press(GdkEventKey* key) { return true;} bool CompletionDialog::on_key_press(GdkEventKey *key) { return true; }
bool CompletionDialog::on_key_release(GdkEventKey* key) {return true;} bool CompletionDialog::on_key_release(GdkEventKey *key) { return true; }

6
tests/stubs/tooltips.cc

@ -1,15 +1,15 @@
#include "tooltips.h" #include "tooltips.h"
Gdk::Rectangle Tooltips::drawn_tooltips_rectangle=Gdk::Rectangle(); Gdk::Rectangle Tooltips::drawn_tooltips_rectangle = Gdk::Rectangle();
Tooltip::Tooltip(std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer, Tooltip::Tooltip(std::function<Glib::RefPtr<Gtk::TextBuffer>()> create_tooltip_buffer,
Gtk::TextView *text_view, Gtk::TextView *text_view,
Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark, Glib::RefPtr<Gtk::TextBuffer::Mark> start_mark,
Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark): text_view(text_view) {} Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark) : text_view(text_view) {}
Tooltip::~Tooltip() {} Tooltip::~Tooltip() {}
void Tooltips::show(Gdk::Rectangle const&, bool) {} void Tooltips::show(Gdk::Rectangle const &, bool) {}
void Tooltips::show(bool) {} void Tooltips::show(bool) {}

44
tests/terminal_test.cc

@ -8,34 +8,34 @@
int main() { int main() {
auto app = Gtk::Application::create(); auto app = Gtk::Application::create();
{ {
auto link=Terminal::get().find_link("~/test/test.cc:7:41: error: expected ';' after expression."); auto link = Terminal::get().find_link("~/test/test.cc:7:41: error: expected ';' after expression.");
assert(std::get<0>(link)==0); assert(std::get<0>(link) == 0);
assert(std::get<1>(link)==19); assert(std::get<1>(link) == 19);
assert(std::get<2>(link)=="~/test/test.cc"); assert(std::get<2>(link) == "~/test/test.cc");
assert(std::get<3>(link)=="7"); assert(std::get<3>(link) == "7");
assert(std::get<4>(link)=="41"); assert(std::get<4>(link) == "41");
} }
{ {
auto link=Terminal::get().find_link("Assertion failed: (false), function main, file ~/test/test.cc, line 15."); auto link = Terminal::get().find_link("Assertion failed: (false), function main, file ~/test/test.cc, line 15.");
assert(std::get<0>(link)==47); assert(std::get<0>(link) == 47);
assert(std::get<1>(link)==70); assert(std::get<1>(link) == 70);
assert(std::get<2>(link)=="~/test/test.cc"); assert(std::get<2>(link) == "~/test/test.cc");
assert(std::get<3>(link)=="15"); assert(std::get<3>(link) == "15");
} }
{ {
auto link=Terminal::get().find_link("test: ~/examples/main.cpp:17: int main(int, char**): Assertion `false' failed."); auto link = Terminal::get().find_link("test: ~/examples/main.cpp:17: int main(int, char**): Assertion `false' failed.");
assert(std::get<0>(link)==6); assert(std::get<0>(link) == 6);
assert(std::get<1>(link)==28); assert(std::get<1>(link) == 28);
assert(std::get<2>(link)=="~/examples/main.cpp"); assert(std::get<2>(link) == "~/examples/main.cpp");
assert(std::get<3>(link)=="17"); assert(std::get<3>(link) == "17");
} }
{ {
auto link=Terminal::get().find_link("ERROR:~/test/test.cc:36:int main(): assertion failed: (false)"); auto link = Terminal::get().find_link("ERROR:~/test/test.cc:36:int main(): assertion failed: (false)");
assert(std::get<0>(link)==6); assert(std::get<0>(link) == 6);
assert(std::get<1>(link)==23); assert(std::get<1>(link) == 23);
assert(std::get<2>(link)=="~/test/test.cc"); assert(std::get<2>(link) == "~/test/test.cc");
assert(std::get<3>(link)=="36"); assert(std::get<3>(link) == "36");
} }
} }

26
tests/usages_clang_test.cc

@ -43,7 +43,7 @@ int main() {
assert(usages.size() == 1); assert(usages.size() == 1);
assert(usages[0].path == path); assert(usages[0].path == path);
assert(usages[0].lines.size() == 1); assert(usages[0].lines.size() == 1);
assert(usages[0].lines[0] == " test.a=2;"); assert(usages[0].lines[0] == " test.a = 2;");
assert(usages[0].offsets.size() == 1); assert(usages[0].offsets.size() == 1);
assert(usages[0].offsets[0].first.line == 6); assert(usages[0].offsets[0].first.line == 6);
assert(usages[0].offsets[0].first.index == 8); assert(usages[0].offsets[0].first.index == 8);
@ -54,7 +54,7 @@ int main() {
assert(usages.size() == 2); assert(usages.size() == 2);
assert(usages[1].path == project_path / "test.hpp"); assert(usages[1].path == project_path / "test.hpp");
assert(usages[1].lines.size() == 2); assert(usages[1].lines.size() == 2);
assert(usages[1].lines[0] == " int a=0;"); assert(usages[1].lines[0] == " int a = 0;");
assert(usages[1].lines[1] == " ++a;"); assert(usages[1].lines[1] == " ++a;");
assert(usages[1].offsets.size() == 2); assert(usages[1].offsets.size() == 2);
assert(usages[1].offsets[0].first.line == 6); assert(usages[1].offsets[0].first.line == 6);
@ -193,7 +193,7 @@ int main() {
assert(usages.size() == 1); assert(usages.size() == 1);
assert(usages[0].path == cache_it->first); assert(usages[0].path == cache_it->first);
assert(usages[0].lines.size() == 1); assert(usages[0].lines.size() == 1);
assert(usages[0].lines[0] == " test.a=2;"); assert(usages[0].lines[0] == " test.a = 2;");
assert(usages[0].offsets.size() == 1); assert(usages[0].offsets.size() == 1);
assert(usages[0].offsets[0].first.line == 6); assert(usages[0].offsets[0].first.line == 6);
assert(usages[0].offsets[0].first.index == 8); assert(usages[0].offsets[0].first.index == 8);
@ -217,7 +217,7 @@ int main() {
assert(usages.size() == 1); assert(usages.size() == 1);
assert(usages[0].path == cache_it->first); assert(usages[0].path == cache_it->first);
assert(usages[0].lines.size() == 2); assert(usages[0].lines.size() == 2);
assert(usages[0].lines[0] == " int a=0;"); assert(usages[0].lines[0] == " int a = 0;");
assert(usages[0].lines[1] == " ++a;"); assert(usages[0].lines[1] == " ++a;");
assert(usages[0].offsets.size() == 2); assert(usages[0].offsets.size() == 2);
assert(usages[0].offsets[0].first.line == 6); assert(usages[0].offsets[0].first.line == 6);
@ -258,16 +258,16 @@ int main() {
} }
{ {
assert(!Usages::Clang::caches.empty()); assert(!Usages::Clang::caches.empty());
assert(boost::filesystem::exists(build_path/Usages::Clang::cache_folder)); assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder));
assert(boost::filesystem::exists(build_path/Usages::Clang::cache_folder/"main.cpp.usages")); assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "main.cpp.usages"));
assert(boost::filesystem::exists(build_path/Usages::Clang::cache_folder/"test.hpp.usages")); assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "test.hpp.usages"));
assert(boost::filesystem::exists(build_path/Usages::Clang::cache_folder/"test2.hpp.usages")); assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "test2.hpp.usages"));
Usages::Clang::erase_all_caches_for_project(project_path, build_path); Usages::Clang::erase_all_caches_for_project(project_path, build_path);
assert(Usages::Clang::caches.empty()); assert(Usages::Clang::caches.empty());
assert(boost::filesystem::exists(build_path/Usages::Clang::cache_folder)); assert(boost::filesystem::exists(build_path / Usages::Clang::cache_folder));
assert(!boost::filesystem::exists(build_path/Usages::Clang::cache_folder/"main.cpp.usages")); assert(!boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "main.cpp.usages"));
assert(!boost::filesystem::exists(build_path/Usages::Clang::cache_folder/"test.hpp.usages")); assert(!boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "test.hpp.usages"));
assert(!boost::filesystem::exists(build_path/Usages::Clang::cache_folder/"test2.hpp.usages")); assert(!boost::filesystem::exists(build_path / Usages::Clang::cache_folder / "test2.hpp.usages"));
} }
} }

4
tests/usages_clang_test_files/main.cpp

@ -1,9 +1,9 @@
#include <iostream>
#include "test.hpp" #include "test.hpp"
#include <iostream>
int main() { int main() {
Test test; Test test;
test.a=2; test.a = 2;
test.b(); test.b();
test.c(); test.c();
} }

4
tests/usages_clang_test_files/test.hpp

@ -2,8 +2,8 @@ class Test {
public: public:
Test() {} Test() {}
~Test() {} ~Test() {}
int a=0; int a = 0;
void b() { void b() {
++a; ++a;
} }

Loading…
Cancel
Save