aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.def.h14
-rw-r--r--editor.c34
-rw-r--r--editor.h3
-rw-r--r--ui-curses.c15
-rw-r--r--ui.h1
-rw-r--r--view.c589
-rw-r--r--view.h104
-rw-r--r--vis.c217
8 files changed, 607 insertions, 370 deletions
diff --git a/config.def.h b/config.def.h
index 3007b60..f8f07bf 100644
--- a/config.def.h
+++ b/config.def.h
@@ -440,14 +440,15 @@ static KeyBinding vis_mode_visual[] = {
static void vis_mode_visual_enter(Mode *old) {
if (!old->visual) {
- view_selection_start(vis->win->view);
+ for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
+ view_cursors_selection_start(c);
vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_TEXTOBJ];
}
}
static void vis_mode_visual_leave(Mode *new) {
if (!new->visual) {
- view_selection_clear(vis->win->view);
+ view_selections_clear(vis->win->view);
vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
}
}
@@ -459,10 +460,9 @@ static KeyBinding vis_mode_visual_line[] = {
};
static void vis_mode_visual_line_enter(Mode *old) {
- View *view = vis->win->view;
- view_cursor_to(view, text_line_begin(vis->win->file->text, view_cursor_get(view)));
if (!old->visual) {
- view_selection_start(view);
+ for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
+ view_cursors_selection_start(c);
vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_TEXTOBJ];
}
movement(&(const Arg){ .i = MOVE_LINE_END });
@@ -470,7 +470,7 @@ static void vis_mode_visual_line_enter(Mode *old) {
static void vis_mode_visual_line_leave(Mode *new) {
if (!new->visual) {
- view_selection_clear(vis->win->view);
+ view_selections_clear(vis->win->view);
vis_modes[VIS_MODE_OPERATOR].parent = &vis_modes[VIS_MODE_MOVE];
} else {
view_cursor_to(vis->win->view, view_cursor_get(vis->win->view));
@@ -478,7 +478,7 @@ static void vis_mode_visual_line_leave(Mode *new) {
}
static KeyBinding vis_mode_readline[] = {
- { { KEY(BACKSPACE) }, call, { .f = editor_backspace_key } },
+ { { KEY(BACKSPACE) }, delete, { .i = MOVE_CHAR_PREV } },
{ { NONE(ESC) }, switchmode, { .i = VIS_MODE_NORMAL } },
{ { CONTROL('c') }, switchmode, { .i = VIS_MODE_NORMAL } },
{ { CONTROL('D') }, delete , { .i = MOVE_CHAR_NEXT } },
diff --git a/editor.c b/editor.c
index fac598c..a47c141 100644
--- a/editor.c
+++ b/editor.c
@@ -414,12 +414,11 @@ void editor_insert(Editor *ed, size_t pos, const char *data, size_t len) {
}
void editor_insert_key(Editor *ed, const char *data, size_t len) {
- View *view = ed->win->view;
- size_t pos = view_cursor_get(view);
- if (memchr(data, '\n', len) && view_cursor_viewpos(view).y+1 == view_height_get(view))
- view_viewport_down(view, 1);
- editor_insert(ed, pos, data, len);
- view_cursor_to(view, pos + len);
+ for (Cursor *c = view_cursors(ed->win->view); c; c = view_cursors_next(c)) {
+ size_t pos = view_cursors_pos(c);
+ editor_insert(ed, pos, data, len);
+ view_cursors_scroll_to(c, pos + len);
+ }
}
void editor_replace(Editor *ed, size_t pos, const char *data, size_t len) {
@@ -439,26 +438,11 @@ void editor_replace(Editor *ed, size_t pos, const char *data, size_t len) {
}
void editor_replace_key(Editor *ed, const char *data, size_t len) {
- View *view = ed->win->view;
- size_t pos = view_cursor_get(view);
- if (memchr(data, '\n', len) && view_cursor_viewpos(view).y+1 == view_height_get(view))
- view_viewport_down(view, 1);
- editor_replace(ed, pos, data, len);
- view_cursor_to(view, pos + len);
-}
-
-void editor_backspace_key(Editor *ed) {
- View *view = ed->win->view;
- Text *txt = ed->win->file->text;
- size_t end = view_cursor_get(view);
- size_t start = text_char_prev(txt, end);
- if (view_viewport_get(view).start == end) {
- view_viewport_up(view, 1);
- view_cursor_to(view, end);
+ for (Cursor *c = view_cursors(ed->win->view); c; c = view_cursors_next(c)) {
+ size_t pos = view_cursors_pos(c);
+ editor_replace(ed, pos, data, len);
+ view_cursors_scroll_to(c, pos + len);
}
- text_delete(txt, start, end-start);
- editor_windows_invalidate(ed, start, end);
- view_cursor_to(view, start);
}
void editor_delete(Editor *ed, size_t pos, size_t len) {
diff --git a/editor.h b/editor.h
index fb57469..7f05b12 100644
--- a/editor.h
+++ b/editor.h
@@ -75,7 +75,7 @@ typedef struct {
typedef struct {
size_t (*cmd)(const Arg*); /* a custom movement based on user input from vis.c */
- size_t (*view)(View*); /* a movement based on current window content from view.h */
+ size_t (*view)(Cursor*); /* a movement based on current window content from view.h */
size_t (*txt)(Text*, size_t pos); /* a movement form text-motions.h */
size_t (*file)(File*, size_t pos);
enum {
@@ -260,7 +260,6 @@ void editor_suspend(Editor*);
* that all windows which show the affected region are redrawn too. */
void editor_insert_key(Editor*, const char *data, size_t len);
void editor_replace_key(Editor*, const char *data, size_t len);
-void editor_backspace_key(Editor*);
void editor_insert(Editor*, size_t pos, const char *data, size_t len);
void editor_delete(Editor*, size_t pos, size_t len);
void editor_replace(Editor*, size_t pos, const char *data, size_t len);
diff --git a/ui-curses.c b/ui-curses.c
index 283ee16..5f72a2e 100644
--- a/ui-curses.c
+++ b/ui-curses.c
@@ -190,7 +190,6 @@ static void ui_window_draw(UiWin *w) {
if (win->winstatus)
ui_window_draw_status((UiWin*)win);
view_draw(win->view);
- view_cursor_to(win->view, view_cursor_get(win->view));
}
static void ui_window_reload(UiWin *w, File *file) {
@@ -356,14 +355,6 @@ static void ui_window_free(UiWin *w) {
free(win);
}
-static void ui_window_cursor_to(UiWin *w, int x, int y) {
- UiCursesWin *win = (UiCursesWin*)w;
- wmove(win->win, y, x);
- ui_window_draw_status(w);
- if (win->options & UI_OPTION_LINE_NUMBERS_RELATIVE)
- ui_window_draw_sidebar(win, view_lines_get(win->view));
-}
-
static void ui_window_draw_text(UiWin *w, const Line *line) {
UiCursesWin *win = (UiCursesWin*)w;
wmove(win->win, 0, 0);
@@ -372,6 +363,8 @@ static void ui_window_draw_text(UiWin *w, const Line *line) {
* the selection cohorent */
if (l->width == 1 && l->cells[0].data[0] == '\n') {
int attr = l->cells[0].attr;
+ if (l->cells[0].cursor)
+ attr = A_NORMAL | A_REVERSE;
if (l->cells[0].selected)
attr |= A_REVERSE;
wattrset(win->win, attr);
@@ -379,6 +372,8 @@ static void ui_window_draw_text(UiWin *w, const Line *line) {
} else {
for (int x = 0; x < l->width; x++) {
int attr = l->cells[x].attr;
+ if (l->cells[x].cursor)
+ attr = A_NORMAL | A_REVERSE;
if (l->cells[x].selected)
attr |= A_REVERSE;
wattrset(win->win, attr);
@@ -433,7 +428,6 @@ static UiWin *ui_window_new(Ui *ui, View *view, File *file) {
.draw = ui_window_draw,
.draw_status = ui_window_draw_status,
.draw_text = ui_window_draw_text,
- .cursor_to = ui_window_cursor_to,
.options = ui_window_options,
.reload = ui_window_reload,
};
@@ -610,6 +604,7 @@ Ui *ui_curses_new(Color *colors) {
noecho();
keypad(stdscr, TRUE);
meta(stdscr, TRUE);
+ curs_set(0);
/* needed because we use getch() which implicitly calls refresh() which
would clear the screen (overwrite it with an empty / unused stdscr */
refresh();
diff --git a/ui.h b/ui.h
index 1a65184..4f61ec1 100644
--- a/ui.h
+++ b/ui.h
@@ -54,7 +54,6 @@ struct UiWin {
void (*draw)(UiWin*);
void (*draw_text)(UiWin*, const Line*);
void (*draw_status)(UiWin*);
- void (*cursor_to)(UiWin*, int x, int y);
void (*reload)(UiWin*, File*);
void (*options)(UiWin*, enum UiOption);
};
diff --git a/view.c b/view.c
index eb858d9..157b2c9 100644
--- a/view.c
+++ b/view.c
@@ -26,16 +26,28 @@
#include "text-motions.h"
#include "util.h"
-typedef struct { /* cursor position */
+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 */
+ Selection *prev, *next; /* previsous/next selections in no particular order */
+};
+
+struct Cursor { /* cursor position */
Filepos pos; /* in bytes from the start of the file */
- Filepos lastpos; /* previous cursor position */
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 */
- bool highlighted; /* true e.g. when cursor is on a bracket */
-} Cursor;
+ Mark mark; /* mark used to keep track of current cursor position */
+ Selection *sel; /* selection (if any) which folows the cursor upon movement */
+ View *view; /* associated view to which this cursor belongs */
+ Cursor *prev, *next;/* previous/next cursors in no particular order */
+};
-struct View { /* viewable area, showing part of a file */
+/* Viewable area, showing part of a file. Keeps track of cursors and selections.
+ * At all times there exists at least one cursor, which is placed in the visible viewport.
+ * Additional cursors can be created and positioned anywhere in the file. */
+struct View {
Text *text; /* underlying text management */
UiWin *ui;
ViewEvent *events;
@@ -46,13 +58,14 @@ struct View { /* viewable area, showing part of a file */
Line *topline; /* top of the view, first line currently shown */
Line *lastline; /* last currently used line, always <= bottomline */
Line *bottomline; /* bottom of view, might be unused if lastline < bottomline */
- Filerange sel; /* selected text range in bytes from start of file */
- Cursor cursor; /* current cursor position within view */
+ Cursor *cursor; /* main cursor, always placed within the visible viewport */
Line *line; /* used while drawing view content, line where next char will be drawn */
int col; /* used while drawing view content, column where next char will be drawn */
Syntax *syntax; /* syntax highlighting definitions for this view or NULL */
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 */
};
static SyntaxSymbol symbols_none[] = {
@@ -73,21 +86,15 @@ static SyntaxSymbol symbols_default[] = {
static void view_clear(View *view);
static bool view_addch(View *view, Cell *cell);
-static size_t view_cursor_update(View *view);
+static bool view_coord_get(View *view, size_t pos, Line **retline, int *retrow, int *retcol);
/* set/move current cursor position to a given (line, column) pair */
-static size_t view_cursor_set(View *view, Line *line, int col);
+static size_t cursor_set(Cursor *cursor, Line *line, int col);
void view_tabwidth_set(View *view, int tabwidth) {
view->tabwidth = tabwidth;
view_draw(view);
}
-void view_selection_clear(View *view) {
- view->sel = text_range_empty();
- view_draw(view);
- view_cursor_update(view);
-}
-
/* reset internal view data structures (cell matrix, line offsets etc.) */
static void view_clear(View *view) {
/* calculate line number of first line */
@@ -115,27 +122,6 @@ static void view_clear(View *view) {
view->col = 0;
}
-Filerange view_selection_get(View *view) {
- Filerange sel = view->sel;
- if (sel.start > sel.end) {
- size_t tmp = sel.start;
- sel.start = sel.end;
- sel.end = tmp;
- }
- if (!text_range_valid(&sel))
- return text_range_empty();
- sel.end = text_char_next(view->text, sel.end);
- return sel;
-}
-
-void view_selection_set(View *view, Filerange *sel) {
- Cursor *cursor = &view->cursor;
- view->sel = *sel;
- view_draw(view);
- if (view->ui)
- view->ui->cursor_to(view->ui, cursor->col, cursor->row);
-}
-
Filerange view_viewport_get(View *view) {
return (Filerange){ .start = view->start, .end = view->end };
}
@@ -242,7 +228,7 @@ static bool view_addch(View *view, Cell *cell) {
}
CursorPos view_cursor_getpos(View *view) {
- Cursor *cursor = &view->cursor;
+ Cursor *cursor = view->cursor;
Line *line = cursor->line;
CursorPos pos = { .line = line->lineno, .col = cursor->col };
while (line->prev && line->prev->lineno == pos.line) {
@@ -253,16 +239,53 @@ CursorPos view_cursor_getpos(View *view) {
return pos;
}
-ViewPos view_cursor_viewpos(View *view) {
- return (ViewPos){ .x = view->cursor.col, .y = view->cursor.row };
+static void cursor_to(Cursor *c, size_t pos) {
+ Text *txt = c->view->text;
+ c->mark = text_mark_set(txt, pos);
+ if (pos != c->pos)
+ c->lastcol = 0;
+ c->pos = pos;
+ if (c->sel) {
+ size_t anchor = text_mark_get(txt, c->sel->anchor);
+ size_t cursor = text_mark_get(txt, c->sel->cursor);
+ /* do we have to change the orientation of the selection? */
+ if (pos < anchor && anchor < cursor) {
+ /* right extend -> left extend */
+ anchor = text_char_next(txt, anchor);
+ c->sel->anchor = text_mark_set(txt, anchor);
+ } else if (cursor < anchor && anchor <= pos) {
+ /* left extend -> right extend */
+ anchor = text_char_prev(txt, anchor);
+ c->sel->anchor = text_mark_set(txt, anchor);
+ }
+ if (anchor <= pos)
+ pos = text_char_next(txt, pos);
+ 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;
+ c->row = 0;
+ c->col = 0;
+ }
+ return;
+ }
+ // TODO: minimize number of redraws
+ view_draw(c->view);
}
-/* snyc current cursor position with internal Line/Cell structures */
-static void view_cursor_sync(View *view) {
+static bool view_coord_get(View *view, size_t pos, Line **retline, int *retrow, int *retcol) {
int row = 0, col = 0;
- size_t cur = view->start, pos = view->cursor.pos;
+ size_t cur = view->start;
Line *line = view->topline;
+ if (pos < view->start || pos > view->end) {
+ if (retline) *retline = NULL;
+ if (retrow) *retrow = -1;
+ if (retcol) *retcol = -1;
+ return false;
+ }
+
while (line && line != view->lastline && cur < pos) {
if (cur + line->len > pos)
break;
@@ -283,71 +306,16 @@ static void view_cursor_sync(View *view) {
row = view->height - 1;
}
- view->cursor.line = line;
- view->cursor.row = row;
- view->cursor.col = col;
-}
-
-/* place the cursor according to the screen coordinates in view->{row,col} and
- * fire user callback. if a selection is active, redraw the view to reflect
- * its changes. */
-static size_t view_cursor_update(View *view) {
- Cursor *cursor = &view->cursor;
- if (view->sel.start != EPOS) {
- view->sel.end = cursor->pos;
- view_draw(view);
- } else if (view->ui && view->syntax) {
- size_t pos = cursor->pos;
- size_t pos_match = text_bracket_match_except(view->text, pos, "<>");
- if (pos != pos_match && view->start <= pos_match && pos_match < view->end) {
- if (cursor->highlighted)
- view_draw(view); /* clear active highlighting */
- cursor->pos = pos_match;
- view_cursor_sync(view);
- cursor->line->cells[cursor->col].selected = true;
- cursor->pos = pos;
- view_cursor_sync(view);
- view->ui->draw_text(view->ui, view->topline);
- cursor->highlighted = true;
- } else if (cursor->highlighted) {
- cursor->highlighted = false;
- view_draw(view);
- }
- }
- if (cursor->pos != cursor->lastpos)
- cursor->lastcol = 0;
- cursor->lastpos = cursor->pos;
- if (view->ui)
- view->ui->cursor_to(view->ui, cursor->col, cursor->row);
- return cursor->pos;
+ if (retline) *retline = line;
+ if (retrow) *retrow = row;
+ if (retcol) *retcol = col;
+ return true;
}
/* move the cursor to the character at pos bytes from the begining of the file.
* if pos is not in the current viewport, redraw the view to make it visible */
void view_cursor_to(View *view, size_t pos) {
- size_t max = text_size(view->text);
-
- if (pos > max)
- pos = max > 0 ? max - 1 : 0;
-
- if (pos == max && view->end != max) {
- /* do not display an empty screen when shoviewg the end of the file */
- view->start = max - 1;
- view_viewport_up(view, view->height / 2);
- } else {
- /* set the start of the viewable region to the start of the line on which
- * the cursor should be placed. if this line requires more space than
- * available in the view then simply start displaying text at the new
- * cursor position */
- for (int i = 0; i < 2 && (pos < view->start || pos > view->end); i++) {
- view->start = i == 0 ? text_line_begin(view->text, pos) : pos;
- view_draw(view);
- }
- }
-
- view->cursor.pos = pos;
- view_cursor_sync(view);
- view_cursor_update(view);
+ view_cursors_to(view->cursor, pos);
}
/* redraw the complete with data starting from view->start bytes into the file.
@@ -366,8 +334,6 @@ void view_draw(View *view) {
text[rem] = '\0';
/* current position into buffer from which to interpret a character */
char *cur = text;
- /* current selection */
- Filerange sel = view_selection_get(view);
/* syntax definition to use */
Syntax *syntax = view->syntax;
/* matched tokens for each syntax rule */
@@ -467,8 +433,6 @@ void view_draw(View *view) {
}
cell.attr = attrs;
- if (sel.start <= pos && pos < sel.end)
- cell.selected = true;
if (!view_addch(view, &cell))
break;
@@ -489,11 +453,63 @@ void view_draw(View *view) {
l->len = 0;
}
- view_cursor_sync(view);
+ for (Selection *s = view->selections; s; s = s->next) {
+ Filerange sel = view_selections_get(s);
+ if (text_range_valid(&sel)) {
+ Line *start_line; int start_col;
+ Line *end_line; int end_col;
+ view_coord_get(view, sel.start, &start_line, NULL, &start_col);
+ view_coord_get(view, sel.end, &end_line, NULL, &end_col);
+ if (start_line || end_line) {
+ if (!start_line) {
+ start_line = view->topline;
+ start_col = 0;
+ }
+ if (!end_line) {
+ end_line = view->lastline;
+ end_col = end_line->width;
+ }
+ for (Line *l = start_line; l != end_line->next; l = l->next) {
+ int col = (l == start_line) ? start_col : 0;
+ int end = (l == end_line) ? end_col : l->width;
+ while (col < end) {
+ l->cells[col++].selected = true;
+ }
+ }
+ }
+
+ if (view->events && view->events->selection)
+ view->events->selection(view->events->data, &sel);
+ }
+ }
+
+ for (Cursor *c = view->cursors; c; c = c->next) {
+ size_t pos = view_cursors_pos(c);
+ if (view_coord_get(view, pos, &c->line, &c->row, &c->col)) {
+ c->line->cells[c->col].cursor = true;
+ /* if the cursor is at the end of the document where nothing
+ * is currently displayed, add an empty cell */
+ if (pos == text_size(view->text)) {
+ c->line->width++;
+ c->line->cells[c->col].data[0] = ' ';
+ }
+
+ if (view->ui && view->syntax) {
+ Line *line_match; int col_match;
+ size_t pos_match = text_bracket_match_except(view->text, pos, "<>");
+ if (pos != pos_match && view_coord_get(view, pos_match, &line_match, NULL, &col_match)) {
+ line_match->cells[col_match].selected = true;
+ }
+ }
+ } else if (c == view->cursor) {
+ c->line = view->topline;
+ c->row = 0;
+ c->col = 0;
+ }
+ }
+
if (view->ui)
view->ui->draw_text(view->ui, view->topline);
- if (sel.start != EPOS && view->events && view->events->selection)
- view->events->selection(view->events->data, &sel);
}
bool view_resize(View *view, int width, int height) {
@@ -510,6 +526,7 @@ bool view_resize(View *view, int width, int height) {
if (view->lines)
memset(view->lines, 0, view->lines_size);
view_draw(view);
+ view_cursors_to(view->cursor, view_cursors_pos(view->cursor));
return true;
}
@@ -520,13 +537,17 @@ int view_height_get(View *view) {
void view_free(View *view) {
if (!view)
return;
+ while (view->cursors)
+ view_cursors_free(view->cursors);
+ while (view->selections)
+ view_selections_free(view->selections);
free(view->lines);
free(view);
}
void view_reload(View *view, Text *text) {
view->text = text;
- view_selection_clear(view);
+ view_selections_clear(view);
view_cursor_to(view, 0);
}
@@ -536,6 +557,11 @@ View *view_new(Text *text, ViewEvent *events) {
View *view = calloc(1, sizeof(View));
if (!view)
return NULL;
+ if (!view_cursors_new(view)) {
+ view_free(view);
+ return NULL;
+ }
+
view->text = text;
view->events = events;
view->tabwidth = 8;
@@ -545,7 +571,7 @@ View *view_new(Text *text, ViewEvent *events) {
view_free(view);
return NULL;
}
- view_selection_clear(view);
+
view_cursor_to(view, 0);
return view;
@@ -555,10 +581,10 @@ void view_ui(View *view, UiWin* ui) {
view->ui = ui;
}
-static size_t view_cursor_set(View *view, Line *line, int col) {
+static size_t cursor_set(Cursor *cursor, Line *line, int col) {
int row = 0;
+ View *view = cursor->view;
size_t pos = view->start;
- Cursor *cursor = &view->cursor;
/* get row number and file offset at start of the given line */
for (Line *cur = view->topline; cur && cur != line; cur = cur->next) {
pos += cur->len;
@@ -577,10 +603,9 @@ static size_t view_cursor_set(View *view, Line *line, int col) {
cursor->col = col;
cursor->row = row;
- cursor->pos = pos;
cursor->line = line;
- view_cursor_update(view);
+ cursor_to(cursor, pos);
return pos;
}
@@ -632,19 +657,19 @@ bool view_viewport_up(View *view, int n) {
}
void view_redraw_top(View *view) {
- Line *line = view->cursor.line;
+ Line *line = view->cursor->line;
for (Line *cur = view->topline; cur && cur != line; cur = cur->next)
view->start += cur->len;
view_draw(view);
- view_cursor_to(view, view->cursor.pos);
+ view_cursor_to(view, view->cursor->pos);
}
void view_redraw_center(View *view) {
int center = view->height / 2;
- size_t pos = view->cursor.pos;
+ size_t pos = view->cursor->pos;
for (int i = 0; i < 2; i++) {
int linenr = 0;
- Line *line = view->cursor.line;
+ Line *line = view->cursor->line;
for (Line *cur = view->topline; cur && cur != line; cur = cur->next)
linenr++;
if (linenr < center) {
@@ -662,11 +687,11 @@ void view_redraw_center(View *view) {
}
void view_redraw_bottom(View *view) {
- Line *line = view->cursor.line;
+ Line *line = view->cursor->line;
if (line == view->lastline)
return;
int linenr = 0;
- size_t pos = view->cursor.pos;
+ size_t pos = view->cursor->pos;
for (Line *cur = view->topline; cur && cur != line; cur = cur->next)
linenr++;
view_slide_down(view, view->height - linenr - 1);
@@ -674,36 +699,36 @@ void view_redraw_bottom(View *view) {
}
size_t view_slide_up(View *view, int lines) {
- Cursor *cursor = &view->cursor;
+ Cursor *cursor = view->cursor;
if (view_viewport_down(view, lines)) {
if (cursor->line == view->topline)
- view_cursor_set(view, view->topline, cursor->col);
+ cursor_set(cursor, view->topline, cursor->col);
else
view_cursor_to(view, cursor->pos);
} else {
- view_screenline_down(view);
+ view_screenline_down(cursor);
}
return cursor->pos;
}
size_t view_slide_down(View *view, int lines) {
- Cursor *cursor = &view->cursor;
+ Cursor *cursor = view->cursor;
if (view_viewport_up(view, lines)) {
if (cursor->line == view->lastline)
- view_cursor_set(view, view->lastline, cursor->col);
+ cursor_set(cursor, view->lastline, cursor->col);
else
view_cursor_to(view, cursor->pos);
} else {
- view_screenline_up(view);
+ view_screenline_up(cursor);
}
return cursor->pos;
}
size_t view_scroll_up(View *view, int lines) {
- Cursor *cursor = &view->cursor;
+ Cursor *cursor = view->cursor;
if (view_viewport_up(view, lines)) {
Line *line = cursor->line < view->lastline ? cursor->line : view->lastline;
- view_cursor_set(view, line, view->cursor.col);
+ cursor_set(cursor, line, view->cursor->col);
} else {
view_cursor_to(view, 0);
}
@@ -711,83 +736,79 @@ size_t view_scroll_up(View *view, int lines) {
}
size_t view_scroll_down(View *view, int lines) {
- Cursor *cursor = &view->cursor;
+ Cursor *cursor = view->cursor;
if (view_viewport_down(view, lines)) {
Line *line = cursor->line > view->topline ? cursor->line : view->topline;
- view_cursor_set(view, line, cursor->col);
+ cursor_set(cursor, line, cursor->col);
} else {
view_cursor_to(view, text_size(view->text));
}
return cursor->pos;
}
-size_t view_line_up(View *view) {
- Cursor *cursor = &view->cursor;
- if (cursor->line->prev && cursor->line->prev->prev &&
+size_t view_line_up(Cursor *cursor) {
+ if (cursor->line && cursor->line->prev && cursor->line->prev->prev &&
cursor->line->lineno != cursor->line->prev->lineno &&
cursor->line->prev->lineno != cursor->line->prev->prev->lineno)
- return view_screenline_up(view);
- size_t bol = text_line_begin(view->text, cursor->pos);
- size_t prev = text_line_prev(view->text, bol);
- size_t pos = text_line_offset(view->text, prev, cursor->pos - bol);
- view_cursor_to(view, pos);
- return cursor->pos;
+ return view_screenline_up(cursor);
+ size_t pos = text_line_up(cursor->view->text, cursor->pos);
+ view_cursors_to(cursor, pos);
+ return pos;
}
-size_t view_line_down(View *view) {
- Cursor *cursor = &view->cursor;
- if (!cursor->line->next || cursor->line->next->lineno != cursor->line->lineno)
- return view_screenline_down(view);
- size_t bol = text_line_begin(view->text, cursor->pos);
- size_t next = text_line_next(view->text, bol);
- size_t pos = text_line_offset(view->text, next, cursor->pos - bol);
- view_cursor_to(view, pos);
- return cursor->pos;
+size_t view_line_down(Cursor *cursor) {
+ if (cursor->line && (!cursor->line->next || cursor->line->next->lineno != cursor->line->lineno))
+ return view_screenline_down(cursor);
+ size_t pos = text_line_down(cursor->view->text, cursor->pos);
+ view_cursors_to(cursor, pos);
+ return pos;
}
-size_t view_screenline_up(View *view) {
- Cursor *cursor = &view->cursor;
+size_t view_screenline_up(Cursor *cursor) {
int lastcol = cursor->lastcol;
if (!lastcol)
lastcol = cursor->col;
if (!cursor->line->prev)
- view_scroll_up(view, 1);
+ view_scroll_up(cursor->view, 1);
if (cursor->line->prev)
- view_cursor_set(view, cursor->line->prev, lastcol);
+ cursor_set(cursor, cursor->line->prev, lastcol);
cursor->lastcol = lastcol;
return cursor->pos;
}
-size_t view_screenline_down(View *view) {
- Cursor *cursor = &view->cursor;
+size_t view_screenline_down(Cursor *cursor) {
int lastcol = cursor->lastcol;
if (!lastcol)
lastcol = cursor->col;
- if (!cursor->line->next && cursor->line == view->bottomline)
- view_scroll_down(view, 1);
+ if (!cursor->line->next && cursor->line == cursor->view->bottomline)
+ view_scroll_down(cursor->view, 1);
if (cursor->line->next)
- view_cursor_set(view, cursor->line->next, lastcol);
+ cursor_set(cursor, cursor->line->next, lastcol);
cursor->lastcol = lastcol;
return cursor->pos;
}
-size_t view_screenline_begin(View *view) {
- return view_cursor_set(view, view->cursor.line, 0);
+size_t view_screenline_begin(Cursor *cursor) {
+ if (!cursor->line)
+ return cursor->pos;
+ return cursor_set(cursor, cursor->line, 0);
}
-size_t view_screenline_middle(View *view) {
- Cursor *cursor = &view->cursor;
- return view_cursor_set(view, cursor->line, cursor->line->width / 2);
+size_t view_screenline_middle(Cursor *cursor) {
+ if (!cursor->line)
+ return cursor->pos;
+ return cursor_set(cursor, cursor->line, cursor->line->width / 2);
}
-size_t view_screenline_end(View *view) {
- Cursor *cursor = &view->cursor;
+size_t view_screenline_end(Cursor *cursor) {
+ if (!cursor->line)
+ return cursor->pos;
int col = cursor->line->width - 1;
- return view_cursor_set(view, cursor->line, col >= 0 ? col : 0);
+ return cursor_set(cursor, cursor->line, col >= 0 ? col : 0);
}
size_t view_cursor_get(View *view) {
- return view->cursor.pos;
+ return view_cursors_pos(view->cursor);
}
const Line *view_lines_get(View *view) {
@@ -795,18 +816,7 @@ const Line *view_lines_get(View *view) {
}
void view_scroll_to(View *view, size_t pos) {
- while (pos < view->start && view_viewport_up(view, 1));
- while (pos > view->end && view_viewport_down(view, 1));
- view_cursor_to(view, pos);
-}
-
-void view_selection_start(View *view) {
- if (view->sel.start != EPOS && view->sel.end != EPOS)
- return;
- size_t pos = view_cursor_get(view);
- view->sel.start = view->sel.end = pos;
- view_draw(view);
- view_cursor_to(view, pos);
+ view_cursors_scroll_to(view->cursor, pos);
}
void view_syntax_set(View *view, Syntax *syntax) {
@@ -851,3 +861,224 @@ size_t view_screenline_goto(View *view, int n) {
pos += line->len;
return pos;
}
+
+Cursor *view_cursors_new(View *view) {
+ Cursor *c = calloc(1, sizeof(*c));
+ if (!c)
+ return NULL;
+
+ c->view = view;
+ c->next = view->cursors;
+ if (view->cursors)
+ view->cursors->prev = c;
+ view->cursors = c;
+ view->cursor = c;
+ return c;
+}
+
+void view_cursors_free(Cursor *c) {
+ if (!c)
+ return;
+ if (c->prev)
+ c->prev->next = c->next;
+ if (c->next)
+ c->next->prev = c->prev;
+ if (c->view->cursors == c)
+ c->view->cursors = c->next;
+ if (c->view->cursor == c)
+ c->view->cursor = c->next ? c->next : c->prev;
+ free(c);
+}
+
+Cursor *view_cursors(View *view) {
+ return view->cursors;
+}
+
+Cursor *view_cursor(View *view) {
+ return view->cursor;
+}
+
+Cursor *view_cursors_prev(Cursor *c) {
+ return c->prev;
+}
+
+Cursor *view_cursors_next(Cursor *c) {
+ return c->next;
+}
+
+size_t view_cursors_pos(Cursor *c) {
+ return text_mark_get(c->view->text, c->mark);
+}
+
+void view_cursors_scroll_to(Cursor *c, size_t pos) {
+ View *view = c->view;
+ if (view->cursor == c) {
+ while (pos < view->start && view_viewport_up(view, 1));
+ while (pos > view->end && view_viewport_down(view, 1));
+ }
+ view_cursors_to(c, pos);
+}
+
+void view_cursors_to(Cursor *c, size_t pos) {
+ View *view = c->view;
+ if (c->view->cursors == c) {
+ c->mark = text_mark_set(view->text, pos);
+
+ size_t max = text_size(view->text);
+ if (pos == max && view->end != max) {
+ /* do not display an empty screen when shoviewg the end of the file */
+ view->start = pos;
+ view_viewport_up(view, view->height / 2);
+ } else {
+ /* set the start of the viewable region to the start of the line on which
+ * the cursor should be placed. if this line requires more space than
+ * available in the view then simply start displaying text at the new
+ * cursor position */
+ for (int i = 0; i < 2 && (pos < view->start || pos > view->end); i++) {
+ view->start = i == 0 ? text_line_begin(view->text, pos) : pos;
+ view_draw(view);
+ }
+ }
+ }
+
+ cursor_to(c, pos);
+}
+
+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)))
+ return;
+ Text *txt = c->view->text;
+ c->sel->anchor = text_mark_set(txt, pos);
+ c->sel->cursor = text_mark_set(txt, text_char_next(txt, pos));
+ view_draw(c->view);
+}
+
+void view_cursors_selection_stop(Cursor *c) {
+ c->sel = NULL;
+}
+
+void view_cursors_selection_clear(Cursor *c) {
+ view_selections_free(c->sel);
+ view_draw(c->view);
+}
+
+void view_cursors_selection_swap(Cursor *c) {
+ if (!c->sel)
+ return;
+ Text *txt = c->view->text;
+ bool left_extending = c->mark == c->sel->cursor;
+ view_selections_swap(c->sel);
+ c->mark = c->sel->cursor;
+ size_t pos = text_mark_get(txt, c->mark);
+ if (left_extending)
+ pos = text_char_prev(txt, pos);
+ cursor_to(c, pos);
+}
+
+Filerange view_cursors_selection_get(Cursor *c) {
+ return view_selections_get(c->sel);
+}
+
+void view_cursors_selection_set(Cursor *c, Filerange *r) {
+ if (!text_range_valid(r))
+ return;
+ if (!c->sel)
+ c->sel = view_selections_new(c->view);
+ if (!c->sel)
+ return;
+
+ view_selections_set(c->sel, r);
+}
+
+Selection *view_selections_new(View *view) {
+ 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;
+ return s;
+}
+
+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;
+ // XXX: add backlink Selection->Cursor?
+ for (Cursor *c = s->view->cursors; c; c = c->next) {
+ if (c->sel == s)
+ c->sel = NULL;
+ }
+ free(s);
+}
+
+void view_selections_clear(View *view) {
+ while (view->selections)
+ view_selections_free(view->selections);
+ view_draw(view);
+}
+
+void view_cursors_clear(View *view) {
+ for (Cursor *c = view->cursors, *next; c; c = next) {
+ next = c->next;
+ 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_selections_get(Selection *s) {
+ if (!s)
+ return text_range_empty();
+ Text *txt = s->view->text;
+ size_t anchor = text_mark_get(txt, s->anchor);
+ size_t cursor = text_mark_get(txt, s->cursor);
+ return text_range_new(anchor, cursor);
+}
+
+void view_selections_set(Selection *s, Filerange *r) {
+ if (!text_range_valid(r))
+ return;
+ Text *txt = s->view->text;
+ size_t anchor = text_mark_get(txt, s->anchor);
+ size_t cursor = text_mark_get(txt, s->cursor);
+ bool left_extending = anchor > cursor;
+ if (left_extending) {
+ s->anchor = text_mark_set(txt, r->end);
+ s->cursor = text_mark_set(txt, r->start);
+ } else {
+ s->anchor = text_mark_set(txt, r->start);
+ s->cursor = text_mark_set(txt, r->end);
+ }
+ view_draw(s->view);
+}
diff --git a/view.h b/view.h
index 0adec05..d993fbd 100644
--- a/view.h
+++ b/view.h
@@ -8,6 +8,8 @@
#include "syntax.h"
typedef struct View View;
+typedef struct Cursor Cursor;
+typedef struct Selection Selection;
typedef struct {
void *data;
@@ -24,7 +26,8 @@ typedef struct {
the same as in the underlying text, for example tabs get expanded */
unsigned int attr;
bool istab;
- bool selected;
+ bool selected; /* whether this cell is part of a selected region */
+ bool cursor; /* whether a cursor is currently locaated on the cell */
} Cell;
typedef struct Line Line;
@@ -55,13 +58,13 @@ void view_tabwidth_set(View*, int tabwidth);
/* cursor movements which also update selection if one is active.
* they return new cursor postion */
-size_t view_line_down(View*);
-size_t view_line_up(View*);
-size_t view_screenline_down(View*);
-size_t view_screenline_up(View*);
-size_t view_screenline_begin(View*);
-size_t view_screenline_middle(View*);
-size_t view_screenline_end(View*);
+size_t view_line_down(Cursor*);
+size_t view_line_up(Cursor*);
+size_t view_screenline_down(Cursor*);
+size_t view_screenline_up(Cursor*);
+size_t view_screenline_begin(Cursor*);
+size_t view_screenline_middle(Cursor*);
+size_t view_screenline_end(Cursor*);
/* move window content up/down, but keep cursor position unchanged unless it is
* on a now invisible line in which case we try to preserve the column position */
size_t view_slide_up(View*, int lines);
@@ -73,38 +76,11 @@ size_t view_scroll_down(View*, int lines);
/* place the cursor at the start ot the n-th window line, counting from 1 */
size_t view_screenline_goto(View*, int n);
-/* get cursor position in bytes from start of the file */
-size_t view_cursor_get(View*);
-
-typedef struct {
- int x, y;
-} ViewPos;
-
-ViewPos view_cursor_viewpos(View*);
-
const Line *view_lines_get(View*);
-/* get cursor position in terms of screen coordinates */
-CursorPos view_cursor_getpos(View*);
-/* moves window viewport in direction until pos is visible. should only be
- * used for short distances between current cursor position and destination */
-void view_scroll_to(View*, size_t pos);
-/* move cursor to a given position. changes the viewport to make sure that
- * position is visible. if the position is in the middle of a line, try to
- * adjust the viewport in such a way that the whole line is displayed */
-void view_cursor_to(View*, size_t pos);
/* redraw current cursor line at top/center/bottom of window */
void view_redraw_top(View*);
void view_redraw_center(View*);
void view_redraw_bottom(View*);
-/* start selected area at current cursor position. further cursor movements will
- * affect the selected region. */
-void view_selection_start(View*);
-/* returns the currently selected text region, is either empty or well defined,
- * i.e. sel.start <= sel.end */
-Filerange view_selection_get(View*);
-void view_selection_set(View*, Filerange *sel);
-/* clear selection and redraw window */
-void view_selection_clear(View*);
/* get the currently displayed area in bytes from the start of the file */
Filerange view_viewport_get(View*);
/* move visible viewport n-lines up/down, redraws the view but does not change
@@ -119,4 +95,62 @@ Syntax *view_syntax_get(View*);
void view_symbols_set(View*, int flags);
int view_symbols_get(View*);
+/* A view can manage multiple cursors, one of which (the main cursor) is always
+ * placed within the visible viewport. All functions named view_cursor_* operate
+ * on this cursor. Additional cursor can be created and manipulated using the
+ * functions named view_cursors_* */
+
+/* get main cursor position in terms of screen coordinates */
+CursorPos view_cursor_getpos(View*);
+/* get main cursor position in bytes from start of the file */
+size_t view_cursor_get(View*);
+/* moves window viewport in direction until pos is visible. should only be
+ * used for short distances between current cursor position and destination */
+void view_scroll_to(View*, size_t pos);
+/* move cursor to a given position. changes the viewport to make sure that
+ * position is visible. if the position is in the middle of a line, try to
+ * adjust the viewport in such a way that the whole line is displayed */
+void view_cursor_to(View*, size_t pos);
+/* create a new cursor */
+Cursor *view_cursors_new(View*);
+/* dispose an existing cursor */
+void view_cursors_free(Cursor*);
+/* only keep the main cursor, release all others together with their
+ * selections (if any) */
+void view_cursors_clear(View*);
+/* get the main cursor which is always in the visible viewport */
+Cursor *view_cursor(View*);
+/* get the first cursor */
+Cursor *view_cursors(View*);
+Cursor *view_cursors_prev(Cursor*);
+Cursor *view_cursors_next(Cursor*);
+/* get current position of cursor in bytes from the start of the file */
+size_t view_cursors_pos(Cursor*);
+/* place cursor at `pos' which should be in the interval [0, text-size] */
+void view_cursors_to(Cursor*, size_t pos);
+void view_cursors_scroll_to(Cursor*, size_t pos);
+/* 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*);
+/* get/set the selected region associated with this cursor */
+Filerange view_cursors_selection_get(Cursor*);
+void view_cursors_selection_set(Cursor*, Filerange*);
+
+Selection *view_selections_new(View*);
+void view_selections_free(Selection*);
+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*, Filerange*);
+
#endif
diff --git a/vis.c b/vis.c
index 594a7a9..76e806d 100644
--- a/vis.c
+++ b/vis.c
@@ -187,8 +187,8 @@ static Movement moves[] = {
[MOVE_LINE_NEXT] = { .txt = text_line_next, .type = LINEWISE },
[MOVE_LINE] = { .txt = line, .type = LINEWISE|IDEMPOTENT|JUMP},
[MOVE_COLUMN] = { .txt = column, .type = CHARWISE|IDEMPOTENT},
- [MOVE_CHAR_PREV] = { .txt = text_char_prev },
- [MOVE_CHAR_NEXT] = { .txt = text_char_next },
+ [MOVE_CHAR_PREV] = { .txt = text_char_prev, .type = CHARWISE },
+ [MOVE_CHAR_NEXT] = { .txt = text_char_next, .type = CHARWISE },
[MOVE_LINE_CHAR_PREV] = { .txt = text_line_char_prev, .type = CHARWISE },
[MOVE_LINE_CHAR_NEXT] = { .txt = text_line_char_next, .type = CHARWISE },
[MOVE_WORD_START_PREV] = { .txt = text_word_start_prev, .type = CHARWISE },
@@ -456,7 +456,6 @@ static size_t op_delete(OperatorContext *c) {
static size_t op_change(OperatorContext *c) {
op_delete(c);
- switchmode(&(const Arg){ .i = VIS_MODE_INSERT });
return c->range.start;
}
@@ -571,10 +570,13 @@ static size_t op_case_change(OperatorContext *c) {
static size_t op_join(OperatorContext *c) {
Text *txt = vis->win->file->text;
size_t pos = text_line_begin(txt, c->range.end), prev_pos;
- Filerange sel = view_selection_get(vis->win->view);
- /* if a selection ends at the begin of a line, skip line break */
- if (pos == c->range.end && text_range_valid(&sel))
- pos = text_line_prev(txt, pos);
+ /* if selection is linewise, skip last line break */
+ if (c->range.start == text_line_begin(txt, c->range.start) && pos == c->range.end) {
+ size_t line_prev = text_line_prev(txt, pos);
+ size_t line_prev_prev = text_line_prev(txt, line_prev);
+ if (line_prev_prev >= c->range.start)
+ pos = line_prev;
+ }
do {
prev_pos = pos;
@@ -811,13 +813,11 @@ static void replace(const Arg *arg) {
Key k = getkey();
if (!k.str[0])
return;
- size_t pos = view_cursor_get(vis->win->view);
action_reset(&vis->action_prev);
vis->action_prev.op = &ops[OP_REPEAT_REPLACE];
buffer_put(&vis->buffer_repeat, k.str, strlen(k.str));
- editor_replace(vis, pos, k.str, strlen(k.str));
+ editor_replace_key(vis, k.str, strlen(k.str));
text_snapshot(vis->win->file->text);
- view_cursor_to(vis->win->view, pos);
}
static void count(const Arg *arg) {
@@ -902,17 +902,8 @@ static void textobj(const Arg *arg) {
}
static void selection_end(const Arg *arg) {
- size_t pos = view_cursor_get(vis->win->view);
- Filerange sel = view_selection_get(vis->win->view);
- if (pos == sel.start) {
- pos = text_char_prev(vis->win->file->text, sel.end);
- } else {
- pos = sel.start;
- sel.start = text_char_prev(vis->win->file->text, sel.end);
- sel.end = pos;
- }
- view_selection_set(vis->win->view, &sel);
- view_cursor_to(vis->win->view, pos);
+ for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c))
+ view_cursors_selection_swap(c);
}
static void reg(const Arg *arg) {
@@ -1219,116 +1210,120 @@ static void switchmode(const Arg *arg) {
static void action_do(Action *a) {
Text *txt = vis->win->file->text;
View *view = vis->win->view;
- size_t pos = view_cursor_get(view);
int count = MAX(1, a->count);
- OperatorContext c = {
- .count = a->count,
- .pos = pos,
- .range = text_range_empty(),
- .reg = a->reg ? a->reg : &vis->registers[REG_DEFAULT],
- .linewise = a->linewise,
- .arg = &a->arg,
- };
- if (a->movement) {
- size_t start = pos;
- for (int i = 0; i < count; i++) {
- if (a->movement->txt)
- pos = a->movement->txt(txt, pos);
- else if (a->movement->view)
- pos = a->movement->view(view);
- else if (a->movement->file)
- pos = a->movement->file(vis->win->file, pos);
- else
- pos = a->movement->cmd(&a->arg);
- if (pos == EPOS || a->movement->type & IDEMPOTENT)
- break;
- }
+ for (Cursor *cursor = view_cursors(view), *next; cursor; cursor = next) {
+
+ next = view_cursors_next(cursor);
+ size_t pos = view_cursors_pos(cursor);
+ OperatorContext c = {
+ .count = a->count,
+ .pos = pos,
+ .range = text_range_empty(),
+ .reg = a->reg ? a->reg : &vis->registers[REG_DEFAULT],
+ .linewise = a->linewise,
+ .arg = &a->arg,
+ };
- if (pos == EPOS) {
- c.range.start = start;
- c.range.end = start;
- pos = start;
- } else {
- c.range.start = MIN(start, pos);
- c.range.end = MAX(start, pos);
- }
+ if (a->movement) {
+ size_t start = pos;
+ for (int i = 0; i < count; i++) {
+ if (a->movement->txt)
+ pos = a->movement->txt(txt, pos);
+ else if (a->movement->view)
+ pos = a->movement->view(cursor);
+ else if (a->movement->file)
+ pos = a->movement->file(vis->win->file, pos);
+ else
+ pos = a->movement->cmd(&a->arg);
+ if (pos == EPOS || a->movement->type & IDEMPOTENT)
+ break;
+ }
- if (!a->op) {
- if (a->movement->type & CHARWISE)
- view_scroll_to(view, pos);
- else
- view_cursor_to(view, pos);
- if (a->movement->type & JUMP)
- editor_window_jumplist_add(vis->win, pos);
- else
- editor_window_jumplist_invalidate(vis->win);
- } else if (a->movement->type & INCLUSIVE) {
- Iterator it = text_iterator_get(txt, c.range.end);
- text_iterator_char_next(&it, NULL);
- c.range.end = it.pos;
- }
- } else if (a->textobj) {
- if (vis->mode->visual)
- c.range = view_selection_get(view);
- else
- c.range.start = c.range.end = pos;
- for (int i = 0; i < count; i++) {
- Filerange r = a->textobj->range(txt, pos);
- if (!text_range_valid(&r))
- break;
- if (a->textobj->type == OUTER) {
- r.start--;
- r.end++;
+ if (pos == EPOS) {
+ c.range.start = start;
+ c.range.end = start;
+ pos = start;
+ } else {
+ c.range.start = MIN(start, pos);
+ c.range.end = MAX(start, pos);
}
- c.range = text_range_union(&c.range, &r);
+ if (!a->op) {
+ if (a->movement->type & CHARWISE)
+ view_cursors_scroll_to(cursor, pos);
+ else
+ view_cursors_to(cursor, pos);
+ if (a->movement->type & JUMP)
+ editor_window_jumplist_add(vis->win, pos);
+ else
+ editor_window_jumplist_invalidate(vis->win);
+ } else if (a->movement->type & INCLUSIVE) {
+ Iterator it = text_iterator_get(txt, c.range.end);
+ text_iterator_char_next(&it, NULL);
+ c.range.end = it.pos;
+ }
+ } else if (a->textobj) {
+ if (vis->mode->visual)
+ c.range = view_cursors_selection_get(cursor);
+ else
+ c.range.start = c.range.end = pos;
+ for (int i = 0; i < count; i++) {
+ Filerange r = a->textobj->range(txt, pos);
+ if (!text_range_valid(&r))
+ break;
+ if (a->textobj->type == OUTER) {
+ r.start--;
+ r.end++;
+ }
+
+ c.range = text_range_union(&c.range, &r);
- if (i < count - 1) {
- if (a->textobj == &textobjs[TEXT_OBJ_LINE_UP]) {
- pos = c.range.start - 1;
- } else {
- pos = c.range.end + 1;
+ if (i < count - 1) {
+ if (a->textobj == &textobjs[TEXT_OBJ_LINE_UP]) {
+ pos = c.range.start - 1;
+ } else {
+ pos = c.range.end + 1;
+ }
}
}
+
+ if (vis->mode->visual) {
+ view_cursors_selection_set(cursor, &c.range);
+ pos = c.range.end;
+ view_cursors_to(cursor, pos);
+ }
+ } else if (vis->mode->visual) {
+ c.range = view_cursors_selection_get(cursor);
+ if (!text_range_valid(&c.range))
+ c.range.start = c.range.end = pos;
}
- if (vis->mode->visual) {
- view_selection_set(view, &c.range);
- pos = c.range.end;
- view_cursor_to(view, pos);
+ if (vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE] && (a->movement || a->textobj)) {
+ Filerange sel = view_cursors_selection_get(cursor);
+ c.range = text_range_linewise(txt, &sel);
+ view_cursors_selection_set(cursor, &c.range);
}
- } else if (vis->mode->visual) {
- c.range = view_selection_get(view);
- if (!text_range_valid(&c.range))
- c.range.start = c.range.end = pos;
- }
-
- if (vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE] && (a->movement || a->textobj)) {
- Filerange sel = view_selection_get(view);
- sel.end = text_char_prev(txt, sel.end);
- size_t start = text_line_begin(txt, sel.start);
- size_t end = text_line_end(txt, sel.end);
- if (sel.start == pos) { /* extend selection upwards */
- sel.end = start;
- sel.start = end;
- } else { /* extend selection downwards */
- sel.start = start;
- sel.end = end;
+
+ if (a->op) {
+ size_t pos = a->op->func(&c);
+ if (pos != EPOS) {
+ view_cursors_to(cursor, pos);
+ } else {
+ view_cursors_free(cursor);
+ }
}
- view_selection_set(view, &sel);
- c.range = sel;
}
if (a->op) {
- view_cursor_to(view, a->op->func(&c));
- editor_draw(vis);
-
- if (vis->mode == &vis_modes[VIS_MODE_OPERATOR])
+ if (a->op == &ops[OP_CHANGE])
+ switchmode(&(const Arg){ .i = VIS_MODE_INSERT });
+ else if (vis->mode == &vis_modes[VIS_MODE_OPERATOR])
switchmode_to(vis->mode_prev);
else if (vis->mode->visual)
switchmode(&(const Arg){ .i = VIS_MODE_NORMAL });
text_snapshot(txt);
+ editor_draw(vis);
}
if (a != &vis->action_prev) {