summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxime Coste <mawww@kakoune.org>2020-04-06 11:13:24 +1000
committerMaxime Coste <mawww@kakoune.org>2020-04-06 11:23:47 +1000
commitf7a2ecfacb566a219d9deef3a5cfaa2d1c5bd404 (patch)
treeeb044d77d2c31c40b4b99767b22bca9e505f44ba
parentc585107ab5e7155f7da648c3752cf360f7156177 (diff)
Support empty ranges in replace-ranges highlighter
-rw-r--r--doc/pages/changelog.asciidoc2
-rw-r--r--doc/pages/options.asciidoc40
-rw-r--r--src/changes.hh35
-rw-r--r--src/highlighters.cc48
4 files changed, 78 insertions, 47 deletions
diff --git a/doc/pages/changelog.asciidoc b/doc/pages/changelog.asciidoc
index 4e706c89..a4ed429b 100644
--- a/doc/pages/changelog.asciidoc
+++ b/doc/pages/changelog.asciidoc
@@ -5,6 +5,8 @@ released versions.
== Development version
+* replace-ranges highlighter now support empty ranges
+
* `%var{...}` now expands to list of strings, `$kak_quoted_...` now work
as expected with these.
diff --git a/doc/pages/options.asciidoc b/doc/pages/options.asciidoc
index d074accf..db070781 100644
--- a/doc/pages/options.asciidoc
+++ b/doc/pages/options.asciidoc
@@ -88,19 +88,37 @@ are exclusively available to built-in options.
*range-specs*::
a timestamp (like `%val{timestamp}`,
see <<expansions#value-expansions,`:doc expansions value-expansions`>>)
- followed by a list of range descriptors. Each range descriptor must use
- 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
- of the line to the first byte of the first character, _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 and _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. See
- <<highlighters#specs-highlighters,`:doc highlighters specs-highlighters`>>)
+ followed by a list of range descriptors.
+
+ Each range descriptor must use the syntax `a.b,c.d|string` or
+ `a.b+length|string`, with:
+
+ * _a_ is the line containing the first character
+
+ * _b_ is the number of bytes from the start of the line to the
+ first byte of the first character
+
+ * _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
+ See <<highlighters#specs-highlighters,`:doc highlighters specs-highlighters`>>)
+
*line-specs*::
a list of a line number and a corresponding flag (`<line>|<flag
text>`), except for the first element which is just the timestamp
diff --git a/src/changes.hh b/src/changes.hh
index 5b64cbec..5ebb3ded 100644
--- a/src/changes.hh
+++ b/src/changes.hh
@@ -28,6 +28,21 @@ struct ForwardChangesTracker
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);
+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>
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)
- {
- 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);
- }
+ update_range(changes_tracker, range, advance_while_relevant);
}
template<typename RangeContainer>
@@ -69,15 +76,7 @@ void update_backward(ConstArrayView<Buffer::Change> changes, RangeContainer& ran
};
for (auto& range : ranges)
- {
- 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);
- }
+ update_range(changes_tracker, range, advance_while_relevant);
}
template<typename RangeContainer>
diff --git a/src/highlighters.cc b/src/highlighters.cc
index 464f78cd..d3fe1164 100644
--- a/src/highlighters.cc
+++ b/src/highlighters.cc
@@ -89,8 +89,8 @@ void replace_range(DisplayBuffer& display_buffer,
BufferCoord begin, BufferCoord end, T func)
{
// tolerate begin > end as that can be triggered by wrong encodings
- if (begin >= end or end <= display_buffer.range().begin
- or begin >= display_buffer.range().end)
+ if (begin > end or end <= display_buffer.range().begin
+ or begin >= display_buffer.range().end)
return;
for (auto& line : display_buffer.lines())
@@ -112,7 +112,9 @@ void replace_range(DisplayBuffer& display_buffer,
atom_it = ++line.split(atom_it, 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())
atom_it = line.split(atom_it, end);
@@ -1448,8 +1450,15 @@ private:
String m_default_face;
};
+bool is_empty(const InclusiveBufferRange& range)
+{
+ return range.last < BufferCoord{0,0};
+}
+
String option_to_string(InclusiveBufferRange range)
{
+ if (is_empty(range))
+ return format("{}.{}+0", range.first.line+1, range.first.column+1);
return format("{}.{},{}.{}",
range.first.line+1, range.first.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,
str_to_int({dot_beg+1, sep}) - 1};
- const bool len = (*sep == '+');
- const BufferCoord last{len ? first.line : str_to_int({sep+1, dot_end}) - 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)
+ throw runtime_error("coordinates elements should be >= 1");
+
+ if (*sep == '+')
+ {
+ auto len = str_to_int({sep+1, str.end()});
+ return {first, len == 0 ? BufferCoord{-1,-1} : BufferCoord{first.line, first.column + len}};
+ }
- if (first.line < 0 or first.column < 0 or last.line < 0 or last.column < 0)
+ 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");
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>();
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
{
- auto& r = std::get<0>(range);
- if (buffer.is_valid(r.first) and (buffer.is_valid(r.last) and not buffer.is_end(r.last)))
- highlight_range(display_buffer, r.first, buffer.char_next(r.last), false,
- apply_face(context.context.faces()[std::get<1>(range)]));
+ if (buffer.is_valid(range.first) and (buffer.is_valid(range.last) and not buffer.is_end(range.last)))
+ highlight_range(display_buffer, range.first, buffer.char_next(range.last), false,
+ apply_face(context.context.faces()[face]));
}
catch (runtime_error&)
{}
@@ -1564,15 +1577,14 @@ private:
auto& range_and_faces = context.context.options()[m_option_name].get_mutable<RangeAndStringList>();
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
{
- auto& r = std::get<0>(range);
- if (buffer.is_valid(r.first) and buffer.is_valid(r.last))
+ if (buffer.is_valid(range.first) and (buffer.is_valid(range.last) or is_empty(range)))
{
- auto replacement = parse_display_line(std::get<1>(range), context.context.faces());
- replace_range(display_buffer, r.first, buffer.char_next(r.last),
+ auto replacement = parse_display_line(spec, context.context.faces());
+ replace_range(display_buffer, range.first, is_empty(range) ? range.first : buffer.char_next(range.last),
[&](DisplayLine& line, int beg_idx, int end_idx){
auto it = line.erase(line.begin() + beg_idx, line.begin() + end_idx);
for (auto& atom : replacement)