aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2015-07-28 12:10:22 +0200
committerMarc André Tanner <mat@brain-dump.org>2015-07-28 13:21:50 +0200
commitb366f55f29af3775c055115572dd84873b1921e0 (patch)
treeb9c6a710997272d8b84238965e6f992abfbed42c
parenta19159c506577a168655afcd961381dd1f995610 (diff)
downloadvis-b366f55f29af3775c055115572dd84873b1921e0.tar.gz
vis-b366f55f29af3775c055115572dd84873b1921e0.tar.xz
vis: use multiple cursor/selection infrastructure
This commits introduces the following keybindings, in normal mode: CTRL-N select word the cursor is currently over, switch to visual mode CTRL-P remove least recently added cursor ESC if a selection is active, clear it. Otherwise dispose all but the primary cursor. In visual mode: CTRL-N create new cursor and select next word matching current selection CTRL-X clear (skip) current selection, but select next matching word CTRL-P remove least recently added cursor
-rw-r--r--config.def.h5
-rw-r--r--view.c11
-rw-r--r--view.h5
-rw-r--r--vis.c61
4 files changed, 81 insertions, 1 deletions
diff --git a/config.def.h b/config.def.h
index 4402cd0..859b3db 100644
--- a/config.def.h
+++ b/config.def.h
@@ -363,6 +363,8 @@ static KeyBinding vis_mode_normal[] = {
{ { CONTROL('K') }, cursors_new, { .i = -1 } },
{ { CONTROL('J') }, cursors_new, { .i = +1 } },
{ { CONTROL('A') }, cursors_align, { } },
+ { { CONTROL('N') }, cursors_select, { } },
+ { { CONTROL('P') }, cursors_remove, { } },
{ { CONTROL('w'), NONE('n') }, cmd, { .s = "open" } },
{ { CONTROL('w'), NONE('c') }, cmd, { .s = "q" } },
{ { CONTROL('w'), NONE('s') }, cmd, { .s = "split" } },
@@ -423,6 +425,9 @@ static KeyBinding vis_mode_normal[] = {
};
static KeyBinding vis_mode_visual[] = {
+ { { CONTROL('N') }, cursors_select_next, { } },
+ { { CONTROL('X') }, cursors_select_skip, { } },
+ { { CONTROL('P') }, cursors_remove, { } },
{ { KEY(BACKSPACE) }, operator, { .i = OP_DELETE } },
{ { KEY(DELETE) }, operator, { .i = OP_DELETE } },
{ { CONTROL('O') }, operator, { .i = OP_CURSOR } },
diff --git a/view.c b/view.c
index 4af93f8..ff3ab42 100644
--- a/view.c
+++ b/view.c
@@ -912,6 +912,17 @@ void view_cursors_free(Cursor *c) {
free(c);
}
+void view_cursors_dispose(Cursor *c) {
+ if (!c)
+ return;
+ View *view = c->view;
+ if (view->cursors && view->cursors->next) {
+ view_selections_free(c->sel);
+ view_cursors_free(c);
+ view_draw(view);
+ }
+}
+
Cursor *view_cursors(View *view) {
return view->cursors;
}
diff --git a/view.h b/view.h
index 4e750c0..d0e7070 100644
--- a/view.h
+++ b/view.h
@@ -117,7 +117,10 @@ void view_cursor_to(View*, size_t pos);
Cursor *view_cursors_new(View*);
/* get number of active cursors */
int view_cursors_count(View*);
-/* dispose an existing cursor */
+/* dispose an existing cursor with its associated selection (if any),
+ * not applicaple for the last existing cursor */
+void view_cursors_dispose(Cursor*);
+/* dispose an existing cursor, no matter what */
void view_cursors_free(Cursor*);
/* only keep the main cursor, release all others together with their
* selections (if any) */
diff --git a/vis.c b/vis.c
index b10f798..b385135 100644
--- a/vis.c
+++ b/vis.c
@@ -326,6 +326,14 @@ static void cursors_new(const Arg *arg);
static void cursors_align(const Arg *arg);
/* remove all but the primary cursor and their selections */
static void cursors_clear(const Arg *arg);
+/* remove the least recently added cursor */
+static void cursors_remove(const Arg *arg);
+/* select the word the cursor is currently over */
+static void cursors_select(const Arg *arg);
+/* select the next region matching the current selection */
+static void cursors_select_next(const Arg *arg);
+/* clear current selection but select next match */
+static void cursors_select_skip(const Arg *arg);
/* adjust action.count by arg->i */
static void count(const Arg *arg);
/* move to the action.count-th line or if not given either to the first (arg->i < 0)
@@ -869,6 +877,59 @@ static void cursors_clear(const Arg *arg) {
view_cursors_selection_clear(view_cursor(view));
}
+static void cursors_select(const Arg *arg) {
+ Text *txt = vis->win->file->text;
+ View *view = vis->win->view;
+ for (Cursor *cursor = view_cursors(view); cursor; cursor = view_cursors_next(cursor)) {
+ Filerange sel = view_cursors_selection_get(cursor);
+ Filerange word = text_object_word(txt, view_cursors_pos(cursor));
+ if (!text_range_valid(&sel) && text_range_valid(&word)) {
+ view_cursors_selection_set(cursor, &word);
+ view_cursors_to(cursor, text_char_prev(txt, word.end));
+ }
+ }
+ switchmode(&(const Arg){ .i = VIS_MODE_VISUAL });
+}
+
+static void cursors_select_next(const Arg *arg) {
+ Text *txt = vis->win->file->text;
+ View *view = vis->win->view;
+ Cursor *cursor = view_cursor(view);
+ Filerange sel = view_cursors_selection_get(cursor);
+ if (!text_range_valid(&sel))
+ return;
+
+ size_t len = text_range_size(&sel);
+ char *buf = malloc(len+1);
+ if (!buf)
+ return;
+ len = text_bytes_get(txt, sel.start, len, buf);
+ buf[len] = '\0';
+ Filerange word = text_object_word_find_next(txt, sel.end, buf);
+ free(buf);
+
+ if (text_range_valid(&word)) {
+ cursor = view_cursors_new(view);
+ if (!cursor)
+ return;
+ view_cursors_selection_set(cursor, &word);
+ view_cursors_to(cursor, text_char_prev(txt, word.end));
+ }
+}
+
+static void cursors_select_skip(const Arg *arg) {
+ View *view = vis->win->view;
+ Cursor *cursor = view_cursor(view);
+ cursors_select_next(arg);
+ if (cursor != view_cursor(view))
+ view_cursors_dispose(cursor);
+}
+
+static void cursors_remove(const Arg *arg) {
+ View *view = vis->win->view;
+ view_cursors_dispose(view_cursor(view));
+}
+
static void replace(const Arg *arg) {
Key k = getkey();
if (!k.str[0])