Compare commits

...

16 Commits

  1. 2812
      src/documentation.cpp
  2. 4
      src/notebook.cpp
  3. 36
      src/project.cpp
  4. 25
      src/project.hpp
  5. 9
      src/project_build.cpp
  6. 3
      src/project_build.hpp
  7. 82
      src/source.cpp
  8. 13
      src/source.hpp
  9. 94
      src/source_base.cpp
  10. 18
      src/source_base.hpp
  11. 105
      src/source_language_protocol.cpp
  12. 2
      src/source_language_protocol.hpp
  13. 179
      src/terminal.cpp
  14. 4
      src/terminal.hpp
  15. 51
      src/tooltips.cpp
  16. 1
      src/tooltips.hpp
  17. 18
      src/window.cpp
  18. 78
      tests/terminal_test.cpp
  19. 33
      tests/tooltips_test.cpp

2812
src/documentation.cpp

File diff suppressed because it is too large Load Diff

4
src/notebook.cpp

@ -254,7 +254,7 @@ bool Notebook::open(const boost::filesystem::path &file_path_, Position position
});
boost::optional<int> exit_status;
std::string command = "rustup component add rust-analyzer";
Terminal::get().print("\e[2mRunning: " + command + "\e[m\n");
Terminal::get().print("\e[37mRunning: " + command + "\e[m\n");
auto process = Terminal::get().async_process(command, "", [&exit_status](int exit_status_) {
exit_status = exit_status_;
});
@ -627,7 +627,7 @@ void Notebook::install_rust() {
});
boost::optional<int> exit_status;
std::string command = "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y";
Terminal::get().print("\e[2mRunning: " + command + "\e[m\n");
Terminal::get().print("\e[37mRunning: " + command + "\e[m\n");
auto process = Terminal::get().async_process(command, "", [&exit_status](int exit_status_) {
exit_status = exit_status_;
});

36
src/project.cpp

