Support empty ranges in replace-ranges highlighter
This commit is contained in:
parent
c585107ab5
commit
f7a2ecfacb
|
@ -5,6 +5,8 @@ released versions.
|
||||||
|
|
||||||
== Development version
|
== Development version
|
||||||
|
|
||||||
|
* replace-ranges highlighter now support empty ranges
|
||||||
|
|
||||||
* `%var{...}` now expands to list of strings, `$kak_quoted_...` now work
|
* `%var{...}` now expands to list of strings, `$kak_quoted_...` now work
|
||||||
as expected with these.
|
as expected with these.
|
||||||
|
|
||||||
|
|
|
@ -88,19 +88,37 @@ are exclusively available to built-in options.
|
||||||
*range-specs*::
|
*range-specs*::
|
||||||
a timestamp (like `%val{timestamp}`,
|
a timestamp (like `%val{timestamp}`,
|
||||||
see <<expansions#value-expansions,`:doc expansions value-expansions`>>)
|
see <<expansions#value-expansions,`:doc expansions value-expansions`>>)
|
||||||
followed by a list of range descriptors. Each range descriptor must use
|
followed by a list of range descriptors.
|
||||||
the syntax `a.b,c.d|string` or `a.b+length|string`, where _a_ is the line
|
|
||||||
containing the first character, _b_ is the number of bytes from the start
|
Each range descriptor must use the syntax `a.b,c.d|string` or
|
||||||
of the line to the first byte of the first character, _c_ is the line
|
`a.b+length|string`, with:
|
||||||
containing the last character, _d_ is the number of bytes from the start of
|
|
||||||
the line to the first byte of the last character, _length_ is the length of
|
* _a_ is the line containing the first character
|
||||||
the range in bytes and _string_ is an arbitrary string which is associated
|
|
||||||
with the range. All numeric fields are 1-based. When the `update-option` is
|
* _b_ is the number of bytes from the start of the line to the
|
||||||
used on an option of this type, its ranges gets updated according to all the
|
first byte of the first character
|
||||||
buffer modifications that happened since its timestamp. See
|
|
||||||
<<highlighters#specs-highlighters,`:doc highlighters specs-highlighters`>>)
|
* _c_ is the line containing the last character
|
||||||
|
|
||||||
|
* _d_ is the number of bytes from the start of the line to the
|
||||||
|
first byte of the last character
|
||||||
|
|
||||||
|
* _length_ is the length of the range in bytes, if 0 the range
|
||||||
|
is empty, but still valid.
|
||||||
|
|
||||||
|
* _string_ is an arbitrary string which is associated with
|
||||||
|
the range.
|
||||||
|
|
||||||
|
All numeric fields are 1-based.
|
||||||
|
|
||||||
|
When the `update-option` is used on an option of this type, its
|
||||||
|
ranges gets updated according to all the buffer modifications that
|
||||||
|
happened since its timestamp.
|
||||||
|
|
||||||
`set -add` appends the new pair to the list
|
`set -add` appends the new pair to the list
|
||||||
|
|
||||||
|
See <<highlighters#specs-highlighters,`:doc highlighters specs-highlighters`>>)
|
||||||
|
|
||||||
*line-specs*::
|
*line-specs*::
|
||||||
a list of a line number and a corresponding flag (`<line>|<flag
|
a list of a line number and a corresponding flag (`<line>|<flag
|
||||||
text>`), except for the first element which is just the timestamp
|
text>`), except for the first element which is just the timestamp
|
||||||
|
|
|
@ -28,6 +28,21 @@ struct ForwardChangesTracker
|
||||||
const Buffer::Change* forward_sorted_until(const Buffer::Change* first, const Buffer::Change* last);
|
const Buffer::Change* forward_sorted_until(const Buffer::Change* first, const Buffer::Change* last);
|
||||||
const Buffer::Change* backward_sorted_until(const Buffer::Change* first, const Buffer::Change* last);
|
const Buffer::Change* backward_sorted_until(const Buffer::Change* first, const Buffer::Change* last);
|
||||||
|
|
||||||
|
template<typename Range, typename AdvanceFunc>
|
||||||
|
auto update_range(ForwardChangesTracker& changes_tracker, Range& range, AdvanceFunc&& advance_while_relevant)
|
||||||
|
{
|
||||||
|
auto& first = get_first(range);
|
||||||
|
auto& last = get_last(range);
|
||||||
|
advance_while_relevant(first);
|
||||||
|
first = changes_tracker.get_new_coord_tolerant(first);
|
||||||
|
|
||||||
|
if (last < BufferCoord{0,0})
|
||||||
|
return;
|
||||||
|
|
||||||
|
advance_while_relevant(last);
|
||||||
|
last = changes_tracker.get_new_coord_tolerant(last);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename RangeContainer>
|
template<typename RangeContainer>
|
||||||
void update_forward(ConstArrayView<Buffer::Change> changes, RangeContainer& ranges)
|
void update_forward(ConstArrayView<Buffer::Change> changes, RangeContainer& ranges)
|
||||||
{
|
{
|
||||||
|
@ -39,15 +54,7 @@ void update_forward(ConstArrayView<Buffer::Change> changes, RangeContainer& rang
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto& range : ranges)
|
for (auto& range : ranges)
|
||||||
{
|
update_range(changes_tracker, range, advance_while_relevant);
|
||||||
auto& first = get_first(range);
|
|
||||||
auto& last = get_last(range);
|
|
||||||
advance_while_relevant(first);
|
|
||||||
first = changes_tracker.get_new_coord_tolerant(first);
|
|
||||||
|
|
||||||
advance_while_relevant(last);
|
|
||||||
last = changes_tracker.get_new_coord_tolerant(last);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RangeContainer>
|
template<typename RangeContainer>
|
||||||
|
@ -69,15 +76,7 @@ void update_backward(ConstArrayView<Buffer::Change> changes, RangeContainer& ran
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto& range : ranges)
|
for (auto& range : ranges)
|
||||||
{
|
update_range(changes_tracker, range, advance_while_relevant);
|
||||||
auto& first = get_first(range);
|
|
||||||
auto& last = get_last(range);
|
|
||||||
advance_while_relevant(first);
|
|
||||||
first = changes_tracker.get_new_coord_tolerant(first);
|
|
||||||
|
|
||||||
advance_while_relevant(last);
|
|
||||||
last = changes_tracker.get_new_coord_tolerant(last);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RangeContainer>
|
template<typename RangeContainer>
|
||||||
|
|
|
@ -89,7 +89,7 @@ void replace_range(DisplayBuffer& display_buffer,
|
||||||
BufferCoord begin, BufferCoord end, T func)
|
BufferCoord begin, BufferCoord end, T func)
|
||||||
{
|
{
|
||||||
// tolerate begin > end as that can be triggered by wrong encodings
|
// tolerate begin > end as that can be triggered by wrong encodings
|
||||||
if (begin >= end or end <= display_buffer.range().begin
|
if (begin > end or end <= display_buffer.range().begin
|
||||||
or begin >= display_buffer.range().end)
|
or begin >= display_buffer.range().end)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -112,7 +112,9 @@ void replace_range(DisplayBuffer& display_buffer,
|
||||||
atom_it = ++line.split(atom_it, begin);
|
atom_it = ++line.split(atom_it, begin);
|
||||||
beg_idx = atom_it - line.begin();
|
beg_idx = atom_it - line.begin();
|
||||||
}
|
}
|
||||||
if (end <= atom_it->end())
|
if (end == atom_it->begin())
|
||||||
|
end_idx = atom_it - line.begin();
|
||||||
|
else if (end <= atom_it->end())
|
||||||
{
|
{
|
||||||
if (end < atom_it->end())
|
if (end < atom_it->end())
|
||||||
atom_it = line.split(atom_it, end);
|
atom_it = line.split(atom_it, end);
|
||||||
|
@ -1448,8 +1450,15 @@ private:
|
||||||
String m_default_face;
|
String m_default_face;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool is_empty(const InclusiveBufferRange& range)
|
||||||
|
{
|
||||||
|
return range.last < BufferCoord{0,0};
|
||||||
|
}
|
||||||
|
|
||||||
String option_to_string(InclusiveBufferRange range)
|
String option_to_string(InclusiveBufferRange range)
|
||||||
{
|
{
|
||||||
|
if (is_empty(range))
|
||||||
|
return format("{}.{}+0", range.first.line+1, range.first.column+1);
|
||||||
return format("{}.{},{}.{}",
|
return format("{}.{},{}.{}",
|
||||||
range.first.line+1, range.first.column+1,
|
range.first.line+1, range.first.column+1,
|
||||||
range.last.line+1, range.last.column+1);
|
range.last.line+1, range.last.column+1);
|
||||||
|
@ -1468,12 +1477,17 @@ InclusiveBufferRange option_from_string(Meta::Type<InclusiveBufferRange>, String
|
||||||
const BufferCoord first{str_to_int({str.begin(), dot_beg}) - 1,
|
const BufferCoord first{str_to_int({str.begin(), dot_beg}) - 1,
|
||||||
str_to_int({dot_beg+1, sep}) - 1};
|
str_to_int({dot_beg+1, sep}) - 1};
|
||||||
|
|
||||||
const bool len = (*sep == '+');
|
if (first.line < 0 or first.column < 0)
|
||||||
const BufferCoord last{len ? first.line : str_to_int({sep+1, dot_end}) - 1,
|
throw runtime_error("coordinates elements should be >= 1");
|
||||||
len ? first.column + str_to_int({sep+1, str.end()}) - 1
|
|
||||||
: str_to_int({dot_end+1, str.end()}) - 1 };
|
|
||||||
|
|
||||||
if (first.line < 0 or first.column < 0 or last.line < 0 or last.column < 0)
|
if (*sep == '+')
|
||||||
|
{
|
||||||
|
auto len = str_to_int({sep+1, str.end()});
|
||||||
|
return {first, len == 0 ? BufferCoord{-1,-1} : BufferCoord{first.line, first.column + len}};
|
||||||
|
}
|
||||||
|
|
||||||
|
const BufferCoord last{str_to_int({sep+1, dot_end}) - 1, str_to_int({dot_end+1, str.end()}) - 1};
|
||||||
|
if (last.line < 0 or last.column < 0)
|
||||||
throw runtime_error("coordinates elements should be >= 1");
|
throw runtime_error("coordinates elements should be >= 1");
|
||||||
|
|
||||||
return { std::min(first, last), std::max(first, last) };
|
return { std::min(first, last), std::max(first, last) };
|
||||||
|
@ -1522,14 +1536,13 @@ private:
|
||||||
auto& range_and_faces = context.context.options()[m_option_name].get_mutable<RangeAndStringList>();
|
auto& range_and_faces = context.context.options()[m_option_name].get_mutable<RangeAndStringList>();
|
||||||
update_ranges(buffer, range_and_faces.prefix, range_and_faces.list);
|
update_ranges(buffer, range_and_faces.prefix, range_and_faces.list);
|
||||||
|
|
||||||
for (auto& range : range_and_faces.list)
|
for (auto& [range, face] : range_and_faces.list)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto& r = std::get<0>(range);
|
if (buffer.is_valid(range.first) and (buffer.is_valid(range.last) and not buffer.is_end(range.last)))
|
||||||
if (buffer.is_valid(r.first) and (buffer.is_valid(r.last) and not buffer.is_end(r.last)))
|
highlight_range(display_buffer, range.first, buffer.char_next(range.last), false,
|
||||||
highlight_range(display_buffer, r.first, buffer.char_next(r.last), false,
|
apply_face(context.context.faces()[face]));
|
||||||
apply_face(context.context.faces()[std::get<1>(range)]));
|
|
||||||
}
|
}
|
||||||
catch (runtime_error&)
|
catch (runtime_error&)
|
||||||
{}
|
{}
|
||||||
|
@ -1564,15 +1577,14 @@ private:
|
||||||
auto& range_and_faces = context.context.options()[m_option_name].get_mutable<RangeAndStringList>();
|
auto& range_and_faces = context.context.options()[m_option_name].get_mutable<RangeAndStringList>();
|
||||||
update_ranges(buffer, range_and_faces.prefix, range_and_faces.list);
|
update_ranges(buffer, range_and_faces.prefix, range_and_faces.list);
|
||||||
|
|
||||||
for (auto& range : range_and_faces.list)
|
for (auto& [range, spec] : range_and_faces.list)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto& r = std::get<0>(range);
|
if (buffer.is_valid(range.first) and (buffer.is_valid(range.last) or is_empty(range)))
|
||||||
if (buffer.is_valid(r.first) and buffer.is_valid(r.last))
|
|
||||||
{
|
{
|
||||||
auto replacement = parse_display_line(std::get<1>(range), context.context.faces());
|
auto replacement = parse_display_line(spec, context.context.faces());
|
||||||
replace_range(display_buffer, r.first, buffer.char_next(r.last),
|
replace_range(display_buffer, range.first, is_empty(range) ? range.first : buffer.char_next(range.last),
|
||||||
[&](DisplayLine& line, int beg_idx, int end_idx){
|
[&](DisplayLine& line, int beg_idx, int end_idx){
|
||||||
auto it = line.erase(line.begin() + beg_idx, line.begin() + end_idx);
|
auto it = line.erase(line.begin() + beg_idx, line.begin() + end_idx);
|
||||||
for (auto& atom : replacement)
|
for (auto& atom : replacement)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user