diff --git a/text-motions.c b/text-motions.c index 3a970e84e..00e3cc86c 100644 --- a/text-motions.c +++ b/text-motions.c @@ -154,6 +154,15 @@ size_t text_line_next(Text *txt, size_t pos) { return it.pos; } +size_t text_line_offset(Text *txt, size_t pos, size_t off) { + char c; + size_t bol = text_line_begin(txt, pos); + Iterator it = text_iterator_get(txt, bol); + while (off-- > 0 && text_iterator_byte_get(&it, &c) && c != '\r' && c != '\n') + text_iterator_byte_next(&it, NULL); + return it.pos; +} + static size_t text_customword_start_next(Text *txt, size_t pos, int (*isboundry)(int)) { char c; Iterator it = text_iterator_get(txt, pos); diff --git a/text-motions.h b/text-motions.h index e69a07327..dca9fac41 100644 --- a/text-motions.h +++ b/text-motions.h @@ -34,6 +34,7 @@ size_t text_line_finish(Text*, size_t pos); size_t text_line_lastchar(Text*, size_t pos); size_t text_line_end(Text*, size_t pos); size_t text_line_next(Text*, size_t pos); +size_t text_line_offset(Text*, size_t pos, size_t off); /* * A longword consists of a sequence of non-blank characters, separated with * white space. TODO?: An empty line is also considered to be a word. diff --git a/vis.c b/vis.c index 88452099d..e32835e97 100644 --- a/vis.c +++ b/vis.c @@ -202,6 +202,8 @@ static Operator ops[] = { /* these can be passed as int argument to movement(&(const Arg){ .i = MOVE_* }) */ enum { + MOVE_LINE_DOWN, + MOVE_LINE_UP, MOVE_SCREEN_LINE_UP, MOVE_SCREEN_LINE_DOWN, MOVE_SCREEN_LINE_BEGIN, @@ -281,6 +283,8 @@ static size_t window_lines_middle(const Arg *arg); static size_t window_lines_bottom(const Arg *arg); static Movement moves[] = { + [MOVE_LINE_UP] = { .win = window_line_up }, + [MOVE_LINE_DOWN] = { .win = window_line_down }, [MOVE_SCREEN_LINE_UP] = { .win = window_screenline_up }, [MOVE_SCREEN_LINE_DOWN] = { .win = window_screenline_down }, [MOVE_SCREEN_LINE_BEGIN] = { .win = window_screenline_begin, .type = CHARWISE }, @@ -380,6 +384,8 @@ static TextObject textobjs[] = { /* if some movements are forced to be linewise, they are translated to text objects */ static TextObject *moves_linewise[] = { + [MOVE_LINE_UP] = &textobjs[TEXT_OBJ_LINE_UP], + [MOVE_LINE_DOWN] = &textobjs[TEXT_OBJ_LINE_DOWN], [MOVE_SCREEN_LINE_UP] = &textobjs[TEXT_OBJ_LINE_UP], [MOVE_SCREEN_LINE_DOWN] = &textobjs[TEXT_OBJ_LINE_DOWN], }; diff --git a/window.c b/window.c index d54678029..47fad1ac2 100644 --- a/window.c +++ b/window.c @@ -788,6 +788,30 @@ size_t window_scroll_down(Win *win, int lines) { return cursor->pos; } +size_t window_line_up(Win *win) { + Cursor *cursor = &win->cursor; + if (cursor->line->prev && cursor->line->prev->prev && + cursor->line->lineno != cursor->line->prev->lineno && + cursor->line->prev->lineno != cursor->line->prev->prev->lineno) + return window_screenline_up(win); + size_t bol = text_line_begin(win->text, cursor->pos); + size_t prev = text_line_prev(win->text, bol); + size_t pos = text_line_offset(win->text, prev, cursor->pos - bol); + window_cursor_to(win, pos); + return cursor->pos; +} + +size_t window_line_down(Win *win) { + Cursor *cursor = &win->cursor; + if (!cursor->line->next || cursor->line->next->lineno != cursor->line->lineno) + return window_screenline_down(win); + size_t bol = text_line_begin(win->text, cursor->pos); + size_t next = text_line_next(win->text, bol); + size_t pos = text_line_offset(win->text, next, cursor->pos - bol); + window_cursor_to(win, pos); + return cursor->pos; +} + size_t window_screenline_up(Win *win) { Cursor *cursor = &win->cursor; if (!cursor->line->prev) diff --git a/window.h b/window.h index add8e2a75..686f67fbf 100644 --- a/window.h +++ b/window.h @@ -36,6 +36,8 @@ void window_tabwidth_set(Win*, int tabwidth); * they return new cursor postion */ size_t window_char_next(Win*); size_t window_char_prev(Win*); +size_t window_line_down(Win*); +size_t window_line_up(Win*); size_t window_screenline_down(Win*); size_t window_screenline_up(Win*); size_t window_screenline_begin(Win*);