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. 8
      src/autocomplete.cc
  4. 2
      src/autocomplete.h
  5. 320
      src/cmake.cc
  6. 10
      src/cmake.h
  7. 117
      src/compile_commands.cc
  8. 2
      src/compile_commands.h
  9. 160
      src/config.cc
  10. 12
      src/config.h
  11. 174
      src/ctags.cc
  12. 5
      src/ctags.h
  13. 377
      src/debug_lldb.cc
  14. 14
      src/debug_lldb.h
  15. 42
      src/dialogs.cc
  16. 5
      src/dialogs.h
  17. 9
      src/dialogs_unix.cc
  18. 60
      src/dialogs_win.cc
  19. 419
      src/directories.cc
  20. 23
      src/directories.h
  21. 8
      src/dispatcher.cc
  22. 7
      src/dispatcher.h
  23. 34
      src/documentation_cppreference.cc
  24. 2
      src/documentation_cppreference.h
  25. 58
      src/entrybox.cc
  26. 24
      src/entrybox.h
  27. 28
      src/files.h
  28. 137
      src/filesystem.cc
  29. 4
      src/filesystem.h
  30. 198
      src/git.cc
  31. 37
      src/git.h
  32. 14
      src/info.cc
  33. 1
      src/info.h
  34. 54
      src/juci.cc
  35. 3
      src/juci.h
  36. 26
      src/menu.cc
  37. 10
      src/menu.h
  38. 74
      src/meson.cc
  39. 4
      src/meson.h
  40. 478
      src/notebook.cc
  41. 41
      src/notebook.h
  42. 613
      src/project.cc
  43. 21
      src/project.h
  44. 80
      src/project_build.cc
  45. 28
      src/project_build.h
  46. 256
      src/selection_dialog.cc
  47. 43
      src/selection_dialog.h
  48. 2367
      src/source.cc
  49. 73
      src/source.h
  50. 196
      src/source_base.cc
  51. 13
      src/source_base.h
  52. 1376
      src/source_clang.cc
  53. 38
      src/source_clang.h
  54. 200
      src/source_diff.cc
  55. 16
      src/source_diff.h
  56. 978
      src/source_language_protocol.cc
  57. 10
      src/source_language_protocol.h
  58. 336
      src/source_spellcheck.cc
  59. 15
      src/source_spellcheck.h
  60. 277
      src/terminal.cc
  61. 31
      src/terminal.h
  62. 120
      src/tooltips.cc
  63. 23
      src/tooltips.h
  64. 16
      src/usages_clang.cc
  65. 976
      src/window.cc
  66. 9
      src/window.h
  67. 78
      tests/cmake_build_test.cc
  68. 14
      tests/compile_commands_test.cc
  69. 60
      tests/filesystem_test.cc
  70. 54
      tests/git_test.cc
  71. 126
      tests/lldb_test.cc
  72. 2
      tests/lldb_test_files/main.cpp
  73. 34
      tests/meson_build_test.cc
  74. 32
      tests/process_test.cc
  75. 114
      tests/source_clang_test.cc
  76. 4
      tests/source_clang_test_files/main.cpp
  77. 146
      tests/source_test.cc
  78. 2
      tests/stubs/dialogs.cc
  79. 20
      tests/stubs/selection_dialog.cc
  80. 6
      tests/stubs/tooltips.cc
  81. 42
      tests/terminal_test.cc
  82. 24
      tests/usages_clang_test.cc
  83. 4
      tests/usages_clang_test_files/main.cpp
  84. 2
      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
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))
```

8
src/autocomplete.cc

@ -130,7 +130,7 @@ void Autocomplete::stop() {
}
void Autocomplete::setup_dialog() {
CompletionDialog::get()->on_show=[this] {
CompletionDialog::get()->on_show = [this] {
on_show();
};
@ -155,7 +155,7 @@ void Autocomplete::setup_dialog() {
tooltips.hide();
else {
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());
tooltip_buffer->insert(tooltip_buffer->get_insert()->get_iter(), tooltip);
@ -170,8 +170,8 @@ void Autocomplete::setup_dialog() {
}
};
CompletionDialog::get()->on_select=[this](unsigned int index, const std::string &text, bool hide_window) {
if(index>=rows.size())
CompletionDialog::get()->on_select = [this](unsigned int index, const std::string &text, bool hide_window) {
if(index >= rows.size())
return;
on_select(index, text, hide_window);

2
src/autocomplete.h

@ -46,7 +46,7 @@ public:
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<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);

320
src/cmake.cc

@ -1,14 +1,14 @@
#include "cmake.h"
#include "filesystem.h"
#include "dialogs.h"
#include "compile_commands.h"
#include "config.h"
#include "dialogs.h"
#include "filesystem.h"
#include "terminal.h"
#include <regex>
#include "compile_commands.h"
CMake::CMake(const boost::filesystem::path &path) {
const auto find_cmake_project=[](const boost::filesystem::path &cmake_path) {
for(auto &line: filesystem::read_lines(cmake_path)) {
const auto find_cmake_project = [](const boost::filesystem::path &cmake_path) {
for(auto &line : filesystem::read_lines(cmake_path)) {
const static std::regex project_regex(R"(^ *project *\(.*\r?$)", std::regex::icase);
std::smatch sm;
if(std::regex_match(line, sm, project_regex))
@ -17,52 +17,52 @@ CMake::CMake(const boost::filesystem::path &path) {
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) {
auto search_cmake_path=search_path/"CMakeLists.txt";
auto search_cmake_path = search_path / "CMakeLists.txt";
if(boost::filesystem::exists(search_cmake_path)) {
paths.emplace(paths.begin(), search_cmake_path);
if(find_cmake_project(search_cmake_path)) {
project_path=search_path;
project_path = search_path;
break;
}
}
if(search_path==search_path.root_directory())
if(search_path == search_path.root_directory())
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) {
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;
if(!boost::filesystem::exists(default_build_path)) {
boost::system::error_code ec;
boost::filesystem::create_directories(default_build_path, 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;
}
}
if(!force && boost::filesystem::exists(default_build_path/"compile_commands.json"))
if(!force && boost::filesystem::exists(default_build_path / "compile_commands.json"))
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");
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);
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);
message.hide();
if(exit_status==EXIT_SUCCESS) {
if(exit_status == EXIT_SUCCESS) {
#ifdef _WIN32 //Temporary fix to MSYS2's libclang
auto compile_commands_file=filesystem::read(compile_commands_path);
auto replace_drive = [&compile_commands_file](const std::string& param) {
size_t pos=0;
auto compile_commands_file = filesystem::read(compile_commands_path);
auto replace_drive = [&compile_commands_file](const std::string &param) {
size_t pos = 0;
auto param_size = param.length();
while((pos=compile_commands_file.find(param+"/", pos))!=std::string::npos) {
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]+":");
while((pos = compile_commands_file.find(param + "/", pos)) != std::string::npos) {
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] + ":");
else
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) {
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;
if(!boost::filesystem::exists(debug_build_path)) {
boost::system::error_code ec;
boost::filesystem::create_directories(debug_build_path, 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;
}
}
if(!force && boost::filesystem::exists(debug_build_path/"CMakeCache.txt"))
if(!force && boost::filesystem::exists(debug_build_path / "CMakeCache.txt"))
return true;
Dialog::Message message("Creating/updating debug build");
auto exit_status=Terminal::get().process(Config::get().project.cmake.command+' '+
filesystem::escape_argument(project_path.string())+" -DCMAKE_BUILD_TYPE=Debug", debug_build_path);
auto exit_status = Terminal::get().process(Config::get().project.cmake.command + ' ' +
filesystem::escape_argument(project_path.string()) + " -DCMAKE_BUILD_TYPE=Debug", debug_build_path);
message.hide();
if(exit_status==EXIT_SUCCESS)
if(exit_status == EXIT_SUCCESS)
return true;
return false;
}
@ -109,12 +109,12 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui
auto parameters = get_functions_parameters("add_executable");
std::vector<boost::filesystem::path> cmake_executables;
for(auto &parameter: parameters) {
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 project_path_str=project_path.string();
size_t pos=executable.find(project_path_str);
if(pos!=std::string::npos)
for(auto &parameter : parameters) {
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 project_path_str = project_path.string();
size_t pos = executable.find(project_path_str);
if(pos != std::string::npos)
executable.replace(pos, project_path_str.size(), build_path.string());
cmake_executables.emplace_back(executable);
}
@ -123,35 +123,35 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui
CompileCommands compile_commands(build_path);
std::vector<std::pair<boost::filesystem::path, boost::filesystem::path>> command_files_and_maybe_executables;
for(auto &command: compile_commands.commands) {
auto command_file=filesystem::get_normal_path(command.file);
auto values=command.parameter_values("-o");
for(auto &command : compile_commands.commands) {
auto command_file = filesystem::get_normal_path(command.file);
auto values = command.parameter_values("-o");
if(!values.empty()) {
size_t pos;
values[0].erase(0, 11);
if((pos=values[0].find(".dir"))!=std::string::npos) {
auto executable=command.directory/values[0].substr(0, pos);
if((pos = values[0].find(".dir")) != std::string::npos) {
auto executable = command.directory / values[0].substr(0, pos);
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;
for(auto &cmake_executable: cmake_executables) {
for(auto &command_file_and_maybe_executable: command_files_and_maybe_executables) {
auto &command_file=command_file_and_maybe_executable.first;
auto &maybe_executable=command_file_and_maybe_executable.second;
if(cmake_executable==maybe_executable) {
if(command_file==file_path)
for(auto &cmake_executable : cmake_executables) {
for(auto &command_file_and_maybe_executable : command_files_and_maybe_executables) {
auto &command_file = command_file_and_maybe_executable.first;
auto &maybe_executable = command_file_and_maybe_executable.second;
if(cmake_executable == maybe_executable) {
if(command_file == file_path)
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)) {
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) {
best_match_size=size;
best_match_executable=maybe_executable;
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) {
best_match_size = size;
best_match_executable = maybe_executable;
}
}
}
@ -160,17 +160,17 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui
if(!best_match_executable.empty())
return best_match_executable;
for(auto &command_file_and_maybe_executable: command_files_and_maybe_executables) {
auto &command_file=command_file_and_maybe_executable.first;
auto &maybe_executable=command_file_and_maybe_executable.second;
if(command_file==file_path)
for(auto &command_file_and_maybe_executable : command_files_and_maybe_executables) {
auto &command_file = command_file_and_maybe_executable.first;
auto &maybe_executable = command_file_and_maybe_executable.second;
if(command_file == file_path)
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)) {
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) {
best_match_size=size;
best_match_executable=maybe_executable;
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) {
best_match_size = size;
best_match_executable = maybe_executable;
}
}
}
@ -178,33 +178,33 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui
}
void CMake::read_files() {
for(auto &path: paths)
for(auto &path : paths)
files.emplace_back(filesystem::read(path));
}
void CMake::remove_tabs() {
for(auto &file: files) {
for(auto &chr: file) {
if(chr=='\t')
chr=' ';
for(auto &file : files) {
for(auto &chr : file) {
if(chr == '\t')
chr = ' ';
}
}
}
void CMake::remove_comments() {
for(auto &file: files) {
size_t pos=0;
for(auto &file : files) {
size_t pos = 0;
size_t comment_start;
bool inside_comment=false;
while(pos<file.size()) {
if(!inside_comment && file[pos]=='#') {
comment_start=pos;
inside_comment=true;
bool inside_comment = false;
while(pos < file.size()) {
if(!inside_comment && file[pos] == '#') {
comment_start = pos;
inside_comment = true;
}
if(inside_comment && file[pos]=='\n') {
file.erase(comment_start, pos-comment_start);
pos-=pos-comment_start;
inside_comment=false;
if(inside_comment && file[pos] == '\n') {
file.erase(comment_start, pos - comment_start);
pos -= pos - comment_start;
inside_comment = false;
}
pos++;
}
@ -214,69 +214,69 @@ void CMake::remove_comments() {
}
void CMake::remove_newlines_inside_parentheses() {
for(auto &file: files) {
size_t pos=0;
bool inside_para=false;
bool inside_quote=false;
char last_char=0;
while(pos<file.size()) {
if(!inside_quote && file[pos]=='"' && last_char!='\\')
inside_quote=true;
else if(inside_quote && file[pos]=='"' && last_char!='\\')
inside_quote=false;
for(auto &file : files) {
size_t pos = 0;
bool inside_para = false;
bool inside_quote = false;
char last_char = 0;
while(pos < file.size()) {
if(!inside_quote && file[pos] == '"' && last_char != '\\')
inside_quote = true;
else if(inside_quote && file[pos] == '"' && last_char != '\\')
inside_quote = false;
else if(!inside_quote && file[pos]=='(')
inside_para=true;
else if(!inside_quote && file[pos]==')')
inside_para=false;
else if(!inside_quote && file[pos] == '(')
inside_para = true;
else if(!inside_quote && file[pos] == ')')
inside_para = false;
else if(inside_para && file[pos]=='\n')
else if(inside_para && file[pos] == '\n')
file.replace(pos, 1, 1, ' ');
last_char=file[pos];
last_char = file[pos];
pos++;
}
}
}
void CMake::parse_variable_parameters(std::string &data) {
size_t pos=0;
bool inside_quote=false;
char last_char=0;
while(pos<data.size()) {
if(!inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=true;
size_t pos = 0;
bool inside_quote = false;
char last_char = 0;
while(pos < data.size()) {
if(!inside_quote && data[pos] == '"' && last_char != '\\') {
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}
pos--;
}
else if(inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=false;
else if(inside_quote && data[pos] == '"' && last_char != '\\') {
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}
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);
pos--;
}
if(pos!=static_cast<size_t>(-1))
last_char=data[pos];
if(pos != static_cast<size_t>(-1))
last_char = data[pos];
pos++;
}
for(auto &var: variables) {
auto pos=data.find("${"+var.first+'}');
while(pos!=std::string::npos) {
data.replace(pos, var.first.size()+3, var.second);
pos=data.find("${"+var.first+'}');
for(auto &var : variables) {
auto pos = data.find("${" + var.first + '}');
while(pos != std::string::npos) {
data.replace(pos, var.first.size() + 3, var.second);
pos = data.find("${" + var.first + '}');
}
}
//Remove variables we do not know:
pos=data.find("${");
auto pos_end=data.find('}', pos+2);
while(pos!=std::string::npos && pos_end!=std::string::npos) {
data.erase(pos, pos_end-pos+1);
pos=data.find("${");
pos_end=data.find('}', pos+2);
pos = data.find("${");
auto pos_end = data.find('}', pos + 2);
while(pos != std::string::npos && pos_end != std::string::npos) {
data.erase(pos, pos_end - pos + 1);
pos = data.find("${");
pos_end = data.find('}', pos + 2);
}
}
@ -285,93 +285,93 @@ void CMake::parse() {
remove_tabs();
remove_comments();
remove_newlines_inside_parentheses();
parsed=true;
parsed = true;
}
std::vector<std::string> CMake::get_function_parameters(std::string &data) {
std::vector<std::string> parameters;
size_t pos=0;
size_t parameter_pos=0;
bool inside_quote=false;
char last_char=0;
while(pos<data.size()) {
if(!inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=true;
size_t pos = 0;
size_t parameter_pos = 0;
bool inside_quote = false;
char last_char = 0;
while(pos < data.size()) {
if(!inside_quote && data[pos] == '"' && last_char != '\\') {
inside_quote = true;
data.erase(pos, 1);
pos--;
}
else if(inside_quote && data[pos]=='"' && last_char!='\\') {
inside_quote=false;
else if(inside_quote && data[pos] == '"' && last_char != '\\') {
inside_quote = false;
data.erase(pos, 1);
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);
pos--;
}
else if(!inside_quote && data[pos]==' ') {
parameters.emplace_back(data.substr(parameter_pos, pos-parameter_pos));
if(pos+1<data.size())
parameter_pos=pos+1;
else if(!inside_quote && data[pos] == ' ') {
parameters.emplace_back(data.substr(parameter_pos, pos - parameter_pos));
if(pos + 1 < data.size())
parameter_pos = pos + 1;
}
if(pos!=static_cast<size_t>(-1))
last_char=data[pos];
if(pos != static_cast<size_t>(-1))
last_char = data[pos];
pos++;
}
parameters.emplace_back(data.substr(parameter_pos));
for(auto &var: variables) {
for(auto &parameter: parameters) {
auto pos=parameter.find("${"+var.first+'}');
while(pos!=std::string::npos) {
parameter.replace(pos, var.first.size()+3, var.second);
pos=parameter.find("${"+var.first+'}');
for(auto &var : variables) {
for(auto &parameter : parameters) {
auto pos = parameter.find("${" + var.first + '}');
while(pos != std::string::npos) {
parameter.replace(pos, var.first.size() + 3, var.second);
pos = parameter.find("${" + var.first + '}');
}
}
}
return parameters;
}
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);
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);
variables.clear();
if(!parsed)
parse();
std::vector<std::pair<boost::filesystem::path, std::vector<std::string> > > functions;
for(size_t c=0;c<files.size();++c) {
size_t pos=0;
while(pos<files[c].size()) {
auto start_line=pos;
auto end_line=files[c].find('\n', start_line);
if(end_line==std::string::npos)
end_line=files[c].size();
if(end_line>start_line) {
auto line=files[c].substr(start_line, end_line-start_line);
std::vector<std::pair<boost::filesystem::path, std::vector<std::string>>> functions;
for(size_t c = 0; c < files.size(); ++c) {
size_t pos = 0;
while(pos < files[c].size()) {
auto start_line = pos;
auto end_line = files[c].find('\n', start_line);
if(end_line == std::string::npos)
end_line = files[c].size();
if(end_line > start_line) {
auto line = files[c].substr(start_line, end_line - start_line);
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 project_regex(R"(^ *project *\( *([^ ]+).*\) *\r?$)", std::regex::icase);
if(std::regex_match(line, sm, set_regex)) {
auto data=sm[2].str();
while(data.size()>0 && data.back()==' ')
auto data = sm[2].str();
while(data.size() > 0 && data.back() == ' ')
data.pop_back();
parse_variable_parameters(data);
variables[sm[1].str()]=data;
variables[sm[1].str()] = data;
}
else if(std::regex_match(line, sm, project_regex)) {
auto data=sm[1].str();
auto data = sm[1].str();
parse_variable_parameters(data);
variables["CMAKE_PROJECT_NAME"]=data; //TODO: is this variable deprecated/non-standard?
variables["PROJECT_NAME"]=data;
variables["CMAKE_PROJECT_NAME"] = data; //TODO: is this variable deprecated/non-standard?
variables["PROJECT_NAME"] = data;
}
if(std::regex_match(line, sm, function_regex)) {
auto data=sm[1].str();
while(data.size()>0 && data.back()==' ')
auto data = sm[1].str();
while(data.size() > 0 && data.back() == ' ')
data.pop_back();
auto parameters=get_function_parameters(data);
auto parameters = get_function_parameters(data);
functions.emplace_back(paths[c], parameters);
}
}
pos=end_line+1;
pos = end_line + 1;
}
}
return functions;

10
src/cmake.h

@ -1,16 +1,16 @@
#pragma once
#include <boost/filesystem.hpp>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <vector>
class CMake {
public:
CMake(const boost::filesystem::path &path);
boost::filesystem::path project_path;
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_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);
boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
@ -25,6 +25,6 @@ private:
void parse_variable_parameters(std::string &data);
void parse();
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);
bool parsed=false;
std::vector<std::pair<boost::filesystem::path, std::vector<std::string>>> get_functions_parameters(const std::string &name);
bool parsed = false;
};

117
src/compile_commands.cc

@ -6,14 +6,14 @@
std::vector<std::string> CompileCommands::Command::parameter_values(const std::string &parameter_name) const {
std::vector<std::string> parameter_values;
bool found_argument=false;
for(auto &parameter: parameters) {
bool found_argument = false;
for(auto &parameter : parameters) {
if(found_argument) {
parameter_values.emplace_back(parameter);
found_argument=false;
found_argument = false;
}
else if(parameter==parameter_name)
found_argument=true;
else if(parameter == parameter_name)
found_argument = true;
}
return parameter_values;
@ -22,66 +22,67 @@ std::vector<std::string> CompileCommands::Command::parameter_values(const std::s
CompileCommands::CompileCommands(const boost::filesystem::path &build_path) {
try {
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("");
for(auto &command: commands_pt) {
boost::filesystem::path directory=command.second.get<std::string>("directory");
auto parameters_str=command.second.get<std::string>("command");
boost::filesystem::path file=command.second.get<std::string>("file");
auto commands_pt = root_pt.get_child("");
for(auto &command : commands_pt) {
boost::filesystem::path directory = command.second.get<std::string>("directory");
auto parameters_str = command.second.get<std::string>("command");
boost::filesystem::path file = command.second.get<std::string>("file");
std::vector<std::string> parameters;
bool backslash=false;
bool single_quote=false;
bool double_quote=false;
size_t parameter_start_pos=std::string::npos;
size_t parameter_size=0;
auto add_parameter=[&parameters, &parameters_str, &parameter_start_pos, &parameter_size] {
auto parameter=parameters_str.substr(parameter_start_pos, parameter_size);
bool backslash = false;
bool single_quote = false;
bool double_quote = false;
size_t parameter_start_pos = std::string::npos;
size_t parameter_size = 0;
auto add_parameter = [&parameters, &parameters_str, &parameter_start_pos, &parameter_size] {
auto parameter = parameters_str.substr(parameter_start_pos, parameter_size);
// Remove escaping
for(size_t c=0;c<parameter.size()-1;++c) {
if(parameter[c]=='\\')
parameter.replace(c, 2, std::string()+parameter[c+1]);
for(size_t c = 0; c < parameter.size() - 1; ++c) {
if(parameter[c] == '\\')
parameter.replace(c, 2, std::string() + parameter[c + 1]);
}
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)
backslash=false;
else if(parameters_str[c]=='\\')
backslash=true;
else if((parameters_str[c]==' ' || parameters_str[c]=='\t') && !backslash && !single_quote && !double_quote) {
if(parameter_start_pos!=std::string::npos) {
backslash = false;
else if(parameters_str[c] == '\\')
backslash = true;
else if((parameters_str[c] == ' ' || parameters_str[c] == '\t') && !backslash && !single_quote && !double_quote) {
if(parameter_start_pos != std::string::npos) {
add_parameter();
parameter_start_pos=std::string::npos;
parameter_size=0;
parameter_start_pos = std::string::npos;
parameter_size = 0;
}
continue;
}
else if(parameters_str[c]=='\'' && !backslash && !double_quote) {
single_quote=!single_quote;
else if(parameters_str[c] == '\'' && !backslash && !double_quote) {
single_quote = !single_quote;
continue;
}
else if(parameters_str[c]=='\"' && !backslash && !single_quote) {
double_quote=!double_quote;
else if(parameters_str[c] == '\"' && !backslash && !single_quote) {
double_quote = !double_quote;
continue;
}
if(parameter_start_pos==std::string::npos)
parameter_start_pos=c;
if(parameter_start_pos == std::string::npos)
parameter_start_pos = c;
++parameter_size;
}
if(parameter_start_pos!=std::string::npos)
if(parameter_start_pos != std::string::npos)
add_parameter();
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::string default_std_argument="-std=c++1y";
std::string default_std_argument = "-std=c++1y";
std::vector<std::string> arguments;
if(!build_path.empty()) {
@ -89,17 +90,17 @@ std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem:
if(db) {
clangmm::CompileCommands commands(file_path.string(), db);
auto cmds = commands.get_commands();
for (auto &cmd : cmds) {
for(auto &cmd : cmds) {
auto cmd_arguments = cmd.get_arguments();
bool ignore_next=false;
for (size_t c = 1; c < cmd_arguments.size(); c++) {
bool ignore_next = false;
for(size_t c = 1; c < cmd_arguments.size(); c++) {
if(ignore_next) {
ignore_next=false;
ignore_next = false;
continue;
}
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
ignore_next=true;
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
ignore_next = true;
continue;
}
arguments.emplace_back(cmd_arguments[c]);
@ -112,28 +113,28 @@ std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem:
else
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.]+).*$)");
std::smatch sm;
if(std::regex_match(clang_version_string, sm, clang_version_regex)) {
auto clang_version=sm[1].str();
arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include");
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)
arguments.emplace_back("-I/usr/local/Cellar/llvm/"+clang_version+"/lib/clang/"+clang_version+"/include");
auto clang_version = sm[1].str();
arguments.emplace_back("-I/usr/lib/clang/" + clang_version + "/include");
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)
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/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1"); //Added for OS X 10.11
#endif
#ifdef _WIN32
auto env_msystem_prefix=std::getenv("MSYSTEM_PREFIX");
if(env_msystem_prefix!=nullptr)
arguments.emplace_back("-I"+(boost::filesystem::path(env_msystem_prefix)/"lib/clang"/clang_version/"include").string());
auto env_msystem_prefix = std::getenv("MSYSTEM_PREFIX");
if(env_msystem_prefix != nullptr)
arguments.emplace_back("-I" + (boost::filesystem::path(env_msystem_prefix) / "lib/clang" / clang_version / "include").string());
#endif
}
arguments.emplace_back("-fretain-comments-from-system-headers");
auto extension=file_path.extension().string();
bool is_header=CompileCommands::is_header(file_path) || extension.empty(); // Include std C++ headers that are without extensions
auto extension = file_path.extension().string();
bool is_header = CompileCommands::is_header(file_path) || extension.empty(); // Include std C++ headers that are without extensions
if(is_header) {
arguments.emplace_back("-Wno-pragma-once-outside-header");
@ -141,14 +142,14 @@ std::vector<std::string> CompileCommands::get_arguments(const boost::filesystem:
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("-D__CUDACC__");
arguments.emplace_back("-include");
arguments.emplace_back("cuda_runtime.h");
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("-cl-std=CL2.0");
arguments.emplace_back("-Xclang");

2
src/compile_commands.h

@ -1,7 +1,7 @@
#pragma once
#include <boost/filesystem.hpp>
#include <vector>
#include <string>
#include <vector>
class CompileCommands {
public:

160
src/config.cc

@ -1,20 +1,20 @@
#include "config.h"
#include <exception>
#include "files.h"
#include <iostream>
#include "filesystem.h"
#include "terminal.h"
#include <algorithm>
#include <exception>
#include <iostream>
Config::Config() {
home_path=filesystem::get_home_path();
home_path = filesystem::get_home_path();
if(home_path.empty())
throw std::runtime_error("Could not find home path");
home_juci_path=home_path/".juci";
home_juci_path = home_path / ".juci";
}
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;
try {
find_or_create_config_files();
@ -23,8 +23,8 @@ void Config::load() {
read(cfg);
}
catch(const std::exception &e) {
dispatcher.post([config_json, e_what=std::string(e.what())] {
::Terminal::get().print("Error: could not parse "+config_json+": "+e_what+"\n", true);
dispatcher.post([config_json, e_what = std::string(e.what())] {
::Terminal::get().print("Error: could not parse " + config_json + ": " + e_what + "\n", true);
});
std::stringstream ss;
ss << default_config_file;
@ -34,67 +34,67 @@ void Config::load() {
}
void Config::find_or_create_config_files() {
auto config_dir = home_juci_path/"config";
auto config_json = config_dir/"config.json";
auto config_dir = home_juci_path / "config";
auto config_json = config_dir / "config.json";
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);
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
juci_style_path/="juci-light.xml";
juci_style_path /= "juci-light.xml";
if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_light_style);
juci_style_path=juci_style_path.parent_path();
juci_style_path/="juci-dark.xml";
juci_style_path = juci_style_path.parent_path();
juci_style_path /= "juci-dark.xml";
if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_dark_style);
juci_style_path=juci_style_path.parent_path();
juci_style_path/="juci-dark-blue.xml";
juci_style_path = juci_style_path.parent_path();
juci_style_path /= "juci-dark-blue.xml";
if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_dark_blue_style);
}
void Config::update(boost::property_tree::ptree &cfg) {
boost::property_tree::ptree default_cfg;
bool cfg_ok=true;
if(cfg.get<std::string>("version")!=JUCI_VERSION) {
bool cfg_ok = true;
if(cfg.get<std::string>("version") != JUCI_VERSION) {
std::stringstream ss;
ss << default_config_file;
boost::property_tree::read_json(ss, default_cfg);
cfg_ok=false;
auto it_version=cfg.find("version");
if(it_version!=cfg.not_found()) {
cfg_ok = false;
auto it_version = cfg.find("version");
if(it_version != cfg.not_found()) {
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";
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-blue.xml", juci_dark_blue_style);
auto style_path = home_juci_path / "styles";
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-blue.xml", juci_dark_blue_style);
}
else
return;
cfg_ok&=add_missing_nodes(cfg, default_cfg);
cfg_ok&=remove_deprecated_nodes(cfg, default_cfg);
cfg_ok &= add_missing_nodes(cfg, default_cfg);
cfg_ok &= remove_deprecated_nodes(cfg, default_cfg);
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) {
auto &keybindings_cfg=cfg.get_child("keybindings");
auto &keybindings_cfg = cfg.get_child("keybindings");
try {
if(version<="1.2.4") {
auto it_file_print=keybindings_cfg.find("print");
if(it_file_print!=keybindings_cfg.not_found() && it_file_print->second.data()=="<primary>p") {
if(version <= "1.2.4") {
auto it_file_print = keybindings_cfg.find("print");
if(it_file_print != keybindings_cfg.not_found() && it_file_print->second.data() == "<primary>p") {
dispatcher.post([] {
::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) {
if(parent_path.size()>0)
parent_path+=".";
bool unchanged=true;
for(auto &node: default_cfg) {
auto path=parent_path+node.first;
if(parent_path.size() > 0)
parent_path += ".";
bool unchanged = true;
for(auto &node : default_cfg) {
auto path = parent_path + node.first;
try {
cfg.get<std::string>(path);
}
catch(const std::exception &e) {
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;
}
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)
parent_path+=".";
bool unchanged=true;
for(auto it=cfg.begin();it!=cfg.end();) {
auto path=parent_path+it->first;
if(parent_path.size() > 0)
parent_path += ".";
bool unchanged = true;
for(auto it = cfg.begin(); it != cfg.end();) {
auto path = parent_path + it->first;
try {
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;
}
catch(const std::exception &e) {
it=cfg.erase(it);
unchanged=false;
it = cfg.erase(it);
unchanged = false;
}
}
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) {
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>();
}
auto source_json = cfg.get_child("source");
source.style=source_json.get<std::string>("style");
source.font=source_json.get<std::string>("font");
source.cleanup_whitespace_characters=source_json.get<bool>("cleanup_whitespace_characters");
source.show_whitespace_characters=source_json.get<std::string>("show_whitespace_characters");
source.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.smart_brackets=source_json.get<bool>("smart_brackets");
source.smart_inserts=source_json.get<bool>("smart_inserts");
source.style = source_json.get<std::string>("style");
source.font = source_json.get<std::string>("font");
source.cleanup_whitespace_characters = source_json.get<bool>("cleanup_whitespace_characters");
source.show_whitespace_characters = source_json.get<std::string>("show_whitespace_characters");
source.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.smart_brackets = source_json.get<bool>("smart_brackets");
source.smart_inserts = source_json.get<bool>("smart_inserts");
if(source.smart_inserts)
source.smart_brackets=true;
source.smart_brackets = true;
source.show_map = source_json.get<bool>("show_map");
source.map_font_size = source_json.get<std::string>("map_font_size");
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.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"));
auto pt_doc_search=cfg.get_child("documentation_searches");
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");
auto &queries=source.documentation_searches.find(pt_doc_search_lang.first)->second.queries;
for(auto &i: pt_doc_search_lang.second.get_child("queries")) {
queries[i.first]=i.second.get_value<std::string>();
auto pt_doc_search = cfg.get_child("documentation_searches");
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");
auto &queries = source.documentation_searches.find(pt_doc_search_lang.first)->second.queries;
for(auto &i : pt_doc_search_lang.second.get_child("queries")) {
queries[i.first] = i.second.get_value<std::string>();
}
}
window.theme_name=cfg.get<std::string>("gtk_theme.name");
window.theme_variant=cfg.get<std::string>("gtk_theme.variant");
window.theme_name = cfg.get<std::string>("gtk_theme.name");
window.theme_variant = cfg.get<std::string>("gtk_theme.variant");
window.version = cfg.get<std::string>("version");
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.cmake.command=cfg.get<std::string>("project.cmake.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.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.clear_terminal_on_compile=cfg.get<bool>("project.clear_terminal_on_compile");
project.ctags_command=cfg.get<std::string>("project.ctags_command");
project.python_command=cfg.get<std::string>("project.python_command");
terminal.history_size=cfg.get<int>("terminal.history_size");
terminal.font=cfg.get<std::string>("terminal.font");
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.cmake.command = cfg.get<std::string>("project.cmake.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.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.clear_terminal_on_compile = cfg.get<bool>("project.clear_terminal_on_compile");
project.ctags_command = cfg.get<std::string>("project.ctags_command");
project.python_command = cfg.get<std::string>("project.python_command");
terminal.history_size = cfg.get<int>("terminal.history_size");
terminal.font = cfg.get<std::string>("terminal.font");
}

12
src/config.h

@ -1,11 +1,11 @@
#pragma once
#include <boost/property_tree/json_parser.hpp>
#include "dispatcher.h"
#include <boost/filesystem.hpp>
#include <unordered_map>
#include <boost/property_tree/json_parser.hpp>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "dispatcher.h"
class Config {
public:
@ -93,8 +93,10 @@ public:
std::unordered_map<std::string, DocumentationSearch> documentation_searches;
};
private:
Config();
public:
static Config &get() {
static Config singleton;
@ -119,7 +121,7 @@ private:
void find_or_create_config_files();
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);
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 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 = "");
void read(const boost::property_tree::ptree &cfg);
};

174
src/ctags.cc

@ -1,38 +1,38 @@
#include "ctags.h"
#include "config.h"
#include "terminal.h"
#include "project_build.h"
#include "filesystem.h"
#include "project_build.h"
#include "terminal.h"
#include <climits>
#include <iostream>
#include <vector>
#include <regex>
#include <climits>
#include <vector>
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 run_path=build->project_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 run_path = build->project_path;
std::string exclude;
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())
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())
exclude+=" --exclude="+relative_debug_path.string();
exclude += " --exclude=" + relative_debug_path.string();
}
else {
boost::system::error_code ec;
if(boost::filesystem::is_directory(path, ec) || ec)
run_path=path;
run_path = path;
else
run_path=path.parent_path();
run_path = path.parent_path();
}
std::stringstream stdin_stream;
//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 command=Config::get().project.ctags_command+exclude+" --fields=ns --sort=foldcase -I \"override noexcept\" -f - -R *";
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 *";
Terminal::get().process(stdin_stream, *stdout_stream, command, run_path);
return {run_path, std::move(stdout_stream)};
}
@ -41,56 +41,56 @@ Ctags::Location Ctags::get_location(const std::string &line, bool markup) {
Location location;
#ifdef _WIN32
auto line_fixed=line;
if(!line_fixed.empty() && line_fixed.back()=='\r')
auto line_fixed = line;
if(!line_fixed.empty() && line_fixed.back() == '\r')
line_fixed.pop_back();
#else
auto &line_fixed=line;
auto &line_fixed = line;
#endif
const static std::regex regex(R"(^([^\t]+)\t([^\t]+)\t(?:/\^)?([ \t]*)(.+?)(\$/)?;"\tline:([0-9]+)\t?[a-zA-Z]*:?(.*)$)");
std::smatch sm;
if(std::regex_match(line_fixed, sm, regex)) {
location.symbol=sm[1].str();
location.symbol = sm[1].str();
//fix location.symbol for operators
if(9<location.symbol.size() && location.symbol[8]==' ' && location.symbol.compare(0, 8, "operator")==0) {
auto &chr=location.symbol[9];
if(!((chr>='a' && chr<='z') || (chr>='A' && chr<='Z') || (chr>='0' && chr<='9') || chr=='_'))
if(9 < location.symbol.size() && location.symbol[8] == ' ' && location.symbol.compare(0, 8, "operator") == 0) {
auto &chr = location.symbol[9];
if(!((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr == '_'))
location.symbol.erase(8, 1);
}
location.file_path=sm[2].str();
location.source=sm[4].str();
location.file_path = sm[2].str();
location.source = sm[4].str();
try {
location.line=std::stoul(sm[6])-1;
location.line = std::stoul(sm[6]) - 1;
}
catch(const std::exception&) {
location.line=0;
catch(const std::exception &) {
location.line = 0;
}
location.scope=sm[7].str();
location.scope = sm[7].str();
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);
if(pos!=std::string::npos)
location.index+=pos;
size_t pos = location.source.find(location.symbol);
if(pos != std::string::npos)
location.index += pos;
if(markup) {
location.source=Glib::Markup::escape_text(location.source);
auto symbol=Glib::Markup::escape_text(location.symbol);
pos=-1;
while((pos=location.source.find(symbol, pos+1))!=std::string::npos) {
location.source.insert(pos+symbol.size(), "</b>");
location.source = Glib::Markup::escape_text(location.source);
auto symbol = Glib::Markup::escape_text(location.symbol);
pos = -1;
while((pos = location.source.find(symbol, pos + 1)) != std::string::npos) {
location.source.insert(pos + symbol.size(), "</b>");
location.source.insert(pos, "<b>");
pos+=7+symbol.size();
pos += 7 + symbol.size();
}
}
}
else {
location.index=0;
location.source=location.symbol;
location.index = 0;
location.source = location.symbol;
if(markup)
location.source="<b>"+Glib::Markup::escape_text(location.source)+"</b>";
location.source = "<b>" + Glib::Markup::escape_text(location.source) + "</b>";
}
}
else
@ -102,90 +102,90 @@ Ctags::Location Ctags::get_location(const std::string &line, bool markup) {
///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> parts;
size_t text_start=-1;
for(size_t c=0;c<type.size();++c) {
auto &chr=type[c];
if((chr>='0' && chr<='9') || (chr>='a' && chr<='z') || (chr>='A' && chr<='Z') || chr=='_' || chr=='~') {
if(text_start==static_cast<size_t>(-1))
text_start=c;
size_t text_start = -1;
for(size_t c = 0; c < type.size(); ++c) {
auto &chr = type[c];
if((chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || chr == '_' || chr == '~') {
if(text_start == static_cast<size_t>(-1))
text_start = c;
}
else {
if(text_start!=static_cast<size_t>(-1)) {
parts.emplace_back(type.substr(text_start, c-text_start));
text_start=-1;
if(text_start != static_cast<size_t>(-1)) {
parts.emplace_back(type.substr(text_start, c - text_start));
text_start = -1;
}
if(chr=='*' || chr=='&')
parts.emplace_back(std::string()+chr);
if(chr == '*' || chr == '&')
parts.emplace_back(std::string() + chr);
}
}
return parts;
}
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);
if(result.second->tellg()==0)
if(result.second->tellg() == 0)
return std::vector<Location>();
result.second->seekg(0, std::ios::beg);
//insert name into type
size_t c=0;
size_t bracket_count=0;
for(;c<type.size();++c) {
if(type[c]=='<')
size_t c = 0;
size_t bracket_count = 0;
for(; c < type.size(); ++c) {
if(type[c] == '<')
++bracket_count;
else if(type[c]=='>')
else if(type[c] == '>')
--bracket_count;
else if(bracket_count==0 && type[c]=='(')
else if(bracket_count == 0 && type[c] == '(')
break;
}
auto full_type=type;
auto full_type = type;
full_type.insert(c, name);
auto parts=get_type_parts(full_type);
auto parts = get_type_parts(full_type);
std::string line;
long best_score=LONG_MIN;
long best_score = LONG_MIN;
std::vector<Location> best_locations;
while(std::getline(*result.second, line)) {
if(line.size()>2048)
if(line.size() > 2048)
continue;
auto location=Ctags::get_location(line, false);
auto location = Ctags::get_location(line, false);
if(!location.scope.empty()) {
if(location.scope+"::"+location.symbol!=name)
if(location.scope + "::" + location.symbol != name)
continue;
}
else if(location.symbol!=name)
else if(location.symbol != name)
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
long score=0;
size_t source_index=0;
for(auto &part: parts) {
bool found=false;
for(auto c=source_index;c<source_parts.size();++c) {
if(part==source_parts[c]) {
source_index=c+1;
long score = 0;
size_t source_index = 0;
for(auto &part : parts) {
bool found = false;
for(auto c = source_index; c < source_parts.size(); ++c) {
if(part == source_parts[c]) {
source_index = c + 1;
++score;
found=true;
found = true;
break;
}
}
if(!found)
--score;
}
size_t index=0;
for(auto &source_part: source_parts) {
bool found=false;
for(auto c=index;c<parts.size();++c) {
if(source_part==parts[c]) {
index=c+1;
size_t index = 0;
for(auto &source_part : source_parts) {
bool found = false;
for(auto c = index; c < parts.size(); ++c) {
if(source_part == parts[c]) {
index = c + 1;
++score;
found=true;
found = true;
break;
}
}
@ -193,12 +193,12 @@ std::vector<Ctags::Location> Ctags::get_locations(const boost::filesystem::path
--score;
}
if(score>best_score) {
best_score=score;
if(score > best_score) {
best_score = score;
best_locations.clear();
best_locations.emplace_back(location);
}
else if(score==best_score)
else if(score == best_score)
best_locations.emplace_back(location);
}

5
src/ctags.h

@ -1,7 +1,7 @@
#pragma once
#include <string>
#include <boost/filesystem.hpp>
#include <sstream>
#include <string>
#include <vector>
class Ctags {
@ -17,11 +17,12 @@ public:
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 std::vector<Location> get_locations(const boost::filesystem::path &path, const std::string &name, const std::string &type);
private:
static std::vector<std::string> get_type_parts(const std::string &type);
};

377
src/debug_lldb.cc

@ -3,12 +3,12 @@
#ifdef __APPLE__
#include <cstdlib>
#endif
#include <boost/filesystem.hpp>
#include <iostream>
#include "terminal.h"
#include "config.h"
#include "filesystem.h"
#include "process.hpp"
#include "config.h"
#include "terminal.h"
#include <boost/filesystem.hpp>
#include <iostream>
extern char **environ;
@ -16,7 +16,7 @@ void log(const char *msg, void *) {
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")) {
#ifdef __APPLE__
std::string debug_server_path("/usr/local/opt/llvm/bin/debugserver");
@ -35,29 +35,29 @@ 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::string executable;
std::vector<std::string> arguments;
size_t start_pos=std::string::npos;
bool quote=false;
bool double_quote=false;
size_t backslash_count=0;
for(size_t c=0;c<=command.size();c++) {
if(c==command.size() || (!quote && !double_quote && backslash_count%2==0 && command[c]==' ')) {
if(c>0 && start_pos!=std::string::npos) {
auto argument=command.substr(start_pos, c-start_pos);
size_t start_pos = std::string::npos;
bool quote = false;
bool double_quote = false;
size_t backslash_count = 0;
for(size_t c = 0; c <= command.size(); c++) {
if(c == command.size() || (!quote && !double_quote && backslash_count % 2 == 0 && command[c] == ' ')) {
if(c > 0 && start_pos != std::string::npos) {
auto argument = command.substr(start_pos, c - start_pos);
if(executable.empty()) {
//Check for environment variable
bool env_arg=false;
for(size_t c=0;c<argument.size();++c) {
if((argument[c]>='a' && argument[c]<='z') || (argument[c]>='A' && argument[c]<='Z') ||
(argument[c]>='0' && argument[c]<='9') || argument[c]=='_')
bool env_arg = false;
for(size_t c = 0; c < argument.size(); ++c) {
if((argument[c] >= 'a' && argument[c] <= 'z') || (argument[c] >= 'A' && argument[c] <= 'Z') ||
(argument[c] >= '0' && argument[c] <= '9') || argument[c] == '_')
continue;
else if(argument[c]=='=' && c+1<argument.size()) {
environment.emplace_back(argument.substr(0, c+1)+filesystem::unescape_argument(argument.substr(c+1)));
env_arg=true;
else if(argument[c] == '=' && c + 1 < argument.size()) {
environment.emplace_back(argument.substr(0, c + 1) + filesystem::unescape_argument(argument.substr(c + 1)));
env_arg = true;
break;
}
else
@ -65,66 +65,66 @@ std::tuple<std::vector<std::string>, std::string, std::vector<std::string> > Deb
}
if(!env_arg)
executable=filesystem::unescape_argument(argument);
executable = filesystem::unescape_argument(argument);
}
else
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)
quote=!quote;
else if(command[c]=='"' && backslash_count%2==0 && !quote)
double_quote=!double_quote;
else if(command[c]=='\\' && !quote && !double_quote)
else if(command[c] == '\'' && backslash_count % 2 == 0 && !double_quote)
quote = !quote;
else if(command[c] == '"' && backslash_count % 2 == 0 && !quote)
double_quote = !double_quote;
else if(command[c] == '\\' && !quote && !double_quote)
++backslash_count;
else
backslash_count=0;
if(c<command.size() && start_pos==std::string::npos && command[c]!=' ')
start_pos=c;
backslash_count = 0;
if(c < command.size() && start_pos == std::string::npos && command[c] != ' ')
start_pos = c;
}
return std::make_tuple(environment, executable, arguments);
}
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) {
if(!debugger) {
lldb::SBDebugger::Initialize();
debugger=std::make_unique<lldb::SBDebugger>(lldb::SBDebugger::Create(true, log, nullptr));
listener=std::make_unique<lldb::SBListener>("juCi++ lldb listener");
debugger = std::make_unique<lldb::SBDebugger>(lldb::SBDebugger::Create(true, log, nullptr));
listener = std::make_unique<lldb::SBListener>("juCi++ lldb listener");
}
//Create executable string and argument array
auto parsed_run_arguments=parse_run_arguments(command);
auto &environment_from_arguments=std::get<0>(parsed_run_arguments);
auto &executable=std::get<1>(parsed_run_arguments);
auto parsed_run_arguments = parse_run_arguments(command);
auto &environment_from_arguments = std::get<0>(parsed_run_arguments);
auto &executable = std::get<1>(parsed_run_arguments);
#ifdef _WIN32
if(remote_host.empty())
executable+=".exe";
executable += ".exe";
#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());
for(auto &argument : arguments)
argv.emplace_back(argument.c_str());
argv.emplace_back(nullptr);
auto target=debugger->CreateTarget(executable.c_str());
auto target = debugger->CreateTarget(executable.c_str());
if(!target.IsValid()) {
Terminal::get().async_print("Error (debug): Could not create debug target to: "+executable+'\n', true);
for(auto &handler: on_exit)
Terminal::get().async_print("Error (debug): Could not create debug target to: " + executable + '\n', true);
for(auto &handler : on_exit)
handler(-1);
return;
}
//Set breakpoints
for(auto &breakpoint: breakpoints) {
for(auto &breakpoint : breakpoints) {
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);
for(auto &handler: on_exit)
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)
handler(-1);
return;
}
@ -132,30 +132,30 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
lldb::SBError error;
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));
if(error.Fail()) {
Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true);
for(auto &handler: on_exit)
Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
for(auto &handler : on_exit)
handler(-1);
return;
}
lldb::SBEvent event;
while(true) {
if(listener->GetNextEvent(event)) {
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged)>0) {
auto state=process->GetStateFromEvent(event);
this->state=state;
if(state==lldb::StateType::eStateConnected)
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) {
auto state = process->GetStateFromEvent(event);
this->state = state;
if(state == lldb::StateType::eStateConnected)
break;
}
}
}
// Create environment array
std::vector<const char*> environment;
std::vector<const char *> environment;
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(nullptr);
@ -165,48 +165,48 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
}
else {
// Create environment array
std::vector<const char*> environment;
std::vector<const char *> environment;
environment.reserve(environment_from_arguments.size());
for(auto &e: environment_from_arguments)
for(auto &e : environment_from_arguments)
environment.emplace_back(e.c_str());
size_t environ_size=0;
while(environ[environ_size]!=nullptr)
size_t environ_size = 0;
while(environ[environ_size] != nullptr)
++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(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));
}
if(error.Fail()) {
Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true);
for(auto &handler: on_exit)
Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
for(auto &handler : on_exit)
handler(-1);
return;
}
if(debug_thread.joinable())
debug_thread.join();
for(auto &handler: on_start)
for(auto &handler : on_start)
handler(*process);
for(auto &command: startup_commands) {
for(auto &command : startup_commands) {
lldb::SBCommandReturnObject command_return_object;
debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, false);
}
debug_thread=std::thread([this]() {
debug_thread = std::thread([this]() {
lldb::SBEvent event;
while(true) {
std::unique_lock<std::mutex> lock(mutex);
if(listener->GetNextEvent(event)) {
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged)>0) {
auto state=process->GetStateFromEvent(event);
this->state=state;
if(state==lldb::StateType::eStateStopped) {
for(uint32_t c=0;c<process->GetNumThreads();c++) {
auto thread=process->GetThreadAtIndex(c);
if(thread.GetStopReason()>=2) {
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) {
auto state = process->GetStateFromEvent(event);
this->state = state;
if(state == lldb::StateType::eStateStopped) {
for(uint32_t c = 0; c < process->GetNumThreads(); c++) {
auto thread = process->GetThreadAtIndex(c);
if(thread.GetStopReason() >= 2) {
process->SetSelectedThreadByIndexID(thread.GetIndexID());
break;
}
@ -214,32 +214,32 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
}
lock.unlock();
for(auto &handler: on_event)
for(auto &handler : on_event)
handler(event);
lock.lock();
if(state==lldb::StateType::eStateExited || state==lldb::StateType::eStateCrashed) {
auto exit_status=state==lldb::StateType::eStateCrashed?-1:process->GetExitStatus();
if(state == lldb::StateType::eStateExited || state == lldb::StateType::eStateCrashed) {
auto exit_status = state == lldb::StateType::eStateCrashed ? -1 : process->GetExitStatus();
lock.unlock();
for(auto &handler: on_exit)
for(auto &handler : on_exit)
handler(exit_status);
lock.lock();
process.reset();
this->state=lldb::StateType::eStateInvalid;
this->state = lldb::StateType::eStateInvalid;
return;
}
}
if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDOUT)>0) {
if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDOUT) > 0) {
char buffer[buffer_size];
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));
}
//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];
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);
}
}
@ -251,45 +251,45 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat
void Debug::LLDB::continue_debug() {
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped)
if(state == lldb::StateType::eStateStopped)
process->Continue();
}
void Debug::LLDB::stop() {
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateRunning) {
auto error=process->Stop();
if(state == lldb::StateType::eStateRunning) {
auto error = process->Stop();
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() {
std::unique_lock<std::mutex> lock(mutex);
if(process) {
auto error=process->Kill();
auto error = process->Kill();
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() {
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) {
if(state == lldb::StateType::eStateStopped) {
process->GetSelectedThread().StepOver();
}
}
void Debug::LLDB::step_into() {
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) {
if(state == lldb::StateType::eStateStopped) {
process->GetSelectedThread().StepInto();
}
}
void Debug::LLDB::step_out() {
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) {
if(state == lldb::StateType::eStateStopped) {
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> command_return;
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;
debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true);
auto output=command_return_object.GetOutput();
auto output = command_return_object.GetOutput();
if(output)
command_return.first=output;
auto error=command_return_object.GetError();
command_return.first = output;
auto error = command_return_object.GetError();
if(error)
command_return.second=error;
command_return.second = error;
}
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<Frame> backtrace;
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) {
auto thread=process->GetSelectedThread();
for(uint32_t c_f=0;c_f<thread.GetNumFrames();c_f++) {
if(state == lldb::StateType::eStateStopped) {
auto thread = process->GetSelectedThread();
for(uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) {
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)
backtrace_frame.function_name=frame.GetFunctionName();
if(frame.GetFunctionName() != nullptr)
backtrace_frame.function_name = frame.GetFunctionName();
auto module_filename=frame.GetModule().GetFileSpec().GetFilename();
if(module_filename!=nullptr) {
backtrace_frame.module_filename=module_filename;
auto module_filename = frame.GetModule().GetFileSpec().GetFilename();
if(module_filename != nullptr) {
backtrace_frame.module_filename = module_filename;
}
auto line_entry=frame.GetLineEntry();
auto line_entry = frame.GetLineEntry();
if(line_entry.IsValid()) {
lldb::SBStream stream;
line_entry.GetFileSpec().GetDescription(stream);
auto column=line_entry.GetColumn();
if(column==0)
column=1;
backtrace_frame.file_path=filesystem::get_normal_path(stream.GetData());
backtrace_frame.line_nr=line_entry.GetLine();
backtrace_frame.line_index=column;
auto column = line_entry.GetColumn();
if(column == 0)
column = 1;
backtrace_frame.file_path = filesystem::get_normal_path(stream.GetData());
backtrace_frame.line_nr = line_entry.GetLine();
backtrace_frame.line_index = column;
}
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> variables;
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) {
for(uint32_t c_t=0;c_t<process->GetNumThreads();c_t++) {
auto thread=process->GetThreadAtIndex(c_t);
for(uint32_t c_f=0;c_f<thread.GetNumFrames();c_f++) {
auto frame=thread.GetFrameAtIndex(c_f);
auto values=frame.GetVariables(true, true, true, false);
for(uint32_t value_index=0;value_index<values.GetSize();value_index++) {
if(state == lldb::StateType::eStateStopped) {
for(uint32_t c_t = 0; c_t < process->GetNumThreads(); c_t++) {
auto thread = process->GetThreadAtIndex(c_t);
for(uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) {
auto frame = thread.GetFrameAtIndex(c_f);
auto values = frame.GetVariables(true, true, true, false);
for(uint32_t value_index = 0; value_index < values.GetSize(); value_index++) {
lldb::SBStream stream;
auto value=values.GetValueAtIndex(value_index);
auto value = values.GetValueAtIndex(value_index);
Debug::LLDB::Variable variable;
variable.thread_index_id=thread.GetIndexID();
variable.frame_index=c_f;
if(value.GetName()!=nullptr)
variable.name=value.GetName();
variable.thread_index_id = thread.GetIndexID();
variable.frame_index = c_f;
if(value.GetName() != nullptr)
variable.name = value.GetName();
value.GetDescription(stream);
variable.value=stream.GetData();
variable.value = stream.GetData();
auto declaration=value.GetDeclaration();
auto declaration = value.GetDeclaration();
if(declaration.IsValid()) {
variable.declaration_found=true;
variable.line_nr=declaration.GetLine();
variable.line_index=declaration.GetColumn();
if(variable.line_index==0)
variable.line_index=1;
variable.declaration_found = true;
variable.line_nr = declaration.GetLine();
variable.line_index = declaration.GetColumn();
if(variable.line_index == 0)
variable.line_index = 1;
auto file_spec=declaration.GetFileSpec();
variable.file_path=filesystem::get_normal_path(file_spec.GetDirectory());
variable.file_path/=file_spec.GetFilename();
auto file_spec = declaration.GetFileSpec();
variable.file_path = filesystem::get_normal_path(file_spec.GetDirectory());
variable.file_path /= file_spec.GetFilename();
}
else {
variable.declaration_found=false;
auto line_entry=frame.GetLineEntry();
variable.declaration_found = false;
auto line_entry = frame.GetLineEntry();
if(line_entry.IsValid()) {
variable.line_nr=line_entry.GetLine();
variable.line_index=line_entry.GetColumn();
if(variable.line_index==0)
variable.line_index=1;
variable.line_nr = line_entry.GetLine();
variable.line_index = line_entry.GetColumn();
if(variable.line_index == 0)
variable.line_index = 1;
auto file_spec=line_entry.GetFileSpec();
variable.file_path=filesystem::get_normal_path(file_spec.GetDirectory());
variable.file_path/=file_spec.GetFilename();
auto file_spec = line_entry.GetFileSpec();
variable.file_path = filesystem::get_normal_path(file_spec.GetDirectory());
variable.file_path /= file_spec.GetFilename();
}
}
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) {
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) {
if(thread_index_id!=0)
if(state == lldb::StateType::eStateStopped) {
if(thread_index_id != 0)
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 variable_value;
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) {
auto frame=process->GetSelectedThread().GetSelectedFrame();
if(state == lldb::StateType::eStateStopped) {
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
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;
auto value=values.GetValueAtIndex(value_index);
auto value = values.GetValueAtIndex(value_index);
if(value.GetName()!=nullptr && value.GetName()==variable) {
auto declaration=value.GetDeclaration();
if(value.GetName() != nullptr && value.GetName() == variable) {
auto declaration = value.GetDeclaration();
if(declaration.IsValid()) {
if(declaration.GetLine()==line_nr && (declaration.GetColumn()==0 || declaration.GetColumn()==line_index)) {
auto file_spec=declaration.GetFileSpec();
auto value_decl_path=filesystem::get_normal_path(file_spec.GetDirectory());
value_decl_path/=file_spec.GetFilename();
if(value_decl_path==file_path) {
if(declaration.GetLine() == line_nr && (declaration.GetColumn() == 0 || declaration.GetColumn() == line_index)) {
auto file_spec = declaration.GetFileSpec();
auto value_decl_path = filesystem::get_normal_path(file_spec.GetDirectory());
value_decl_path /= file_spec.GetFilename();
if(value_decl_path == file_path) {
value.GetDescription(stream);
variable_value=stream.GetData();
variable_value = stream.GetData();
break;
}
}
@ -448,11 +449,11 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil
}
if(variable_value.empty()) {
//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()) {
lldb::SBStream 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 return_value;
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateStopped) {
auto thread=process->GetSelectedThread();
auto thread_return_value=thread.GetStopReturnValue();
if(state == lldb::StateType::eStateStopped) {
auto thread = process->GetSelectedThread();
auto thread_return_value = thread.GetStopReturnValue();
if(thread_return_value.IsValid()) {
auto line_entry=thread.GetSelectedFrame().GetLineEntry();
auto line_entry = thread.GetSelectedFrame().GetLineEntry();
if(line_entry.IsValid()) {
lldb::SBStream stream;
line_entry.GetFileSpec().GetDescription(stream);
if(filesystem::get_normal_path(stream.GetData())==file_path && line_entry.GetLine()==line_nr &&
(line_entry.GetColumn()==0 || line_entry.GetColumn()==line_index)) {
if(filesystem::get_normal_path(stream.GetData()) == file_path && line_entry.GetLine() == line_nr &&
(line_entry.GetColumn() == 0 || line_entry.GetColumn() == line_index)) {
lldb::SBStream 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() {
std::unique_lock<std::mutex> lock(mutex);
return state==lldb::StateType::eStateInvalid;
return state == lldb::StateType::eStateInvalid;
}
bool Debug::LLDB::is_stopped() {
std::unique_lock<std::mutex> lock(mutex);
return state==lldb::StateType::eStateStopped;
return state == lldb::StateType::eStateStopped;
}
bool Debug::LLDB::is_running() {
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) {
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())
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) {
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::eStateStopped || state==lldb::eStateRunning) {
auto target=process->GetTarget();
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++) {
auto breakpoint=target.GetBreakpointAtIndex(b_index);
for(uint32_t l_index=0;l_index<breakpoint.GetNumLocations();l_index++) {
auto line_entry=breakpoint.GetLocationAtIndex(l_index).GetAddress().GetLineEntry();
if(line_entry.GetLine()==static_cast<uint32_t>(line_nr_try)) {
auto file_spec=line_entry.GetFileSpec();
auto breakpoint_path=filesystem::get_normal_path(file_spec.GetDirectory());
breakpoint_path/=file_spec.GetFilename();
if(breakpoint_path==file_path) {
if(state == lldb::eStateStopped || state == lldb::eStateRunning) {
auto target = process->GetTarget();
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++) {
auto breakpoint = target.GetBreakpointAtIndex(b_index);
for(uint32_t l_index = 0; l_index < breakpoint.GetNumLocations(); l_index++) {
auto line_entry = breakpoint.GetLocationAtIndex(l_index).GetAddress().GetLineEntry();
if(line_entry.GetLine() == static_cast<uint32_t>(line_nr_try)) {
auto file_spec = line_entry.GetFileSpec();
auto breakpoint_path = filesystem::get_normal_path(file_spec.GetDirectory());
breakpoint_path /= file_spec.GetFilename();
if(breakpoint_path == file_path) {
if(!target.BreakpointDelete(breakpoint.GetID()))
Terminal::get().async_print("Error (debug): Could not delete breakpoint at: "+file_path.string()+":"+std::to_string(line_nr)+'\n', true);
Terminal::get().async_print("Error (debug): Could not delete breakpoint at: " + file_path.string() + ":" + std::to_string(line_nr) + '\n', true);
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) {
std::unique_lock<std::mutex> lock(mutex);
if(state==lldb::StateType::eStateRunning) {
if(state == lldb::StateType::eStateRunning) {
process->PutSTDIN(buffer.c_str(), buffer.size());
}
}

14
src/debug_lldb.h

@ -2,8 +2,8 @@
#include <boost/filesystem.hpp>
#include <list>
#include <lldb/API/LLDB.h>
#include <thread>
#include <mutex>
#include <thread>
#include <tuple>
namespace Debug {
@ -29,8 +29,10 @@ namespace Debug {
int line_nr;
int line_index;
};
private:
LLDB();
public:
static LLDB &get() {
static LLDB singleton;
@ -45,9 +47,9 @@ namespace Debug {
std::mutex mutex;
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::string> &startup_commands={}, const std::string &remote_host="");
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::string> &startup_commands = {}, const std::string &remote_host = "");
void continue_debug(); //can't use continue as function name
void stop();
void kill();
@ -57,7 +59,7 @@ namespace Debug {
std::pair<std::string, std::string> run_command(const std::string &command);
std::vector<Frame> get_backtrace();
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();
@ -85,4 +87,4 @@ namespace Debug {
size_t buffer_size;
};
}
} // namespace Debug

42
src/dialogs.cc

@ -1,20 +1,20 @@
#include "dialogs.h"
#include <cmath>
Dialog::Message::Message(const std::string &text): Gtk::Window(Gtk::WindowType::WINDOW_POPUP) {
auto g_application=g_application_get_default();
auto gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
Dialog::Message::Message(const std::string &text) : Gtk::Window(Gtk::WindowType::WINDOW_POPUP) {
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
set_transient_for(*application->get_active_window());
set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
set_modal(true);
set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_NOTIFICATION);
property_decorated()=false;
property_decorated() = false;
set_skip_taskbar_hint(true);
auto box=Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL));
auto label=Gtk::manage(new Gtk::Label(text));
auto box = Gtk::manage(new Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL));
auto label = Gtk::manage(new Gtk::Label(text));
label->set_padding(10, 10);
box->pack_start(*label);
add(*box);
@ -39,13 +39,15 @@ std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::s
#ifdef __APPLE__
class FileChooserDialog : public Gtk::FileChooserDialog {
Gtk::FileChooserAction action;
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:
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) {
auto unicode=gdk_keyval_to_unicode(key_event->keyval);
if(unicode>31 && unicode!=127)
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);
if(unicode > 31 && unicode != 127)
return true;
}
return Gtk::FileChooserDialog::on_key_press_event(key_event);
@ -56,24 +58,24 @@ std::string Dialog::gtk_dialog(const boost::filesystem::path &path, const std::s
Gtk::FileChooserDialog dialog(title, action);
#endif
auto g_application=g_application_get_default();
auto gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
dialog.set_transient_for(*application->get_active_window());
dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
if(title=="Save File As")
gtk_file_chooser_set_filename(reinterpret_cast<GtkFileChooser*>(dialog.gobj()), path.string().c_str());
if(title == "Save File As")
gtk_file_chooser_set_filename(reinterpret_cast<GtkFileChooser *>(dialog.gobj()), path.string().c_str());
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 {
boost::system::error_code ec;
auto current_path=boost::filesystem::current_path(ec);
auto current_path = boost::filesystem::current_path(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);
return dialog.run() == Gtk::RESPONSE_OK ? dialog.get_filename() : "";
}

5
src/dialogs.h

@ -1,8 +1,8 @@
#pragma once
#include <string>
#include <boost/filesystem.hpp>
#include <vector>
#include <gtkmm.h>
#include <string>
#include <vector>
class Dialog {
public:
@ -15,6 +15,7 @@ public:
class Message : public Gtk::Window {
public:
Message(const std::string &text);
protected:
bool on_delete_event(GdkEventAny *event) override;
};

9
src/dialogs_unix.cc

@ -2,7 +2,7 @@
std::string Dialog::open_folder(const boost::filesystem::path &path) {
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);
}
@ -14,19 +14,18 @@ std::string Dialog::new_file(const boost::filesystem::path &path) {
std::string Dialog::new_folder(const boost::filesystem::path &path) {
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);
}
std::string Dialog::open_file(const boost::filesystem::path &path) {
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);
}
std::string Dialog::save_file_as(const boost::filesystem::path &path) {
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);
}

60
src/dialogs_win.cc

@ -1,30 +1,30 @@
#include "dialogs.h"
#include "singletons.h"
#include "juci.h"
#include "singletons.h"
#undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_VISTA
#undef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#include <windows.h>
#include <shobjidl.h>
#include <vector>
#include <windows.h>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
class Win32Dialog {
public:
Win32Dialog() {};
Win32Dialog(){};
~Win32Dialog() {
if(dialog!=nullptr)
if(dialog != nullptr)
dialog->Release();
}
/** 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))
return "";
@ -36,7 +36,7 @@ public:
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))
return "";
@ -46,91 +46,91 @@ public:
return "";
std::vector<COMDLG_FILTERSPEC> extensions;
if(!file_path.empty()) {
if(file_path.has_extension() && file_path.filename()!=file_path.extension()) {
auto extension=(L"*"+file_path.extension().native()).c_str();
if(file_path.has_extension() && file_path.filename() != file_path.extension()) {
auto extension = (L"*" + file_path.extension().native()).c_str();
extensions.emplace_back(COMDLG_FILTERSPEC{extension, extension});
if(!set_default_file_extension(extension))
return "";
}
}
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 show();
}
private:
IFileDialog *dialog=nullptr;
IFileDialog *dialog = nullptr;
DWORD options;
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;
if(dialog->GetOptions(&options)!=S_OK)
if(dialog->GetOptions(&options) != S_OK)
return false;
return true;
}
/** available options are listed at https://msdn.microsoft.com/en-gb/library/windows/desktop/dn457282(v=vs.85).aspx */
bool add_option(unsigned option) {
if(dialog->SetOptions(options | option)!=S_OK)
if(dialog->SetOptions(options | option) != S_OK)
return false;
return true;
}
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 true;
}
/** Sets the extensions the browser can find */
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 true;
}
/** Sets the directory to start browsing */
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 gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Application>::cast_static(gio_application);
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 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;
if(current_path.empty())
current_path=boost::filesystem::current_path(ec);
current_path = boost::filesystem::current_path(ec);
if(ec)
return false;
std::wstring path=current_path.native();
size_t pos=0;
while((pos=path.find(L'/', pos))!=std::wstring::npos) {//TODO: issue bug report on boost::filesystem::path::native on MSYS2
std::wstring path = current_path.native();
size_t pos = 0;
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"\\");
pos++;
}
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;
if(dialog->SetFolder(folder)!=S_OK)
if(dialog->SetFolder(folder) != S_OK)
return false;
folder->Release();
return true;
}
std::string show() {
if(dialog->Show(nullptr)!=S_OK)
if(dialog->Show(nullptr) != S_OK)
return "";
IShellItem *result = nullptr;
if(dialog->GetResult(&result)!=S_OK)
if(dialog->GetResult(&result) != S_OK)
return "";
LPWSTR file_path = nullptr;
auto hresult=result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
auto hresult = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
result->Release();
if(hresult!=S_OK)
if(hresult != S_OK)
return "";
std::wstring file_path_wstring(file_path);
std::string file_path_string(file_path_wstring.begin(), file_path_wstring.end());
@ -150,7 +150,7 @@ std::string Dialog::new_file() {
std::string Dialog::new_folder() {
//Win32 (IFileDialog) does not support create 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);
}

419
src/directories.cc

@ -1,38 +1,38 @@
#include "directories.h"
#include <algorithm>
#include "entrybox.h"
#include "filesystem.h"
#include "notebook.h"
#include "source.h"
#include "terminal.h"
#include "notebook.h"
#include "filesystem.h"
#include "entrybox.h"
#include <algorithm>
bool Directories::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path &path, const Gtk::SelectionData &selection_data) const {
return true;
}
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) {
if(path.size()==1)
auto get_target_folder = [this, &directories](const TreeModel::Path &path) {
if(path.size() == 1)
return directories.path;
else {
auto it=get_iter(path);
auto it = get_iter(path);
if(it) {
auto prev_path=path;
auto prev_path = path;
prev_path.up();
it=get_iter(prev_path);
it = get_iter(prev_path);
if(it)
return it->get_value(directories.column_record.path);
}
else {
auto prev_path=path;
auto prev_path = path;
prev_path.up();
if(prev_path.size()==1)
if(prev_path.size() == 1)
return directories.path;
else {
prev_path.up();
it=get_iter(prev_path);
it = get_iter(prev_path);
if(it)
return it->get_value(directories.column_record.path);
}
@ -41,24 +41,24 @@ bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &pat
return boost::filesystem::path();
};
auto it=directories.get_selection()->get_selected();
auto it = directories.get_selection()->get_selected();
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())
return false;
auto target_path=get_target_folder(path);
target_path/=source_path.filename();
auto target_path = get_target_folder(path);
target_path /= source_path.filename();
if(source_path==target_path)
if(source_path == target_path)
return false;
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;
}
bool is_directory=boost::filesystem::is_directory(source_path);
bool is_directory = boost::filesystem::is_directory(source_path);
if(is_directory)
Directories::get().remove_path(source_path);
@ -66,24 +66,24 @@ bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &pat
boost::system::error_code ec;
boost::filesystem::rename(source_path, target_path, 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;
}
for(size_t c=0;c<Notebook::get().size();c++) {
auto view=Notebook::get().get_view(c);
for(size_t c = 0; c < Notebook::get().size(); c++) {
auto view = Notebook::get().get_view(c);
if(is_directory) {
if(filesystem::file_in_path(view->file_path, source_path)) {
auto file_it=view->file_path.begin();
for(auto source_it=source_path.begin();source_it!=source_path.end();source_it++)
auto file_it = view->file_path.begin();
for(auto source_it = source_path.begin(); source_it != source_path.end(); source_it++)
file_it++;
auto new_file_path=target_path;
for(;file_it!=view->file_path.end();file_it++)
new_file_path/=*file_it;
auto new_file_path = target_path;
for(; file_it != view->file_path.end(); file_it++)
new_file_path /= *file_it;
view->rename(new_file_path);
}
}
else if(view->file_path==source_path) {
else if(view->file_path == source_path) {
view->rename(target_path);
break;
}
@ -98,7 +98,7 @@ bool Directories::TreeStore::drag_data_received_vfunc(const TreeModel::Path &pat
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;
}
@ -111,10 +111,10 @@ Directories::Directories() : Gtk::ListViewText(1) {
get_column(0)->set_title("");
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) {
if(auto renderer_text=dynamic_cast<Gtk::CellRendererText*>(renderer))
renderer_text->property_markup()=iter->get_value(column_record.markup);
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) {
if(auto renderer_text = dynamic_cast<Gtk::CellRendererText *>(renderer))
renderer_text->property_markup() = iter->get_value(column_record.markup);
});
get_style_context()->add_class("juci_directories");
@ -123,12 +123,12 @@ Directories::Directories() : Gtk::ListViewText(1) {
set_enable_search(true); //TODO: why does this not work in OS X?
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);
if (iter) {
auto filesystem_path=iter->get_value(column_record.path);
if(filesystem_path!="") {
if (boost::filesystem::is_directory(boost::filesystem::path(filesystem_path)))
if(iter) {
auto filesystem_path = iter->get_value(column_record.path);
if(filesystem_path != "") {
if(boost::filesystem::is_directory(boost::filesystem::path(filesystem_path)))
row_expanded(path) ? collapse_row(path) : expand_row(path, false);
else
Notebook::get().open(filesystem_path);
@ -136,12 +136,12 @@ Directories::Directories() : Gtk::ListViewText(1) {
}
});
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)=="")
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) == "")
add_or_update_path(iter->get_value(column_record.path), *iter, true);
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));
});
@ -153,9 +153,9 @@ Directories::Directories() : Gtk::ListViewText(1) {
if(menu_popup_row_path.empty())
return;
EntryBox::get().clear();
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);
auto target_path = (is_directory ? source_path : source_path.parent_path())/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);
auto target_path = (is_directory ? source_path : source_path.parent_path()) / content;
if(!boost::filesystem::exists(target_path)) {
if(filesystem::write(target_path, "")) {
update();
@ -163,20 +163,20 @@ Directories::Directories() : Gtk::ListViewText(1) {
on_save_file(target_path);
}
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;
}
}
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;
}
EntryBox::get().hide();
});
auto entry_it=EntryBox::get().entries.begin();
auto entry_it = EntryBox::get().entries.begin();
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();
});
EntryBox::get().show();
@ -195,9 +195,9 @@ Directories::Directories() : Gtk::ListViewText(1) {
if(menu_popup_row_path.empty())
return;
EntryBox::get().clear();
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);
auto target_path = (is_directory ? source_path : source_path.parent_path())/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);
auto target_path = (is_directory ? source_path : source_path.parent_path()) / content;
if(!boost::filesystem::exists(target_path)) {
boost::system::error_code ec;
boost::filesystem::create_directory(target_path, ec);
@ -206,20 +206,20 @@ Directories::Directories() : Gtk::ListViewText(1) {
select(target_path);
}
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;
}
}
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;
}
EntryBox::get().hide();
});
auto entry_it=EntryBox::get().entries.begin();
auto entry_it = EntryBox::get().entries.begin();
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();
});
EntryBox::get().show();
@ -240,13 +240,13 @@ Directories::Directories() : Gtk::ListViewText(1) {
if(menu_popup_row_path.empty())
return;
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){
bool is_directory=boost::filesystem::is_directory(source_path);
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);
auto target_path=source_path.parent_path()/content;
auto target_path = source_path.parent_path() / content;
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;
}
@ -256,55 +256,55 @@ Directories::Directories() : Gtk::ListViewText(1) {
boost::system::error_code ec;
boost::filesystem::rename(source_path, target_path, 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;
}
update();
on_save_file(target_path);
select(target_path);
for(size_t c=0;c<Notebook::get().size();c++) {
auto view=Notebook::get().get_view(c);
for(size_t c = 0; c < Notebook::get().size(); c++) {
auto view = Notebook::get().get_view(c);
if(is_directory) {
if(filesystem::file_in_path(view->file_path, source_path)) {
auto file_it=view->file_path.begin();
for(auto source_it=source_path.begin();source_it!=source_path.end();source_it++)
auto file_it = view->file_path.begin();
for(auto source_it = source_path.begin(); source_it != source_path.end(); source_it++)
file_it++;
auto new_file_path=target_path;
for(;file_it!=view->file_path.end();file_it++)
new_file_path/=*file_it;
auto new_file_path = target_path;
for(; file_it != view->file_path.end(); file_it++)
new_file_path /= *file_it;
view->rename(new_file_path);
}
}
else if(view->file_path==source_path) {
else if(view->file_path == source_path) {
view->rename(target_path);
std::string old_language_id;
if(view->language)
old_language_id=view->language->get_id();
view->language=Source::guess_language(target_path);
old_language_id = view->language->get_id();
view->language = Source::guess_language(target_path);
std::string new_language_id;
if(view->language)
new_language_id=view->language->get_id();
if(new_language_id!=old_language_id)
Terminal::get().print("Warning: language for "+target_path.string()+" has changed. Please reopen the file\n");
new_language_id = view->language->get_id();
if(new_language_id != old_language_id)
Terminal::get().print("Warning: language for " + target_path.string() + " has changed. Please reopen the file\n");
}
}
EntryBox::get().hide();
});
auto entry_it=EntryBox::get().entries.begin();
auto entry_it = EntryBox::get().entries.begin();
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();
});
EntryBox::get().show();
auto end_pos=Glib::ustring(menu_popup_row_path.filename().string()).rfind('.');
if(end_pos!=Glib::ustring::npos)
auto end_pos = Glib::ustring(menu_popup_row_path.filename().string()).rfind('.');
if(end_pos != Glib::ustring::npos)
entry_it->select_region(0, end_pos);
});
menu.append(menu_item_rename);
@ -313,28 +313,28 @@ Directories::Directories() : Gtk::ListViewText(1) {
menu_item_delete.signal_activate().connect([this] {
if(menu_popup_row_path.empty())
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_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();
if(result==Gtk::RESPONSE_YES) {
bool is_directory=boost::filesystem::is_directory(menu_popup_row_path);
if(result == Gtk::RESPONSE_YES) {
bool is_directory = boost::filesystem::is_directory(menu_popup_row_path);
boost::system::error_code ec;
boost::filesystem::remove_all(menu_popup_row_path, 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 {
update();
for(size_t c=0;c<Notebook::get().size();c++) {
auto view=Notebook::get().get_view(c);
for(size_t c = 0; c < Notebook::get().size(); c++) {
auto view = Notebook::get().get_view(c);
if(is_directory) {
if(filesystem::file_in_path(view->file_path, menu_popup_row_path))
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();
}
}
@ -350,10 +350,10 @@ Directories::Directories() : Gtk::ListViewText(1) {
set_headers_clickable();
forall([this](Gtk::Widget &widget) {
if(widget.get_name()=="GtkButton") {
if(widget.get_name() == "GtkButton") {
widget.signal_button_press_event().connect([this](GdkEventButton *event) {
if(event->type==GDK_BUTTON_PRESS && event->button==GDK_BUTTON_SECONDARY && !path.empty()) {
menu_popup_row_path=this->path;
if(event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY && !path.empty()) {
menu_popup_row_path = this->path;
menu_root.popup(event->button, event->time);
}
return true;
@ -373,18 +373,18 @@ void Directories::open(const boost::filesystem::path &dir_path) {
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?
auto title=path.filename().string();
size_t pos=0;
while((pos=title.find('_', pos))!=std::string::npos) {
auto title = path.filename().string();
size_t pos = 0;
while((pos = title.find('_', pos)) != std::string::npos) {
title.replace(pos, 1, "__");
pos+=2;
pos += 2;
}
get_column(0)->set_title(title);
for(auto &directory: directories) {
for(auto &directory : directories) {
if(directory.second.repository)
directory.second.repository->clear_saved_status();
}
@ -394,16 +394,16 @@ void Directories::open(const boost::filesystem::path &dir_path) {
}
void Directories::update() {
std::vector<std::pair<std::string, Gtk::TreeModel::Row> > saved_directories;
for(auto &directory: directories)
std::vector<std::pair<std::string, Gtk::TreeModel::Row>> saved_directories;
for(auto &directory : directories)
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);
}
void Directories::on_save_file(const boost::filesystem::path &file_path) {
auto it=directories.find(file_path.parent_path().string());
if(it!=directories.end()) {
auto it = directories.find(file_path.parent_path().string());
if(it != directories.end()) {
if(it->second.repository)
it->second.repository->clear_saved_status();
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) {
if(path=="")
if(path == "")
return;
if(!filesystem::file_in_path(select_path, path))
return;
//return if the select_path is already selected
auto iter=get_selection()->get_selected();
auto iter = get_selection()->get_selected();
if(iter) {
if(iter->get_value(column_record.path)==select_path)
if(iter->get_value(column_record.path) == select_path)
return;
}
std::list<boost::filesystem::path> paths;
boost::filesystem::path parent_path;
if(boost::filesystem::is_directory(select_path))
parent_path=select_path;
parent_path = select_path;
else
parent_path=select_path.parent_path();
parent_path = select_path.parent_path();
//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
tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter){
if(iter->get_value(column_record.path)==select_path) {
auto tree_path=Gtk::TreePath(iter);
tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter) {
if(iter->get_value(column_record.path) == select_path) {
auto tree_path = Gtk::TreePath(iter);
expand_to_path(tree_path);
set_cursor(tree_path);
return true;
@ -447,15 +447,15 @@ void Directories::select(const boost::filesystem::path &select_path) {
}
paths.emplace_front(parent_path);
while(parent_path!=path) {
parent_path=parent_path.parent_path();
while(parent_path != path) {
parent_path = parent_path.parent_path();
paths.emplace_front(parent_path);
}
//expand to select_path
for(auto &a_path: paths) {
tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator &iter){
if(iter->get_value(column_record.path)==a_path) {
for(auto &a_path : paths) {
tree_store->foreach_iter([this, &a_path](const Gtk::TreeModel::iterator &iter) {
if(iter->get_value(column_record.path) == a_path) {
add_or_update_path(a_path, *iter, true);
return true;
}
@ -464,9 +464,9 @@ void Directories::select(const boost::filesystem::path &select_path) {
}
//set cursor at select_path
tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter){
if(iter->get_value(column_record.path)==select_path) {
auto tree_path=Gtk::TreePath(iter);
tree_store->foreach_iter([this, &select_path](const Gtk::TreeModel::iterator &iter) {
if(iter->get_value(column_record.path) == select_path) {
auto tree_path = Gtk::TreePath(iter);
expand_to_path(tree_path);
set_cursor(tree_path);
return true;
@ -475,18 +475,18 @@ void Directories::select(const boost::filesystem::path &select_path) {
});
}
bool Directories::on_button_press_event(GdkEventButton* event) {
if(event->type==GDK_BUTTON_PRESS && event->button==GDK_BUTTON_SECONDARY) {
bool Directories::on_button_press_event(GdkEventButton *event) {
if(event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) {
EntryBox::get().hide();
Gtk::TreeModel::Path 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()) {
auto parent=get_model()->get_iter(path)->parent();
auto parent = get_model()->get_iter(path)->parent();
if(parent)
menu_popup_row_path=parent->get_value(column_record.path);
menu_popup_row_path = parent->get_value(column_record.path);
else {
menu_popup_row_path=this->path;
menu_popup_row_path = this->path;
menu_root.popup(event->button, event->time);
return true;
}
@ -495,7 +495,7 @@ bool Directories::on_button_press_event(GdkEventButton* event) {
return true;
}
else if(!this->path.empty()) {
menu_popup_row_path=this->path;
menu_popup_row_path = this->path;
menu_root.popup(event->button, event->time);
return true;
}
@ -505,34 +505,35 @@ bool Directories::on_button_press_event(GdkEventButton* event) {
}
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(path_it!=directories.end())
if(path_it != directories.end())
directories.erase(path_it);
return;
}
if(path_it==directories.end()) {
auto g_file=Gio::File::create_for_path(dir_path.string());
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 connection=std::make_shared<sigc::connection>();
if(path_it == directories.end()) {
auto g_file = Gio::File::create_for_path(dir_path.string());
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 connection = std::make_shared<sigc::connection>();
std::shared_ptr<Git::Repository> repository;
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,
const Glib::RefPtr<Gio::File> &,
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) {
if(repository)
repository->clear_saved_status();
connection->disconnect();
*connection=Glib::signal_timeout().connect([path_and_row, this]() {
if(directories.find(path_and_row->first.string())!=directories.end())
*connection = Glib::signal_timeout().connect([path_and_row, this]() {
if(directories.find(path_and_row->first.string()) != directories.end())
add_or_update_path(path_and_row->first, path_and_row->second, true);
return false;
}, 500);
@ -545,38 +546,38 @@ void Directories::add_or_update_path(const boost::filesystem::path &dir_path, co
});
if(repository) {
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,
const Glib::RefPtr<Gio::File>&,
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,
const Glib::RefPtr<Gio::File> &,
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=Glib::signal_timeout().connect([this, path_and_row] {
if(directories.find(path_and_row->first.string())!=directories.end())
*connection = Glib::signal_timeout().connect([this, path_and_row] {
if(directories.find(path_and_row->first.string()) != directories.end())
colorize_path(path_and_row->first, false);
return false;
}, 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.begin()->get_value(column_record.path)=="")
if(children.begin()->get_value(column_record.path) == "")
tree_store->erase(children.begin());
}
std::unordered_set<std::string> not_deleted;
boost::filesystem::directory_iterator end_it;
for(boost::filesystem::directory_iterator it(dir_path);it!=end_it;it++) {
auto filename=it->path().filename().string();
bool already_added=false;
for(boost::filesystem::directory_iterator it(dir_path); it != end_it; it++) {
auto filename = it->path().filename().string();
bool already_added = false;
if(children) {
for(auto &child: children) {
if(child->get_value(column_record.name)==filename) {
for(auto &child : children) {
if(child->get_value(column_record.name) == filename) {
not_deleted.emplace(filename);
already_added=true;
already_added = true;
break;
}
}
@ -587,33 +588,33 @@ 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.markup, Glib::Markup::escape_text(filename));
child->set_value(column_record.path, it->path());
if (boost::filesystem::is_directory(it->path())) {
child->set_value(column_record.id, '1'+filename);
auto grandchild=tree_store->append(child->children());
if(boost::filesystem::is_directory(it->path())) {
child->set_value(column_record.id, '1' + filename);
auto grandchild = tree_store->append(child->children());
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.type, PathType::UNKNOWN);
}
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)
child->set_value(column_record.type, PathType::UNKNOWN);
}
}
}
if(children) {
for(auto it=children.begin();it!=children.end();) {
if(not_deleted.count(it->get_value(column_record.name))==0) {
it=tree_store->erase(it);
for(auto it = children.begin(); it != children.end();) {
if(not_deleted.count(it->get_value(column_record.name)) == 0) {
it = tree_store->erase(it);
}
else
it++;
}
}
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.markup, Glib::Markup::escape_text("(empty)"));
child->set_value(column_record.type, PathType::UNKNOWN);
@ -623,14 +624,14 @@ void Directories::add_or_update_path(const boost::filesystem::path &dir_path, co
}
void Directories::remove_path(const boost::filesystem::path &dir_path) {
auto it=directories.find(dir_path.string());
if(it==directories.end())
auto it = directories.find(dir_path.string());
if(it == directories.end())
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))
it=directories.erase(it);
it = directories.erase(it);
else
it++;
}
@ -639,7 +640,7 @@ void Directories::remove_path(const boost::filesystem::path &dir_path) {
while(children) {
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.markup, Glib::Markup::escape_text("(empty)"));
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) {
auto dir_path=std::make_shared<boost::filesystem::path>(std::move(dir_path_));
auto it=directories.find(dir_path->string());
if(it==directories.end())
auto dir_path = std::make_shared<boost::filesystem::path>(std::move(dir_path_));
auto it = directories.find(dir_path->string());
if(it == directories.end())
return;
if(it!=directories.end() && it->second.repository) {
auto repository=it->second.repository;
if(it != directories.end() && it->second.repository) {
auto repository = it->second.repository;
std::thread git_status_thread([this, dir_path, repository, include_parent_paths] {
Git::Repository::Status status;
try {
status=repository->get_status();
status = repository->get_status();
}
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)] {
auto it=directories.find(dir_path->string());
if(it==directories.end())
dispatcher.post([this, dir_path, include_parent_paths, status = std::move(status)] {
auto it = directories.find(dir_path->string());
if(it == directories.end())
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;
gray.set_rgba(0.5, 0.5, 0.5);
Gdk::RGBA yellow;
yellow.set_rgba(1.0, 1.0, 0.2);
double factor=0.5;
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_blue(normal_color.get_blue()+factor*(yellow.get_blue()-normal_color.get_blue()));
double factor = 0.5;
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_blue(normal_color.get_blue() + factor * (yellow.get_blue() - normal_color.get_blue()));
Gdk::RGBA green;
green.set_rgba(0.0, 1.0, 0.0);
factor=0.4;
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_blue(normal_color.get_blue()+factor*(green.get_blue()-normal_color.get_blue()));
factor = 0.4;
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_blue(normal_color.get_blue() + factor * (green.get_blue() - normal_color.get_blue()));
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)
return;
for(auto &child: children) {
auto name=Glib::Markup::escape_text(child.get_value(column_record.name));
auto path=child.get_value(column_record.path);
for(auto &child : children) {
auto name = Glib::Markup::escape_text(child.get_value(column_record.name));
auto path = child.get_value(column_record.path);
Gdk::RGBA *color;
if(status.modified.find(path.generic_string())!=status.modified.end())
color=&yellow;
else if(status.added.find(path.generic_string())!=status.added.end())
color=&green;
if(status.modified.find(path.generic_string()) != status.modified.end())
color = &yellow;
else if(status.added.find(path.generic_string()) != status.added.end())
color = &green;
else
color=&normal_color;
color = &normal_color;
std::stringstream ss;
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_green_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>");
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_blue_u() >> 8);
child.set_value(column_record.markup, "<span foreground=\"" + ss.str() + "\">" + name + "</span>");
auto type=child.get_value(column_record.type);
if(type==PathType::UNKNOWN)
child.set_value(column_record.markup, "<i>"+child.get_value(column_record.markup)+"</i>");
auto type = child.get_value(column_record.type);
if(type == PathType::UNKNOWN)
child.set_value(column_record.markup, "<i>" + child.get_value(column_record.markup) + "</i>");
}
if(!include_parent_paths)
break;
auto path=boost::filesystem::path(it->first);
if(boost::filesystem::exists(path/".git"))
auto path = boost::filesystem::path(it->first);
if(boost::filesystem::exists(path / ".git"))
break;
if(path==path.root_directory())
if(path == path.root_directory())
break;
auto parent_path=boost::filesystem::path(it->first).parent_path();
it=directories.find(parent_path.string());
} while(it!=directories.end());
auto parent_path = boost::filesystem::path(it->first).parent_path();
it = directories.find(parent_path.string());
} while(it != directories.end());
});
});
git_status_thread.detach();

23
src/directories.h

@ -1,15 +1,15 @@
#pragma once
#include "boost/filesystem.hpp"
#include "dispatcher.h"
#include "git.h"
#include <atomic>
#include <gtkmm.h>
#include <vector>
#include <mutex>
#include <string>
#include "boost/filesystem.hpp"
#include <thread>
#include <mutex>
#include <atomic>
#include <unordered_map>
#include <unordered_set>
#include "git.h"
#include "dispatcher.h"
#include <vector>
class Directories : public Gtk::ListViewText {
class DirectoryData {
@ -20,15 +20,15 @@ class Directories : public Gtk::ListViewText {
std::shared_ptr<sigc::connection> connection;
};
enum class PathType {KNOWN, UNKNOWN};
enum class PathType { KNOWN, UNKNOWN };
class TreeStore : public Gtk::TreeStore {
protected:
TreeStore()=default;
TreeStore() = default;
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_delete_vfunc (const Gtk::TreeModel::Path &path) override;
bool drag_data_delete_vfunc(const Gtk::TreeModel::Path &path) override;
public:
class ColumnRecord : public Gtk::TreeModel::ColumnRecord {
@ -47,10 +47,11 @@ class Directories : public Gtk::ListViewText {
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();
public:
static Directories &get() {
static Directories singleton;
@ -58,7 +59,7 @@ public:
}
~Directories() override;
void open(const boost::filesystem::path &dir_path="");
void open(const boost::filesystem::path &dir_path = "");
void update();
void on_save_file(const boost::filesystem::path &file_path);
void select(const boost::filesystem::path &path);

8
src/dispatcher.cc

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

7
src/dispatcher.h

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

34
src/documentation_cppreference.cc

@ -12177,21 +12177,21 @@ std::experimental::filesystem::status_known cpp/experimental/fs/status_known"}
class SymbolToUrl {
public:
SymbolToUrl(const std::string &symbol_urls) {
size_t symbol_start=0;
size_t symbol_end=std::string::npos;
size_t url_start=std::string::npos;
for(size_t c=0;c<symbol_urls.size();++c) {
auto &chr=symbol_urls[c];
if(chr=='\t') {
symbol_end=c;
url_start=c+1;
size_t symbol_start = 0;
size_t symbol_end = std::string::npos;
size_t url_start = std::string::npos;
for(size_t c = 0; c < symbol_urls.size(); ++c) {
auto &chr = symbol_urls[c];
if(chr == '\t') {
symbol_end = c;
url_start = c + 1;
}
else if(chr=='\n') {
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));
symbol_start=c+1;
symbol_end=std::string::npos;
url_start=std::string::npos;
else if(chr == '\n') {
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));
symbol_start = c + 1;
symbol_end = std::string::npos;
url_start = std::string::npos;
}
}
}
@ -12199,8 +12199,8 @@ std::experimental::filesystem::status_known cpp/experimental/fs/status_known"}
};
static SymbolToUrl symbol_to_url(symbol_urls);
auto it=symbol_to_url.map.find(symbol);
if(it==symbol_to_url.map.end())
auto it = symbol_to_url.map.find(symbol);
if(it == symbol_to_url.map.end())
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:
static std::string get_url(const std::string &symbol) noexcept;
};
}
} // namespace Documentation

58
src/entrybox.cc

@ -1,37 +1,37 @@
#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_width_chars(width_chars);
set_text(content);
selected_history=0;
signal_activate().connect([this](){
selected_history = 0;
signal_activate().connect([this]() {
if(this->on_activate) {
auto &history=EntryBox::entry_histories[get_placeholder_text()];
auto text=get_text();
if(history.size()==0 || (history.size()>0 && *history.begin()!=text))
auto &history = EntryBox::entry_histories[get_placeholder_text()];
auto text = get_text();
if(history.size() == 0 || (history.size() > 0 && *history.begin() != text))
history.emplace(history.begin(), text);
selected_history=0;
selected_history = 0;
this->on_activate(text);
}
});
signal_key_press_event().connect([this](GdkEventKey* key){
if(key->keyval==GDK_KEY_Up || key->keyval==GDK_KEY_KP_Up) {
auto &history=entry_histories[get_placeholder_text()];
if(history.size()>0) {
signal_key_press_event().connect([this](GdkEventKey *key) {
if(key->keyval == GDK_KEY_Up || key->keyval == GDK_KEY_KP_Up) {
auto &history = entry_histories[get_placeholder_text()];
if(history.size() > 0) {
selected_history++;
if(selected_history>=history.size())
selected_history=history.size()-1;
if(selected_history >= history.size())
selected_history = history.size() - 1;
set_text(history[selected_history]);
set_position(-1);
}
}
if(key->keyval==GDK_KEY_Down || key->keyval==GDK_KEY_KP_Down) {
auto &history=entry_histories[get_placeholder_text()];
if(history.size()>0) {
if(selected_history!=0)
if(key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_KP_Down) {
auto &history = entry_histories[get_placeholder_text()];
if(history.size() > 0) {
if(selected_history != 0)
selected_history--;
set_text(history[selected_history]);
set_position(-1);
@ -41,23 +41,23 @@ 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);
signal_clicked().connect([this](){
signal_clicked().connect([this]() {
if(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);
signal_clicked().connect([this](){
signal_clicked().connect([this]() {
if(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)
this->update(-1, "");
}
@ -77,20 +77,20 @@ void EntryBox::clear() {
}
void EntryBox::show() {
std::vector<Gtk::Widget*> focus_chain;
for(auto& entry: entries) {
std::vector<Gtk::Widget *> focus_chain;
for(auto &entry : entries) {
lower_box.pack_start(entry, Gtk::PACK_SHRINK);
focus_chain.emplace_back(&entry);
}
for(auto& button: buttons)
for(auto &button : buttons)
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);
for(auto& label: labels)
for(auto &label : labels)
upper_box.pack_start(label, Gtk::PACK_SHRINK);
lower_box.set_focus_chain(focus_chain);
show_all();
if(entries.size()>0) {
if(entries.size() > 0) {
entries.begin()->grab_focus();
entries.begin()->select_region(0, entries.begin()->get_text_length());
}

24
src/entrybox.h

@ -1,38 +1,40 @@
#pragma once
#include <list>
#include <functional>
#include "gtkmm.h"
#include <unordered_map>
#include <functional>
#include <list>
#include <string>
#include <unordered_map>
#include <vector>
class EntryBox : public Gtk::Box {
public:
class Entry : public Gtk::Entry {
public:
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;
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;
private:
size_t selected_history;
};
class Button : public Gtk::Button {
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;
};
class ToggleButton : public Gtk::ToggleButton {
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;
};
class Label : public Gtk::Label {
public:
Label(std::function<void(int state, const std::string& message)> update_=nullptr);
std::function<void(int state, const std::string& message)> update;
Label(std::function<void(int state, const std::string &message)> update_ = nullptr);
std::function<void(int state, const std::string &message)> update;
};
private:
EntryBox();
public:
static EntryBox &get() {
static EntryBox singleton;
@ -42,7 +44,7 @@ public:
Gtk::Box upper_box;
Gtk::Box lower_box;
void clear();
void hide() {clear();}
void hide() { clear(); }
void show();
std::list<Entry> entries;
std::list<Button> buttons;
@ -50,5 +52,5 @@ public:
std::list<Label> labels;
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
/// the changes to user's ~/.juci/config/config.json files
const std::string default_config_file = R"RAW({
"version": ")RAW"+std::string(JUCI_VERSION)+R"RAW(",
"version": ")RAW" + std::string(JUCI_VERSION) + R"RAW(",
"gtk_theme": {
"name_comment": "Use \"\" for default theme, At least these two exist on all systems: Adwaita, Raleigh",
"name": "",
@ -17,18 +17,18 @@ const std::string default_config_file = R"RAW({
"style": "juci-light",
"font_comment": "Use \"\" for default font, and for instance \"Monospace 12\" to also set size",)RAW"
#ifdef __APPLE__
R"RAW(
R"RAW(
"font": "Menlo",)RAW"
#else
#ifdef _WIN32
R"RAW(
R"RAW(
"font": "Consolas",)RAW"
#else
R"RAW(
R"RAW(
"font": "Monospace",)RAW"
#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": 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",
@ -133,25 +133,25 @@ R"RAW(
"debug_toggle_breakpoint": "<primary>b",
"debug_goto_stop": "<primary><shift>l",)RAW"
#ifdef __linux
R"RAW(
R"RAW(
"window_next_tab": "<primary>Tab",
"window_previous_tab": "<primary><shift>Tab",)RAW"
#else
R"RAW(
R"RAW(
"window_next_tab": "<primary><alt>Right",
"window_previous_tab": "<primary><alt>Left",)RAW"
#endif
R"RAW(
R"RAW(
"window_close_tab": "<primary>w",
"window_toggle_split": "",)RAW"
#ifdef __APPLE__
R"RAW(
R"RAW(
"window_toggle_full_screen": "<primary><control>f",)RAW"
#else
R"RAW(
R"RAW(
"window_toggle_full_screen": "F11",)RAW"
#endif
R"RAW(
R"RAW(
"window_toggle_tabs": "",
"window_clear_terminal": ""
},
@ -162,13 +162,13 @@ R"RAW(
"debug_build_path": "<default_build_path>/debug",
"cmake": {)RAW"
#ifdef _WIN32
R"RAW(
R"RAW(
"command": "cmake -G\"MSYS Makefiles\"",)RAW"
#else
R"RAW(
R"RAW(
"command": "cmake",)RAW"
#endif
R"RAW(
R"RAW(
"compile_command": "cmake --build ."
},
"meson": {

137
src/filesystem.cc

@ -1,7 +1,7 @@
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>
#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> res;
std::ifstream input(path, std::ofstream::binary);
if (input) {
do { res.emplace_back(); } while(getline(input, res.back()));
if(input) {
do {
res.emplace_back();
} while(getline(input, res.back()));
}
input.close();
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) {
auto escaped=argument;
for(size_t pos=0;pos<escaped.size();++pos) {
if(escaped[pos]==' ' || escaped[pos]=='(' || escaped[pos]==')' || escaped[pos]=='\'' || escaped[pos]=='"') {
auto escaped = argument;
for(size_t pos = 0; pos < escaped.size(); ++pos) {
if(escaped[pos] == ' ' || escaped[pos] == '(' || escaped[pos] == ')' || escaped[pos] == '\'' || escaped[pos] == '"') {
escaped.insert(pos, "\\");
++pos;
}
@ -50,40 +52,40 @@ std::string filesystem::escape_argument(const std::string &argument) {
}
std::string filesystem::unescape_argument(const std::string &argument) {
auto unescaped=argument;
if(unescaped.size()>=2) {
if((unescaped[0]=='\'' && unescaped[unescaped.size()-1]=='\'') ||
(unescaped[0]=='"' && unescaped[unescaped.size()-1]=='"')) {
char quotation_mark=unescaped[0];
unescaped=unescaped.substr(1, unescaped.size()-2);
size_t backslash_count=0;
for(size_t pos=0;pos<unescaped.size();++pos) {
if(backslash_count%2==1 && (unescaped[pos]=='\\' || unescaped[pos]==quotation_mark)) {
unescaped.erase(pos-1, 1);
auto unescaped = argument;
if(unescaped.size() >= 2) {
if((unescaped[0] == '\'' && unescaped[unescaped.size() - 1] == '\'') ||
(unescaped[0] == '"' && unescaped[unescaped.size() - 1] == '"')) {
char quotation_mark = unescaped[0];
unescaped = unescaped.substr(1, unescaped.size() - 2);
size_t backslash_count = 0;
for(size_t pos = 0; pos < unescaped.size(); ++pos) {
if(backslash_count % 2 == 1 && (unescaped[pos] == '\\' || unescaped[pos] == quotation_mark)) {
unescaped.erase(pos - 1, 1);
--pos;
backslash_count=0;
backslash_count = 0;
}
else if(unescaped[pos]=='\\')
else if(unescaped[pos] == '\\')
++backslash_count;
else
backslash_count=0;
backslash_count = 0;
}
return unescaped;
}
}
size_t backslash_count=0;
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]=='"')) {
unescaped.erase(pos-1, 1);
size_t backslash_count = 0;
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] == '"')) {
unescaped.erase(pos - 1, 1);
--pos;
backslash_count=0;
backslash_count = 0;
}
else if(unescaped[pos]=='\\')
else if(unescaped[pos] == '\\')
++backslash_count;
else
backslash_count=0;
backslash_count = 0;
}
return unescaped;
}
@ -91,10 +93,10 @@ std::string filesystem::unescape_argument(const std::string &argument) {
boost::filesystem::path filesystem::get_home_path() noexcept {
std::vector<std::string> environment_variables = {"HOME", "AppData"};
char *ptr = nullptr;
for (auto &variable : environment_variables) {
ptr=std::getenv(variable.c_str());
for(auto &variable : environment_variables) {
ptr = std::getenv(variable.c_str());
boost::system::error_code ec;
if (ptr!=nullptr && boost::filesystem::exists(ptr, ec))
if(ptr != nullptr && boost::filesystem::exists(ptr, ec))
return ptr;
}
return boost::filesystem::path();
@ -104,51 +106,51 @@ boost::filesystem::path filesystem::get_short_path(const boost::filesystem::path
#ifdef _WIN32
return path;
#else
static auto home_path=get_home_path();
static auto home_path = get_home_path();
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())
return "~"/relative_path;
return "~" / relative_path;
}
return path;
#endif
}
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 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) {
auto current_path=path;
auto current_path = path;
while(true) {
auto test_path=current_path/file_name;
auto test_path = current_path / file_name;
if(boost::filesystem::exists(test_path))
return test_path;
if(current_path==current_path.root_directory())
if(current_path == current_path.root_directory())
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 normal_path;
for(auto &e: path) {
if(e==".")
for(auto &e : path) {
if(e == ".")
continue;
else if(e=="..") {
auto parent_path=normal_path.parent_path();
else if(e == "..") {
auto parent_path = normal_path.parent_path();
if(!parent_path.empty())
normal_path=parent_path;
normal_path = parent_path;
else
normal_path/=e;
normal_path /= e;
}
else if(e.empty())
continue;
else
normal_path/=e;
normal_path /= e;
}
return normal_path;
@ -157,19 +159,19 @@ boost::filesystem::path filesystem::get_normal_path(const boost::filesystem::pat
boost::filesystem::path filesystem::get_relative_path(const boost::filesystem::path &path, const boost::filesystem::path &base) noexcept {
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();
auto base_it=base.begin();
auto path_it=path.begin();
while(path_it!=path.end() && base_it!=base.end()) {
if(*path_it!=*base_it)
auto base_it = base.begin();
auto path_it = path.begin();
while(path_it != path.end() && base_it != base.end()) {
if(*path_it != *base_it)
return boost::filesystem::path();
++path_it;
++base_it;
}
for(;path_it!=path.end();++path_it)
relative_path/=*path_it;
for(; path_it != path.end(); ++path_it)
relative_path /= *path_it;
return relative_path;
}
@ -179,38 +181,39 @@ boost::filesystem::path filesystem::get_executable(const boost::filesystem::path
return executable_name;
#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 {
for(auto &path: bin_paths) {
if(boost::filesystem::exists(path/executable_name))
for(auto &path : bin_paths) {
if(boost::filesystem::exists(path / executable_name))
return executable_name;
}
auto &executable_name_str = executable_name.string();
for(auto &path: bin_paths) {
for(auto &path : bin_paths) {
boost::filesystem::path executable;
for(boost::filesystem::directory_iterator it(path), end; it != end; ++it) {
auto it_path = it->path();
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 &&
((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()]<='9') ||
(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()+1]>='0' &&
it_path_filename_str[executable_name_str.size()+1]<='9')) &&
it_path_filename_str[executable_name_str.size()] >= '0' &&
it_path_filename_str[executable_name_str.size()] <= '9') ||
(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() + 1] >= '0' &&
it_path_filename_str[executable_name_str.size() + 1] <= '9')) &&
!boost::filesystem::is_directory(it_path))
executable=it_path;
executable = it_path;
}
}
if(!executable.empty())
return executable;
}
}
catch(...) {}
catch(...) {
}
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) {
for(auto &path: get_executable_search_paths()) {
for(auto &path : get_executable_search_paths()) {
boost::system::error_code ec;
auto executable_path=path/executable_name;
auto executable_path = path / executable_name;
if(boost::filesystem::exists(executable_path, ec))
return executable_path;
}

4
src/filesystem.h

@ -1,7 +1,7 @@
#pragma once
#include <vector>
#include <string>
#include <boost/filesystem.hpp>
#include <string>
#include <vector>
class filesystem {
public:

198
src/git.cc

@ -2,40 +2,41 @@
#include <cstring>
#include <unordered_map>
bool Git::initialized=false;
bool Git::initialized = false;
std::mutex Git::mutex;
std::string Git::Error::message() noexcept {
const git_error *last_error = giterr_last();
if(last_error==nullptr)
if(last_error == nullptr)
return std::string();
else
return last_error->message;
}
Git::Repository::Diff::Diff(const boost::filesystem::path &path, git_repository *repository) : repository(repository) {
blob=std::shared_ptr<git_blob>(nullptr, [](git_blob *blob) {
if(blob) git_blob_free(blob);
blob = std::shared_ptr<git_blob>(nullptr, [](git_blob *blob) {
if(blob)
git_blob_free(blob);
});
Error error;
std::lock_guard<std::mutex> lock(mutex);
auto spec="HEAD:"+path.generic_string();
error.code = git_revparse_single(reinterpret_cast<git_object**>(&blob), repository, spec.c_str());
auto spec = "HEAD:" + path.generic_string();
error.code = git_revparse_single(reinterpret_cast<git_object **>(&blob), repository, spec.c_str());
if(error)
throw std::runtime_error(error.message());
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
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 start=hunk->new_start-1;
auto end=hunk->new_start+hunk->new_lines-1;
if(hunk->old_lines==0 && hunk->new_lines>0)
auto lines = static_cast<Lines *>(payload);
auto start = hunk->new_start - 1;
auto end = hunk->new_start + hunk->new_lines - 1;
if(hunk->old_lines == 0 && hunk->new_lines > 0)
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);
else
lines->modified.emplace_back(start, end);
@ -47,7 +48,7 @@ Git::Repository::Diff::Lines Git::Repository::Diff::get_lines(const std::string
Lines lines;
Error error;
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)
throw std::runtime_error(error.message());
return lines;
@ -58,10 +59,9 @@ std::vector<Git::Repository::Diff::Hunk> Git::Repository::Diff::get_hunks(const
Error error;
git_diff_options options;
git_diff_init_options(&options, GIT_DIFF_OPTIONS_VERSION);
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,
[](const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) {
auto hunks=static_cast<std::vector<Git::Repository::Diff::Hunk>*>(payload);
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, [](const git_diff_delta *delta, const git_diff_hunk *hunk, void *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);
return 0;
}, 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 {
auto details=static_cast<std::pair<std::string, int> *>(payload);
auto line_nr=details->second;
auto start=hunk->new_start-1;
auto end=hunk->new_start+hunk->new_lines-1;
if(line_nr==start || (line_nr>=start && line_nr<end)) {
auto details = static_cast<std::pair<std::string, int> *>(payload);
auto line_nr = details->second;
auto start = hunk->new_start - 1;
auto end = hunk->new_start + hunk->new_lines - 1;
if(line_nr == start || (line_nr >= start && line_nr < end)) {
if(details->first.empty())
details->first+=std::string(hunk->header, hunk->header_len);
details->first+=line->origin+std::string(line->content, line->content_len);
details->first += std::string(hunk->header, hunk->header_len);
details->first += line->origin + std::string(line->content, line->content_len);
}
return 0;
}
std::string Git::Repository::Diff::get_details(const std::string &buffer, int line_nr) {
std::pair<std::string, int> details;
details.second=line_nr;
details.second = line_nr;
Error error;
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)
throw std::runtime_error(error.message());
return details.first;
@ -103,20 +103,20 @@ Git::Repository::Repository(const boost::filesystem::path &path) {
if(error)
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);
});
work_path=get_work_path();
work_path = get_work_path();
if(work_path.empty())
throw std::runtime_error("Could not find work path");
auto git_directory=Gio::File::create_for_path(get_path().string());
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,
const Glib::RefPtr<Gio::File>&,
auto git_directory = Gio::File::create_for_path(get_path().string());
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,
const Glib::RefPtr<Gio::File> &,
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();
}
}, false);
@ -128,41 +128,51 @@ Git::Repository::~Repository() {
std::string Git::Repository::status_string(STATUS status) noexcept {
switch(status) {
case STATUS::CURRENT: return "current";
case STATUS::NEW: return "new";
case STATUS::MODIFIED: return "modified";
case STATUS::DELETED: return "deleted";
case STATUS::RENAMED: return "renamed";
case STATUS::TYPECHANGE: return "typechange";
case STATUS::UNREADABLE: return "unreadable";
case STATUS::IGNORED: return "ignored";
case STATUS::CONFLICTED: return "conflicted";
default: return "";
case STATUS::CURRENT:
return "current";
case STATUS::NEW:
return "new";
case STATUS::MODIFIED:
return "modified";
case STATUS::DELETED:
return "deleted";
case STATUS::RENAMED:
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 {
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;
if((status_flags&(GIT_STATUS_INDEX_NEW|GIT_STATUS_WT_NEW))>0)
status=STATUS::NEW;
else if((status_flags&(GIT_STATUS_INDEX_MODIFIED|GIT_STATUS_WT_MODIFIED))>0)
status=STATUS::MODIFIED;
else if((status_flags&(GIT_STATUS_INDEX_DELETED|GIT_STATUS_WT_DELETED))>0)
status=STATUS::DELETED;
else if((status_flags&(GIT_STATUS_INDEX_RENAMED|GIT_STATUS_WT_RENAMED))>0)
status=STATUS::RENAMED;
else if((status_flags&(GIT_STATUS_INDEX_TYPECHANGE|GIT_STATUS_WT_TYPECHANGE))>0)
status=STATUS::TYPECHANGE;
else if((status_flags&(GIT_STATUS_WT_UNREADABLE))>0)
status=STATUS::UNREADABLE;
else if((status_flags&(GIT_STATUS_IGNORED))>0)
status=STATUS::IGNORED;
else if((status_flags&(GIT_STATUS_CONFLICTED))>0)
status=STATUS::CONFLICTED;
if((status_flags & (GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW)) > 0)
status = STATUS::NEW;
else if((status_flags & (GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED)) > 0)
status = STATUS::MODIFIED;
else if((status_flags & (GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED)) > 0)
status = STATUS::DELETED;
else if((status_flags & (GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED)) > 0)
status = STATUS::RENAMED;
else if((status_flags & (GIT_STATUS_INDEX_TYPECHANGE | GIT_STATUS_WT_TYPECHANGE)) > 0)
status = STATUS::TYPECHANGE;
else if((status_flags & (GIT_STATUS_WT_UNREADABLE)) > 0)
status = STATUS::UNREADABLE;
else if((status_flags & (GIT_STATUS_IGNORED)) > 0)
status = STATUS::IGNORED;
else if((status_flags & (GIT_STATUS_CONFLICTED)) > 0)
status = STATUS::CONFLICTED;
else
status=STATUS::CURRENT;
status = STATUS::CURRENT;
if(*callback)
(*callback)(path, status);
@ -178,20 +188,20 @@ Git::Repository::Status Git::Repository::get_status() {
}
Status status;
bool first=true;
bool first = true;
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) {
status_saved_lock.lock();
first=false;
first = false;
}
boost::filesystem::path rel_path(path_cstr);
do {
if(status_enum==STATUS::MODIFIED)
status.modified.emplace((work_path/rel_path).generic_string());
if(status_enum==STATUS::NEW)
status.added.emplace((work_path/rel_path).generic_string());
rel_path=rel_path.parent_path();
if(status_enum == STATUS::MODIFIED)
status.modified.emplace((work_path / rel_path).generic_string());
if(status_enum == STATUS::NEW)
status.added.emplace((work_path / rel_path).generic_string());
rel_path = rel_path.parent_path();
} while(!rel_path.empty());
};
Error error;
@ -199,8 +209,8 @@ Git::Repository::Status Git::Repository::get_status() {
error.code = git_status_foreach(repository.get(), status_callback, &callback);
if(error)
throw std::runtime_error(error.message());
saved_status=status;
has_saved_status=true;
saved_status = status;
has_saved_status = true;
if(status_saved_lock)
status_saved_lock.unlock();
return status;
@ -210,7 +220,7 @@ void Git::Repository::clear_saved_status() {
std::unique_lock<std::mutex> lock(saved_status_mutex);
saved_status.added.clear();
saved_status.modified.clear();
has_saved_status=false;
has_saved_status = false;
}
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)
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);
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 branch;
git_reference *reference;
if(git_repository_head(&reference, repository.get())==0) {
if(auto reference_name_cstr=git_reference_name(reference)) {
if(git_repository_head(&reference, repository.get()) == 0) {
if(auto reference_name_cstr = git_reference_name(reference)) {
std::string reference_name(reference_name_cstr);
size_t pos;
if((pos=reference_name.rfind('/'))!=std::string::npos) {
if(pos+1<reference_name.size())
branch=reference_name.substr(pos+1);
if((pos = reference_name.rfind('/')) != std::string::npos) {
if(pos + 1 < reference_name.size())
branch = reference_name.substr(pos + 1);
}
else if((pos=reference_name.rfind('\\'))!=std::string::npos) {
if(pos+1<reference_name.size())
branch=reference_name.substr(pos+1);
else if((pos = reference_name.rfind('\\')) != std::string::npos) {
if(pos + 1 < reference_name.size())
branch = reference_name.substr(pos + 1);
}
}
git_reference_free(reference);
@ -267,33 +277,33 @@ void Git::initialize() noexcept {
std::lock_guard<std::mutex> lock(mutex);
if(!initialized) {
git_libgit2_init();
initialized=true;
initialized = true;
}
}
std::shared_ptr<Git::Repository> Git::get_repository(const boost::filesystem::path &path) {
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;
std::lock_guard<std::mutex> lock(mutex);
auto root_path=Repository::get_root_path(path).generic_string();
auto it=cache.find(root_path);
if(it==cache.end())
it=cache.emplace(root_path, std::weak_ptr<Git::Repository>()).first;
auto instance=it->second.lock();
auto root_path = Repository::get_root_path(path).generic_string();
auto it = cache.find(root_path);
if(it == cache.end())
it = cache.emplace(root_path, std::weak_ptr<Git::Repository>()).first;
auto instance = it->second.lock();
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;
}
boost::filesystem::path Git::path(const char *cpath, size_t cpath_length) noexcept {
if(cpath==nullptr)
if(cpath == nullptr)
return boost::filesystem::path();
if(cpath_length==static_cast<size_t>(-1))
cpath_length=strlen(cpath);
if(cpath_length>0 && (cpath[cpath_length-1]=='/' || cpath[cpath_length-1]=='\\'))
return std::string(cpath, cpath_length-1);
if(cpath_length == static_cast<size_t>(-1))
cpath_length = strlen(cpath);
if(cpath_length > 0 && (cpath[cpath_length - 1] == '/' || cpath[cpath_length - 1] == '\\'))
return std::string(cpath, cpath_length - 1);
else
return std::string(cpath, cpath_length);
}

37
src/git.h

@ -1,22 +1,24 @@
#pragma once
#include <boost/filesystem.hpp>
#include <giomm.h>
#include <git2.h>
#include <mutex>
#include <memory>
#include <iostream>
#include <memory>
#include <mutex>
#include <unordered_set>
#include <vector>
#include <giomm.h>
#include <boost/filesystem.hpp>
class Git {
friend class Repository;
public:
class Error {
friend class Git;
std::string message() noexcept;
public:
int code=0;
operator bool() noexcept {return code<0;}
int code = 0;
operator bool() noexcept { return code < 0; }
};
class Repository {
@ -25,51 +27,55 @@ public:
public:
class Lines {
public:
std::vector<std::pair<int, int> > added;
std::vector<std::pair<int, int> > modified;
std::vector<std::pair<int, int>> added;
std::vector<std::pair<int, int>> modified;
std::vector<int> removed;
};
class Hunk {
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
std::pair<int, int> old_lines;
/// Start and size
std::pair<int, int> new_lines;
};
private:
friend class Repository;
Diff(const boost::filesystem::path &path, git_repository *repository);
git_repository *repository=nullptr;
std::shared_ptr<git_blob> blob=nullptr;
git_repository *repository = nullptr;
std::shared_ptr<git_blob> blob = nullptr;
git_diff_options options;
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;
public:
Lines get_lines(const std::string &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);
};
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 {
public:
std::unordered_set<std::string> added;
std::unordered_set<std::string> modified;
};
private:
friend class Git;
Repository(const boost::filesystem::path &path);
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;
sigc::connection monitor_changed_connection;
Status saved_status;
bool has_saved_status=false;
bool has_saved_status = false;
std::mutex saved_status_mutex;
public:
~Repository();
@ -98,7 +104,8 @@ private:
///Call initialize in public static methods
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:
static std::shared_ptr<Repository> get_repository(const boost::filesystem::path &path);
};

14
src/info.cc

@ -4,7 +4,7 @@ Info::Info() {
set_hexpand(false);
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_line_wrap(true);
content_area->add(label);
@ -14,10 +14,10 @@ Info::Info() {
//Workaround from https://bugzilla.gnome.org/show_bug.cgi?id=710888
//Issue described at the same issue report
//TODO: remove later
auto revealer = gtk_widget_get_template_child (GTK_WIDGET (gobj()), GTK_TYPE_INFO_BAR, "revealer");
if (revealer) {
gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 0);
auto revealer = gtk_widget_get_template_child(GTK_WIDGET(gobj()), GTK_TYPE_INFO_BAR, "revealer");
if(revealer) {
gtk_revealer_set_transition_type(GTK_REVEALER(revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
gtk_revealer_set_transition_duration(GTK_REVEALER(revealer), 0);
}
}
@ -25,8 +25,8 @@ void Info::print(const std::string &text) {
timeout_connection.disconnect();
//Timeout based on https://en.wikipedia.org/wiki/Words_per_minute
//(average_words_per_minute*average_letters_per_word)/60 => (228*4.5)/60 = 17.1
double timeout=1000.0*std::max(3.0, 1.0+text.size()/17.1);
timeout_connection=Glib::signal_timeout().connect([this]() {
double timeout = 1000.0 * std::max(3.0, 1.0 + text.size() / 17.1);
timeout_connection = Glib::signal_timeout().connect([this]() {
hide();
return false;
}, timeout);

1
src/info.h

@ -3,6 +3,7 @@
class Info : public Gtk::InfoBar {
Info();
public:
static Info &get() {
static Info instance;

54
src/juci.cc

@ -1,10 +1,10 @@
#include "juci.h"
#include "window.h"
#include "notebook.h"
#include "config.h"
#include "directories.h"
#include "menu.h"
#include "config.h"
#include "notebook.h"
#include "terminal.h"
#include "window.h"
#ifndef _WIN32
#include <csignal>
#endif
@ -17,15 +17,15 @@ int Application::on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine>
int argc;
char **argv = cmd->get_arguments(argc);
ctx.parse(argc, argv);
if(argc>=2) {
if(argc >= 2) {
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)
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]);
if(path.is_relative() && !current_path_ec)
path=current_path/path;
path = current_path / path;
if(boost::filesystem::exists(path)) {
if(boost::filesystem::is_regular_file(path))
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()))
files.emplace_back(path, 0);
else
errors.emplace_back("Error: could not create "+path.string()+".\n");
errors.emplace_back("Error: could not create " + path.string() + ".\n");
}
}
}
@ -55,47 +55,47 @@ void Application::on_activate() {
add_window(Window::get());
Window::get().show();
bool first_directory=true;
for(auto &directory: directories) {
bool first_directory = true;
for(auto &directory : directories) {
if(first_directory) {
Directories::get().open(directory);
first_directory=false;
first_directory = false;
}
else {
std::string files_in_directory;
for(auto it=files.begin();it!=files.end();) {
if(it->first.generic_string().compare(0, directory.generic_string().size()+1, directory.generic_string()+'/')==0) {
files_in_directory+=" "+it->first.string();
it=files.erase(it);
for(auto it = files.begin(); it != files.end();) {
if(it->first.generic_string().compare(0, directory.generic_string().size() + 1, directory.generic_string() + '/') == 0) {
files_in_directory += " " + it->first.string();
it = files.erase(it);
}
else
it++;
}
std::thread another_juci_app([directory, files_in_directory](){
Terminal::get().async_print("Executing: juci "+directory.string()+files_in_directory+"\n");
Terminal::get().process("juci "+directory.string()+files_in_directory, "", false);
std::thread another_juci_app([directory, files_in_directory]() {
Terminal::get().async_print("Executing: juci " + directory.string() + files_in_directory + "\n");
Terminal::get().process("juci " + directory.string() + files_in_directory, "", false);
});
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);
if(i<file_offsets.size()) {
if(auto view=Notebook::get().get_current_view()) {
if(i < file_offsets.size()) {
if(auto view = Notebook::get().get_current_view()) {
view->place_cursor_at_line_offset(file_offsets[i].first, file_offsets[i].second);
view->hide_tooltips();
}
}
}
for(auto &error: errors)
for(auto &error : errors)
Terminal::get().print(error, true);
if(!current_file.empty()) {
Notebook::get().open(current_file);
if(auto view=Notebook::get().get_current_view()) {
auto iter=view->get_buffer()->get_insert()->get_iter();
if(auto view = Notebook::get().get_current_view()) {
auto iter = view->get_buffer()->get_insert()->get_iter();
// To update cursor history
view->place_cursor_at_line_offset(iter.get_line(), iter.get_line_offset());
view->hide_tooltips();
@ -104,7 +104,7 @@ void Application::on_activate() {
while(Gtk::Main::events_pending())
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);
}
@ -113,7 +113,7 @@ void Application::on_startup() {
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;
}
else {
@ -126,7 +126,7 @@ Application::Application() : Gtk::Application("no.sout.juci", Gio::APPLICATION_N
Glib::set_application_name("juCi++");
//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[]) {

3
src/juci.h

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

26
src/menu.cc

@ -1,9 +1,9 @@
#include "menu.h"
#include "config.h"
#include <string>
#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'>
<section>
<item>
@ -478,21 +478,21 @@ const Glib::ustring menu_xml= R"RAW(<interface>
)RAW";
void Menu::add_action(const std::string &name, const std::function<void()> &action) {
auto g_application=g_application_get_default();
auto gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
actions[name]=application->add_action(name, action);
actions[name] = application->add_action(name, action);
}
void Menu::set_keys() {
auto g_application=g_application_get_default();
auto gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
for(auto &key: Config::get().menu.keys) {
if(key.second.size()>0 && actions.find(key.first)!=actions.end())
application->set_accel_for_action("app."+key.first, key.second);
for(auto &key : Config::get().menu.keys) {
if(key.second.size() > 0 && actions.find(key.first) != actions.end())
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);
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();
}
}

10
src/menu.h

@ -1,11 +1,12 @@
#pragma once
#include <string>
#include <unordered_map>
#include <functional>
#include <gtkmm.h>
#include <string>
#include <unordered_map>
class Menu {
Menu() = default;
public:
static Menu &get() {
static Menu singleton;
@ -13,7 +14,7 @@ public:
}
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 build();
@ -22,7 +23,8 @@ public:
Glib::RefPtr<Gio::Menu> window_menu;
std::unique_ptr<Gtk::Menu> right_click_line_menu;
std::unique_ptr<Gtk::Menu> right_click_selected_menu;
std::function<void()> toggle_menu_items = []{};
std::function<void()> toggle_menu_items = [] {};
private:
Glib::RefPtr<Gtk::Builder> builder;
};

74
src/meson.cc

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

4
src/meson.h

@ -8,8 +8,8 @@ public:
boost::filesystem::path project_path;
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_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);
boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
};

478
src/notebook.cc

@ -1,20 +1,20 @@
#include "notebook.h"
#include "config.h"
#include "directories.h"
#include <fstream>
#include <regex>
#include "project.h"
#include "filesystem.h"
#include "gtksourceview-3.0/gtksourceview/gtksourcemap.h"
#include "project.h"
#include "selection_dialog.h"
#include "source_clang.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) {
set_can_focus(false);
auto button=Gtk::manage(new Gtk::Button());
auto hbox=Gtk::manage(new Gtk::Box());
auto button = Gtk::manage(new Gtk::Button());
auto hbox = Gtk::manage(new Gtk::Box());
hbox->set_can_focus(false);
label.set_can_focus(false);
@ -28,7 +28,7 @@ Notebook::TabLabel::TabLabel(const std::function<void()> &on_close) {
button->signal_clicked().connect(on_close);
signal_button_press_event().connect([on_close](GdkEventButton *event) {
if(event->button==GDK_BUTTON_MIDDLE) {
if(event->button == GDK_BUTTON_MIDDLE) {
on_close();
return true;
}
@ -39,25 +39,25 @@ Notebook::TabLabel::TabLabel(const std::function<void()> &on_close) {
}
Notebook::Notebook() : Gtk::Paned(), notebooks(2) {
for(auto &notebook: notebooks) {
for(auto &notebook : notebooks) {
notebook.get_style_context()->add_class("juci_notebook");
notebook.set_scrollable();
notebook.set_group_name("source_notebooks");
notebook.signal_switch_page().connect([this](Gtk::Widget *widget, guint) {
auto hbox=dynamic_cast<Gtk::Box*>(widget);
for(size_t c=0;c<hboxes.size();++c) {
if(hboxes[c].get()==hbox) {
auto hbox = dynamic_cast<Gtk::Box *>(widget);
for(size_t c = 0; c < hboxes.size(); ++c) {
if(hboxes[c].get() == hbox) {
focus_view(source_views[c]);
set_current_view(source_views[c]);
break;
}
}
last_index=-1;
last_index = -1;
});
notebook.signal_page_added().connect([this](Gtk::Widget* widget, guint) {
auto hbox=dynamic_cast<Gtk::Box*>(widget);
for(size_t c=0;c<hboxes.size();++c) {
if(hboxes[c].get()==hbox) {
notebook.signal_page_added().connect([this](Gtk::Widget *widget, guint) {
auto hbox = dynamic_cast<Gtk::Box *>(widget);
for(size_t c = 0; c < hboxes.size(); ++c) {
if(hboxes[c].get() == hbox) {
focus_view(source_views[c]);
set_current_view(source_views[c]);
break;
@ -72,50 +72,50 @@ size_t Notebook::size() {
return source_views.size();
}
Source::View* Notebook::get_view(size_t index) {
if(index>=size())
Source::View *Notebook::get_view(size_t index) {
if(index >= size())
return nullptr;
return source_views[index];
}
Source::View* Notebook::get_current_view() {
Source::View *Notebook::get_current_view() {
if(intermediate_view) {
for(auto view: source_views) {
if(view==intermediate_view)
for(auto view : source_views) {
if(view == intermediate_view)
return view;
}
}
for(auto view: source_views) {
if(view==current_view)
for(auto view : source_views) {
if(view == current_view)
return view;
}
//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) {
auto page=notebooks[notebook_index].get_current_page();
if(page>=0)
for(int notebook_index = 0; notebook_index < 2; ++notebook_index) {
auto page = notebooks[notebook_index].get_current_page();
if(page >= 0)
return get_view(notebook_index, page);
}
return nullptr;
}
std::vector<Source::View*> &Notebook::get_views() {
std::vector<Source::View *> &Notebook::get_views() {
return source_views;
}
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();
// Use canonical path to follow symbolic links
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)
canonical_file_path=file_path;
for(size_t c=0;c<size();c++) {
if(canonical_file_path==source_views[c]->canonical_file_path) {
auto notebook_page=get_notebook_page(c);
canonical_file_path = file_path;
for(size_t c = 0; c < size(); c++) {
if(canonical_file_path == source_views[c]->canonical_file_path) {
auto notebook_page = get_notebook_page(c);
notebooks[notebook_page.first].set_current_page(notebook_page.second);
focus_view(source_views[c]);
return;
@ -125,41 +125,41 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
if(boost::filesystem::exists(file_path)) {
std::ifstream can_read(file_path.string());
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;
}
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;
if(language) {
language_protocol_language_id=language->get_id();
if(language_protocol_language_id=="js") {
if(file_path.extension()==".ts")
language_protocol_language_id="typescript";
language_protocol_language_id = language->get_id();
if(language_protocol_language_id == "js") {
if(file_path.extension() == ".ts")
language_protocol_language_id = "typescript";
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));
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));
else
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->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())
Gtk::Main::iteration(false);
if(get_current_view()==view) {
if(get_current_view() == view) {
if(center)
view->scroll_to(view->get_buffer()->get_insert(), 0.0, 1.0, 0.5);
else
@ -168,88 +168,88 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
view->hide_tooltips();
}
};
source_view->update_status_location=[this](Source::BaseView* view) {
if(get_current_view()==view) {
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));
source_view->update_status_location = [this](Source::BaseView *view) {
if(get_current_view() == view) {
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));
}
};
source_view->update_status_file_path=[this](Source::BaseView* view) {
if(get_current_view()==view)
status_file_path.set_text(' '+filesystem::get_short_path(view->file_path).string());
source_view->update_status_file_path = [this](Source::BaseView *view) {
if(get_current_view() == view)
status_file_path.set_text(' ' + filesystem::get_short_path(view->file_path).string());
};
source_view->update_status_branch=[this](Source::BaseView* view) {
if(get_current_view()==view) {
source_view->update_status_branch = [this](Source::BaseView *view) {
if(get_current_view() == view) {
if(!view->status_branch.empty())
status_branch.set_text(" ("+view->status_branch+")");
status_branch.set_text(" (" + view->status_branch + ")");
else
status_branch.set_text("");
}
};
source_view->update_status_diagnostics=[this](Source::BaseView* view) {
if(get_current_view()==view) {
source_view->update_status_diagnostics = [this](Source::BaseView *view) {
if(get_current_view() == view) {
std::string diagnostic_info;
auto num_warnings=std::get<0>(view->status_diagnostics);
auto num_errors=std::get<1>(view->status_diagnostics);
auto num_fix_its=std::get<2>(view->status_diagnostics);
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 num_warnings = std::get<0>(view->status_diagnostics);
auto num_errors = std::get<1>(view->status_diagnostics);
auto num_fix_its = std::get<2>(view->status_diagnostics);
if(num_warnings > 0 || num_errors > 0 || num_fix_its > 0) {
auto normal_color = get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL);
Gdk::RGBA yellow;
yellow.set_rgba(1.0, 1.0, 0.2);
double factor=0.5;
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_blue(normal_color.get_blue()+factor*(yellow.get_blue()-normal_color.get_blue()));
double factor = 0.5;
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_blue(normal_color.get_blue() + factor * (yellow.get_blue() - normal_color.get_blue()));
Gdk::RGBA red;
red.set_rgba(1.0, 0.0, 0.0);
factor=0.5;
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_blue(normal_color.get_blue()+factor*(red.get_blue()-normal_color.get_blue()));
factor = 0.5;
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_blue(normal_color.get_blue() + factor * (red.get_blue() - normal_color.get_blue()));
Gdk::RGBA green;
green.set_rgba(0.0, 1.0, 0.0);
factor=0.4;
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_blue(normal_color.get_blue()+factor*(green.get_blue()-normal_color.get_blue()));
factor = 0.4;
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_blue(normal_color.get_blue() + factor * (green.get_blue() - normal_color.get_blue()));
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);
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);
if(num_warnings>0) {
diagnostic_info+="<span color='#"+yellow_ss.str()+"'>";
diagnostic_info+=std::to_string(num_warnings)+" warning";
if(num_warnings>1)
diagnostic_info+='s';
diagnostic_info+="</span>";
}
if(num_errors>0) {
if(num_warnings>0)
diagnostic_info+=", ";
diagnostic_info+="<span color='#"+red_ss.str()+"'>";
diagnostic_info+=std::to_string(num_errors)+" error";
if(num_errors>1)
diagnostic_info+='s';
diagnostic_info+="</span>";
}
if(num_fix_its>0) {
if(num_warnings>0 || num_errors>0)
diagnostic_info+=", ";
diagnostic_info+="<span color='#"+green_ss.str()+"'>";
diagnostic_info+=std::to_string(num_fix_its)+" fix it";
if(num_fix_its>1)
diagnostic_info+='s';
diagnostic_info+="</span>";
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);
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) {
diagnostic_info += "<span color='#" + yellow_ss.str() + "'>";
diagnostic_info += std::to_string(num_warnings) + " warning";
if(num_warnings > 1)
diagnostic_info += 's';
diagnostic_info += "</span>";
}
if(num_errors > 0) {
if(num_warnings > 0)
diagnostic_info += ", ";
diagnostic_info += "<span color='#" + red_ss.str() + "'>";
diagnostic_info += std::to_string(num_errors) + " error";
if(num_errors > 1)
diagnostic_info += 's';
diagnostic_info += "</span>";
}
if(num_fix_its > 0) {
if(num_warnings > 0 || num_errors > 0)
diagnostic_info += ", ";
diagnostic_info += "<span color='#" + green_ss.str() + "'>";
diagnostic_info += std::to_string(num_fix_its) + " fix it";
if(num_fix_its > 1)
diagnostic_info += 's';
diagnostic_info += "</span>";
}
}
status_diagnostics.set_markup(diagnostic_info);
}
};
source_view->update_status_state=[this](Source::BaseView* view) {
if(get_current_view()==view)
status_state.set_text(view->status_state+" ");
source_view->update_status_state = [this](Source::BaseView *view) {
if(get_current_view() == view)
status_state.set_text(view->status_state + " ");
};
scrolled_windows.emplace_back(new Gtk::ScrolledWindow());
@ -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()));
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
tab_labels.emplace_back(new TabLabel([this, source_view]() {
auto index=get_index(source_view);
if(index!=static_cast<size_t>(-1))
auto index = get_index(source_view);
if(index != static_cast<size_t>(-1))
close(index);
}));
source_view->update_tab_label=[this](Source::BaseView *view) {
std::string title=view->file_path.filename().string();
source_view->update_tab_label = [this](Source::BaseView *view) {
std::string title = view->file_path.filename().string();
if(view->get_buffer()->get_modified())
title+='*';
title += '*';
else
title+=' ';
for(size_t c=0;c<size();++c) {
if(source_views[c]==view) {
auto &tab_label=tab_labels.at(c);
title += ' ';
for(size_t c = 0; c < size(); ++c) {
if(source_views[c] == view) {
auto &tab_label = tab_labels.at(c);
tab_label->label.set_text(title);
tab_label->set_tooltip_text(filesystem::get_short_path(view->file_path).string());
return;
@ -292,36 +292,36 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
});
//Cursor history
auto update_cursor_locations=[this, source_view](const Gtk::TextBuffer::iterator &iter) {
bool mark_moved=false;
if(current_cursor_location!=static_cast<size_t>(-1)) {
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) {
auto update_cursor_locations = [this, source_view](const Gtk::TextBuffer::iterator &iter) {
bool mark_moved = false;
if(current_cursor_location != static_cast<size_t>(-1)) {
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) {
source_view->get_buffer()->move_mark(cursor_location.mark, iter);
mark_moved=true;
mark_moved = true;
}
}
if(!mark_moved) {
if(current_cursor_location!=static_cast<size_t>(-1)) {
for(auto it=cursor_locations.begin()+current_cursor_location+1;it!=cursor_locations.end();) {
if(current_cursor_location != static_cast<size_t>(-1)) {
for(auto it = cursor_locations.begin() + current_cursor_location + 1; it != cursor_locations.end();) {
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));
current_cursor_location=cursor_locations.size()-1;
current_cursor_location = cursor_locations.size() - 1;
}
// Combine adjacent cursor histories that are similar
if(!cursor_locations.empty()) {
size_t cursor_locations_index=1;
auto last_it=cursor_locations.begin();
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) {
size_t cursor_locations_index = 1;
auto last_it = cursor_locations.begin();
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) {
last_it->view->get_buffer()->delete_mark(last_it->mark);
last_it->mark=it->mark;
it=cursor_locations.erase(it);
if(current_cursor_location!=static_cast<size_t>(-1) && current_cursor_location>cursor_locations_index)
last_it->mark = it->mark;
it = cursor_locations.erase(it);
if(current_cursor_location != static_cast<size_t>(-1) && current_cursor_location > cursor_locations_index)
--current_cursor_location;
}
else {
@ -333,20 +333,20 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
}
// 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.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;
}
if(current_cursor_location>=cursor_locations.size())
current_cursor_location=cursor_locations.size()-1;
if(current_cursor_location >= cursor_locations.size())
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) {
if(mark->get_name()=="insert") {
if(mark->get_name() == "insert") {
if(disable_next_update_cursor_locations) {
disable_next_update_cursor_locations=false;
disable_next_update_cursor_locations = false;
return;
}
update_cursor_locations(iter);
@ -357,23 +357,23 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
});
#ifdef JUCI_ENABLE_DEBUG
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) {
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 end_iter=source_view->get_iter_at_line_end(line_nr);
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) {
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 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_and_stop");
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 {
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);
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);
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);
}
};
}
@ -384,17 +384,17 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
return false;
});
if(notebook_index==static_cast<size_t>(-1)) {
if(notebook_index == static_cast<size_t>(-1)) {
if(!split)
notebook_index=0;
else if(notebooks[0].get_n_pages()==0)
notebook_index=0;
else if(notebooks[1].get_n_pages()==0)
notebook_index=1;
notebook_index = 0;
else if(notebooks[0].get_n_pages() == 0)
notebook_index = 0;
else if(notebooks[1].get_n_pages() == 0)
notebook_index = 1;
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());
@ -402,13 +402,13 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
notebook.set_tab_detachable(*hboxes.back(), true);
show_all_children();
notebook.set_current_page(notebook.get_n_pages()-1);
last_index=-1;
notebook.set_current_page(notebook.get_n_pages() - 1);
last_index = -1;
if(last_view) {
auto index=get_index(last_view);
auto notebook_page=get_notebook_page(index);
if(notebook_page.first==notebook_index)
last_index=index;
auto index = get_index(last_view);
auto notebook_page = get_notebook_page(index);
if(notebook_page.first == notebook_index)
last_index = index;
}
set_focus_child(*source_views.back());
@ -416,14 +416,14 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
}
void Notebook::configure(size_t index) {
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_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);
source_maps.at(index)->override_font(source_map_font_desc);
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);
}
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));
}
@ -435,77 +435,77 @@ bool Notebook::save(size_t index) {
}
bool Notebook::save_current() {
if(auto view=get_current_view())
if(auto view = get_current_view())
return save(get_index(view));
return false;
}
bool Notebook::close(size_t index) {
if(auto view=get_view(index)) {
if(view->get_buffer()->get_modified()){
if(auto view = get_view(index)) {
if(view->get_buffer()->get_modified()) {
if(!save_modified_dialog(index))
return false;
}
if(view==get_current_view()) {
bool focused=false;
if(last_index!=static_cast<size_t>(-1)) {
auto notebook_page=get_notebook_page(last_index);
if(notebook_page.first==get_notebook_page(get_index(view)).first) {
if(view == get_current_view()) {
bool focused = false;
if(last_index != static_cast<size_t>(-1)) {
auto notebook_page = get_notebook_page(last_index);
if(notebook_page.first == get_notebook_page(get_index(view)).first) {
focus_view(source_views[last_index]);
notebooks[notebook_page.first].set_current_page(notebook_page.second);
last_index=-1;
focused=true;
last_index = -1;
focused = true;
}
}
if(!focused) {
auto notebook_page=get_notebook_page(get_index(view));
if(notebook_page.second>0)
focus_view(get_view(notebook_page.first, notebook_page.second-1));
auto notebook_page = get_notebook_page(get_index(view));
if(notebook_page.second > 0)
focus_view(get_view(notebook_page.first, notebook_page.second - 1));
else {
size_t notebook_index=notebook_page.first==0?1:0;
if(notebooks[notebook_index].get_n_pages()>0)
size_t notebook_index = notebook_page.first == 0 ? 1 : 0;
if(notebooks[notebook_index].get_n_pages() > 0)
focus_view(get_view(notebook_index, notebooks[notebook_index].get_current_page()));
else
set_current_view(nullptr);
}
}
}
else if(index==last_index)
last_index=-1;
else if(index<last_index && last_index!=static_cast<size_t>(-1))
else if(index == last_index)
last_index = -1;
else if(index < last_index && last_index != static_cast<size_t>(-1))
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);
source_maps.erase(source_maps.begin()+index);
source_maps.erase(source_maps.begin() + index);
if(on_close_page)
on_close_page(view);
delete_cursor_locations(view);
SelectionDialog::get()=nullptr;
CompletionDialog::get()=nullptr;
SelectionDialog::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();
else
delete view;
source_views.erase(source_views.begin()+index);
scrolled_windows.erase(scrolled_windows.begin()+index);
hboxes.erase(hboxes.begin()+index);
tab_labels.erase(tab_labels.begin()+index);
source_views.erase(source_views.begin() + index);
scrolled_windows.erase(scrolled_windows.begin() + index);
hboxes.erase(hboxes.begin() + index);
tab_labels.erase(tab_labels.begin() + index);
}
return true;
}
void Notebook::delete_cursor_locations(Source::View *view) {
size_t cursor_locations_index=0;
for(auto it=cursor_locations.begin();it!=cursor_locations.end();) {
if(it->view==view) {
size_t cursor_locations_index = 0;
for(auto it = cursor_locations.begin(); it != cursor_locations.end();) {
if(it->view == view) {
view->get_buffer()->delete_mark(it->mark);
it=cursor_locations.erase(it);
if(current_cursor_location!=static_cast<size_t>(-1) && current_cursor_location>cursor_locations_index)
it = cursor_locations.erase(it);
if(current_cursor_location != static_cast<size_t>(-1) && current_cursor_location > cursor_locations_index)
--current_cursor_location;
}
else {
@ -513,8 +513,8 @@ void Notebook::delete_cursor_locations(Source::View *view) {
++cursor_locations_index;
}
}
if(current_cursor_location>=cursor_locations.size())
current_cursor_location=cursor_locations.size()-1;
if(current_cursor_location >= cursor_locations.size())
current_cursor_location = cursor_locations.size() - 1;
}
bool Notebook::close_current() {
@ -522,10 +522,10 @@ bool Notebook::close_current() {
}
void Notebook::next() {
if(auto view=get_current_view()) {
auto notebook_page=get_notebook_page(get_index(view));
int page=notebook_page.second+1;
if(page>=notebooks[notebook_page.first].get_n_pages())
if(auto view = get_current_view()) {
auto notebook_page = get_notebook_page(get_index(view));
int page = notebook_page.second + 1;
if(page >= notebooks[notebook_page.first].get_n_pages())
notebooks[notebook_page.first].set_current_page(0);
else
notebooks[notebook_page.first].set_current_page(page);
@ -533,11 +533,11 @@ void Notebook::next() {
}
void Notebook::previous() {
if(auto view=get_current_view()) {
auto notebook_page=get_notebook_page(get_index(view));
int page=notebook_page.second-1;
if(page<0)
notebooks[notebook_page.first].set_current_page(notebooks[notebook_page.first].get_n_pages()-1);
if(auto view = get_current_view()) {
auto notebook_page = get_notebook_page(get_index(view));
int page = notebook_page.second - 1;
if(page < 0)
notebooks[notebook_page.first].set_current_page(notebooks[notebook_page.first].get_n_pages() - 1);
else
notebooks[notebook_page.first].set_current_page(page);
}
@ -546,24 +546,24 @@ void Notebook::previous() {
void Notebook::toggle_split() {
if(!split) {
pack2(notebooks[1], true, true);
set_position(get_width()/2);
set_position(get_width() / 2);
show_all();
//Make sure the position is correct
//TODO: report bug to gtk if it is not fixed in gtk3.22
Glib::signal_timeout().connect([this] {
set_position(get_width()/2);
set_position(get_width() / 2);
return false;
}, 200);
}
else {
for(size_t c=size()-1;c!=static_cast<size_t>(-1);--c) {
auto notebook_index=get_notebook_page(c).first;
if(notebook_index==1 && !close(c))
for(size_t c = size() - 1; c != static_cast<size_t>(-1); --c) {
auto notebook_index = get_notebook_page(c).first;
if(notebook_index == 1 && !close(c))
return;
}
remove(notebooks[1]);
}
split=!split;
split = !split;
}
void Notebook::toggle_tabs() {
//Show / Hide tabs for each notebook.
@ -574,7 +574,7 @@ void Notebook::toggle_tabs() {
boost::filesystem::path Notebook::get_current_folder() {
if(!Directories::get().path.empty())
return Directories::get().path;
else if(auto view=get_current_view())
else if(auto view = get_current_view())
return view->file_path.parent_path();
else
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_views;
for(size_t notebook_index=0;notebook_index<notebooks.size();++notebook_index) {
for(int page=0;page<notebooks[notebook_index].get_n_pages();++page) {
if(auto view=get_view(notebook_index, page))
for(size_t notebook_index = 0; notebook_index < notebooks.size(); ++notebook_index) {
for(int page = 0; page < notebooks[notebook_index].get_n_pages(); ++page) {
if(auto view = get_view(notebook_index, page))
notebook_views.emplace_back(notebook_index, view);
}
}
@ -613,60 +613,60 @@ void Notebook::clear_status() {
}
size_t Notebook::get_index(Source::View *view) {
for(size_t c=0;c<size();++c) {
if(source_views[c]==view)
for(size_t c = 0; c < size(); ++c) {
if(source_views[c] == view)
return c;
}
return -1;
}
Source::View *Notebook::get_view(size_t notebook_index, int page) {
if(notebook_index==static_cast<size_t>(-1) || notebook_index>=notebooks.size() ||
page<0 || page>=notebooks[notebook_index].get_n_pages())
if(notebook_index == static_cast<size_t>(-1) || notebook_index >= notebooks.size() ||
page < 0 || page >= notebooks[notebook_index].get_n_pages())
return nullptr;
auto hbox=dynamic_cast<Gtk::Box*>(notebooks[notebook_index].get_nth_page(page));
auto scrolled_window=dynamic_cast<Gtk::ScrolledWindow*>(hbox->get_children()[0]);
return dynamic_cast<Source::View*>(scrolled_window->get_children()[0]);
auto hbox = dynamic_cast<Gtk::Box *>(notebooks[notebook_index].get_nth_page(page));
auto scrolled_window = dynamic_cast<Gtk::ScrolledWindow *>(hbox->get_children()[0]);
return dynamic_cast<Source::View *>(scrolled_window->get_children()[0]);
}
void Notebook::focus_view(Source::View *view) {
intermediate_view=view;
intermediate_view = view;
view->grab_focus();
}
std::pair<size_t, int> Notebook::get_notebook_page(size_t index) {
if(index>=hboxes.size())
if(index >= hboxes.size())
return {-1, -1};
for(size_t c=0;c<notebooks.size();++c) {
auto page_num=notebooks[c].page_num(*hboxes[index]);
if(page_num>=0)
for(size_t c = 0; c < notebooks.size(); ++c) {
auto page_num = notebooks[c].page_num(*hboxes[index]);
if(page_num >= 0)
return {c, page_num};
}
return {-1, -1};
}
void Notebook::set_current_view(Source::View *view) {
intermediate_view=nullptr;
if(current_view!=view) {
if(auto view=get_current_view()) {
intermediate_view = nullptr;
if(current_view != view) {
if(auto view = get_current_view()) {
view->hide_tooltips();
view->hide_dialogs();
}
current_view=view;
current_view = view;
if(view && on_change_page)
on_change_page(view);
}
}
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_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();
if(result==Gtk::RESPONSE_YES) {
if(result == Gtk::RESPONSE_YES) {
return save(index);
}
else if(result==Gtk::RESPONSE_NO) {
else if(result == Gtk::RESPONSE_NO) {
return true;
}
else {

41
src/notebook.h

@ -1,10 +1,10 @@
#pragma once
#include <iostream>
#include "gtkmm.h"
#include "source.h"
#include <type_traits>
#include <iostream>
#include <map>
#include <sigc++/sigc++.h>
#include <type_traits>
class Notebook : public Gtk::Paned {
class TabLabel : public Gtk::EventBox {
@ -22,6 +22,7 @@ class Notebook : public Gtk::Paned {
private:
Notebook();
public:
static Notebook &get() {
static Notebook singleton;
@ -29,11 +30,11 @@ public:
}
size_t size();
Source::View* get_view(size_t index);
Source::View* get_current_view();
std::vector<Source::View*> &get_views();
Source::View *get_view(size_t index);
Source::View *get_current_view();
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);
bool save(size_t index);
bool save_current();
@ -45,7 +46,7 @@ public:
/// Hide/Show tabs.
void toggle_tabs();
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_file_path;
@ -55,13 +56,13 @@ public:
void update_status(Source::BaseView *view);
void clear_status();
std::function<void(Source::View*)> on_change_page;
std::function<void(Source::View*)> on_close_page;
std::function<void(Source::View *)> on_change_page;
std::function<void(Source::View *)> on_close_page;
/// Cursor history
std::vector<CursorLocation> cursor_locations;
size_t current_cursor_location=-1;
bool disable_next_update_cursor_locations=false;
size_t current_cursor_location = -1;
bool disable_next_update_cursor_locations = false;
void delete_cursor_locations(Source::View *view);
private:
@ -71,18 +72,18 @@ private:
std::pair<size_t, int> get_notebook_page(size_t index);
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<std::unique_ptr<Gtk::Widget> > source_maps;
std::vector<std::unique_ptr<Gtk::ScrolledWindow> > scrolled_windows;
std::vector<std::unique_ptr<Gtk::Box> > hboxes;
std::vector<std::unique_ptr<TabLabel> > tab_labels;
std::vector<Source::View *> source_views; //Is NOT freed in destructor, this is intended for quick program exit.
std::vector<std::unique_ptr<Gtk::Widget>> source_maps;
std::vector<std::unique_ptr<Gtk::ScrolledWindow>> scrolled_windows;
std::vector<std::unique_ptr<Gtk::Box>> hboxes;
std::vector<std::unique_ptr<TabLabel>> tab_labels;
bool split=false;
size_t last_index=-1;
bool split = false;
size_t last_index = -1;
void set_current_view(Source::View *view);
Source::View* current_view=nullptr;
Source::View* intermediate_view=nullptr;
Source::View *current_view = nullptr;
Source::View *intermediate_view = nullptr;
bool save_modified_dialog(size_t index);
};

613
src/project.cc

File diff suppressed because it is too large Load Diff

21
src/project.h

@ -1,12 +1,12 @@
#pragma once
#include <gtkmm.h>
#include <boost/filesystem.hpp>
#include <atomic>
#include <unordered_map>
#include "tooltips.h"
#include "dispatcher.h"
#include <iostream>
#include "project_build.h"
#include "tooltips.h"
#include <atomic>
#include <boost/filesystem.hpp>
#include <gtkmm.h>
#include <iostream>
#include <unordered_map>
namespace Project {
class DebugRunArguments {
@ -31,7 +31,7 @@ namespace Project {
extern std::unordered_map<std::string, DebugRunArguments> debug_run_arguments;
extern std::atomic<bool> compiling;
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;
void debug_update_status(const std::string &new_debug_status);
void debug_activate_menu_items();
@ -40,9 +40,10 @@ namespace Project {
class Base : public std::enable_shared_from_this<Base> {
protected:
static std::unique_ptr<DebugOptions> debug_options;
public:
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;
std::unique_ptr<Build> build;
@ -103,7 +104,7 @@ namespace Project {
class LanguageProtocol : public virtual Base {
public:
virtual std::string get_language_id()=0;
virtual std::string get_language_id() = 0;
void show_symbols() override;
};
@ -164,4 +165,4 @@ namespace Project {
std::shared_ptr<Base> create();
extern std::shared_ptr<Base> current;
};
}; // namespace Project

80
src/project_build.cc

@ -3,10 +3,10 @@
#include "filesystem.h"
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) {
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));
if(!build->project_path.empty())
return build;
@ -14,27 +14,27 @@ std::unique_ptr<Project::Build> Project::Build::create(const boost::filesystem::
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));
if(!build->project_path.empty())
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());
build->project_path=search_path;
build->project_path = search_path;
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());
build->project_path=search_path;
build->project_path = search_path;
return build;
}
if(search_path==search_path.root_directory())
if(search_path == search_path.root_directory())
break;
search_path=search_path.parent_path();
search_path = search_path.parent_path();
}
return std::make_unique<Project::Build>();
@ -44,21 +44,21 @@ boost::filesystem::path Project::Build::get_default_path() {
if(project_path.empty())
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>";
size_t pos=0;
auto default_build_path_string=default_build_path.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) {
const std::string path_variable_project_directory_name = "<project_directory_name>";
size_t pos = 0;
auto default_build_path_string = default_build_path.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) {
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)
default_build_path=default_build_path_string;
if(pos != 0)
default_build_path = default_build_path_string;
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);
}
@ -67,38 +67,38 @@ boost::filesystem::path Project::Build::get_debug_path() {
if(project_path.empty())
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>";
size_t pos=0;
auto debug_build_path_string=debug_build_path.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) {
const std::string path_variable_project_directory_name = "<project_directory_name>";
size_t pos = 0;
auto debug_build_path_string = debug_build_path.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) {
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)
debug_build_path=debug_build_path_string;
const std::string path_variable_default_build_path="<default_build_path>";
pos=0;
debug_build_path_string=debug_build_path.string();
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) {
if(pos != 0)
debug_build_path = debug_build_path_string;
const std::string path_variable_default_build_path = "<default_build_path>";
pos = 0;
debug_build_path_string = debug_build_path.string();
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) {
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)
debug_build_path=debug_build_path_string;
if(pos != 0)
debug_build_path = debug_build_path_string;
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);
}
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) {
@ -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_path=meson.project_path;
project_path = meson.project_path;
}
bool Project::MesonBuild::update_default(bool force) {

28
src/project_build.h

@ -1,7 +1,7 @@
#pragma once
#include <boost/filesystem.hpp>
#include "cmake.h"
#include "meson.h"
#include <boost/filesystem.hpp>
namespace Project {
class Build {
@ -11,23 +11,24 @@ namespace Project {
boost::filesystem::path project_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 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 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);
};
class CMakeBuild : public Build {
::CMake cmake;
public:
CMakeBuild(const boost::filesystem::path &path);
bool update_default(bool force=false) override;
bool update_debug(bool force=false) override;
bool update_default(bool force = false) override;
bool update_debug(bool force = false) override;
std::string get_compile_command() override;
boost::filesystem::path get_executable(const boost::filesystem::path &path) override;
@ -35,11 +36,12 @@ namespace Project {
class MesonBuild : public Build {
Meson meson;
public:
MesonBuild(const boost::filesystem::path &path);
bool update_default(bool force=false) override;
bool update_debug(bool force=false) override;
bool update_default(bool force = false) override;
bool update_debug(bool force = false) override;
std::string get_compile_command() override;
boost::filesystem::path get_executable(const boost::filesystem::path &path) override;
@ -47,14 +49,14 @@ namespace Project {
class CargoBuild : public Build {
public:
boost::filesystem::path get_default_path() override { return project_path/"target"/"debug"; }
bool update_default(bool force=false) override { return true; }
boost::filesystem::path get_default_path() override { return project_path / "target" / "debug"; }
bool update_default(bool force = false) override { return true; }
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"; }
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 {};
}
} // namespace Project

256
src/selection_dialog.cc

@ -19,28 +19,28 @@ SelectionDialogBase::ListViewText::ListViewText(bool use_markup) : Gtk::TreeView
set_rules_hint(true);
}
void SelectionDialogBase::ListViewText::append(const std::string& value) {
auto new_row=list_store->append();
void SelectionDialogBase::ListViewText::append(const std::string &value) {
auto new_row = list_store->append();
new_row->set_value(column_record.text, value);
new_row->set_value(column_record.index, size++);
}
void SelectionDialogBase::ListViewText::erase_rows() {
list_store->clear();
size=0;
size = 0;
}
void SelectionDialogBase::ListViewText::clear() {
unset_model();
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):
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 gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
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) {
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
window.set_transient_for(*application->get_active_window());
window.set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_COMBO);
@ -53,7 +53,7 @@ SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const Glib::R
list_view_text.set_search_entry(search_entry);
window.set_default_size(0, 0);
window.property_decorated()=false;
window.property_decorated() = false;
window.set_skip_taskbar_hint(true);
scrolled_window.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC);
@ -64,37 +64,37 @@ SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const Glib::R
vbox.pack_start(scrolled_window, true, true);
window.add(vbox);
list_view_text.signal_realize().connect([this](){
auto g_application=g_application_get_default();
auto gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
auto application_window=application->get_active_window();
list_view_text.signal_realize().connect([this]() {
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
auto application_window = application->get_active_window();
// 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;
auto children=list_view_text.get_model()->children();
size_t c=0;
for(auto it=children.begin();it!=children.end() && c<10;++it) {
auto children = list_view_text.get_model()->children();
size_t c = 0;
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);
if(c==0) {
row_width=rect.get_width()+rect.get_x()*2;
padding_height=rect.get_y()*2;
if(c == 0) {
row_width = rect.get_width() + rect.get_x() * 2;
padding_height = rect.get_y() * 2;
}
window_height+=rect.get_height()+padding_height;
window_height += rect.get_height() + padding_height;
++c;
}
if(this->text_view && 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)
row_width=application_window->get_width()/2;
if(this->text_view && 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)
row_width = application_window->get_width() / 2;
else
scrolled_window.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC);
if(this->show_search_entry)
window_height+=search_entry.get_height();
int window_width=row_width+1;
window_height += search_entry.get_height();
int window_width = row_width + 1;
window.resize(window_width, window_height);
if(this->text_view) {
@ -102,19 +102,19 @@ SelectionDialogBase::SelectionDialogBase(Gtk::TextView *text_view, const Glib::R
this->text_view->get_iter_location(this->start_mark->get_iter(), iter_rect);
Gdk::Rectangle 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_y=iter_rect.get_y()+iter_rect.get_height();
int buffer_x = std::max(iter_rect.get_x(), visible_rect.get_x());
int buffer_y = iter_rect.get_y() + iter_rect.get_height();
int window_x, window_y;
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;
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 {
int root_x, root_y;
application_window->get_position(root_x, root_y);
root_x+=application_window->get_width()/2-window_width/2;
root_y+=application_window->get_height()/2-window_height/2;
root_x += application_window->get_width() / 2 - window_width / 2;
root_y += application_window->get_height() / 2 - window_height / 2;
window.move(root_x, root_y);
}
});
@ -132,21 +132,21 @@ SelectionDialogBase::~SelectionDialogBase() {
void SelectionDialogBase::cursor_changed() {
if(!is_visible())
return;
auto it=list_view_text.get_selection()->get_selected();
auto index=static_cast<unsigned int>(-1);
auto it = list_view_text.get_selection()->get_selected();
auto index = static_cast<unsigned int>(-1);
if(it)
index=it->get_value(list_view_text.column_record.index);
if(last_index==index)
index = it->get_value(list_view_text.column_record.index);
if(last_index == index)
return;
if(on_changed) {
std::string text;
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);
}
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);
}
@ -159,12 +159,12 @@ void SelectionDialogBase::show() {
if(text_view)
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()) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(list_view_text.get_model()->children().begin()));
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())
Gtk::Main::iteration(false);
if(is_visible())
@ -176,9 +176,9 @@ void SelectionDialogBase::show() {
}
void SelectionDialogBase::set_cursor_at_last_row() {
auto children=list_view_text.get_model()->children();
if(children.size()>0) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(children[children.size()-1]));
auto children = list_view_text.get_model()->children();
if(children.size() > 0) {
list_view_text.set_cursor(list_view_text.get_model()->get_path(children[children.size() - 1]));
cursor_changed();
}
}
@ -190,70 +190,71 @@ void SelectionDialogBase::hide() {
if(on_hide)
on_hide();
list_view_text.clear();
last_index=static_cast<unsigned int>(-1);
last_index = static_cast<unsigned int>(-1);
}
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) {
auto search_key=std::make_shared<std::string>();
auto filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model());
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) {
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;
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(search_key_lc.begin(), search_key_lc.end(), search_key_lc.begin(), ::tolower);
if(list_view_text.use_markup) {
size_t pos=0;
while((pos=row_lc.find('<', pos))!=std::string::npos) {
auto pos2=row_lc.find('>', pos+1);
row_lc.erase(pos, pos2-pos+1);
size_t pos = 0;
while((pos = row_lc.find('<', pos)) != std::string::npos) {
auto pos2 = row_lc.find('>', 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 false;
});
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;
});
search_entry.signal_changed().connect([this, search_key, filter_model](){
*search_key=search_entry.get_text();
search_entry.signal_changed().connect([this, search_key, filter_model]() {
*search_key = search_entry.get_text();
filter_model->refilter();
list_view_text.set_search_entry(search_entry); //TODO:Report the need of this to GTK's git (bug)
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()));
}
});
auto activate=[this](){
auto it=list_view_text.get_selection()->get_selected();
auto activate = [this]() {
auto it = list_view_text.get_selection()->get_selected();
if(on_select && it) {
auto index=it->get_value(list_view_text.column_record.index);
auto text=it->get_value(list_view_text.column_record.text);
auto index = it->get_value(list_view_text.column_record.index);
auto text = it->get_value(list_view_text.column_record.text);
on_select(index, text, true);
}
hide();
};
search_entry.signal_activate().connect([activate](){
search_entry.signal_activate().connect([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();
});
}
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) {
auto it=list_view_text.get_selection()->get_selected();
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) {
auto it = list_view_text.get_selection()->get_selected();
if(it) {
it++;
if(it)
@ -261,8 +262,8 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) {
}
return true;
}
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();
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();
if(it) {
it--;
if(it)
@ -270,43 +271,43 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) {
}
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) {
auto it=list_view_text.get_selection()->get_selected();
auto column=list_view_text.get_column(0);
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 column = list_view_text.get_column(0);
list_view_text.row_activated(list_view_text.get_model()->get_path(it), *column);
return true;
}
else if(key->keyval==GDK_KEY_Escape) {
else if(key->keyval == GDK_KEY_Escape) {
hide();
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();
return false;
}
else if(show_search_entry) {
#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();
if(key->keyval==GDK_KEY_dead_tilde) {
int search_entry_length = search_entry.get_text_length();
if(key->keyval == GDK_KEY_dead_tilde) {
search_entry.insert_text("~", 1, search_entry_length);
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);
return true;
}
else if(key->is_modifier)
return true;
else if(key->keyval==GDK_KEY_BackSpace) {
auto length=search_entry.get_text_length();
if(length>0)
search_entry.delete_text(length-1, length);
else if(key->keyval == GDK_KEY_BackSpace) {
auto length = search_entry.get_text_length();
if(length > 0)
search_entry.delete_text(length - 1, length);
return true;
}
else {
gunichar unicode=gdk_keyval_to_unicode(key->keyval);
if(unicode>=32 && unicode!=126) {
auto ustr=Glib::ustring(1, unicode);
gunichar unicode = gdk_keyval_to_unicode(key->keyval);
if(unicode >= 32 && unicode != 126) {
auto ustr = Glib::ustring(1, unicode);
search_entry.insert_text(ustr, ustr.bytes(), search_entry_length);
return true;
}
@ -323,75 +324,75 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) {
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) {
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 filter_model=Gtk::TreeModelFilter::create(list_view_text.get_model());
if(show_offset==start_mark->get_iter().get_offset()) {
filter_model->set_visible_func([search_key](const Gtk::TreeModel::const_iterator& iter){
auto search_key = std::make_shared<std::string>();
auto filter_model = Gtk::TreeModelFilter::create(list_view_text.get_model());
if(show_offset == start_mark->get_iter().get_offset()) {
filter_model->set_visible_func([search_key](const Gtk::TreeModel::const_iterator &iter) {
std::string 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(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 false;
});
}
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;
iter->get_value(0, row);
if(row.find(*search_key)==0)
if(row.find(*search_key) == 0)
return true;
return false;
});
}
list_view_text.set_model(filter_model);
search_entry.signal_changed().connect([this, search_key, filter_model](){
*search_key=search_entry.get_text();
search_entry.signal_changed().connect([this, search_key, filter_model]() {
*search_key = search_entry.get_text();
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.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();
});
auto text=text_view->get_buffer()->get_text(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter());
if(text.size()>0) {
auto text = text_view->get_buffer()->get_text(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter());
if(text.size() > 0) {
search_entry.set_text(text);
list_view_text.set_search_entry(search_entry);
}
}
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) {
auto index=it->get_value(list_view_text.column_record.index);
auto text=it->get_value(list_view_text.column_record.text);
auto index = it->get_value(list_view_text.column_record.index);
auto text = it->get_value(list_view_text.column_record.text);
on_select(index, text, hide_window);
}
if(hide_window)
hide();
}
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)
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)
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();
}
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);
list_view_text.set_search_entry(search_entry);
if(text=="") {
if(list_view_text.get_model()->children().size()>0)
if(text == "") {
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()));
}
cursor_changed();
@ -399,23 +400,24 @@ bool CompletionDialog::on_key_release(GdkEventKey* key) {
return false;
}
bool CompletionDialog::on_key_press(GdkEventKey* key) {
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_underscore || key->keyval==GDK_KEY_BackSpace) {
bool CompletionDialog::on_key_press(GdkEventKey *key) {
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_underscore || key->keyval == GDK_KEY_BackSpace) {
if(row_in_entry) {
text_view->get_buffer()->erase(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter());
row_in_entry=false;
if(key->keyval==GDK_KEY_BackSpace)
row_in_entry = false;
if(key->keyval == GDK_KEY_BackSpace)
return true;
}
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;
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();
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();
if(it) {
it++;
if(it) {
@ -428,8 +430,8 @@ bool CompletionDialog::on_key_press(GdkEventKey* key) {
select(false);
return true;
}
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();
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();
if(it) {
it--;
if(it) {
@ -438,7 +440,7 @@ bool CompletionDialog::on_key_press(GdkEventKey* key) {
}
}
else {
auto last_it=list_view_text.get_model()->children().end();
auto last_it = list_view_text.get_model()->children().end();
last_it--;
if(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);
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();
return true;
}
hide();
if(key->keyval==GDK_KEY_Escape)
if(key->keyval == GDK_KEY_Escape)
return true;
return false;
}

43
src/selection_dialog.h

@ -1,7 +1,7 @@
#pragma once
#include "gtkmm.h"
#include <unordered_map>
#include <functional>
#include <unordered_map>
class SelectionDialogBase {
class ListViewText : public Gtk::TreeView {
@ -14,17 +14,19 @@ class SelectionDialogBase {
Gtk::TreeModelColumn<std::string> text;
Gtk::TreeModelColumn<unsigned int> index;
};
public:
bool use_markup;
ColumnRecord column_record;
ListViewText(bool use_markup);
void append(const std::string& value);
void append(const std::string &value);
void erase_rows();
void clear();
private:
Glib::RefPtr<Gtk::ListStore> list_store;
Gtk::CellRendererText cell_renderer;
unsigned int size=0;
unsigned int size = 0;
};
class SearchEntry : public Gtk::Entry {
@ -36,14 +38,14 @@ class SelectionDialogBase {
public:
SelectionDialogBase(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark, bool show_search_entry, bool use_markup);
virtual ~SelectionDialogBase();
void add_row(const std::string& row);
void add_row(const std::string &row);
void erase_rows();
void set_cursor_at_last_row();
void show();
void hide();
bool is_visible() {return window.is_visible();}
void get_position(int &root_x, int &root_y) {window.get_position(root_x, root_y);}
bool is_visible() { return window.is_visible(); }
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_hide;
@ -63,38 +65,41 @@ protected:
SearchEntry 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 {
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;
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) {
instance=std::unique_ptr<SelectionDialog>(new SelectionDialog(text_view, start_mark, show_search_entry, use_markup));
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));
}
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));
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));
}
static std::unique_ptr<SelectionDialog> &get() {return instance;}
static std::unique_ptr<SelectionDialog> &get() { return instance; }
};
class CompletionDialog : public SelectionDialogBase {
CompletionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gtk::TextBuffer::Mark> &start_mark);
static std::unique_ptr<CompletionDialog> instance;
public:
bool on_key_release(GdkEventKey* key);
bool on_key_press(GdkEventKey* key);
bool on_key_release(GdkEventKey *key);
bool on_key_press(GdkEventKey *key);
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:
void select(bool hide_window=true);
void select(bool hide_window = true);
int show_offset;
bool row_in_entry=false;
bool row_in_entry = false;
};

2367
src/source.cc

File diff suppressed because it is too large Load Diff

73
src/source.h

@ -1,14 +1,14 @@
#pragma once
#include "source_spellcheck.h"
#include "source_diff.h"
#include "source_spellcheck.h"
#include "tooltips.h"
#include <boost/property_tree/xml_parser.hpp>
#include <boost/filesystem.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <set>
#include <string>
#include <tuple>
#include <unordered_map>
#include <vector>
#include <set>
#include <tuple>
namespace Source {
/// Workaround for buggy Gsv::LanguageManager::get_default()
@ -27,9 +27,9 @@ namespace Source {
class Offset {
public:
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(); }
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 index;
@ -38,7 +38,7 @@ namespace Source {
class FixIt {
public:
enum class Type {INSERT, REPLACE, ERASE};
enum class Type { INSERT, REPLACE, ERASE };
FixIt(std::string source_, std::pair<Offset, Offset> offsets_);
@ -51,10 +51,10 @@ namespace Source {
class View : public SpellCheckView, public DiffView {
public:
static std::set<View*> non_deleted_views;
static std::set<View*> views;
static std::set<View *> non_deleted_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;
bool save() override;
@ -77,9 +77,9 @@ namespace Source {
std::function<Offset()> get_type_declaration_location;
std::function<std::vector<Offset>()> get_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::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::string()> get_token_spelling;
std::function<void(const std::string &text)> rename_similar_tokens;
@ -93,12 +93,13 @@ namespace Source {
void hide_dialogs() override;
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 full_reparse_needed = false;
virtual void soft_reparse(bool delayed = false) { soft_reparse_needed = false; }
virtual void full_reparse() { full_reparse_needed = false; }
bool soft_reparse_needed=false;
bool full_reparse_needed=false;
virtual void soft_reparse(bool delayed=false) {soft_reparse_needed=false;}
virtual void full_reparse() {full_reparse_needed=false;}
protected:
std::atomic<bool> parsed;
Tooltips diagnostic_tooltips;
@ -110,8 +111,8 @@ namespace Source {
void add_diagnostic_tooltip(const Gtk::TextIter &start, const Gtk::TextIter &end, std::string spelling, bool error);
void clear_diagnostic_tooltips();
virtual void show_type_tooltips(const Gdk::Rectangle &rectangle) {}
gdouble on_motion_last_x=0.0;
gdouble on_motion_last_y=0.0;
gdouble on_motion_last_x = 0.0;
gdouble on_motion_last_y = 0.0;
Gtk::TextIter find_non_whitespace_code_iter_backward(Gtk::TextIter iter);
/// If closing bracket is found, continues until the open bracket.
@ -127,11 +128,11 @@ namespace Source {
void cleanup_whitespace_characters_on_return(const Gtk::TextIter &iter);
bool on_key_press_event(GdkEventKey* key) override;
bool on_key_press_event_basic(GdkEventKey* key);
bool on_key_press_event_bracket_language(GdkEventKey* key);
bool on_key_press_event_smart_brackets(GdkEventKey* key);
bool on_key_press_event_smart_inserts(GdkEventKey* key);
bool on_key_press_event(GdkEventKey *key) override;
bool on_key_press_event_basic(GdkEventKey *key);
bool on_key_press_event_bracket_language(GdkEventKey *key);
bool on_key_press_event_smart_brackets(GdkEventKey *key);
bool on_key_press_event_smart_inserts(GdkEventKey *key);
bool on_button_press_event(GdkEventButton *event) override;
bool on_motion_notify_event(GdkEventMotion *motion_event) override;
@ -140,7 +141,8 @@ namespace Source {
char tab_char;
std::string tab;
bool interactive_completion=true;
bool interactive_completion = true;
private:
void setup_tooltip_and_dialog_events();
void setup_format_style(bool is_generic_view);
@ -150,33 +152,34 @@ namespace Source {
GtkSourceSearchContext *search_context;
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;
bool is_bracket_language=false;
bool use_fixed_continuation_indenting=true;
bool is_cpp=false;
guint previous_non_modifier_keyval=0;
bool is_bracket_language = false;
bool use_fixed_continuation_indenting = true;
bool is_cpp = false;
guint previous_non_modifier_keyval = 0;
bool multiple_cursors_signals_set=false;
bool multiple_cursors_use=false;
bool multiple_cursors_signals_set = false;
bool multiple_cursors_use = false;
std::vector<std::pair<Glib::RefPtr<Gtk::TextBuffer::Mark>, int>> multiple_cursors_extra_cursors;
Glib::RefPtr<Gtk::TextBuffer::Mark> multiple_cursors_last_insert;
int multiple_cursors_erase_backward_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 {
private:
class CompletionBuffer : public Gtk::TextBuffer {
public:
static Glib::RefPtr<CompletionBuffer> create() {return Glib::RefPtr<CompletionBuffer>(new CompletionBuffer());}
static Glib::RefPtr<CompletionBuffer> create() { return Glib::RefPtr<CompletionBuffer>(new CompletionBuffer()); }
};
public:
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);
};
}
} // namespace Source

196
src/source_base.cc

@ -1,16 +1,16 @@
#include "source_base.h"
#include "config.h"
#include "git.h"
#include "info.h"
#include "terminal.h"
#include "git.h"
#include "config.h"
#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);
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(0));
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();
return false;
});
@ -25,11 +25,11 @@ Source::BaseView::~BaseView() {
bool Source::BaseView::load(bool not_undoable_action) {
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)
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)
get_source_buffer()->begin_not_undoable_action();
@ -40,7 +40,7 @@ bool Source::BaseView::load(bool not_undoable_action) {
~Guard() {
if(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};
@ -50,21 +50,21 @@ bool Source::BaseView::load(bool not_undoable_action) {
if(input) {
std::stringstream ss;
ss << input.rdbuf();
Glib::ustring ustr=ss.str();
Glib::ustring ustr = ss.str();
bool valid=true;
bool valid = true;
Glib::ustring::iterator iter;
while(!ustr.validate(iter)) {
auto next_char_iter=iter;
auto next_char_iter = iter;
next_char_iter++;
ustr.replace(iter, next_char_iter, "?");
valid=false;
valid = false;
}
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);
else
replace_text(ustr.raw());
@ -77,16 +77,16 @@ bool Source::BaseView::load(bool not_undoable_action) {
if(input) {
std::stringstream ss;
ss << input.rdbuf();
Glib::ustring ustr=ss.str();
Glib::ustring ustr = ss.str();
if(ustr.validate()) {
if(get_buffer()->size()==0)
if(get_buffer()->size() == 0)
get_buffer()->insert_at_cursor(ustr);
else
replace_text(ustr.raw());
}
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;
}
}
@ -101,7 +101,7 @@ bool Source::BaseView::load(bool not_undoable_action) {
void Source::BaseView::replace_text(const std::string &new_text) {
get_buffer()->begin_user_action();
if(get_buffer()->size()==0) {
if(get_buffer()->size() == 0) {
get_buffer()->insert_at_cursor(new_text);
get_buffer()->end_user_action();
return;
@ -112,47 +112,47 @@ void Source::BaseView::replace_text(const std::string &new_text) {
return;
}
auto iter=get_buffer()->get_insert()->get_iter();
int cursor_line_nr=iter.get_line();
int cursor_line_offset=iter.ends_line() ? std::numeric_limits<int>::max() : iter.get_line_offset();
auto iter = get_buffer()->get_insert()->get_iter();
int cursor_line_nr = iter.get_line();
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();
for(const char &chr: new_text) {
if(chr=='\n') {
new_lines.emplace_back(line_start, &chr+1);
line_start = &chr+1;
const char *line_start = new_text.c_str();
for(const char &chr : new_text) {
if(chr == '\n') {
new_lines.emplace_back(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()]);
try {
auto hunks = Git::Repository::Diff::get_hunks(get_buffer()->get_text().raw(), new_text);
for(auto it=hunks.rbegin();it!=hunks.rend();++it) {
bool place_cursor=false;
for(auto it = hunks.rbegin(); it != hunks.rend(); ++it) {
bool place_cursor = false;
Gtk::TextIter start;
if(it->old_lines.second!=0) {
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);
if(it->old_lines.second != 0) {
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);
if(cursor_line_nr>=start.get_line() && cursor_line_nr<end.get_line()) {
if(it->new_lines.second!=0) {
if(cursor_line_nr >= start.get_line() && cursor_line_nr < end.get_line()) {
if(it->new_lines.second != 0) {
place_cursor = true;
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;
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;
}
}
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
start=get_buffer()->get_iter_at_line(it->old_lines.first);
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);
start = get_buffer()->get_iter_at_line(it->old_lines.first);
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);
if(place_cursor)
place_cursor_at_line_offset(cursor_line_nr, cursor_line_offset);
}
@ -166,7 +166,7 @@ void Source::BaseView::replace_text(const std::string &new_text) {
}
void Source::BaseView::rename(const boost::filesystem::path &path) {
file_path=path;
file_path = path;
if(update_status_file_path)
update_status_file_path(this);
@ -180,10 +180,10 @@ void Source::BaseView::monitor_file() {
public:
static void f(BaseView *view, std::time_t last_write_time_) {
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;
auto last_write_time=boost::filesystem::last_write_time(view->file_path, ec);
if(last_write_time!=last_write_time_)
auto last_write_time = boost::filesystem::last_write_time(view->file_path, ec);
if(last_write_time != last_write_time_)
view->check_last_write_time(last_write_time);
Recursive::f(view, last_write_time);
return false;
@ -191,18 +191,18 @@ void Source::BaseView::monitor_file() {
}
};
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);
#else
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);
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_changed_connection.disconnect();
monitor_changed_connection=monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File>&,
monitor_changed_connection = monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File> &,
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=Glib::signal_timeout().connect([this]() {
delayed_monitor_changed_connection = Glib::signal_timeout().connect([this]() {
check_last_write_time();
return false;
}, 500);
@ -213,21 +213,21 @@ void Source::BaseView::monitor_file() {
}
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;
if(Config::get().source.auto_reload_changed_files && !get_buffer()->get_modified()) {
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);
if(!ec && last_write_time!=this->last_write_time) {
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(load())
return;
}
}
else if(has_focus()) {
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);
if(!ec && last_write_time!=this->last_write_time)
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)
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) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_iter_at_line_end(line);
offset=std::min(offset, iter.get_line_offset());
if(offset<0)
offset=0;
line = std::min(line, get_buffer()->get_line_count() - 1);
if(line < 0)
line = 0;
auto iter = get_iter_at_line_end(line);
offset = std::min(offset, iter.get_line_offset());
if(offset < 0)
offset = 0;
return get_buffer()->get_iter_at_line_offset(line, offset);
}
Gtk::TextIter Source::BaseView::get_iter_at_line_index(int line, int index) {
line=std::min(line, get_buffer()->get_line_count()-1);
if(line<0)
line=0;
auto iter=get_iter_at_line_end(line);
index=std::min(index, iter.get_line_index());
if(index<0)
index=0;
line = std::min(line, get_buffer()->get_line_count() - 1);
if(line < 0)
line = 0;
auto iter = get_iter_at_line_end(line);
index = std::min(index, iter.get_line_index());
if(index < 0)
index = 0;
return get_buffer()->get_iter_at_line_index(line, index);
}
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();
else if(line_nr+1<get_buffer()->get_line_count()) {
auto iter=get_buffer()->get_iter_at_line(line_nr+1);
else if(line_nr + 1 < get_buffer()->get_line_count()) {
auto iter = get_buffer()->get_iter_at_line(line_nr + 1);
iter.backward_char();
if(!iter.ends_line()) // for CR+LF
iter.backward_char();
return iter;
}
else {
auto iter=get_buffer()->get_iter_at_line(line_nr);
while(!iter.ends_line() && iter.forward_char()) {}
auto iter = get_buffer()->get_iter_at_line(line_nr);
while(!iter.ends_line() && iter.forward_char()) {
}
return iter;
}
}
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;
get_visible_rect(visible_rect);
Gdk::Rectangle iter_rect;
get_iter_location(iter, iter_rect);
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_location(iter, iter_rect);
}
if(!visible_rect.intersects(iter_rect))
get_iter_at_location(iter, visible_rect.get_x(), visible_rect.get_y()+visible_rect.get_height()/3);
get_iter_at_location(iter, visible_rect.get_x(), visible_rect.get_y() + visible_rect.get_height() / 3);
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) {
auto start_line_iter=get_buffer()->get_iter_at_line(iter.get_line());
auto start_sentence_iter=start_line_iter;
auto start_line_iter = get_buffer()->get_iter_at_line(iter.get_line());
auto start_sentence_iter = start_line_iter;
while(!start_sentence_iter.ends_line() &&
(*start_sentence_iter==' ' || *start_sentence_iter=='\t') &&
start_sentence_iter.forward_char()) {}
(*start_sentence_iter == ' ' || *start_sentence_iter == '\t') &&
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;
else
return start_line_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_sentence_iter=end_line_iter;
auto end_line_iter = get_iter_at_line_end(iter.get_line());
auto end_sentence_iter = end_line_iter;
while(!end_sentence_iter.starts_line() &&
(*end_sentence_iter==' ' || *end_sentence_iter=='\t' || end_sentence_iter.ends_line()) &&
end_sentence_iter.backward_char()) {}
if(!end_sentence_iter.ends_line() && *end_sentence_iter!=' ' && *end_sentence_iter!='\t')
(*end_sentence_iter == ' ' || *end_sentence_iter == '\t' || end_sentence_iter.ends_line()) &&
end_sentence_iter.backward_char()) {
}
if(!end_sentence_iter.ends_line() && *end_sentence_iter != ' ' && *end_sentence_iter != '\t')
end_sentence_iter.forward_char();
if(iter==end_line_iter)
if(iter == end_line_iter)
return end_sentence_iter;
else
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) {
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;
}
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() {
auto insert_offset=get_buffer()->get_insert()->get_iter().get_offset();
for(auto offset: diagnostic_offsets) {
if(offset>insert_offset) {
auto insert_offset = get_buffer()->get_insert()->get_iter().get_offset();
for(auto offset : diagnostic_offsets) {
if(offset > insert_offset) {
get_buffer()->place_cursor(get_buffer()->get_iter_at_offset(offset));
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return;
}
}
if(diagnostic_offsets.size()==0)
if(diagnostic_offsets.size() == 0)
Info::get().print("No diagnostics found in current buffer");
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);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
}

13
src/source_base.h

@ -1,9 +1,9 @@
#pragma once
#include <boost/filesystem.hpp>
#include <gtksourceviewmm.h>
#include <mutex>
#include <set>
#include <boost/filesystem.hpp>
namespace Source {
class BaseView : public Gsv::View {
@ -14,7 +14,7 @@ namespace Source {
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
void replace_text(const std::string &new_text);
virtual void rename(const boost::filesystem::path &path);
@ -28,7 +28,7 @@ namespace Source {
virtual void hide_tooltips() = 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.
virtual Gtk::TextIter get_iter_at_line_pos(int line, int pos);
@ -50,7 +50,7 @@ namespace Source {
protected:
std::time_t last_write_time;
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.
/// Works with wrapped lines.
@ -74,6 +74,7 @@ namespace Source {
std::set<int> diagnostic_offsets;
void place_cursor_at_next_diagnostic();
public:
std::function<void(BaseView *view)> update_tab_label;
std::function<void(BaseView *view)> update_status_location;
@ -85,6 +86,6 @@ namespace Source {
std::function<void(BaseView *view)> update_status_branch;
std::string status_branch;
bool disable_spellcheck=false;
bool disable_spellcheck = false;
};
}
} // namespace Source

1376
src/source_clang.cc

File diff suppressed because it is too large Load Diff

38
src/source_clang.h

@ -1,20 +1,20 @@
#pragma once
#include <thread>
#include "autocomplete.h"
#include "clangmm.h"
#include "dispatcher.h"
#include "source.h"
#include "terminal.h"
#include <atomic>
#include <map>
#include <mutex>
#include <set>
#include "clangmm.h"
#include "source.h"
#include "terminal.h"
#include "dispatcher.h"
#include "autocomplete.h"
#include <thread>
namespace Source {
class ClangViewParse : public View {
protected:
enum class ParseState {PROCESSING, RESTARTING, STOP};
enum class ParseProcessState {IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING};
enum class ParseState { PROCESSING, RESTARTING, STOP };
enum class ParseProcessState { IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING };
public:
ClangViewParse(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
@ -22,7 +22,8 @@ namespace Source {
bool save() override;
void configure() override;
void soft_reparse(bool delayed=false) override;
void soft_reparse(bool delayed = false) override;
protected:
Dispatcher dispatcher;
void parse_initialize();
@ -40,7 +41,8 @@ namespace Source {
std::atomic<ParseState> parse_state;
std::atomic<ParseProcessState> parse_process_state;
CXCompletionString selected_completion_string=nullptr;
CXCompletionString selected_completion_string = nullptr;
private:
Glib::ustring parse_thread_buffer;
@ -57,11 +59,13 @@ namespace Source {
class ClangViewAutocomplete : public virtual ClangViewParse {
public:
ClangViewAutocomplete(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
protected:
Autocomplete autocomplete;
std::unique_ptr<clangmm::CodeCompleteResults> code_complete_results;
std::vector<CXCompletionString> completion_strings;
sigc::connection delayed_show_arguments_connection;
private:
bool is_possible_parameter();
bool show_arguments;
@ -75,17 +79,19 @@ namespace Source {
: kind(cursor.get_kind()), spelling(std::move(spelling_)), usr_extended(cursor.get_usr_extended()), cursor(cursor) {}
Identifier() : kind(static_cast<clangmm::Cursor::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 !(*this==rhs); }
bool operator<(const Identifier &rhs) const { return spelling<rhs.spelling || (spelling==rhs.spelling && usr_extended<rhs.usr_extended); }
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 !(*this == rhs); }
bool operator<(const Identifier &rhs) const { return spelling < rhs.spelling || (spelling == rhs.spelling && usr_extended < rhs.usr_extended); }
clangmm::Cursor::Kind kind;
std::string spelling;
std::string usr_extended;
clangmm::Cursor cursor;
};
public:
ClangViewRefactor(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
private:
Identifier get_identifier();
void wait_parsing();
@ -105,6 +111,6 @@ namespace Source {
Glib::Dispatcher do_delete_object;
std::thread delete_thread;
std::thread full_reparse_thread;
bool full_reparse_running=false;
bool full_reparse_running = false;
};
}
} // namespace Source

200
src/source_diff.cc

@ -1,8 +1,8 @@
#include "source_diff.h"
#include "config.h"
#include "terminal.h"
#include "filesystem.h"
#include "info.h"
#include "terminal.h"
#include <boost/version.hpp>
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)) {
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();
}
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->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();
}
}
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;
canonical_file_path=boost::filesystem::canonical(file_path, ec);
canonical_file_path = boost::filesystem::canonical(file_path, ec);
if(ec)
canonical_file_path=file_path;
canonical_file_path = file_path;
renderer->tag_added=get_buffer()->create_tag("git_added");
renderer->tag_modified=get_buffer()->create_tag("git_modified");
renderer->tag_removed=get_buffer()->create_tag("git_removed");
renderer->tag_removed_below=get_buffer()->create_tag();
renderer->tag_removed_above=get_buffer()->create_tag();
renderer->tag_added = get_buffer()->create_tag("git_added");
renderer->tag_modified = get_buffer()->create_tag("git_modified");
renderer->tag_removed = get_buffer()->create_tag("git_removed");
renderer->tag_removed_below = get_buffer()->create_tag();
renderer->tag_removed_above = get_buffer()->create_tag();
}
Source::DiffView::~DiffView() {
@ -57,7 +57,7 @@ Source::DiffView::~DiffView() {
delayed_buffer_changed_connection.disconnect();
delayed_monitor_changed_connection.disconnect();
parse_stop=true;
parse_stop = true;
if(parse_thread.joinable())
parse_thread.join();
}
@ -76,11 +76,11 @@ void Source::DiffView::configure() {
delayed_buffer_changed_connection.disconnect();
delayed_monitor_changed_connection.disconnect();
parse_stop=true;
parse_stop = true;
if(parse_thread.joinable())
parse_thread.join();
repository=nullptr;
diff=nullptr;
repository = nullptr;
diff = nullptr;
return;
}
@ -88,24 +88,24 @@ void Source::DiffView::configure() {
return;
try {
repository=Git::get_repository(this->file_path.parent_path());
repository = Git::get_repository(this->file_path.parent_path());
}
catch(const std::exception &) {
return;
}
get_gutter(Gtk::TextWindowType::TEXT_WINDOW_LEFT)->insert(renderer.get(), -40);
parse_state=ParseState::STARTING;
parse_stop=false;
monitor_changed=false;
parse_state = ParseState::STARTING;
parse_stop = 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
if(!iter.starts_line() && iter.has_tag(renderer->tag_added)) {
bool newline=false;
for(auto &c: text.raw()) {
if(c=='\n') {
newline=true;
bool newline = false;
for(auto &c : text.raw()) {
if(c == '\n') {
newline = true;
break;
}
}
@ -113,92 +113,92 @@ void Source::DiffView::configure() {
return;
}
//Remove tag_removed_above/below if newline is inserted
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 end_iter=get_iter_at_line_end(iter.get_line());
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 end_iter = get_iter_at_line_end(iter.get_line());
end_iter.forward_char();
get_buffer()->remove_tag(renderer->tag_removed_above, 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=Glib::signal_timeout().connect([this]() {
parse_state=ParseState::STARTING;
delayed_buffer_changed_connection = Glib::signal_timeout().connect([this]() {
parse_state = ParseState::STARTING;
return false;
}, 250);
}, 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
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;
parse_state=ParseState::IDLE;
parse_state = ParseState::IDLE;
delayed_buffer_changed_connection.disconnect();
delayed_buffer_changed_connection=Glib::signal_timeout().connect([this]() {
parse_state=ParseState::STARTING;
delayed_buffer_changed_connection = Glib::signal_timeout().connect([this]() {
parse_state = ParseState::STARTING;
return false;
}, 250);
}, false);
monitor_changed_connection=repository->monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File>&,
monitor_changed_connection = repository->monitor->signal_changed().connect([this](const Glib::RefPtr<Gio::File> &file,
const Glib::RefPtr<Gio::File> &,
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=Glib::signal_timeout().connect([this]() {
monitor_changed=true;
parse_state=ParseState::STARTING;
delayed_monitor_changed_connection = Glib::signal_timeout().connect([this]() {
monitor_changed = true;
parse_state = ParseState::STARTING;
std::unique_lock<std::mutex> lock(parse_mutex);
diff=nullptr;
diff = nullptr;
return false;
}, 500);
}
});
parse_thread=std::thread([this]() {
parse_thread = std::thread([this]() {
std::string status_branch;
try {
diff=get_diff();
status_branch=repository->get_branch();
diff = get_diff();
status_branch = repository->get_branch();
}
catch(const std::exception &) {
status_branch="";
status_branch = "";
}
dispatcher.post([this, status_branch=std::move(status_branch)] {
this->status_branch=status_branch;
dispatcher.post([this, status_branch = std::move(status_branch)] {
this->status_branch = status_branch;
if(update_status_branch)
update_status_branch(this);
});
try {
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));
if(parse_stop)
break;
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)) {
dispatcher.post([this] {
auto expected=ParseState::PREPROCESSING;
auto expected = ParseState::PREPROCESSING;
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock);
if(parse_lock.try_lock()) {
if(parse_state.compare_exchange_strong(expected, ParseState::PROCESSING))
parse_buffer=get_buffer()->get_text();
parse_buffer = get_buffer()->get_text();
parse_lock.unlock();
}
else
parse_state.compare_exchange_strong(expected, ParseState::STARTING);
});
}
else if (parse_state==ParseState::PROCESSING && parse_lock.try_lock()) {
bool expected_monitor_changed=true;
else if(parse_state == ParseState::PROCESSING && parse_lock.try_lock()) {
bool expected_monitor_changed = true;
if(monitor_changed.compare_exchange_strong(expected_monitor_changed, false)) {
try {
diff=get_diff();
dispatcher.post([this, status_branch=repository->get_branch()] {
this->status_branch=status_branch;
diff = get_diff();
dispatcher.post([this, status_branch = repository->get_branch()] {
this->status_branch = status_branch;
if(update_status_branch)
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_above, get_buffer()->begin(), get_buffer()->end());
renderer->queue_draw();
this->status_branch="";
this->status_branch = "";
if(update_status_branch)
update_status_branch(this);
});
}
}
if(diff)
lines=diff->get_lines(parse_buffer.raw());
lines = diff->get_lines(parse_buffer.raw());
else {
lines.added.clear();
lines.modified.clear();
lines.removed.clear();
}
auto expected=ParseState::PROCESSING;
auto expected = ParseState::PROCESSING;
if(parse_state.compare_exchange_strong(expected, ParseState::POSTPROCESSING)) {
parse_lock.unlock();
dispatcher.post([this] {
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock);
if(parse_lock.try_lock()) {
auto expected=ParseState::POSTPROCESSING;
auto expected = ParseState::POSTPROCESSING;
if(parse_state.compare_exchange_strong(expected, ParseState::IDLE))
update_lines();
}
@ -240,14 +240,14 @@ void Source::DiffView::configure() {
}
}
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_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_below, get_buffer()->begin(), get_buffer()->end());
get_buffer()->remove_tag(renderer->tag_removed_above, get_buffer()->begin(), get_buffer()->end());
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);
});
}
});
@ -258,32 +258,32 @@ void Source::DiffView::rename(const boost::filesystem::path &path) {
std::lock_guard<std::mutex> lock(canonical_file_path_mutex);
boost::system::error_code ec;
canonical_file_path=boost::filesystem::canonical(path, ec);
canonical_file_path = boost::filesystem::canonical(path, ec);
if(ec)
canonical_file_path=path;
canonical_file_path = path;
}
void Source::DiffView::git_goto_next_diff() {
auto iter=get_buffer()->get_insert()->get_iter();
auto insert_iter=iter;
bool wrapped=false;
auto iter = get_buffer()->get_insert()->get_iter();
auto insert_iter = iter;
bool wrapped = false;
iter.forward_char();
for(;;) {
auto toggled_tags=iter.get_toggled_tags();
for(auto &toggled_tag: toggled_tags) {
if(toggled_tag->property_name()=="git_added" ||
toggled_tag->property_name()=="git_modified" ||
toggled_tag->property_name()=="git_removed") {
auto toggled_tags = iter.get_toggled_tags();
for(auto &toggled_tag : toggled_tags) {
if(toggled_tag->property_name() == "git_added" ||
toggled_tag->property_name() == "git_modified" ||
toggled_tag->property_name() == "git_removed") {
get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return;
}
}
if(wrapped && (iter==insert_iter || iter==get_buffer()->end()))
if(wrapped && (iter == insert_iter || iter == get_buffer()->end()))
break;
if(!wrapped && iter==get_buffer()->end()) {
iter=get_buffer()->begin();
wrapped=true;
if(!wrapped && iter == get_buffer()->end()) {
iter = get_buffer()->begin();
wrapped = true;
}
else
iter.forward_char();
@ -294,13 +294,13 @@ void Source::DiffView::git_goto_next_diff() {
std::string Source::DiffView::git_get_diff_details() {
std::string details;
if(diff) {
auto line_nr=get_buffer()->get_insert()->get_iter().get_line();
auto iter=get_buffer()->get_iter_at_line(line_nr);
auto line_nr = get_buffer()->get_insert()->get_iter().get_line();
auto iter = get_buffer()->get_iter_at_line(line_nr);
if(iter.has_tag(renderer->tag_removed_above))
--line_nr;
std::unique_lock<std::mutex> lock(parse_mutex);
parse_buffer=get_buffer()->get_text();
details=diff->get_details(parse_buffer.raw(), line_nr);
parse_buffer = get_buffer()->get_text();
details = diff->get_details(parse_buffer.raw(), line_nr);
}
if(details.empty())
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
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;
{
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())
throw std::runtime_error("not a relative path");
}
@ -327,35 +327,35 @@ void Source::DiffView::update_lines() {
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());
for(auto &added: lines.added) {
auto start_iter=get_buffer()->get_iter_at_line(added.first);
auto end_iter=get_iter_at_line_end(added.second-1);
for(auto &added : lines.added) {
auto start_iter = get_buffer()->get_iter_at_line(added.first);
auto end_iter = get_iter_at_line_end(added.second - 1);
end_iter.forward_char();
get_buffer()->apply_tag(renderer->tag_added, start_iter, end_iter);
}
for(auto &modified: lines.modified) {
auto start_iter=get_buffer()->get_iter_at_line(modified.first);
auto end_iter=get_iter_at_line_end(modified.second-1);
for(auto &modified : lines.modified) {
auto start_iter = get_buffer()->get_iter_at_line(modified.first);
auto end_iter = get_iter_at_line_end(modified.second - 1);
end_iter.forward_char();
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;
if(line_nr>=0) {
auto start_iter=get_buffer()->get_iter_at_line(line_nr);
removed_start=start_iter;
auto end_iter=get_iter_at_line_end(line_nr);
if(line_nr >= 0) {
auto start_iter = get_buffer()->get_iter_at_line(line_nr);
removed_start = start_iter;
auto end_iter = get_iter_at_line_end(line_nr);
end_iter.forward_char();
removed_end=end_iter;
removed_end = end_iter;
get_buffer()->apply_tag(renderer->tag_removed_below, start_iter, end_iter);
}
if(line_nr+1<get_buffer()->get_line_count()) {
auto start_iter=get_buffer()->get_iter_at_line(line_nr+1);
if(line_nr<0)
removed_start=start_iter;
auto end_iter=get_iter_at_line_end(line_nr+1);
if(line_nr + 1 < get_buffer()->get_line_count()) {
auto start_iter = get_buffer()->get_iter_at_line(line_nr + 1);
if(line_nr < 0)
removed_start = start_iter;
auto end_iter = get_iter_at_line_end(line_nr + 1);
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, removed_start, removed_end);

16
src/source_diff.h

@ -1,17 +1,17 @@
#pragma once
#include "dispatcher.h"
#include "git.h"
#include "source_base.h"
#include <atomic>
#include <boost/filesystem.hpp>
#include "dispatcher.h"
#include <set>
#include <map>
#include <thread>
#include <atomic>
#include <mutex>
#include "git.h"
#include <set>
#include <thread>
namespace Source {
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 {
public:
@ -28,6 +28,7 @@ namespace Source {
const Gdk::Rectangle &cell_area, Gtk::TextIter &start, Gtk::TextIter &end,
Gsv::GutterRendererState p6) override;
};
public:
DiffView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language);
~DiffView() override;
@ -41,6 +42,7 @@ namespace Source {
/// Use canonical path to follow symbolic links
boost::filesystem::path canonical_file_path;
private:
std::mutex canonical_file_path_mutex;
@ -66,4 +68,4 @@ namespace Source {
Git::Repository::Diff::Lines lines;
void update_lines();
};
}
} // namespace Source

978
src/source_language_protocol.cc

File diff suppressed because it is too large Load Diff

10
src/source_language_protocol.h

@ -4,10 +4,10 @@
#include "source.h"
#include <boost/property_tree/json_parser.hpp>
#include <list>
#include <mutex>
#include <sstream>
#include <map>
#include <mutex>
#include <set>
#include <sstream>
namespace Source {
class LanguageProtocolView;
@ -61,7 +61,7 @@ namespace LanguageProtocol {
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::mutex timeout_threads_mutex;
@ -126,8 +126,8 @@ namespace Source {
boost::filesystem::path flow_coverage_executable;
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";
size_t num_warnings=0, num_errors=0, num_fix_its=0, num_flow_coverage_warnings=0;
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;
void add_flow_coverage_tooltips(bool called_in_thread);
};
} // namespace Source

336
src/source_spellcheck.cc

@ -4,14 +4,14 @@
#include "selection_dialog.h"
#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) {
if(spellcheck_config==nullptr)
spellcheck_config=new_aspell_config();
spellcheck_checker=nullptr;
spellcheck_error_tag=get_buffer()->create_tag("spellcheck_error");
spellcheck_error_tag->property_underline()=Pango::Underline::UNDERLINE_ERROR;
Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language) : BaseView(file_path, language) {
if(spellcheck_config == nullptr)
spellcheck_config = new_aspell_config();
spellcheck_checker = nullptr;
spellcheck_error_tag = get_buffer()->create_tag("spellcheck_error");
spellcheck_error_tag->property_underline() = Pango::Underline::UNDERLINE_ERROR;
signal_key_press_event().connect([](GdkEventKey *event) {
if(SelectionDialog::get() && SelectionDialog::get()->is_visible()) {
@ -24,101 +24,102 @@ Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path,
//The following signal is added in case SpellCheckView is not subclassed
signal_key_press_event().connect([this](GdkEventKey *event) {
last_keyval=event->keyval;
last_keyval = event->keyval;
return false;
});
get_buffer()->signal_changed().connect([this]() {
if(spellcheck_checker==nullptr)
if(spellcheck_checker == nullptr)
return;
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())
iter.backward_char();
if(disable_spellcheck) {
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);
}
return;
}
if(!is_code_iter(iter)) {
if(last_keyval==GDK_KEY_Return || last_keyval==GDK_KEY_KP_Enter) {
auto previous_line_iter=iter;
while(previous_line_iter.backward_char() && !previous_line_iter.ends_line()) {}
if(last_keyval == GDK_KEY_Return || last_keyval == GDK_KEY_KP_Enter) {
auto previous_line_iter = iter;
while(previous_line_iter.backward_char() && !previous_line_iter.ends_line()) {
}
if(previous_line_iter.backward_char()) {
get_buffer()->remove_tag(spellcheck_error_tag, previous_line_iter, 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);
}
auto word=get_word(iter);
auto word = get_word(iter);
spellcheck_word(word.first, word.second);
}
}
else {
auto previous_iter=iter;
auto previous_iter = iter;
//When for instance using space to split two words
if(!iter.starts_line() && !iter.ends_line() && is_word_iter(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()) {
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);
word=get_word(iter);
word = get_word(iter);
spellcheck_word(word.first, word.second);
}
}
else {
auto word=get_word(iter);
auto word = get_word(iter);
spellcheck_word(word.first, word.second);
}
}
}
delayed_spellcheck_error_clear.disconnect();
delayed_spellcheck_error_clear=Glib::signal_timeout().connect([this]() {
auto iter=get_buffer()->begin();
delayed_spellcheck_error_clear = Glib::signal_timeout().connect([this]() {
auto iter = get_buffer()->begin();
Gtk::TextIter begin_no_spellcheck_iter;
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)
begin_no_spellcheck_iter=iter;
while(iter!=get_buffer()->end()) {
begin_no_spellcheck_iter = iter;
while(iter != get_buffer()->end()) {
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)
begin_no_spellcheck_iter=iter;
begin_no_spellcheck_iter = iter;
else
get_buffer()->remove_tag(spellcheck_error_tag, begin_no_spellcheck_iter, iter);
}
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)
begin_no_spellcheck_iter=iter;
while(iter!=get_buffer()->end()) {
auto iter1=iter;
auto iter2=iter;
begin_no_spellcheck_iter = iter;
while(iter != get_buffer()->end()) {
auto iter1 = iter;
auto iter2 = iter;
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"))
iter2=get_buffer()->end();
iter2 = get_buffer()->end();
if(iter2<iter1)
iter=iter2;
if(iter2 < iter1)
iter = iter2;
else
iter=iter1;
spell_check=!spell_check;
iter = iter1;
spell_check = !spell_check;
if(!spell_check)
begin_no_spellcheck_iter=iter;
begin_no_spellcheck_iter = iter;
else
get_buffer()->remove_tag(spellcheck_error_tag, begin_no_spellcheck_iter, iter);
}
@ -128,46 +129,46 @@ Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path,
// 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) {
if(spellcheck_checker==nullptr)
if(spellcheck_checker == nullptr)
return;
if(!disable_spellcheck)
return;
auto iter=start_iter;
auto iter = start_iter;
if(!is_word_iter(iter) && !iter.starts_line())
iter.backward_char();
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);
}
}, false);
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iter, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(spellcheck_checker==nullptr)
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator &iter, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) {
if(spellcheck_checker == nullptr)
return;
if(mark->get_name()=="insert") {
if(mark->get_name() == "insert") {
if(SelectionDialog::get())
SelectionDialog::get()->hide();
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)) {
SelectionDialog::create(this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()), false);
auto word=get_word(get_buffer()->get_insert()->get_iter());
if(*word.first=='\'' && word.second.get_offset()-word.first.get_offset()>=3) {
auto before_end=word.second;
if(before_end.backward_char() && *before_end=='\'') {
auto word = get_word(get_buffer()->get_insert()->get_iter());
if(*word.first == '\'' && word.second.get_offset() - word.first.get_offset() >= 3) {
auto before_end = word.second;
if(before_end.backward_char() && *before_end == '\'') {
word.first.forward_char();
word.second.backward_char();
}
}
auto suggestions=get_spellcheck_suggestions(word.first, word.second);
if(suggestions.size()==0)
auto suggestions = get_spellcheck_suggestions(word.first, word.second);
if(suggestions.size() == 0)
return false;
for(auto &suggestion: suggestions)
for(auto &suggestion : suggestions)
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()->erase(word.first, word.second);
get_buffer()->insert(get_buffer()->get_insert()->get_iter(), text);
@ -181,37 +182,37 @@ Source::SpellCheckView::SpellCheckView(const boost::filesystem::path &file_path,
}
});
get_buffer()->signal_mark_set().connect([](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(mark->get_name()=="insert") {
get_buffer()->signal_mark_set().connect([](const Gtk::TextBuffer::iterator &iterator, const Glib::RefPtr<Gtk::TextBuffer::Mark> &mark) {
if(mark->get_name() == "insert") {
if(SelectionDialog::get())
SelectionDialog::get()->hide();
}
});
signal_focus_out_event().connect([this](GdkEventFocus* event) {
signal_focus_out_event().connect([this](GdkEventFocus *event) {
delayed_spellcheck_suggestions_connection.disconnect();
return false;
});
signal_leave_notify_event().connect([this](GdkEventCrossing*) {
signal_leave_notify_event().connect([this](GdkEventCrossing *) {
delayed_spellcheck_suggestions_connection.disconnect();
return false;
});
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")
comment_tag=tag;
else if(tag->property_name()=="gtksourceview:context-classes:string")
string_tag=tag;
else if(tag->property_name()=="gtksourceview:context-classes:no-spell-check")
no_spell_check_tag=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")
comment_tag = tag;
else if(tag->property_name() == "gtksourceview:context-classes:string")
string_tag = tag;
else if(tag->property_name() == "gtksourceview:context-classes:no-spell-check")
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) {
if(tag->property_name()=="gtksourceview:context-classes:comment")
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")
comment_tag.reset();
else if(tag->property_name()=="gtksourceview:context-classes:string")
else if(tag->property_name() == "gtksourceview:context-classes:string")
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();
});
}
@ -220,7 +221,7 @@ Source::SpellCheckView::~SpellCheckView() {
delayed_spellcheck_suggestions_connection.disconnect();
delayed_spellcheck_error_clear.disconnect();
if(spellcheck_checker!=nullptr)
if(spellcheck_checker != nullptr)
delete_aspell_speller(spellcheck_checker);
signal_tag_added_connection.disconnect();
@ -228,15 +229,15 @@ Source::SpellCheckView::~SpellCheckView() {
}
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, "encoding", "utf-8");
}
spellcheck_possible_err=new_aspell_speller(spellcheck_config);
if(spellcheck_checker!=nullptr)
spellcheck_possible_err = new_aspell_speller(spellcheck_config);
if(spellcheck_checker != nullptr)
delete_aspell_speller(spellcheck_checker);
spellcheck_checker=nullptr;
if (aspell_error_number(spellcheck_possible_err) != 0)
spellcheck_checker = nullptr;
if(aspell_error_number(spellcheck_possible_err) != 0)
std::cerr << "Spell check error: " << aspell_error_message(spellcheck_possible_err) << std::endl;
else
spellcheck_checker = to_aspell_speller(spellcheck_possible_err);
@ -249,57 +250,57 @@ void Source::SpellCheckView::hide_dialogs() {
SelectionDialog::get()->hide();
}
void Source::SpellCheckView::spellcheck(const Gtk::TextIter& start, const Gtk::TextIter& end) {
if(spellcheck_checker==nullptr)
void Source::SpellCheckView::spellcheck(const Gtk::TextIter &start, const Gtk::TextIter &end) {
if(spellcheck_checker == nullptr)
return;
auto iter=start;
while(iter && iter<end) {
auto iter = start;
while(iter && iter < end) {
if(is_word_iter(iter)) {
auto word=get_word(iter);
auto word = get_word(iter);
spellcheck_word(word.first, word.second);
iter=word.second;
iter = word.second;
}
iter.forward_char();
}
}
void Source::SpellCheckView::spellcheck() {
auto iter=get_buffer()->begin();
auto iter = get_buffer()->begin();
Gtk::TextIter begin_spellcheck_iter;
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)
begin_spellcheck_iter=iter;
while(iter!=get_buffer()->end()) {
begin_spellcheck_iter = iter;
while(iter != get_buffer()->end()) {
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)
begin_spellcheck_iter=iter;
begin_spellcheck_iter = iter;
else
spellcheck(begin_spellcheck_iter, iter);
}
}
else {
bool spell_check=!is_code_iter(iter);
bool spell_check = !is_code_iter(iter);
if(spell_check)
begin_spellcheck_iter=iter;
while(iter!=get_buffer()->end()) {
auto iter1=iter;
auto iter2=iter;
begin_spellcheck_iter = iter;
while(iter != get_buffer()->end()) {
auto iter1 = iter;
auto iter2 = iter;
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"))
iter2=get_buffer()->end();
iter2 = get_buffer()->end();
if(iter2<iter1)
iter=iter2;
if(iter2 < iter1)
iter = iter2;
else
iter=iter1;
spell_check=!spell_check;
iter = iter1;
spell_check = !spell_check;
if(spell_check)
begin_spellcheck_iter=iter;
begin_spellcheck_iter = iter;
else
spellcheck(begin_spellcheck_iter, iter);
}
@ -311,32 +312,32 @@ void Source::SpellCheckView::remove_spellcheck_errors() {
}
void Source::SpellCheckView::goto_next_spellcheck_error() {
auto iter=get_buffer()->get_insert()->get_iter();
auto insert_iter=iter;
bool wrapped=false;
auto iter = get_buffer()->get_insert()->get_iter();
auto insert_iter = iter;
bool wrapped = false;
iter.forward_char();
while(!wrapped || iter<insert_iter) {
auto toggled_tags=iter.get_toggled_tags();
for(auto &toggled_tag: toggled_tags) {
if(toggled_tag==spellcheck_error_tag) {
while(!wrapped || iter < insert_iter) {
auto toggled_tags = iter.get_toggled_tags();
for(auto &toggled_tag : toggled_tags) {
if(toggled_tag == spellcheck_error_tag) {
get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return;
}
}
iter.forward_char();
if(!wrapped && iter==get_buffer()->end()) {
iter=get_buffer()->begin();
wrapped=true;
if(!wrapped && iter == get_buffer()->end()) {
iter = get_buffer()->begin();
wrapped = true;
}
}
Info::get().print("No spelling errors found in current buffer");
}
bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
if(*iter=='\'') {
auto previous_iter=iter;
if(!iter.starts_line() && previous_iter.backward_char() && *previous_iter=='\'')
if(*iter == '\'') {
auto previous_iter = iter;
if(!iter.starts_line() && previous_iter.backward_char() && *previous_iter == '\'')
return false;
}
if(spellcheck_all) {
@ -344,10 +345,10 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
return true;
// workaround for gtksourceview bug
if(iter.ends_line()) {
auto previous_iter=iter;
auto previous_iter = iter;
if(previous_iter.backward_char()) {
if(*previous_iter=='\'' || *previous_iter=='"') {
auto next_iter=iter;
if(*previous_iter == '\'' || *previous_iter == '"') {
auto next_iter = iter;
next_iter.forward_char();
if(next_iter.begins_tag(no_spell_check_tag) || next_iter.is_end())
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""
if(*iter=='\'' || *iter=='"') {
auto previous_iter=iter;
if(previous_iter.backward_char() && *previous_iter!='\'' && *previous_iter!='\"' && previous_iter.ends_tag(no_spell_check_tag))
if(*iter == '\'' || *iter == '"') {
auto previous_iter = iter;
if(previous_iter.backward_char() && *previous_iter != '\'' && *previous_iter != '\"' && previous_iter.ends_tag(no_spell_check_tag))
return true;
}
return false;
@ -367,14 +368,15 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
return false;
//Exception at the end of /**/
else if(iter.ends_tag(comment_tag)) {
auto previous_iter=iter;
if(previous_iter.backward_char() && *previous_iter=='/') {
auto previous_previous_iter=previous_iter;
if(previous_previous_iter.backward_char() && *previous_previous_iter=='*') {
auto it=previous_iter;
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 previous_iter = iter;
if(previous_iter.backward_char() && *previous_iter == '/') {
auto previous_previous_iter = previous_iter;
if(previous_previous_iter.backward_char() && *previous_previous_iter == '*') {
auto it = previous_iter;
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)
return true;
}
}
@ -385,15 +387,16 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
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
// For instance 'test, when inserting ' at end, would lead to spellcheck error of test'
if(*iter=='\'') {
long backslash_count=0;
auto it=iter;
while(it.backward_char() && *it=='\\')
if(*iter == '\'') {
long backslash_count = 0;
auto it = iter;
while(it.backward_char() && *it == '\\')
++backslash_count;
if(backslash_count%2==0) {
auto it=iter;
while(!it.begins_tag(string_tag) && it.backward_to_tag_toggle(string_tag)) {}
if(it.begins_tag(string_tag) && *it=='\'' && iter!=it)
if(backslash_count % 2 == 0) {
auto it = iter;
while(!it.begins_tag(string_tag) && it.backward_to_tag_toggle(string_tag)) {
}
if(it.begins_tag(string_tag) && *it == '\'' && iter != it)
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 '
else if(iter.ends_tag(string_tag)) {
auto previous_iter=iter;
auto previous_iter = iter;
if(!iter.starts_line() && previous_iter.backward_char()) {
if((*previous_iter=='"' || *previous_iter=='\'')) {
long backslash_count=0;
auto it=previous_iter;
while(it.backward_char() && *it=='\\')
if((*previous_iter == '"' || *previous_iter == '\'')) {
long backslash_count = 0;
auto it = previous_iter;
while(it.backward_char() && *it == '\\')
++backslash_count;
if(backslash_count%2==0) {
auto it=previous_iter;
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(backslash_count % 2 == 0) {
auto it = previous_iter;
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)
return true;
}
}
@ -423,19 +427,19 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
return true;
}
bool Source::SpellCheckView::is_word_iter(const Gtk::TextIter& iter) {
auto previous_iter=iter;
size_t backslash_count=0;
while(previous_iter.backward_char() && *previous_iter=='\\')
bool Source::SpellCheckView::is_word_iter(const Gtk::TextIter &iter) {
auto previous_iter = iter;
size_t backslash_count = 0;
while(previous_iter.backward_char() && *previous_iter == '\\')
++backslash_count;
if(backslash_count%2==1)
if(backslash_count % 2 == 1)
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;
if(*iter=='\'') {
if(*iter == '\'') {
if(is_code_iter(iter))
return false;
auto next_iter=iter;
auto next_iter = 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
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) {
auto start=iter;
auto end=iter;
auto start = iter;
auto end = iter;
while(is_word_iter(iter)) {
start=iter;
start = iter;
if(!iter.backward_char())
break;
}
@ -462,34 +466,34 @@ std::pair<Gtk::TextIter, Gtk::TextIter> Source::SpellCheckView::get_word(Gtk::Te
}
void Source::SpellCheckView::spellcheck_word(Gtk::TextIter start, Gtk::TextIter end) {
if(*start=='\'' && end.get_offset()-start.get_offset()>=3) {
auto before_end=end;
if(before_end.backward_char() && *before_end=='\'') {
if(*start == '\'' && end.get_offset() - start.get_offset() >= 3) {
auto before_end = end;
if(before_end.backward_char() && *before_end == '\'') {
get_buffer()->remove_tag(spellcheck_error_tag, start, end);
start.forward_char();
end.backward_char();
}
}
auto word=get_buffer()->get_text(start, end);
if(word.size()>0) {
auto word = get_buffer()->get_text(start, end);
if(word.size() > 0) {
auto correct = aspell_speller_check(spellcheck_checker, word.data(), word.bytes());
if(correct==0)
if(correct == 0)
get_buffer()->apply_tag(spellcheck_error_tag, start, end);
else
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) {
auto word_with_error=get_buffer()->get_text(start, 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);
const AspellWordList *suggestions = aspell_speller_suggest(spellcheck_checker, word_with_error.data(), word_with_error.bytes());
AspellStringEnumeration *elements = aspell_word_list_elements(suggestions);
std::vector<std::string> words;
const char *word;
while ((word = aspell_string_enumeration_next(elements))!= nullptr) {
while((word = aspell_string_enumeration_next(elements)) != nullptr) {
words.emplace_back(word);
}
delete_aspell_string_enumeration(elements);

15
src/source_spellcheck.h

@ -17,28 +17,29 @@ namespace Source {
protected:
bool is_code_iter(const Gtk::TextIter &iter);
bool spellcheck_all=false;
guint last_keyval=0;
bool spellcheck_all = false;
guint last_keyval = 0;
Glib::RefPtr<Gtk::TextTag> comment_tag;
Glib::RefPtr<Gtk::TextTag> string_tag;
Glib::RefPtr<Gtk::TextTag> no_spell_check_tag;
private:
Glib::RefPtr<Gtk::TextTag> spellcheck_error_tag;
sigc::connection signal_tag_added_connection;
sigc::connection signal_tag_removed_connection;
static AspellConfig* spellcheck_config;
static AspellConfig *spellcheck_config;
AspellCanHaveError *spellcheck_possible_err;
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);
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_error_clear;
void spellcheck(const Gtk::TextIter& start, const Gtk::TextIter& end);
void spellcheck(const Gtk::TextIter &start, const Gtk::TextIter &end);
};
}
} // namespace Source

277
src/terminal.cc

@ -1,9 +1,9 @@
#include "terminal.h"
#include "config.h"
#include "project.h"
#include "filesystem.h"
#include "info.h"
#include "notebook.h"
#include "filesystem.h"
#include "project.h"
#include <iostream>
#include <regex>
#include <thread>
@ -11,28 +11,28 @@
Terminal::Terminal() {
set_editable(false);
bold_tag=get_buffer()->create_tag();
bold_tag->property_weight()=Pango::WEIGHT_ULTRAHEAVY;
bold_tag = get_buffer()->create_tag();
bold_tag->property_weight() = Pango::WEIGHT_ULTRAHEAVY;
link_tag=get_buffer()->create_tag();
link_tag->property_underline()=Pango::Underline::UNDERLINE_SINGLE;
link_tag = get_buffer()->create_tag();
link_tag->property_underline() = Pango::Underline::UNDERLINE_SINGLE;
link_mouse_cursor=Gdk::Cursor::create(Gdk::CursorType::HAND1);
default_mouse_cursor=Gdk::Cursor::create(Gdk::CursorType::XTERM);
link_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::HAND1);
default_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::XTERM);
}
int Terminal::process(const std::string &command, const boost::filesystem::path &path, bool use_pipes) {
std::unique_ptr<TinyProcessLib::Process> process;
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));
}, [this](const char* bytes, size_t n) {
}, [this](const char *bytes, size_t n) {
async_print(std::string(bytes, n), true);
});
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);
return -1;
}
@ -41,23 +41,23 @@ int Terminal::process(const std::string &command, const boost::filesystem::path
}
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::iterator iter;
while(!umessage.validate(iter)) {
auto next_char_iter=iter;
auto next_char_iter = iter;
next_char_iter++;
umessage.replace(iter, next_char_iter, "?");
}
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)
stderr_stream->write(bytes, n);
else
async_print(std::string(bytes, n), true);
}, true);
if(process.get_id()<=0) {
if(process.get_id() <= 0) {
async_print("Error: failed to run command: " + command + "\n", true);
return -1;
}
@ -65,8 +65,8 @@ int Terminal::process(std::istream &stdin_stream, std::ostream &stdout_stream, c
char buffer[131072];
for(;;) {
stdin_stream.readsome(buffer, 131072);
auto read_n=stdin_stream.gcount();
if(read_n==0)
auto read_n = stdin_stream.gcount();
if(read_n == 0)
break;
if(!process.write(buffer, read_n)) {
break;
@ -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::unique_lock<std::mutex> processes_lock(processes_mutex);
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)
async_print(std::string(bytes, n));
}, [this, quiet](const char* bytes, size_t n) {
}, [this, quiet](const char *bytes, size_t n) {
if(!quiet)
async_print(std::string(bytes, n), true);
}, true);
auto pid=process->get_id();
if (pid<=0) {
auto pid = process->get_id();
if(pid <= 0) {
processes_lock.unlock();
async_print("Error: failed to run command: " + command + "\n", true);
if(callback)
@ -101,11 +101,11 @@ void Terminal::async_process(const std::string &command, const boost::filesystem
processes_lock.unlock();
}
auto exit_status=process->get_exit_status();
auto exit_status = process->get_exit_status();
processes_lock.lock();
for(auto it=processes.begin();it!=processes.end();it++) {
if((*it)->get_id()==pid) {
for(auto it = processes.begin(); it != processes.end(); it++) {
if((*it)->get_id() == pid) {
processes.erase(it);
break;
}
@ -129,7 +129,7 @@ void Terminal::kill_last_async_process(bool force) {
void Terminal::kill_async_processes(bool force) {
std::unique_lock<std::mutex> lock(processes_mutex);
for(auto &process: processes)
for(auto &process : processes)
process->kill(force);
}
@ -168,124 +168,124 @@ std::tuple<size_t, size_t, std::string, std::string, std::string> Terminal::find
"^Assertion failed: .*file ([A-Z]:)?([^:]+), line ([0-9]+)\\.$|" //clang assert()
"^[^:]*: ([A-Z]:)?([^:]+):([0-9]+): .* Assertion .* failed\\.$|" //gcc assert()
"^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::smatch sm;
if(std::regex_match(line, sm, link_regex)) {
for(size_t sub=1;sub<link_regex.mark_count();) {
size_t subs=sub==1?4:3;
if(sm.length(sub+1)) {
start_position=sm.position(sub+1)-sm.length(sub);
end_position=sm.position(sub+subs-1)+sm.length(sub+subs-1);
for(size_t sub = 1; sub < link_regex.mark_count();) {
size_t subs = sub == 1 ? 4 : 3;
if(sm.length(sub + 1)) {
start_position = sm.position(sub + 1) - sm.length(sub);
end_position = sm.position(sub + subs - 1) + sm.length(sub + subs - 1);
if(sm.length(sub))
path+=sm[sub].str();
path+=sm[sub+1].str();
line_number=sm[sub+2].str();
line_offset=subs==4?sm[sub+3].str():"1";
path += sm[sub].str();
path += sm[sub + 1].str();
line_number = sm[sub + 2].str();
line_offset = subs == 4 ? sm[sub + 3].str() : "1";
break;
}
sub+=subs;
sub += subs;
}
}
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) {
auto iter=start_iter;
auto iter = start_iter;
Gtk::TextIter line_start;
bool line_start_set=false;
bool delimiter_found=false;
bool dot_found=false;
bool number_found=false;
bool line_start_set = false;
bool delimiter_found = false;
bool dot_found = false;
bool number_found = false;
do {
if(iter.starts_line()) {
line_start=iter;
line_start_set=true;
delimiter_found=false;
dot_found=false;
number_found=false;
}
if(line_start_set && (*iter=='\\' || *iter=='/'))
delimiter_found=true;
else if(line_start_set && *iter=='.')
dot_found=true;
else if(line_start_set && (*iter>='0' && *iter<='9'))
number_found=true;
line_start = iter;
line_start_set = true;
delimiter_found = false;
dot_found = false;
number_found = false;
}
if(line_start_set && (*iter == '\\' || *iter == '/'))
delimiter_found = true;
else if(line_start_set && *iter == '.')
dot_found = true;
else if(line_start_set && (*iter >= '0' && *iter <= '9'))
number_found = true;
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
for(size_t c=0;c<line.size();++c) {
if(line[c]>127)
for(size_t c = 0; c < line.size(); ++c) {
if(line[c] > 127)
line.replace(c, 1, "a");
}
auto link=find_link(line.raw());
if(std::get<0>(link)!=static_cast<size_t>(-1)) {
auto link_start=line_start;
auto link_end=line_start;
auto link = find_link(line.raw());
if(std::get<0>(link) != static_cast<size_t>(-1)) {
auto link_start = line_start;
auto link_end = line_start;
link_start.forward_chars(std::get<0>(link));
link_end.forward_chars(std::get<1>(link));
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
//Remove color codes
auto message_no_color=message; //copy here since operations on Glib::ustring is too slow
size_t pos=0;
while((pos=message_no_color.find('\e', pos))!=std::string::npos) {
if((pos+2)>=message_no_color.size())
auto message_no_color = message; //copy here since operations on Glib::ustring is too slow
size_t pos = 0;
while((pos = message_no_color.find('\e', pos)) != std::string::npos) {
if((pos + 2) >= message_no_color.size())
break;
if(message_no_color[pos+1]=='[') {
size_t end_pos=pos+2;
bool color_code_found=false;
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[pos + 1] == '[') {
size_t end_pos = pos + 2;
bool color_code_found = false;
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] == ';')
end_pos++;
else if(message_no_color[end_pos]=='m') {
color_code_found=true;
else if(message_no_color[end_pos] == 'm') {
color_code_found = true;
break;
}
else
break;
}
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
Glib::ustring umessage=message;
Glib::ustring umessage = message;
#endif
Glib::ustring::iterator iter;
while(!umessage.validate(iter)) {
auto next_char_iter=iter;
auto next_char_iter = 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)
get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag);
else
get_buffer()->insert(get_buffer()->end(), umessage);
auto start_iter=start_mark->get_iter();
auto start_iter = start_mark->get_iter();
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);
if(get_buffer()->get_line_count()>Config::get().terminal.history_size) {
int lines=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;
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) {
@ -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) {
dispatcher.post([this, line_nr, message] {
if(line_nr<deleted_lines)
if(line_nr < deleted_lines)
return;
Glib::ustring umessage=message;
Glib::ustring umessage = message;
Glib::ustring::iterator iter;
while(!umessage.validate(iter)) {
auto next_char_iter=iter;
auto next_char_iter = 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));
while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {}
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()) {
}
get_buffer()->insert(end_line_iter, umessage);
});
}
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));
}
else if(Config::get().source.font.size()>0) {
else if(Config::get().source.font.size() > 0) {
Pango::FontDescription font_description(Config::get().source.font);
auto font_description_size=font_description.get_size();
if(font_description_size==0) {
auto font_description_size = font_description.get_size();
if(font_description_size == 0) {
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)
font_description.set_size(font_description_size*0.95);
if(font_description_size > 0)
font_description.set_size(font_description_size * 0.95);
override_font(font_description);
}
}
@ -336,57 +337,59 @@ void Terminal::clear() {
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
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;
int location_x, location_y;
window_to_buffer_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, button_event->x, button_event->y, location_x, location_y);
get_iter_at_location(iter, location_x, location_y);
if(iter.has_tag(link_tag)) {
auto start_iter=get_buffer()->get_iter_at_line(iter.get_line());
auto end_iter=start_iter;
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)) {
boost::filesystem::path path=std::get<2>(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 ~
auto start_iter = get_buffer()->get_iter_at_line(iter.get_line());
auto end_iter = start_iter;
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)) {
boost::filesystem::path path = std::get<2>(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 ~
boost::filesystem::path corrected_path;
corrected_path=filesystem::get_home_path();
corrected_path = filesystem::get_home_path();
if(!corrected_path.empty()) {
auto it=path.begin();
auto it = path.begin();
++it;
for(;it!=path.end();++it)
corrected_path/=*it;
path=corrected_path;
for(; it != path.end(); ++it)
corrected_path /= *it;
path = corrected_path;
}
}
if(path.is_relative()) {
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))
path=absolute_path;
path = absolute_path;
else
path=Project::current->build->get_debug_path()/path;
path = Project::current->build->get_debug_path() / path;
}
else
return Gtk::TextView::on_button_press_event(button_event);
}
if(boost::filesystem::is_regular_file(path)) {
Notebook::get().open(path);
if(auto view=Notebook::get().get_current_view()) {
if(auto view = Notebook::get().get_current_view()) {
try {
int line_int = std::stoi(line)-1;
int index_int = std::stoi(index)-1;
int line_int = std::stoi(line) - 1;
int index_int = std::stoi(index) - 1;
view->place_cursor_at_line_index(line_int, index_int);
view->scroll_to_cursor_delayed(view, true, 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) {
std::unique_lock<std::mutex> lock(processes_mutex);
bool debug_is_running=false;
bool debug_is_running = false;
#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
if(processes.size()>0 || debug_is_running) {
auto unicode=gdk_keyval_to_unicode(event->keyval);
if(unicode>=32 && unicode!=126 && unicode!=0) {
if(processes.size() > 0 || debug_is_running) {
auto unicode = gdk_keyval_to_unicode(event->keyval);
if(unicode >= 32 && unicode != 126 && unicode != 0) {
get_buffer()->place_cursor(get_buffer()->end());
stdin_buffer+=unicode;
get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
stdin_buffer += unicode;
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());
if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) {
auto iter=get_buffer()->end();
if(stdin_buffer.size() > 0 && get_buffer()->get_char_count() > 0) {
auto iter = get_buffer()->end();
iter--;
stdin_buffer.erase(stdin_buffer.size()-1);
stdin_buffer.erase(stdin_buffer.size() - 1);
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());
stdin_buffer+='\n';
stdin_buffer += '\n';
if(debug_is_running) {
#ifdef JUCI_ENABLE_DEBUG
Project::current->debug_write(stdin_buffer);
@ -427,7 +430,7 @@ bool Terminal::on_key_press_event(GdkEventKey *event) {
}
else
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();
}
}

31
src/terminal.h

@ -1,45 +1,48 @@
#pragma once
#include <mutex>
#include <functional>
#include "dispatcher.h"
#include "gtkmm.h"
#include "process.hpp"
#include <boost/filesystem.hpp>
#include <functional>
#include <iostream>
#include "process.hpp"
#include "dispatcher.h"
#include <mutex>
#include <tuple>
class Terminal : public Gtk::TextView {
Terminal();
public:
static Terminal &get() {
static Terminal singleton;
return singleton;
}
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);
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_async_processes(bool force=false);
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);
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_async_processes(bool force = false);
size_t print(const std::string &message, bool bold=false);
void async_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(size_t line_nr, const std::string &message);
void configure();
void clear();
protected:
bool on_motion_notify_event (GdkEventMotion* motion_event) override;
bool on_button_press_event(GdkEventButton* button_event) override;
bool on_motion_notify_event(GdkEventMotion *motion_event) override;
bool on_button_press_event(GdkEventButton *button_event) override;
bool on_key_press_event(GdkEventKey *event) override;
private:
Dispatcher dispatcher;
Glib::RefPtr<Gtk::TextTag> bold_tag;
Glib::RefPtr<Gtk::TextTag> link_tag;
Glib::RefPtr<Gdk::Cursor> link_mouse_cursor;
Glib::RefPtr<Gdk::Cursor> default_mouse_cursor;
size_t deleted_lines=0;
size_t deleted_lines = 0;
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);

120
src/tooltips.cc

@ -1,8 +1,8 @@
#include "tooltips.h"
#include "selection_dialog.h"
std::set<Tooltip*> Tooltips::shown_tooltips;
Gdk::Rectangle Tooltips::drawn_tooltips_rectangle=Gdk::Rectangle();
std::set<Tooltip *> Tooltips::shown_tooltips;
Gdk::Rectangle Tooltips::drawn_tooltips_rectangle = Gdk::Rectangle();
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_)
@ -18,11 +18,11 @@ Tooltip::~Tooltip() {
void Tooltip::update() {
if(text_view) {
auto iter=start_mark->get_iter();
auto end_iter=end_mark->get_iter();
auto iter = start_mark->get_iter();
auto end_iter = end_mark->get_iter();
text_view->get_iter_location(iter, activation_rectangle);
if(iter.get_offset()<end_iter.get_offset()) {
while(iter.forward_char() && iter!=end_iter) {
if(iter.get_offset() < end_iter.get_offset()) {
while(iter.forward_char() && iter != end_iter) {
Gdk::Rectangle rectangle;
text_view->get_iter_location(iter, rectangle);
activation_rectangle.join(rectangle);
@ -40,17 +40,17 @@ void Tooltip::show(bool disregard_drawn, const std::function<void()> &on_motion)
if(!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 gio_application=Glib::wrap(g_application, true);
auto application=Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
auto g_application = g_application_get_default();
auto gio_application = Glib::wrap(g_application, true);
auto application = Glib::RefPtr<Gtk::Application>::cast_static(gio_application);
window->set_transient_for(*application->get_active_window());
window->set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_TOOLTIP);
window->set_events(Gdk::POINTER_MOTION_MASK);
window->property_decorated()=false;
window->property_decorated() = false;
window->set_accept_focus(false);
window->set_skip_taskbar_hint(true);
window->set_default_size(0, 0);
@ -64,43 +64,43 @@ void Tooltip::show(bool disregard_drawn, const std::function<void()> &on_motion)
window->get_style_context()->add_class("juci_tooltip_window");
auto visual = window->get_screen()->get_rgba_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");
window->add(*box);
text_buffer=create_tooltip_buffer();
text_buffer = create_tooltip_buffer();
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->set_editable(false);
#if GTK_VERSION_GE(3, 20)
box->add(*tooltip_text_view);
#else
auto box2=Gtk::manage(new Gtk::Box());
auto box2 = Gtk::manage(new Gtk::Box());
box2->pack_start(*tooltip_text_view, true, true, 3);
box->pack_start(*box2, true, true, 3);
#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->get_pixel_size(size.first, size.second);
size.first+=6; // 2xpadding
size.second+=8; // 2xpadding + 2
size.first += 6; // 2xpadding
size.second += 8; // 2xpadding + 2
window->signal_realize().connect([this] {
if(!text_view) {
auto &dialog=SelectionDialog::get();
auto &dialog = SelectionDialog::get();
if(dialog && dialog->is_visible()) {
int 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_y(root_y-size.second);
if(rectangle.get_y()<0)
rectangle.set_y(root_y - size.second);
if(rectangle.get_y() < 0)
rectangle.set_y(0);
}
}
@ -108,93 +108,93 @@ void Tooltip::show(bool disregard_drawn, const std::function<void()> &on_motion)
});
}
int root_x=0, root_y=0;
int root_x = 0, root_y = 0;
if(text_view) {
//Adjust if tooltip is left of text_view
Gdk::Rectangle visible_rect;
text_view->get_visible_rect(visible_rect);
int visible_x, visible_y;
text_view->buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, visible_rect.get_x(), visible_rect.get_y(), visible_x, visible_y);
auto activation_rectangle_x=std::max(activation_rectangle.get_x(), visible_x);
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);
root_x-=3; // -1xpadding
if(root_y<size.second)
root_x+=visible_rect.get_width()*0.1;
root_x -= 3; // -1xpadding
if(root_y < size.second)
root_x += visible_rect.get_width() * 0.1;
}
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_height(size.second);
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)) {
int new_y=Tooltips::drawn_tooltips_rectangle.get_y()-size.second;
if(new_y>=0)
int new_y = Tooltips::drawn_tooltips_rectangle.get_y() - size.second;
if(new_y >= 0)
rectangle.set_y(new_y);
else
rectangle.set_x(Tooltips::drawn_tooltips_rectangle.get_x()+Tooltips::drawn_tooltips_rectangle.get_width()+2);
rectangle.set_x(Tooltips::drawn_tooltips_rectangle.get_x() + Tooltips::drawn_tooltips_rectangle.get_width() + 2);
}
Tooltips::drawn_tooltips_rectangle.join(rectangle);
}
else
Tooltips::drawn_tooltips_rectangle=rectangle;
Tooltips::drawn_tooltips_rectangle = rectangle;
}
if(window->get_realized())
window->move(rectangle.get_x(), rectangle.get_y());
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) {
// Keep tooltip if mouse is moving towards it
// 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;
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_y=mouse_pos.second-last_mouse_pos.second;
int diff_x = mouse_pos.first - last_mouse_pos.first;
int diff_y = mouse_pos.second - last_mouse_pos.second;
class Corner {
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;
};
std::vector<Corner> corners;
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_y()+rectangle.get_height());
corners.emplace_back(rectangle.get_x()+rectangle.get_width(), rectangle.get_y()+rectangle.get_height());
for(auto &corner: corners) {
if(diff_x*corner.x + diff_y*corner.y >= 0)
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_width(), rectangle.get_y() + rectangle.get_height());
for(auto &corner : corners) {
if(diff_x * corner.x + diff_y * corner.y >= 0)
return;
}
}
Tooltips::shown_tooltips.erase(this);
if(window)
window->hide();
shown=false;
shown = false;
}
void Tooltip::wrap_lines() {
if(!text_buffer)
return;
auto iter=text_buffer->begin();
auto iter = text_buffer->begin();
while(iter) {
auto last_space=text_buffer->end();
bool end=false;
for(unsigned c=0;c<=80;c++) {
auto last_space = text_buffer->end();
bool end = false;
for(unsigned c = 0; c <= 80; c++) {
if(!iter) {
end=true;
end = true;
break;
}
if(*iter==' ')
last_space=iter;
if(*iter=='\n') {
end=true;
if(*iter == ' ')
last_space = iter;
if(*iter == '\n') {
end = true;
iter.forward_char();
break;
}
@ -203,17 +203,17 @@ void Tooltip::wrap_lines() {
if(!end) {
while(!last_space && iter) { //If no space (word longer than 80)
iter.forward_char();
if(iter && *iter==' ')
last_space=iter;
if(iter && *iter == ' ')
last_space = iter;
}
if(iter && last_space) {
auto mark=text_buffer->create_mark(last_space);
auto last_space_p=last_space;
auto mark = text_buffer->create_mark(last_space);
auto last_space_p = last_space;
last_space.forward_char();
text_buffer->erase(last_space_p, last_space);
text_buffer->insert(mark->get_iter(), "\n");
iter=mark->get_iter();
iter = mark->get_iter();
iter.forward_char();
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) {
tooltip.update();
if(rectangle.intersects(tooltip.activation_rectangle))

23
src/tooltips.h

@ -1,9 +1,9 @@
#pragma once
#include "gtkmm.h"
#include <string>
#include <list>
#include <functional>
#include <list>
#include <set>
#include <string>
class Tooltip {
public:
@ -12,7 +12,7 @@ public:
~Tooltip();
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});
Gdk::Rectangle activation_rectangle;
@ -20,6 +20,7 @@ public:
Glib::RefPtr<Gtk::TextBuffer::Mark> end_mark;
Glib::RefPtr<Gtk::TextBuffer> text_buffer;
private:
std::unique_ptr<Gtk::Window> window;
void wrap_lines();
@ -29,22 +30,22 @@ private:
std::pair<int, int> size;
Gdk::Rectangle rectangle;
bool shown=false;
bool shown = false;
};
class Tooltips {
public:
static std::set<Tooltip*> shown_tooltips;
static std::set<Tooltip *> shown_tooltips;
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(bool disregard_drawn=false);
void show(const Gdk::Rectangle &rectangle, 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 clear() {tooltip_list.clear();};
void clear() { tooltip_list.clear(); };
template<typename... Ts>
void emplace_back(Ts&&... params) {
template <typename... Ts>
void emplace_back(Ts &&... params) {
tooltip_list.emplace_back(std::forward<Ts>(params)...);
}

16
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,
@ -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 pair = parse_paths(spelling, 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());
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());
auto pair2 = find_potential_paths(all_cursors_paths, project_path, pair.first, pair.second);
auto &potential_paths = pair2.first;
@ -321,8 +320,7 @@ void Usages::Clang::cache(const boost::filesystem::path &project_path, const boo
visitor_data->paths.emplace(path);
return CXChildVisit_Continue;
},
&visitor_data);
}, &visitor_data);
visitor_data.paths.erase(path);
@ -632,8 +630,8 @@ std::pair<Usages::Clang::PathSet, Usages::Clang::PathSet> Usages::Clang::find_po
PathSet potential_paths;
PathSet all_includes;
bool first=true;
for(auto &path: paths) {
bool first = true;
for(auto &path : paths) {
if(filesystem::file_in_path(path, project_path)) {
for(auto &path_with_spelling : paths_with_spelling) {
auto path_all_includes = get_all_includes(path_with_spelling, paths_includes);
@ -654,7 +652,7 @@ std::pair<Usages::Clang::PathSet, Usages::Clang::PathSet> Usages::Clang::find_po
for(auto &include : path_all_includes)
all_includes.emplace(include);
}
first=false;
first = false;
}
}
}

976
src/window.cc

File diff suppressed because it is too large Load Diff

9
src/window.h

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

78
tests/cmake_build_test.cc

@ -1,74 +1,74 @@
#include <glib.h>
#include "cmake.h"
#include "project_build.h"
#include "config.h"
#include <boost/filesystem.hpp>
#include "process.hpp"
#include "project_build.h"
#include <boost/filesystem.hpp>
#include <glib.h>
#include <iostream>
using namespace std;
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);
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);
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(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", project_path) == "");
g_assert(cmake.get_executable(project_path / "build" / "non_existing_file.cc", project_path) == "");
}
{
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"/"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"/"non_existing_file.cc")==project_path/"build"/"src"/"juci");
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" / "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" / "non_existing_file.cc") == project_path / "build" / "src" / "juci");
}
{
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");
g_assert(functions_parameters.at(0).second.at(0)=="juci");
auto functions_parameters = cmake.get_functions_parameters("project");
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/"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).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 / "non_existing_file.cc").parent_path() == project_path / "build" / "tests");
}
auto build=Project::Build::create(tests_path);
g_assert(dynamic_cast<Project::CMakeBuild*>(build.get()));
auto build = Project::Build::create(tests_path);
g_assert(dynamic_cast<Project::CMakeBuild *>(build.get()));
build=Project::Build::create(tests_path/"stubs");
g_assert(dynamic_cast<Project::CMakeBuild*>(build.get()));
g_assert(build->project_path==project_path);
build = Project::Build::create(tests_path / "stubs");
g_assert(dynamic_cast<Project::CMakeBuild *>(build.get()));
g_assert(build->project_path == project_path);
Config::get().project.default_build_path="./build";
g_assert(build->get_default_path()==project_path/"build");
Config::get().project.default_build_path = "./build";
g_assert(build->get_default_path() == project_path / "build");
Config::get().project.debug_build_path="<default_build_path>/debug";
g_assert(build->get_debug_path()==project_path/"build/debug");
Config::get().project.debug_build_path = "<default_build_path>/debug";
g_assert(build->get_debug_path() == project_path / "build/debug");
auto project_path_filename=project_path.filename();
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()));
auto project_path_filename = project_path.filename();
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()));
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()));
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()));
}
{
auto project_path=tests_path/"source_clang_test_files";
auto project_path = tests_path / "source_clang_test_files";
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/"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 / "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) == boost::filesystem::path(".") / "test");
}
}

14
tests/compile_commands_test.cc

@ -2,12 +2,12 @@
#include <glib.h>
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);
@ -17,17 +17,17 @@ int main() {
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");
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_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);

60
tests/filesystem_test.cc

@ -3,80 +3,80 @@
int main() {
{
auto home_path=filesystem::get_home_path();
auto home_path = filesystem::get_home_path();
g_assert(!home_path.empty());
}
{
auto original="test () '\"";
auto escaped=filesystem::escape_argument(original);
auto original = "test () '\"";
auto escaped = filesystem::escape_argument(original);
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);
}
{
auto unescaped=filesystem::unescape_argument("'test \\()\"\\''");
auto unescaped = filesystem::unescape_argument("'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 \\'()\"");
}
{
auto unescaped=filesystem::unescape_argument("\\\\");
auto unescaped = filesystem::unescape_argument("\\\\");
g_assert_cmpstr(unescaped.c_str(), ==, "\\");
}
{
auto unescaped=filesystem::unescape_argument("\\\\\\ ");
auto unescaped = filesystem::unescape_argument("\\\\\\ ");
g_assert_cmpstr(unescaped.c_str(), ==, "\\ ");
}
{
auto unescaped=filesystem::unescape_argument("\\\\\\ \\ \\ \\\\");
auto unescaped = filesystem::unescape_argument("\\\\\\ \\ \\ \\\\");
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");
}
{
auto unescaped=filesystem::unescape_argument("\"\\\\\\\"\"");
auto unescaped = filesystem::unescape_argument("\"\\\\\\\"\"");
g_assert_cmpstr(unescaped.c_str(), ==, "\\\"");
}
{
auto unescaped=filesystem::unescape_argument("\"\\\"\"");
auto unescaped = filesystem::unescape_argument("\"\\\"\"");
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");
}
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(boost::filesystem::canonical(tests_path/".."/"CMakeLists.txt"), 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));
}
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("/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")=="../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(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/./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");
}
{
boost::filesystem::path relative_path="filesystem_test.cc";
g_assert(filesystem::get_relative_path(tests_path/relative_path, tests_path)==relative_path);
boost::filesystem::path relative_path = "filesystem_test.cc";
g_assert(filesystem::get_relative_path(tests_path / relative_path, tests_path) == relative_path);
}
}

54
tests/git_test.cc

@ -1,25 +1,25 @@
#include <glib.h>
#include <gtkmm.h>
#include "git.h"
#include <boost/filesystem.hpp>
#include <glib.h>
#include <gtkmm.h>
int main() {
auto app=Gtk::Application::create();
auto app = Gtk::Application::create();
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH);
auto jucipp_path=tests_path.parent_path();
auto git_path=jucipp_path/".git";
auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
auto jucipp_path = tests_path.parent_path();
auto git_path = jucipp_path / ".git";
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_work_path()==jucipp_path);
g_assert(repository->get_path() == git_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 lines=diff.get_lines("#include added\n#include <glib.h>\n#include modified\n#include \"git.h\"\n");
auto diff = repository->get_diff((boost::filesystem::path("tests") / "git_test.cc"));
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.modified.size(), ==, 1);
g_assert_cmpuint(lines.removed.size(), ==, 1);
@ -30,7 +30,7 @@ int main() {
}
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) {
std::cerr << e.what() << std::endl;
@ -40,19 +40,19 @@ int main() {
{
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");
auto hunks=Git::Repository::Diff::get_hunks(old_text, new_text);
assert(hunks.size()==3);
assert(hunks[0].old_lines.first==1);
assert(hunks[0].old_lines.second==1);
assert(hunks[0].new_lines.first==0);
assert(hunks[0].new_lines.second==0);
assert(hunks[1].old_lines.first==4);
assert(hunks[1].old_lines.second==1);
assert(hunks[1].new_lines.first==3);
assert(hunks[1].new_lines.second==2);
assert(hunks[2].old_lines.first==5);
assert(hunks[2].old_lines.second==0);
assert(hunks[2].new_lines.first==6);
assert(hunks[2].new_lines.second==2);
auto hunks = Git::Repository::Diff::get_hunks(old_text, new_text);
assert(hunks.size() == 3);
assert(hunks[0].old_lines.first == 1);
assert(hunks[0].old_lines.second == 1);
assert(hunks[0].new_lines.first == 0);
assert(hunks[0].new_lines.second == 0);
assert(hunks[1].old_lines.first == 4);
assert(hunks[1].old_lines.second == 1);
assert(hunks[1].new_lines.first == 3);
assert(hunks[1].new_lines.second == 2);
assert(hunks[2].old_lines.first == 5);
assert(hunks[2].old_lines.second == 0);
assert(hunks[2].new_lines.first == 6);
assert(hunks[2].new_lines.second == 2);
}
}

126
tests/lldb_test.cc

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

2
tests/lldb_test_files/main.cpp

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

34
tests/meson_build_test.cc

@ -1,34 +1,34 @@
#include "meson.h"
#include <glib.h>
#include "project.h"
#include <glib.h>
int main() {
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH);
auto meson_test_files_path=boost::filesystem::canonical(tests_path/"meson_test_files");
auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
auto meson_test_files_path = boost::filesystem::canonical(tests_path / "meson_test_files");
{
Meson meson(meson_test_files_path/"a_subdir");
g_assert(meson.project_path==meson_test_files_path);
Meson meson(meson_test_files_path / "a_subdir");
g_assert(meson.project_path == 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/"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 / "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 / "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)==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/"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") == 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);
g_assert(dynamic_cast<Project::MesonBuild*>(build.get()));
auto build = Project::Build::create(meson_test_files_path);
g_assert(dynamic_cast<Project::MesonBuild *>(build.get()));
build=Project::Build::create(meson_test_files_path/"a_subdir");
g_assert(dynamic_cast<Project::MesonBuild*>(build.get()));
build = Project::Build::create(meson_test_files_path / "a_subdir");
g_assert(dynamic_cast<Project::MesonBuild *>(build.get()));
}

32
tests/process_test.cc

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

114
tests/source_clang_test.cc

@ -1,9 +1,9 @@
#include <glib.h>
#include "source_clang.h"
#include "config.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;
number=3
}
@ -20,15 +20,15 @@ void flush_events() {
}
int main() {
auto app=Gtk::Application::create();
auto app = Gtk::Application::create();
Gsv::init();
#ifdef _WIN32
g_assert_cmpstr(std::getenv("MSYSTEM_PREFIX"), !=, nullptr);
#endif
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"),
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"),
Gsv::LanguageManager::get_default()->get_language("cpp"));
while(!clang_view->parsed)
flush_events();
@ -36,38 +36,38 @@ int main() {
//test get_declaration and get_implementation
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);
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[0].line, ==, 11);
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);
//test get_usages and get_methods
auto locations=clang_view->get_usages();
auto locations = clang_view->get_usages();
g_assert_cmpuint(locations.size(), >, 0);
locations=clang_view->get_methods();
locations = clang_view->get_methods();
g_assert_cmpuint(locations.size(), >, 0);
//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);
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");
location=clang_view->get_declaration_location();
location = clang_view->get_declaration_location();
g_assert_cmpuint(location.line, ==, 0);
clang_view->rename_similar_tokens("RenamedTestClass");
while(!clang_view->parsed)
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();
token=clang_view->get_token(iter);
token = clang_view->get_token(iter);
g_assert_cmpstr(token.c_str(), ==, "RenamedTestClass");
g_assert_cmpuint(clang_view->clang_diagnostics.size(), ==, 0);
clang_view->get_buffer()->set_text(saved_main);
@ -82,60 +82,60 @@ int main() {
// test remove_include_guard
{
clang_view->language=Gsv::LanguageManager::get_default()->get_language("chdr");
std::string source="#ifndef F\n#define F\n#endif // F";
clang_view->language = Gsv::LanguageManager::get_default()->get_language("chdr");
std::string source = "#ifndef F\n#define F\n#endif // F";
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);
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);
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);
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);
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);
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);
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);
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";
auto old_source=source;
source = "#ifndef F\ntest\n#define F\n#endif // F\n";
auto old_source = 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";
old_source=source;
source = "test\n#ifndef F\n#define F\n#endif // F\n";
old_source = 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";
old_source=source;
source = "#ifndef F\n#define F\n#endif // F\ntest\n";
old_source = 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
{
clang_view->get_buffer()->set_text(R"(#include <string>
#include <vector>
#include <map>
#include <vector>
namespace N {
class T {
@ -162,46 +162,46 @@ namespace N {
flush_events();
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));
auto method=clang_view->get_method();
auto method = clang_view->get_method();
g_assert_cmpstr(method.c_str(), ==, "void N::T::f1() {}");
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) {}");
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) {}");
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) {}");
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) {}");
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() {}");
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() {}");
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() {}");
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() {}");
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() {}");
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() {}");
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() {}");
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 {}");
}

4
tests/source_clang_test_files/main.cpp

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

146
tests/source_test.cc

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

2
tests/stubs/dialogs.cc

@ -1,6 +1,6 @@
#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) {
return true;

20
tests/stubs/selection_dialog.cc

@ -2,29 +2,29 @@
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):
text_view(text_view), list_view_text(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) {}
void SelectionDialogBase::show() {}
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;
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)
: SelectionDialogBase(text_view, start_mark, show_search_entry, use_markup) {}
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;
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) {}
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"
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,
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() {}
void Tooltips::show(Gdk::Rectangle const&, bool) {}
void Tooltips::show(Gdk::Rectangle const &, bool) {}
void Tooltips::show(bool) {}

42
tests/terminal_test.cc

@ -10,32 +10,32 @@ int main() {
auto app = Gtk::Application::create();
{
auto link=Terminal::get().find_link("~/test/test.cc:7:41: error: expected ';' after expression.");
assert(std::get<0>(link)==0);
assert(std::get<1>(link)==19);
assert(std::get<2>(link)=="~/test/test.cc");
assert(std::get<3>(link)=="7");
assert(std::get<4>(link)=="41");
auto link = Terminal::get().find_link("~/test/test.cc:7:41: error: expected ';' after expression.");
assert(std::get<0>(link) == 0);
assert(std::get<1>(link) == 19);
assert(std::get<2>(link) == "~/test/test.cc");
assert(std::get<3>(link) == "7");
assert(std::get<4>(link) == "41");
}
{
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<1>(link)==70);
assert(std::get<2>(link)=="~/test/test.cc");
assert(std::get<3>(link)=="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<1>(link) == 70);
assert(std::get<2>(link) == "~/test/test.cc");
assert(std::get<3>(link) == "15");
}
{
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<1>(link)==28);
assert(std::get<2>(link)=="~/examples/main.cpp");
assert(std::get<3>(link)=="17");
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<1>(link) == 28);
assert(std::get<2>(link) == "~/examples/main.cpp");
assert(std::get<3>(link) == "17");
}
{
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<1>(link)==23);
assert(std::get<2>(link)=="~/test/test.cc");
assert(std::get<3>(link)=="36");
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<1>(link) == 23);
assert(std::get<2>(link) == "~/test/test.cc");
assert(std::get<3>(link) == "36");
}
}

24
tests/usages_clang_test.cc

@ -43,7 +43,7 @@ int main() {
assert(usages.size() == 1);
assert(usages[0].path == path);
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[0].first.line == 6);
assert(usages[0].offsets[0].first.index == 8);
@ -54,7 +54,7 @@ int main() {
assert(usages.size() == 2);
assert(usages[1].path == project_path / "test.hpp");
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].offsets.size() == 2);
assert(usages[1].offsets[0].first.line == 6);
@ -193,7 +193,7 @@ int main() {
assert(usages.size() == 1);
assert(usages[0].path == cache_it->first);
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[0].first.line == 6);
assert(usages[0].offsets[0].first.index == 8);
@ -217,7 +217,7 @@ int main() {
assert(usages.size() == 1);
assert(usages[0].path == cache_it->first);
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].offsets.size() == 2);
assert(usages[0].offsets[0].first.line == 6);
@ -258,16 +258,16 @@ int main() {
}
{
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/"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/"test2.hpp.usages"));
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 / "test.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);
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/"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/"test2.hpp.usages"));
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 / "test.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 <iostream>
int main() {
Test test;
test.a=2;
test.a = 2;
test.b();
test.c();
}

2
tests/usages_clang_test_files/test.hpp

@ -3,7 +3,7 @@ public:
Test() {}
~Test() {}
int a=0;
int a = 0;
void b() {
++a;
}

Loading…
Cancel
Save