Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keybinding for copying selections on matching substrings vertically #1116

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,9 @@ not containing a match.
`C` copies the current selection to the next line (or lines if a count is given)
`<a-C>` does the same to previous lines.

`^` copies the current selections to preceding and following lines if the copied
selection contains the same substring as the source one.

`$` allows you to enter a shell command and pipe each selections to it.
Selections whose shell command returns 0 will be kept, other will be dropped.

Expand Down
4 changes: 4 additions & 0 deletions doc/manpages/shortcuts.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,10 @@ Multiple selections
*<a-C>*::
copy the current selection to the previous line

*^*::
copy the current selection to the following and preceding lines
while contents of the copies match the contents of the source

*<space>*::
clear a multiple selection

Expand Down
64 changes: 64 additions & 0 deletions src/normal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,68 @@ void copy_selections_on_next_lines(Context& context, NormalParams params)
selections.sort_and_merge_overlapping();
}

void copy_selections_on_same_substring(Context& context, NormalParams params)
{
auto& selections = context.selections();
auto& buffer = context.buffer();
const ColumnCount tabstop = context.options()["tabstop"].get<int>();
Vector<Selection> result;
size_t main_index = 0;
for (auto& sel : selections)
{
const bool is_main = (&sel == &selections.main());
auto anchor = sel.anchor();
auto cursor = sel.cursor();
ColumnCount cursor_col = get_column(buffer, tabstop, cursor);
ColumnCount anchor_col = get_column(buffer, tabstop, anchor);

const auto beg = sel.min(), end = buffer.char_next(sel.max());
const auto orig_str = buffer.string(beg, end);

if (is_main)
main_index = result.size();
result.push_back(std::move(sel));
for (Direction direction : {Backward, Forward}) {
for (int i = 1;; ++i)
{
if (params.count && i > params.count)
break;

LineCount offset = (direction == Forward ? 1 : -1) * i;

const LineCount anchor_line = anchor.line + offset;
const LineCount cursor_line = cursor.line + offset;

if (anchor_line < 0 or cursor_line < 0 or
anchor_line >= buffer.line_count() or cursor_line >= buffer.line_count())
break;

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() or
cursor_byte == buffer[cursor_line].length())
break;

const auto anchor_coord = BufferCoord{anchor_line, anchor_byte};
const auto cursor_coord = BufferCoordAndTarget{cursor_line, cursor_byte, cursor.target};
const auto new_sel = Selection(anchor_coord, cursor_coord);
const auto beg = new_sel.min(), end = buffer.char_next(new_sel.max());
const auto str = buffer.string(beg, end);

if (str != orig_str)
break;

if (is_main)
main_index = result.size();
result.push_back(new_sel);
}
}
}
selections.set(std::move(result), main_index);
selections.sort_and_merge_overlapping();
}

void rotate_selections(Context& context, NormalParams params)
{
context.selections().rotate_main(params.count != 0 ? params.count : 1);
Expand Down Expand Up @@ -1845,6 +1907,8 @@ static NormalCmdDesc cmds[] =
{ 'C', "copy selection on next lines", copy_selections_on_next_lines<Forward> },
{ alt('C'), "copy selection on previous lines", copy_selections_on_next_lines<Backward> },

{ '^', "copy selection on same substring", copy_selections_on_same_substring },

{ ',', "user mappings", exec_user_mappings },

{ Key::Left, "move left", move<CharCount, Backward> },
Expand Down
1 change: 1 addition & 0 deletions test/unit/copy-on-same-substring/cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
^
4 changes: 4 additions & 0 deletions test/unit/copy-on-same-substring/in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# foo
%(#) bar
# baz
qux
1 change: 1 addition & 0 deletions test/unit/copy-on-same-substring/selections
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#:#:#