Browse Source

Added and made use of string functions starts_with and ends_with, and minor cleanup for selection window code

pipelines/235045657
eidheim 6 years ago
parent
commit
60195fa0ec
  1. 3
      src/cmake.cpp
  2. 3
      src/compile_commands.cpp
  3. 5
      src/ctags.cpp
  4. 3
      src/debug_lldb.cpp
  5. 8
      src/filesystem.cpp
  6. 3
      src/grep.cpp
  7. 3
      src/juci.cpp
  8. 3
      src/meson.cpp
  9. 19
      src/selection_dialog.cpp
  10. 7
      src/source.cpp
  11. 45
      src/source_base.cpp
  12. 2
      src/source_base.hpp
  13. 35
      src/source_clang.cpp
  14. 10
      src/source_generic.cpp
  15. 14
      src/source_language_protocol.cpp
  16. 15
      src/tooltips.cpp
  17. 70
      src/utility.cpp
  18. 11
      src/utility.hpp
  19. 6
      src/window.cpp
  20. 3
      tests/CMakeLists.txt
  21. 88
      tests/utility_test.cpp

3
src/cmake.cpp

@ -4,6 +4,7 @@
#include "dialogs.hpp" #include "dialogs.hpp"
#include "filesystem.hpp" #include "filesystem.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp"
#include <regex> #include <regex>
CMake::CMake(const boost::filesystem::path &path) { CMake::CMake(const boost::filesystem::path &path) {
@ -113,7 +114,7 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui
std::vector<boost::filesystem::path> cmake_executables; std::vector<boost::filesystem::path> cmake_executables;
for(auto &parameter : parameters) { for(auto &parameter : parameters) {
if(parameter.second.size() > 1 && parameter.second[0].size() > 0 && parameter.second[0].compare(0, 2, "${") != 0) { if(parameter.second.size() > 1 && parameter.second[0].size() > 0 && !starts_with(parameter.second[0], "${")) {
auto executable = (parameter.first.parent_path() / parameter.second[0]).string(); auto executable = (parameter.first.parent_path() / parameter.second[0]).string();
auto project_path_str = project_path.string(); auto project_path_str = project_path.string();
size_t pos = executable.find(project_path_str); size_t pos = executable.find(project_path_str);

3
src/compile_commands.cpp

@ -2,6 +2,7 @@
#include "clangmm.hpp" #include "clangmm.hpp"
#include "config.hpp" #include "config.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp"
#include <algorithm> #include <algorithm>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <regex> #include <regex>
@ -14,7 +15,7 @@ CompileCommands::FindSystemIncludePaths::FindSystemIncludePaths() {
return; return;
std::string line; std::string line;
while(std::getline(stdout_stream, 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)) { while(std::getline(stdout_stream, line)) {
if(!line.empty() && line[0] == ' ') { if(!line.empty() && line[0] == ' ') {
#ifdef _WIN32 #ifdef _WIN32

5
src/ctags.cpp

@ -3,6 +3,7 @@
#include "filesystem.hpp" #include "filesystem.hpp"
#include "project_build.hpp" #include "project_build.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp"
#include <climits> #include <climits>
#include <vector> #include <vector>
@ -60,7 +61,7 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup) c
return location; return location;
} }
location.symbol = line.substr(0, symbol_end); 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]; auto &chr = location.symbol[9];
if(!((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr == '_')) if(!((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr == '_'))
location.symbol.erase(8, 1); location.symbol.erase(8, 1);
@ -151,7 +152,7 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup) c
bool escaped = false; bool escaped = false;
for(size_t i = 0; i < location.source.size(); i++) { for(size_t i = 0; i < location.source.size(); i++) {
if(!escaped) { if(!escaped) {
if(location.source.compare(i, symbol.size(), symbol) == 0) { if(starts_with(location.source, i, symbol)) {
location.source.insert(i + symbol.size(), "</b>"); location.source.insert(i + symbol.size(), "</b>");
location.source.insert(i, "<b>"); location.source.insert(i, "<b>");
i += 7 + symbol.size() - 1; i += 7 + symbol.size() - 1;

3
src/debug_lldb.cpp

@ -7,6 +7,7 @@
#include "filesystem.hpp" #include "filesystem.hpp"
#include "process.hpp" #include "process.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <iostream> #include <iostream>
@ -451,7 +452,7 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil
if(value_decl_path == file_path) { if(value_decl_path == file_path) {
value.GetDescription(stream); value.GetDescription(stream);
std::string variable_value = stream.GetData(); 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 variable_value.pop_back(); // Remove newline at end of string
return variable_value; return variable_value;
} }

8
src/filesystem.cpp

@ -1,10 +1,10 @@
#include "filesystem.hpp"
#include "utility.hpp"
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "filesystem.hpp"
//Only use on small files //Only use on small files
std::string filesystem::read(const std::string &path) { std::string filesystem::read(const std::string &path) {
std::string str; 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) { for(boost::filesystem::directory_iterator it(path), end; it != end; ++it) {
auto it_path = it->path(); auto it_path = it->path();
auto it_path_filename_str = it_path.filename().string(); auto it_path_filename_str = it_path.filename().string();
if(!it_path_filename_str.empty() && it_path_filename_str.compare(0, executable_name_str.size(), executable_name_str) == 0) { if(starts_with(it_path_filename_str, executable_name_str)) {
if(it_path > executable && if(it_path > executable &&
((it_path_filename_str.size() > executable_name_str.size() && ((it_path_filename_str.size() > executable_name_str.size() &&
it_path_filename_str[executable_name_str.size()] >= '0' && it_path_filename_str[executable_name_str.size()] >= '0' &&
@ -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) { boost::filesystem::path filesystem::get_path_from_uri(const std::string &uri) {
std::string encoded; std::string encoded;
if(uri.compare(0, 7, "file://") == 0) if(starts_with(uri, "file://"))
encoded = uri.substr(7); encoded = uri.substr(7);
else else
encoded = uri; encoded = uri;

3
src/grep.cpp

@ -3,6 +3,7 @@
#include "filesystem.hpp" #include "filesystem.hpp"
#include "project_build.hpp" #include "project_build.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp"
Grep::Grep(const boost::filesystem::path &path, const std::string &pattern, bool case_sensitive, bool extended_regex) { Grep::Grep(const boost::filesystem::path &path, const std::string &pattern, bool case_sensitive, bool extended_regex) {
auto build = Project::Build::create(path); 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) { if(color_codes_to_markup) {
std::string escaped = Glib::Markup::escape_text(line); std::string escaped = Glib::Markup::escape_text(line);
auto decode_escape_sequence = [](const std::string &line, size_t &i) -> bool { auto decode_escape_sequence = [](const std::string &line, size_t &i) -> bool {
if(line.compare(i, 7, "&#x1b;[") != 0) if(!starts_with(line, i, "&#x1b;["))
return false; return false;
i += 7; i += 7;
for(; i < line.size(); ++i) { for(; i < line.size(); ++i) {

3
src/juci.cpp

@ -5,6 +5,7 @@
#include "menu.hpp" #include "menu.hpp"
#include "notebook.hpp" #include "notebook.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp"
#include "window.hpp" #include "window.hpp"
#ifndef _WIN32 #ifndef _WIN32
#include <csignal> #include <csignal>
@ -70,7 +71,7 @@ void Application::on_activate() {
else { else {
std::string files_in_directory; std::string files_in_directory;
for(auto it = files.begin(); it != files.end();) { for(auto it = files.begin(); it != files.end();) {
if(it->first.generic_string().compare(0, directory.generic_string().size() + 1, directory.generic_string() + '/') == 0) { if(filesystem::file_in_path(it->first, directory)) {
files_in_directory += " " + filesystem::escape_argument(it->first.string()); files_in_directory += " " + filesystem::escape_argument(it->first.string());
it = files.erase(it); it = files.erase(it);
} }

3
src/meson.cpp

@ -4,6 +4,7 @@
#include "dialogs.hpp" #include "dialogs.hpp"
#include "filesystem.hpp" #include "filesystem.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp"
#include <regex> #include <regex>
Meson::Meson(const boost::filesystem::path &path) { 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()) { if(!values.empty()) {
size_t pos; size_t pos;
if((pos = values[0].find('@')) != std::string::npos) { if((pos = values[0].find('@')) != std::string::npos) {
if(pos + 1 < values[0].size() && values[0].compare(pos + 1, 3, "exe") == 0) { if(starts_with(values[0], pos + 1, "exe")) {
auto executable = build_path / values[0].substr(0, pos); auto executable = build_path / values[0].substr(0, pos);
if(command_file == file_path) if(command_file == file_path)
return executable; return executable;

19
src/selection_dialog.cpp

@ -254,13 +254,8 @@ SelectionDialog::SelectionDialog(Gtk::TextView *text_view, const Glib::RefPtr<Gt
auto activate = [this]() { auto activate = [this]() {
auto it = list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
if(on_select && it) { if(on_select && it)
auto index = it->get_value(list_view_text.column_record.index); on_select(it->get_value(list_view_text.column_record.index), it->get_value(list_view_text.column_record.text), true);
auto text = it->get_value(list_view_text.column_record.text);
hide();
on_select(index, text, true);
}
else
hide(); hide();
}; };
search_entry.signal_activate().connect([activate]() { search_entry.signal_activate().connect([activate]() {
@ -390,11 +385,8 @@ void CompletionDialog::select(bool hide_window) {
row_in_entry = true; row_in_entry = true;
auto it = list_view_text.get_selection()->get_selected(); auto it = list_view_text.get_selection()->get_selected();
if(on_select && it) { if(on_select && it)
auto index = it->get_value(list_view_text.column_record.index); on_select(it->get_value(list_view_text.column_record.index), it->get_value(list_view_text.column_record.text), hide_window);
auto text = it->get_value(list_view_text.column_record.text);
on_select(index, text, hide_window);
}
if(hide_window) if(hide_window)
hide(); 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) if(key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_KP_Down || key->keyval == GDK_KEY_Up || key->keyval == GDK_KEY_KP_Up)
return false; return false;
if(show_offset > text_view->get_buffer()->get_insert()->get_iter().get_offset()) { if(show_offset > text_view->get_buffer()->get_insert()->get_iter().get_offset())
hide(); hide();
}
else { else {
auto text = text_view->get_buffer()->get_text(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter()); auto text = text_view->get_buffer()->get_text(start_mark->get_iter(), text_view->get_buffer()->get_insert()->get_iter());
search_entry.set_text(text); search_entry.set_text(text);

7
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) { 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) { 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()); end = get_buffer()->get_iter_at_line(end.get_line());
break; 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)) { if(tabs.size() % tab_size == 1 && !start_iter.ends_line() && !is_code_iter(start_iter)) {
auto end_of_line_iter = start_iter; auto end_of_line_iter = start_iter;
end_of_line_iter.forward_to_line_end(); end_of_line_iter.forward_to_line_end();
auto line = get_buffer()->get_text(tabs_end_iter, end_of_line_iter); if(starts_with(get_buffer()->get_text(tabs_end_iter, end_of_line_iter).raw(), "*/")) {
if(!line.empty() && line.raw().compare(0, 2, "*/") == 0) {
tabs.pop_back(); tabs.pop_back();
get_buffer()->insert_at_cursor('\n' + tabs); get_buffer()->insert_at_cursor('\n' + tabs);
scroll_to(get_buffer()->get_insert()); scroll_to(get_buffer()->get_insert());

45
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<size_t, std::vector<std::pair<size_t, size_t>>> arguments_offsets; std::map<size_t, std::vector<std::pair<size_t, size_t>>> arguments_offsets;
Glib::ustring insert; std::string insert;
insert.reserve(snippet.size()); insert.reserve(snippet.size());
size_t i = 0; size_t i = 0;
int number; int number;
@ -1058,12 +1058,18 @@ void Source::BaseView::insert_snippet(Gtk::TextIter iter, const Glib::ustring &s
return false; 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; 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()) if(i >= snippet.size())
throw std::out_of_range("unexpected end"); throw std::out_of_range("unexpected end");
if(snippet.compare(i, 16, "TM_SELECTED_TEXT") == 0) { if(compare_variable("TM_SELECTED_TEXT")) {
i += 16;
Gtk::TextIter start, end; Gtk::TextIter start, end;
if(get_buffer()->get_selection_bounds(start, end)) { if(get_buffer()->get_selection_bounds(start, end)) {
insert += get_buffer()->get_text(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; return false;
} }
else if(snippet.compare(i, 15, "TM_CURRENT_LINE") == 0) { else if(compare_variable("TM_CURRENT_LINE")) {
i += 15;
auto start = get_buffer()->get_iter_at_line(iter.get_line()); auto start = get_buffer()->get_iter_at_line(iter.get_line());
auto end = get_iter_at_line_end(iter.get_line()); auto end = get_iter_at_line_end(iter.get_line());
insert += get_buffer()->get_text(start, end); insert += get_buffer()->get_text(start, end);
erase_line = true; erase_line = true;
return true; return true;
} }
else if(snippet.compare(i, 15, "TM_CURRENT_WORD") == 0) { else if(compare_variable("TM_CURRENT_WORD")) {
i += 15;
if(is_token_char(*iter)) { if(is_token_char(*iter)) {
auto token_iters = get_token_iters(iter); auto token_iters = get_token_iters(iter);
insert += get_buffer()->get_text(token_iters.first, token_iters.second); 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; return false;
} }
else if(snippet.compare(i, 13, "TM_LINE_INDEX") == 0) { else if(compare_variable("TM_LINE_INDEX")) {
i += 13;
insert += std::to_string(iter.get_line()); insert += std::to_string(iter.get_line());
return true; return true;
} }
else if(snippet.compare(i, 14, "TM_LINE_NUMBER") == 0) { else if(compare_variable("TM_LINE_NUMBER")) {
i += 14;
insert += std::to_string(iter.get_line() + 1); insert += std::to_string(iter.get_line() + 1);
return true; return true;
} }
else if(snippet.compare(i, 16, "TM_FILENAME_BASE") == 0) { else if(compare_variable("TM_FILENAME_BASE")) {
i += 16;
insert += file_path.stem().string(); insert += file_path.stem().string();
return true; return true;
} }
else if(snippet.compare(i, 11, "TM_FILENAME") == 0) { else if(compare_variable("TM_FILENAME")) {
i += 11;
insert += file_path.filename().string(); insert += file_path.filename().string();
return true; return true;
} }
else if(snippet.compare(i, 12, "TM_DIRECTORY") == 0) { else if(compare_variable("TM_DIRECTORY")) {
i += 12;
insert += file_path.parent_path().string(); insert += file_path.parent_path().string();
return true; return true;
} }
else if(snippet.compare(i, 11, "TM_FILEPATH") == 0) { else if(compare_variable("TM_FILEPATH")) {
i += 11;
insert += file_path.string(); insert += file_path.string();
return true; return true;
} }
else if(snippet.compare(i, 9, "CLIPBOARD") == 0) { else if(compare_variable("CLIPBOARD")) {
i += 9;
insert += Gtk::Clipboard::get()->wait_for_text(); insert += Gtk::Clipboard::get()->wait_for_text();
return true; return true;
} }
@ -1144,7 +1141,7 @@ void Source::BaseView::insert_snippet(Gtk::TextIter iter, const Glib::ustring &s
if(snippet.at(i) == '{') { if(snippet.at(i) == '{') {
++i; ++i;
if(parse_number()) { if(parse_number()) {
Glib::ustring placeholder; std::string placeholder;
if(snippet.at(i) == ':') { if(snippet.at(i) == ':') {
++i; ++i;
for(; snippet.at(i) != '}'; ++i) for(; snippet.at(i) != '}'; ++i)

2
src/source_base.hpp

@ -152,7 +152,7 @@ namespace Source {
std::vector<Snippets::Snippet> *snippets GUARDED_BY(snippets_mutex) = nullptr; std::vector<Snippets::Snippet> *snippets GUARDED_BY(snippets_mutex) = nullptr;
std::list<std::vector<std::pair<Glib::RefPtr<Gtk::TextBuffer::Mark>, Glib::RefPtr<Gtk::TextBuffer::Mark>>>> snippets_marks; std::list<std::vector<std::pair<Glib::RefPtr<Gtk::TextBuffer::Mark>, Glib::RefPtr<Gtk::TextBuffer::Mark>>>> snippets_marks;
Glib::RefPtr<Gtk::TextTag> snippet_argument_tag; Glib::RefPtr<Gtk::TextTag> 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 select_snippet_argument();
bool clear_snippet_marks(); bool clear_snippet_marks();
}; };

35
src/source_clang.cpp

@ -13,6 +13,7 @@
#include "info.hpp" #include "info.hpp"
#include "selection_dialog.hpp" #include "selection_dialog.hpp"
#include "usages_clang.hpp" #include "usages_clang.hpp"
#include "utility.hpp"
const std::regex include_regex(R"(^[ \t]*#[ \t]*include[ \t]*[<"]([^<>"]+)[>"].*$)"); 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 *c_header = nullptr;
std::string *cpp_header = nullptr; std::string *cpp_header = nullptr;
for(auto &header : headers) { for(auto &header : headers) {
if(!c_header && header.find(".h") != std::string::npos) if(!c_header && ends_with(header, ".h"))
c_header = &header; c_header = &header;
else if(!cpp_header) else if(!cpp_header)
cpp_header = &header; cpp_header = &header;
@ -393,17 +394,17 @@ void Source::ClangViewParse::update_diagnostics() {
if(token.get_kind() == clangmm::Token::Kind::Identifier) { if(token.get_kind() == clangmm::Token::Kind::Identifier) {
auto &token_offsets = clang_tokens_offsets[c]; auto &token_offsets = clang_tokens_offsets[c];
if(static_cast<unsigned int>(line) == token_offsets.first.line - 1 && static_cast<unsigned int>(index) >= token_offsets.first.index - 1 && static_cast<unsigned int>(index) <= token_offsets.second.index - 1) { if(static_cast<unsigned int>(line) == token_offsets.first.line - 1 && static_cast<unsigned int>(index) >= token_offsets.first.index - 1 && static_cast<unsigned int>(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(); auto cursor = token.get_cursor();
if(cursor.get_referenced()) { if(cursor.get_referenced()) {
auto type_description = cursor.get_type_description(); auto type_description = cursor.get_type_description();
bool has_std = false; bool has_std = false;
if(is_cpp) { if(is_cpp) {
if(type_description.compare(0, 5, "std::") == 0) { if(starts_with(type_description, "std::")) {
has_std = true; has_std = true;
type_description.erase(0, 5); type_description.erase(0, 5);
} }
if(type_description.compare(0, 5, "__1::") == 0) if(starts_with(type_description, "__1::"))
type_description.erase(0, 5); type_description.erase(0, 5);
auto pos = type_description.find('<'); auto pos = type_description.find('<');
if(pos != std::string::npos) 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); add_include_fixit(has_std, is_cpp && has_using_namespace_std(c), type_description);
} }
} }
if(diagnostic.spelling.compare(0, 17, "unknown type name") == 0 || if(starts_with(diagnostic.spelling, "unknown type name") ||
diagnostic.spelling.compare(0, 13, "no type named") == 0 || starts_with(diagnostic.spelling, "no type named") ||
diagnostic.spelling.compare(0, 15, "no member named") == 0 || starts_with(diagnostic.spelling, "no member named") ||
diagnostic.spelling.compare(0, 17, "no template named") == 0 || starts_with(diagnostic.spelling, "no template named") ||
diagnostic.spelling.compare(0, 28, "use of undeclared identifier") == 0 || starts_with(diagnostic.spelling, "use of undeclared identifier") ||
diagnostic.spelling.compare(0, 44, "implicit instantiation of undefined template") == 0 || starts_with(diagnostic.spelling, "implicit instantiation of undefined template") ||
diagnostic.spelling.compare(0, 79, "no viable constructor or deduction guide for deduction of template arguments of") == 0) { starts_with(diagnostic.spelling, "no viable constructor or deduction guide for deduction of template arguments of")) {
bool has_std = false; bool has_std = false;
if(is_cpp) { if(is_cpp) {
if(token_string == "std" && c + 2 < clang_tokens->size() && (*clang_tokens)[c + 2].get_kind() == clangmm::Token::Kind::Identifier) { 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) { if(kind != clangmm::CompletionChunk_Informative) {
auto chunk_cstr = clangmm::String(clang_getCompletionChunkText(result.cx_completion_string, i)); auto chunk_cstr = clangmm::String(clang_getCompletionChunkText(result.cx_completion_string, i));
if(kind == clangmm::CompletionChunk_TypedText) { 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; match = true;
else else
break; break;
@ -900,7 +901,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
LockGuard lock(snippets_mutex); LockGuard lock(snippets_mutex);
if(snippets) { if(snippets) {
for(auto &snippet : *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(snippet.prefix);
completion_strings.emplace_back(nullptr); completion_strings.emplace_back(nullptr);
snippet_inserts.emplace(autocomplete.rows.size() - 1, snippet.body); 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 if(!completion_strings[index]) { // Insert snippet instead
get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter()); 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); get_buffer()->insert(CompletionDialog::get()->start_mark->get_iter(), text);
}
else else
insert_snippet(CompletionDialog::get()->start_mark->get_iter(), snippet_inserts[index]); insert_snippet(CompletionDialog::get()->start_mark->get_iter(), snippet_inserts[index]);
return; return;
@ -1231,8 +1231,7 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
if(static_cast<int>(source_location.get_offset().line) - 1 <= client_data->line_nr && if(static_cast<int>(source_location.get_offset().line) - 1 <= client_data->line_nr &&
filesystem::get_normal_path(source_location.get_path()) == client_data->file_path) { filesystem::get_normal_path(source_location.get_path()) == client_data->file_path) {
auto included_file_str = clangmm::to_string(clang_getFileName(included_file)); auto included_file_str = clangmm::to_string(clang_getFileName(included_file));
if(included_file_str.size() >= client_data->sm_str.size() && if(ends_with(included_file_str, client_data->sm_str)) {
included_file_str.compare(included_file_str.size() - client_data->sm_str.size(), client_data->sm_str.size(), client_data->sm_str) == 0) {
client_data->found_include = included_file_str; client_data->found_include = included_file_str;
break; break;
} }
@ -1784,7 +1783,7 @@ Source::ClangViewRefactor::ClangViewRefactor(const boost::filesystem::path &file
auto it = data.end(); auto it = data.end();
do { do {
auto token_spelling = cursor.get_token_spelling(); 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); it = data.emplace(it, token_spelling);
if(symbol.empty()) if(symbol.empty())
symbol = token_spelling; symbol = token_spelling;

10
src/source_generic.cpp

@ -4,6 +4,7 @@
#include "selection_dialog.hpp" #include "selection_dialog.hpp"
#include "snippets.hpp" #include "snippets.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp"
#include <algorithm> #include <algorithm>
Source::GenericView::GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language) : BaseView(file_path, language), View(file_path, language, true), autocomplete(this, interactive_completion, last_keyval, false) { Source::GenericView::GenericView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &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; prefix = autocomplete.prefix;
} }
for(auto &keyword : keywords) { 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.rows.emplace_back(keyword);
autocomplete_insert.emplace_back(keyword); autocomplete_insert.emplace_back(keyword);
autocomplete_comment.emplace_back(""); autocomplete_comment.emplace_back("");
@ -269,7 +270,8 @@ void Source::GenericView::setup_autocomplete() {
{ {
LockGuard lock(buffer_words_mutex); LockGuard lock(buffer_words_mutex);
for(auto &buffer_word : buffer_words) { 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()) { keywords.find(buffer_word.first) == keywords.end()) {
autocomplete.rows.emplace_back(buffer_word.first); autocomplete.rows.emplace_back(buffer_word.first);
autocomplete_insert.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); LockGuard lock(snippets_mutex);
if(snippets) { if(snippets) {
for(auto &snippet : *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(snippet.prefix);
autocomplete_insert.emplace_back(snippet.body); autocomplete_insert.emplace_back(snippet.body);
autocomplete_comment.emplace_back(snippet.description); 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) { 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()); get_buffer()->erase(CompletionDialog::get()->start_mark->get_iter(), get_buffer()->get_insert()->get_iter());

14
src/source_language_protocol.cpp

@ -10,6 +10,7 @@
#endif #endif
#include "config.hpp" #include "config.hpp"
#include "menu.hpp" #include "menu.hpp"
#include "utility.hpp"
#include <future> #include <future>
#include <limits> #include <limits>
#include <regex> #include <regex>
@ -194,7 +195,7 @@ void LanguageProtocol::Client::parse_server_message() {
std::string line; std::string line;
while(!header_read && std::getline(server_message_stream, line)) { while(!header_read && std::getline(server_message_stream, line)) {
if(!line.empty() && line != "\r") { if(!line.empty() && line != "\r") {
if(line.compare(0, 16, "Content-Length: ") == 0) { if(starts_with(line, "Content-Length: ")) {
try { try {
server_message_size = static_cast<size_t>(std::stoul(line.substr(16))); server_message_size = static_cast<size_t>(std::stoul(line.substr(16)));
} }
@ -1607,7 +1608,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
LockGuard lock(autocomplete->prefix_mutex); LockGuard lock(autocomplete->prefix_mutex);
prefix = autocomplete->prefix; 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)); autocomplete->rows.emplace_back(std::move(label));
if(!plaintext.empty() && detail != plaintext) { if(!plaintext.empty() && detail != plaintext) {
if(!detail.empty()) if(!detail.empty())
@ -1628,7 +1629,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
LockGuard lock(snippets_mutex); LockGuard lock(snippets_mutex);
if(snippets) { if(snippets) {
for(auto &snippet : *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(snippet.prefix);
autocomplete_rows.emplace_back(AutocompleteRow{snippet.body, snippet.description, {}}); 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) { 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()); 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(); auto iter = get_buffer()->get_insert()->get_iter();
if(*iter == '(' || *iter == '<') { if(*iter == '(' || *iter == '<') {
auto bracket_pos = insert.find(*iter); auto bracket_pos = insert.find(*iter);
if(bracket_pos != std::string::npos) { if(bracket_pos != std::string::npos)
insert = insert.substr(0, bracket_pos); insert.erase(bracket_pos);
}
} }
if(hide_window) { if(hide_window) {

15
src/tooltips.cpp

@ -4,6 +4,7 @@
#include "info.hpp" #include "info.hpp"
#include "notebook.hpp" #include "notebook.hpp"
#include "selection_dialog.hpp" #include "selection_dialog.hpp"
#include "utility.hpp"
#include <algorithm> #include <algorithm>
#include <regex> #include <regex>
@ -152,7 +153,7 @@ void Tooltip::show(bool disregard_drawn, const std::function<void()> &on_motion)
if(link.empty()) if(link.empty())
link = text; 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); Notebook::get().open_uri(link);
return true; return true;
} }
@ -477,7 +478,7 @@ void Tooltip::insert_markdown(const std::string &input) {
auto start = i; auto start = i;
for(; i < to; i++) { for(; i < to; i++) {
if(!unescape(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_ if(i - 1 > from && is_whitespace_character(i - 1)) { // Do not emphasis _test in: _test _test_
i = i_saved; i = i_saved;
return false; return false;
@ -507,7 +508,7 @@ void Tooltip::insert_markdown(const std::string &input) {
}; };
auto insert_strikethrough = [&] { auto insert_strikethrough = [&] {
if(input.compare(i, 2, "~~") == 0) { if(starts_with(input, i, "~~")) {
insert_with_links_tagged(partial); insert_with_links_tagged(partial);
partial.clear(); partial.clear();
auto i_saved = i; auto i_saved = i;
@ -515,7 +516,7 @@ void Tooltip::insert_markdown(const std::string &input) {
if(i < to) { if(i < to) {
auto start = i; auto start = i;
for(; i < to; i++) { for(; i < to; i++) {
if(!unescape(i) && input.compare(i, 2, "~~") == 0) if(!unescape(i) && starts_with(input, i, "~~"))
break; break;
} }
if(i == to) { if(i == to) {
@ -701,13 +702,13 @@ void Tooltip::insert_markdown(const std::string &input) {
auto insert_code_block = [&] { auto insert_code_block = [&] {
if(input.compare(i, 3, "```") == 0) { if(starts_with(input, i, "```")) {
auto i_saved = i; auto i_saved = i;
if(forward_to({'\n'})) { if(forward_to({'\n'})) {
i++; i++;
if(i < input.size()) { if(i < input.size()) {
auto start = i; 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++; i++;
if(i == input.size()) { if(i == input.size()) {
i = i_saved; i = i_saved;
@ -769,7 +770,7 @@ void Tooltip::insert_markdown(const std::string &input) {
// Insert paragraph: // Insert paragraph:
auto start = i; auto start = i;
for(; forward_to({'\n'}); 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; break;
} }
insert_text(start, i); insert_text(start, i);

70
src/utility.cpp

@ -1,6 +1,76 @@
#include "utility.hpp" #include "utility.hpp"
#include <cstring>
ScopeGuard::~ScopeGuard() { ScopeGuard::~ScopeGuard() {
if(on_exit) if(on_exit)
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;
}

11
src/utility.hpp

@ -1,8 +1,19 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <string>
class ScopeGuard { class ScopeGuard {
public: public:
std::function<void()> on_exit; std::function<void()> on_exit;
~ScopeGuard(); ~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;

6
src/window.cpp

@ -830,8 +830,6 @@ void Window::set_menu_actions() {
}); });
menu.add_action("source_find_pattern", [this]() { menu.add_action("source_find_pattern", [this]() {
std::string excludes = "--exclude-dir=node_modules";
EntryBox::get().clear(); EntryBox::get().clear();
EntryBox::get().entries.emplace_back(last_find_pattern, [this](const std::string &pattern_) { EntryBox::get().entries.emplace_back(last_find_pattern, [this](const std::string &pattern_) {
@ -845,14 +843,14 @@ void Window::set_menu_actions() {
return; return;
} }
if(auto view = Notebook::get().get_current_view()) auto view = Notebook::get().get_current_view();
if(view)
SelectionDialog::create(view, true, true); SelectionDialog::create(view, true, true);
else else
SelectionDialog::create(true, true); SelectionDialog::create(true, true);
std::string current_path; std::string current_path;
unsigned int current_line = 0; unsigned int current_line = 0;
auto view = Notebook::get().get_current_view();
if(view) { if(view) {
current_path = filesystem::get_relative_path(view->file_path, grep->project_path).string(); current_path = filesystem::get_relative_path(view->file_path, grep->project_path).string();
current_line = view->get_buffer()->get_insert()->get_iter().get_line(); current_line = view->get_buffer()->get_insert()->get_iter().get_line();

3
tests/CMakeLists.txt

@ -84,3 +84,6 @@ add_executable(tooltips_test tooltips_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(tooltips_test juci_shared) target_link_libraries(tooltips_test juci_shared)
add_test(tooltips_test tooltips_test) add_test(tooltips_test tooltips_test)
add_executable(utility_test utility_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(utility_test juci_shared)
add_test(utility_test utility_test)

88
tests/utility_test.cpp

@ -0,0 +1,88 @@
#include "utility.hpp"
#include <glib.h>
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));
}
Loading…
Cancel
Save