diff --git a/src/display_buffer.cc b/src/display_buffer.cc index 3796cf2a..c4e2d366 100644 --- a/src/display_buffer.cc +++ b/src/display_buffer.cc @@ -53,7 +53,6 @@ void DisplayAtom::trim_begin(ColumnCount count) count).coord(); else m_text = m_text.substr(count).str(); - check_invariant(); } void DisplayAtom::trim_end(ColumnCount count) @@ -64,18 +63,6 @@ void DisplayAtom::trim_end(ColumnCount count) -count).coord(); else m_text = m_text.substr(0, m_text.column_length() - count).str(); - check_invariant(); -} - -void DisplayAtom::check_invariant() const -{ -#ifdef KAK_DEBUG - if (has_buffer_range()) - { - kak_assert(m_buffer->is_valid(m_range.begin)); - kak_assert(m_buffer->is_valid(m_range.end)); - } -#endif } DisplayLine::DisplayLine(AtomList atoms) @@ -93,8 +80,6 @@ DisplayLine::iterator DisplayLine::split(iterator it, BufferCoord pos) DisplayAtom atom = *it; atom.m_range.end = pos; it->m_range.begin = pos; - atom.check_invariant(); - it->check_invariant(); return m_atoms.insert(it, std::move(atom)); } @@ -106,8 +91,6 @@ DisplayLine::iterator DisplayLine::split(iterator it, ColumnCount pos) DisplayAtom atom(it->m_text.substr(0, pos).str()); it->m_text = it->m_text.substr(pos).str(); - atom.check_invariant(); - it->check_invariant(); return m_atoms.insert(it, std::move(atom)); } @@ -174,7 +157,6 @@ void DisplayLine::optimize() next_atom_it = m_atoms.erase(next_atom_it); else atom_it = next_atom_it++; - atom_it->check_invariant(); } } diff --git a/src/display_buffer.hh b/src/display_buffer.hh index 981f4960..ac6d4e82 100644 --- a/src/display_buffer.hh +++ b/src/display_buffer.hh @@ -31,12 +31,10 @@ public: enum Type { Range, ReplacedRange, Text }; DisplayAtom(const Buffer& buffer, BufferCoord begin, BufferCoord end) - : m_type(Range), m_buffer(&buffer), m_range{begin, end} - { check_invariant(); } + : m_type(Range), m_buffer(&buffer), m_range{begin, end} {} DisplayAtom(String str, Face face = Face{}) - : m_type(Text), m_text(std::move(str)), face(face) - { check_invariant(); } + : m_type(Text), m_text(std::move(str)), face(face) {} StringView content() const; ColumnCount length() const; @@ -72,8 +70,6 @@ public: void trim_begin(ColumnCount count); void trim_end(ColumnCount count); - void check_invariant() const; - bool operator==(const DisplayAtom& other) const { return face == other.face and type() == other.type() and diff --git a/src/highlighters.cc b/src/highlighters.cc index 66ff7d9d..caecdbf5 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -880,40 +880,63 @@ struct WrapHighlighter : Highlighter const ColumnCount m_max_width; }; -void expand_tabulations(const Context& context, HighlightPass, DisplayBuffer& display_buffer, BufferRange) +struct TabulationHighlighter : Highlighter { - const ColumnCount tabstop = context.options()["tabstop"].get(); - auto& buffer = context.buffer(); - for (auto& line : display_buffer.lines()) + TabulationHighlighter() : Highlighter{HighlightPass::Move} {} + + void do_highlight(const Context& context, HighlightPass, + DisplayBuffer& display_buffer, BufferRange) override { - for (auto atom_it = line.begin(); atom_it != line.end(); ++atom_it) + const ColumnCount tabstop = context.options()["tabstop"].get(); + auto& buffer = context.buffer(); + auto win_column = context.window().position().column; + for (auto& line : display_buffer.lines()) { - if (atom_it->type() != DisplayAtom::Range) - continue; - - auto begin = buffer.iterator_at(atom_it->begin()); - auto end = buffer.iterator_at(atom_it->end()); - for (BufferIterator it = begin; it != end; ++it) + for (auto atom_it = line.begin(); atom_it != line.end(); ++atom_it) { - if (*it == '\t') - { - if (it != begin) - atom_it = ++line.split(atom_it, it.coord()); - if (it+1 != end) - atom_it = line.split(atom_it, (it+1).coord()); + if (atom_it->type() != DisplayAtom::Range) + continue; - ColumnCount column = get_column(buffer, tabstop, it.coord()); - ColumnCount count = tabstop - (column % tabstop); - String padding; - for (int i = 0; i < count; ++i) - padding += ' '; - atom_it->replace(padding); - break; + auto begin = buffer.iterator_at(atom_it->begin()); + auto end = buffer.iterator_at(atom_it->end()); + for (BufferIterator it = begin; it != end; ++it) + { + if (*it == '\t') + { + if (it != begin) + atom_it = ++line.split(atom_it, it.coord()); + if (it+1 != end) + atom_it = line.split(atom_it, (it+1).coord()); + + const ColumnCount column = get_column(buffer, tabstop, it.coord()); + const ColumnCount count = tabstop - (column % tabstop) - + std::max(win_column - column, 0_col); + atom_it->replace(String{' ', count}); + break; + } } } } } -} + + void do_compute_display_setup(const Context& context, HighlightPass, DisplaySetup& setup) override + { + auto& buffer = context.buffer(); + // Ensure that a cursor on a tab character makes the full tab character visible + auto cursor = context.selections().main().cursor(); + if (buffer.byte_at(cursor) != '\t') + return; + + const ColumnCount tabstop = context.options()["tabstop"].get(); + const ColumnCount column = get_column(buffer, tabstop, cursor); + const ColumnCount width = tabstop - (column % tabstop); + const ColumnCount win_end = setup.window_pos.column + setup.window_range.column; + const ColumnCount offset = std::max(column + width - win_end, 0_col); + + setup.window_pos.column += offset; + setup.cursor_pos.column -= offset; + } +}; void show_whitespaces(const Context& context, HighlightPass, DisplayBuffer& display_buffer, BufferRange, StringView tab, StringView tabpad, @@ -1935,7 +1958,7 @@ private: void setup_builtin_highlighters(HighlighterGroup& group) { - group.add_child({"tabulations"_str, make_highlighter(expand_tabulations)}); + group.add_child({"tabulations"_str, make_unique()}); group.add_child({"unprintable"_str, make_highlighter(expand_unprintable)}); group.add_child({"selections"_str, make_highlighter(highlight_selections)}); } diff --git a/src/window.cc b/src/window.cc index 1dee2c2a..26cf3021 100644 --- a/src/window.cc +++ b/src/window.cc @@ -122,12 +122,15 @@ const DisplayBuffer& Window::update_display_buffer(const Context& context) kak_assert(&buffer() == &context.buffer()); compute_display_setup(context); + const int tabstop = context.options()["tabstop"].get(); for (LineCount line = 0; line < m_range.line; ++line) { LineCount buffer_line = m_position.line + line; if (buffer_line >= buffer().line_count()) break; - lines.emplace_back(AtomList{ {buffer(), buffer_line, buffer_line+1} }); + auto beg_byte = get_byte_to_column(buffer(), tabstop, {buffer_line, m_position.column}); + auto end_byte = get_byte_to_column(buffer(), tabstop, {buffer_line, m_position.column + m_range.column}); + lines.emplace_back(AtomList{ {buffer(), {buffer_line, beg_byte}, {buffer_line, end_byte}} }); } m_display_buffer.compute_range(); @@ -137,9 +140,6 @@ const DisplayBuffer& Window::update_display_buffer(const Context& context) for (auto pass : { HighlightPass::Wrap, HighlightPass::Move, HighlightPass::Colorize }) m_builtin_highlighters.highlight(context, pass, m_display_buffer, range); - // cut the start of the line before m_position.column - for (auto& line : lines) - line.trim(m_position.column, m_dimensions.column, true); m_display_buffer.optimize(); m_last_setup = build_setup(context); diff --git a/test/display/horizontal-scroll-onto-tab/display b/test/display/horizontal-scroll-onto-tab/display index 3505ae3b..1b655f09 100644 --- a/test/display/horizontal-scroll-onto-tab/display +++ b/test/display/horizontal-scroll-onto-tab/display @@ -1,6 +1,6 @@ -{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "23456789012345678901234567890123456789012345678901234567890123456789012345678901" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "234567890123456789012345678901234567890123456789012345678901234567890123456" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": " " }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "90" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "7890\u000a" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] } +{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "90123456789012345678901234567890123456789012345678901234567890123456789012345678" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "90123456789012345678901234567890123456789012345678901234567890123456" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": " " }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "90" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "7890\u000a" }]], { "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 2:78 " }, { "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": 1, "column": 79 }] } +{ "jsonrpc": "2.0", "method": "set_cursor", "params": ["buffer", { "line": 1, "column": 72 }] } { "jsonrpc": "2.0", "method": "refresh", "params": [true] } diff --git a/test/highlight/regions/display b/test/highlight/regions/display index ee4ba80b..8581701e 100644 --- a/test/highlight/regions/display +++ b/test/highlight/regions/display @@ -1,4 +1,4 @@ -{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "\"" }, { "face": { "fg": "green", "bg": "default", "attributes": [] }, "contents": "abcdefgh\"" }, { "face": { "fg": "yellow", "bg": "default", "attributes": [] }, "contents": " hehe " }, { "face": { "fg": "red", "bg": "default", "attributes": [] }, "contents": "${ youhou{hihi} }" }, { "face": { "fg": "yellow", "bg": "default", "attributes": [] }, "contents": "\u000a" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] } +{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "\"" }, { "face": { "fg": "green", "bg": "default", "attributes": [] }, "contents": "abcdefgh\"" }, { "face": { "fg": "yellow", "bg": "default", "attributes": [] }, "contents": " hehe " }, { "face": { "fg": "red", "bg": "default", "attributes": [] }, "contents": "${ youhou{hihi} }" }, { "face": { "fg": "yellow", "bg": "default", "attributes": [] }, "contents": "\u000a" }]], { "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": [] }] }