diff --git a/src/buffer_utils.cc b/src/buffer_utils.cc index 0b5941b9..c768a9eb 100644 --- a/src/buffer_utils.cc +++ b/src/buffer_utils.cc @@ -26,6 +26,26 @@ CharCount get_column(const Buffer& buffer, return col; } +ByteCount get_byte_to_column(const Buffer& buffer, CharCount tabstop, CharCoord coord) +{ + auto line = buffer[coord.line]; + auto col = 0_char; + auto it = line.begin(); + while (it != line.end() and coord.column > col) + { + if (*it == '\t') + { + col = (col / tabstop + 1) * tabstop; + if (col > coord.column) // the target column was in the tab + break; + } + else + ++col; + it = utf8::next(it, line.end()); + } + return (int)(it - line.begin()); +} + Buffer* create_buffer_from_data(StringView data, StringView name, Buffer::Flags flags, time_t fs_timestamp) { diff --git a/src/buffer_utils.hh b/src/buffer_utils.hh index 80bd3112..591ea455 100644 --- a/src/buffer_utils.hh +++ b/src/buffer_utils.hh @@ -27,6 +27,9 @@ inline CharCount char_length(const Buffer& buffer, const Selection& range) CharCount get_column(const Buffer& buffer, CharCount tabstop, ByteCoord coord); +ByteCount get_byte_to_column(const Buffer& buffer, CharCount tabstop, + CharCoord coord); + Buffer* create_fifo_buffer(String name, int fd, bool scroll = false); Buffer* create_buffer_from_data(StringView data, StringView name, diff --git a/src/normal.cc b/src/normal.cc index 611698e7..f587a513 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -976,20 +976,32 @@ void copy_selections_on_next_lines(Context& context, NormalParams params) { auto& selections = context.selections(); auto& buffer = context.buffer(); + const CharCount tabstop = context.options()["tabstop"].get(); Vector result; for (auto& sel : selections) { auto anchor = sel.anchor(); auto cursor = sel.cursor(); + CharCount cursor_col = get_column(buffer, tabstop, cursor); + CharCount anchor_col = get_column(buffer, tabstop, anchor); result.push_back(std::move(sel)); for (int i = 0; i < std::max(params.count, 1); ++i) { LineCount offset = (direction == Forward ? 1 : -1) * (i + 1); - ByteCoord new_anchor{anchor.line + offset, anchor.column}; - ByteCoordAndTarget new_cursor{cursor.line + offset, cursor.column, cursor.target}; - if (buffer.is_valid(new_anchor) and not buffer.is_end(new_anchor) and - buffer.is_valid(new_cursor) and not buffer.is_end(new_cursor)) - result.emplace_back(new_anchor, new_cursor); + + const LineCount anchor_line = anchor.line + offset; + const LineCount cursor_line = cursor.line + offset; + + if (anchor_line >= buffer.line_count() or cursor_line >= buffer.line_count()) + continue; + + ByteCount anchor_byte = get_byte_to_column(buffer, tabstop, {anchor_line, anchor_col}); + ByteCount cursor_byte = get_byte_to_column(buffer, tabstop, {cursor_line, cursor_col}); + + if (anchor_byte != buffer[anchor_line].length() and + cursor_byte != buffer[cursor_line].length()) + result.emplace_back(ByteCoord{anchor_line, anchor_byte}, + ByteCoordAndTarget{cursor_line, cursor_byte, cursor.target}); } } selections = std::move(result);