aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2017-06-10 15:03:46 +0200
committerMarc André Tanner <mat@brain-dump.org>2017-06-15 15:51:42 +0200
commit708216769c7cdf02ce3f69785a4356efd7dea0f7 (patch)
tree19f8faad6ff334d3d09d1f41ab1070a52bdbc54c
parent0df87384ac791bbf85cf4b3d386fb851e5b87de4 (diff)
downloadvis-708216769c7cdf02ce3f69785a4356efd7dea0f7.tar.gz
vis-708216769c7cdf02ce3f69785a4356efd7dea0f7.tar.xz
vis: promote selections to first class primitives
This unifies cursors and selections. The cursor are now represendted as singleton selections.
-rw-r--r--main.c31
-rw-r--r--sam.c31
-rw-r--r--view.c196
-rw-r--r--view.h22
-rw-r--r--vis-prompt.c2
-rw-r--r--vis.c33
6 files changed, 86 insertions, 229 deletions
diff --git a/main.c b/main.c
index 11ec217..350f00d 100644
--- a/main.c
+++ b/main.c
@@ -1309,12 +1309,8 @@ static const char *cursors_align_indent(Vis *vis, const char *keys, const Arg *a
for (int i = 0; i < columns; i++) {
int mincol = INT_MAX, maxcol = 0;
for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
- size_t pos;
Filerange sel = view_cursors_selection_get(c);
- if (text_range_valid(&sel))
- pos = left_align ? sel.start : sel.end;
- else
- pos = view_cursors_pos(c);
+ size_t pos = left_align ? sel.start : sel.end;
int col = text_line_width_get(txt, pos);
if (col < mincol)
mincol = col;
@@ -1329,15 +1325,9 @@ static const char *cursors_align_indent(Vis *vis, const char *keys, const Arg *a
memset(buf, ' ', len);
for (Cursor *c = view_cursors_column(view, i); c; c = view_cursors_column_next(c, i)) {
- size_t pos, ipos;
Filerange sel = view_cursors_selection_get(c);
- if (text_range_valid(&sel)) {
- pos = left_align ? sel.start : sel.end;
- ipos = sel.start;
- } else {
- pos = view_cursors_pos(c);
- ipos = pos;
- }
+ size_t pos = left_align ? sel.start : sel.end;
+ size_t ipos = sel.start;
int col = text_line_width_get(txt, pos);
if (col < maxcol) {
size_t off = maxcol - col;
@@ -1366,12 +1356,9 @@ static const char *cursors_select(Vis *vis, const char *keys, const Arg *arg) {
Text *txt = vis_text(vis);
View *view = vis_view(vis);
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)) {
+ if (text_range_valid(&word))
view_cursors_selection_set(cursor, &word);
- view_cursors_to(cursor, text_char_prev(txt, word.end));
- }
}
vis_mode_switch(vis, VIS_MODE_VISUAL);
return keys;
@@ -1476,12 +1463,8 @@ static const char *cursors_remove_column_except(Vis *vis, const char *keys, cons
static const char *cursors_navigate(Vis *vis, const char *keys, const Arg *arg) {
View *view = vis_view(vis);
- if (!view_cursors_multiple(view)) {
- Filerange sel = view_selection_get(view);
- if (!text_range_valid(&sel))
- return wscroll(vis, keys, arg);
- return keys;
- }
+ if (!view_cursors_multiple(view))
+ return wscroll(vis, keys, arg);
Cursor *c = view_cursors_primary_get(view);
VisCountIterator it = vis_count_iterator_get(vis, 1);
while (vis_count_iterator_next(&it)) {
@@ -1558,7 +1541,6 @@ static const char *selections_rotate(Vis *vis, const char *keys, const Arg *arg)
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);
@@ -1585,7 +1567,6 @@ static const char *selections_trim(Vis *vis, const char *keys, const Arg *arg) {
&& isspace((unsigned char)b); sel.start++);
if (sel.start < sel.end) {
view_cursors_selection_set(c, &sel);
- view_cursors_selection_sync(c);
} else if (!view_cursors_dispose(c)) {
vis_mode_switch(vis, VIS_MODE_NORMAL);
}
diff --git a/sam.c b/sam.c
index d04c230..815940f 100644
--- a/sam.c
+++ b/sam.c
@@ -1213,7 +1213,7 @@ enum SamError sam_cmd(Vis *vis, const char *s) {
if (c->cursor) {
if (visual) {
view_cursors_selection_set(c->cursor, &sel);
- view_cursors_selection_sync(c->cursor);
+ view_cursors_selection_start(c->cursor);
} else {
if (memchr(c->data, '\n', c->len))
view_cursors_to(c->cursor, sel.start);
@@ -1224,7 +1224,7 @@ enum SamError sam_cmd(Vis *vis, const char *s) {
Cursor *cursor = view_cursors_new(c->win->view, sel.start);
if (cursor) {
view_cursors_selection_set(cursor, &sel);
- view_cursors_selection_sync(cursor);
+ view_cursors_selection_start(cursor);
}
}
}
@@ -1239,8 +1239,7 @@ enum SamError sam_cmd(Vis *vis, const char *s) {
view_cursors_primary_set(view_cursors(vis->win->view));
bool completed = true;
for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) {
- Filerange sel = view_cursors_selection_get(c);
- if (text_range_valid(&sel)) {
+ if (view_selection_anchored(c)) {
completed = false;
break;
}
@@ -1504,10 +1503,12 @@ static bool cmd_print(Vis *vis, Win *win, Command *cmd, const char *argv[], Curs
else
cur = view_cursors_new_force(view, pos);
if (cur) {
- if (range->start != range->end)
+ if (range->start != range->end) {
view_cursors_selection_set(cur, range);
- else
+ view_cursors_selection_start(cur);
+ } else {
view_cursors_selection_clear(cur);
+ }
}
return cur != NULL;
}
@@ -1559,17 +1560,16 @@ static bool cmd_write(Vis *vis, Win *win, Command *cmd, const char *argv[], Curs
return false;
}
+ bool visual = vis->mode->visual;
+
for (Cursor *c = view_cursors(win->view); c; c = view_cursors_next(c)) {
- Filerange range = view_cursors_selection_get(c);
- bool invalid_range = !text_range_valid(&range);
- if (invalid_range)
- range = *r;
+ Filerange range = visual ? view_cursors_selection_get(c) : *r;
ssize_t written = text_write_range(text, &range, file->fd);
if (written == -1 || (size_t)written != text_range_size(&range)) {
vis_info_show(vis, "Can not write to stdout");
return false;
}
- if (invalid_range)
+ if (!visual)
break;
}
@@ -1612,13 +1612,10 @@ static bool cmd_write(Vis *vis, Win *win, Command *cmd, const char *argv[], Curs
}
bool failure = false;
+ bool visual = vis->mode->visual;
for (Cursor *c = view_cursors(win->view); c; c = view_cursors_next(c)) {
- Filerange range = view_cursors_selection_get(c);
- bool invalid_range = !text_range_valid(&range);
- if (invalid_range)
- range = *r;
-
+ Filerange range = visual ? view_cursors_selection_get(c) : *r;
ssize_t written = text_save_write_range(ctx, &range);
failure = (written == -1 || (size_t)written != text_range_size(&range));
if (failure) {
@@ -1626,7 +1623,7 @@ static bool cmd_write(Vis *vis, Win *win, Command *cmd, const char *argv[], Curs
break;
}
- if (invalid_range)
+ if (!visual)
break;
}
diff --git a/view.c b/view.c
index 76410aa..2106b0e 100644
--- a/view.c
+++ b/view.c
@@ -37,26 +37,23 @@ enum {
* The view_selections_{get,set} functions take care of adding/removing
* the necessary offset for the last character.
*/
-struct Selection {
- Mark anchor; /* position where the selection was created */
- Mark cursor; /* other selection endpoint where it changes */
- View *view; /* associated view to which this selection belongs */
- Cursor *cur; /* cursor whose movement will affect this selection */
- Selection *prev, *next; /* previsous/next selections in no particular order */
-};
+
+typedef struct {
+ Mark anchor;
+ Mark cursor;
+} SelectionRegion;
struct Cursor { /* cursor position */
+ Mark cursor; /* other selection endpoint where it changes */
+ Mark anchor; /* position where the selection was created */
+ bool anchored; /* whether anchor remains fixed */
size_t pos; /* in bytes from the start of the file */
int row, col; /* in terms of zero based screen coordinates */
int lastcol; /* remembered column used when moving across lines */
Line *line; /* screen line on which cursor currently resides */
- Mark mark; /* mark used to keep track of current cursor position */
- Mark mark_old; /* previous value of the mark, used to recover cursor position */
- Selection *sel; /* selection (if any) which folows the cursor upon movement */
- Mark lastsel_anchor;/* previously used selection data, */
- Mark lastsel_cursor;/* used to restore it */
int generation; /* used to filter out newly created cursors during iteration */
int number; /* how many cursors are located before this one */
+ SelectionRegion region; /* saved selection region */
View *view; /* associated view to which this cursor belongs */
Cursor *prev, *next;/* previous/next cursors ordered by location at creation time */
};
@@ -86,7 +83,6 @@ struct View {
const SyntaxSymbol *symbols[SYNTAX_SYMBOL_LAST]; /* symbols to use for white spaces etc */
int tabwidth; /* how many spaces should be used to display a tab character */
Cursor *cursors; /* all cursors currently active */
- Selection *selections; /* all selected regions */
int cursor_generation; /* used to filter out newly created cursors during iteration */
bool need_update; /* whether view has been redrawn */
bool large_file; /* optimize for displaying large files */
@@ -261,13 +257,12 @@ static bool view_addch(View *view, Cell *cell) {
static void cursor_to(Cursor *c, size_t pos) {
Text *txt = c->view->text;
- c->mark_old = c->mark;
- c->mark = text_mark_set(txt, pos);
+ c->cursor = text_mark_set(txt, pos);
+ if (!c->anchored)
+ c->anchor = c->cursor;
if (pos != c->pos)
c->lastcol = 0;
c->pos = pos;
- if (c->sel)
- c->sel->cursor = text_mark_set(txt, pos);
if (!view_coord_get(c->view, pos, &c->line, &c->row, &c->col)) {
if (c->view->cursor == c) {
c->line = c->view->topline;
@@ -484,8 +479,6 @@ void view_free(View *view) {
return;
while (view->cursors)
view_cursors_free(view->cursors);
- while (view->selections)
- view_selections_free(view->selections);
free(view->lines);
free(view);
}
@@ -1028,7 +1021,6 @@ bool view_cursors_dispose(Cursor *c) {
View *view = c->view;
if (!view->cursors || !view->cursors->next)
return false;
- view_selections_free(c->sel);
view_cursors_free(c);
view_cursors_primary_set(view->cursor);
return true;
@@ -1064,11 +1056,7 @@ Cursor *view_cursors_primary_get(View *view) {
void view_cursors_primary_set(Cursor *c) {
if (!c)
return;
- View *view = c->view;
- view->cursor = c;
- Filerange sel = view_cursors_selection_get(c);
- view_cursors_to(c, view_cursors_pos(c));
- view_cursors_selection_set(c, &sel);
+ c->view->cursor = c;
}
Cursor *view_cursors_prev(Cursor *c) {
@@ -1092,8 +1080,7 @@ Cursor *view_cursors_next(Cursor *c) {
}
size_t view_cursors_pos(Cursor *c) {
- size_t pos = text_mark_get(c->view->text, c->mark);
- return pos != EPOS ? pos : text_mark_get(c->view->text, c->mark_old);
+ return text_mark_get(c->view->text, c->cursor);
}
size_t view_cursors_line(Cursor *c) {
@@ -1171,147 +1158,46 @@ void view_cursors_place(Cursor *c, size_t line, size_t col) {
}
void view_cursors_selection_start(Cursor *c) {
- if (c->sel)
- return;
- size_t pos = view_cursors_pos(c);
- if (pos == EPOS || !(c->sel = view_selections_new(c->view, c)))
- return;
- Text *txt = c->view->text;
- c->sel->anchor = text_mark_set(txt, pos);
- c->sel->cursor = c->sel->anchor;
- c->view->need_update = true;
-}
-
-void view_cursors_selection_restore(Cursor *c) {
- Text *txt = c->view->text;
- if (c->sel)
- return;
- Filerange sel = text_range_new(
- text_mark_get(txt, c->lastsel_anchor),
- text_mark_get(txt, c->lastsel_cursor)
- );
- if (!text_range_valid(&sel))
- return;
- view_cursors_to(c, sel.end);
- sel.end = text_char_next(txt, sel.end);
- if (!(c->sel = view_selections_new(c->view, c)))
- return;
- view_selections_set(c->sel, &sel);
-}
-
-void view_cursors_selection_stop(Cursor *c) {
- c->sel = NULL;
+ c->anchored = true;
}
void view_cursors_selection_clear(Cursor *c) {
- view_selections_free(c->sel);
+ c->anchored = false;
+ c->anchor = c->cursor;
c->view->need_update = true;
}
-void view_cursors_selection_swap(Cursor *c) {
- if (!c->sel)
- return;
- view_selections_swap(c->sel);
- view_cursors_selection_sync(c);
-}
-
-void view_cursors_selection_sync(Cursor *c) {
- if (!c->sel)
- return;
- Text *txt = c->view->text;
- size_t pos = text_mark_get(txt, c->sel->cursor);
- view_cursors_to(c, pos);
-}
-
-Filerange view_cursors_selection_get(Cursor *c) {
- return view_selections_get(c->sel);
-}
-
-void view_cursors_selection_set(Cursor *c, const Filerange *r) {
- if (!text_range_valid(r))
- return;
- bool new = !c->sel;
- if (new && !(c->sel = view_selections_new(c->view, c)))
- return;
- view_selections_set(c->sel, r);
- if (!new) {
- size_t pos = view_cursors_pos(c);
- if (!text_range_contains(r, pos))
- view_cursors_selection_sync(c);
- }
-}
-
-Selection *view_selections_new(View *view, Cursor *c) {
- Selection *s = calloc(1, sizeof(*s));
- if (!s)
- return NULL;
- s->view = view;
- s->next = view->selections;
- if (view->selections)
- view->selections->prev = s;
- view->selections = s;
- s->cur = c;
- return s;
+void view_cursors_selection_swap(Cursor *s) {
+ Mark temp = s->anchor;
+ s->anchor = s->cursor;
+ s->cursor = temp;
+ view_cursors_to(s, text_mark_get(s->view->text, s->cursor));
}
-void view_selections_free(Selection *s) {
- if (!s)
- return;
- if (s->prev)
- s->prev->next = s->next;
- if (s->next)
- s->next->prev = s->prev;
- if (s->view->selections == s)
- s->view->selections = s->next;
- Cursor *c = s->cur;
- if (c) {
- c->lastsel_anchor = s->anchor;
- c->lastsel_cursor = s->cursor;
- c->sel = NULL;
- }
- free(s);
+bool view_selection_anchored(Selection *s) {
+ return s->anchored;
}
void view_selections_clear(View *view) {
- while (view->selections)
- view_selections_free(view->selections);
+ for (Cursor *c = view->cursors; c; c = c->next)
+ view_cursors_selection_clear(c);
view_draw(view);
}
void view_cursors_clear(View *view) {
for (Cursor *c = view->cursors, *next; c; c = next) {
next = c->next;
- if (c != view->cursor) {
- view_selections_free(c->sel);
+ if (c != view->cursor)
view_cursors_free(c);
- }
}
view_draw(view);
}
-void view_selections_swap(Selection *s) {
- Mark temp = s->anchor;
- s->anchor = s->cursor;
- s->cursor = temp;
-}
-
-Selection *view_selections(View *view) {
- return view->selections;
-}
-
-Selection *view_selections_prev(Selection *s) {
- return s->prev;
-}
-
-Selection *view_selections_next(Selection *s) {
- return s->next;
-}
-
Filerange view_selection_get(View *view) {
- return view_selections_get(view->cursor->sel);
+ return view_cursors_selection_get(view->cursor);
}
-Filerange view_selections_get(Selection *s) {
+Filerange view_cursors_selection_get(Selection *s) {
if (!s)
return text_range_empty();
Text *txt = s->view->text;
@@ -1323,7 +1209,7 @@ Filerange view_selections_get(Selection *s) {
return sel;
}
-void view_selections_set(Selection *s, const Filerange *r) {
+void view_cursors_selection_set(Selection *s, const Filerange *r) {
if (!text_range_valid(r))
return;
Text *txt = s->view->text;
@@ -1340,7 +1226,27 @@ void view_selections_set(Selection *s, const Filerange *r) {
s->anchor = text_mark_set(txt, r->start);
s->cursor = text_mark_set(txt, end);
}
- s->view->need_update = true;
+ s->anchored = true;
+ view_cursors_to(s, text_mark_get(s->view->text, s->cursor));
+}
+
+void view_cursors_selection_save(Selection *s) {
+ s->region.cursor = s->cursor;
+ s->region.anchor = s->anchor;
+}
+
+bool view_cursors_selection_restore(Selection *s) {
+ Text *txt = s->view->text;
+ size_t pos = text_mark_get(txt, s->region.cursor);
+ if (pos == EPOS)
+ return false;
+ if (s->region.anchor != s->region.cursor && text_mark_get(txt, s->region.anchor) == EPOS)
+ return false;
+ s->cursor = s->region.cursor;
+ s->anchor = s->region.anchor;
+ s->anchored = true;
+ view_cursors_to(s, pos);
+ return true;
}
Text *view_text(View *view) {
diff --git a/view.h b/view.h
index a6d455e..833252a 100644
--- a/view.h
+++ b/view.h
@@ -6,7 +6,7 @@
typedef struct View View;
typedef struct Cursor Cursor;
-typedef struct Selection Selection;
+typedef Cursor Selection;
#include "text.h"
#include "ui.h"
@@ -162,30 +162,20 @@ void view_cursors_place(Cursor*, size_t line, size_t col);
/* start selected area at current cursor position. further cursor movements
* will affect the selected region. */
void view_cursors_selection_start(Cursor*);
-/* detach cursor from selection, further cursor movements will not affect
- * the selected region. */
-void view_cursors_selection_stop(Cursor*);
/* clear selection associated with this cursor (if any) */
void view_cursors_selection_clear(Cursor*);
/* move cursor position from one end of the selection to the other */
void view_cursors_selection_swap(Cursor*);
-/* move cursor to the end/boundary of the associated selection */
-void view_cursors_selection_sync(Cursor*);
-/* restore previous used selection of this cursor */
-void view_cursors_selection_restore(Cursor*);
/* get/set the selected region associated with this cursor */
Filerange view_cursors_selection_get(Cursor*);
void view_cursors_selection_set(Cursor*, const Filerange*);
-Selection *view_selections_new(View*, Cursor*);
-void view_selections_free(Selection*);
+void view_cursors_selection_save(Selection*);
+bool view_cursors_selection_restore(Selection*);
+
+bool view_selection_anchored(Cursor*);
+
void view_selections_clear(View*);
-void view_selections_swap(Selection*);
-Selection *view_selections(View*);
-Selection *view_selections_prev(Selection*);
-Selection *view_selections_next(Selection*);
-Filerange view_selections_get(Selection*);
-void view_selections_set(Selection*, const Filerange*);
Text *view_text(View*);
/* get number of columns, that is maximal number of cursors on a line */
diff --git a/vis-prompt.c b/vis-prompt.c
index a08a19f..c37f093 100644
--- a/vis-prompt.c
+++ b/vis-prompt.c
@@ -55,7 +55,7 @@ static const char *prompt_enter(Vis *vis, const char *keys, const Arg *arg) {
char *cmd = NULL;
Filerange range = view_selection_get(view);
- if (!text_range_valid(&range)) {
+ if (!vis->mode->visual) {
const char *pattern = NULL;
Regex *regex = text_regex_new();
size_t pos = view_cursor_get(view);
diff --git a/vis.c b/vis.c
index b689e6f..2455a59 100644
--- a/vis.c
+++ b/vis.c
@@ -245,10 +245,15 @@ void vis_window_status(Win *win, const char *status) {
}
void window_selection_save(Win *win) {
+ Vis *vis = win->vis;
File *file = win->file;
Filerange sel = view_cursors_selection_get(view_cursors(win->view));
file->marks[VIS_MARK_SELECTION_START] = text_mark_set(file->text, sel.start);
file->marks[VIS_MARK_SELECTION_END] = text_mark_set(file->text, sel.end);
+ if (!vis->action.op) {
+ for (Selection *s = view_cursors(win->view); s; s = view_cursors_next(s))
+ view_cursors_selection_save(s);
+ }
}
static void window_free(Win *win) {
@@ -966,39 +971,17 @@ void vis_do(Vis *vis) {
if (linewise && vis->mode != &vis_modes[VIS_MODE_VISUAL])
c.range = text_range_linewise(txt, &c.range);
- if (vis->mode->visual) {
+ if (vis->mode->visual)
view_cursors_selection_set(cursor, &c.range);
- if (vis->mode == &vis_modes[VIS_MODE_VISUAL] || a->textobj)
- view_cursors_selection_sync(cursor);
- }
if (a->op) {
size_t pos = a->op->func(vis, txt, &c);
if (pos == EPOS) {
view_cursors_dispose(cursor);
} else if (pos <= text_size(txt)) {
- /* moving the cursor will affect the selection.
- * because we want to be able to later restore
- * the old selection we update it again before
- * leaving visual mode.
- */
- Filerange sel = view_cursors_selection_get(cursor);
+ if (vis->mode->visual)
+ view_cursors_selection_save(cursor);
view_cursors_to(cursor, pos);
- if (vis->mode->visual) {
- if (sel.start == EPOS && sel.end == EPOS)
- sel = c.range;
- else if (sel.start == EPOS)
- sel = text_range_new(c.range.start, sel.end);
- else if (sel.end == EPOS)
- sel = text_range_new(c.range.start, sel.start);
- if (vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE])
- sel = text_range_linewise(txt, &sel);
- if (!text_range_contains(&sel, pos)) {
- Filerange cur = text_range_new(pos, pos);
- sel = text_range_union(&sel, &cur);
- }
- view_cursors_selection_set(cursor, &sel);
- }
}
}
}