aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2016-03-29 09:00:55 +0200
committerMarc André Tanner <mat@brain-dump.org>2016-03-30 14:03:12 +0200
commite2f4b5bd6e9d8cba940074e9959d6cebf496132a (patch)
treeaafa1de663101b34e7991c1930b2ae7cd93fdb74
parent5f8865e78977c9ca43c3016fa92b79349b2d47cd (diff)
downloadvis-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.
-rw-r--r--README.md2
-rw-r--r--config.def.h6
-rw-r--r--main.c79
3 files changed, 85 insertions, 2 deletions
diff --git a/README.md b/README.md
index da9ccf3..c8b0ddb 100644
--- a/README.md
+++ b/README.md
@@ -221,6 +221,8 @@ Operators can be forced to work line wise by specifying `V`.
Ctrl-P remove primary cursor
Ctrl-U make the count previous cursor primary
Ctrl-D make the count next cursor primary
+ + rotates selections rightwards count times
+ - rotates selections leftwards count times
Esc clear all selections, switch to normal mode
In insert/replace mode
diff --git a/config.def.h b/config.def.h
index 0b91be8..67e4c73 100644
--- a/config.def.h
+++ b/config.def.h
@@ -280,8 +280,10 @@ static const KeyBinding bindings_visual[] = {
{ "o", ACTION(SELECTION_FLIP) },
{ ">", ALIAS("<vis-operator-shift-right>gv") },
{ "<", ALIAS("<vis-operator-shift-left>gv") },
- { "<Tab>" , ACTION(CURSORS_ALIGN_INDENT_LEFT) },
- { "<S-Tab>" , ACTION(CURSORS_ALIGN_INDENT_RIGHT) },
+ { "<Tab>", ACTION(CURSORS_ALIGN_INDENT_LEFT) },
+ { "<S-Tab>", ACTION(CURSORS_ALIGN_INDENT_RIGHT) },
+ { "-", ACTION(SELECTIONS_ROTATE_LEFT) },
+ { "+", ACTION(SELECTIONS_ROTATE_RIGHT) },
{ 0 /* empty last element, array terminator */ },
};
diff --git a/main.c b/main.c
index a5fc2d3..6e8e245 100644
--- a/main.c
+++ b/main.c
@@ -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;