From 5068d34c08f951a7ea6257d305a1627b09a95817 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 19:44:55 +0200 Subject: Adding upstream version 0.11.1. Signed-off-by: Daniel Baumann --- src/base/attr_line.hh | 758 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 758 insertions(+) create mode 100644 src/base/attr_line.hh (limited to 'src/base/attr_line.hh') diff --git a/src/base/attr_line.hh b/src/base/attr_line.hh new file mode 100644 index 0000000..c9cb6a8 --- /dev/null +++ b/src/base/attr_line.hh @@ -0,0 +1,758 @@ +/** + * Copyright (c) 2017, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file attr_line.hh + */ + +#ifndef attr_line_hh +#define attr_line_hh + +#include +#include +#include + +#include + +#include "fmt/format.h" +#include "intern_string.hh" +#include "string_attr_type.hh" +#include "string_util.hh" + +/** + * Encapsulates a range in a string. + */ +struct line_range { + enum class unit { + bytes, + codepoint, + }; + + int lr_start; + int lr_end; + unit lr_unit; + + explicit line_range(int start = -1, int end = -1, unit u = unit::bytes) + : lr_start(start), lr_end(end), lr_unit(u) + { + } + + bool is_valid() const { return this->lr_start != -1; } + + int length() const + { + return this->lr_end == -1 ? INT_MAX : this->lr_end - this->lr_start; + } + + bool empty() const { return this->length() == 0; } + + void clear() + { + this->lr_start = -1; + this->lr_end = -1; + } + + int end_for_string(const std::string& str) const + { + return this->lr_end == -1 ? str.length() : this->lr_end; + } + + bool contains(int pos) const + { + return this->lr_start <= pos + && (this->lr_end == -1 || pos < this->lr_end); + } + + bool contains(const struct line_range& other) const + { + return this->contains(other.lr_start) + && (this->lr_end == -1 || other.lr_end <= this->lr_end); + } + + bool intersects(const struct line_range& other) const + { + if (this->contains(other.lr_start)) { + return true; + } + if (other.lr_end > 0 && this->contains(other.lr_end - 1)) { + return true; + } + if (other.contains(this->lr_start)) { + return true; + } + + return false; + } + + line_range intersection(const struct line_range& other) const; + + line_range& shift(int32_t start, int32_t amount); + + void ltrim(const char* str) + { + while (this->lr_start < this->lr_end && isspace(str[this->lr_start])) { + this->lr_start += 1; + } + } + + bool operator<(const struct line_range& rhs) const + { + if (this->lr_start < rhs.lr_start) { + return true; + } + if (this->lr_start > rhs.lr_start) { + return false; + } + + // this->lr_start == rhs.lr_start + if (this->lr_end == rhs.lr_end) { + return false; + } + + if (this->lr_end < rhs.lr_end) { + return false; + } + return true; + } + + bool operator==(const struct line_range& rhs) const + { + return (this->lr_start == rhs.lr_start && this->lr_end == rhs.lr_end); + } + + const char* substr(const std::string& str) const + { + if (this->lr_start == -1) { + return str.c_str(); + } + return &(str.c_str()[this->lr_start]); + } + + size_t sublen(const std::string& str) const + { + if (this->lr_start == -1) { + return str.length(); + } + if (this->lr_end == -1) { + return str.length() - this->lr_start; + } + return this->length(); + } +}; + +inline line_range +to_line_range(const string_fragment& frag) +{ + return line_range{frag.sf_begin, frag.sf_end}; +} + +struct string_attr { + string_attr(const struct line_range& lr, const string_attr_pair& value) + : sa_range(lr), sa_type(value.first), sa_value(value.second) + { + } + + string_attr() = default; + + bool operator<(const struct string_attr& rhs) const + { + if (this->sa_range < rhs.sa_range) { + return true; + } + if (this->sa_range == rhs.sa_range && this->sa_type == rhs.sa_type + && this->sa_type == &VC_ROLE + && this->sa_value.get() < rhs.sa_value.get()) + { + return true; + } + + return false; + } + + struct line_range sa_range; + const string_attr_type_base* sa_type{nullptr}; + string_attr_value sa_value; +}; + +template +struct string_attr_wrapper { + explicit string_attr_wrapper(const string_attr* sa) : saw_string_attr(sa) {} + + template + std::enable_if_t::value, const U&> get() const + { + return this->saw_string_attr->sa_value.template get(); + } + + const string_attr* saw_string_attr; +}; + +/** A map of line ranges to attributes for that range. */ +using string_attrs_t = std::vector; + +inline string_attrs_t::const_iterator +find_string_attr(const string_attrs_t& sa, + const string_attr_type_base* type, + int start = 0) +{ + string_attrs_t::const_iterator iter; + + for (iter = sa.begin(); iter != sa.end(); ++iter) { + if (iter->sa_type == type && iter->sa_range.lr_start >= start) { + break; + } + } + + return iter; +} + +inline nonstd::optional +get_string_attr(const string_attrs_t& sa, + const string_attr_type_base* type, + int start = 0) +{ + auto iter = find_string_attr(sa, type, start); + + if (iter == sa.end()) { + return nonstd::nullopt; + } + + return nonstd::make_optional(&(*iter)); +} + +template +inline nonstd::optional> +get_string_attr(const string_attrs_t& sa, + const string_attr_type& type, + int start = 0) +{ + auto iter = find_string_attr(sa, &type, start); + + if (iter == sa.end()) { + return nonstd::nullopt; + } + + return nonstd::make_optional(string_attr_wrapper(&(*iter))); +} + +template +inline string_attrs_t::const_iterator +find_string_attr_containing(const string_attrs_t& sa, + const string_attr_type_base* type, + T x) +{ + string_attrs_t::const_iterator iter; + + for (iter = sa.begin(); iter != sa.end(); ++iter) { + if (iter->sa_type == type && iter->sa_range.contains(x)) { + break; + } + } + + return iter; +} + +inline string_attrs_t::iterator +find_string_attr(string_attrs_t& sa, const struct line_range& lr) +{ + string_attrs_t::iterator iter; + + for (iter = sa.begin(); iter != sa.end(); ++iter) { + if (lr.contains(iter->sa_range)) { + break; + } + } + + return iter; +} + +inline string_attrs_t::const_iterator +find_string_attr(const string_attrs_t& sa, size_t near) +{ + auto nearest = sa.end(); + ssize_t last_diff = INT_MAX; + + for (auto iter = sa.begin(); iter != sa.end(); ++iter) { + const auto& lr = iter->sa_range; + + if (!lr.is_valid() || !lr.contains(near)) { + continue; + } + + ssize_t diff = near - lr.lr_start; + if (diff < last_diff) { + last_diff = diff; + nearest = iter; + } + } + + return nearest; +} + +template +inline string_attrs_t::const_iterator +rfind_string_attr_if(const string_attrs_t& sa, ssize_t near, T predicate) +{ + auto nearest = sa.end(); + ssize_t last_diff = INT_MAX; + + for (auto iter = sa.begin(); iter != sa.end(); ++iter) { + const auto& lr = iter->sa_range; + + if (lr.lr_start > near) { + continue; + } + + if (!predicate(*iter)) { + continue; + } + + ssize_t diff = near - lr.lr_start; + if (diff < last_diff) { + last_diff = diff; + nearest = iter; + } + } + + return nearest; +} + +inline struct line_range +find_string_attr_range(const string_attrs_t& sa, string_attr_type_base* type) +{ + auto iter = find_string_attr(sa, type); + + if (iter != sa.end()) { + return iter->sa_range; + } + + return line_range(); +} + +inline void +remove_string_attr(string_attrs_t& sa, const struct line_range& lr) +{ + string_attrs_t::iterator iter; + + while ((iter = find_string_attr(sa, lr)) != sa.end()) { + sa.erase(iter); + } +} + +inline void +remove_string_attr(string_attrs_t& sa, string_attr_type_base* type) +{ + for (auto iter = sa.begin(); iter != sa.end();) { + if (iter->sa_type == type) { + iter = sa.erase(iter); + } else { + ++iter; + } + } +} + +inline void +shift_string_attrs(string_attrs_t& sa, int32_t start, int32_t amount) +{ + for (auto& iter : sa) { + iter.sa_range.shift(start, amount); + } +} + +struct text_wrap_settings { + text_wrap_settings& with_indent(int indent) + { + this->tws_indent = indent; + return *this; + } + + text_wrap_settings& with_padding_indent(int indent) + { + this->tws_padding_indent = indent; + return *this; + } + + text_wrap_settings& with_width(int width) + { + this->tws_width = width; + return *this; + } + + int tws_indent{2}; + int tws_width{80}; + int tws_padding_indent{0}; +}; + +/** + * A line that has attributes. + */ +class attr_line_t { +public: + attr_line_t() = default; + + attr_line_t(std::string str) : al_string(std::move(str)) {} + + attr_line_t(const char* str) : al_string(str) {} + + static inline attr_line_t from_ansi_str(const char* str) + { + attr_line_t retval; + + return retval.with_ansi_string("%s", str); + } + + /** @return The string itself. */ + std::string& get_string() { return this->al_string; } + + const std::string& get_string() const { return this->al_string; } + + /** @return The attributes for the string. */ + string_attrs_t& get_attrs() { return this->al_attrs; } + + const string_attrs_t& get_attrs() const { return this->al_attrs; } + + attr_line_t& with_string(const std::string& str) + { + this->al_string = str; + return *this; + } + + attr_line_t& with_ansi_string(const char* str, ...); + + attr_line_t& with_ansi_string(const std::string& str); + + attr_line_t& with_attr(const string_attr& sa) + { + this->al_attrs.push_back(sa); + return *this; + } + + attr_line_t& ensure_space() + { + if (!this->al_string.empty() && this->al_string.back() != ' ' + && this->al_string.back() != '[') + { + this->append(1, ' '); + } + + return *this; + } + + template + attr_line_t& append(S str, const string_attr_pair& value) + { + size_t start_len = this->al_string.length(); + + this->al_string.append(str); + + line_range lr{(int) start_len, (int) this->al_string.length()}; + + this->al_attrs.emplace_back(lr, value); + + return *this; + } + + template + attr_line_t& append(const std::pair& value) + { + size_t start_len = this->al_string.length(); + + this->al_string.append(std::move(value.first)); + + line_range lr{(int) start_len, (int) this->al_string.length()}; + + this->al_attrs.emplace_back(lr, value.second); + + return *this; + } + + template + attr_line_t& append_quoted(const std::pair& value) + { + this->al_string.append("\u201c"); + + size_t start_len = this->al_string.length(); + + this->al_string.append(std::move(value.first)); + + line_range lr{(int) start_len, (int) this->al_string.length()}; + + this->al_attrs.emplace_back(lr, value.second); + + this->al_string.append("\u201d"); + + return *this; + } + + attr_line_t& append_quoted(const intern_string_t str) + { + this->al_string.append("\u201c"); + this->al_string.append(str.get(), str.size()); + this->al_string.append("\u201d"); + + return *this; + } + + attr_line_t& append_quoted(const attr_line_t& al) + { + this->al_string.append("\u201c"); + this->append(al); + this->al_string.append("\u201d"); + + return *this; + } + + template + attr_line_t& append_quoted(S s) + { + this->al_string.append("\u201c"); + this->append(std::move(s)); + this->al_string.append("\u201d"); + + return *this; + } + + attr_line_t& append(const intern_string_t str) + { + this->al_string.append(str.get(), str.size()); + return *this; + } + + attr_line_t& append(const string_fragment& str) + { + this->al_string.append(str.data(), str.length()); + return *this; + } + + template + attr_line_t& append(S str) + { + this->al_string.append(str); + return *this; + } + + template + attr_line_t& appendf(fmt::format_string fstr, Args&&... args) + { + this->template append( + fmt::vformat(fstr, fmt::make_format_args(args...))); + return *this; + } + + attr_line_t& with_attr_for_all(const string_attr_pair& sap) + { + this->al_attrs.emplace_back(line_range{0, -1}, sap); + return *this; + } + + template + attr_line_t& join(const C& container, + const string_attr_pair& sap, + const F& fill) + { + bool init = true; + for (const auto& elem : container) { + if (!init) { + this->append(fill); + } + this->append(std::make_pair(elem, sap)); + init = false; + } + + return *this; + } + + template + attr_line_t& join(const C& container, const F& fill) + { + bool init = true; + for (const auto& elem : container) { + if (!init) { + this->append(fill); + } + this->append(elem); + init = false; + } + + return *this; + } + + attr_line_t& insert(size_t index, + const attr_line_t& al, + text_wrap_settings* tws = nullptr); + + attr_line_t& append(const attr_line_t& al, + text_wrap_settings* tws = nullptr) + { + return this->insert(this->al_string.length(), al, tws); + } + + attr_line_t& append(size_t len, char c) + { + this->al_string.append(len, c); + return *this; + } + + attr_line_t& insert(size_t index, size_t len, char c) + { + this->al_string.insert(index, len, c); + + shift_string_attrs(this->al_attrs, index, len); + + return *this; + } + + attr_line_t& insert(size_t index, const char* str) + { + this->al_string.insert(index, str); + + shift_string_attrs(this->al_attrs, index, strlen(str)); + + return *this; + } + + template + attr_line_t& add_header(Args... args) + { + if (!this->blank()) { + this->insert(0, args...); + } + return *this; + } + + template + attr_line_t& with_default(Args... args) + { + if (this->blank()) { + this->clear(); + this->append(args...); + } + + return *this; + } + + attr_line_t& erase(size_t pos, size_t len = std::string::npos); + + attr_line_t& rtrim(); + + attr_line_t& erase_utf8_chars(size_t start) + { + auto byte_index = utf8_char_to_byte_index(this->al_string, start); + this->erase(byte_index); + + return *this; + } + + attr_line_t& right_justify(unsigned long width); + + attr_line_t& pad_to(ssize_t size); + + ssize_t length() const + { + size_t retval = this->al_string.length(); + + for (const auto& al_attr : this->al_attrs) { + retval = std::max(retval, (size_t) al_attr.sa_range.lr_start); + if (al_attr.sa_range.lr_end != -1) { + retval = std::max(retval, (size_t) al_attr.sa_range.lr_end); + } + } + + return retval; + } + + Result utf8_length() const + { + return utf8_string_length(this->al_string); + } + + ssize_t utf8_length_or_length() const + { + return utf8_string_length(this->al_string).unwrapOr(this->length()); + } + + std::string get_substring(const line_range& lr) const + { + if (!lr.is_valid()) { + return ""; + } + return this->al_string.substr(lr.lr_start, lr.sublen(this->al_string)); + } + + string_fragment to_string_fragment( + string_attrs_t::const_iterator iter) const + { + return string_fragment(this->al_string.c_str(), + iter->sa_range.lr_start, + iter->sa_range.end_for_string(this->al_string)); + } + + string_attrs_t::const_iterator find_attr(size_t near) const + { + near = std::min(near, this->al_string.length() - 1); + + while (near > 0 && isspace(this->al_string[near])) { + near -= 1; + } + + return find_string_attr(this->al_attrs, near); + } + + bool empty() const { return this->length() == 0; } + + bool blank() const { return is_blank(this->al_string); } + + /** Clear the string and the attributes for the string. */ + attr_line_t& clear() + { + this->al_string.clear(); + this->al_attrs.clear(); + + return *this; + } + + attr_line_t subline(size_t start, size_t len = std::string::npos) const; + + void split_lines(std::vector& lines) const; + + std::vector split_lines() const + { + std::vector retval; + + this->split_lines(retval); + return retval; + } + + size_t nearest_text(size_t x) const; + + void apply_hide(); + + std::string al_string; + string_attrs_t al_attrs; +}; + +#endif -- cgit v1.2.3