2011-09-02 18:51:20 +02:00
|
|
|
#include "display_buffer.hh"
|
|
|
|
|
2011-09-29 11:10:27 +02:00
|
|
|
#include "assert.hh"
|
2014-11-12 22:27:07 +01:00
|
|
|
#include "buffer.hh"
|
2016-07-28 10:41:47 +02:00
|
|
|
#include "buffer_utils.hh"
|
2018-04-07 07:36:39 +02:00
|
|
|
#include "face_registry.hh"
|
2014-11-12 22:27:07 +01:00
|
|
|
#include "utf8.hh"
|
2011-09-29 10:55:08 +02:00
|
|
|
|
2015-09-19 13:19:17 +02:00
|
|
|
#include "face_registry.hh"
|
|
|
|
|
2011-09-02 18:51:20 +02:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2017-06-15 19:12:21 +02:00
|
|
|
BufferIterator get_iterator(const Buffer& buffer, BufferCoord coord)
|
|
|
|
{
|
|
|
|
// Correct one past the end of line as next line
|
|
|
|
if (not buffer.is_end(coord) and coord.column == buffer[coord.line].length())
|
|
|
|
coord = coord.line+1;
|
|
|
|
return buffer.iterator_at(coord);
|
|
|
|
}
|
|
|
|
|
2014-11-12 22:27:07 +01:00
|
|
|
StringView DisplayAtom::content() const
|
|
|
|
{
|
|
|
|
switch (m_type)
|
|
|
|
{
|
2016-10-13 20:55:15 +02:00
|
|
|
case Range:
|
2014-11-12 22:27:07 +01:00
|
|
|
{
|
2015-04-23 22:38:45 +02:00
|
|
|
auto line = (*m_buffer)[m_range.begin.line];
|
|
|
|
if (m_range.begin.line == m_range.end.line)
|
|
|
|
return line.substr(m_range.begin.column, m_range.end.column - m_range.begin.column);
|
|
|
|
else if (m_range.begin.line+1 == m_range.end.line and m_range.end.column == 0)
|
|
|
|
return line.substr(m_range.begin.column);
|
2014-11-12 22:27:07 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Text:
|
2016-10-13 20:55:15 +02:00
|
|
|
case ReplacedRange:
|
2014-11-12 22:27:07 +01:00
|
|
|
return m_text;
|
|
|
|
}
|
|
|
|
kak_assert(false);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2016-09-22 21:36:26 +02:00
|
|
|
ColumnCount DisplayAtom::length() const
|
2014-11-12 22:27:07 +01:00
|
|
|
{
|
|
|
|
switch (m_type)
|
|
|
|
{
|
2016-10-13 20:55:15 +02:00
|
|
|
case Range:
|
2017-06-15 19:12:21 +02:00
|
|
|
return utf8::column_distance(get_iterator(*m_buffer, m_range.begin),
|
|
|
|
get_iterator(*m_buffer, m_range.end));
|
2014-11-12 22:27:07 +01:00
|
|
|
case Text:
|
2016-10-13 20:55:15 +02:00
|
|
|
case ReplacedRange:
|
2016-10-04 20:37:17 +02:00
|
|
|
return m_text.column_length();
|
2014-11-12 22:27:07 +01:00
|
|
|
}
|
|
|
|
kak_assert(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-22 21:36:26 +02:00
|
|
|
void DisplayAtom::trim_begin(ColumnCount count)
|
2013-06-28 00:03:11 +02:00
|
|
|
{
|
2016-10-13 20:55:15 +02:00
|
|
|
if (m_type == Range)
|
2017-06-15 19:12:21 +02:00
|
|
|
m_range.begin = utf8::advance(get_iterator(*m_buffer, m_range.begin),
|
|
|
|
get_iterator(*m_buffer, m_range.end),
|
2016-09-22 21:36:26 +02:00
|
|
|
count).coord();
|
2013-06-28 00:03:11 +02:00
|
|
|
else
|
2015-03-10 20:33:46 +01:00
|
|
|
m_text = m_text.substr(count).str();
|
2013-06-28 00:03:11 +02:00
|
|
|
}
|
|
|
|
|
2016-09-22 21:36:26 +02:00
|
|
|
void DisplayAtom::trim_end(ColumnCount count)
|
2013-06-28 00:03:11 +02:00
|
|
|
{
|
2016-10-13 20:55:15 +02:00
|
|
|
if (m_type == Range)
|
2017-06-15 19:12:21 +02:00
|
|
|
m_range.end = utf8::advance(get_iterator(*m_buffer, m_range.end),
|
|
|
|
get_iterator(*m_buffer, m_range.begin),
|
2016-09-22 21:36:26 +02:00
|
|
|
-count).coord();
|
2013-06-28 00:03:11 +02:00
|
|
|
else
|
2016-09-22 21:36:26 +02:00
|
|
|
m_text = m_text.substr(0, m_text.column_length() - count).str();
|
2013-06-28 00:03:11 +02:00
|
|
|
}
|
|
|
|
|
2013-07-23 20:11:26 +02:00
|
|
|
DisplayLine::DisplayLine(AtomList atoms)
|
|
|
|
: m_atoms(std::move(atoms))
|
|
|
|
{
|
|
|
|
compute_range();
|
|
|
|
}
|
|
|
|
|
2016-09-22 21:36:26 +02:00
|
|
|
DisplayLine::iterator DisplayLine::split(iterator it, BufferCoord pos)
|
2011-10-15 06:45:49 +02:00
|
|
|
{
|
2016-10-13 20:55:15 +02:00
|
|
|
kak_assert(it->type() == DisplayAtom::Range);
|
2013-07-24 14:55:57 +02:00
|
|
|
kak_assert(it->begin() < pos);
|
|
|
|
kak_assert(it->end() > pos);
|
2011-10-15 06:45:49 +02:00
|
|
|
|
2012-07-12 23:19:10 +02:00
|
|
|
DisplayAtom atom = *it;
|
2015-04-23 22:38:45 +02:00
|
|
|
atom.m_range.end = pos;
|
|
|
|
it->m_range.begin = pos;
|
2012-07-12 23:19:10 +02:00
|
|
|
return m_atoms.insert(it, std::move(atom));
|
2011-09-29 11:10:27 +02:00
|
|
|
}
|
|
|
|
|
2017-10-12 08:38:19 +02:00
|
|
|
DisplayLine::iterator DisplayLine::split(iterator it, ColumnCount count)
|
2015-07-23 14:58:23 +02:00
|
|
|
{
|
2017-10-12 08:38:19 +02:00
|
|
|
kak_assert(count > 0);
|
|
|
|
kak_assert(count < it->length());
|
2015-07-23 14:58:23 +02:00
|
|
|
|
2017-10-12 08:38:19 +02:00
|
|
|
if (it->type() == DisplayAtom::Text or it->type() == DisplayAtom::ReplacedRange)
|
|
|
|
{
|
|
|
|
DisplayAtom atom = *it;
|
|
|
|
atom.m_text = atom.m_text.substr(0, count).str();
|
|
|
|
it->m_text = it->m_text.substr(count).str();
|
|
|
|
return m_atoms.insert(it, std::move(atom));
|
|
|
|
}
|
|
|
|
auto pos = utf8::advance(get_iterator(it->buffer(), it->begin()),
|
2018-04-05 00:52:33 +02:00
|
|
|
get_iterator(it->buffer(), it->end()),
|
2017-10-12 08:38:19 +02:00
|
|
|
count).coord();
|
2022-10-17 08:46:26 +02:00
|
|
|
if (pos == it->begin()) // Can happen if we try to split in the middle of a multi-column codepoint
|
|
|
|
return m_atoms.insert(it, {it->buffer(), {pos, pos}, it->face});
|
|
|
|
if (pos == it->end())
|
|
|
|
return std::prev(m_atoms.insert(std::next(it), {it->buffer(), {pos, pos}, it->face}));
|
2017-10-12 08:38:19 +02:00
|
|
|
return split(it, pos);
|
2015-07-23 14:58:23 +02:00
|
|
|
}
|
|
|
|
|
2020-04-27 05:36:12 +02:00
|
|
|
DisplayLine::iterator DisplayLine::split(BufferCoord pos)
|
|
|
|
{
|
2021-02-16 02:32:05 +01:00
|
|
|
auto it = find_if(begin(), end(), [pos](const DisplayAtom& a) {
|
|
|
|
return (a.has_buffer_range() && a.begin() >= pos) ||
|
|
|
|
(a.type() == DisplayAtom::Range and a.end() > pos);
|
|
|
|
});
|
2020-04-27 05:36:12 +02:00
|
|
|
if (it == end() or it->begin() >= pos)
|
|
|
|
return it;
|
|
|
|
return ++split(it, pos);
|
|
|
|
}
|
|
|
|
|
2013-07-23 20:11:26 +02:00
|
|
|
DisplayLine::iterator DisplayLine::insert(iterator it, DisplayAtom atom)
|
|
|
|
{
|
2013-07-24 14:55:57 +02:00
|
|
|
if (atom.has_buffer_range())
|
2013-07-23 20:11:26 +02:00
|
|
|
{
|
2015-04-23 22:38:45 +02:00
|
|
|
m_range.begin = std::min(m_range.begin, atom.begin());
|
|
|
|
m_range.end = std::max(m_range.end, atom.end());
|
2013-07-23 20:11:26 +02:00
|
|
|
}
|
2020-04-27 05:36:12 +02:00
|
|
|
auto res = m_atoms.insert(it, std::move(atom));
|
|
|
|
compute_range();
|
|
|
|
return res;
|
2013-07-23 20:11:26 +02:00
|
|
|
}
|
|
|
|
|
2022-07-11 10:17:51 +02:00
|
|
|
DisplayAtom& DisplayLine::push_back(DisplayAtom atom)
|
2013-07-23 20:11:26 +02:00
|
|
|
{
|
2013-07-24 14:55:57 +02:00
|
|
|
if (atom.has_buffer_range())
|
2013-07-23 20:11:26 +02:00
|
|
|
{
|
2015-04-23 22:38:45 +02:00
|
|
|
m_range.begin = std::min(m_range.begin, atom.begin());
|
|
|
|
m_range.end = std::max(m_range.end, atom.end());
|
2013-07-23 20:11:26 +02:00
|
|
|
}
|
|
|
|
m_atoms.push_back(std::move(atom));
|
2022-07-11 10:17:51 +02:00
|
|
|
return m_atoms.back();
|
2013-07-23 20:11:26 +02:00
|
|
|
}
|
|
|
|
|
2013-12-11 22:38:43 +01:00
|
|
|
DisplayLine::iterator DisplayLine::erase(iterator beg, iterator end)
|
|
|
|
{
|
2017-01-08 23:30:15 +01:00
|
|
|
auto res = m_atoms.erase(beg, end);
|
2013-12-11 22:38:43 +01:00
|
|
|
compute_range();
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2012-10-22 13:20:02 +02:00
|
|
|
void DisplayLine::optimize()
|
|
|
|
{
|
2013-05-02 18:57:15 +02:00
|
|
|
if (m_atoms.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto atom_it = m_atoms.begin();
|
2017-08-23 03:42:00 +02:00
|
|
|
for (auto next_it = atom_it + 1; next_it != m_atoms.end(); ++next_it)
|
2012-10-22 13:20:02 +02:00
|
|
|
{
|
2013-05-02 18:57:15 +02:00
|
|
|
auto& atom = *atom_it;
|
2017-08-23 03:42:00 +02:00
|
|
|
auto& next = *next_it;
|
2012-10-22 13:20:02 +02:00
|
|
|
|
2017-08-23 03:42:00 +02:00
|
|
|
const auto type = atom.type();
|
|
|
|
if (type == next.type() and atom.face == next.face)
|
2013-05-02 18:57:15 +02:00
|
|
|
{
|
2013-07-24 14:55:57 +02:00
|
|
|
if (type == DisplayAtom::Text)
|
2017-08-23 03:42:00 +02:00
|
|
|
atom.m_text += next.m_text;
|
|
|
|
else if ((type == DisplayAtom::Range or
|
|
|
|
type == DisplayAtom::ReplacedRange) and
|
|
|
|
next.begin() == atom.end())
|
2013-05-02 18:57:15 +02:00
|
|
|
{
|
2017-08-23 03:42:00 +02:00
|
|
|
atom.m_range.end = next.end();
|
|
|
|
if (type == DisplayAtom::ReplacedRange)
|
|
|
|
atom.m_text += next.m_text;
|
2012-10-22 13:20:02 +02:00
|
|
|
}
|
2017-08-23 03:42:00 +02:00
|
|
|
else
|
|
|
|
*++atom_it = std::move(*next_it);
|
2012-10-22 13:20:02 +02:00
|
|
|
}
|
2013-05-02 18:57:15 +02:00
|
|
|
else
|
2017-08-23 03:42:00 +02:00
|
|
|
*++atom_it = std::move(*next_it);
|
2012-10-22 13:20:02 +02:00
|
|
|
}
|
2017-08-23 03:42:00 +02:00
|
|
|
m_atoms.erase(atom_it+1, m_atoms.end());
|
2012-10-22 13:20:02 +02:00
|
|
|
}
|
|
|
|
|
2016-09-22 21:36:26 +02:00
|
|
|
ColumnCount DisplayLine::length() const
|
2013-04-04 18:50:00 +02:00
|
|
|
{
|
2016-09-22 21:36:26 +02:00
|
|
|
ColumnCount len = 0;
|
2013-04-04 18:50:00 +02:00
|
|
|
for (auto& atom : m_atoms)
|
2013-07-24 14:55:57 +02:00
|
|
|
len += atom.length();
|
2013-04-04 18:50:00 +02:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2022-07-13 04:24:14 +02:00
|
|
|
bool DisplayLine::trim(ColumnCount front, ColumnCount col_count)
|
2013-06-28 00:03:11 +02:00
|
|
|
{
|
2022-07-13 04:24:14 +02:00
|
|
|
return trim_from(0_col, front, col_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DisplayLine::trim_from(ColumnCount first_col, ColumnCount front, ColumnCount col_count)
|
|
|
|
{
|
|
|
|
auto it = begin();
|
|
|
|
while (first_col > 0 and it != end())
|
2013-06-28 00:03:11 +02:00
|
|
|
{
|
2022-07-13 04:24:14 +02:00
|
|
|
auto len = it->length();
|
|
|
|
if (len <= first_col)
|
2022-07-10 02:04:15 +02:00
|
|
|
{
|
|
|
|
++it;
|
2022-07-13 04:24:14 +02:00
|
|
|
first_col -= len;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
it = ++split(it, front);
|
|
|
|
first_col = 0;
|
2022-07-10 02:04:15 +02:00
|
|
|
}
|
2022-07-13 04:24:14 +02:00
|
|
|
}
|
2022-07-10 02:04:15 +02:00
|
|
|
|
2022-07-13 04:24:14 +02:00
|
|
|
while (front > 0 and it != end())
|
|
|
|
{
|
2013-07-24 14:55:57 +02:00
|
|
|
auto len = it->length();
|
2022-07-13 04:24:14 +02:00
|
|
|
if (len <= front)
|
2013-06-28 00:03:11 +02:00
|
|
|
{
|
|
|
|
m_atoms.erase(it);
|
2022-07-13 04:24:14 +02:00
|
|
|
front -= len;
|
2013-06-28 00:03:11 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-07-13 04:24:14 +02:00
|
|
|
it->trim_begin(front);
|
|
|
|
front = 0;
|
2013-06-28 00:03:11 +02:00
|
|
|
}
|
|
|
|
}
|
2022-07-13 04:24:14 +02:00
|
|
|
|
|
|
|
it = begin();
|
2016-09-22 21:36:26 +02:00
|
|
|
for (; it != end() and col_count > 0; ++it)
|
|
|
|
col_count -= it->length();
|
2013-06-28 00:03:11 +02:00
|
|
|
|
2019-11-24 01:39:33 +01:00
|
|
|
bool did_trim = it != end() || col_count < 0;
|
2016-09-22 21:36:26 +02:00
|
|
|
if (col_count < 0)
|
|
|
|
(it-1)->trim_end(-col_count);
|
2013-06-28 00:03:11 +02:00
|
|
|
m_atoms.erase(it, end());
|
2013-07-23 20:11:26 +02:00
|
|
|
|
|
|
|
compute_range();
|
2019-11-24 01:39:33 +01:00
|
|
|
return did_trim;
|
2013-07-23 20:11:26 +02:00
|
|
|
}
|
|
|
|
|
2014-04-02 23:41:06 +02:00
|
|
|
const BufferRange init_range{ {INT_MAX, INT_MAX}, {INT_MIN, INT_MIN} };
|
2013-12-11 22:38:43 +01:00
|
|
|
|
2013-07-23 20:11:26 +02:00
|
|
|
void DisplayLine::compute_range()
|
|
|
|
{
|
2013-12-11 22:38:43 +01:00
|
|
|
m_range = init_range;
|
2013-07-23 20:11:26 +02:00
|
|
|
for (auto& atom : m_atoms)
|
|
|
|
{
|
2013-07-24 14:55:57 +02:00
|
|
|
if (not atom.has_buffer_range())
|
2013-07-23 20:11:26 +02:00
|
|
|
continue;
|
2015-04-23 22:38:45 +02:00
|
|
|
m_range.begin = std::min(m_range.begin, atom.begin());
|
|
|
|
m_range.end = std::max(m_range.end, atom.end());
|
2013-07-23 20:11:26 +02:00
|
|
|
}
|
2013-12-11 22:38:43 +01:00
|
|
|
if (m_range == init_range)
|
|
|
|
m_range = { { 0, 0 }, { 0, 0 } };
|
2015-04-23 22:38:45 +02:00
|
|
|
kak_assert(m_range.begin <= m_range.end);
|
2013-06-28 00:03:11 +02:00
|
|
|
}
|
|
|
|
|
2012-07-12 23:51:13 +02:00
|
|
|
void DisplayBuffer::compute_range()
|
|
|
|
{
|
2013-12-11 22:38:43 +01:00
|
|
|
m_range = init_range;
|
2012-07-12 23:51:13 +02:00
|
|
|
for (auto& line : m_lines)
|
|
|
|
{
|
2015-04-23 22:38:45 +02:00
|
|
|
m_range.begin = std::min(line.range().begin, m_range.begin);
|
|
|
|
m_range.end = std::max(line.range().end, m_range.end);
|
2012-07-12 23:51:13 +02:00
|
|
|
}
|
2013-12-11 22:38:43 +01:00
|
|
|
if (m_range == init_range)
|
|
|
|
m_range = { { 0, 0 }, { 0, 0 } };
|
2015-04-23 22:38:45 +02:00
|
|
|
kak_assert(m_range.begin <= m_range.end);
|
2012-07-12 23:51:13 +02:00
|
|
|
}
|
|
|
|
|
2012-10-22 13:20:02 +02:00
|
|
|
void DisplayBuffer::optimize()
|
|
|
|
{
|
|
|
|
for (auto& line : m_lines)
|
|
|
|
line.optimize();
|
|
|
|
}
|
2015-09-19 13:19:17 +02:00
|
|
|
|
2021-08-28 05:53:01 +02:00
|
|
|
DisplayLine parse_display_line(StringView line, Face& face, const FaceRegistry& faces, const HashMap<String, DisplayLine>& builtins)
|
2015-09-19 13:19:17 +02:00
|
|
|
{
|
|
|
|
DisplayLine res;
|
|
|
|
bool was_antislash = false;
|
|
|
|
auto pos = line.begin();
|
|
|
|
String content;
|
|
|
|
for (auto it = line.begin(), end = line.end(); it != end; ++it)
|
|
|
|
{
|
|
|
|
const char c = *it;
|
|
|
|
if (c == '{')
|
|
|
|
{
|
|
|
|
if (was_antislash)
|
|
|
|
{
|
|
|
|
content += StringView{pos, it};
|
|
|
|
content.back() = '{';
|
|
|
|
pos = it + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
content += StringView{pos, it};
|
2020-04-26 11:27:44 +02:00
|
|
|
if (not content.empty())
|
|
|
|
res.push_back({std::move(content), face});
|
2015-09-19 13:19:17 +02:00
|
|
|
content.clear();
|
|
|
|
auto closing = std::find(it+1, end, '}');
|
|
|
|
if (closing == end)
|
|
|
|
throw runtime_error("unclosed face definition");
|
2017-03-10 10:06:37 +01:00
|
|
|
if (*(it+1) == '{' and closing+1 != end and *(closing+1) == '}')
|
|
|
|
{
|
|
|
|
auto builtin_it = builtins.find(StringView{it+2, closing});
|
|
|
|
if (builtin_it == builtins.end())
|
|
|
|
throw runtime_error(format("undefined atom {}", StringView{it+2, closing}));
|
|
|
|
for (auto& atom : builtin_it->value)
|
|
|
|
res.push_back(atom);
|
|
|
|
// closing is now at the first char of "}}", advance it to the second
|
|
|
|
++closing;
|
|
|
|
}
|
2019-11-28 10:22:40 +01:00
|
|
|
else if (closing == it+2 and *(it+1) == '\\')
|
|
|
|
{
|
|
|
|
pos = closing + 1;
|
|
|
|
break;
|
|
|
|
}
|
2017-03-10 10:06:37 +01:00
|
|
|
else
|
2018-04-07 07:36:39 +02:00
|
|
|
face = faces[{it+1, closing}];
|
2015-09-19 13:19:17 +02:00
|
|
|
it = closing;
|
|
|
|
pos = closing + 1;
|
|
|
|
}
|
|
|
|
}
|
2019-11-24 07:25:14 +01:00
|
|
|
if (c == '\n' or c == '\t') // line breaks and tabs are forbidden, replace with space
|
2016-12-07 14:43:27 +01:00
|
|
|
{
|
|
|
|
content += StringView{pos, it+1};
|
|
|
|
content.back() = ' ';
|
|
|
|
pos = it + 1;
|
|
|
|
}
|
|
|
|
|
2015-09-19 13:19:17 +02:00
|
|
|
if (c == '\\')
|
|
|
|
{
|
|
|
|
if (was_antislash)
|
|
|
|
{
|
|
|
|
content += StringView{pos, it};
|
|
|
|
pos = it + 1;
|
|
|
|
was_antislash = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
was_antislash = true;
|
|
|
|
}
|
2016-12-07 14:43:27 +01:00
|
|
|
else
|
2015-09-20 12:23:10 +02:00
|
|
|
was_antislash = false;
|
2015-09-19 13:19:17 +02:00
|
|
|
}
|
|
|
|
content += StringView{pos, line.end()};
|
|
|
|
if (not content.empty())
|
|
|
|
res.push_back({std::move(content), face});
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2021-08-28 05:53:01 +02:00
|
|
|
DisplayLine parse_display_line(StringView line, const FaceRegistry& faces, const HashMap<String, DisplayLine>& builtins)
|
|
|
|
{
|
|
|
|
Face face{};
|
|
|
|
return parse_display_line(line, face, faces, builtins);
|
|
|
|
}
|
|
|
|
|
|
|
|
DisplayLineList parse_display_line_list(StringView content, const FaceRegistry& faces, const HashMap<String, DisplayLine>& builtins)
|
|
|
|
{
|
|
|
|
return content | split<StringView>('\n')
|
|
|
|
| transform([&, face=Face{}](StringView s) mutable {
|
|
|
|
return parse_display_line(s, face, faces, builtins);
|
|
|
|
})
|
|
|
|
| gather<DisplayLineList>();
|
|
|
|
}
|
|
|
|
|
2011-09-02 18:51:20 +02:00
|
|
|
}
|