Refactor column highlighter to make it more robust
Support arbitrary orders for column highlighters (it was previously failing when column highlighters were not applied in column order). Fix show_matching tab handling at the same time (horizontal scrolling, tab characters and show_matching were behaving badly). Window highlighting now runs user highlighters, then built-ins for each phases, instead of running all phases for user highlighters, then all phases for built-ins. We now consider unprintable character to be 1-column width as we know we will display them as "�". Fixes #1615 Fixes #1023
This commit is contained in:
parent
446085d32b
commit
89f016d871
|
@ -92,15 +92,22 @@ DisplayLine::iterator DisplayLine::split(iterator it, BufferCoord pos)
|
|||
return m_atoms.insert(it, std::move(atom));
|
||||
}
|
||||
|
||||
DisplayLine::iterator DisplayLine::split(iterator it, ColumnCount pos)
|
||||
DisplayLine::iterator DisplayLine::split(iterator it, ColumnCount count)
|
||||
{
|
||||
kak_assert(it->type() == DisplayAtom::Text);
|
||||
kak_assert(pos > 0);
|
||||
kak_assert(pos < it->length());
|
||||
kak_assert(count > 0);
|
||||
kak_assert(count < it->length());
|
||||
|
||||
DisplayAtom atom(it->m_text.substr(0, pos).str());
|
||||
it->m_text = it->m_text.substr(pos).str();
|
||||
return m_atoms.insert(it, std::move(atom));
|
||||
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()),
|
||||
get_iterator(it->buffer(), it->end()),
|
||||
count).coord();
|
||||
return split(it, pos);
|
||||
}
|
||||
|
||||
DisplayLine::iterator DisplayLine::insert(iterator it, DisplayAtom atom)
|
||||
|
|
|
@ -615,59 +615,37 @@ HighlighterAndId create_column_highlighter(HighlighterParameters params)
|
|||
if (column < 0)
|
||||
return;
|
||||
|
||||
const Buffer& buffer = context.buffer();
|
||||
const int tabstop = context.options()["tabstop"].get<int>();
|
||||
auto face = get_face(facespec);
|
||||
auto win_column = context.window().position().column;
|
||||
for (auto& line : display_buffer.lines())
|
||||
{
|
||||
const LineCount buf_line = line.range().begin.line;
|
||||
const ByteCount byte_col = get_byte_to_column(buffer, tabstop, {buf_line, column});
|
||||
const BufferCoord coord{buf_line, byte_col};
|
||||
auto target_col = column - win_column;
|
||||
if (target_col < 0)
|
||||
return;
|
||||
|
||||
bool found = false;
|
||||
if (buffer.is_valid(coord) and not buffer.is_end(coord))
|
||||
auto first_buf = find_if(line, [](auto& atom) { return atom.has_buffer_range(); });
|
||||
for (auto atom_it = first_buf; atom_it != line.end(); ++atom_it)
|
||||
{
|
||||
for (auto atom_it = line.begin(); atom_it != line.end(); ++atom_it)
|
||||
const auto atom_len = atom_it->length();
|
||||
if (target_col < atom_len)
|
||||
{
|
||||
if (atom_it->type() != DisplayAtom::Range)
|
||||
continue;
|
||||
|
||||
kak_assert(atom_it->begin().line == buf_line);
|
||||
if (coord >= atom_it->begin() and coord < atom_it->end())
|
||||
{
|
||||
if (coord > atom_it->begin())
|
||||
atom_it = ++line.split(atom_it, coord);
|
||||
if (buffer.next(coord) < atom_it->end())
|
||||
atom_it = line.split(atom_it, buffer.next(coord));
|
||||
|
||||
apply_face(face)(*atom_it);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (target_col > 0)
|
||||
atom_it = ++line.split(atom_it, target_col);
|
||||
if (atom_it->length() > 1)
|
||||
atom_it = line.split(atom_it, 1_col);
|
||||
atom_it->face = merge_faces(atom_it->face, face);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
target_col -= atom_len;
|
||||
}
|
||||
if (not found)
|
||||
{
|
||||
ColumnCount last_buffer_col = context.window().position().column;
|
||||
for (auto& atom : line)
|
||||
{
|
||||
if (atom.has_buffer_range())
|
||||
{
|
||||
auto pos = atom.end();
|
||||
if (pos.column == 0)
|
||||
pos = {pos.line-1, buffer[pos.line-1].length()};
|
||||
if (pos != atom.begin())
|
||||
last_buffer_col = get_column(buffer, tabstop, pos);
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
ColumnCount count = column - last_buffer_col;
|
||||
if (count >= 0)
|
||||
{
|
||||
if (count > 0)
|
||||
line.push_back({String{' ', count}});
|
||||
line.push_back({String{" "}, face});
|
||||
}
|
||||
}
|
||||
if (target_col > 0)
|
||||
line.push_back({String{' ', target_col}});
|
||||
line.push_back({" ", face});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -930,6 +908,7 @@ void show_whitespaces(const Context& context, HighlightPass, DisplayBuffer& disp
|
|||
const int tabstop = context.options()["tabstop"].get<int>();
|
||||
auto whitespaceface = get_face("Whitespace");
|
||||
auto& buffer = context.buffer();
|
||||
auto win_column = context.window().position().column;
|
||||
for (auto& line : display_buffer.lines())
|
||||
{
|
||||
for (auto atom_it = line.begin(); atom_it != line.end(); ++atom_it)
|
||||
|
@ -952,9 +931,10 @@ void show_whitespaces(const Context& context, HighlightPass, DisplayBuffer& disp
|
|||
|
||||
if (cp == '\t')
|
||||
{
|
||||
int column = (int)get_column(buffer, tabstop, coord);
|
||||
int count = tabstop - (column % tabstop);
|
||||
atom_it->replace(tab + String(tabpad[(CharCount)0], CharCount{count-1}));
|
||||
const ColumnCount column = get_column(buffer, tabstop, coord);
|
||||
const ColumnCount count = tabstop - (column % tabstop) -
|
||||
std::max(win_column - column, 0_col);
|
||||
atom_it->replace(tab + String(tabpad[(CharCount)0], count - tab.column_length()));
|
||||
}
|
||||
else if (cp == ' ')
|
||||
atom_it->replace(spc.str());
|
||||
|
@ -996,7 +976,7 @@ HighlighterAndId show_whitespaces_factory(HighlighterParameters params)
|
|||
get_param("lf", "¬"),
|
||||
get_param("nbsp", "⍽"));
|
||||
|
||||
return {"show_whitespaces", make_highlighter(std::move(func))};
|
||||
return {"show_whitespaces", make_highlighter(std::move(func), HighlightPass::Move)};
|
||||
}
|
||||
|
||||
struct LineNumbersHighlighter : Highlighter
|
||||
|
|
|
@ -58,7 +58,7 @@ inline ColumnCount codepoint_width(Codepoint c) noexcept
|
|||
if (c == '\n')
|
||||
return 1;
|
||||
const auto width = wcwidth((wchar_t)c);
|
||||
return width > 0 ? width : 0;
|
||||
return width >= 0 ? width : 1;
|
||||
}
|
||||
|
||||
enum class CharCategories
|
||||
|
|
|
@ -136,15 +136,18 @@ const DisplayBuffer& Window::update_display_buffer(const Context& context)
|
|||
buffer()[buffer_line].length()
|
||||
: get_byte_to_column(buffer(), tabstop, {buffer_line, m_position.column + m_range.column});
|
||||
|
||||
// The display buffer always has at least one buffer atom, which might be empty if
|
||||
// beg_byte == end_byte
|
||||
lines.emplace_back(AtomList{ {buffer(), {buffer_line, beg_byte}, {buffer_line, end_byte}} });
|
||||
}
|
||||
|
||||
m_display_buffer.compute_range();
|
||||
BufferRange range{{0,0}, buffer().end_coord()};
|
||||
for (auto pass : { HighlightPass::Wrap, HighlightPass::Move, HighlightPass::Colorize })
|
||||
{
|
||||
m_highlighters.highlight(context, pass, m_display_buffer, range);
|
||||
for (auto pass : { HighlightPass::Wrap, HighlightPass::Move, HighlightPass::Colorize })
|
||||
m_builtin_highlighters.highlight(context, pass, m_display_buffer, range);
|
||||
}
|
||||
|
||||
m_display_buffer.optimize();
|
||||
|
||||
|
|
0
test/highlight/column/multi-columns/cmd
Normal file
0
test/highlight/column/multi-columns/cmd
Normal file
6
test/highlight/column/multi-columns/display
Normal file
6
test/highlight/column/multi-columns/display
Normal file
|
@ -0,0 +1,6 @@
|
|||
{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "1│" }, { "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "a" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }, { "face": { "fg": "default", "bg": "red", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "blue", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "green", "attributes": [] }, "contents": " " }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "2│" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "red", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "blue", "attributes": [] }, "contents": "a" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "b" }, { "face": { "fg": "default", "bg": "green", "attributes": [] }, "contents": "\u000a" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "3│" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "ab" }, { "face": { "fg": "default", "bg": "red", "attributes": [] }, "contents": "c" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }, { "face": { "fg": "default", "bg": "blue", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "green", "attributes": [] }, "contents": " " }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "4│" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "ab" }, { "face": { "fg": "default", "bg": "red", "attributes": [] }, "contents": "c" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "d" }, { "face": { "fg": "default", "bg": "blue", "attributes": [] }, "contents": "\u000a" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "green", "attributes": [] }, "contents": " " }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }
|
||||
{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] }
|
||||
{ "jsonrpc": "2.0", "method": "info_hide", "params": [] }
|
||||
{ "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": " - unnamed0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }
|
||||
{ "jsonrpc": "2.0", "method": "set_cursor", "params": ["buffer", { "line": 0, "column": 2 }] }
|
||||
{ "jsonrpc": "2.0", "method": "refresh", "params": [true] }
|
4
test/highlight/column/multi-columns/in
Normal file
4
test/highlight/column/multi-columns/in
Normal file
|
@ -0,0 +1,4 @@
|
|||
a
|
||||
ab
|
||||
abc
|
||||
abcd
|
5
test/highlight/column/multi-columns/rc
Normal file
5
test/highlight/column/multi-columns/rc
Normal file
|
@ -0,0 +1,5 @@
|
|||
add-highlighter number_lines
|
||||
set window tabstop 4
|
||||
add-highlighter column 3 default,red
|
||||
add-highlighter column 7 default,green
|
||||
add-highlighter column 5 default,blue
|
Loading…
Reference in New Issue
Block a user