@ -397,7 +397,7 @@ void Project::LLDB::debug_compile_and_start() {
if(Config::get().terminal.clear_on_compile)
Terminal::get().clear();
Terminal::get().print("\e[2mCompiling and debugging: " + run_arguments + "\e[m\n");
Terminal::get().print("\e[37mCompiling and debugging: " + run_arguments + "\e[m\n");
Terminal::get().async_process(build->get_compile_command(), debug_build_path, [self = shared_from_this(), debug_build_path, run_arguments, project_path = build->project_path, remote_host](int exit_status) {
if(exit_status != 0) {
debugging = false;
@ -428,7 +428,7 @@ void Project::LLDB::debug_start(const std::string &command, const boost::filesys
Debug::LLDB::get().on_exit.erase(on_exit_it);
Debug::LLDB::get().on_exit.emplace_back([self = shared_from_this(), command](int exit_status) {
debugging = false;
Terminal::get().async_print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().async_print("\e[37m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
self->dispatcher.post([] {
debug_update_status("");
});
@ -728,7 +728,7 @@ void Project::Clang::compile() {
if(Config::get().terminal.clear_on_compile)
Terminal::get().clear();
Terminal::get().print("\e[2mCompiling project: " + filesystem::get_short_path(build->project_path).string() + "\e[m\n");
Terminal::get().print("\e[37mCompiling project: " + filesystem::get_short_path(build->project_path).string() + "\e[m\n");
Terminal::get().async_process(build->get_compile_command(), default_build_path, [self = shared_from_this(), default_build_path](int exit_status) {
compiling = false;
if(exit_status != 0 && !self->build->is_valid(default_build_path))
@ -773,12 +773,12 @@ void Project::Clang::compile_and_run(const boost::filesystem::path &file_path) {
if(Config::get().terminal.clear_on_compile)
Terminal::get().clear();
Terminal::get().print("\e[2mCompiling and running: " + arguments + "\e[m\n");
Terminal::get().print("\e[37mCompiling and running: " + arguments + "\e[m\n");
Terminal::get().async_process(build->get_compile_command(), default_build_path, [self = shared_from_this(), arguments, project_path, default_build_path](int exit_status) {
compiling = false;
if(exit_status == 0) {
Terminal::get().async_process(arguments, project_path, [arguments](int exit_status) {
Terminal::get().print("\e[2m" + arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}
else if(!self->build->is_valid(default_build_path))
@ -896,9 +896,9 @@ void Project::Python::compile_and_run(const boost::filesystem::path &file_path)
if(Config::get().terminal.clear_on_compile)
Terminal::get().clear();
Terminal::get().print("\e[2mRunning: " + command + "\e[m\n");
Terminal::get().print("\e[37mRunning: " + command + "\e[m\n");
Terminal::get().async_process(command, path, [command](int exit_status) {
Terminal::get().print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}
@ -923,9 +923,9 @@ void Project::JavaScript::compile_and_run(const boost::filesystem::path &file_pa
if(Config::get().terminal.clear_on_compile)
Terminal::get().clear();
Terminal::get().print("\e[2mRunning: " + command + "\e[m\n");
Terminal::get().print("\e[37mRunning: " + command + "\e[m\n");
Terminal::get().async_process(command, path, [command](int exit_status) {
Terminal::get().print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}
@ -936,9 +936,9 @@ void Project::HTML::compile_and_run(const boost::filesystem::path &file_path) {
if(Config::get().terminal.clear_on_compile)
Terminal::get().clear();
Terminal::get().print("\e[2mRunning: " + command + "\e[m\n");
Terminal::get().print("\e[37mRunning: " + command + "\e[m\n");
Terminal::get().async_process(command, build->project_path, [command](int exit_status) {
Terminal::get().print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}
else if(!file_path.empty())
@ -966,7 +966,7 @@ void Project::Rust::compile() {
if(Config::get().terminal.clear_on_compile)
Terminal::get().clear();
Terminal::get().print("\e[2mCompiling project: " + filesystem::get_short_path(build->project_path).string() + "\e[m\n");
Terminal::get().print("\e[37mCompiling project: " + filesystem::get_short_path(build->project_path).string() + "\e[m\n");
Terminal::get().async_process(build->get_compile_command(), build->project_path, [](int exit_status) {
compiling = false;
@ -980,14 +980,14 @@ void Project::Rust::compile_and_run(const boost::filesystem::path &file_path) {
Terminal::get().clear();
auto arguments = get_run_arguments().second;
Terminal::get().print("\e[2mCompiling and running: " + arguments + "\e[m\n");
Terminal::get().print("\e[37mCompiling and running: " + arguments + "\e[m\n");
auto self = this->shared_from_this();
Terminal::get().async_process(build->get_compile_command(), build->project_path, [self, arguments = std::move(arguments)](int exit_status) {
compiling = false;
if(exit_status == 0) {
Terminal::get().async_process(arguments, self->build->project_path, [arguments](int exit_status) {
Terminal::get().print("\e[2m" + arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + arguments + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}
});
@ -1014,9 +1014,9 @@ void Project::Go::compile_and_run(const boost::filesystem::path &file_path) {
if(Config::get().terminal.clear_on_compile)
Terminal::get().clear();
Terminal::get().print("\e[2mRunning: " + command + "\e[m\n");
Terminal::get().print("\e[37mRunning: " + command + "\e[m\n");
Terminal::get().async_process(command, path, [command](int exit_status) {
Terminal::get().print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}
@ -1037,8 +1037,8 @@ void Project::Julia::compile_and_run(const boost::filesystem::path &file_path) {
if(Config::get().terminal.clear_on_compile)
Terminal::get().clear();
Terminal::get().print("\e[2mRunning: " + command + "\e[m\n");
Terminal::get().print("\e[37mRunning: " + command + "\e[m\n");
Terminal::get().async_process(command, path, [command](int exit_status) {
Terminal::get().print("\e[2m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + command + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}

25
src/project.hpp

@ -104,11 +104,6 @@ namespace Project {
#endif
};
class LanguageProtocol : public virtual Base {
public:
virtual std::string get_language_id() = 0;
};
class Clang : public LLDB {
public:
Clang(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
@ -126,22 +121,18 @@ namespace Project {
void compile_and_run(const boost::filesystem::path &file_path = {}) override;
};
class Python : public LanguageProtocol {
class Python : public Base {
public:
Python(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run(const boost::filesystem::path &file_path = {}) override;
std::string get_language_id() override { return "python"; }
};
class JavaScript : public LanguageProtocol {
class JavaScript : public Base {
public:
JavaScript(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run(const boost::filesystem::path &file_path = {}) override;
std::string get_language_id() override { return "javascript"; }
};
class HTML : public Base {
@ -151,33 +142,27 @@ namespace Project {
void compile_and_run(const boost::filesystem::path &file_path = {}) override;
};
class Rust : public LLDB, public LanguageProtocol {
class Rust : public LLDB {
public:
Rust(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
std::pair<std::string, std::string> get_run_arguments() override;
void compile() override;
void compile_and_run(const boost::filesystem::path &file_path = {}) override;
std::string get_language_id() override { return "rust"; }
};
class Go : public LanguageProtocol {
class Go : public Base {
public:
Go(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run(const boost::filesystem::path &file_path = {}) override;
std::string get_language_id() override { return "go"; }
};
class Julia : public LanguageProtocol {
class Julia : public Base {
public:
Julia(std::unique_ptr<Build> &&build) : Base(std::move(build)) {}
void compile_and_run(const boost::filesystem::path &file_path = {}) override;
std::string get_language_id() override { return "julia"; }
};
std::shared_ptr<Base> create(const boost::filesystem::path &file_path = {});

9
src/project_build.cpp

@ -63,6 +63,12 @@ std::unique_ptr<Project::Build> Project::Build::create(const boost::filesystem::
return build;
}
if(boost::filesystem::exists(search_path / ".git", ec)) {
std::unique_ptr<Project::Build> build(new GitBuild());
build->project_path = search_path;
return build;
}
if(search_path == search_path.root_directory())
break;
search_path = search_path.parent_path();
@ -118,8 +124,7 @@ std::vector<std::string> Project::Build::get_exclude_folders() {
boost::filesystem::path(default_build_path).filename().string(), boost::filesystem::path(debug_build_path).filename().string(), // C/C++
"target", // Rust
"node_modules", "dist", "coverage", ".expo", // JavaScript
".mypy_cache",
"__pycache__" // Python
".mypy_cache", "__pycache__", ".venv" // Python
};
}

3
src/project_build.hpp

@ -85,4 +85,7 @@ namespace Project {
class GoBuild : public Build {
};
class GitBuild : public Build {
};
} // namespace Project

82
src/source.cpp

@ -36,84 +36,6 @@ inline pid_t get_current_process_id() {
std::unique_ptr<TinyProcessLib::Process> Source::View::prettier_background_process = {};
Glib::RefPtr<Gsv::LanguageManager> Source::LanguageManager::get_default() {
static auto instance = Gsv::LanguageManager::create();
return instance;
}
Glib::RefPtr<Gsv::StyleSchemeManager> Source::StyleSchemeManager::get_default() {
static auto instance = Gsv::StyleSchemeManager::create();
static bool first = true;
if(first) {
instance->prepend_search_path((Config::get().home_juci_path / "styles").string());
first = false;
}
return instance;
}
Glib::RefPtr<Gsv::Language> Source::guess_language(const boost::filesystem::path &file_path) {
auto language_manager = LanguageManager::get_default();
bool result_uncertain = false;
auto filename = file_path.filename().string();
auto content_type = Gio::content_type_guess(filename, nullptr, 0, result_uncertain);
if(result_uncertain)
content_type.clear();
auto language = language_manager->guess_language(filename, content_type);
auto extension = file_path.extension().string();
if(!language) {
if(filename == "CMakeLists.txt")
language = language_manager->get_language("cmake");
else if(filename == "meson.build")
language = language_manager->get_language("meson");
else if(filename == "Makefile")
language = language_manager->get_language("makefile");
else if(filename == "Jenkinsfile")
language = language_manager->get_language("groovy");
else if(extension == ".tcc")
language = language_manager->get_language("cpphdr");
else if(extension == ".ts" || extension == ".tsx" || extension == ".jsx" || extension == ".flow")
language = language_manager->get_language("js");
else if(extension == ".svelte")
language = language_manager->get_language("html");
else if(extension == ".vert" || // listed on https://github.com/KhronosGroup/glslang
extension == ".frag" ||
extension == ".tesc" ||
extension == ".tese" ||
extension == ".geom" ||
extension == ".comp")
language = language_manager->get_language("glsl");
else if(extension == ".in" || extension == ".bak")
return guess_language(boost::filesystem::path(file_path).replace_extension());
else if(extension == ".mm")
language = language_manager->get_language("objc");
else if(!file_path.has_extension() && std::any_of(file_path.begin(), file_path.end(),
[](const boost::filesystem::path &path) { return path == "include"; }))
language = language_manager->get_language("cpphdr");
else {
std::ifstream input(file_path.string(), std::ios::binary);
std::string tag(5, '\0');
if(input && input.read(&tag[0], static_cast<std::streamsize>(tag.size())) && tag == "<?xml")
language = language_manager->get_language("xml");
}
}
else if(language->get_id() == "cuda") {
if(extension == ".cuh")
language = language_manager->get_language("cpphdr");
else
language = language_manager->get_language("cpp");
}
else if(language->get_id() == "opencl")
language = language_manager->get_language("cpp");
else if(language->get_id() == "octave" && extension == ".m") {
// .m is used for both Octave and Objective-C, so try to differentiate
std::ifstream input(file_path.string(), std::ios::binary);
std::string line;
if(input && std::getline(input, line) &&
(starts_with(line, "#import ") || starts_with(line, "@interface ") || starts_with(line, "/*") || starts_with(line, "//")))
language = language_manager->get_language("objc");
}
return language;
}
Source::FixIt::FixIt(std::string source_, std::string path_, std::pair<Offset, Offset> offsets_) : source(std::move(source_)), path(std::move(path_)), offsets(std::move(offsets_)) {
if(this->source.size() == 0)
type = Type::erase;
@ -157,7 +79,7 @@ Source::View::View(const boost::filesystem::path &file_path, const Glib::RefPtr<
views.emplace(this);
similar_symbol_tag = get_buffer()->create_tag();
similar_symbol_tag->property_weight() = Pango::WEIGHT_ULTRAHEAVY;
similar_symbol_tag->property_weight() = Pango::WEIGHT_BOLD;
similar_symbol_tag->property_background_rgba() = Gdk::RGBA("rgba(255, 255, 255, 0.075)");
clickable_tag = get_buffer()->create_tag();
clickable_tag->property_underline() = Pango::Underline::UNDERLINE_SINGLE;
@ -586,7 +508,7 @@ void Source::View::configure() {
if(style && style->property_background_set())
extra_cursor_selection->property_background() = style->property_background().get_value();
else
extra_cursor_selection->property_background_rgba() = get_style_context()->get_background_color(Gtk::StateFlags::STATE_FLAG_SELECTED);
extra_cursor_selection->property_background_rgba() = StyleContext::get_background_color(get_style_context(), Gtk::StateFlags::STATE_FLAG_SELECTED);
if(Config::get().menu.keys["source_show_completion"].empty()) {
get_completion()->unblock_interactive();

13
src/source.hpp

@ -13,19 +13,6 @@
#include <vector>
namespace Source {
/// Workaround for buggy Gsv::LanguageManager::get_default()
class LanguageManager {
public:
static Glib::RefPtr<Gsv::LanguageManager> get_default();
};
/// Workaround for buggy Gsv::StyleSchemeManager::get_default()
class StyleSchemeManager {
public:
static Glib::RefPtr<Gsv::StyleSchemeManager> get_default();
};
Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path);
class Offset {
public:
Offset() = default;

94
src/source_base.cpp

@ -16,6 +16,92 @@ GdkModifierType Source::CommonView::primary_modifier_mask = GDK_MOD2_MASK;
GdkModifierType Source::CommonView::primary_modifier_mask = GDK_CONTROL_MASK;
#endif
Gdk::RGBA StyleContext::get_background_color(const Glib::RefPtr<Gtk::StyleContext> &style_context, Gtk::StateFlags state) {
GdkRGBA *background_color = nullptr;
gtk_style_context_get(style_context->gobj(), static_cast<GtkStateFlags>(state), "background-color", &background_color, nullptr);
if(background_color != nullptr)
return Glib::wrap(background_color);
return Gdk::RGBA();
}
Glib::RefPtr<Gsv::LanguageManager> Source::LanguageManager::get_default() {
static auto instance = Gsv::LanguageManager::create();
return instance;
}
Glib::RefPtr<Gsv::StyleSchemeManager> Source::StyleSchemeManager::get_default() {
static auto instance = Gsv::StyleSchemeManager::create();
static bool first = true;
if(first) {
instance->prepend_search_path((Config::get().home_juci_path / "styles").string());
first = false;
}
return instance;
}
Glib::RefPtr<Gsv::Language> Source::guess_language(const boost::filesystem::path &file_path) {
auto language_manager = LanguageManager::get_default();
bool result_uncertain = false;
auto filename = file_path.filename().string();
auto content_type = Gio::content_type_guess(filename, nullptr, 0, result_uncertain);
if(result_uncertain)
content_type.clear();
auto language = language_manager->guess_language(filename, content_type);
auto extension = file_path.extension().string();
if(!language) {
if(filename == "CMakeLists.txt")
language = language_manager->get_language("cmake");
else if(filename == "meson.build")
language = language_manager->get_language("meson");
else if(filename == "Makefile")
language = language_manager->get_language("makefile");
else if(filename == "Jenkinsfile")
language = language_manager->get_language("groovy");
else if(extension == ".tcc")
language = language_manager->get_language("cpphdr");
else if(extension == ".ts" || extension == ".tsx" || extension == ".jsx" || extension == ".flow")
language = language_manager->get_language("js");
else if(extension == ".svelte")
language = language_manager->get_language("html");
else if(extension == ".vert" || // listed on https://github.com/KhronosGroup/glslang
extension == ".frag" ||
extension == ".tesc" ||
extension == ".tese" ||
extension == ".geom" ||
extension == ".comp")
language = language_manager->get_language("glsl");
else if(extension == ".in" || extension == ".bak")
return guess_language(boost::filesystem::path(file_path).replace_extension());
else if(extension == ".mm")
language = language_manager->get_language("objc");
else if(!file_path.has_extension() && std::any_of(file_path.begin(), file_path.end(),
[](const boost::filesystem::path &path) { return path == "include"; }))
language = language_manager->get_language("cpphdr");
else {
std::ifstream input(file_path.string(), std::ios::binary);
std::string tag(5, '\0');
if(input && input.read(&tag[0], static_cast<std::streamsize>(tag.size())) && tag == "<?xml")
language = language_manager->get_language("xml");
}
}
else if(language->get_id() == "cuda") {
if(extension == ".cuh")
language = language_manager->get_language("cpphdr");
else
language = language_manager->get_language("cpp");
}
else if(language->get_id() == "opencl")
language = language_manager->get_language("cpp");
else if(language->get_id() == "octave" && extension == ".m") {
// .m is used for both Octave and Objective-C, so try to differentiate
std::ifstream input(file_path.string(), std::ios::binary);
std::string line;
if(input && std::getline(input, line) &&
(starts_with(line, "#import ") || starts_with(line, "@interface ") || starts_with(line, "/*") || starts_with(line, "//")))
language = language_manager->get_language("objc");
}
return language;
}
Source::CommonView::CommonView(const Glib::RefPtr<Gsv::Language> &language) : Gsv::View() {
set_tab_width(4); // Visual size of a \t hardcoded to be equal to visual size of 4 spaces
@ -896,6 +982,7 @@ bool Source::BaseView::is_token_char(gunichar chr) {
return (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z') || (chr >= '0' && chr <= '9') || chr == '_' || (!is_latex && chr == '$') || chr >= 128 ||
(language_id == "css" && chr == '-') ||
(language_id == "rust" && chr == '!') ||
(language_id == "lean" && (chr == '#' || chr == '\\')) ||
(is_latex && chr == '\\');
}
@ -999,6 +1086,13 @@ void Source::BaseView::paste() {
}
}
if(language_id == "lean") {
Gtk::Clipboard::get()->set_text(text);
get_buffer()->paste_clipboard(Gtk::Clipboard::get());
scroll_to_cursor_delayed(false, false);
return;
}
// Exception for when pasted text is only whitespaces
bool only_whitespaces = true;
for(auto &chr : text) {

18
src/source_base.hpp

@ -10,7 +10,25 @@
#include <set>
#include <vector>
namespace StyleContext {
/// Replacement of deprecated Gtk::StyleContext::get_background_color()
Gdk::RGBA get_background_color(const Glib::RefPtr<Gtk::StyleContext> &style_context, Gtk::StateFlags state = Gtk::STATE_FLAG_NORMAL);
} // namespace StyleContext
namespace Source {
/// Workaround for buggy Gsv::LanguageManager::get_default()
class LanguageManager {
public:
static Glib::RefPtr<Gsv::LanguageManager> get_default();
};
/// Workaround for buggy Gsv::StyleSchemeManager::get_default()
class StyleSchemeManager {
public:
static Glib::RefPtr<Gsv::StyleSchemeManager> get_default();
};
Glib::RefPtr<Gsv::Language> guess_language(const boost::filesystem::path &file_path);
/// RAII-style text mark. Use instead of Gtk::TextBuffer::create_mark and Gtk::TextBuffer::delete_mark,
/// since Gtk::TextBuffer::delete_mark is not called upon Glib::RefPtr<Gtk::TextMark> deletion
class Mark : public Glib::RefPtr<Gtk::TextMark> {

105
src/source_language_protocol.cpp

@ -64,13 +64,14 @@ LanguageProtocol::TextDocumentEdit::TextDocumentEdit(const JSON &text_document_e
LanguageProtocol::TextDocumentEdit::TextDocumentEdit(std::string file, std::vector<TextEdit> text_edits) : file(std::move(file)), text_edits(std::move(text_edits)) {}
LanguageProtocol::WorkspaceEdit::WorkspaceEdit(const JSON &workspace_edit, boost::filesystem::path file_path) {
LanguageProtocol::WorkspaceEdit::WorkspaceEdit(const JSON &workspace_edit, boost::filesystem::path path) {
boost::filesystem::path project_path;
auto build = Project::Build::create(file_path);
auto build = Project::Build::create(path);
boost::system::error_code ec;
if(!build->project_path.empty())
project_path = build->project_path;
else
project_path = file_path.parent_path();
project_path = boost::filesystem::is_directory(path, ec) ? path : path.parent_path();
try {
if(auto changes = workspace_edit.children_optional("changes")) {
for(auto &change : *changes) {
@ -576,7 +577,7 @@ void LanguageProtocol::Client::handle_server_notification(const std::string &met
else if(method == "window/logMessage") {
if(language_id == "python" && !pyright) {
if(auto message = params.string_optional("message")) {
if(starts_with(*message, "Pyright language server"))
if(starts_with(*message, "Pyright language server") || starts_with(*message, "basedpyright language server"))
pyright = true;
}
}
@ -587,67 +588,72 @@ void LanguageProtocol::Client::handle_server_request(const boost::variant<size_t
if(method == "workspace/applyEdit") {
std::promise<void> result_processed;
bool applied = true;
dispatcher->post([&result_processed, &applied, params = std::make_shared<JSON>(std::move(params))] {
dispatcher->post([this, &result_processed, &applied, params = std::make_shared<JSON>(std::move(params))] {
ScopeGuard guard({[&result_processed] {
result_processed.set_value();
}});
if(auto current_view = dynamic_cast<Source::LanguageProtocolView *>(Notebook::get().get_current_view())) {
LanguageProtocol::WorkspaceEdit workspace_edit;
try {
workspace_edit = LanguageProtocol::WorkspaceEdit(params->object("edit"), current_view->file_path);
}
catch(...) {
applied = false;
return;
}
LanguageProtocol::WorkspaceEdit workspace_edit;
try {
workspace_edit = LanguageProtocol::WorkspaceEdit(params->object("edit"), root_path);
}
catch(...) {
applied = false;
return;
}
for(auto &document_change : workspace_edit.document_changes) {
if(auto edit = boost::get<TextDocumentEdit>(&document_change)) {
Source::View *view = nullptr;
for(auto &e : Source::View::views) {
auto current_view = Notebook::get().get_current_view();
for(auto &document_change : workspace_edit.document_changes) {
if(auto edit = boost::get<TextDocumentEdit>(&document_change)) {
Source::LanguageProtocolView *view = nullptr;
{
LockGuard lock(views_mutex);
for(auto &e : views) {
if(e->file_path == edit->file) {
view = e;
break;
}
}
}
if(!view) {
if(!Notebook::get().open(edit->file)) {
applied = false;
return;
}
view = dynamic_cast<Source::LanguageProtocolView *>(Notebook::get().get_current_view());
if(!view) {
if(!Notebook::get().open(edit->file)) {
applied = false;
return;
}
view = Notebook::get().get_current_view();
applied = false;
return;
}
}
auto buffer = view->get_buffer();
buffer->begin_user_action();
auto buffer = view->get_buffer();
buffer->begin_user_action();
auto end_iter = buffer->end();
// If entire buffer is replaced
if(edit->text_edits.size() == 1 &&
edit->text_edits[0].range.start.line == 0 && edit->text_edits[0].range.start.character == 0 &&
(edit->text_edits[0].range.end.line > end_iter.get_line() ||
(edit->text_edits[0].range.end.line == end_iter.get_line() && edit->text_edits[0].range.end.character >= current_view->get_line_pos(end_iter)))) {
view->replace_text(edit->text_edits[0].new_text);
}
else {
for(auto text_edit_it = edit->text_edits.rbegin(); text_edit_it != edit->text_edits.rend(); ++text_edit_it) {
auto start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character);
auto end_iter = view->get_iter_at_line_pos(text_edit_it->range.end.line, text_edit_it->range.end.character);
if(view != current_view)
view->get_buffer()->place_cursor(start_iter);
buffer->erase(start_iter, end_iter);
start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character);
buffer->insert(start_iter, text_edit_it->new_text);
}
auto end_iter = buffer->end();
// If entire buffer is replaced
if(edit->text_edits.size() == 1 &&
edit->text_edits[0].range.start.line == 0 && edit->text_edits[0].range.start.character == 0 &&
(edit->text_edits[0].range.end.line > end_iter.get_line() ||
(edit->text_edits[0].range.end.line == end_iter.get_line() && edit->text_edits[0].range.end.character >= view->get_line_pos(end_iter)))) {
view->replace_text(edit->text_edits[0].new_text);
}
else {
for(auto text_edit_it = edit->text_edits.rbegin(); text_edit_it != edit->text_edits.rend(); ++text_edit_it) {
auto start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character);
auto end_iter = view->get_iter_at_line_pos(text_edit_it->range.end.line, text_edit_it->range.end.character);
if(view != current_view)
view->get_buffer()->place_cursor(start_iter);
buffer->erase(start_iter, end_iter);
start_iter = view->get_iter_at_line_pos(text_edit_it->range.start.line, text_edit_it->range.start.character);
buffer->insert(start_iter, text_edit_it->new_text);
}
buffer->end_user_action();
}
}
if(current_view)
Notebook::get().open(current_view);
buffer->end_user_action();
}
}
if(current_view)
Notebook::get().open(current_view);
});
result_processed.get_future().get();
write_response(id, std::string("{\"applied\":") + (applied ? "true" : "false") + '}');
@ -676,7 +682,8 @@ void LanguageProtocol::Client::handle_server_request(const boost::variant<size_t
write_response(id, "{}");
}
else if(method == "workspace/configuration") {
auto search_path = root_path;
auto current_view = Notebook::get().get_current_view();
auto search_path = current_view && filesystem::file_in_path(current_view->file_path, root_path) ? current_view->file_path.parent_path() : root_path;
auto file = '.' + language_id + "-lsp-configuration-response.json";
boost::filesystem::path settings_path;
while(true) {

2
src/source_language_protocol.hpp

@ -111,7 +111,7 @@ namespace LanguageProtocol {
class WorkspaceEdit {
public:
WorkspaceEdit() = default;
WorkspaceEdit(const JSON &workspace_edit, boost::filesystem::path file_path);
WorkspaceEdit(const JSON &workspace_edit, boost::filesystem::path path);
std::vector<boost::variant<TextDocumentEdit, RenameFile>> document_changes;
};

179
src/terminal.cpp

@ -16,7 +16,16 @@ Terminal::Terminal() : Source::CommonView() {
set_editable(false);
bold_tag = get_buffer()->create_tag();
bold_tag->property_weight() = Pango::WEIGHT_ULTRAHEAVY;
bold_tag->property_weight() = Pango::WEIGHT_BOLD;
light_tag = get_buffer()->create_tag();
light_tag->property_weight() = Pango::WEIGHT_LIGHT;
italic_tag = get_buffer()->create_tag();
italic_tag->property_style() = Pango::STYLE_ITALIC;
underline_tag = get_buffer()->create_tag();
underline_tag->property_underline() = Pango::Underline::UNDERLINE_SINGLE;
strikethrough_tag = get_buffer()->create_tag();
strikethrough_tag->property_strikethrough() = true;
red_tag = get_buffer()->create_tag();
green_tag = get_buffer()->create_tag();
@ -24,7 +33,7 @@ Terminal::Terminal() : Source::CommonView() {
blue_tag = get_buffer()->create_tag();
magenta_tag = get_buffer()->create_tag();
cyan_tag = get_buffer()->create_tag();
gray_tag = get_buffer()->create_tag();
white_tag = get_buffer()->create_tag();
link_tag = get_buffer()->create_tag();
link_tag->property_underline() = Pango::Underline::UNDERLINE_SINGLE;
@ -32,8 +41,8 @@ Terminal::Terminal() : Source::CommonView() {
invisible_tag = get_buffer()->create_tag();
invisible_tag->property_invisible() = true;
link_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::HAND1);
default_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::XTERM);
link_mouse_cursor = Gdk::Cursor::create(get_display(), Gdk::CursorType::HAND1);
default_mouse_cursor = Gdk::Cursor::create(get_display(), Gdk::CursorType::XTERM);
class DetectPossibleLink {
bool delimiter_found = false, dot_found = false;
@ -110,7 +119,38 @@ Terminal::Terminal() : Source::CommonView() {
return {};
}
};
get_buffer()->signal_insert().connect([this, detect_possible_link = DetectPossibleLink(), parse_ansi_escape_sequence = ParseAnsiEscapeSequence(), last_color = -1, last_color_sequence_mark = std::shared_ptr<Source::Mark>()](const Gtk::TextIter &iter, const Glib::ustring &text_, int /*bytes*/) mutable {
class Code {
public:
int code = -1;
std::shared_ptr<Source::Mark> start_mark;
};
auto code_to_color_tag = [this](int code) {
if(code == 31)
return red_tag;
else if(code == 32)
return green_tag;
else if(code == 33)
return yellow_tag;
else if(code == 34)
return blue_tag;
else if(code == 35)
return magenta_tag;
else if(code == 36)
return cyan_tag;
else if(code == 37)
return white_tag;
return Glib::RefPtr<Gtk::TextTag>();
};
auto code_to_weight_tag = [this](int code) {
if(code == 1)
return bold_tag;
else if(code == 2)
return light_tag;
return Glib::RefPtr<Gtk::TextTag>();
};
get_buffer()->signal_insert().connect([this, code_to_color_tag, code_to_weight_tag,
detect_possible_link = DetectPossibleLink(), parse_ansi_escape_sequence = ParseAnsiEscapeSequence(),
color_code = Code(), weight_code = Code(), italic_code = Code(), underline_code = Code(), strikethrough_code = Code()](const Gtk::TextIter &iter, const Glib::ustring &text_, int /*bytes*/) mutable {
boost::optional<Gtk::TextIter> start_of_text;
int line_nr_offset = 0;
auto get_line_nr = [&] {
@ -144,9 +184,31 @@ Terminal::Terminal() : Source::CommonView() {
start.backward_chars(sequence->length);
get_buffer()->apply_tag(invisible_tag, start, end);
if(sequence->command == 'm') {
int color = -1;
if(sequence->arguments.empty())
color = 0;
auto reset = [&] {
if(color_code.start_mark) {
auto tag = code_to_color_tag(color_code.code);
if(tag)
get_buffer()->apply_tag(tag, (*color_code.start_mark)->get_iter(), start);
}
if(weight_code.start_mark) {
auto tag = code_to_weight_tag(weight_code.code);
if(tag)
get_buffer()->apply_tag(tag, (*weight_code.start_mark)->get_iter(), start);
}
if(italic_code.start_mark)
get_buffer()->apply_tag(italic_tag, (*italic_code.start_mark)->get_iter(), start);
if(underline_code.start_mark)
get_buffer()->apply_tag(underline_tag, (*underline_code.start_mark)->get_iter(), start);
if(strikethrough_code.start_mark)
get_buffer()->apply_tag(strikethrough_tag, (*strikethrough_code.start_mark)->get_iter(), start);
color_code = {};
weight_code = {};
italic_code = {};
underline_code = {};
strikethrough_code = {};
};
if(sequence->arguments.empty()) // Reset
reset();
else {
size_t pos = 0;
size_t start_pos = pos;
@ -154,16 +216,74 @@ Terminal::Terminal() : Source::CommonView() {
pos = sequence->arguments.find(";", pos);
try {
auto code = std::stoi(sequence->arguments.substr(start_pos, pos != std::string::npos ? pos - start_pos : pos));
if(code == 39)
color = 0;
else if(code == 38) {
color = 0;
break; // Do not read next arguments
if(code == 0) // Reset
reset();
else if(code == 1 || code == 2) { // Bold or faint
if(weight_code.start_mark) {
auto tag = code_to_weight_tag(weight_code.code);
if(tag)
get_buffer()->apply_tag(tag, (*weight_code.start_mark)->get_iter(), start);
}
weight_code = {code, std::make_shared<Source::Mark>(end)};
}
else if(code == 3) // Italic
italic_code = {code, std::make_shared<Source::Mark>(end)};
else if(code == 4) // Underline
underline_code = {code, std::make_shared<Source::Mark>(end)};
else if(code == 9) // Strikethrough
strikethrough_code = {code, std::make_shared<Source::Mark>(end)};
else if(code == 22) { // Normal intensity
if(weight_code.start_mark) {
auto tag = code_to_weight_tag(weight_code.code);
if(tag)
get_buffer()->apply_tag(tag, (*weight_code.start_mark)->get_iter(), start);
}
weight_code = {};
}
else if(code == 23) { // No italic
if(italic_code.start_mark)
get_buffer()->apply_tag(italic_tag, (*italic_code.start_mark)->get_iter(), start);
italic_code = {};
}
else if(code == 48 || code == 58)
break; // Do not read next arguments
else if(code == 0 || code == 2 || code == 22 || (code >= 30 && code <= 37))
color = code;
else if(code == 24) { // No underline
if(underline_code.start_mark)
get_buffer()->apply_tag(underline_tag, (*underline_code.start_mark)->get_iter(), start);
underline_code = {};
}
else if(code == 29) { // No strikethrough
if(strikethrough_code.start_mark)
get_buffer()->apply_tag(strikethrough_tag, (*strikethrough_code.start_mark)->get_iter(), start);
strikethrough_code = {};
}
else if(code >= 30 && code <= 37) { // Foreground color
if(color_code.start_mark) {
auto tag = code_to_color_tag(color_code.code);
if(tag)
get_buffer()->apply_tag(tag, (*color_code.start_mark)->get_iter(), start);
}
color_code = {code, std::make_shared<Source::Mark>(end)};
}
else if(code == 38) { // Set specific color not supported
if(color_code.start_mark) {
auto tag = code_to_color_tag(color_code.code);
if(tag)
get_buffer()->apply_tag(tag, (*color_code.start_mark)->get_iter(), start);
}
color_code = {};
break;
}
else if(code == 39) { // Default color
if(color_code.start_mark) {
auto tag = code_to_color_tag(color_code.code);
if(tag)
get_buffer()->apply_tag(tag, (*color_code.start_mark)->get_iter(), start);
}
color_code = {};
}
else if(code == 48) // Set specific background color not supported
break;
else if(code == 58) // Set specific underline color not supported
break;
}
catch(...) {
}
@ -173,27 +293,6 @@ Terminal::Terminal() : Source::CommonView() {
start_pos = pos;
}
}
if(last_color >= 0) {
if(last_color == 31)
get_buffer()->apply_tag(red_tag, (*last_color_sequence_mark)->get_iter(), start);
else if(last_color == 32)
get_buffer()->apply_tag(green_tag, (*last_color_sequence_mark)->get_iter(), start);
else if(last_color == 33)
get_buffer()->apply_tag(yellow_tag, (*last_color_sequence_mark)->get_iter(), start);
else if(last_color == 34)
get_buffer()->apply_tag(blue_tag, (*last_color_sequence_mark)->get_iter(), start);
else if(last_color == 35)
get_buffer()->apply_tag(magenta_tag, (*last_color_sequence_mark)->get_iter(), start);
else if(last_color == 36)
get_buffer()->apply_tag(cyan_tag, (*last_color_sequence_mark)->get_iter(), start);
else if(last_color == 37 || last_color == 2)
get_buffer()->apply_tag(gray_tag, (*last_color_sequence_mark)->get_iter(), start);
}
if(color >= 0) {
last_color = color;
last_color_sequence_mark = std::make_shared<Source::Mark>(end);
}
}
}
if(text[i] == '\n')
@ -540,11 +639,11 @@ void Terminal::configure() {
cyan_tag->property_foreground_rgba() = rgba;
rgba.set_rgba(0.5, 0.5, 0.5);
factor = light_theme ? 0.6 : 0.4;
factor = light_theme ? 0.7 : 0.4;
rgba.set_red(normal_color.get_red() + factor * (rgba.get_red() - normal_color.get_red()));
rgba.set_green(normal_color.get_green() + factor * (rgba.get_green() - normal_color.get_green()));
rgba.set_blue(normal_color.get_blue() + factor * (rgba.get_blue() - normal_color.get_blue()));
gray_tag->property_foreground_rgba() = rgba;
white_tag->property_foreground_rgba() = rgba;
// Set search match style:
get_buffer()->get_tag_table()->foreach([](const Glib::RefPtr<Gtk::TextTag> &tag) {

4
src/terminal.hpp

@ -46,10 +46,10 @@ protected:
private:
Dispatcher dispatcher;
Glib::RefPtr<Gtk::TextTag> bold_tag;
Glib::RefPtr<Gtk::TextTag> bold_tag, light_tag, italic_tag, underline_tag, strikethrough_tag;
Glib::RefPtr<Gtk::TextTag> link_tag;
Glib::RefPtr<Gtk::TextTag> invisible_tag;
Glib::RefPtr<Gtk::TextTag> red_tag, green_tag, yellow_tag, blue_tag, magenta_tag, cyan_tag, gray_tag;
Glib::RefPtr<Gtk::TextTag> red_tag, green_tag, yellow_tag, blue_tag, magenta_tag, cyan_tag, white_tag;
Glib::RefPtr<Gdk::Cursor> link_mouse_cursor;
Glib::RefPtr<Gdk::Cursor> default_mouse_cursor;

51
src/tooltips.cpp

@ -110,8 +110,8 @@ void Tooltip::show(bool disregard_drawn, const std::function<void()> &on_motion)
tooltip_text_view->get_style_context()->add_class("juci_tooltip_text_view");
tooltip_text_view->set_editable(false);
static auto link_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::HAND1);
static auto default_mouse_cursor = Gdk::Cursor::create(Gdk::CursorType::XTERM);
static auto link_mouse_cursor = Gdk::Cursor::create(window->get_display(), Gdk::CursorType::HAND1);
static auto default_mouse_cursor = Gdk::Cursor::create(window->get_display(), Gdk::CursorType::XTERM);
tooltip_text_view->signal_motion_notify_event().connect([this, tooltip_text_view](GdkEventMotion *event) {
Gtk::TextIter iter;
int location_x, location_y;
@ -395,7 +395,27 @@ void Tooltip::wrap_lines() {
auto next = last_space;
next.forward_char();
buffer->erase(last_space, next);
buffer->insert(mark->get_iter(), "\n");
// Indent next line if current line is indented, or for list items
auto start = buffer->get_iter_at_line(mark->get_iter().get_line());
auto end = start;
if((*end == '-' || *end == '*' || *end == '+') && end.forward_char() && *end == ' ')
buffer->insert(mark->get_iter(), "\n ");
else if((*end >= '1' && *end <= '9') && end.forward_char()) {
while((*end >= '0' && *end <= '9') && end.forward_char()) {
}
if(*end == '.' && end.forward_char() && *end == ' ' && end.forward_char())
buffer->insert(mark->get_iter(), "\n" + std::string(end.get_line_offset(), ' '));
else
buffer->insert(mark->get_iter(), "\n");
}
else if((*end == ' ' || *end == '\t') && end.forward_char()) {
while((*end == ' ' || *end == '\t') && end.forward_char()) {
}
buffer->insert(mark->get_iter(), "\n" + std::string(end.get_line_offset(), ' '));
}
else
buffer->insert(mark->get_iter(), "\n");
iter = mark->get_iter();
buffer->delete_mark(mark);
@ -410,11 +430,11 @@ void Tooltip::wrap_lines() {
void Tooltip::create_tags() {
if(!h1_tag) {
h1_tag = buffer->create_tag();
h1_tag->property_weight() = Pango::WEIGHT_ULTRAHEAVY;
h1_tag->property_weight() = Pango::WEIGHT_BOLD;
h1_tag->property_underline() = Pango::UNDERLINE_SINGLE;
h2_tag = buffer->create_tag();
h2_tag->property_weight() = Pango::WEIGHT_ULTRAHEAVY;
h2_tag->property_weight() = Pango::WEIGHT_BOLD;
h3_tag = buffer->create_tag();
h3_tag->property_style() = Pango::Style::STYLE_ITALIC;
@ -424,12 +444,17 @@ void Tooltip::create_tags() {
code_tag = buffer->create_tag();
code_tag->property_family() = source_font_family;
auto background_rgba = Gdk::RGBA();
auto line_rgba = Gdk::RGBA();
auto normal_color = window->get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL);
auto light_theme = (normal_color.get_red() + normal_color.get_green() + normal_color.get_blue()) / 3 < 0.5;
if(light_theme)
if(light_theme) {
background_rgba.set_rgba(1.0, 1.0, 1.0, 0.4);
else
line_rgba.set_rgba(0.0, 0.0, 0.0, 0.25);
}
else {
background_rgba.set_rgba(0.0, 0.0, 0.0, 0.2);
line_rgba.set_rgba(1.0, 1.0, 1.0, 0.25);
}
code_tag->property_background_rgba() = background_rgba;
code_block_tag = buffer->create_tag();
@ -437,13 +462,18 @@ void Tooltip::create_tags() {
code_block_tag->property_paragraph_background_rgba() = background_rgba;
bold_tag = buffer->create_tag();
bold_tag->property_weight() = Pango::WEIGHT_ULTRAHEAVY;
bold_tag->property_weight() = Pango::WEIGHT_BOLD;
italic_tag = buffer->create_tag();
italic_tag->property_style() = Pango::Style::STYLE_ITALIC;
strikethrough_tag = buffer->create_tag();
strikethrough_tag->property_strikethrough() = true;
line_tag = buffer->create_tag();
line_tag->property_paragraph_background_rgba() = line_rgba;
line_tag->property_foreground_rgba() = line_rgba;
line_tag->property_size() = 1;
}
}
@ -826,7 +856,7 @@ void Tooltip::insert_markdown(const std::string &input) {
if(i == input.size() || input[i] == '\n') {
if(i < input.size())
++i;
buffer->insert_at_cursor("---\n");
buffer->insert_with_tag(buffer->get_insert()->get_iter(), "---\n", line_tag);
if(is_empty_line())
buffer->insert_at_cursor("\n");
return true;
@ -966,7 +996,8 @@ void Tooltip::insert_markdown(const std::string &input) {
if(i < input.size() && (input[i] == '#' ||
starts_with(input, i, "```") || starts_with(input, i, "- ") ||
starts_with(input, i, "+ ") || starts_with(input, i, "* ") ||
starts_with(input, i, "1. "))) {
starts_with(input, i, "1. ") ||
starts_with(input, i, "***") || starts_with(input, i, "___"))) { // test\n---\ntest does not work for horizontal lines since it conflicts with headers
break;
}
}

1
src/tooltips.hpp

@ -56,6 +56,7 @@ private:
Glib::RefPtr<Gtk::TextTag> bold_tag;
Glib::RefPtr<Gtk::TextTag> italic_tag;
Glib::RefPtr<Gtk::TextTag> strikethrough_tag;
Glib::RefPtr<Gtk::TextTag> line_tag;
std::map<Glib::RefPtr<Gtk::TextTag>, std::string> links;
std::map<Glib::RefPtr<Gtk::TextTag>, std::string> reference_links;

18
src/window.cpp

@ -269,7 +269,7 @@ void Window::configure() {
}
}
auto foreground_value = style && style->property_foreground_set() ? style->property_foreground().get_value() : get_style_context()->get_color().to_string();
auto background_value = style && style->property_background_set() ? style->property_background().get_value() : get_style_context()->get_background_color().to_string();
auto background_value = style && style->property_background_set() ? style->property_background().get_value() : StyleContext::get_background_color(get_style_context()).to_string();
#if GTK_VERSION_GE(3, 20)
css_provider_tooltips->load_from_data(".juci_tooltip_box {background-color: " + background_value + ";}" +
".juci_tooltip_text_view text {color: " + foreground_value + ";background-color: " + background_value + ";}");
@ -1453,10 +1453,10 @@ void Window::set_menu_actions() {
auto directory_folder = Project::get_preferably_directory_folder();
if(Config::get().terminal.clear_on_run_command)
Terminal::get().clear();
Terminal::get().async_print("\e[2mRunning: " + content + "\e[m\n");
Terminal::get().async_print("\e[37mRunning: " + content + "\e[m\n");
Terminal::get().async_process(content, directory_folder, [content](int exit_status) {
Terminal::get().print("\e[2m" + content + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + content + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}
if(Config::get().terminal.hide_entry_on_run_command)
@ -1975,19 +1975,19 @@ bool Window::on_key_press_event(GdkEventKey *event) {
if(!command.debug) {
Project::compiling = true;
Terminal::get().print("\e[2mCompiling and running: " + label + "\e[m\n");
Terminal::get().print("\e[37mCompiling and running: " + label + "\e[m\n");
Terminal::get().async_process(compile, run_path, [run, run_path, label](int exit_status) {
Project::compiling = false;
if(exit_status == 0) {
Terminal::get().async_process(run, run_path, [label](int exit_status) {
Terminal::get().print("\e[2m" + label + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + label + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}
});
}
else { // Debug
Project::debugging = true;
Terminal::get().print("\e[2mCompiling and debugging: " + label + "\e[m\n");
Terminal::get().print("\e[37mCompiling and debugging: " + label + "\e[m\n");
Terminal::get().async_process(compile, run_path, [project = project->shared_from_this(), run, run_path, debug_remote_host = command.debug_remote_host](int exit_status) {
if(exit_status != EXIT_SUCCESS)
Project::debugging = false;
@ -1997,14 +1997,14 @@ bool Window::on_key_press_event(GdkEventKey *event) {
}
}
else if(!command.debug) {
Terminal::get().async_print("\e[2mRunning: " + label + "\e[m\n");
Terminal::get().async_print("\e[37mRunning: " + label + "\e[m\n");
Terminal::get().async_process(run, run_path, [label](int exit_status) {
Terminal::get().print("\e[2m" + label + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
Terminal::get().print("\e[37m" + label + " returned: " + (exit_status == 0 ? "\e[32m" : "\e[31m") + std::to_string(exit_status) + "\e[m\n");
});
}
else { // Debug
Project::debugging = true;
Terminal::get().async_print("\e[2mDebugging: " + label + "\e[m\n");
Terminal::get().async_print("\e[37mDebugging: " + label + "\e[m\n");
project->debug_start(run, run_path, command.debug_remote_host);
}
return true;

78
tests/terminal_test.cpp

@ -252,8 +252,10 @@ int main() {
auto iter = buffer->begin();
iter.forward_chars(15);
assert(iter.starts_tag(terminal.red_tag));
assert(iter.starts_tag(terminal.bold_tag));
iter.forward_chars(7);
assert(iter.ends_tag(terminal.red_tag));
assert(iter.ends_tag(terminal.bold_tag));
}
{
terminal.clear();
@ -276,9 +278,15 @@ int main() {
iter.forward_visible_cursor_positions(3);
assert(iter.get_tags().empty());
iter.forward_visible_cursor_positions(1);
assert(iter.get_tags() == std::vector<Glib::RefPtr<Gtk::TextTag>>{terminal.red_tag});
auto tags = iter.get_tags();
assert(tags.size() == 2);
assert(std::find(tags.begin(), tags.end(), Glib::RefPtr<Gtk::TextTag>{terminal.red_tag}) != tags.end());
assert(std::find(tags.begin(), tags.end(), Glib::RefPtr<Gtk::TextTag>{terminal.bold_tag}) != tags.end());
iter.forward_visible_cursor_positions(3);
assert(iter.get_tags() == std::vector<Glib::RefPtr<Gtk::TextTag>>{terminal.red_tag});
tags = iter.get_tags();
assert(tags.size() == 2);
assert(std::find(tags.begin(), tags.end(), Glib::RefPtr<Gtk::TextTag>{terminal.red_tag}) != tags.end());
assert(std::find(tags.begin(), tags.end(), Glib::RefPtr<Gtk::TextTag>{terminal.bold_tag}) != tags.end());
iter.forward_visible_cursor_positions(1);
assert(iter.get_tags().empty());
}
@ -295,6 +303,72 @@ int main() {
iter.forward_visible_cursor_positions(1);
assert(iter.get_tags().empty());
}
{
terminal.clear();
terminal.print("test\e[02mtest\e[0mtest");
assert(buffer->get_text(true) == "test\e[02mtest\e[0mtest");
assert(buffer->get_text(false) == "testtesttest");
auto iter = buffer->begin();
iter.forward_chars(9);
assert(iter.starts_tag(terminal.light_tag));
iter.forward_chars(4);
assert(iter.ends_tag(terminal.light_tag));
}
{
terminal.clear();
terminal.print("test\e[02mtest\e[22mtest");
assert(buffer->get_text(true) == "test\e[02mtest\e[22mtest");
assert(buffer->get_text(false) == "testtesttest");
auto iter = buffer->begin();
iter.forward_chars(9);
assert(iter.starts_tag(terminal.light_tag));
iter.forward_chars(4);
assert(iter.ends_tag(terminal.light_tag));
}
{
terminal.clear();
terminal.print("test\e[03mtest\e[23mtest");
assert(buffer->get_text(true) == "test\e[03mtest\e[23mtest");
assert(buffer->get_text(false) == "testtesttest");
auto iter = buffer->begin();
iter.forward_chars(9);
assert(iter.starts_tag(terminal.italic_tag));
iter.forward_chars(4);
assert(iter.ends_tag(terminal.italic_tag));
}
{
terminal.clear();
terminal.print("test\e[04mtest\e[24mtest");
assert(buffer->get_text(true) == "test\e[04mtest\e[24mtest");
assert(buffer->get_text(false) == "testtesttest");
auto iter = buffer->begin();
iter.forward_chars(9);
assert(iter.starts_tag(terminal.underline_tag));
iter.forward_chars(4);
assert(iter.ends_tag(terminal.underline_tag));
}
{
terminal.clear();
terminal.print("test\e[09mtest\e[29mtest");
assert(buffer->get_text(true) == "test\e[09mtest\e[29mtest");
assert(buffer->get_text(false) == "testtesttest");
auto iter = buffer->begin();
iter.forward_chars(9);
assert(iter.starts_tag(terminal.strikethrough_tag));
iter.forward_chars(4);
assert(iter.ends_tag(terminal.strikethrough_tag));
}
{
terminal.clear();
terminal.print("test\e[31mtest\e[32mtest\e[0m");
assert(buffer->get_text(true) == "test\e[31mtest\e[32mtest\e[0m");
assert(buffer->get_text(false) == "testtesttest");
auto iter = buffer->begin();
iter.forward_chars(9);
assert(iter.starts_tag(terminal.red_tag));
iter.forward_chars(4);
assert(iter.ends_tag(terminal.red_tag));
}
// async_process tests
{

33
tests/tooltips_test.cpp

@ -81,6 +81,14 @@ int main() {
auto tooltip = get_markdown_tooltip("test\n\n***\n\ntest");
g_assert(tooltip->buffer->get_text() == "test\n\n---\n\ntest");
}
{
auto tooltip = get_markdown_tooltip("test\n***\ntest");
g_assert(tooltip->buffer->get_text() == "test\n---\ntest");
}
{
auto tooltip = get_markdown_tooltip("test\n___\ntest");
g_assert(tooltip->buffer->get_text() == "test\n---\ntest");
}
{
auto tooltip = get_markdown_tooltip("test\n\n___\n\ntest");
g_assert(tooltip->buffer->get_text() == "test\n\n---\n\ntest");
@ -532,6 +540,31 @@ int main() {
tooltip->wrap_lines();
g_assert(tooltip->buffer->get_text() == "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest\ntest test");
}
{
auto tooltip = get_markdown_tooltip("- test test test test test test test test test test test test test test test test test test test test test");
tooltip->wrap_lines();
g_assert(tooltip->buffer->get_text() == "- test test test test test test test test test test test test test test test\n test test test test test test");
}
{
auto tooltip = get_markdown_tooltip("+ test test test test test test test test test test test test test test test test test test test test test");
tooltip->wrap_lines();
g_assert(tooltip->buffer->get_text() == "+ test test test test test test test test test test test test test test test\n test test test test test test");
}
{
auto tooltip = get_markdown_tooltip("* test test test test test test test test test test test test test test test test test test test test test");
tooltip->wrap_lines();
g_assert(tooltip->buffer->get_text() == "* test test test test test test test test test test test test test test test\n test test test test test test");
}
{
auto tooltip = get_markdown_tooltip("1. test test test test test test test test test test test test test test test test test test test test test");
tooltip->wrap_lines();
g_assert(tooltip->buffer->get_text() == "1. test test test test test test test test test test test test test test test\n test test test test test test");
}
{
auto tooltip = get_markdown_tooltip("10. test test test test test test test test test test test test test test test test test test test test test");
tooltip->wrap_lines();
g_assert(tooltip->buffer->get_text() == "10. test test test test test test test test test test test test test test test\n test test test test test test");
}
// Testing insert_doxygen
{

Loading…
Cancel
Save