diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2016-03-29 09:00:55 +0200 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2016-03-30 14:03:12 +0200 |
| commit | e2f4b5bd6e9d8cba940074e9959d6cebf496132a (patch) | |
| tree | aafa1de663101b34e7991c1930b2ae7cd93fdb74 /main.c | |
| parent | 5f8865e78977c9ca43c3016fa92b79349b2d47cd (diff) | |
| download | vis-e2f4b5bd6e9d8cba940074e9959d6cebf496132a.tar.gz vis-e2f4b5bd6e9d8cba940074e9959d6cebf496132a.tar.xz | |
vis: implement selection rotation
In visual mode + and - will rotate the selection count times to the
right or left respectively. If there exists a line containing multiple
selections then the rotation happens within each line. Otherwise if each
line contains at most one selection the rotation is performed among all
existing selections.
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 79 |
1 files changed, 79 insertions, 0 deletions
@@ -12,6 +12,7 @@ #include "text-objects.h" #include "util.h" #include "libutf.h" +#include "array.h" #define PAGE INT_MAX #define PAGE_HALF (INT_MAX-1) @@ -56,6 +57,8 @@ static const char *cursors_select(Vis*, const char *keys, const Arg *arg); static const char *cursors_select_next(Vis*, const char *keys, const Arg *arg); /* clear current selection but select next match */ static const char *cursors_select_skip(Vis*, const char *keys, const Arg *arg); +/* rotate selection content count times left (arg->i < 0) or right (arg->i > 0) */ +static const char *selections_rotate(Vis*, const char *keys, const Arg *arg); /* adjust current used count according to keys */ static const char *count(Vis*, const char *keys, const Arg *arg); /* move to the count-th line or if not given either to the first (arg->i < 0) @@ -251,6 +254,8 @@ enum { VIS_ACTION_CURSORS_REMOVE_LAST, VIS_ACTION_CURSORS_PREV, VIS_ACTION_CURSORS_NEXT, + VIS_ACTION_SELECTIONS_ROTATE_LEFT, + VIS_ACTION_SELECTIONS_ROTATE_RIGHT, VIS_ACTION_TEXT_OBJECT_WORD_OUTER, VIS_ACTION_TEXT_OBJECT_WORD_INNER, VIS_ACTION_TEXT_OBJECT_LONGWORD_OUTER, @@ -961,6 +966,16 @@ static const KeyAction vis_action[] = { "Move to the next cursor", cursors_navigate, { .i = +PAGE_HALF } }, + [VIS_ACTION_SELECTIONS_ROTATE_LEFT] = { + "selections-rotate-left", + "Rotate selections left", + selections_rotate, { .i = -1 } + }, + [VIS_ACTION_SELECTIONS_ROTATE_RIGHT] = { + "selections-rotate-right", + "Rotate selections right", + selections_rotate, { .i = +1 } + }, [VIS_ACTION_TEXT_OBJECT_WORD_OUTER] = { "text-object-word-outer", "A word leading and trailing whitespace included", @@ -1405,6 +1420,70 @@ static const char *cursors_navigate(Vis *vis, const char *keys, const Arg *arg) return keys; } +static const char *selections_rotate(Vis *vis, const char *keys, const Arg *arg) { + + typedef struct { + Cursor *cursor; + char *data; + size_t len; + } Rotate; + + Array arr; + Text *txt = vis_text(vis); + View *view = vis_view(vis); + int columns = view_cursors_column_count(view); + int selections = columns == 1 ? view_cursors_count(view) : columns; + int count = vis_count_get_default(vis, 1); + array_init_sized(&arr, sizeof(Rotate)); + if (!array_reserve(&arr, selections)) + return keys; + size_t line_prev = 0; + + for (Cursor *c = view_cursors(view), *next; c; c = next) { + next = view_cursors_next(c); + + Filerange sel = view_cursors_selection_get(c); + Rotate rot; + rot.cursor = c; + rot.len = text_range_size(&sel); + if ((rot.data = malloc(rot.len))) + rot.len = text_bytes_get(txt, sel.start, rot.len, rot.data); + else + rot.len = 0; + array_add(&arr, &rot); + + size_t pos = view_cursors_pos(c); + size_t line = text_lineno_by_pos(txt, pos); + if (!next || (columns > 1 && line_prev && line != line_prev)) { + size_t len = array_length(&arr); + size_t off = arg->i > 0 ? count % len : len - (count % len); + for (size_t i = 0; i < len; i++) { + size_t j = (i + off) % len; + Rotate *oldrot = array_get(&arr, i); + Rotate *newrot = array_get(&arr, j); + if (!oldrot || !newrot || oldrot == newrot) + continue; + Filerange newsel = view_cursors_selection_get(newrot->cursor); + if (!text_range_valid(&newsel)) + continue; + if (!text_delete_range(txt, &newsel)) + continue; + if (!text_insert(txt, newsel.start, oldrot->data, oldrot->len)) + continue; + newsel.end = newsel.start + oldrot->len; + view_cursors_selection_set(newrot->cursor, &newsel); + view_cursors_selection_sync(newrot->cursor); + free(oldrot->data); + } + array_clear(&arr); + } + line_prev = line; + } + + vis_count_set(vis, VIS_COUNT_UNKNOWN); + return keys; +} + static const char *replace(Vis *vis, const char *keys, const Arg *arg) { if (!keys[0]) return NULL; |
