Make wrap_lines a lazy range view
Avoid the need to allocate a vector by using the ranges framework.
This commit is contained in:
parent
936bd923ea
commit
19e1be8e0d
|
@ -235,58 +235,61 @@ String expand_tabs(StringView line, ColumnCount tabstop, ColumnCount col)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<StringView> wrap_lines(StringView text, ColumnCount max_width)
|
WrapView::Iterator::Iterator(StringView text, ColumnCount max_width)
|
||||||
|
: m_remaining{text}, m_max_width{max_width}
|
||||||
{
|
{
|
||||||
if (max_width <= 0)
|
if (max_width <= 0)
|
||||||
throw runtime_error("Invalid max width");
|
throw runtime_error("Invalid max width");
|
||||||
|
++*this;
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapView::Iterator& WrapView::Iterator::operator++()
|
||||||
|
{
|
||||||
using Utf8It = utf8::iterator<const char*>;
|
using Utf8It = utf8::iterator<const char*>;
|
||||||
Utf8It it{text.begin(), text};
|
Utf8It it{m_remaining.begin(), m_remaining};
|
||||||
Utf8It end{text.end(), text};
|
|
||||||
Utf8It line_begin = it;
|
|
||||||
Utf8It last_word_end = it;
|
Utf8It last_word_end = it;
|
||||||
|
|
||||||
Vector<StringView> lines;
|
while (it != m_remaining.end())
|
||||||
while (it != end)
|
|
||||||
{
|
{
|
||||||
const CharCategories cat = categorize(*it, {'_'});
|
const CharCategories cat = categorize(*it, {'_'});
|
||||||
if (cat == CharCategories::EndOfLine)
|
if (cat == CharCategories::EndOfLine)
|
||||||
{
|
{
|
||||||
lines.emplace_back(line_begin.base(), it.base());
|
m_current = StringView{m_remaining.begin(), it.base()};
|
||||||
line_begin = it = it+1;
|
m_remaining = StringView{(it+1).base(), m_remaining.end()};
|
||||||
continue;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utf8It word_end = it+1;
|
Utf8It word_end = it+1;
|
||||||
while (word_end != end and categorize(*word_end, {'_'}) == cat)
|
while (word_end != m_remaining.end() and categorize(*word_end, {'_'}) == cat)
|
||||||
++word_end;
|
++word_end;
|
||||||
|
|
||||||
while (word_end > line_begin and
|
if (word_end > m_remaining.begin() and
|
||||||
utf8::column_distance(line_begin.base(), word_end.base()) >= max_width)
|
utf8::column_distance(m_remaining.begin(), word_end.base()) >= m_max_width)
|
||||||
{
|
{
|
||||||
auto line_end = last_word_end <= line_begin ?
|
auto line_end = last_word_end <= m_remaining.begin() ?
|
||||||
Utf8It{utf8::advance(line_begin.base(), text.end(), max_width), text}
|
Utf8It{utf8::advance(m_remaining.begin(), m_remaining.end(), m_max_width), m_remaining}
|
||||||
: last_word_end;
|
: last_word_end;
|
||||||
|
|
||||||
lines.emplace_back(line_begin.base(), line_end.base());
|
m_current = StringView{m_remaining.begin(), line_end.base()};
|
||||||
|
|
||||||
while (line_end != end and is_horizontal_blank(*line_end))
|
while (line_end != m_remaining.end() and is_horizontal_blank(*line_end))
|
||||||
++line_end;
|
++line_end;
|
||||||
|
|
||||||
if (line_end != end and *line_end == '\n')
|
if (line_end != m_remaining.end() and *line_end == '\n')
|
||||||
++line_end;
|
++line_end;
|
||||||
|
|
||||||
it = line_begin = line_end;
|
m_remaining = StringView{line_end.base(), m_remaining.end()};
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
if (cat == CharCategories::Word or cat == CharCategories::Punctuation)
|
if (cat == CharCategories::Word or cat == CharCategories::Punctuation)
|
||||||
last_word_end = word_end;
|
last_word_end = word_end;
|
||||||
|
|
||||||
if (word_end > line_begin)
|
if (word_end > m_remaining.begin())
|
||||||
it = word_end;
|
it = word_end;
|
||||||
}
|
}
|
||||||
if (line_begin != end)
|
m_current = m_remaining;
|
||||||
lines.emplace_back(line_begin.base(), text.end());
|
m_remaining = StringView{};
|
||||||
return lines;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename AppendFunc>
|
template<typename AppendFunc>
|
||||||
|
@ -378,7 +381,7 @@ UnitTest test_string{[]()
|
||||||
{
|
{
|
||||||
kak_assert(String("youpi ") + "matin" == "youpi matin");
|
kak_assert(String("youpi ") + "matin" == "youpi matin");
|
||||||
|
|
||||||
Vector<StringView> wrapped = wrap_lines("wrap this paragraph\n respecting whitespaces and much_too_long_words", 16);
|
auto wrapped = "wrap this paragraph\n respecting whitespaces and much_too_long_words" | wrap_at(16) | gather<Vector>();
|
||||||
kak_assert(wrapped.size() == 6);
|
kak_assert(wrapped.size() == 6);
|
||||||
kak_assert(wrapped[0] == "wrap this");
|
kak_assert(wrapped[0] == "wrap this");
|
||||||
kak_assert(wrapped[1] == "paragraph");
|
kak_assert(wrapped[1] == "paragraph");
|
||||||
|
@ -387,7 +390,7 @@ UnitTest test_string{[]()
|
||||||
kak_assert(wrapped[4] == "much_too_long_wo");
|
kak_assert(wrapped[4] == "much_too_long_wo");
|
||||||
kak_assert(wrapped[5] == "rds");
|
kak_assert(wrapped[5] == "rds");
|
||||||
|
|
||||||
Vector<StringView> wrapped2 = wrap_lines("error: unknown type", 7);
|
auto wrapped2 = "error: unknown type" | wrap_at(7) | gather<Vector>();
|
||||||
kak_assert(wrapped2.size() == 3);
|
kak_assert(wrapped2.size() == 3);
|
||||||
kak_assert(wrapped2[0] == "error:");
|
kak_assert(wrapped2[0] == "error:");
|
||||||
kak_assert(wrapped2[1] == "unknown");
|
kak_assert(wrapped2[1] == "unknown");
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "string.hh"
|
#include "string.hh"
|
||||||
#include "enum.hh"
|
#include "enum.hh"
|
||||||
#include "vector.hh"
|
#include "vector.hh"
|
||||||
|
#include "ranges.hh"
|
||||||
#include "optional.hh"
|
#include "optional.hh"
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
|
@ -61,7 +62,39 @@ bool subsequence_match(StringView str, StringView subseq);
|
||||||
|
|
||||||
String expand_tabs(StringView line, ColumnCount tabstop, ColumnCount col = 0);
|
String expand_tabs(StringView line, ColumnCount tabstop, ColumnCount col = 0);
|
||||||
|
|
||||||
Vector<StringView> wrap_lines(StringView text, ColumnCount max_width);
|
struct WrapView
|
||||||
|
{
|
||||||
|
struct Iterator : std::iterator<std::forward_iterator_tag, StringView>
|
||||||
|
{
|
||||||
|
Iterator(StringView text, ColumnCount max_width);
|
||||||
|
|
||||||
|
Iterator& operator++();
|
||||||
|
Iterator operator++(int) { auto copy = *this; ++(*this); return copy; }
|
||||||
|
|
||||||
|
bool operator==(Iterator other) const { return m_remaining == other.m_remaining and m_current == other.m_current; }
|
||||||
|
bool operator!=(Iterator other) const { return not (*this == other); }
|
||||||
|
|
||||||
|
StringView operator*() { return m_current; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
StringView m_current;
|
||||||
|
StringView m_remaining;
|
||||||
|
ColumnCount m_max_width;
|
||||||
|
};
|
||||||
|
|
||||||
|
Iterator begin() const { return {text, max_width}; }
|
||||||
|
Iterator end() const { return {{}, 1}; }
|
||||||
|
|
||||||
|
StringView text;
|
||||||
|
ColumnCount max_width;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto wrap_at(ColumnCount max_width)
|
||||||
|
{
|
||||||
|
return make_view_factory([=](StringView text) {
|
||||||
|
return WrapView{text, max_width};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
int str_to_int(StringView str); // throws on error
|
int str_to_int(StringView str); // throws on error
|
||||||
Optional<int> str_to_int_ifp(StringView str);
|
Optional<int> str_to_int_ifp(StringView str);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user