diff --git a/src/cmake.cpp b/src/cmake.cpp index b38274a..116a530 100644 --- a/src/cmake.cpp +++ b/src/cmake.cpp @@ -4,6 +4,7 @@ #include "dialogs.hpp" #include "filesystem.hpp" #include "terminal.hpp" +#include "utility.hpp" #include CMake::CMake(const boost::filesystem::path &path) { @@ -113,7 +114,7 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui std::vector cmake_executables; for(auto ¶meter : parameters) { - if(parameter.second.size() > 1 && parameter.second[0].size() > 0 && parameter.second[0].compare(0, 2, "${") != 0) { + if(parameter.second.size() > 1 && parameter.second[0].size() > 0 && !starts_with(parameter.second[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); diff --git a/src/compile_commands.cpp b/src/compile_commands.cpp index d9e5ae5..b610208 100644 --- a/src/compile_commands.cpp +++ b/src/compile_commands.cpp @@ -2,6 +2,7 @@ #include "clangmm.hpp" #include "config.hpp" #include "terminal.hpp" +#include "utility.hpp" #include #include #include @@ -14,7 +15,7 @@ CompileCommands::FindSystemIncludePaths::FindSystemIncludePaths() { return; std::string line; while(std::getline(stdout_stream, line)) { - if(line.compare(0, 34, "#include <...> search starts here:") == 0) { + if(starts_with(line, "#include <...> search starts here:")) { while(std::getline(stdout_stream, line)) { if(!line.empty() && line[0] == ' ') { #ifdef _WIN32 diff --git a/src/ctags.cpp b/src/ctags.cpp index cd57f0a..1933afb 100644 --- a/src/ctags.cpp +++ b/src/ctags.cpp @@ -3,6 +3,7 @@ #include "filesystem.hpp" #include "project_build.hpp" #include "terminal.hpp" +#include "utility.hpp" #include #include @@ -60,7 +61,7 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup) c return location; } location.symbol = line.substr(0, symbol_end); - if(9 < location.symbol.size() && location.symbol[8] == ' ' && location.symbol.compare(0, 8, "operator") == 0) { + if(9 < location.symbol.size() && location.symbol[8] == ' ' && starts_with(location.symbol, "operator")) { auto &chr = location.symbol[9]; if(!((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr == '_')) location.symbol.erase(8, 1); @@ -151,7 +152,7 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup) c bool escaped = false; for(size_t i = 0; i < location.source.size(); i++) { if(!escaped) { - if(location.source.compare(i, symbol.size(), symbol) == 0) { + if(starts_with(location.source, i, symbol)) { location.source.insert(i + symbol.size(), ""); location.source.insert(i, ""); i += 7 + symbol.size() - 1; diff --git a/src/debug_lldb.cpp b/src/debug_lldb.cpp index 06f2b57..7501fb5 100644 --- a/src/debug_lldb.cpp +++ b/src/debug_lldb.cpp @@ -7,6 +7,7 @@ #include "filesystem.hpp" #include "process.hpp" #include "terminal.hpp" +#include "utility.hpp" #include #include @@ -451,7 +452,7 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil if(value_decl_path == file_path) { value.GetDescription(stream); std::string variable_value = stream.GetData(); - if(variable_value.size() >= 2 && variable_value.compare(variable_value.size() - 2, 2, "\n\n", 2) == 0) + if(ends_with(variable_value, "\n\n")) variable_value.pop_back(); // Remove newline at end of string return variable_value; } diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 10b4820..ee1f44c 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -1,10 +1,10 @@ +#include "filesystem.hpp" +#include "utility.hpp" #include #include #include #include -#include "filesystem.hpp" - //Only use on small files std::string filesystem::read(const std::string &path) { std::string str; @@ -222,7 +222,7 @@ boost::filesystem::path filesystem::get_executable(const boost::filesystem::path 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(starts_with(it_path_filename_str, executable_name_str)) { if(it_path > executable && ((it_path_filename_str.size() > executable_name_str.size() && it_path_filename_str[executable_name_str.size()] >= '0' && @@ -295,7 +295,7 @@ std::string filesystem::get_uri_from_path(const boost::filesystem::path &path) { boost::filesystem::path filesystem::get_path_from_uri(const std::string &uri) { std::string encoded; - if(uri.compare(0, 7, "file://") == 0) + if(starts_with(uri, "file://")) encoded = uri.substr(7); else encoded = uri; diff --git a/src/grep.cpp b/src/grep.cpp index 510fe9f..d636ee8 100644 --- a/src/grep.cpp +++ b/src/grep.cpp @@ -3,6 +3,7 @@ #include "filesystem.hpp" #include "project_build.hpp" #include "terminal.hpp" +#include "utility.hpp" Grep::Grep(const boost::filesystem::path &path, const std::string &pattern, bool case_sensitive, bool extended_regex) { auto build = Project::Build::create(path); @@ -64,7 +65,7 @@ Grep::Location Grep::get_location(std::string line, bool color_codes_to_markup, if(color_codes_to_markup) { std::string escaped = Glib::Markup::escape_text(line); auto decode_escape_sequence = [](const std::string &line, size_t &i) -> bool { - if(line.compare(i, 7, "[") != 0) + if(!starts_with(line, i, "[")) return false; i += 7; for(; i < line.size(); ++i) { diff --git a/src/juci.cpp b/src/juci.cpp index 86038cb..ab0d7ca 100644 --- a/src/juci.cpp +++ b/src/juci.cpp @@ -5,6 +5,7 @@ #include "menu.hpp" #include "notebook.hpp" #include "terminal.hpp" +#include "utility.hpp" #include "window.hpp" #ifndef _WIN32 #include @@ -70,7 +71,7 @@ void Application::on_activate() { 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) { + if(filesystem::file_in_path(it->first, directory)) { files_in_directory += " " + filesystem::escape_argument(it->first.string()); it = files.erase(it); } diff --git a/src/meson.cpp b/src/meson.cpp index 47b9f87..cffa9a2 100644 --- a/src/meson.cpp +++ b/src/meson.cpp @@ -4,6 +4,7 @@ #include "dialogs.hpp" #include "filesystem.hpp" #include "terminal.hpp" +#include "utility.hpp" #include Meson::Meson(const boost::filesystem::path &path) { @@ -101,7 +102,7 @@ boost::filesystem::path Meson::get_executable(const boost::filesystem::path &bui 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) { + if(starts_with(values[0], pos + 1, "exe")) { auto executable = build_path / values[0].substr(0, pos); if(command_file == file_path) return executable; diff --git a/src/selection_dialog.cpp b/src/selection_dialog.cpp index 54e2905..9d85c6c 100644 --- a/src/selection_dialog.cpp +++ b/src/selection_dialog.cpp @@ -254,14 +254,9 @@ SelectionDialog::SelectionDialog(Gtk::TextView *text_view, const Glib::RefPtrget_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); - hide(); - on_select(index, text, true); - } - else - hide(); + if(on_select && it) + on_select(it->get_value(list_view_text.column_record.index), it->get_value(list_view_text.column_record.text), true); + hide(); }; search_entry.signal_activate().connect([activate]() { activate(); @@ -390,11 +385,8 @@ void CompletionDialog::select(bool hide_window) { row_in_entry = true; 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); - on_select(index, text, hide_window); - } + if(on_select && it) + on_select(it->get_value(list_view_text.column_record.index), it->get_value(list_view_text.column_record.text), hide_window); if(hide_window) hide(); } @@ -403,9 +395,8 @@ 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()); search_entry.set_text(text); diff --git a/src/source.cpp b/src/source.cpp index 2877910..bd83939 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -1558,10 +1558,10 @@ void Source::View::show_or_hide() { } } else if(std::none_of(exact.begin(), exact.end(), [&text](const std::string &e) { - return text.compare(0, e.size(), e) == 0; + return starts_with(text, e); }) && std::none_of(followed_by_non_token_char.begin(), followed_by_non_token_char.end(), [this, &text](const std::string &e) { - return text.compare(0, e.size(), e) == 0 && text.size() > e.size() && !is_token_char(text[e.size()]); + return starts_with(text, e) && text.size() > e.size() && !is_token_char(text[e.size()]); })) { end = get_buffer()->get_iter_at_line(end.get_line()); break; @@ -2411,8 +2411,7 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey *key) { if(tabs.size() % tab_size == 1 && !start_iter.ends_line() && !is_code_iter(start_iter)) { auto end_of_line_iter = start_iter; end_of_line_iter.forward_to_line_end(); - auto line = get_buffer()->get_text(tabs_end_iter, end_of_line_iter); - if(!line.empty() && line.raw().compare(0, 2, "*/") == 0) { + if(starts_with(get_buffer()->get_text(tabs_end_iter, end_of_line_iter).raw(), "*/")) { tabs.pop_back(); get_buffer()->insert_at_cursor('\n' + tabs); scroll_to(get_buffer()->get_insert()); diff --git a/src/source_base.cpp b/src/source_base.cpp index 6cf0672..00c4675 100644 --- a/src/source_base.cpp +++ b/src/source_base.cpp @@ -1035,10 +1035,10 @@ void Source::BaseView::setup_extra_cursor_signals() { }); } -void Source::BaseView::insert_snippet(Gtk::TextIter iter, const Glib::ustring &snippet) { +void Source::BaseView::insert_snippet(Gtk::TextIter iter, const std::string &snippet) { std::map>> arguments_offsets; - Glib::ustring insert; + std::string insert; insert.reserve(snippet.size()); size_t i = 0; int number; @@ -1058,12 +1058,18 @@ void Source::BaseView::insert_snippet(Gtk::TextIter iter, const Glib::ustring &s return false; } }; + auto compare_variable = [&snippet, &i](const char *text) { + if(starts_with(snippet, i, text)) { + i += strlen(text); + return true; + } + return false; + }; bool erase_line = false, erase_word = false; - auto parse_variable = [this, &iter, &snippet, &i, &insert, &erase_line, &erase_word] { + auto parse_variable = [this, &iter, &snippet, &i, &insert, &compare_variable, &erase_line, &erase_word] { if(i >= snippet.size()) throw std::out_of_range("unexpected end"); - if(snippet.compare(i, 16, "TM_SELECTED_TEXT") == 0) { - i += 16; + if(compare_variable("TM_SELECTED_TEXT")) { Gtk::TextIter start, end; if(get_buffer()->get_selection_bounds(start, end)) { insert += get_buffer()->get_text(start, end); @@ -1071,16 +1077,14 @@ void Source::BaseView::insert_snippet(Gtk::TextIter iter, const Glib::ustring &s } return false; } - else if(snippet.compare(i, 15, "TM_CURRENT_LINE") == 0) { - i += 15; + else if(compare_variable("TM_CURRENT_LINE")) { auto start = get_buffer()->get_iter_at_line(iter.get_line()); auto end = get_iter_at_line_end(iter.get_line()); insert += get_buffer()->get_text(start, end); erase_line = true; return true; } - else if(snippet.compare(i, 15, "TM_CURRENT_WORD") == 0) { - i += 15; + else if(compare_variable("TM_CURRENT_WORD")) { if(is_token_char(*iter)) { auto token_iters = get_token_iters(iter); insert += get_buffer()->get_text(token_iters.first, token_iters.second); @@ -1089,38 +1093,31 @@ void Source::BaseView::insert_snippet(Gtk::TextIter iter, const Glib::ustring &s } return false; } - else if(snippet.compare(i, 13, "TM_LINE_INDEX") == 0) { - i += 13; + else if(compare_variable("TM_LINE_INDEX")) { insert += std::to_string(iter.get_line()); return true; } - else if(snippet.compare(i, 14, "TM_LINE_NUMBER") == 0) { - i += 14; + else if(compare_variable("TM_LINE_NUMBER")) { insert += std::to_string(iter.get_line() + 1); return true; } - else if(snippet.compare(i, 16, "TM_FILENAME_BASE") == 0) { - i += 16; + else if(compare_variable("TM_FILENAME_BASE")) { insert += file_path.stem().string(); return true; } - else if(snippet.compare(i, 11, "TM_FILENAME") == 0) { - i += 11; + else if(compare_variable("TM_FILENAME")) { insert += file_path.filename().string(); return true; } - else if(snippet.compare(i, 12, "TM_DIRECTORY") == 0) { - i += 12; + else if(compare_variable("TM_DIRECTORY")) { insert += file_path.parent_path().string(); return true; } - else if(snippet.compare(i, 11, "TM_FILEPATH") == 0) { - i += 11; + else if(compare_variable("TM_FILEPATH")) { insert += file_path.string(); return true; } - else if(snippet.compare(i, 9, "CLIPBOARD") == 0) { - i += 9; + else if(compare_variable("CLIPBOARD")) { insert += Gtk::Clipboard::get()->wait_for_text(); return true; } @@ -1144,7 +1141,7 @@ void Source::BaseView::insert_snippet(Gtk::TextIter iter, const Glib::ustring &s if(snippet.at(i) == '{') { ++i; if(parse_number()) { - Glib::ustring placeholder; + std::string placeholder; if(snippet.at(i) == ':') { ++i; for(; snippet.at(i) != '}'; ++i) diff --git a/src/source_base.hpp b/src/source_base.hpp index 8283f07..a4df26e 100644 --- a/src/source_base.hpp +++ b/src/source_base.hpp @@ -152,7 +152,7 @@ namespace Source { std::vector *snippets GUARDED_BY(snippets_mutex) = nullptr; std::list, Glib::RefPtr>>> snippets_marks; Glib::RefPtr snippet_argument_tag; - void insert_snippet(Gtk::TextIter iter, const Glib::ustring &snippet); + void insert_snippet(Gtk::TextIter iter, const std::string &snippet); bool select_snippet_argument(); bool clear_snippet_marks(); }; diff --git a/src/source_clang.cpp b/src/source_clang.cpp index 4ec8f4d..e809be6 100644 --- a/src/source_clang.cpp +++ b/src/source_clang.cpp @@ -13,6 +13,7 @@ #include "info.hpp" #include "selection_dialog.hpp" #include "usages_clang.hpp" +#include "utility.hpp" const std::regex include_regex(R"(^[ \t]*#[ \t]*include[ \t]*[<"]([^<>"]+)[>"].*$)"); @@ -375,7 +376,7 @@ void Source::ClangViewParse::update_diagnostics() { std::string *c_header = nullptr; std::string *cpp_header = nullptr; for(auto &header : headers) { - if(!c_header && header.find(".h") != std::string::npos) + if(!c_header && ends_with(header, ".h")) c_header = &header; else if(!cpp_header) cpp_header = &header; @@ -393,17 +394,17 @@ void Source::ClangViewParse::update_diagnostics() { if(token.get_kind() == clangmm::Token::Kind::Identifier) { auto &token_offsets = clang_tokens_offsets[c]; if(static_cast(line) == token_offsets.first.line - 1 && static_cast(index) >= token_offsets.first.index - 1 && static_cast(index) <= token_offsets.second.index - 1) { - if(diagnostic.spelling.compare(0, 44, "implicit instantiation of undefined template") == 0) { + if(starts_with(diagnostic.spelling, "implicit instantiation of undefined template")) { auto cursor = token.get_cursor(); if(cursor.get_referenced()) { auto type_description = cursor.get_type_description(); bool has_std = false; if(is_cpp) { - if(type_description.compare(0, 5, "std::") == 0) { + if(starts_with(type_description, "std::")) { has_std = true; type_description.erase(0, 5); } - if(type_description.compare(0, 5, "__1::") == 0) + if(starts_with(type_description, "__1::")) type_description.erase(0, 5); auto pos = type_description.find('<'); if(pos != std::string::npos) @@ -413,13 +414,13 @@ void Source::ClangViewParse::update_diagnostics() { add_include_fixit(has_std, is_cpp && has_using_namespace_std(c), type_description); } } - if(diagnostic.spelling.compare(0, 17, "unknown type name") == 0 || - diagnostic.spelling.compare(0, 13, "no type named") == 0 || - diagnostic.spelling.compare(0, 15, "no member named") == 0 || - diagnostic.spelling.compare(0, 17, "no template named") == 0 || - diagnostic.spelling.compare(0, 28, "use of undeclared identifier") == 0 || - diagnostic.spelling.compare(0, 44, "implicit instantiation of undefined template") == 0 || - diagnostic.spelling.compare(0, 79, "no viable constructor or deduction guide for deduction of template arguments of") == 0) { + if(starts_with(diagnostic.spelling, "unknown type name") || + starts_with(diagnostic.spelling, "no type named") || + starts_with(diagnostic.spelling, "no member named") || + starts_with(diagnostic.spelling, "no template named") || + starts_with(diagnostic.spelling, "use of undeclared identifier") || + starts_with(diagnostic.spelling, "implicit instantiation of undefined template") || + starts_with(diagnostic.spelling, "no viable constructor or deduction guide for deduction of template arguments of")) { bool has_std = false; if(is_cpp) { if(token_string == "std" && c + 2 < clang_tokens->size() && (*clang_tokens)[c + 2].get_kind() == clangmm::Token::Kind::Identifier) { @@ -876,7 +877,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa if(kind != clangmm::CompletionChunk_Informative) { auto chunk_cstr = clangmm::String(clang_getCompletionChunkText(result.cx_completion_string, i)); if(kind == clangmm::CompletionChunk_TypedText) { - if(strlen(chunk_cstr.c_str) >= prefix.size() && prefix.compare(0, prefix.size(), chunk_cstr.c_str, prefix.size()) == 0) + if(starts_with(chunk_cstr.c_str, prefix)) match = true; else break; @@ -900,7 +901,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa LockGuard lock(snippets_mutex); if(snippets) { for(auto &snippet : *snippets) { - if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) { + if(starts_with(snippet.prefix, prefix)) { autocomplete.rows.emplace_back(snippet.prefix); completion_strings.emplace_back(nullptr); snippet_inserts.emplace(autocomplete.rows.size() - 1, snippet.body); @@ -929,9 +930,8 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa if(!completion_strings[index]) { // Insert snippet instead get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter()); - if(!hide_window) { + if(!hide_window) get_buffer()->insert(CompletionDialog::get()->start_mark->get_iter(), text); - } else insert_snippet(CompletionDialog::get()->start_mark->get_iter(), snippet_inserts[index]); return; @@ -1231,8 +1231,7 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file if(static_cast(source_location.get_offset().line) - 1 <= client_data->line_nr && filesystem::get_normal_path(source_location.get_path()) == client_data->file_path) { auto included_file_str = clangmm::to_string(clang_getFileName(included_file)); - if(included_file_str.size() >= client_data->sm_str.size() && - included_file_str.compare(included_file_str.size() - client_data->sm_str.size(), client_data->sm_str.size(), client_data->sm_str) == 0) { + if(ends_with(included_file_str, client_data->sm_str)) { client_data->found_include = included_file_str; break; } @@ -1784,7 +1783,7 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file auto it = data.end(); do { auto token_spelling = cursor.get_token_spelling(); - if(!token_spelling.empty() && token_spelling != "__1" && token_spelling.compare(0, 5, "__cxx") != 0) { + if(!token_spelling.empty() && token_spelling != "__1" && !starts_with(token_spelling, "__cxx")) { it = data.emplace(it, token_spelling); if(symbol.empty()) symbol = token_spelling; diff --git a/src/source_generic.cpp b/src/source_generic.cpp index 60eeab2..54df6ab 100644 --- a/src/source_generic.cpp +++ b/src/source_generic.cpp @@ -4,6 +4,7 @@ #include "selection_dialog.hpp" #include "snippets.hpp" #include "terminal.hpp" +#include "utility.hpp" #include Source::GenericView::GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr &language) : BaseView(file_path, language), View(file_path, language, true), autocomplete(this, interactive_completion, last_keyval, false) { @@ -260,7 +261,7 @@ void Source::GenericView::setup_autocomplete() { prefix = autocomplete.prefix; } for(auto &keyword : keywords) { - if(prefix.compare(0, prefix.size(), keyword, 0, prefix.size()) == 0) { + if(starts_with(keyword, prefix)) { autocomplete.rows.emplace_back(keyword); autocomplete_insert.emplace_back(keyword); autocomplete_comment.emplace_back(""); @@ -269,7 +270,8 @@ void Source::GenericView::setup_autocomplete() { { LockGuard lock(buffer_words_mutex); for(auto &buffer_word : buffer_words) { - if((show_prefix_buffer_word || buffer_word.first.size() > prefix.size()) && prefix.compare(0, prefix.size(), buffer_word.first, 0, prefix.size()) == 0 && + if((show_prefix_buffer_word || buffer_word.first.size() > prefix.size()) && + starts_with(buffer_word.first, prefix) && keywords.find(buffer_word.first) == keywords.end()) { autocomplete.rows.emplace_back(buffer_word.first); autocomplete_insert.emplace_back(buffer_word.first); @@ -280,7 +282,7 @@ void Source::GenericView::setup_autocomplete() { LockGuard lock(snippets_mutex); if(snippets) { for(auto &snippet : *snippets) { - if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) { + if(starts_with(snippet.prefix, prefix)) { autocomplete.rows.emplace_back(snippet.prefix); autocomplete_insert.emplace_back(snippet.body); autocomplete_comment.emplace_back(snippet.description); @@ -300,7 +302,7 @@ void Source::GenericView::setup_autocomplete() { }; autocomplete.on_select = [this](unsigned int index, const std::string &text, bool hide_window) { - Glib::ustring insert = hide_window ? autocomplete_insert[index] : text; + const auto &insert = hide_window ? autocomplete_insert[index] : text; get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter()); diff --git a/src/source_language_protocol.cpp b/src/source_language_protocol.cpp index c0e491c..6121f34 100644 --- a/src/source_language_protocol.cpp +++ b/src/source_language_protocol.cpp @@ -10,6 +10,7 @@ #endif #include "config.hpp" #include "menu.hpp" +#include "utility.hpp" #include #include #include @@ -194,7 +195,7 @@ void LanguageProtocol::Client::parse_server_message() { std::string line; while(!header_read && std::getline(server_message_stream, line)) { if(!line.empty() && line != "\r") { - if(line.compare(0, 16, "Content-Length: ") == 0) { + if(starts_with(line, "Content-Length: ")) { try { server_message_size = static_cast(std::stoul(line.substr(16))); } @@ -1607,7 +1608,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { LockGuard lock(autocomplete->prefix_mutex); prefix = autocomplete->prefix; } - if(prefix.compare(0, prefix.size(), label, 0, prefix.size()) == 0) { + if(starts_with(label, prefix)) { autocomplete->rows.emplace_back(std::move(label)); if(!plaintext.empty() && detail != plaintext) { if(!detail.empty()) @@ -1628,7 +1629,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { LockGuard lock(snippets_mutex); if(snippets) { for(auto &snippet : *snippets) { - if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) { + if(starts_with(snippet.prefix, prefix)) { autocomplete->rows.emplace_back(snippet.prefix); autocomplete_rows.emplace_back(AutocompleteRow{snippet.body, snippet.description, {}}); } @@ -1652,7 +1653,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { }; autocomplete->on_select = [this](unsigned int index, const std::string &text, bool hide_window) { - Glib::ustring insert = hide_window ? autocomplete_rows[index].insert : text; + auto insert = hide_window ? autocomplete_rows[index].insert : text; get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter()); @@ -1660,9 +1661,8 @@ void Source::LanguageProtocolView::setup_autocomplete() { auto iter = get_buffer()->get_insert()->get_iter(); if(*iter == '(' || *iter == '<') { auto bracket_pos = insert.find(*iter); - if(bracket_pos != std::string::npos) { - insert = insert.substr(0, bracket_pos); - } + if(bracket_pos != std::string::npos) + insert.erase(bracket_pos); } if(hide_window) { diff --git a/src/tooltips.cpp b/src/tooltips.cpp index b98b75d..027b079 100644 --- a/src/tooltips.cpp +++ b/src/tooltips.cpp @@ -4,6 +4,7 @@ #include "info.hpp" #include "notebook.hpp" #include "selection_dialog.hpp" +#include "utility.hpp" #include #include @@ -152,7 +153,7 @@ void Tooltip::show(bool disregard_drawn, const std::function &on_motion) if(link.empty()) link = text; - if(link.compare(0, 7, "http://") == 0 || link.compare(0, 8, "https://") == 0) { + if(starts_with(link, "http://") || starts_with(link, "https://")) { Notebook::get().open_uri(link); return true; } @@ -477,7 +478,7 @@ void Tooltip::insert_markdown(const std::string &input) { auto start = i; for(; i < to; i++) { if(!unescape(i)) { - if(input.compare(i, prefix.size(), prefix) == 0) { + if(starts_with(input, i, prefix)) { if(i - 1 > from && is_whitespace_character(i - 1)) { // Do not emphasis _test in: _test _test_ i = i_saved; return false; @@ -507,7 +508,7 @@ void Tooltip::insert_markdown(const std::string &input) { }; auto insert_strikethrough = [&] { - if(input.compare(i, 2, "~~") == 0) { + if(starts_with(input, i, "~~")) { insert_with_links_tagged(partial); partial.clear(); auto i_saved = i; @@ -515,7 +516,7 @@ void Tooltip::insert_markdown(const std::string &input) { if(i < to) { auto start = i; for(; i < to; i++) { - if(!unescape(i) && input.compare(i, 2, "~~") == 0) + if(!unescape(i) && starts_with(input, i, "~~")) break; } if(i == to) { @@ -701,13 +702,13 @@ void Tooltip::insert_markdown(const std::string &input) { auto insert_code_block = [&] { - if(input.compare(i, 3, "```") == 0) { + if(starts_with(input, i, "```")) { auto i_saved = i; if(forward_to({'\n'})) { i++; if(i < input.size()) { auto start = i; - while(i < input.size() && !(input[i - 1] == '\n' && input.compare(i, 3, "```") == 0)) + while(i < input.size() && !(input[i - 1] == '\n' && starts_with(input, i, "```"))) i++; if(i == input.size()) { i = i_saved; @@ -769,7 +770,7 @@ void Tooltip::insert_markdown(const std::string &input) { // Insert paragraph: auto start = i; for(; forward_to({'\n'}); i++) { - if(i + 1 < input.size() && (input[i + 1] == '\n' || input[i + 1] == '#' || input.compare(i + 1, 3, "```") == 0)) + if(i + 1 < input.size() && (input[i + 1] == '\n' || input[i + 1] == '#' || starts_with(input, i + 1, "```"))) break; } insert_text(start, i); diff --git a/src/utility.cpp b/src/utility.cpp index 8689c68..8e1c894 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -1,6 +1,76 @@ #include "utility.hpp" +#include ScopeGuard::~ScopeGuard() { if(on_exit) on_exit(); } + +bool starts_with(const char *str, const std::string &test) noexcept { + for(size_t i = 0; i < test.size(); ++i) { + if(*str == '\0') + return false; + if(*str != test[i]) + return false; + ++str; + } + return true; +} + +bool starts_with(const char *str, const char *test) noexcept { + for(; *test != '\0'; ++test) { + if(*str == '\0') + return false; + if(*str != *test) + return false; + ++str; + } + return true; +} + +bool starts_with(const std::string &str, const std::string &test) noexcept { + return str.compare(0, test.size(), test) == 0; +} + +bool starts_with(const std::string &str, const char *test) noexcept { + for(size_t i = 0; i < str.size(); ++i) { + if(*test == '\0') + return true; + if(str[i] != *test) + return false; + ++test; + } + return *test == '\0'; +} + +bool starts_with(const std::string &str, size_t pos, const std::string &test) noexcept { + if(pos > str.size()) + return false; + return str.compare(pos, test.size(), test) == 0; +} + +bool starts_with(const std::string &str, size_t pos, const char *test) noexcept { + if(pos > str.size()) + return false; + for(size_t i = pos; i < str.size(); ++i) { + if(*test == '\0') + return true; + if(str[i] != *test) + return false; + ++test; + } + return *test == '\0'; +} + +bool ends_with(const std::string &str, const std::string &test) noexcept { + if(test.size() > str.size()) + return false; + return str.compare(str.size() - test.size(), test.size(), test) == 0; +} + +bool ends_with(const std::string &str, const char *test) noexcept { + auto test_size = strlen(test); + if(test_size > str.size()) + return false; + return str.compare(str.size() - test_size, test_size, test) == 0; +} diff --git a/src/utility.hpp b/src/utility.hpp index 3f0e1a0..e143b8d 100644 --- a/src/utility.hpp +++ b/src/utility.hpp @@ -1,8 +1,19 @@ #pragma once #include +#include class ScopeGuard { public: std::function on_exit; ~ScopeGuard(); }; + +bool starts_with(const char *str, const std::string &test) noexcept; +bool starts_with(const char *str, const char *test) noexcept; +bool starts_with(const std::string &str, const std::string &test) noexcept; +bool starts_with(const std::string &str, const char *test) noexcept; +bool starts_with(const std::string &str, size_t pos, const std::string &test) noexcept; +bool starts_with(const std::string &str, size_t pos, const char *test) noexcept; + +bool ends_with(const std::string &str, const std::string &test) noexcept; +bool ends_with(const std::string &str, const char *test) noexcept; diff --git a/src/window.cpp b/src/window.cpp index e794097..9c19682 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -830,8 +830,6 @@ void Window::set_menu_actions() { }); menu.add_action("source_find_pattern", [this]() { - std::string excludes = "--exclude-dir=node_modules"; - EntryBox::get().clear(); EntryBox::get().entries.emplace_back(last_find_pattern, [this](const std::string &pattern_) { @@ -845,14 +843,14 @@ void Window::set_menu_actions() { return; } - if(auto view = Notebook::get().get_current_view()) + auto view = Notebook::get().get_current_view(); + if(view) SelectionDialog::create(view, true, true); else SelectionDialog::create(true, true); std::string current_path; unsigned int current_line = 0; - auto view = Notebook::get().get_current_view(); if(view) { current_path = filesystem::get_relative_path(view->file_path, grep->project_path).string(); current_line = view->get_buffer()->get_insert()->get_iter().get_line(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 90dbf07..6a88646 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -84,3 +84,6 @@ add_executable(tooltips_test tooltips_test.cpp $) target_link_libraries(tooltips_test juci_shared) add_test(tooltips_test tooltips_test) +add_executable(utility_test utility_test.cpp $) +target_link_libraries(utility_test juci_shared) +add_test(utility_test utility_test) diff --git a/tests/utility_test.cpp b/tests/utility_test.cpp new file mode 100644 index 0000000..ae0e319 --- /dev/null +++ b/tests/utility_test.cpp @@ -0,0 +1,88 @@ +#include "utility.hpp" +#include + +int main() { + static bool scope_exit = false; + { + ScopeGuard guard{[] { + scope_exit = true; + }}; + g_assert(!scope_exit); + } + g_assert(scope_exit); + + std::string empty; + std::string test("test"); + std::string testtest("testtest"); + + g_assert(starts_with("", empty)); + g_assert(starts_with("", "")); + g_assert(starts_with(empty, "")); + g_assert(starts_with(empty, empty)); + g_assert(starts_with(empty, 0, "")); + g_assert(starts_with(empty, 0, empty)); + g_assert(ends_with(empty, "")); + g_assert(ends_with(empty, empty)); + + g_assert(starts_with(test.c_str(), empty)); + g_assert(starts_with(test.c_str(), "")); + g_assert(starts_with(test, "")); + g_assert(starts_with(test, empty)); + g_assert(starts_with(test, 0, "")); + g_assert(starts_with(test, 0, empty)); + g_assert(ends_with(test, "")); + g_assert(ends_with(test, empty)); + + g_assert(!starts_with(empty, 10, "")); + g_assert(!starts_with(empty, 10, empty)); + + g_assert(!starts_with(test, 10, "")); + g_assert(!starts_with(test, 10, empty)); + + g_assert(!starts_with(test, 10, test.c_str())); + g_assert(!starts_with(test, 10, test)); + + g_assert(starts_with(test, 2, test.c_str() + 2)); + g_assert(starts_with(test, 2, test.substr(2))); + + g_assert(ends_with(test, test.c_str() + 2)); + g_assert(ends_with(test, test.substr(2))); + + g_assert(starts_with(test.c_str(), test)); + g_assert(starts_with(test.c_str(), test.c_str())); + g_assert(starts_with(test, test.c_str())); + g_assert(starts_with(test, test)); + g_assert(starts_with(test, 0, test.c_str())); + g_assert(starts_with(test, 0, test)); + g_assert(ends_with(test, test.c_str())); + g_assert(ends_with(test, test)); + + g_assert(starts_with(testtest.c_str(), test)); + g_assert(starts_with(testtest.c_str(), test.c_str())); + g_assert(starts_with(testtest, test.c_str())); + g_assert(starts_with(testtest, test)); + g_assert(starts_with(testtest, 0, test.c_str())); + g_assert(starts_with(testtest, 0, test)); + g_assert(ends_with(testtest, test.c_str())); + g_assert(ends_with(testtest, test)); + g_assert(ends_with(testtest, "ttest")); + g_assert(ends_with(testtest, std::string("ttest"))); + + g_assert(!starts_with(test.c_str(), testtest)); + g_assert(!starts_with(test.c_str(), testtest.c_str())); + g_assert(!starts_with(test, testtest.c_str())); + g_assert(!starts_with(test, testtest)); + g_assert(!starts_with(test, 0, testtest.c_str())); + g_assert(!starts_with(test, 0, testtest)); + g_assert(!ends_with(test, testtest.c_str())); + g_assert(!ends_with(test, testtest)); + + g_assert(!starts_with(empty.c_str(), test)); + g_assert(!starts_with(empty.c_str(), test.c_str())); + g_assert(!starts_with(empty, test.c_str())); + g_assert(!starts_with(empty, test)); + g_assert(!starts_with(empty, 0, test.c_str())); + g_assert(!starts_with(empty, 0, test)); + g_assert(!ends_with(empty, test.c_str())); + g_assert(!ends_with(empty, test)); +} \ No newline at end of file