Support multi-line replace-ranges
This likely has lots of rough edges, but should be an initial proof of concept to support folding.
This commit is contained in:
parent
65620fb830
commit
954373d3cf
|
@ -111,6 +111,14 @@ DisplayLine::iterator DisplayLine::split(iterator it, ColumnCount count)
|
||||||
return split(it, pos);
|
return split(it, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisplayLine::iterator DisplayLine::split(BufferCoord pos)
|
||||||
|
{
|
||||||
|
auto it = find_if(begin(), end(), [pos](const DisplayAtom& a) { return a.type() == DisplayAtom::Range and a.end() > pos; });
|
||||||
|
if (it == end() or it->begin() >= pos)
|
||||||
|
return it;
|
||||||
|
return ++split(it, pos);
|
||||||
|
}
|
||||||
|
|
||||||
DisplayLine::iterator DisplayLine::insert(iterator it, DisplayAtom atom)
|
DisplayLine::iterator DisplayLine::insert(iterator it, DisplayAtom atom)
|
||||||
{
|
{
|
||||||
if (atom.has_buffer_range())
|
if (atom.has_buffer_range())
|
||||||
|
@ -118,7 +126,9 @@ DisplayLine::iterator DisplayLine::insert(iterator it, DisplayAtom atom)
|
||||||
m_range.begin = std::min(m_range.begin, atom.begin());
|
m_range.begin = std::min(m_range.begin, atom.begin());
|
||||||
m_range.end = std::max(m_range.end, atom.end());
|
m_range.end = std::max(m_range.end, atom.end());
|
||||||
}
|
}
|
||||||
return m_atoms.insert(it, std::move(atom));
|
auto res = m_atoms.insert(it, std::move(atom));
|
||||||
|
compute_range();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayLine::push_back(DisplayAtom atom)
|
void DisplayLine::push_back(DisplayAtom atom)
|
||||||
|
|
|
@ -126,7 +126,18 @@ public:
|
||||||
// returns an iterator to the first atom
|
// returns an iterator to the first atom
|
||||||
iterator split(iterator it, ColumnCount pos);
|
iterator split(iterator it, ColumnCount pos);
|
||||||
|
|
||||||
|
iterator split(BufferCoord pos);
|
||||||
|
|
||||||
iterator insert(iterator it, DisplayAtom atom);
|
iterator insert(iterator it, DisplayAtom atom);
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
iterator insert(iterator it, It beg, It end)
|
||||||
|
{
|
||||||
|
auto res = m_atoms.insert(it, beg, end);
|
||||||
|
compute_range();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
iterator erase(iterator beg, iterator end);
|
iterator erase(iterator beg, iterator end);
|
||||||
void push_back(DisplayAtom atom);
|
void push_back(DisplayAtom atom);
|
||||||
|
|
||||||
|
|
|
@ -92,41 +92,37 @@ void replace_range(DisplayBuffer& display_buffer,
|
||||||
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;
|
return;
|
||||||
|
|
||||||
for (auto& line : display_buffer.lines())
|
auto& lines = display_buffer.lines();
|
||||||
{
|
auto first_it = std::lower_bound(lines.begin(), lines.end(), begin, [](const DisplayLine& l, const BufferCoord& c) { return l.range().end < c; });
|
||||||
auto& range = line.range();
|
if (first_it == lines.end())
|
||||||
if ((begin == end) and begin == range.end)
|
return;
|
||||||
return func(line, line.end());
|
|
||||||
|
|
||||||
if (range.end <= begin or end < range.begin)
|
auto first_atom_it = std::find_if(first_it->begin(), first_it->end(), [&begin](const DisplayAtom& a) { return a.has_buffer_range() and a.end() > begin; });
|
||||||
continue;
|
first_atom_it = first_it->split(begin);
|
||||||
|
|
||||||
int beg_idx = -1, end_idx = -1;
|
auto last_it = std::lower_bound(first_it, lines.end(), end, [](const DisplayLine& l, const BufferCoord& c) { return l.range().end < c; });
|
||||||
for (auto atom_it = line.begin(); atom_it != line.end(); ++atom_it)
|
|
||||||
{
|
|
||||||
if (not atom_it->has_buffer_range() or
|
|
||||||
end < atom_it->begin() or begin >= atom_it->end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (begin >= atom_it->begin())
|
if (first_it == last_it)
|
||||||
{
|
{
|
||||||
if (begin > atom_it->begin())
|
auto first_atom_idx = first_atom_it - first_it->begin();
|
||||||
atom_it = ++line.split(atom_it, begin);
|
auto end_atom_it = first_it->split(end);
|
||||||
beg_idx = atom_it - line.begin();
|
first_atom_it = first_it->erase(first_it->begin() + first_atom_idx, end_atom_it);
|
||||||
}
|
}
|
||||||
if (end == atom_it->begin())
|
else
|
||||||
end_idx = atom_it - line.begin();
|
|
||||||
else if (end <= atom_it->end())
|
|
||||||
{
|
{
|
||||||
if (end < atom_it->end())
|
first_atom_it = first_it->erase(first_atom_it, first_it->end());
|
||||||
atom_it = line.split(atom_it, end);
|
if (last_it != lines.end())
|
||||||
end_idx = (atom_it - line.begin()) + 1;
|
{
|
||||||
|
auto end_atom_it = last_it->split(end);
|
||||||
|
end_atom_it = last_it->erase(last_it->begin(), end_atom_it);
|
||||||
|
|
||||||
|
first_atom_it = first_it->insert(first_atom_it, end_atom_it, last_it->end());
|
||||||
|
++last_it;
|
||||||
}
|
}
|
||||||
|
first_it = --lines.erase(first_it+1, last_it);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (beg_idx != -1 and end_idx != -1)
|
func(*first_it, first_atom_it);
|
||||||
return func(line, line.erase(line.begin() + beg_idx, line.begin() + end_idx));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_highlighter(HighlightContext context,
|
void apply_highlighter(HighlightContext context,
|
||||||
|
@ -1495,11 +1491,11 @@ InclusiveBufferRange option_from_string(Meta::Type<InclusiveBufferRange>, String
|
||||||
return { std::min(first, last), std::max(first, last) };
|
return { std::min(first, last), std::max(first, last) };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename OptionType, typename DerivedType>
|
template<typename OptionType, typename DerivedType, HighlightPass pass = HighlightPass::Colorize>
|
||||||
struct OptionBasedHighlighter : Highlighter
|
struct OptionBasedHighlighter : Highlighter
|
||||||
{
|
{
|
||||||
OptionBasedHighlighter(String option_name)
|
OptionBasedHighlighter(String option_name)
|
||||||
: Highlighter{HighlightPass::Colorize}
|
: Highlighter{pass}
|
||||||
, m_option_name{std::move(option_name)} {}
|
, m_option_name{std::move(option_name)} {}
|
||||||
|
|
||||||
static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*)
|
static std::unique_ptr<Highlighter> create(HighlighterParameters params, Highlighter*)
|
||||||
|
@ -1514,7 +1510,7 @@ struct OptionBasedHighlighter : Highlighter
|
||||||
return std::make_unique<DerivedType>(option_name);
|
return std::make_unique<DerivedType>(option_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionType& get_option(const HighlightContext& context)
|
OptionType& get_option(const HighlightContext& context) const
|
||||||
{
|
{
|
||||||
return context.context.options()[m_option_name].get_mutable<OptionType>();
|
return context.context.options()[m_option_name].get_mutable<OptionType>();
|
||||||
}
|
}
|
||||||
|
@ -1565,32 +1561,35 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReplaceRangesHighlighter : OptionBasedHighlighter<RangeAndStringList, ReplaceRangesHighlighter>
|
struct ReplaceRangesHighlighter : OptionBasedHighlighter<RangeAndStringList, ReplaceRangesHighlighter, HighlightPass::Move>
|
||||||
{
|
{
|
||||||
using ReplaceRangesHighlighter::OptionBasedHighlighter::OptionBasedHighlighter;
|
using ReplaceRangesHighlighter::OptionBasedHighlighter::OptionBasedHighlighter;
|
||||||
private:
|
private:
|
||||||
void do_highlight(HighlightContext context, DisplayBuffer& display_buffer, BufferRange) override
|
static bool is_valid(Buffer& buffer, BufferCoord c)
|
||||||
{
|
{
|
||||||
auto& buffer = context.context.buffer();
|
|
||||||
auto& range_and_faces = get_option(context);
|
|
||||||
update_ranges(buffer, range_and_faces.prefix, range_and_faces.list);
|
|
||||||
|
|
||||||
auto is_valid = [&buffer](BufferCoord c) {
|
|
||||||
return c.line >= 0 and c.column >= 0 and c.line < buffer.line_count() and c.column <= buffer[c.line].length();
|
return c.line >= 0 and c.column >= 0 and c.line < buffer.line_count() and c.column <= buffer[c.line].length();
|
||||||
};
|
};
|
||||||
|
|
||||||
auto is_fully_selected = [&sels=context.context.selections()](const InclusiveBufferRange& range) {
|
static bool is_fully_selected(const SelectionList& sels, const InclusiveBufferRange& range)
|
||||||
|
{
|
||||||
auto it = std::lower_bound(sels.begin(), sels.end(), range.first, [](const Selection& s, const BufferCoord& c) { return s.max() < c; });
|
auto it = std::lower_bound(sels.begin(), sels.end(), range.first, [](const Selection& s, const BufferCoord& c) { return s.max() < c; });
|
||||||
if (it == sels.end())
|
if (it == sels.end())
|
||||||
return true;
|
return true;
|
||||||
return it->min() > range.last or (it->min() <= range.first and it->max() >= range.last);
|
return it->min() > range.last or (it->min() <= range.first and it->max() >= range.last);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void do_highlight(HighlightContext context, DisplayBuffer& display_buffer, BufferRange) override
|
||||||
|
{
|
||||||
|
auto& buffer = context.context.buffer();
|
||||||
|
auto& sels = context.context.selections();
|
||||||
|
auto& range_and_faces = get_option(context);
|
||||||
|
update_ranges(buffer, range_and_faces.prefix, range_and_faces.list);
|
||||||
|
|
||||||
for (auto& [range, spec] : range_and_faces.list)
|
for (auto& [range, spec] : range_and_faces.list)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!is_valid(range.first) or (!is_empty(range) and !is_valid(range.last)) or !is_fully_selected(range))
|
if (!is_valid(buffer, range.first) or (!is_empty(range) and !is_valid(buffer, range.last)) or !is_fully_selected(sels, range))
|
||||||
continue;
|
continue;
|
||||||
auto replacement = parse_display_line(spec, context.context.faces());
|
auto replacement = parse_display_line(spec, context.context.faces());
|
||||||
auto end = is_empty(range) ? range.first : buffer.char_next(range.last);
|
auto end = is_empty(range) ? range.first : buffer.char_next(range.last);
|
||||||
|
@ -1607,6 +1606,31 @@ private:
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void do_compute_display_setup(HighlightContext context, DisplaySetup& setup) const override
|
||||||
|
{
|
||||||
|
auto& buffer = context.context.buffer();
|
||||||
|
auto& sels = context.context.selections();
|
||||||
|
auto& range_and_faces = get_option(context);
|
||||||
|
update_ranges(buffer, range_and_faces.prefix, range_and_faces.list);
|
||||||
|
|
||||||
|
for (auto& [range, spec] : range_and_faces.list)
|
||||||
|
{
|
||||||
|
if (!is_valid(buffer, range.first) or (!is_empty(range) and !is_valid(buffer, range.last)) or !is_fully_selected(sels, range))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (range.first.line < setup.window_pos.line and range.last.line >= setup.window_pos.line)
|
||||||
|
setup.window_pos.line = range.first.line;
|
||||||
|
|
||||||
|
if (range.last.line >= setup.window_pos.line and
|
||||||
|
range.first.line <= setup.window_pos.line + setup.window_range.line and
|
||||||
|
range.first.line != range.last.line)
|
||||||
|
{
|
||||||
|
auto removed_count = range.last.line - range.first.line;
|
||||||
|
setup.window_range.line += removed_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
HighlightPass parse_passes(StringView str)
|
HighlightPass parse_passes(StringView str)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
30
test/highlight/replace-multiline-range-pulls-new-lines/in
Normal file
30
test/highlight/replace-multiline-range-pulls-new-lines/in
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
01
|
||||||
|
02
|
||||||
|
03
|
||||||
|
04
|
||||||
|
05
|
||||||
|
06
|
||||||
|
07
|
||||||
|
08
|
||||||
|
09
|
||||||
|
10
|
||||||
|
11
|
||||||
|
12
|
||||||
|
13
|
||||||
|
14
|
||||||
|
15
|
||||||
|
16
|
||||||
|
17
|
||||||
|
18
|
||||||
|
19
|
||||||
|
20
|
||||||
|
21
|
||||||
|
22
|
||||||
|
23
|
||||||
|
24
|
||||||
|
25
|
||||||
|
26
|
||||||
|
27
|
||||||
|
28
|
||||||
|
29
|
||||||
|
30
|
|
@ -0,0 +1,3 @@
|
||||||
|
declare-option range-specs ranges
|
||||||
|
add-highlighter window/ replace-ranges ranges
|
||||||
|
set-option buffer ranges %val{timestamp} 2.1,6.2|..
|
|
@ -0,0 +1,7 @@
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "0" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "1\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": ".." }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "07\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "08\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "09\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "10\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "11\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "12\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "13\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "14\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "15\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "16\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "17\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "18\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "19\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "20\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "21\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "22\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "23\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "24\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "25\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "26\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "27\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "28\u000a" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "info_hide", "params": [] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:1 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "set_cursor", "params": ["buffer", { "line": 0, "column": 0 }] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "refresh", "params": [true] }'
|
1
test/highlight/replace-multiline-range/cmd
Normal file
1
test/highlight/replace-multiline-range/cmd
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
5
test/highlight/replace-multiline-range/in
Normal file
5
test/highlight/replace-multiline-range/in
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
12345
|
||||||
|
67890
|
||||||
|
%(12345
|
||||||
|
67890)
|
2
test/highlight/replace-multiline-range/rc
Normal file
2
test/highlight/replace-multiline-range/rc
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
declare-option range-specs test_ranges %val{timestamp} '2.1,3.5|..' '4.2,5.4|..'
|
||||||
|
add-highlighter window/ replace-ranges test_ranges
|
7
test/highlight/replace-multiline-range/script
Normal file
7
test/highlight/replace-multiline-range/script
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": ".." }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }], [{ "face": { "fg": "white", "bg": "blue", "attributes": [] }, "contents": "1" }, { "face": { "fg": "white", "bg": "blue", "attributes": [] }, "contents": ".." }, { "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "0" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "info_hide", "params": [] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 5:5 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "[+]" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "set_cursor", "params": ["buffer", { "line": 2, "column": 3 }] }'
|
||||||
|
ui_out '{ "jsonrpc": "2.0", "method": "refresh", "params": [true] }'
|
Loading…
Reference in New Issue
Block a user