diff --git a/vimode/src/cmd-runner.c b/vimode/src/cmd-runner.c index 49706a539..43878c6c1 100644 --- a/vimode/src/cmd-runner.c +++ b/vimode/src/cmd-runner.c @@ -704,6 +704,7 @@ static gboolean process_cmd(CmdDef *cmds, CmdContext *ctx, gboolean ins_mode) { if (orig_mode == VI_MODE_COMMAND_SINGLE) vi_set_mode(VI_MODE_INSERT); + ensure_current_line_expanded(ctx->sci); } else if (!consumed && ctx->kpl) { diff --git a/vimode/src/cmds/motion.c b/vimode/src/cmds/motion.c index 4492d98c9..d528d5d5d 100644 --- a/vimode/src/cmds/motion.c +++ b/vimode/src/cmds/motion.c @@ -41,9 +41,39 @@ void cmd_goto_right(CmdContext *c, CmdParams *p) } +static gint doc_line_from_visible_delta(CmdParams *p, gint line, gint delta, gboolean *overflow) +{ + gboolean oflow = FALSE; + gint new_line = line; + gint i = 0; + + while (i < ABS(delta)) + { + gint inc = delta > 0 ? 1 : -1; + gint tmp = new_line + inc; + + if (tmp < 0 || tmp >= p->line_num) + { + oflow = TRUE; + break; + } + new_line = tmp; + + if (SSM(p->sci, SCI_GETLINEVISIBLE, new_line, 0)) + i++; + } + + if (overflow) + *overflow = oflow; + + return new_line; +} + + void cmd_goto_up(CmdContext *c, CmdParams *p) { gint one_above, pos; + gboolean line_underflow; if (p->line == 0) return; @@ -53,8 +83,8 @@ void cmd_goto_up(CmdContext *c, CmdParams *p) * SCI_CHOOSECARETX which we cannot read directly from Scintilla and which * we want to keep - perform jump to previous/following line and add * one final SCI_LINEUP/SCI_LINEDOWN which recovers SCI_CHOOSECARETX for us. */ - one_above = p->line - p->num - 1; - if (one_above >= 0) + one_above = doc_line_from_visible_delta(p, p->line, -p->num - 1, &line_underflow); + if (!line_underflow) { /* Every case except for the first line - go one line above and perform * SCI_LINEDOWN. This ensures that even with wrapping on, we get the @@ -70,7 +100,7 @@ void cmd_goto_up(CmdContext *c, CmdParams *p) * on, we need to repeat SCI_LINEUP to get to the first line of wrapping. * This may lead to visible slow scrolling which is why there's the * fast case above for anything else but the first line. */ - gint one_below = p->line - p->num + 1; + gint one_below = doc_line_from_visible_delta(p, p->line, -p->num + 1, NULL); gint wrap_count; one_below = one_below > 0 ? one_below : 1; @@ -98,15 +128,13 @@ void cmd_goto_up_nonempty(CmdContext *c, CmdParams *p) static void goto_down(CmdParams *p, gint num) { gint one_above, pos; - gint last_line = p->line_num - 1; - if (p->line == last_line) + if (p->line >= p->line_num - 1) return; - /* see cmd_goto_up() for explanation */ - one_above = p->line + num - 1; - one_above = one_above < last_line ? one_above : last_line - 1; + one_above = doc_line_from_visible_delta(p, p->line, p->num - 1, NULL); pos = SSM(p->sci, SCI_GETLINEENDPOSITION, one_above, 0); + SET_POS_NOX(p->sci, pos, FALSE); SSM(p->sci, SCI_LINEDOWN, 0, 0); } @@ -136,7 +164,7 @@ void cmd_goto_down_one_less_nonempty(CmdContext *c, CmdParams *p) void cmd_goto_page_up(CmdContext *c, CmdParams *p) { gint shift = p->line_visible_num * p->num; - gint new_line = get_line_number_rel(p->sci, -shift); + gint new_line = doc_line_from_visible_delta(p, p->line, -shift, NULL); goto_nonempty(p->sci, new_line, TRUE); } @@ -144,7 +172,7 @@ void cmd_goto_page_up(CmdContext *c, CmdParams *p) void cmd_goto_page_down(CmdContext *c, CmdParams *p) { gint shift = p->line_visible_num * p->num; - gint new_line = get_line_number_rel(p->sci, shift); + gint new_line = doc_line_from_visible_delta(p, p->line, shift, NULL); goto_nonempty(p->sci, new_line, TRUE); } @@ -152,7 +180,7 @@ void cmd_goto_page_down(CmdContext *c, CmdParams *p) void cmd_goto_halfpage_up(CmdContext *c, CmdParams *p) { gint shift = p->num_present ? p->num : p->line_visible_num / 2; - gint new_line = get_line_number_rel(p->sci, -shift); + gint new_line = doc_line_from_visible_delta(p, p->line, -shift, NULL); goto_nonempty(p->sci, new_line, TRUE); } @@ -160,7 +188,7 @@ void cmd_goto_halfpage_up(CmdContext *c, CmdParams *p) void cmd_goto_halfpage_down(CmdContext *c, CmdParams *p) { gint shift = p->num_present ? p->num : p->line_visible_num / 2; - gint new_line = get_line_number_rel(p->sci, shift); + gint new_line = doc_line_from_visible_delta(p, p->line, shift, NULL); goto_nonempty(p->sci, new_line, TRUE); } @@ -168,7 +196,8 @@ void cmd_goto_halfpage_down(CmdContext *c, CmdParams *p) void cmd_goto_line(CmdContext *c, CmdParams *p) { gint num = p->num > p->line_num ? p->line_num : p->num; - goto_nonempty(p->sci, num - 1, TRUE); + num = doc_line_from_visible_delta(p, num, -1, NULL); + goto_nonempty(p->sci, num, TRUE); } @@ -177,22 +206,31 @@ void cmd_goto_line_last(CmdContext *c, CmdParams *p) gint num = p->num > p->line_num ? p->line_num : p->num; if (!p->num_present) num = p->line_num; - goto_nonempty(p->sci, num - 1, TRUE); + num = doc_line_from_visible_delta(p, num, -1, NULL); + goto_nonempty(p->sci, num, TRUE); } void cmd_goto_screen_top(CmdContext *c, CmdParams *p) { + gint line; gint top = p->line_visible_first; gint count = p->line_visible_num; - gint line = top + p->num; - goto_nonempty(p->sci, line > top + count ? top + count : line, FALSE); + gint max = doc_line_from_visible_delta(p, top, count, NULL); + gint num = p->num; + + if (!p->num_present) + num = 0; + + line = doc_line_from_visible_delta(p, top, num, NULL); + goto_nonempty(p->sci, line > max ? max : line, FALSE); } void cmd_goto_screen_middle(CmdContext *c, CmdParams *p) { - goto_nonempty(p->sci, p->line_visible_first + p->line_visible_num/2, FALSE); + gint num = doc_line_from_visible_delta(p, p->line_visible_first, p->line_visible_num / 2, NULL); + goto_nonempty(p->sci, num, FALSE); } @@ -200,7 +238,7 @@ void cmd_goto_screen_bottom(CmdContext *c, CmdParams *p) { gint top = p->line_visible_first; gint count = p->line_visible_num; - gint line = top + count - p->num; + gint line = doc_line_from_visible_delta(p, top, count - p->num, NULL); goto_nonempty(p->sci, line < top ? top : line, FALSE); } diff --git a/vimode/src/excmd-runner.c b/vimode/src/excmd-runner.c index 663291813..4cca689df 100644 --- a/vimode/src/excmd-runner.c +++ b/vimode/src/excmd-runner.c @@ -465,6 +465,7 @@ void excmd_perform(CmdContext *ctx, const gchar *cmd) { case ':': perform_simple_ex_cmd(ctx, cmd + 1); + ensure_current_line_expanded(ctx->sci); break; case '/': case '?': @@ -483,6 +484,7 @@ void excmd_perform(CmdContext *ctx, const gchar *cmd) pos = perform_search(ctx->sci, ctx->search_text, ctx->num, FALSE); if (pos >= 0) SET_POS(ctx->sci, pos, TRUE); + ensure_current_line_expanded(ctx->sci); break; } } diff --git a/vimode/src/utils.c b/vimode/src/utils.c index b491a2dc1..2a367a80c 100644 --- a/vimode/src/utils.c +++ b/vimode/src/utils.c @@ -219,3 +219,11 @@ void goto_nonempty(ScintillaObject *sci, gint line, gboolean scroll) pos = NEXT(sci, pos); SET_POS(sci, pos, scroll); } + + +void ensure_current_line_expanded(ScintillaObject *sci) +{ + gint line = GET_CUR_LINE(sci); + if (!SSM(sci, SCI_GETLINEVISIBLE, line, 0)) + SSM(sci, SCI_ENSUREVISIBLE, line, 0); +} diff --git a/vimode/src/utils.h b/vimode/src/utils.h index e2e83d97c..b535cae81 100644 --- a/vimode/src/utils.h +++ b/vimode/src/utils.h @@ -32,5 +32,6 @@ void perform_substitute(ScintillaObject *sci, const gchar *cmd, gint from, gint const gchar *flag_override); gint get_line_number_rel(ScintillaObject *sci, gint shift); +void ensure_current_line_expanded(ScintillaObject *sci); #endif