#ifndef string_hh_INCLUDED #define string_hh_INCLUDED #include "units.hh" #include "utf8.hh" #include #include #include #include namespace Kakoune { class StringView; class String : public std::string { public: String() {} String(const char* content) : std::string(content) {} String(std::string content) : std::string(std::move(content)) {} explicit String(char content, CharCount count = 1) : std::string((size_t)(int)count, content) {} explicit String(Codepoint cp, CharCount count = 1) { while (count-- > 0) utf8::dump(back_inserter(*this), cp); } template String(Iterator begin, Iterator end) : std::string(begin, end) {} std::string& stdstr() { return *this; } const std::string& stdstr() const { return *this; } [[gnu::always_inline]] char operator[](ByteCount pos) const { return std::string::operator[]((int)pos); } [[gnu::always_inline]] char& operator[](ByteCount pos) { return std::string::operator[]((int)pos); } Codepoint operator[](CharCount pos) { return utf8::codepoint(utf8::advance(begin(), end(), pos), end()); } [[gnu::always_inline]] ByteCount length() const { return ByteCount{(int)std::string::length()}; } CharCount char_length() const { return utf8::distance(begin(), end()); } ByteCount byte_count_to(CharCount count) const { return utf8::advance(begin(), end(), (int)count) - begin(); } CharCount char_count_to(ByteCount count) const { return utf8::distance(begin(), begin() + (int)count); } String operator+(const String& other) const { return String{stdstr() + other.stdstr()}; } String& operator+=(const String& other) { std::string::operator+=(other); return *this; } String operator+(const char* other) const { return String{stdstr() + other}; } String& operator+=(const char* other) { std::string::operator+=(other); return *this; } String operator+(char other) const { return String{stdstr() + other}; } String& operator+=(char other) { std::string::operator+=(other); return *this; } String operator+(Codepoint cp) const { String res = *this; utf8::dump(back_inserter(res), cp); return res; } String& operator+=(Codepoint cp) { utf8::dump(back_inserter(*this), cp); return *this; } StringView substr(ByteCount pos, ByteCount length = INT_MAX) const; StringView substr(CharCount pos, CharCount length = INT_MAX) const; }; class StringView { public: constexpr StringView() : m_data{nullptr}, m_length{0} {} constexpr StringView(const char* data, ByteCount length) : m_data{data}, m_length{length} {} StringView(const char* data) : m_data{data}, m_length{(int)strlen(data)} {} constexpr StringView(const char* begin, const char* end) : m_data{begin}, m_length{(int)(end - begin)} {} StringView(const std::string& str) : m_data{str.data()}, m_length{(int)str.length()} {} StringView(const char& c) : m_data(&c), m_length(1) {} friend bool operator==(StringView lhs, StringView rhs); [[gnu::always_inline]] const char* data() const { return m_data; } using iterator = const char*; using reverse_iterator = std::reverse_iterator; iterator begin() const { return m_data; } iterator end() const { return m_data + (int)m_length; } reverse_iterator rbegin() const { return reverse_iterator{m_data + (int)m_length}; } reverse_iterator rend() const { return reverse_iterator{m_data}; } char front() const { return *m_data; } char back() const { return m_data[(int)m_length - 1]; } [[gnu::always_inline]] char operator[](ByteCount pos) const { return m_data[(int)pos]; } Codepoint operator[](CharCount pos) { return utf8::codepoint(utf8::advance(begin(), end(), pos), end()); } [[gnu::always_inline]] ByteCount length() const { return m_length; } CharCount char_length() const { return utf8::distance(begin(), end()); } [[gnu::always_inline]] bool empty() { return m_length == 0_byte; } ByteCount byte_count_to(CharCount count) const; CharCount char_count_to(ByteCount count) const; StringView substr(ByteCount from, ByteCount length = INT_MAX) const; StringView substr(CharCount from, CharCount length = INT_MAX) const; String str() const { return String{begin(), end()}; } operator String() const { return str(); } // to remove struct ZeroTerminatedString { ZeroTerminatedString(const char* begin, const char* end) { if (*end == '\0') unowned = begin; else owned = std::string(begin, end); } operator const char*() const { return unowned ? unowned : owned.c_str(); } private: std::string owned; const char* unowned = nullptr; }; ZeroTerminatedString zstr() const { return ZeroTerminatedString{begin(), end()}; } private: const char* m_data; ByteCount m_length; }; inline bool operator==(StringView lhs, StringView rhs) { return lhs.m_length == rhs.m_length and memcmp(lhs.m_data, rhs.m_data, (int)lhs.m_length) == 0; } inline bool operator!=(StringView lhs, StringView rhs) { return not (lhs == rhs); } bool operator<(StringView lhs, StringView rhs); inline ByteCount StringView::byte_count_to(CharCount count) const { return utf8::advance(begin(), end(), (int)count) - begin(); } inline CharCount StringView::char_count_to(ByteCount count) const { return utf8::distance(begin(), begin() + (int)count); } inline StringView StringView::substr(ByteCount from, ByteCount length) const { if (length < 0) length = INT_MAX; return StringView{ m_data + (int)from, std::min(m_length - from, length) }; } inline StringView StringView::substr(CharCount from, CharCount length) const { if (length < 0) length = INT_MAX; auto beg = utf8::advance(begin(), end(), (int)from); return StringView{ beg, utf8::advance(beg, end(), length) }; } inline StringView String::substr(ByteCount pos, ByteCount length) const { return StringView{*this}.substr(pos, length); } inline StringView String::substr(CharCount pos, CharCount length) const { return StringView{*this}.substr(pos, length); } inline String operator+(const char* lhs, const String& rhs) { return String(lhs) + rhs; } inline String operator+(const std::string& lhs, const String& rhs) { return String(lhs) + rhs; } inline String operator+(const String& lhs, const std::string& rhs) { return lhs + String(rhs); } inline String& operator+=(String& lhs, StringView rhs) { lhs.append(rhs.data(), (size_t)(int)rhs.length()); return lhs; } inline String operator+(const char* lhs, StringView rhs) { String res = lhs; res += rhs; return res; } inline String operator+(const String& lhs, StringView rhs) { String res = lhs; res += rhs; return res; } inline String operator+(StringView lhs, const String& rhs) { String res{lhs.begin(), lhs.end()}; res.append(rhs); return res; } inline String operator+(StringView lhs, const char* rhs) { String res{lhs.begin(), lhs.end()}; res.append(rhs); return res; } inline String operator+(char lhs, const String& rhs) { return String(lhs) + rhs; } inline String operator+(Codepoint lhs, const String& rhs) { return String(lhs) + rhs; } inline String operator+(StringView lhs, StringView rhs) { String res{lhs.begin(), lhs.end()}; res += rhs; return res; } std::vector split(StringView str, char separator, char escape); std::vector split(StringView str, char separator); String escape(StringView str, StringView characters, char escape); String unescape(StringView str, StringView characters, char escape); inline String operator"" _str(const char* str, size_t) { return String(str); } inline String codepoint_to_str(Codepoint cp) { std::string str; utf8::dump(back_inserter(str), cp); return String(str); } int str_to_int(StringView str); String to_string(int val); template String to_string(const StronglyTypedNumber& val) { return to_string((ValueType)val); } inline bool prefix_match(StringView str, StringView prefix) { return str.substr(0_byte, prefix.length()) == prefix; } bool subsequence_match(StringView str, StringView subseq); String expand_tabs(StringView line, CharCount tabstop, CharCount col = 0); std::vector wrap_lines(StringView text, CharCount max_width); size_t hash_data(const char* data, size_t len); } namespace std { template<> struct hash : hash { size_t operator()(const Kakoune::String& str) const { return hash::operator()(str); } }; template<> struct hash { size_t operator()(Kakoune::StringView str) const { return Kakoune::hash_data(str.data(), (int)str.length()); } }; } #endif // string_hh_INCLUDED