Added StringView type (similar to std::string_view)

This commit is contained in:
Twan van Laarhoven
2020-05-21 19:29:42 +02:00
parent 6a299dfe41
commit 1a0e17221b
9 changed files with 153 additions and 106 deletions
+6 -6
View File
@@ -153,18 +153,18 @@ unique_ptr<TextValueAction> toggle_format_action(const TextValueP& value, const
// Are we inside the tag we are toggling?
if (!is_in_tag(str, _("<") + tag, start_i, end_i)) {
// we are not inside this tag, add it
new_value = str.substr(0, start_i);
new_value = substr(str, 0, start_i);
new_value += _("<") + tag + _(">");
new_value += str.substr(start_i, end_i - start_i);
new_value += substr(str, start_i, end_i - start_i);
new_value += _("</") + tag + _(">");
new_value += str.substr(end_i);
new_value += substr(str, end_i);
} else {
// we are inside this tag, 'remove' it
new_value = str.substr(0, start_i);
new_value = substr(str, 0, start_i);
new_value += _("</") + tag + _(">");
new_value += str.substr(start_i, end_i - start_i);
new_value += substr(str, start_i, end_i - start_i);
new_value += _("<") + tag + _(">");
new_value += str.substr(end_i);
new_value += substr(str, end_i);
}
// Build action
if (start != end) {
+1 -1
View File
@@ -65,7 +65,7 @@ void read_compat(Reader& handler, Keyword* k) {
size_t start = separator.find_first_of('[');
size_t end = separator.find_first_of(']');
if (start != String::npos && end != String::npos) {
k->match += separator.substr(start + 1, end - start - 1);
k->match += substr(separator, start + 1, end - start - 1);
}
if (parameter == _("no parameter")) {
parameter.clear(); // was used for magic to indicate absence of parameter
+3 -3
View File
@@ -122,7 +122,7 @@ bool isOper (wxUniChar c) { return wxStrchr(_("+-*/!.@%^&:=<>;,"),c) != nullptr
bool isLparen(wxUniChar c) { return c==_('(') || c==_('[') || c==_('{'); }
bool isRparen(wxUniChar c) { return c==_(')') || c==_(']') || c==_('}'); }
bool isDigitOrDot(wxUniChar c) { return isDigit(c) || c==_('.'); }
bool isLongOper(const String& s) { return s==_(":=") || s==_("==") || s==_("!=") || s==_("<=") || s==_(">="); }
bool isLongOper(StringView s) { return s==_(":=") || s==_("==") || s==_("!=") || s==_("<=") || s==_(">="); }
// moveme
// ----------------------------------------------------------------------------- : Tokenizing
@@ -185,7 +185,7 @@ void TokenIterator::readToken() {
pos += 13; // "include file:"
const char* newlines = "\r\n";
auto eol = find_first_of(pos,end, newlines,newlines+2);
String include_file = trim(String(pos, eol));
String include_file = trim(StringView(pos, eol));
// include_file("filename")
addToken(TOK_NAME, "include_file", pos - 13);
addToken(TOK_LPAREN, "(", pos);
@@ -208,7 +208,7 @@ void TokenIterator::readToken() {
addToken(type, String(start,pos), start);
} else if (isOper(c)) {
// operator
if (pos+1 != end && isLongOper(String(pos, pos+2))) {
if (pos+1 != end && isLongOper(StringView(pos, pos+2))) {
// long operator
addToken(TOK_OPER, String(pos, pos+2), pos);
pos += 2;
+1 -1
View File
@@ -225,7 +225,7 @@ void Reader::readLine(bool in_string) {
}
}
key = canonical_name_form(trim(key));
value = pos == String::npos ? _("") : trim_left(line.substr(pos+1));
value = pos == String::npos ? String() : trim_left(substr(line, pos+1));
if (key.empty() && pos!=String::npos) key = _(" "); // we don't want an empty key if there was a colon
}
+1 -1
View File
@@ -131,7 +131,7 @@ private:
OUTSIDE, ///< We have not entered the block of the current key
ENTERED, ///< We just entered the block of the current key
HANDLED, ///< We have handled a value, and moved to the next line, previous_value is the value we just handled
UNHANDLED, ///< Something has been 'unhandled()'
UNHANDLED, ///< Something has been 'unhandle()-ed'
} state;
/// Should all invalid keys be ignored?
bool ignore_invalid;
-2
View File
@@ -77,8 +77,6 @@ String tr(const Package&, const String& subcat, const String& key, DefaultLocale
/// A localized string for menus, with 1 argument (printf style)
#define _MENU_1_(s,a) format_string(_MENU_(s), a)
/// A localized string for context menus, contains no "\tshortcut"
#define _CONTEXT_MENU_(s) remove_shortcut(_MENU_(s))
/// A localized string for tooltip text, with 1 argument (printf style)
#define _HELP_1_(s,a) format_string(_HELP_(s), a)
+28 -68
View File
@@ -48,25 +48,19 @@ Char toUpper(Char c) {
// ----------------------------------------------------------------------------- : String utilities
String trim(const String& s){
size_t start = s.find_first_not_of(_(" \t"));
size_t end = s.find_last_not_of( _(" \t"));
if (start == String::npos) {
return String();
} else if (start == 0 && end == s.size() - 1) {
return s;
} else {
return s.substr(start, end - start + 1);
}
StringView trim(StringView s) {
String::const_iterator begin = s.begin();
String::const_iterator end = s.end();
while (begin != end && isSpace(*begin)) ++begin;
while (begin != end && isSpace(*(end - 1))) --end;
return StringView(begin, end);
}
String trim_left(const String& s) {
size_t start = s.find_first_not_of(_(" \t"));
if (start == String::npos) {
return String();
} else {
return s.substr(start);
}
StringView trim_left(StringView s) {
String::const_iterator begin = s.begin();
String::const_iterator end = s.end();
while (begin != end && isSpace(*begin)) ++begin;
return StringView(begin, end);
}
String substr_replace(const String& input, size_t start, size_t end, const String& replacement) {
@@ -98,60 +92,42 @@ bool is_substr(const String& s, String::const_iterator it, const Char* cmp) {
return *cmp == 0;
}
String capitalize(const String& s) {
String result = s;
void capitalize_in_place(String& s) {
bool after_space = true;
FOR_EACH_IT(it, result) {
for (String::iterator it = s.begin(); it != s.end(); ++it) {
if (*it == _(' ') || *it == _('/')) {
after_space = true;
} else if (after_space) {
after_space = false;
if (it != result.begin() &&
(is_substr(result,it,_("is ")) || is_substr(result,it,_("the ")) ||
is_substr(result,it,_("in ")) || is_substr(result,it,_("of ")) ||
is_substr(result,it,_("to ")) || is_substr(result,it,_("at ")) ||
is_substr(result,it,_("a " )))) {
if (it != s.begin() &&
(is_substr(it, s.end(), _("is ")) || is_substr(it, s.end(), _("the ")) ||
is_substr(it, s.end(), _("in ")) || is_substr(it, s.end(), _("of ")) ||
is_substr(it, s.end(), _("to ")) || is_substr(it, s.end(), _("at ")) ||
is_substr(it, s.end(), _("a " )))) {
// Short words are not capitalized, keep lower case
} else {
*it = toUpper(*it);
}
}
}
return result;
}
String capitalize_sentence(const String& s) {
String ret = s;//.Lower();
if (!ret.empty()) {
ret[0] = toUpper(ret[0]);
void capitalize_sentence_in_place(String& s) {
if (!s.empty()) {
s[0] = toUpper(s[0]);
}
return ret;
}
wxUniChar canonical_name_form(wxUniChar c) {
if (c == _(' ')) return _('_');
return c;
}
String canonical_name_form(const String& str) {
String ret;
ret.reserve(str.size());
FOR_EACH_CONST(c, str) {
ret += canonical_name_form(c);
void canonical_name_form_in_place(String& str) {
for (String::iterator it = str.begin(); it != str.end(); ++it) {
if (*it == ' ') *it = '_';
}
return ret;
}
wxUniChar uncanonical_name_form(wxUniChar c) {
if (c == _('_')) return _(' ');
return c;
}
String uncanonical_name_form(const String& str) {
String ret;
ret.reserve(str.size());
FOR_EACH_CONST(c, str) {
ret += uncanonical_name_form(c);
void uncanonical_name_form_in_place(String& str) {
for (String::iterator it = str.begin(); it != str.end(); ++it) {
if (*it == '_') *it = ' ';
}
return ret;
}
String name_to_caption(const String& str) {
@@ -183,12 +159,6 @@ String singular_form(const String& str) {
return str.substr(0, str.size() - 1);
}
String remove_shortcut(const String& str) {
size_t tab = str.find_last_of(_('\t'));
if (tab == String::npos) return str;
else return str.substr(0, tab);
}
// ----------------------------------------------------------------------------- : Comparing / finding
// Nice unicode normalization tables, probably not conform the standards
@@ -355,16 +325,6 @@ bool starts_with(const String& str, const String& start) {
return equal(start.begin(), start.end(), str.begin());
}
bool is_substr(const String& str, size_t pos, const Char* cmp) {
for (String::const_iterator it = str.begin() + pos ; *cmp && it < str.end() ; ++cmp, ++it) {
if (*cmp != *it) return false;
}
return *cmp == _('\0');
}
bool is_substr(const String& str, size_t pos, const String& cmp) {
return str.size() >= cmp.size() + pos && str.compare(pos, cmp.size(), cmp) == 0;
}
bool is_substr_i(const String& str, size_t pos, const Char* cmp) {
for (String::const_iterator it = str.begin() + pos ; *cmp && it < str.end() ; ++cmp, ++it) {
@@ -376,7 +336,7 @@ bool is_substr_i(const String& str, size_t pos, const String& cmp) {
return is_substr_i(str, pos, static_cast<const Char*>(cmp.c_str()));
}
bool canonical_name_compare(const String& as, const Char* b) {
bool canonical_name_compare(StringView as, const Char* b) {
assert(canonical_name_form(b) == b);
for (String::const_iterator a_it = as.begin(); a_it != as.end(); ++a_it, ++b) {
if (*a_it != *b && !(*a_it == '_' && *b == ' ')) {
+111 -22
View File
@@ -115,13 +115,77 @@ inline bool isPunct(Char c) { return IF_UNICODE( iswpunct(c) , ispunct((unsigned
inline bool isSpace(Char c) { return IF_UNICODE( iswspace(c) , isspace((unsigned char)c) ) || c == CONNECTION_SPACE; }
#endif
// ----------------------------------------------------------------------------- : String view
// A view of (part of a string)
class StringView {
public:
StringView(String const& str)
: begin_(str.begin()), end_(str.end())
{}
StringView(String const& str, size_t pos)
: begin_(str.begin() + pos), end_(str.end())
{}
StringView(String const& str, size_t pos, size_t count)
: begin_(str.begin() + pos), end_(str.begin() + pos + min(count, str.size()-pos))
{}
StringView(String::const_iterator begin, String::const_iterator end)
: begin_(begin), end_(end)
{
assert(begin <= end);
}
inline operator String () const {
return String(begin_, end_);
}
using iterator = String::const_iterator;
using const_iterator = String::const_iterator;
inline String::const_iterator begin() const {
return begin_;
}
inline String::const_iterator end() const {
return end_;
}
inline size_t size() const {
return end_ - begin_;
}
inline bool empty() const {
return begin() == end();
}
inline bool operator == (StringView const& str) {
return str.size() == size() && std::equal(begin(), end(), str.begin());
}
template <typename AnyChar>
inline bool operator == (const AnyChar* str) {
String::const_iterator it = begin_;
while (true) {
if (it == end_) return *str == '\0';
if (*str == '\0') return false;
if (*str != *it) return false;
++it; ++str;
}
}
private:
String::const_iterator begin_, end_;
};
inline String& operator += (String& a, StringView b) {
return a.append(b.begin(), b.end());
}
inline StringView substr(String const& str, size_t pos, size_t len) {
return StringView(str, pos, len);
}
inline StringView substr(String const& str, size_t pos) {
return StringView(str, pos);
}
// ----------------------------------------------------------------------------- : String utilities
/// Remove whitespace from both ends of a string
String trim(const String&);
StringView trim(StringView);
/// Remove whitespace from the start of a string
String trim_left(const String&);
StringView trim_left(StringView);
/// Replace the substring [start...end) of 'input' with 'replacement'
String substr_replace(const String& input, size_t start, size_t end, const String& replacement);
@@ -136,18 +200,37 @@ String reverse_string(String const& input);
/// Make each word in a string start with an upper case character.
/** for use in menus */
String capitalize(const String&);
void capitalize_in_place(String&);
inline String capitalize(String const& s) {
String result = s;
capitalize_in_place(result);
return result;
}
/// Make the first word in a string start with an upper case character.
/** for use in dialogs */
String capitalize_sentence(const String&);
void capitalize_sentence_in_place(String&);
inline String capitalize_sentence(String const& s) {
String result = s;
capitalize_sentence_in_place(result);
return result;
}
/// Convert a field name to canonical form
/** - converts ' ' to '_'
*/
String canonical_name_form(const String&);
void canonical_name_form_in_place(String&);
inline String canonical_name_form(String s) {
canonical_name_form_in_place(s);
return s;
}
/// Undo canonical_name_form: replace '_' by ' '
String uncanonical_name_form(const String&);
void uncanonical_name_form_in_place(String&);
inline String uncanonical_name_form(String s) {
uncanonical_name_form_in_place(s);
return s;
}
/// Convert a field name to a string that can be shown to the user
String name_to_caption(const String&);
@@ -158,11 +241,6 @@ String name_to_caption(const String&);
*/
String singular_form(const String&);
/// Remove a shortcut from a menu string
/** e.g. "Cut\tCtrl+X" --> "Cut"
*/
String remove_shortcut(const String&);
// ----------------------------------------------------------------------------- : Comparing / finding
/// Compare two strings
@@ -180,19 +258,30 @@ bool smart_equal(const String&, const String&);
/// Return whether str starts with start
/** starts_with(a,b) == is_substr(a,0,b) */
bool starts_with(const String& str, const String& start);
//bool starts_with(const String& str, const String& start);
inline bool starts_with(StringView str, StringView const& start) {
return str.size() >= start.size() && std::equal(start.begin(), start.end(), str.begin());
}
template <typename AnyChar>
inline bool starts_with(StringView str, const AnyChar* start) {
String::const_iterator it = str.begin();
while (true) {
if (*start == '\0') return true;
if (it == str.end()) return false;
if (*start != *it) return false;
++it; ++start;
}
}
/// Return whether str contains the string cmp at position pos
bool is_substr(const String& str, size_t pos, const Char* cmp);
/// Return whether str contains the string cmp at position pos
bool is_substr(const String& str, size_t pos, const String& cmp);
template <typename Cmp>
inline bool is_substr(const String& str, size_t pos, const Cmp& cmp) {
return starts_with(StringView(str, pos), cmp);
}
/// Return whether begin..end contains the string cmp at position begin
template <typename It>
bool is_substr(It begin, It end, const char* cmp) {
for (; begin != end && *cmp; ++begin, ++cmp) {
if (*begin != *cmp) return false;
}
return true;
template <typename It, typename Cmp>
inline bool is_substr(It begin, It end, const Cmp& cmp) {
return starts_with(StringView(begin, end),cmp);
}
/// Return whether str contains the string cmp at position pos, case insensitive compare
@@ -207,7 +296,7 @@ size_t find_i(const String& heystack, const String& needle);
/** canoncial_name_compare(a,b) == (cannocial_name_form(a) == b)
* b should already be in cannonical name form
*/
bool canonical_name_compare(const String& a, const Char* b);
bool canonical_name_compare(StringView a, const Char* b);
// ----------------------------------------------------------------------------- : Regular expressions
+2 -2
View File
@@ -23,8 +23,8 @@ const Char ESCAPED_LANGLE = _('\1');
wxUniChar untag_char(wxUniChar c);
wxUniChar tag_char(wxUniChar c);
/// Remove all tags from a string and convert escaped '<' back to normal '<'
/** e.g. "<sym>R</> something <i>(note)</>"
/// Remove all tags from a string and convert ESCAPED_LANGLE back to normal '<'
/** e.g. "<sym>R</sym> something <i>(note)</i>"
* becomes "R something (note)"
*/
String untag(const String&);