From 18223a592efa4399e3951c86deeb712a13b05ca5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 30 Sep 2019 20:47:54 +0200 Subject: [PATCH 01/67] patch 8.1.2103: wrong error message if "termdebugger" is not executable Problem: wrong error message if "termdebugger" is not executable. Solution: Check if "termdebugger" is executable and give a clear error message. (Ozaki Kiichi, closes #5000) Fix indents. --- runtime/pack/dist/opt/termdebug/plugin/termdebug.vim | 11 ++++++++--- src/version.c | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 8629eb88c0..ce01ae5241 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -65,8 +65,8 @@ command -nargs=* -complete=file -bang Termdebug call s:StartDebug(0, 0, ) " Name of the gdb command, defaults to "gdb". -if !exists('termdebugger') - let termdebugger = 'gdb' +if !exists('g:termdebugger') + let g:termdebugger = 'gdb' endif let s:pc_id = 12 @@ -104,9 +104,14 @@ endfunc func s:StartDebug_internal(dict) if exists('s:gdbwin') - echoerr 'Terminal debugger already running' + echoerr 'Terminal debugger already running, cannot run two' return endif + if !executable(g:termdebugger) + echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"' + return + endif + let s:ptywin = 0 let s:pid = 0 diff --git a/src/version.c b/src/version.c index f5b9d6075e..fe9cee3a09 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2103, /**/ 2102, /**/ From 792cf5e1bec04c6d6d70cfbb9ef24c798b469731 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 30 Sep 2019 23:12:16 +0200 Subject: [PATCH 02/67] patch 8.1.2104: the normal.c file is too big Problem: The normal.c file is too big. Solution: Move do_pending_operator() to ops.c. (Yegappan Lakshmanan, closes #4999). --- src/globals.h | 7 + src/normal.c | 993 +------------------------------------------ src/ops.c | 941 +++++++++++++++++++++++++++++++++++++++- src/proto/normal.pro | 6 +- src/proto/ops.pro | 6 +- src/version.c | 2 + 6 files changed, 963 insertions(+), 992 deletions(-) diff --git a/src/globals.h b/src/globals.h index ac6bd8d81c..105c451381 100644 --- a/src/globals.h +++ b/src/globals.h @@ -778,6 +778,13 @@ EXTERN int VIsual_mode INIT(= 'v'); EXTERN int redo_VIsual_busy INIT(= FALSE); // TRUE when redoing Visual +/* + * The Visual area is remembered for reselection. + */ +EXTERN int resel_VIsual_mode INIT(= NUL); // 'v', 'V', or Ctrl-V +EXTERN linenr_T resel_VIsual_line_count; // number of lines +EXTERN colnr_T resel_VIsual_vcol; // nr of cols or end col + #ifdef FEAT_MOUSE /* * When pasting text with the middle mouse button in visual mode with diff --git a/src/normal.c b/src/normal.c index 98ec9ef843..1fddeae07a 100644 --- a/src/normal.c +++ b/src/normal.c @@ -14,24 +14,14 @@ #include "vim.h" -/* - * The Visual area is remembered for reselection. - */ -static int resel_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */ -static linenr_T resel_VIsual_line_count; /* number of lines */ -static colnr_T resel_VIsual_vcol; /* nr of cols or end col */ -static int VIsual_mode_orig = NUL; /* saved Visual mode */ - +static int VIsual_mode_orig = NUL; // saved Visual mode static int restart_VIsual_select = 0; #ifdef FEAT_EVAL static void set_vcount_ca(cmdarg_T *cap, int *set_prevcount); #endif static int nv_compare(const void *s1, const void *s2); -static void op_colon(oparg_T *oap); -static void op_function(oparg_T *oap); static void unshift_special(cmdarg_T *cap); -static void may_clear_cmdline(void); #ifdef FEAT_CMDL_INFO static void del_from_showcmd(int); #endif @@ -115,7 +105,6 @@ static void nv_wordcmd(cmdarg_T *cap); static void nv_beginline(cmdarg_T *cap); static void adjust_cursor(oparg_T *oap); static void adjust_for_sel(cmdarg_T *cap); -static int unadjust_for_sel(void); static void nv_select(cmdarg_T *cap); static void nv_goto(cmdarg_T *cap); static void nv_normal(cmdarg_T *cap); @@ -139,7 +128,6 @@ static void nv_nbcmd(cmdarg_T *cap); static void nv_drop(cmdarg_T *cap); #endif static void nv_cursorhold(cmdarg_T *cap); -static void get_op_vcol(oparg_T *oap, colnr_T col, int initial); static char *e_noident = N_("E349: No identifier under cursor"); @@ -1294,900 +1282,6 @@ set_vcount_ca(cmdarg_T *cap, int *set_prevcount) } #endif -/* - * Handle an operator after Visual mode or when the movement is finished. - * "gui_yank" is true when yanking text for the clipboard. - */ - void -do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank) -{ - oparg_T *oap = cap->oap; - pos_T old_cursor; - int empty_region_error; - int restart_edit_save; -#ifdef FEAT_LINEBREAK - int lbr_saved = curwin->w_p_lbr; -#endif - - /* The visual area is remembered for redo */ - static int redo_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */ - static linenr_T redo_VIsual_line_count; /* number of lines */ - static colnr_T redo_VIsual_vcol; /* number of cols or end column */ - static long redo_VIsual_count; /* count for Visual operator */ - static int redo_VIsual_arg; /* extra argument */ - int include_line_break = FALSE; - -#if defined(FEAT_CLIPBOARD) - /* - * Yank the visual area into the GUI selection register before we operate - * on it and lose it forever. - * Don't do it if a specific register was specified, so that ""x"*P works. - * This could call do_pending_operator() recursively, but that's OK - * because gui_yank will be TRUE for the nested call. - */ - if ((clip_star.available || clip_plus.available) - && oap->op_type != OP_NOP - && !gui_yank - && VIsual_active - && !redo_VIsual_busy - && oap->regname == 0) - clip_auto_select(); -#endif - old_cursor = curwin->w_cursor; - - /* - * If an operation is pending, handle it... - */ - if ((finish_op || VIsual_active) && oap->op_type != OP_NOP) - { - // Yank can be redone when 'y' is in 'cpoptions', but not when yanking - // for the clipboard. - int redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; - -#ifdef FEAT_LINEBREAK - /* Avoid a problem with unwanted linebreaks in block mode. */ - if (curwin->w_p_lbr) - curwin->w_valid &= ~VALID_VIRTCOL; - curwin->w_p_lbr = FALSE; -#endif - oap->is_VIsual = VIsual_active; - if (oap->motion_force == 'V') - oap->motion_type = MLINE; - else if (oap->motion_force == 'v') - { - /* If the motion was linewise, "inclusive" will not have been set. - * Use "exclusive" to be consistent. Makes "dvj" work nice. */ - if (oap->motion_type == MLINE) - oap->inclusive = FALSE; - /* If the motion already was characterwise, toggle "inclusive" */ - else if (oap->motion_type == MCHAR) - oap->inclusive = !oap->inclusive; - oap->motion_type = MCHAR; - } - else if (oap->motion_force == Ctrl_V) - { - /* Change line- or characterwise motion into Visual block mode. */ - if (!VIsual_active) - { - VIsual_active = TRUE; - VIsual = oap->start; - } - VIsual_mode = Ctrl_V; - VIsual_select = FALSE; - VIsual_reselect = FALSE; - } - - /* Only redo yank when 'y' flag is in 'cpoptions'. */ - /* Never redo "zf" (define fold). */ - if ((redo_yank || oap->op_type != OP_YANK) - && ((!VIsual_active || oap->motion_force) - /* Also redo Operator-pending Visual mode mappings */ - || (VIsual_active && cap->cmdchar == ':' - && oap->op_type != OP_COLON)) - && cap->cmdchar != 'D' -#ifdef FEAT_FOLDING - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC -#endif - ) - { - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - if (cap->cmdchar == '/' || cap->cmdchar == '?') /* was a search */ - { - /* - * If 'cpoptions' does not contain 'r', insert the search - * pattern to really repeat the same command. - */ - if (vim_strchr(p_cpo, CPO_REDO) == NULL) - AppendToRedobuffLit(cap->searchbuf, -1); - AppendToRedobuff(NL_STR); - } - else if (cap->cmdchar == ':') - { - /* do_cmdline() has stored the first typed line in - * "repeat_cmdline". When several lines are typed repeating - * won't be possible. */ - if (repeat_cmdline == NULL) - ResetRedobuff(); - else - { - AppendToRedobuffLit(repeat_cmdline, -1); - AppendToRedobuff(NL_STR); - VIM_CLEAR(repeat_cmdline); - } - } - } - - if (redo_VIsual_busy) - { - /* Redo of an operation on a Visual area. Use the same size from - * redo_VIsual_line_count and redo_VIsual_vcol. */ - oap->start = curwin->w_cursor; - curwin->w_cursor.lnum += redo_VIsual_line_count - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - VIsual_mode = redo_VIsual_mode; - if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') - { - if (VIsual_mode == 'v') - { - if (redo_VIsual_line_count <= 1) - { - validate_virtcol(); - curwin->w_curswant = - curwin->w_virtcol + redo_VIsual_vcol - 1; - } - else - curwin->w_curswant = redo_VIsual_vcol; - } - else - { - curwin->w_curswant = MAXCOL; - } - coladvance(curwin->w_curswant); - } - cap->count0 = redo_VIsual_count; - if (redo_VIsual_count != 0) - cap->count1 = redo_VIsual_count; - else - cap->count1 = 1; - } - else if (VIsual_active) - { - if (!gui_yank) - { - /* Save the current VIsual area for '< and '> marks, and "gv" */ - curbuf->b_visual.vi_start = VIsual; - curbuf->b_visual.vi_end = curwin->w_cursor; - curbuf->b_visual.vi_mode = VIsual_mode; - if (VIsual_mode_orig != NUL) - { - curbuf->b_visual.vi_mode = VIsual_mode_orig; - VIsual_mode_orig = NUL; - } - curbuf->b_visual.vi_curswant = curwin->w_curswant; -# ifdef FEAT_EVAL - curbuf->b_visual_mode_eval = VIsual_mode; -# endif - } - - /* In Select mode, a linewise selection is operated upon like a - * characterwise selection. - * Special case: gH deletes the last line. */ - if (VIsual_select && VIsual_mode == 'V' - && cap->oap->op_type != OP_DELETE) - { - if (LT_POS(VIsual, curwin->w_cursor)) - { - VIsual.col = 0; - curwin->w_cursor.col = - (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); - } - else - { - curwin->w_cursor.col = 0; - VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); - } - VIsual_mode = 'v'; - } - /* If 'selection' is "exclusive", backup one character for - * charwise selections. */ - else if (VIsual_mode == 'v') - include_line_break = unadjust_for_sel(); - - oap->start = VIsual; - if (VIsual_mode == 'V') - { - oap->start.col = 0; - oap->start.coladd = 0; - } - } - - /* - * Set oap->start to the first position of the operated text, oap->end - * to the end of the operated text. w_cursor is equal to oap->start. - */ - if (LT_POS(oap->start, curwin->w_cursor)) - { -#ifdef FEAT_FOLDING - /* Include folded lines completely. */ - if (!VIsual_active) - { - if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) - oap->start.col = 0; - if ((curwin->w_cursor.col > 0 || oap->inclusive) - && hasFolding(curwin->w_cursor.lnum, NULL, - &curwin->w_cursor.lnum)) - curwin->w_cursor.col = (colnr_T)STRLEN(ml_get_curline()); - } -#endif - oap->end = curwin->w_cursor; - curwin->w_cursor = oap->start; - - /* w_virtcol may have been updated; if the cursor goes back to its - * previous position w_virtcol becomes invalid and isn't updated - * automatically. */ - curwin->w_valid &= ~VALID_VIRTCOL; - } - else - { -#ifdef FEAT_FOLDING - /* Include folded lines completely. */ - if (!VIsual_active && oap->motion_type == MLINE) - { - if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, - NULL)) - curwin->w_cursor.col = 0; - if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) - oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); - } -#endif - oap->end = oap->start; - oap->start = curwin->w_cursor; - } - - /* Just in case lines were deleted that make the position invalid. */ - check_pos(curwin->w_buffer, &oap->end); - oap->line_count = oap->end.lnum - oap->start.lnum + 1; - - /* Set "virtual_op" before resetting VIsual_active. */ - virtual_op = virtual_active(); - - if (VIsual_active || redo_VIsual_busy) - { - get_op_vcol(oap, redo_VIsual_vcol, TRUE); - - if (!redo_VIsual_busy && !gui_yank) - { - /* - * Prepare to reselect and redo Visual: this is based on the - * size of the Visual text - */ - resel_VIsual_mode = VIsual_mode; - if (curwin->w_curswant == MAXCOL) - resel_VIsual_vcol = MAXCOL; - else - { - if (VIsual_mode != Ctrl_V) - getvvcol(curwin, &(oap->end), - NULL, NULL, &oap->end_vcol); - if (VIsual_mode == Ctrl_V || oap->line_count <= 1) - { - if (VIsual_mode != Ctrl_V) - getvvcol(curwin, &(oap->start), - &oap->start_vcol, NULL, NULL); - resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; - } - else - resel_VIsual_vcol = oap->end_vcol; - } - resel_VIsual_line_count = oap->line_count; - } - - /* can't redo yank (unless 'y' is in 'cpoptions') and ":" */ - if ((redo_yank || oap->op_type != OP_YANK) - && oap->op_type != OP_COLON -#ifdef FEAT_FOLDING - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC -#endif - && oap->motion_force == NUL - ) - { - /* Prepare for redoing. Only use the nchar field for "r", - * otherwise it might be the second char of the operator. */ - if (cap->cmdchar == 'g' && (cap->nchar == 'n' - || cap->nchar == 'N')) - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - else if (cap->cmdchar != ':') - { - int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; - - /* reverse what nv_replace() did */ - if (nchar == REPLACE_CR_NCHAR) - nchar = CAR; - else if (nchar == REPLACE_NL_NCHAR) - nchar = NL; - prep_redo(oap->regname, 0L, NUL, 'v', - get_op_char(oap->op_type), - get_extra_op_char(oap->op_type), - nchar); - } - if (!redo_VIsual_busy) - { - redo_VIsual_mode = resel_VIsual_mode; - redo_VIsual_vcol = resel_VIsual_vcol; - redo_VIsual_line_count = resel_VIsual_line_count; - redo_VIsual_count = cap->count0; - redo_VIsual_arg = cap->arg; - } - } - - /* - * oap->inclusive defaults to TRUE. - * If oap->end is on a NUL (empty line) oap->inclusive becomes - * FALSE. This makes "d}P" and "v}dP" work the same. - */ - if (oap->motion_force == NUL || oap->motion_type == MLINE) - oap->inclusive = TRUE; - if (VIsual_mode == 'V') - oap->motion_type = MLINE; - else - { - oap->motion_type = MCHAR; - if (VIsual_mode != Ctrl_V && *ml_get_pos(&(oap->end)) == NUL - && (include_line_break || !virtual_op)) - { - oap->inclusive = FALSE; - /* Try to include the newline, unless it's an operator - * that works on lines only. */ - if (*p_sel != 'o' - && !op_on_lines(oap->op_type) - && oap->end.lnum < curbuf->b_ml.ml_line_count) - { - ++oap->end.lnum; - oap->end.col = 0; - oap->end.coladd = 0; - ++oap->line_count; - } - } - } - - redo_VIsual_busy = FALSE; - - /* - * Switch Visual off now, so screen updating does - * not show inverted text when the screen is redrawn. - * With OP_YANK and sometimes with OP_COLON and OP_FILTER there is - * no screen redraw, so it is done here to remove the inverted - * part. - */ - if (!gui_yank) - { - VIsual_active = FALSE; -#ifdef FEAT_MOUSE - setmouse(); - mouse_dragging = 0; -#endif - may_clear_cmdline(); - if ((oap->op_type == OP_YANK - || oap->op_type == OP_COLON - || oap->op_type == OP_FUNCTION - || oap->op_type == OP_FILTER) - && oap->motion_force == NUL) - { -#ifdef FEAT_LINEBREAK - /* make sure redrawing is correct */ - curwin->w_p_lbr = lbr_saved; -#endif - redraw_curbuf_later(INVERTED); - } - } - } - - /* Include the trailing byte of a multi-byte char. */ - if (has_mbyte && oap->inclusive) - { - int l; - - l = (*mb_ptr2len)(ml_get_pos(&oap->end)); - if (l > 1) - oap->end.col += l - 1; - } - curwin->w_set_curswant = TRUE; - - /* - * oap->empty is set when start and end are the same. The inclusive - * flag affects this too, unless yanking and the end is on a NUL. - */ - oap->empty = (oap->motion_type == MCHAR - && (!oap->inclusive - || (oap->op_type == OP_YANK - && gchar_pos(&oap->end) == NUL)) - && EQUAL_POS(oap->start, oap->end) - && !(virtual_op && oap->start.coladd != oap->end.coladd)); - /* - * For delete, change and yank, it's an error to operate on an - * empty region, when 'E' included in 'cpoptions' (Vi compatible). - */ - empty_region_error = (oap->empty - && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); - - /* Force a redraw when operating on an empty Visual region, when - * 'modifiable is off or creating a fold. */ - if (oap->is_VIsual && (oap->empty || !curbuf->b_p_ma -#ifdef FEAT_FOLDING - || oap->op_type == OP_FOLD -#endif - )) - { -#ifdef FEAT_LINEBREAK - curwin->w_p_lbr = lbr_saved; -#endif - redraw_curbuf_later(INVERTED); - } - - /* - * If the end of an operator is in column one while oap->motion_type - * is MCHAR and oap->inclusive is FALSE, we put op_end after the last - * character in the previous line. If op_start is on or before the - * first non-blank in the line, the operator becomes linewise - * (strange, but that's the way vi does it). - */ - if ( oap->motion_type == MCHAR - && oap->inclusive == FALSE - && !(cap->retval & CA_NO_ADJ_OP_END) - && oap->end.col == 0 - && (!oap->is_VIsual || *p_sel == 'o') - && !oap->block_mode - && oap->line_count > 1) - { - oap->end_adjusted = TRUE; /* remember that we did this */ - --oap->line_count; - --oap->end.lnum; - if (inindent(0)) - oap->motion_type = MLINE; - else - { - oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (oap->end.col) - { - --oap->end.col; - oap->inclusive = TRUE; - } - } - } - else - oap->end_adjusted = FALSE; - - switch (oap->op_type) - { - case OP_LSHIFT: - case OP_RSHIFT: - op_shift(oap, TRUE, oap->is_VIsual ? (int)cap->count1 : 1); - auto_format(FALSE, TRUE); - break; - - case OP_JOIN_NS: - case OP_JOIN: - if (oap->line_count < 2) - oap->line_count = 2; - if (curwin->w_cursor.lnum + oap->line_count - 1 > - curbuf->b_ml.ml_line_count) - beep_flush(); - else - { - (void)do_join(oap->line_count, oap->op_type == OP_JOIN, - TRUE, TRUE, TRUE); - auto_format(FALSE, TRUE); - } - break; - - case OP_DELETE: - VIsual_reselect = FALSE; /* don't reselect now */ - if (empty_region_error) - { - vim_beep(BO_OPER); - CancelRedo(); - } - else - { - (void)op_delete(oap); - if (oap->motion_type == MLINE && has_format_option(FO_AUTO)) - u_save_cursor(); /* cursor line wasn't saved yet */ - auto_format(FALSE, TRUE); - } - break; - - case OP_YANK: - if (empty_region_error) - { - if (!gui_yank) - { - vim_beep(BO_OPER); - CancelRedo(); - } - } - else - { -#ifdef FEAT_LINEBREAK - curwin->w_p_lbr = lbr_saved; -#endif - (void)op_yank(oap, FALSE, !gui_yank); - } - check_cursor_col(); - break; - - case OP_CHANGE: - VIsual_reselect = FALSE; /* don't reselect now */ - if (empty_region_error) - { - vim_beep(BO_OPER); - CancelRedo(); - } - else - { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once and not when typed and - * 'insertmode' isn't set. */ - if (p_im || !KeyTyped) - restart_edit_save = restart_edit; - else - restart_edit_save = 0; - restart_edit = 0; -#ifdef FEAT_LINEBREAK - /* Restore linebreak, so that when the user edits it looks as - * before. */ - if (curwin->w_p_lbr != lbr_saved) - { - curwin->w_p_lbr = lbr_saved; - get_op_vcol(oap, redo_VIsual_mode, FALSE); - } -#endif - /* Reset finish_op now, don't want it set inside edit(). */ - finish_op = FALSE; - if (op_change(oap)) /* will call edit() */ - cap->retval |= CA_COMMAND_BUSY; - if (restart_edit == 0) - restart_edit = restart_edit_save; - } - break; - - case OP_FILTER: - if (vim_strchr(p_cpo, CPO_FILTER) != NULL) - AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */ - else - bangredo = TRUE; /* do_bang() will put cmd in redo buffer */ - /* FALLTHROUGH */ - - case OP_INDENT: - case OP_COLON: - -#if defined(FEAT_LISP) || defined(FEAT_CINDENT) - /* - * If 'equalprg' is empty, do the indenting internally. - */ - if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) - { -# ifdef FEAT_LISP - if (curbuf->b_p_lisp) - { - op_reindent(oap, get_lisp_indent); - break; - } -# endif -# ifdef FEAT_CINDENT - op_reindent(oap, -# ifdef FEAT_EVAL - *curbuf->b_p_inde != NUL ? get_expr_indent : -# endif - get_c_indent); - break; -# endif - } -#endif - - op_colon(oap); - break; - - case OP_TILDE: - case OP_UPPER: - case OP_LOWER: - case OP_ROT13: - if (empty_region_error) - { - vim_beep(BO_OPER); - CancelRedo(); - } - else - op_tilde(oap); - check_cursor_col(); - break; - - case OP_FORMAT: -#if defined(FEAT_EVAL) - if (*curbuf->b_p_fex != NUL) - op_formatexpr(oap); /* use expression */ - else -#endif - if (*p_fp != NUL || *curbuf->b_p_fp != NUL) - op_colon(oap); /* use external command */ - else - op_format(oap, FALSE); /* use internal function */ - break; - - case OP_FORMAT2: - op_format(oap, TRUE); /* use internal function */ - break; - - case OP_FUNCTION: -#ifdef FEAT_LINEBREAK - /* Restore linebreak, so that when the user edits it looks as - * before. */ - curwin->w_p_lbr = lbr_saved; -#endif - op_function(oap); /* call 'operatorfunc' */ - break; - - case OP_INSERT: - case OP_APPEND: - VIsual_reselect = FALSE; /* don't reselect now */ - if (empty_region_error) - { - vim_beep(BO_OPER); - CancelRedo(); - } - else - { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once. */ - restart_edit_save = restart_edit; - restart_edit = 0; -#ifdef FEAT_LINEBREAK - /* Restore linebreak, so that when the user edits it looks as - * before. */ - if (curwin->w_p_lbr != lbr_saved) - { - curwin->w_p_lbr = lbr_saved; - get_op_vcol(oap, redo_VIsual_mode, FALSE); - } -#endif - op_insert(oap, cap->count1); -#ifdef FEAT_LINEBREAK - /* Reset linebreak, so that formatting works correctly. */ - curwin->w_p_lbr = FALSE; -#endif - - /* TODO: when inserting in several lines, should format all - * the lines. */ - auto_format(FALSE, TRUE); - - if (restart_edit == 0) - restart_edit = restart_edit_save; - else - cap->retval |= CA_COMMAND_BUSY; - } - break; - - case OP_REPLACE: - VIsual_reselect = FALSE; /* don't reselect now */ - if (empty_region_error) - { - vim_beep(BO_OPER); - CancelRedo(); - } - else - { -#ifdef FEAT_LINEBREAK - /* Restore linebreak, so that when the user edits it looks as - * before. */ - if (curwin->w_p_lbr != lbr_saved) - { - curwin->w_p_lbr = lbr_saved; - get_op_vcol(oap, redo_VIsual_mode, FALSE); - } -#endif - op_replace(oap, cap->nchar); - } - break; - -#ifdef FEAT_FOLDING - case OP_FOLD: - VIsual_reselect = FALSE; /* don't reselect now */ - foldCreate(oap->start.lnum, oap->end.lnum); - break; - - case OP_FOLDOPEN: - case OP_FOLDOPENREC: - case OP_FOLDCLOSE: - case OP_FOLDCLOSEREC: - VIsual_reselect = FALSE; /* don't reselect now */ - opFoldRange(oap->start.lnum, oap->end.lnum, - oap->op_type == OP_FOLDOPEN - || oap->op_type == OP_FOLDOPENREC, - oap->op_type == OP_FOLDOPENREC - || oap->op_type == OP_FOLDCLOSEREC, - oap->is_VIsual); - break; - - case OP_FOLDDEL: - case OP_FOLDDELREC: - VIsual_reselect = FALSE; /* don't reselect now */ - deleteFold(oap->start.lnum, oap->end.lnum, - oap->op_type == OP_FOLDDELREC, oap->is_VIsual); - break; -#endif - case OP_NR_ADD: - case OP_NR_SUB: - if (empty_region_error) - { - vim_beep(BO_OPER); - CancelRedo(); - } - else - { - VIsual_active = TRUE; -#ifdef FEAT_LINEBREAK - curwin->w_p_lbr = lbr_saved; -#endif - op_addsub(oap, cap->count1, redo_VIsual_arg); - VIsual_active = FALSE; - } - check_cursor_col(); - break; - default: - clearopbeep(oap); - } - virtual_op = MAYBE; - if (!gui_yank) - { - /* - * if 'sol' not set, go back to old column for some commands - */ - if (!p_sol && oap->motion_type == MLINE && !oap->end_adjusted - && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT - || oap->op_type == OP_DELETE)) - { -#ifdef FEAT_LINEBREAK - curwin->w_p_lbr = FALSE; -#endif - coladvance(curwin->w_curswant = old_col); - } - } - else - { - curwin->w_cursor = old_cursor; - } - oap->block_mode = FALSE; - clearop(oap); - motion_force = NUL; - } -#ifdef FEAT_LINEBREAK - curwin->w_p_lbr = lbr_saved; -#endif -} - -/* - * Handle indent and format operators and visual mode ":". - */ - static void -op_colon(oparg_T *oap) -{ - stuffcharReadbuff(':'); - if (oap->is_VIsual) - stuffReadbuff((char_u *)"'<,'>"); - else - { - /* - * Make the range look nice, so it can be repeated. - */ - if (oap->start.lnum == curwin->w_cursor.lnum) - stuffcharReadbuff('.'); - else - stuffnumReadbuff((long)oap->start.lnum); - if (oap->end.lnum != oap->start.lnum) - { - stuffcharReadbuff(','); - if (oap->end.lnum == curwin->w_cursor.lnum) - stuffcharReadbuff('.'); - else if (oap->end.lnum == curbuf->b_ml.ml_line_count) - stuffcharReadbuff('$'); - else if (oap->start.lnum == curwin->w_cursor.lnum) - { - stuffReadbuff((char_u *)".+"); - stuffnumReadbuff((long)oap->line_count - 1); - } - else - stuffnumReadbuff((long)oap->end.lnum); - } - } - if (oap->op_type != OP_COLON) - stuffReadbuff((char_u *)"!"); - if (oap->op_type == OP_INDENT) - { -#ifndef FEAT_CINDENT - if (*get_equalprg() == NUL) - stuffReadbuff((char_u *)"indent"); - else -#endif - stuffReadbuff(get_equalprg()); - stuffReadbuff((char_u *)"\n"); - } - else if (oap->op_type == OP_FORMAT) - { - if (*curbuf->b_p_fp != NUL) - stuffReadbuff(curbuf->b_p_fp); - else if (*p_fp != NUL) - stuffReadbuff(p_fp); - else - stuffReadbuff((char_u *)"fmt"); - stuffReadbuff((char_u *)"\n']"); - } - - /* - * do_cmdline() does the rest - */ -} - -/* - * Handle the "g@" operator: call 'operatorfunc'. - */ - static void -op_function(oparg_T *oap UNUSED) -{ -#ifdef FEAT_EVAL - typval_T argv[2]; - int save_virtual_op = virtual_op; - - if (*p_opfunc == NUL) - emsg(_("E774: 'operatorfunc' is empty")); - else - { - /* Set '[ and '] marks to text to be operated on. */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - if (oap->motion_type != MLINE && !oap->inclusive) - /* Exclude the end position. */ - decl(&curbuf->b_op_end); - - argv[0].v_type = VAR_STRING; - if (oap->block_mode) - argv[0].vval.v_string = (char_u *)"block"; - else if (oap->motion_type == MLINE) - argv[0].vval.v_string = (char_u *)"line"; - else - argv[0].vval.v_string = (char_u *)"char"; - argv[1].v_type = VAR_UNKNOWN; - - /* Reset virtual_op so that 'virtualedit' can be changed in the - * function. */ - virtual_op = MAYBE; - - (void)call_func_retnr(p_opfunc, 1, argv); - - virtual_op = save_virtual_op; - } -#else - emsg(_("E775: Eval feature not available")); -#endif -} - /* * Check if highlighting for visual mode is possible, give a warning message * if not. @@ -2273,6 +1367,16 @@ reset_VIsual(void) } } + void +restore_visual_mode(void) +{ + if (VIsual_mode_orig != NUL) + { + curbuf->b_visual.vi_mode = VIsual_mode_orig; + VIsual_mode_orig = NUL; + } +} + /* * Check for a balloon-eval special item to include when searching for an * identifier. When "dir" is BACKWARD "ptr[-1]" must be valid! @@ -2611,7 +1715,7 @@ unshift_special(cmdarg_T *cap) * If the mode is currently displayed clear the command line or update the * command displayed. */ - static void + void may_clear_cmdline(void) { if (mode_displayed) @@ -7606,7 +6710,7 @@ adjust_for_sel(cmdarg_T *cap) * Should check VIsual_mode before calling this. * Returns TRUE when backed up to the previous line. */ - static int + int unadjust_for_sel(void) { pos_T *pp; @@ -8361,74 +7465,3 @@ nv_cursorhold(cmdarg_T *cap) did_cursorhold = TRUE; cap->retval |= CA_COMMAND_BUSY; /* don't call edit() now */ } - -/* - * Calculate start/end virtual columns for operating in block mode. - */ - static void -get_op_vcol( - oparg_T *oap, - colnr_T redo_VIsual_vcol, - int initial) /* when TRUE adjust position for 'selectmode' */ -{ - colnr_T start, end; - - if (VIsual_mode != Ctrl_V - || (!initial && oap->end.col < curwin->w_width)) - return; - - oap->block_mode = TRUE; - - /* prevent from moving onto a trail byte */ - if (has_mbyte) - mb_adjustpos(curwin->w_buffer, &oap->end); - - getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); - - if (!redo_VIsual_busy) - { - getvvcol(curwin, &(oap->end), &start, NULL, &end); - - if (start < oap->start_vcol) - oap->start_vcol = start; - if (end > oap->end_vcol) - { - if (initial && *p_sel == 'e' && start >= 1 - && start - 1 >= oap->end_vcol) - oap->end_vcol = start - 1; - else - oap->end_vcol = end; - } - } - - /* if '$' was used, get oap->end_vcol from longest line */ - if (curwin->w_curswant == MAXCOL) - { - curwin->w_cursor.col = MAXCOL; - oap->end_vcol = 0; - for (curwin->w_cursor.lnum = oap->start.lnum; - curwin->w_cursor.lnum <= oap->end.lnum; - ++curwin->w_cursor.lnum) - { - getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); - if (end > oap->end_vcol) - oap->end_vcol = end; - } - } - else if (redo_VIsual_busy) - oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; - /* - * Correct oap->end.col and oap->start.col to be the - * upper-left and lower-right corner of the block area. - * - * (Actually, this does convert column positions into character - * positions) - */ - curwin->w_cursor.lnum = oap->end.lnum; - coladvance(oap->end_vcol); - oap->end = curwin->w_cursor; - - curwin->w_cursor = oap->start; - coladvance(oap->start_vcol); - oap->start = curwin->w_cursor; -} diff --git a/src/ops.c b/src/ops.c index df765ef2f2..e82a66b132 100644 --- a/src/ops.c +++ b/src/ops.c @@ -96,7 +96,7 @@ get_op_type(int char1, int char2) /* * Return TRUE if operator "op" always works on whole lines. */ - int + static int op_on_lines(int op) { return opchars[op][2] & OPF_LINES; @@ -594,7 +594,7 @@ block_insert( /* * op_reindent - handle reindenting a block of lines. */ - void + static void op_reindent(oparg_T *oap, int (*how)(void)) { long i; @@ -1346,7 +1346,7 @@ static int swapchars(int op_type, pos_T *pos, int length); /* * Handle the (non-standard vi) tilde operator. Also for "gu", "gU" and "g?". */ - void + static void op_tilde(oparg_T *oap) { pos_T pos; @@ -2352,7 +2352,7 @@ same_leader( /* * Implementation of the format operator 'gq'. */ - void + static void op_format( oparg_T *oap, int keep_cursor) /* keep cursor on same text char */ @@ -2425,7 +2425,7 @@ op_format( /* * Implementation of the format operator 'gq' for when using 'formatexpr'. */ - void + static void op_formatexpr(oparg_T *oap) { if (oap->is_VIsual) @@ -3938,3 +3938,934 @@ cursor_pos_info(dict_T *dict) } #endif } + +/* + * Handle indent and format operators and visual mode ":". + */ + static void +op_colon(oparg_T *oap) +{ + stuffcharReadbuff(':'); + if (oap->is_VIsual) + stuffReadbuff((char_u *)"'<,'>"); + else + { + // Make the range look nice, so it can be repeated. + if (oap->start.lnum == curwin->w_cursor.lnum) + stuffcharReadbuff('.'); + else + stuffnumReadbuff((long)oap->start.lnum); + if (oap->end.lnum != oap->start.lnum) + { + stuffcharReadbuff(','); + if (oap->end.lnum == curwin->w_cursor.lnum) + stuffcharReadbuff('.'); + else if (oap->end.lnum == curbuf->b_ml.ml_line_count) + stuffcharReadbuff('$'); + else if (oap->start.lnum == curwin->w_cursor.lnum) + { + stuffReadbuff((char_u *)".+"); + stuffnumReadbuff((long)oap->line_count - 1); + } + else + stuffnumReadbuff((long)oap->end.lnum); + } + } + if (oap->op_type != OP_COLON) + stuffReadbuff((char_u *)"!"); + if (oap->op_type == OP_INDENT) + { +#ifndef FEAT_CINDENT + if (*get_equalprg() == NUL) + stuffReadbuff((char_u *)"indent"); + else +#endif + stuffReadbuff(get_equalprg()); + stuffReadbuff((char_u *)"\n"); + } + else if (oap->op_type == OP_FORMAT) + { + if (*curbuf->b_p_fp != NUL) + stuffReadbuff(curbuf->b_p_fp); + else if (*p_fp != NUL) + stuffReadbuff(p_fp); + else + stuffReadbuff((char_u *)"fmt"); + stuffReadbuff((char_u *)"\n']"); + } + + // do_cmdline() does the rest +} + +/* + * Handle the "g@" operator: call 'operatorfunc'. + */ + static void +op_function(oparg_T *oap UNUSED) +{ +#ifdef FEAT_EVAL + typval_T argv[2]; + int save_virtual_op = virtual_op; + + if (*p_opfunc == NUL) + emsg(_("E774: 'operatorfunc' is empty")); + else + { + // Set '[ and '] marks to text to be operated on. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + if (oap->motion_type != MLINE && !oap->inclusive) + // Exclude the end position. + decl(&curbuf->b_op_end); + + argv[0].v_type = VAR_STRING; + if (oap->block_mode) + argv[0].vval.v_string = (char_u *)"block"; + else if (oap->motion_type == MLINE) + argv[0].vval.v_string = (char_u *)"line"; + else + argv[0].vval.v_string = (char_u *)"char"; + argv[1].v_type = VAR_UNKNOWN; + + // Reset virtual_op so that 'virtualedit' can be changed in the + // function. + virtual_op = MAYBE; + + (void)call_func_retnr(p_opfunc, 1, argv); + + virtual_op = save_virtual_op; + } +#else + emsg(_("E775: Eval feature not available")); +#endif +} + +/* + * Calculate start/end virtual columns for operating in block mode. + */ + static void +get_op_vcol( + oparg_T *oap, + colnr_T redo_VIsual_vcol, + int initial) // when TRUE adjust position for 'selectmode' +{ + colnr_T start, end; + + if (VIsual_mode != Ctrl_V + || (!initial && oap->end.col < curwin->w_width)) + return; + + oap->block_mode = TRUE; + + // prevent from moving onto a trail byte + if (has_mbyte) + mb_adjustpos(curwin->w_buffer, &oap->end); + + getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); + + if (!redo_VIsual_busy) + { + getvvcol(curwin, &(oap->end), &start, NULL, &end); + + if (start < oap->start_vcol) + oap->start_vcol = start; + if (end > oap->end_vcol) + { + if (initial && *p_sel == 'e' && start >= 1 + && start - 1 >= oap->end_vcol) + oap->end_vcol = start - 1; + else + oap->end_vcol = end; + } + } + + // if '$' was used, get oap->end_vcol from longest line + if (curwin->w_curswant == MAXCOL) + { + curwin->w_cursor.col = MAXCOL; + oap->end_vcol = 0; + for (curwin->w_cursor.lnum = oap->start.lnum; + curwin->w_cursor.lnum <= oap->end.lnum; + ++curwin->w_cursor.lnum) + { + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); + if (end > oap->end_vcol) + oap->end_vcol = end; + } + } + else if (redo_VIsual_busy) + oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; + // Correct oap->end.col and oap->start.col to be the + // upper-left and lower-right corner of the block area. + // + // (Actually, this does convert column positions into character + // positions) + curwin->w_cursor.lnum = oap->end.lnum; + coladvance(oap->end_vcol); + oap->end = curwin->w_cursor; + + curwin->w_cursor = oap->start; + coladvance(oap->start_vcol); + oap->start = curwin->w_cursor; +} + +/* + * Handle an operator after Visual mode or when the movement is finished. + * "gui_yank" is true when yanking text for the clipboard. + */ + void +do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank) +{ + oparg_T *oap = cap->oap; + pos_T old_cursor; + int empty_region_error; + int restart_edit_save; +#ifdef FEAT_LINEBREAK + int lbr_saved = curwin->w_p_lbr; +#endif + + // The visual area is remembered for redo + static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V + static linenr_T redo_VIsual_line_count; // number of lines + static colnr_T redo_VIsual_vcol; // number of cols or end column + static long redo_VIsual_count; // count for Visual operator + static int redo_VIsual_arg; // extra argument + int include_line_break = FALSE; + +#if defined(FEAT_CLIPBOARD) + // Yank the visual area into the GUI selection register before we operate + // on it and lose it forever. + // Don't do it if a specific register was specified, so that ""x"*P works. + // This could call do_pending_operator() recursively, but that's OK + // because gui_yank will be TRUE for the nested call. + if ((clip_star.available || clip_plus.available) + && oap->op_type != OP_NOP + && !gui_yank + && VIsual_active + && !redo_VIsual_busy + && oap->regname == 0) + clip_auto_select(); +#endif + old_cursor = curwin->w_cursor; + + // If an operation is pending, handle it... + if ((finish_op || VIsual_active) && oap->op_type != OP_NOP) + { + // Yank can be redone when 'y' is in 'cpoptions', but not when yanking + // for the clipboard. + int redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; + +#ifdef FEAT_LINEBREAK + // Avoid a problem with unwanted linebreaks in block mode. + if (curwin->w_p_lbr) + curwin->w_valid &= ~VALID_VIRTCOL; + curwin->w_p_lbr = FALSE; +#endif + oap->is_VIsual = VIsual_active; + if (oap->motion_force == 'V') + oap->motion_type = MLINE; + else if (oap->motion_force == 'v') + { + // If the motion was linewise, "inclusive" will not have been set. + // Use "exclusive" to be consistent. Makes "dvj" work nice. + if (oap->motion_type == MLINE) + oap->inclusive = FALSE; + // If the motion already was characterwise, toggle "inclusive" + else if (oap->motion_type == MCHAR) + oap->inclusive = !oap->inclusive; + oap->motion_type = MCHAR; + } + else if (oap->motion_force == Ctrl_V) + { + // Change line- or characterwise motion into Visual block mode. + if (!VIsual_active) + { + VIsual_active = TRUE; + VIsual = oap->start; + } + VIsual_mode = Ctrl_V; + VIsual_select = FALSE; + VIsual_reselect = FALSE; + } + + // Only redo yank when 'y' flag is in 'cpoptions'. + // Never redo "zf" (define fold). + if ((redo_yank || oap->op_type != OP_YANK) + && ((!VIsual_active || oap->motion_force) + // Also redo Operator-pending Visual mode mappings + || (VIsual_active && cap->cmdchar == ':' + && oap->op_type != OP_COLON)) + && cap->cmdchar != 'D' +#ifdef FEAT_FOLDING + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC +#endif + ) + { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + if (cap->cmdchar == '/' || cap->cmdchar == '?') // was a search + { + // If 'cpoptions' does not contain 'r', insert the search + // pattern to really repeat the same command. + if (vim_strchr(p_cpo, CPO_REDO) == NULL) + AppendToRedobuffLit(cap->searchbuf, -1); + AppendToRedobuff(NL_STR); + } + else if (cap->cmdchar == ':') + { + // do_cmdline() has stored the first typed line in + // "repeat_cmdline". When several lines are typed repeating + // won't be possible. + if (repeat_cmdline == NULL) + ResetRedobuff(); + else + { + AppendToRedobuffLit(repeat_cmdline, -1); + AppendToRedobuff(NL_STR); + VIM_CLEAR(repeat_cmdline); + } + } + } + + if (redo_VIsual_busy) + { + // Redo of an operation on a Visual area. Use the same size from + // redo_VIsual_line_count and redo_VIsual_vcol. + oap->start = curwin->w_cursor; + curwin->w_cursor.lnum += redo_VIsual_line_count - 1; + if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + VIsual_mode = redo_VIsual_mode; + if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') + { + if (VIsual_mode == 'v') + { + if (redo_VIsual_line_count <= 1) + { + validate_virtcol(); + curwin->w_curswant = + curwin->w_virtcol + redo_VIsual_vcol - 1; + } + else + curwin->w_curswant = redo_VIsual_vcol; + } + else + { + curwin->w_curswant = MAXCOL; + } + coladvance(curwin->w_curswant); + } + cap->count0 = redo_VIsual_count; + if (redo_VIsual_count != 0) + cap->count1 = redo_VIsual_count; + else + cap->count1 = 1; + } + else if (VIsual_active) + { + if (!gui_yank) + { + // Save the current VIsual area for '< and '> marks, and "gv" + curbuf->b_visual.vi_start = VIsual; + curbuf->b_visual.vi_end = curwin->w_cursor; + curbuf->b_visual.vi_mode = VIsual_mode; + restore_visual_mode(); + curbuf->b_visual.vi_curswant = curwin->w_curswant; +# ifdef FEAT_EVAL + curbuf->b_visual_mode_eval = VIsual_mode; +# endif + } + + // In Select mode, a linewise selection is operated upon like a + // characterwise selection. + // Special case: gH deletes the last line. + if (VIsual_select && VIsual_mode == 'V' + && cap->oap->op_type != OP_DELETE) + { + if (LT_POS(VIsual, curwin->w_cursor)) + { + VIsual.col = 0; + curwin->w_cursor.col = + (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); + } + else + { + curwin->w_cursor.col = 0; + VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); + } + VIsual_mode = 'v'; + } + // If 'selection' is "exclusive", backup one character for + // charwise selections. + else if (VIsual_mode == 'v') + include_line_break = unadjust_for_sel(); + + oap->start = VIsual; + if (VIsual_mode == 'V') + { + oap->start.col = 0; + oap->start.coladd = 0; + } + } + + // Set oap->start to the first position of the operated text, oap->end + // to the end of the operated text. w_cursor is equal to oap->start. + if (LT_POS(oap->start, curwin->w_cursor)) + { +#ifdef FEAT_FOLDING + // Include folded lines completely. + if (!VIsual_active) + { + if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) + oap->start.col = 0; + if ((curwin->w_cursor.col > 0 || oap->inclusive) + && hasFolding(curwin->w_cursor.lnum, NULL, + &curwin->w_cursor.lnum)) + curwin->w_cursor.col = (colnr_T)STRLEN(ml_get_curline()); + } +#endif + oap->end = curwin->w_cursor; + curwin->w_cursor = oap->start; + + // w_virtcol may have been updated; if the cursor goes back to its + // previous position w_virtcol becomes invalid and isn't updated + // automatically. + curwin->w_valid &= ~VALID_VIRTCOL; + } + else + { +#ifdef FEAT_FOLDING + // Include folded lines completely. + if (!VIsual_active && oap->motion_type == MLINE) + { + if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, + NULL)) + curwin->w_cursor.col = 0; + if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) + oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); + } +#endif + oap->end = oap->start; + oap->start = curwin->w_cursor; + } + + // Just in case lines were deleted that make the position invalid. + check_pos(curwin->w_buffer, &oap->end); + oap->line_count = oap->end.lnum - oap->start.lnum + 1; + + // Set "virtual_op" before resetting VIsual_active. + virtual_op = virtual_active(); + + if (VIsual_active || redo_VIsual_busy) + { + get_op_vcol(oap, redo_VIsual_vcol, TRUE); + + if (!redo_VIsual_busy && !gui_yank) + { + // Prepare to reselect and redo Visual: this is based on the + // size of the Visual text + resel_VIsual_mode = VIsual_mode; + if (curwin->w_curswant == MAXCOL) + resel_VIsual_vcol = MAXCOL; + else + { + if (VIsual_mode != Ctrl_V) + getvvcol(curwin, &(oap->end), + NULL, NULL, &oap->end_vcol); + if (VIsual_mode == Ctrl_V || oap->line_count <= 1) + { + if (VIsual_mode != Ctrl_V) + getvvcol(curwin, &(oap->start), + &oap->start_vcol, NULL, NULL); + resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; + } + else + resel_VIsual_vcol = oap->end_vcol; + } + resel_VIsual_line_count = oap->line_count; + } + + // can't redo yank (unless 'y' is in 'cpoptions') and ":" + if ((redo_yank || oap->op_type != OP_YANK) + && oap->op_type != OP_COLON +#ifdef FEAT_FOLDING + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC +#endif + && oap->motion_force == NUL + ) + { + // Prepare for redoing. Only use the nchar field for "r", + // otherwise it might be the second char of the operator. + if (cap->cmdchar == 'g' && (cap->nchar == 'n' + || cap->nchar == 'N')) + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + else if (cap->cmdchar != ':') + { + int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; + + // reverse what nv_replace() did + if (nchar == REPLACE_CR_NCHAR) + nchar = CAR; + else if (nchar == REPLACE_NL_NCHAR) + nchar = NL; + prep_redo(oap->regname, 0L, NUL, 'v', + get_op_char(oap->op_type), + get_extra_op_char(oap->op_type), + nchar); + } + if (!redo_VIsual_busy) + { + redo_VIsual_mode = resel_VIsual_mode; + redo_VIsual_vcol = resel_VIsual_vcol; + redo_VIsual_line_count = resel_VIsual_line_count; + redo_VIsual_count = cap->count0; + redo_VIsual_arg = cap->arg; + } + } + + // oap->inclusive defaults to TRUE. + // If oap->end is on a NUL (empty line) oap->inclusive becomes + // FALSE. This makes "d}P" and "v}dP" work the same. + if (oap->motion_force == NUL || oap->motion_type == MLINE) + oap->inclusive = TRUE; + if (VIsual_mode == 'V') + oap->motion_type = MLINE; + else + { + oap->motion_type = MCHAR; + if (VIsual_mode != Ctrl_V && *ml_get_pos(&(oap->end)) == NUL + && (include_line_break || !virtual_op)) + { + oap->inclusive = FALSE; + // Try to include the newline, unless it's an operator + // that works on lines only. + if (*p_sel != 'o' + && !op_on_lines(oap->op_type) + && oap->end.lnum < curbuf->b_ml.ml_line_count) + { + ++oap->end.lnum; + oap->end.col = 0; + oap->end.coladd = 0; + ++oap->line_count; + } + } + } + + redo_VIsual_busy = FALSE; + + // Switch Visual off now, so screen updating does + // not show inverted text when the screen is redrawn. + // With OP_YANK and sometimes with OP_COLON and OP_FILTER there is + // no screen redraw, so it is done here to remove the inverted + // part. + if (!gui_yank) + { + VIsual_active = FALSE; +#ifdef FEAT_MOUSE + setmouse(); + mouse_dragging = 0; +#endif + may_clear_cmdline(); + if ((oap->op_type == OP_YANK + || oap->op_type == OP_COLON + || oap->op_type == OP_FUNCTION + || oap->op_type == OP_FILTER) + && oap->motion_force == NUL) + { +#ifdef FEAT_LINEBREAK + // make sure redrawing is correct + curwin->w_p_lbr = lbr_saved; +#endif + redraw_curbuf_later(INVERTED); + } + } + } + + // Include the trailing byte of a multi-byte char. + if (has_mbyte && oap->inclusive) + { + int l; + + l = (*mb_ptr2len)(ml_get_pos(&oap->end)); + if (l > 1) + oap->end.col += l - 1; + } + curwin->w_set_curswant = TRUE; + + // oap->empty is set when start and end are the same. The inclusive + // flag affects this too, unless yanking and the end is on a NUL. + oap->empty = (oap->motion_type == MCHAR + && (!oap->inclusive + || (oap->op_type == OP_YANK + && gchar_pos(&oap->end) == NUL)) + && EQUAL_POS(oap->start, oap->end) + && !(virtual_op && oap->start.coladd != oap->end.coladd)); + // For delete, change and yank, it's an error to operate on an + // empty region, when 'E' included in 'cpoptions' (Vi compatible). + empty_region_error = (oap->empty + && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); + + // Force a redraw when operating on an empty Visual region, when + // 'modifiable is off or creating a fold. + if (oap->is_VIsual && (oap->empty || !curbuf->b_p_ma +#ifdef FEAT_FOLDING + || oap->op_type == OP_FOLD +#endif + )) + { +#ifdef FEAT_LINEBREAK + curwin->w_p_lbr = lbr_saved; +#endif + redraw_curbuf_later(INVERTED); + } + + // If the end of an operator is in column one while oap->motion_type + // is MCHAR and oap->inclusive is FALSE, we put op_end after the last + // character in the previous line. If op_start is on or before the + // first non-blank in the line, the operator becomes linewise + // (strange, but that's the way vi does it). + if ( oap->motion_type == MCHAR + && oap->inclusive == FALSE + && !(cap->retval & CA_NO_ADJ_OP_END) + && oap->end.col == 0 + && (!oap->is_VIsual || *p_sel == 'o') + && !oap->block_mode + && oap->line_count > 1) + { + oap->end_adjusted = TRUE; // remember that we did this + --oap->line_count; + --oap->end.lnum; + if (inindent(0)) + oap->motion_type = MLINE; + else + { + oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); + if (oap->end.col) + { + --oap->end.col; + oap->inclusive = TRUE; + } + } + } + else + oap->end_adjusted = FALSE; + + switch (oap->op_type) + { + case OP_LSHIFT: + case OP_RSHIFT: + op_shift(oap, TRUE, oap->is_VIsual ? (int)cap->count1 : 1); + auto_format(FALSE, TRUE); + break; + + case OP_JOIN_NS: + case OP_JOIN: + if (oap->line_count < 2) + oap->line_count = 2; + if (curwin->w_cursor.lnum + oap->line_count - 1 > + curbuf->b_ml.ml_line_count) + beep_flush(); + else + { + (void)do_join(oap->line_count, oap->op_type == OP_JOIN, + TRUE, TRUE, TRUE); + auto_format(FALSE, TRUE); + } + break; + + case OP_DELETE: + VIsual_reselect = FALSE; // don't reselect now + if (empty_region_error) + { + vim_beep(BO_OPER); + CancelRedo(); + } + else + { + (void)op_delete(oap); + if (oap->motion_type == MLINE && has_format_option(FO_AUTO)) + u_save_cursor(); // cursor line wasn't saved yet + auto_format(FALSE, TRUE); + } + break; + + case OP_YANK: + if (empty_region_error) + { + if (!gui_yank) + { + vim_beep(BO_OPER); + CancelRedo(); + } + } + else + { +#ifdef FEAT_LINEBREAK + curwin->w_p_lbr = lbr_saved; +#endif + (void)op_yank(oap, FALSE, !gui_yank); + } + check_cursor_col(); + break; + + case OP_CHANGE: + VIsual_reselect = FALSE; // don't reselect now + if (empty_region_error) + { + vim_beep(BO_OPER); + CancelRedo(); + } + else + { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once and not when typed and + // 'insertmode' isn't set. + if (p_im || !KeyTyped) + restart_edit_save = restart_edit; + else + restart_edit_save = 0; + restart_edit = 0; +#ifdef FEAT_LINEBREAK + // Restore linebreak, so that when the user edits it looks as + // before. + if (curwin->w_p_lbr != lbr_saved) + { + curwin->w_p_lbr = lbr_saved; + get_op_vcol(oap, redo_VIsual_mode, FALSE); + } +#endif + // Reset finish_op now, don't want it set inside edit(). + finish_op = FALSE; + if (op_change(oap)) // will call edit() + cap->retval |= CA_COMMAND_BUSY; + if (restart_edit == 0) + restart_edit = restart_edit_save; + } + break; + + case OP_FILTER: + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) + AppendToRedobuff((char_u *)"!\r"); // use any last used !cmd + else + bangredo = TRUE; // do_bang() will put cmd in redo buffer + // FALLTHROUGH + + case OP_INDENT: + case OP_COLON: + +#if defined(FEAT_LISP) || defined(FEAT_CINDENT) + // If 'equalprg' is empty, do the indenting internally. + if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) + { +# ifdef FEAT_LISP + if (curbuf->b_p_lisp) + { + op_reindent(oap, get_lisp_indent); + break; + } +# endif +# ifdef FEAT_CINDENT + op_reindent(oap, +# ifdef FEAT_EVAL + *curbuf->b_p_inde != NUL ? get_expr_indent : +# endif + get_c_indent); + break; +# endif + } +#endif + + op_colon(oap); + break; + + case OP_TILDE: + case OP_UPPER: + case OP_LOWER: + case OP_ROT13: + if (empty_region_error) + { + vim_beep(BO_OPER); + CancelRedo(); + } + else + op_tilde(oap); + check_cursor_col(); + break; + + case OP_FORMAT: +#if defined(FEAT_EVAL) + if (*curbuf->b_p_fex != NUL) + op_formatexpr(oap); // use expression + else +#endif + if (*p_fp != NUL || *curbuf->b_p_fp != NUL) + op_colon(oap); // use external command + else + op_format(oap, FALSE); // use internal function + break; + + case OP_FORMAT2: + op_format(oap, TRUE); // use internal function + break; + + case OP_FUNCTION: +#ifdef FEAT_LINEBREAK + // Restore linebreak, so that when the user edits it looks as + // before. + curwin->w_p_lbr = lbr_saved; +#endif + op_function(oap); // call 'operatorfunc' + break; + + case OP_INSERT: + case OP_APPEND: + VIsual_reselect = FALSE; // don't reselect now + if (empty_region_error) + { + vim_beep(BO_OPER); + CancelRedo(); + } + else + { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once. + restart_edit_save = restart_edit; + restart_edit = 0; +#ifdef FEAT_LINEBREAK + // Restore linebreak, so that when the user edits it looks as + // before. + if (curwin->w_p_lbr != lbr_saved) + { + curwin->w_p_lbr = lbr_saved; + get_op_vcol(oap, redo_VIsual_mode, FALSE); + } +#endif + op_insert(oap, cap->count1); +#ifdef FEAT_LINEBREAK + // Reset linebreak, so that formatting works correctly. + curwin->w_p_lbr = FALSE; +#endif + + // TODO: when inserting in several lines, should format all + // the lines. + auto_format(FALSE, TRUE); + + if (restart_edit == 0) + restart_edit = restart_edit_save; + else + cap->retval |= CA_COMMAND_BUSY; + } + break; + + case OP_REPLACE: + VIsual_reselect = FALSE; // don't reselect now + if (empty_region_error) + { + vim_beep(BO_OPER); + CancelRedo(); + } + else + { +#ifdef FEAT_LINEBREAK + // Restore linebreak, so that when the user edits it looks as + // before. + if (curwin->w_p_lbr != lbr_saved) + { + curwin->w_p_lbr = lbr_saved; + get_op_vcol(oap, redo_VIsual_mode, FALSE); + } +#endif + op_replace(oap, cap->nchar); + } + break; + +#ifdef FEAT_FOLDING + case OP_FOLD: + VIsual_reselect = FALSE; // don't reselect now + foldCreate(oap->start.lnum, oap->end.lnum); + break; + + case OP_FOLDOPEN: + case OP_FOLDOPENREC: + case OP_FOLDCLOSE: + case OP_FOLDCLOSEREC: + VIsual_reselect = FALSE; // don't reselect now + opFoldRange(oap->start.lnum, oap->end.lnum, + oap->op_type == OP_FOLDOPEN + || oap->op_type == OP_FOLDOPENREC, + oap->op_type == OP_FOLDOPENREC + || oap->op_type == OP_FOLDCLOSEREC, + oap->is_VIsual); + break; + + case OP_FOLDDEL: + case OP_FOLDDELREC: + VIsual_reselect = FALSE; // don't reselect now + deleteFold(oap->start.lnum, oap->end.lnum, + oap->op_type == OP_FOLDDELREC, oap->is_VIsual); + break; +#endif + case OP_NR_ADD: + case OP_NR_SUB: + if (empty_region_error) + { + vim_beep(BO_OPER); + CancelRedo(); + } + else + { + VIsual_active = TRUE; +#ifdef FEAT_LINEBREAK + curwin->w_p_lbr = lbr_saved; +#endif + op_addsub(oap, cap->count1, redo_VIsual_arg); + VIsual_active = FALSE; + } + check_cursor_col(); + break; + default: + clearopbeep(oap); + } + virtual_op = MAYBE; + if (!gui_yank) + { + // if 'sol' not set, go back to old column for some commands + if (!p_sol && oap->motion_type == MLINE && !oap->end_adjusted + && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT + || oap->op_type == OP_DELETE)) + { +#ifdef FEAT_LINEBREAK + curwin->w_p_lbr = FALSE; +#endif + coladvance(curwin->w_curswant = old_col); + } + } + else + { + curwin->w_cursor = old_cursor; + } + oap->block_mode = FALSE; + clearop(oap); + motion_force = NUL; + } +#ifdef FEAT_LINEBREAK + curwin->w_p_lbr = lbr_saved; +#endif +} diff --git a/src/proto/normal.pro b/src/proto/normal.pro index 3a0a6e0365..a1e31b341d 100644 --- a/src/proto/normal.pro +++ b/src/proto/normal.pro @@ -1,16 +1,17 @@ /* normal.c */ void init_normal_cmds(void); void normal_cmd(oparg_T *oap, int toplevel); -void do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank); void check_visual_highlight(void); void end_visual_mode(void); void reset_VIsual_and_resel(void); void reset_VIsual(void); +void restore_visual_mode(void); int find_ident_under_cursor(char_u **text, int find_type); int find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **text, int *textcol, int find_type); -void prep_redo(int regname, long, int, int, int, int, int); +void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5); void clearop(oparg_T *oap); void clearopbeep(oparg_T *oap); +void may_clear_cmdline(void); void clear_showcmd(void); int add_to_showcmd(int c); void add_to_showcmd_c(int c); @@ -26,5 +27,6 @@ void do_nv_ident(int c1, int c2); int get_visual_text(cmdarg_T *cap, char_u **pp, int *lenp); void start_selection(void); void may_start_select(int c); +int unadjust_for_sel(void); void set_cursor_for_append_to_line(void); /* vim: set ft=c : */ diff --git a/src/proto/ops.pro b/src/proto/ops.pro index d8e8ad5f48..f4e7de7a26 100644 --- a/src/proto/ops.pro +++ b/src/proto/ops.pro @@ -1,16 +1,13 @@ /* ops.c */ int get_op_type(int char1, int char2); -int op_on_lines(int op); int op_is_change(int op); int get_op_char(int optype); int get_extra_op_char(int optype); void op_shift(oparg_T *oap, int curs_top, int amount); void shift_line(int left, int round, int amount, int call_changed_bytes); -void op_reindent(oparg_T *oap, int (*how)(void)); void stuffescaped(char_u *arg, int literally); int op_delete(oparg_T *oap); int op_replace(oparg_T *oap, int c); -void op_tilde(oparg_T *oap); int swapchar(int op_type, pos_T *pos); void op_insert(oparg_T *oap, long count1); int op_change(oparg_T *oap); @@ -18,8 +15,6 @@ void adjust_cursor_eol(void); int preprocs_left(void); char_u *skip_comment(char_u *line, int process, int include_space, int *is_comment); int do_join(long count, int insert_space, int save_undo, int use_formatoptions, int setmark); -void op_format(oparg_T *oap, int keep_cursor); -void op_formatexpr(oparg_T *oap); int fex_format(linenr_T lnum, long count, int c); void format_lines(linenr_T line_count, int avoid_fex); int paragraph_start(linenr_T lnum); @@ -28,4 +23,5 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, int g_cmd); void x11_export_final_selection(void); void clear_oparg(oparg_T *oap); void cursor_pos_info(dict_T *dict); +void do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank); /* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c index fe9cee3a09..29273b544f 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2104, /**/ 2103, /**/ From 2886dccebaec2da55e5a99bd88d44ae4217dee6e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 1 Oct 2019 12:10:25 +0200 Subject: [PATCH 03/67] patch 8.1.2105: MS-Windows: system() may crash Problem: MS-Windows: system() may crash. Solution: Do not use "itmp" when it is NULL. (Yasuhiro Matsumoto, closes #5005) --- src/ex_cmds.c | 42 ++++++++++++++++++++++-------------------- src/version.c | 2 ++ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 9d58ab41a5..85e5be066d 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -1778,28 +1778,30 @@ make_filter_cmd( } else { - char_u *p; + STRCPY(buf, cmd); + if (itmp != NULL) + { + char_u *p; - /* - * If there is a pipe, we have to put the '<' in front of it. - * Don't do this when 'shellquote' is not empty, otherwise the - * redirection would be inside the quotes. - */ - if (*p_shq == NUL) - { - p = find_pipe(buf); - if (p != NULL) - *p = NUL; - } - STRCAT(buf, " <"); /* " < " causes problems on Amiga */ - STRCAT(buf, itmp); - if (*p_shq == NUL) - { - p = find_pipe(cmd); - if (p != NULL) + // If there is a pipe, we have to put the '<' in front of it. + // Don't do this when 'shellquote' is not empty, otherwise the + // redirection would be inside the quotes. + if (*p_shq == NUL) { - STRCAT(buf, " "); /* insert a space before the '|' for DOS */ - STRCAT(buf, p); + p = find_pipe(buf); + if (p != NULL) + *p = NUL; + } + STRCAT(buf, " <"); // " < " causes problems on Amiga + STRCAT(buf, itmp); + if (*p_shq == NUL) + { + p = find_pipe(cmd); + if (p != NULL) + { + STRCAT(buf, " "); // insert a space before the '|' for DOS + STRCAT(buf, p); + } } } } diff --git a/src/version.c b/src/version.c index 29273b544f..229312bb28 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2105, /**/ 2104, /**/ From b4367b7fb65f6a88f76ef99f79342341af0b1017 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 1 Oct 2019 14:19:07 +0200 Subject: [PATCH 04/67] patch 8.1.2106: no tests for dragging the mouse beyond the window Problem: No tests for dragging the mouse beyond the window. Solution: Add a test. (Dominique Pelle, closes #5004) --- src/testdir/test_termcodes.vim | 91 +++++++++++++++++++++++++++++++++- src/version.c | 2 + 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index e8b36f40ea..96bbdbb44f 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -166,12 +166,12 @@ func Test_xterm_mouse_ctrl_click() call MouseCtrlLeftClick(row, col) call MouseLeftRelease(row, col) call assert_match('usr_02.txt$', bufname('%'), msg) - call assert_equal('*usr_02.txt*', expand('')) + call assert_equal('*usr_02.txt*', expand(''), msg) call MouseCtrlRightClick(row, col) call MouseRightRelease(row, col) call assert_match('help.txt$', bufname('%'), msg) - call assert_equal('|usr_02.txt|', expand('')) + call assert_equal('|usr_02.txt|', expand(''), msg) helpclose endfor @@ -267,6 +267,93 @@ func Test_1xterm_mouse_wheel() bwipe! endfunc +" Test that dragging beyond the window (at the bottom and at the top) +" scrolls window content by the number of of lines beyond the window. +func Test_term_mouse_drag_beyond_window() + let save_mouse = &mouse + let save_term = &term + let save_ttymouse = &ttymouse + call test_override('no_query_mouse', 1) + set mouse=a term=xterm + let col = 1 + call setline(1, range(1, 100)) + + " Split into 3 windows, and go into the middle window + " so we test dragging mouse below and above the window. + 2split + wincmd j + 2split + + for ttymouse_val in s:ttymouse_values + s:ttymouse_dec + let msg = 'ttymouse=' .. ttymouse_val + exe 'set ttymouse=' .. ttymouse_val + + " Line #10 at the top. + norm! 10zt + redraw + call assert_equal(10, winsaveview().topline, msg) + call assert_equal(2, winheight(0), msg) + + let row = 4 + call MouseLeftClick(row, col) + call assert_equal(10, winsaveview().topline, msg) + + " Drag downwards. We're still in the window so topline should + " not change yet. + let row += 1 + call MouseLeftDrag(row, col) + call assert_equal(10, winsaveview().topline, msg) + + " We now leave the window at the bottom, so the window content should + " scroll by 1 line, then 2 lines (etc) as we drag further away. + let row += 1 + call MouseLeftDrag(row, col) + call assert_equal(11, winsaveview().topline, msg) + + let row += 1 + call MouseLeftDrag(row, col) + call assert_equal(13, winsaveview().topline, msg) + + " Now drag upwards. + let row -= 1 + call MouseLeftDrag(row, col) + call assert_equal(14, winsaveview().topline, msg) + + " We're now back in the window so the topline should not change. + let row -= 1 + call MouseLeftDrag(row, col) + call assert_equal(14, winsaveview().topline, msg) + + let row -= 1 + call MouseLeftDrag(row, col) + call assert_equal(14, winsaveview().topline, msg) + + " We now leave the window at the top so the window content should + " scroll by 1 line, then 2, then 3 (etc) in the opposite direction. + let row -= 1 + call MouseLeftDrag(row, col) + call assert_equal(13, winsaveview().topline, msg) + + let row -= 1 + call MouseLeftDrag(row, col) + call assert_equal(11, winsaveview().topline, msg) + + let row -= 1 + call MouseLeftDrag(row, col) + call assert_equal(8, winsaveview().topline, msg) + + call MouseLeftRelease(row, col) + call assert_equal(8, winsaveview().topline, msg) + call assert_equal(2, winheight(0), msg) + endfor + + let &mouse = save_mouse + let &term = save_term + let &ttymouse = save_ttymouse + call test_override('no_query_mouse', 0) + bwipe! +endfunc + func Test_term_mouse_drag_window_separator() let save_mouse = &mouse let save_term = &term diff --git a/src/version.c b/src/version.c index 229312bb28..cfd70c8aed 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2106, /**/ 2105, /**/ From 8617348e2110c2c8387ea448a6258f1effa8d249 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 1 Oct 2019 17:02:16 +0200 Subject: [PATCH 05/67] patch 8.1.2107: various memory leaks reported by asan Problem: Various memory leaks reported by asan. Solution: Free the memory. (Ozaki Kiichi, closes #5003) --- src/buffer.c | 2 ++ src/change.c | 20 +++++++++++++++++++- src/eval.c | 12 ++++++++++-- src/evalfunc.c | 3 +-- src/option.c | 2 +- src/popupwin.c | 1 + src/proto/change.pro | 1 + src/scriptfile.c | 3 +++ src/terminal.c | 3 ++- src/testdir/test_method.vim | 4 ++++ src/version.c | 2 ++ 11 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index aa1770a94b..86f6ffcedb 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -880,6 +880,7 @@ free_buffer(buf_T *buf) /* b:changedtick uses an item in buf_T, remove it now */ dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di); unref_var_dict(buf->b_vars); + remove_listeners(buf); #endif #ifdef FEAT_LUA lua_buffer_free(buf); @@ -908,6 +909,7 @@ free_buffer(buf_T *buf) #ifdef FEAT_JOB_CHANNEL vim_free(buf->b_prompt_text); free_callback(&buf->b_prompt_callback); + free_callback(&buf->b_prompt_interrupt); #endif buf_hashtab_remove(buf); diff --git a/src/change.c b/src/change.c index 6b402f189b..0eb00c2836 100644 --- a/src/change.c +++ b/src/change.c @@ -300,7 +300,7 @@ f_listener_remove(typval_T *argvars, typval_T *rettv) int id = tv_get_number(argvars); buf_T *buf; - for (buf = firstbuf; buf != NULL; buf = buf->b_next) + FOR_ALL_BUFFERS(buf) { prev = NULL; for (lnr = buf->b_listener; lnr != NULL; lnr = next) @@ -402,6 +402,24 @@ invoke_listeners(buf_T *buf) after_updating_screen(TRUE); recursive = FALSE; } + +/* + * Remove all listeners associated with "buf". + */ + void +remove_listeners(buf_T *buf) +{ + listener_T *lnr; + listener_T *next; + + for (lnr = buf->b_listener; lnr != NULL; lnr = next) + { + next = lnr->lr_next; + free_callback(&lnr->lr_callback); + vim_free(lnr); + } + buf->b_listener = NULL; +} #endif /* diff --git a/src/eval.c b/src/eval.c index 680ca1af7b..954e07fb4a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2914,9 +2914,17 @@ eval_lambda( semsg(_(e_missingparen), "lambda"); } clear_tv(rettv); - return FAIL; + ret = FAIL; } - return call_func_rettv(arg, rettv, evaluate, NULL, &base); + else + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base); + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) + clear_tv(&base); + + return ret; } /* diff --git a/src/evalfunc.c b/src/evalfunc.c index 159b874bf4..26d8691ccf 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2213,8 +2213,7 @@ f_expand(typval_T *argvars, typval_T *rettv) { if (rettv_list_alloc(rettv) != FAIL && result != NULL) list_append_string(rettv->vval.v_list, result, -1); - else - vim_free(result); + vim_free(result); } else rettv->vval.v_string = result; diff --git a/src/option.c b/src/option.c index 51e75c3301..3d408143e9 100644 --- a/src/option.c +++ b/src/option.c @@ -686,7 +686,7 @@ set_local_options_default(win_T *wp, int do_buffer) && (do_buffer || (p->indir & PV_BUF) == 0) && !(options[i].flags & P_NODEFAULT) && !optval_default(p, varp, FALSE)) - set_option_default(i, OPT_LOCAL, FALSE); + set_option_default(i, OPT_FREE|OPT_LOCAL, FALSE); } unblock_autocmds(); diff --git a/src/popupwin.c b/src/popupwin.c index d4b3d7c338..a77d98b673 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -3365,6 +3365,7 @@ update_popups(void (*win_update)(win_T *wp)) trunc_string(wp->w_popup_title, title, total_width - 2, len); screen_puts(title, wp->w_winrow, wp->w_wincol + 1, wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr); + vim_free(title); } // Compute scrollbar thumb position and size. diff --git a/src/proto/change.pro b/src/proto/change.pro index f6b3599eba..c31c442a50 100644 --- a/src/proto/change.pro +++ b/src/proto/change.pro @@ -7,6 +7,7 @@ void f_listener_flush(typval_T *argvars, typval_T *rettv); void f_listener_remove(typval_T *argvars, typval_T *rettv); void may_invoke_listeners(buf_T *buf, linenr_T lnum, linenr_T lnume, int added); void invoke_listeners(buf_T *buf); +void remove_listeners(buf_T *buf); void changed_bytes(linenr_T lnum, colnr_T col); void appended_lines(linenr_T lnum, long count); void appended_lines_mark(linenr_T lnum, long count); diff --git a/src/scriptfile.c b/src/scriptfile.c index 92db59c2de..611be04c85 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -1358,7 +1358,10 @@ free_scriptnames(void) int i; for (i = script_items.ga_len; i > 0; --i) + { vim_free(SCRIPT_ITEM(i).sn_name); + ga_clear(&SCRIPT_ITEM(i).sn_prl_ga); + } ga_clear(&script_items); } diff --git a/src/terminal.c b/src/terminal.c index 4f2b8bb25c..52487a3cf3 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -4602,6 +4602,7 @@ read_dump_file(FILE *fd, VTermPos *cursor_pos) } ga_clear(&ga_text); + ga_clear(&ga_cell); vim_free(prev_char); return max_cells; @@ -4733,7 +4734,7 @@ term_load_dump(typval_T *argvars, typval_T *rettv, int do_diff) buf = curbuf; while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) ml_delete((linenr_T)1, FALSE); - ga_clear(&curbuf->b_term->tl_scrollback); + free_scrollback(curbuf->b_term); redraw_later(NOT_VALID); } } diff --git a/src/testdir/test_method.vim b/src/testdir/test_method.vim index 69adc30c06..b82dbb4948 100644 --- a/src/testdir/test_method.vim +++ b/src/testdir/test_method.vim @@ -140,6 +140,10 @@ func Test_method_lambda() " todo: lambda accepts more arguments than it consumes " call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:') + + let l = [1, 2, 3] + eval l->{x -> x}() + call assert_equal(1, test_refcount(l)) endfunc func Test_method_not_supported() diff --git a/src/version.c b/src/version.c index cfd70c8aed..e2f0c92008 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2107, /**/ 2106, /**/ From 23324a0b35d18c5caac20b1d543ed2d1f762f5b5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 1 Oct 2019 17:39:04 +0200 Subject: [PATCH 06/67] patch 8.1.2108: cannot close the cmdline window from CmdWinEnter Problem: Cannot close the cmdline window from CmdWinEnter. (George Brown) Solution: Reset cmdwin_result earlier. (Christian Brabandt, closes #4980) --- src/ex_getln.c | 12 +++++++----- src/testdir/test_autocmd.vim | 29 +++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/ex_getln.c b/src/ex_getln.c index ef8e387b70..ef87413b7c 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -136,11 +136,11 @@ restore_viewstate(viewstate_T *vs) // Struct to store the state of 'incsearch' highlighting. typedef struct { pos_T search_start; // where 'incsearch' starts searching - pos_T save_cursor; + pos_T save_cursor; viewstate_T init_viewstate; viewstate_T old_viewstate; - pos_T match_start; - pos_T match_end; + pos_T match_start; + pos_T match_end; int did_incsearch; int incsearch_postponed; int magic_save; @@ -4152,12 +4152,15 @@ open_cmdwin(void) invalidate_botline(); redraw_later(SOME_VALID); - /* No Ex mode here! */ + // No Ex mode here! exmode_active = 0; State = NORMAL; setmouse(); + // Reset here so it can be set by a CmdWinEnter autocommand. + cmdwin_result = 0; + // Trigger CmdwinEnter autocommands. trigger_cmd_autocmd(cmdwin_type, EVENT_CMDWINENTER); if (restart_edit != 0) // autocmd with ":startinsert" @@ -4169,7 +4172,6 @@ open_cmdwin(void) /* * Call the main loop until or CTRL-C is typed. */ - cmdwin_result = 0; main_loop(TRUE, FALSE); RedrawingDisabled = i; diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index f2c9af50f8..fe77bf5786 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -2259,3 +2259,32 @@ func Test_autocmd_SafeState() call StopVimInTerminal(buf) call delete('XSafeState') endfunc + +func Test_autocmd_CmdWinEnter() + CheckRunVimInTerminal + " There is not cmdwin switch, so + " test for cmdline_hist + " (both are available with small builds) + CheckFeature cmdline_hist + let lines =<< trim END + let b:dummy_var = 'This is a dummy' + autocmd CmdWinEnter * quit + let winnr = winnr('$') + END + let filename='XCmdWinEnter' + call writefile(lines, filename) + let buf = RunVimInTerminal('-S '.filename, #{rows: 6}) + + call term_sendkeys(buf, "q:") + call term_wait(buf) + call term_sendkeys(buf, ":echo b:dummy_var\") + call WaitForAssert({-> assert_match('^This is a dummy', term_getline(buf, 6))}, 1000) + call term_sendkeys(buf, ":echo &buftype\") + call WaitForAssert({-> assert_notmatch('^nofile', term_getline(buf, 6))}, 1000) + call term_sendkeys(buf, ":echo winnr\") + call WaitForAssert({-> assert_match('^1', term_getline(buf, 6))}, 1000) + + " clean up + call StopVimInTerminal(buf) + call delete(filename) +endfunc diff --git a/src/version.c b/src/version.c index e2f0c92008..469b759b67 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2108, /**/ 2107, /**/ From 1824f45883ef7cc236d2bba89811989a1ccb853c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 2 Oct 2019 23:06:46 +0200 Subject: [PATCH 07/67] patch 8.1.2109: popup_getoptions() hangs with tab-local popup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: popup_getoptions() hangs with tab-local popup. Solution: Correct pointer name. (Marko Mahnič, closes #5006) --- src/popupwin.c | 8 ++++---- src/testdir/test_popupwin.vim | 12 ++++++++++++ src/version.c | 2 ++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/popupwin.c b/src/popupwin.c index a77d98b673..06812073e6 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -2636,12 +2636,12 @@ f_popup_getoptions(typval_T *argvars, typval_T *rettv) i = 1; FOR_ALL_TABPAGES(tp) { - win_T *p; + win_T *twp; - for (p = tp->tp_first_popupwin; p != NULL; p = wp->w_next) - if (p->w_id == id) + for (twp = tp->tp_first_popupwin; twp != NULL; twp = twp->w_next) + if (twp->w_id == id) break; - if (p != NULL) + if (twp != NULL) break; ++i; } diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index b9d6d83019..9d8a0d099b 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -2556,4 +2556,16 @@ func Test_popupwin_recycle_bnr() call popup_clear() endfunc +func Test_popupwin_getoptions_tablocal() + topleft split + let win1 = popup_create('nothing', #{maxheight: 8}) + let win2 = popup_create('something', #{maxheight: 10}) + let win3 = popup_create('something', #{maxheight: 15}) + call assert_equal(8, popup_getoptions(win1).maxheight) + call assert_equal(10, popup_getoptions(win2).maxheight) + call assert_equal(15, popup_getoptions(win3).maxheight) + call popup_clear() + quit +endfunc + " vim: shiftwidth=2 sts=2 diff --git a/src/version.c b/src/version.c index 469b759b67..00374c20e5 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2109, /**/ 2108, /**/ From e8a7dfedfc8ea5c376c7912cb27a9405e4b8c972 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 3 Oct 2019 22:35:52 +0200 Subject: [PATCH 08/67] patch 8.1.2110: CTRL-C closes two popups instead of one Problem: CTRL-C closes two popups instead of one. Solution: Reset got_int when the filter consumed the key. --- src/getchar.c | 4 ++++ src/testdir/test_popupwin.vim | 22 ++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 28 insertions(+) diff --git a/src/getchar.c b/src/getchar.c index fcdc639688..5e098dfcf8 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -1791,7 +1791,11 @@ vgetc(void) #endif #ifdef FEAT_TEXT_PROP if (popup_do_filter(c)) + { + if (c == Ctrl_C) + got_int = FALSE; // avoid looping c = K_IGNORE; + } #endif // Need to process the character before we know it's safe to do something diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 9d8a0d099b..771db9a69e 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -2568,4 +2568,26 @@ func Test_popupwin_getoptions_tablocal() quit endfunc +func Test_popupwin_cancel() + let win1 = popup_create('one', #{line: 5, filter: {... -> 0}}) + let win2 = popup_create('two', #{line: 10, filter: {... -> 0}}) + let win3 = popup_create('three', #{line: 15, filter: {... -> 0}}) + call assert_equal(5, popup_getpos(win1).line) + call assert_equal(10, popup_getpos(win2).line) + call assert_equal(15, popup_getpos(win3).line) + " TODO: this also works without patch 8.1.2110 + call feedkeys("\", 'xt') + call assert_equal(5, popup_getpos(win1).line) + call assert_equal(10, popup_getpos(win2).line) + call assert_equal({}, popup_getpos(win3)) + call feedkeys("\", 'xt') + call assert_equal(5, popup_getpos(win1).line) + call assert_equal({}, popup_getpos(win2)) + call assert_equal({}, popup_getpos(win3)) + call feedkeys("\", 'xt') + call assert_equal({}, popup_getpos(win1)) + call assert_equal({}, popup_getpos(win2)) + call assert_equal({}, popup_getpos(win3)) +endfunc + " vim: shiftwidth=2 sts=2 diff --git a/src/version.c b/src/version.c index 00374c20e5..ee1525d889 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2110, /**/ 2109, /**/ From 2a8d3b8997d4fe94bc9c02ae04e873eab2f13b09 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 4 Oct 2019 21:20:25 +0200 Subject: [PATCH 09/67] patch 8.1.2111: viminfo file not sufficiently tested Problem: Viminfo file not sufficiently tested. Solution: Add more tests. (Yegappan Lakshmanan, closes #5009) --- src/testdir/test_viminfo.vim | 194 +++++++++++++++++++++++++++++++++++ src/version.c | 2 + 2 files changed, 196 insertions(+) diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim index 048ebacd30..6e3116271e 100644 --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -40,6 +40,9 @@ function Test_viminfo_read_and_write() endfunc func Test_global_vars() + let g:MY_GLOBAL_STRING = "Vim Editor" + let g:MY_GLOBAL_NUM = 345 + let g:MY_GLOBAL_FLOAT = 3.14 let test_dict = {'foo': 1, 'bar': 0, 'longvarible': 1000} let g:MY_GLOBAL_DICT = test_dict " store a really long list, so line wrapping will occur in viminfo file @@ -59,6 +62,9 @@ func Test_global_vars() set viminfo='100,<50,s10,h,!,nviminfo wv! Xviminfo + unlet g:MY_GLOBAL_STRING + unlet g:MY_GLOBAL_NUM + unlet g:MY_GLOBAL_FLOAT unlet g:MY_GLOBAL_DICT unlet g:MY_GLOBAL_LIST unlet g:MY_GLOBAL_BLOB @@ -68,6 +74,9 @@ func Test_global_vars() unlet g:MY_GLOBAL_NONE rv! Xviminfo + call assert_equal("Vim Editor", g:MY_GLOBAL_STRING) + call assert_equal(345, g:MY_GLOBAL_NUM) + call assert_equal(3.14, g:MY_GLOBAL_FLOAT) call assert_equal(test_dict, g:MY_GLOBAL_DICT) call assert_equal(test_list, g:MY_GLOBAL_LIST) call assert_equal(test_blob, g:MY_GLOBAL_BLOB) @@ -221,6 +230,7 @@ func Test_viminfo_registers() call add(l, 'something') endfor call setreg('d', l, 'l') + call setreg('e', "abc\xyz") wviminfo Xviminfo call test_settime(10) @@ -231,6 +241,7 @@ func Test_viminfo_registers() call setreg('c', 'keep', 'l') call test_settime(30) call setreg('d', 'drop', 'l') + call setreg('e', 'drop') rviminfo Xviminfo call assert_equal("", getreg('a')) @@ -241,6 +252,7 @@ func Test_viminfo_registers() call assert_equal("V", getregtype('c')) call assert_equal(l, getreg('d', 1, 1)) call assert_equal("V", getregtype('d')) + call assert_equal("abc\xyz", getreg('e')) " Length around 440 switches to line continuation. let len = 434 @@ -514,12 +526,41 @@ func Test_viminfo_file_mark_zero_time() delmark B endfunc +" Test for saving and restoring file marks in unloaded buffers +func Test_viminfo_file_mark_unloaded_buf() + let save_viminfo = &viminfo + set viminfo&vim + call writefile(repeat(['vim'], 10), 'Xfile1') + %bwipe + edit! Xfile1 + call setpos("'u", [0, 3, 1, 0]) + call setpos("'v", [0, 5, 1, 0]) + enew + wviminfo Xviminfo + %bwipe + edit Xfile1 + rviminfo! Xviminfo + call assert_equal([0, 3, 1, 0], getpos("'u")) + call assert_equal([0, 5, 1, 0], getpos("'v")) + %bwipe + call delete('Xfile1') + call delete('Xviminfo') + let &viminfo = save_viminfo +endfunc + func Test_viminfo_oldfiles() let v:oldfiles = [] let lines = [ \ '# comment line', \ '*encoding=utf-8', \ '', + \ ':h viminfo', + \ '?/session', + \ '=myvar', + \ '@123', + \ '', + \ "'E 2 0 /tmp/nothing", + \ '', \ "> /tmp/file_one.txt", \ "\t\"\t11\t0", \ "", @@ -531,9 +572,15 @@ func Test_viminfo_oldfiles() \ "", \ ] call writefile(lines, 'Xviminfo') + delmark E rviminfo! Xviminfo call delete('Xviminfo') + call assert_equal('h viminfo', histget(':')) + call assert_equal('session', histget('/')) + call assert_equal('myvar', histget('=')) + call assert_equal('123', histget('@')) + call assert_equal(2, line("'E")) call assert_equal(['1: /tmp/file_one.txt', '2: /tmp/file_two.txt', '3: /tmp/another.txt'], filter(split(execute('oldfiles'), "\n"), {i, v -> v =~ '/tmp/'})) call assert_equal(['1: /tmp/file_one.txt', '2: /tmp/file_two.txt'], filter(split(execute('filter file_ oldfiles'), "\n"), {i, v -> v =~ '/tmp/'})) call assert_equal(['3: /tmp/another.txt'], filter(split(execute('filter /another/ oldfiles'), "\n"), {i, v -> v =~ '/tmp/'})) @@ -543,4 +590,151 @@ func Test_viminfo_oldfiles() browse oldfiles call assert_equal("/tmp/another.txt", expand("%")) bwipe + delmark E +endfunc + +" Test for storing and restoring buffer list in 'viminfo' +func Test_viminfo_bufferlist() + " If there are arguments, then :rviminfo doesn't read the buffer list. + " Need to delete all the arguments for :rviminfo to work. + %argdelete + + edit Xfile1 + edit Xfile2 + set viminfo-=% + wviminfo Xviminfo + %bwipe + rviminfo Xviminfo + call assert_equal(1, len(getbufinfo())) + + edit Xfile1 + edit Xfile2 + set viminfo^=% + wviminfo Xviminfo + %bwipe + rviminfo Xviminfo + let l = getbufinfo() + call assert_equal(3, len(l)) + call assert_equal('Xfile1', bufname(l[1].bufnr)) + call assert_equal('Xfile2', bufname(l[2].bufnr)) + + call delete('Xviminfo') + %bwipe + set viminfo-=% +endfunc + +" Test for errors in a viminfo file +func Test_viminfo_error() + " Non-existing viminfo files + call assert_fails('rviminfo xyz', 'E195:') + + " Illegal starting character + call writefile(["a 123"], 'Xviminfo') + call assert_fails('rv Xviminfo', 'E575:') + + " Illegal register name in the viminfo file + call writefile(['"@ LINE 0'], 'Xviminfo') + call assert_fails('rv Xviminfo', 'E577:') + + " Invalid file mark line + call writefile(['>', '@'], 'Xviminfo') + call assert_fails('rv Xviminfo', 'E576:') + + " Too many errors in viminfo file + call writefile(repeat(["a 123"], 15), 'Xviminfo') + call assert_fails('rv Xviminfo', 'E136:') + + call writefile(['>'] + repeat(['@'], 10), 'Xviminfo') + call assert_fails('rv Xviminfo', 'E136:') + + call writefile(repeat(['"@'], 15), 'Xviminfo') + call assert_fails('rv Xviminfo', 'E136:') + + call delete('Xviminfo') +endfunc + +" Test for saving and restoring last substitute string in viminfo +func Test_viminfo_lastsub() + enew + call append(0, "blue blue blue") + call cursor(1, 1) + s/blue/green/ + wviminfo Xviminfo + s/blue/yellow/ + rviminfo! Xviminfo + & + call assert_equal("green yellow green", getline(1)) + enew! + call delete('Xviminfo') +endfunc + +" Test saving and restoring the register values using the older method +func Test_viminfo_registers_old() + let lines = [ + \ '# Viminfo version', + \ '|1,1', + \ '', + \ '*encoding=utf-8', + \ '', + \ '# Registers:', + \ '""0 CHAR 0', + \ ' Vim', + \ '"a CHAR 0', + \ ' red', + \ '"m@ CHAR 0', + \ " :echo 'Hello'\", + \ "", + \ ] + call writefile(lines, 'Xviminfo') + let @a = 'one' + let @b = 'two' + let @m = 'three' + let @" = 'four' + let @t = ":echo 'Unix'\" + silent! normal @t + rviminfo! Xviminfo + call assert_equal('red', getreg('a')) + call assert_equal('two', getreg('b')) + call assert_equal(":echo 'Hello'\", getreg('m')) + call assert_equal('Vim', getreg('"')) + call assert_equal("\nHello", execute('normal @@')) + call delete('Xviminfo') + let @" = '' +endfunc + +" Test for saving and restoring large number of lines in a register +func Test_viminfo_large_register() + let save_viminfo = &viminfo + set viminfo&vim + set viminfo-=<50 + set viminfo+=<200 + let lines = ['"r CHAR 0'] + call extend(lines, repeat(["\tsun is rising"], 200)) + call writefile(lines, 'Xviminfo') + let @r = '' + rviminfo! Xviminfo + call assert_equal(join(repeat(["sun is rising"], 200), "\n"), @r) + call delete('Xviminfo') + let &viminfo = save_viminfo +endfunc + +" Test for setting 'viminfofile' to NONE +func Test_viminfofile_none() + set viminfofile=NONE + wviminfo Xviminfo + call assert_false(filereadable('Xviminfo')) + call writefile([''], 'Xviminfo') + call assert_fails('rviminfo Xviminfo', 'E195:') + call delete('Xviminfo') +endfunc + +" Test for an unwritable 'viminfo' file +func Test_viminfo_readonly() + if !has('unix') + return + endif + call writefile([''], 'Xviminfo') + call setfperm('Xviminfo', 'r-x------') + call assert_fails('wviminfo Xviminfo', 'E137:') + call delete('Xviminfo') endfunc diff --git a/src/version.c b/src/version.c index ee1525d889..f77ecc6673 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2111, /**/ 2110, /**/ From 4c063dde73c618b0728016d221ef130d9e9ec968 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 4 Oct 2019 21:29:12 +0200 Subject: [PATCH 10/67] patch 8.1.2112: build number for ConPTY is outdated Problem: Build number for ConPTY is outdated. Solution: Update to new build number. (Nobuhiro Takasaki, closes #5014) --- src/os_win32.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/os_win32.c b/src/os_win32.c index 790f75efd7..26005b24fc 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -7277,7 +7277,7 @@ mch_setenv(char *var, char *value, int x UNUSED) * Confirm until this version. Also the logic changes. * insider preview. */ -#define CONPTY_INSIDER_BUILD MAKE_VER(10, 0, 18990) +#define CONPTY_INSIDER_BUILD MAKE_VER(10, 0, 18995) /* * Not stable now. diff --git a/src/version.c b/src/version.c index f77ecc6673..eb1ad1fc26 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2112, /**/ 2111, /**/ From 9ca250855b55f4d3292b010525c827dc6992cb61 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 5 Oct 2019 11:30:09 +0200 Subject: [PATCH 11/67] patch 8.1.2113: ":help expr-!~?" only works after searching Problem: ":help expr-!~?" only works after searching. Solution: Escape "~" after "expr-". (closes #5015) --- src/ex_cmds.c | 16 +++++++++++++--- src/testdir/test_help.vim | 6 ++++++ src/version.c | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 85e5be066d..da01e9ddad 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -5547,12 +5547,22 @@ find_help_tags( if (STRNICMP(arg, "expr-", 5) == 0) { // When the string starting with "expr-" and containing '?' and matches - // the table, it is taken literally. Otherwise '?' is recognized as a - // wildcard. + // the table, it is taken literally (but ~ is escaped). Otherwise '?' + // is recognized as a wildcard. for (i = (int)(sizeof(expr_table) / sizeof(char *)); --i >= 0; ) if (STRCMP(arg + 5, expr_table[i]) == 0) { - STRCPY(d, arg); + int si = 0, di = 0; + + for (;;) + { + if (arg[si] == '~') + d[di++] = '\\'; + d[di++] = arg[si]; + if (arg[si] == NUL) + break; + ++si; + } break; } } diff --git a/src/testdir/test_help.vim b/src/testdir/test_help.vim index c550ff09e2..5dd937a935 100644 --- a/src/testdir/test_help.vim +++ b/src/testdir/test_help.vim @@ -20,6 +20,12 @@ func Test_help_errors() bwipe! endfunc +func Test_help_expr() + help expr-!~? + call assert_equal('eval.txt', expand('%:t')) + close +endfunc + func Test_help_keyword() new set keywordprg=:help diff --git a/src/version.c b/src/version.c index eb1ad1fc26..d37ee657d0 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2113, /**/ 2112, /**/ From fd00c042afc40539447e798aadbd0a2219fdbdc1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 5 Oct 2019 11:56:54 +0200 Subject: [PATCH 12/67] patch 8.1.2114: when a popup is closed with CTRL-C the callback aborts Problem: When a popup is closed with CTRL-C the callback aborts. Solution: Reset got_int when invoking the callback. (closes #5008) --- src/popupwin.c | 5 +++++ src/version.c | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/popupwin.c b/src/popupwin.c index 06812073e6..6b0b383b08 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -2763,7 +2763,12 @@ invoke_popup_filter(win_T *wp, int c) // Emergency exit: CTRL-C closes the popup. if (c == Ctrl_C) { + int save_got_int = got_int; + + // Reset got_int to avoid the callback isn't called. + got_int = FALSE; popup_close_with_retval(wp, -1); + got_int |= save_got_int; return 1; } diff --git a/src/version.c b/src/version.c index d37ee657d0..0673d7e787 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2114, /**/ 2113, /**/ From 2efc44b3f0b6bd8307cb281af095e08e15ab1c24 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 5 Oct 2019 12:09:32 +0200 Subject: [PATCH 13/67] patch 8.1.2115: MS-Windows: shell commands fail if &shell contains a space Problem: MS-Windows: shell commands fail if &shell contains a space. Solution: Use quotes instead of escaping. (closes #4920) --- src/option.c | 19 ++++++++++++++ src/os_win32.c | 19 +++++++++++++- src/testdir/test_startup.vim | 12 ++++++--- src/testdir/test_system.vim | 51 ++++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vimrun.c | 24 ++++++++++++++++- 6 files changed, 122 insertions(+), 5 deletions(-) diff --git a/src/option.c b/src/option.c index 3d408143e9..75ff3cdb29 100644 --- a/src/option.c +++ b/src/option.c @@ -102,7 +102,26 @@ set_init_1(int clean_arg) || ((p = (char_u *)default_shell()) != NULL && *p != NUL) #endif ) +#if defined(MSWIN) + { + // For MS-Windows put the path in quotes instead of escaping spaces. + char_u *cmd; + size_t len; + + if (vim_strchr(p, ' ') != NULL) + { + len = STRLEN(p) + 3; // two quotes and a trailing NUL + cmd = alloc(len); + vim_snprintf((char *)cmd, len, "\"%s\"", p); + set_string_default("sh", cmd); + vim_free(cmd); + } + else + set_string_default("sh", p); + } +#else set_string_default_esc("sh", p, TRUE); +#endif #ifdef FEAT_WILDIGN /* diff --git a/src/os_win32.c b/src/os_win32.c index 26005b24fc..ce035570db 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -4490,8 +4490,25 @@ mch_system_c(char *cmd, int options UNUSED) { int ret; WCHAR *wcmd; + char_u *buf; + size_t len; + + // If the command starts and ends with double quotes, enclose the command + // in parentheses. + len = STRLEN(cmd); + if (len >= 2 && cmd[0] == '"' && cmd[len - 1] == '"') + { + len += 3; + buf = alloc(len); + if (buf == NULL) + return -1; + vim_snprintf((char *)buf, len, "(%s)", cmd); + wcmd = enc_to_utf16(buf, NULL); + free(buf); + } + else + wcmd = enc_to_utf16((char_u *)cmd, NULL); - wcmd = enc_to_utf16((char_u *)cmd, NULL); if (wcmd == NULL) return -1; diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index ceee044ca3..f10eabb798 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -574,11 +574,17 @@ func Test_set_shell() quit! [CODE] - let $SHELL = '/bin/with space/sh' + if has('win32') + let $SHELL = 'C:\with space\cmd.exe' + let expected = '"C:\with space\cmd.exe"' + else + let $SHELL = '/bin/with space/sh' + let expected = '/bin/with\ space/sh' + endif + if RunVimPiped([], after, '', '') let lines = readfile('Xtestout') - " MS-Windows adds a space after the word - call assert_equal('/bin/with\ space/sh', lines[0]) + call assert_equal(expected, lines[0]) endif call delete('Xtestout') endfunc diff --git a/src/testdir/test_system.vim b/src/testdir/test_system.vim index dfe3683723..eabccfb371 100644 --- a/src/testdir/test_system.vim +++ b/src/testdir/test_system.vim @@ -1,6 +1,7 @@ " Tests for system() and systemlist() source shared.vim +source check.vim func Test_System() if !has('win32') @@ -112,3 +113,53 @@ func Test_system_exmode() let a = system(GetVimCommand() . cmd) call assert_notequal(0, v:shell_error) endfunc + +func Test_system_with_shell_quote() + CheckMSWindows + + call mkdir('Xdir with spaces', 'p') + call system('copy "%COMSPEC%" "Xdir with spaces\cmd.exe"') + + let shell_save = &shell + let shellxquote_save = &shellxquote + try + " Set 'shell' always needs noshellslash. + let shellslash_save = &shellslash + set noshellslash + let shell_tests = [ + \ expand('$COMSPEC'), + \ '"' . fnamemodify('Xdir with spaces\cmd.exe', ':p') . '"', + \] + let &shellslash = shellslash_save + + let sxq_tests = ['', '(', '"'] + + " Matrix tests: 'shell' * 'shellxquote' + for shell in shell_tests + let &shell = shell + for sxq in sxq_tests + let &shellxquote = sxq + + let msg = printf('shell=%s shellxquote=%s', &shell, &shellxquote) + + try + let out = 'echo 123'->system() + catch + call assert_report(printf('%s: %s', msg, v:exception)) + continue + endtry + + " On Windows we may get a trailing space and CR. + if out != "123 \n" + call assert_equal("123\n", out, msg) + endif + + endfor + endfor + + finally + let &shell = shell_save + let &shellxquote = shellxquote_save + call delete('Xdir with spaces', 'rf') + endtry +endfunc diff --git a/src/version.c b/src/version.c index 0673d7e787..b1bccb4f6b 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2115, /**/ 2114, /**/ diff --git a/src/vimrun.c b/src/vimrun.c index ece20f8392..26c4aa4c64 100644 --- a/src/vimrun.c +++ b/src/vimrun.c @@ -27,6 +27,8 @@ main(void) { const wchar_t *p; + wchar_t *cmd; + size_t cmdlen; int retval; int inquote = 0; int silent = 0; @@ -63,16 +65,36 @@ main(void) ++p; } - /* Print the command, including quotes and redirection. */ + // Print the command, including quotes and redirection. hstdout = GetStdHandle(STD_OUTPUT_HANDLE); WriteConsoleW(hstdout, p, wcslen(p), &written, NULL); WriteConsoleW(hstdout, L"\r\n", 2, &written, NULL); + // If the command starts and ends with double quotes, + // Enclose the command in parentheses. + cmd = NULL; + cmdlen = wcslen(p); + if (cmdlen >= 2 && p[0] == L'"' && p[cmdlen - 1] == L'"') + { + cmdlen += 3; + cmd = (wchar_t *)malloc(cmdlen * sizeof(wchar_t)); + if (cmd == NULL) + { + perror("vimrun malloc(): "); + return -1; + } + _snwprintf(cmd, cmdlen, L"(%s)", p); + p = cmd; + } + /* * Do it! */ retval = _wsystem(p); + if (cmd) + free(cmd); + if (retval == -1) perror("vimrun system(): "); else if (retval != 0) From 1671de3098b7ab663398dd694b314e7f67a93411 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 5 Oct 2019 21:35:16 +0200 Subject: [PATCH 14/67] patch 8.1.2116: no check for out of memory Problem: No check for out of memory. Solution: Check for NULL pointer. --- src/option.c | 9 ++++++--- src/version.c | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/option.c b/src/option.c index 75ff3cdb29..60c1141b6a 100644 --- a/src/option.c +++ b/src/option.c @@ -112,9 +112,12 @@ set_init_1(int clean_arg) { len = STRLEN(p) + 3; // two quotes and a trailing NUL cmd = alloc(len); - vim_snprintf((char *)cmd, len, "\"%s\"", p); - set_string_default("sh", cmd); - vim_free(cmd); + if (cmd != NULL) + { + vim_snprintf((char *)cmd, len, "\"%s\"", p); + set_string_default("sh", cmd); + vim_free(cmd); + } } else set_string_default("sh", p); diff --git a/src/version.c b/src/version.c index b1bccb4f6b..11bfd68446 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2116, /**/ 2115, /**/ From 49474ca12236776bb56aeb9d39bd6592e28157c7 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 5 Oct 2019 21:57:12 +0200 Subject: [PATCH 15/67] patch 8.1.2117: CursorLine highlight used while 'cursorline' is off Problem: CursorLine highlight used while 'cursorline' is off. Solution: Check 'cursorline' is set. (cloes #5017) --- src/drawline.c | 6 +++--- src/testdir/test_cursorline.vim | 2 +- src/version.c | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/drawline.c b/src/drawline.c index 43604c8262..ea7764ea62 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1113,11 +1113,11 @@ win_line( // the line number itself. // TODO: Can we use CursorLine instead of CursorLineNr // when CursorLineNr isn't set? - if ((wp->w_p_cul || wp->w_p_rnu) + if (wp->w_p_cul + && lnum == wp->w_cursor.lnum && (wp->w_p_culopt_flags & CULOPT_NBR) && (row == startrow - || wp->w_p_culopt_flags & CULOPT_LINE) - && lnum == wp->w_cursor.lnum) + || wp->w_p_culopt_flags & CULOPT_LINE)) char_attr = hl_combine_attr(wcr_attr, HL_ATTR(HLF_CLN)); #endif } diff --git a/src/testdir/test_cursorline.vim b/src/testdir/test_cursorline.vim index 49df94f906..d4a03afd38 100644 --- a/src/testdir/test_cursorline.vim +++ b/src/testdir/test_cursorline.vim @@ -52,7 +52,7 @@ func Test_cursorline_highlight1() setl nocursorline relativenumber redraw let attr31 = s:screen_attr(1) - call assert_equal(attr21[0:3], attr31[0:3]) + call assert_equal(attr22[0:3], attr31[0:3]) call assert_equal(attr11[4:7], attr31[4:7]) call s:close_windows() diff --git a/src/version.c b/src/version.c index 11bfd68446..fa4930b7cc 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2117, /**/ 2116, /**/ From eb66328bd78c3001d71138306325718cb1c94712 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 6 Oct 2019 12:02:15 +0200 Subject: [PATCH 16/67] patch 8.1.2118: termcodes test fails when $TERM is "dumb" Problem: Termcodes test fails when $TERM is "dumb". Solution: Skip the test. (James McCoy, closes #5019) --- src/testdir/test_termcodes.vim | 6 +++++- src/version.c | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index 96bbdbb44f..6174723173 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -798,7 +798,11 @@ func Test_xx_term_style_response() endfunc func Test_get_termcode() - let k1 = &t_k1 + try + let k1 = &t_k1 + catch /E113/ + throw 'Skipped: Unable to query termcodes' + endtry set t_k1= set t_k1& call assert_equal(k1, &t_k1) diff --git a/src/version.c b/src/version.c index fa4930b7cc..0fa8440e35 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2118, /**/ 2117, /**/ From 524f3b19ae16e08350010b5effe38d0637349285 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 6 Oct 2019 20:08:38 +0200 Subject: [PATCH 17/67] patch 8.1.2119: memory access error for empty string Problem: memory access error for empty string when 'encoding' is a single byte encoding. Solution: Check for empty string when getting the length. (Dominique Pelle, closes #5021, closes #5007) --- src/macros.h | 6 +++--- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/macros.h b/src/macros.h index d685deafb9..5e047c873f 100644 --- a/src/macros.h +++ b/src/macros.h @@ -230,11 +230,11 @@ * PTR2CHAR(): get character from pointer. */ /* Get the length of the character p points to, including composing chars */ -#define MB_PTR2LEN(p) (has_mbyte ? (*mb_ptr2len)(p) : 1) +#define MB_PTR2LEN(p) (has_mbyte ? (*mb_ptr2len)(p) : (*p == NUL ? 0 : 1)) /* Advance multi-byte pointer, skip over composing chars. */ -#define MB_PTR_ADV(p) p += has_mbyte ? (*mb_ptr2len)(p) : 1 +#define MB_PTR_ADV(p) p += has_mbyte ? (*mb_ptr2len)(p) : (*p == NUL ? 0 : 1) /* Advance multi-byte pointer, do not skip over composing chars. */ -#define MB_CPTR_ADV(p) p += enc_utf8 ? utf_ptr2len(p) : has_mbyte ? (*mb_ptr2len)(p) : 1 +#define MB_CPTR_ADV(p) p += enc_utf8 ? utf_ptr2len(p) : has_mbyte ? (*mb_ptr2len)(p) : (*p == NUL ? 0 : 1) /* Backup multi-byte pointer. Only use with "p" > "s" ! */ #define MB_PTR_BACK(s, p) p -= has_mbyte ? ((*mb_head_off)(s, p - 1) + 1) : 1 /* get length of multi-byte char, not including composing chars */ diff --git a/src/version.c b/src/version.c index 0fa8440e35..edbd0a717d 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2119, /**/ 2118, /**/ From 1614a14901558ca091329315d14a7d5e1b53aa47 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 6 Oct 2019 22:00:13 +0200 Subject: [PATCH 18/67] patch 8.1.2120: some MB_ macros are more complicated than necessary Problem: Some MB_ macros are more complicated than necessary. (Dominique Pelle) Solution: Simplify the macros. Expand inline. --- src/beval.c | 2 +- src/diff.c | 2 +- src/eval.c | 2 +- src/evalfunc.c | 2 +- src/ex_getln.c | 2 +- src/filepath.c | 12 ++++++------ src/findfile.c | 4 ++-- src/getchar.c | 2 +- src/highlight.c | 2 +- src/macros.h | 6 ++---- src/ops.c | 4 ++-- src/os_mswin.c | 2 +- src/popupmenu.c | 4 ++-- src/search.c | 8 ++++---- src/spell.c | 2 +- src/spellsuggest.c | 28 ++++++++++++++-------------- src/terminal.c | 2 +- src/version.c | 2 ++ 18 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/beval.c b/src/beval.c index 69e667d639..8ef8e8e1d5 100644 --- a/src/beval.c +++ b/src/beval.c @@ -92,7 +92,7 @@ find_word_under_cursor( lbuf = ml_get_buf(curwin->w_buffer, VIsual.lnum, FALSE); len = epos->col - spos->col; if (*p_sel != 'e') - len += MB_PTR2LEN(lbuf + epos->col); + len += mb_ptr2len(lbuf + epos->col); lbuf = vim_strnsave(lbuf + spos->col, len); lnum = spos->lnum; col = spos->col; diff --git a/src/diff.c b/src/diff.c index 01125aaf46..ece7bf57d6 100644 --- a/src/diff.c +++ b/src/diff.c @@ -744,7 +744,7 @@ diff_write_buffer(buf_T *buf, diffin_T *din) // xdiff doesn't support ignoring case, fold-case the text. c = PTR2CHAR(s); c = enc_utf8 ? utf_fold(c) : MB_TOLOWER(c); - orig_len = MB_PTR2LEN(s); + orig_len = mb_ptr2len(s); if (mb_char2bytes(c, cbuf) != orig_len) // TODO: handle byte length difference mch_memmove(ptr + len, s, orig_len); diff --git a/src/eval.c b/src/eval.c index 954e07fb4a..0fe8fd34aa 100644 --- a/src/eval.c +++ b/src/eval.c @@ -6446,7 +6446,7 @@ do_string_sub( if (zero_width == regmatch.startp[0]) { /* avoid getting stuck on a match with an empty string */ - i = MB_PTR2LEN(tail); + i = mb_ptr2len(tail); mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); ga.ga_len += i; diff --git a/src/evalfunc.c b/src/evalfunc.c index 26d8691ccf..4a04696d6b 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -6454,7 +6454,7 @@ f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED) } else set_last_csearch(PTR2CHAR(csearch), - csearch, MB_PTR2LEN(csearch)); + csearch, mb_ptr2len(csearch)); } di = dict_find(d, (char_u *)"forward", -1); diff --git a/src/ex_getln.c b/src/ex_getln.c index ef87413b7c..838bd84045 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -2781,7 +2781,7 @@ redraw: } else { - len = MB_PTR2LEN(p); + len = mb_ptr2len(p); msg_outtrans_len(p, len); vcol += ptr2cells(p); p += len; diff --git a/src/filepath.c b/src/filepath.c index 5ffb8c9b4a..3a06eec7b3 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -2613,9 +2613,9 @@ vim_fnamencmp(char_u *x, char_u *y, size_t len) && !(cx == '/' && cy == '\\') && !(cx == '\\' && cy == '/'))) break; - len -= MB_PTR2LEN(px); - px += MB_PTR2LEN(px); - py += MB_PTR2LEN(py); + len -= mb_ptr2len(px); + px += mb_ptr2len(px); + py += mb_ptr2len(py); } if (len == 0) return 0; @@ -3769,14 +3769,14 @@ pathcmp(const char *p, const char *q, int maxlen) : c1 - c2; // no match } - i += MB_PTR2LEN((char_u *)p + i); - j += MB_PTR2LEN((char_u *)q + j); + i += mb_ptr2len((char_u *)p + i); + j += mb_ptr2len((char_u *)q + j); } if (s == NULL) // "i" or "j" ran into "maxlen" return 0; c1 = PTR2CHAR((char_u *)s + i); - c2 = PTR2CHAR((char_u *)s + i + MB_PTR2LEN((char_u *)s + i)); + c2 = PTR2CHAR((char_u *)s + i + mb_ptr2len((char_u *)s + i)); // ignore a trailing slash, but not "//" or ":/" if (c2 == NUL && i > 0 diff --git a/src/findfile.c b/src/findfile.c index 414dc4e14a..ddb4d10d2c 100644 --- a/src/findfile.c +++ b/src/findfile.c @@ -1337,8 +1337,8 @@ ff_wc_equal(char_u *s1, char_u *s2) prev2 = prev1; prev1 = c1; - i += MB_PTR2LEN(s1 + i); - j += MB_PTR2LEN(s2 + j); + i += mb_ptr2len(s1 + i); + j += mb_ptr2len(s2 + j); } return s1[i] == s2[j]; } diff --git a/src/getchar.c b/src/getchar.c index 5e098dfcf8..85ceddeb4c 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -2254,7 +2254,7 @@ handle_mapping( char_u *p2 = mb_unescape(&p1); if (has_mbyte && p2 != NULL - && MB_BYTE2LEN(tb_c1) > MB_PTR2LEN(p2)) + && MB_BYTE2LEN(tb_c1) > mb_ptr2len(p2)) mlen = 0; } diff --git a/src/highlight.c b/src/highlight.c index 725affcbfc..0fdd93a267 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -4392,7 +4392,7 @@ update_search_hl( && col >= shl->startcol && col < shl->endcol) { - int next_col = col + MB_PTR2LEN(*line + col); + int next_col = col + mb_ptr2len(*line + col); if (shl->endcol < next_col) shl->endcol = next_col; diff --git a/src/macros.h b/src/macros.h index 5e047c873f..f670046b3d 100644 --- a/src/macros.h +++ b/src/macros.h @@ -229,12 +229,10 @@ * MB_COPY_CHAR(f, t): copy one char from "f" to "t" and advance the pointers. * PTR2CHAR(): get character from pointer. */ -/* Get the length of the character p points to, including composing chars */ -#define MB_PTR2LEN(p) (has_mbyte ? (*mb_ptr2len)(p) : (*p == NUL ? 0 : 1)) /* Advance multi-byte pointer, skip over composing chars. */ -#define MB_PTR_ADV(p) p += has_mbyte ? (*mb_ptr2len)(p) : (*p == NUL ? 0 : 1) +#define MB_PTR_ADV(p) p += (*mb_ptr2len)(p) /* Advance multi-byte pointer, do not skip over composing chars. */ -#define MB_CPTR_ADV(p) p += enc_utf8 ? utf_ptr2len(p) : has_mbyte ? (*mb_ptr2len)(p) : (*p == NUL ? 0 : 1) +#define MB_CPTR_ADV(p) p += enc_utf8 ? utf_ptr2len(p) : (*mb_ptr2len)(p) /* Backup multi-byte pointer. Only use with "p" > "s" ! */ #define MB_PTR_BACK(s, p) p -= has_mbyte ? ((*mb_head_off)(s, p - 1) + 1) : 1 /* get length of multi-byte char, not including composing chars */ diff --git a/src/ops.c b/src/ops.c index e82a66b132..aa6858492a 100644 --- a/src/ops.c +++ b/src/ops.c @@ -3223,7 +3223,7 @@ do_addsub( while (ptr[col] != NUL && !vim_isdigit(ptr[col]) && !(doalp && ASCII_ISALPHA(ptr[col]))) - col += MB_PTR2LEN(ptr + col); + col += mb_ptr2len(ptr + col); while (col > 0 && vim_isdigit(ptr[col - 1]) @@ -3242,7 +3242,7 @@ do_addsub( && !vim_isdigit(ptr[col]) && !(doalp && ASCII_ISALPHA(ptr[col]))) { - int mb_len = MB_PTR2LEN(ptr + col); + int mb_len = mb_ptr2len(ptr + col); col += mb_len; length -= mb_len; diff --git a/src/os_mswin.c b/src/os_mswin.c index 5fb4809d25..b5ef8fce89 100644 --- a/src/os_mswin.c +++ b/src/os_mswin.c @@ -1654,7 +1654,7 @@ mch_print_text_out(char_u *p, int len) /* This is wrong when printing spaces for a TAB. */ if (p[len] != NUL) { - wlen = MB_PTR2LEN(p + len); + wlen = mb_ptr2len(p + len); wp = enc_to_utf16(p + len, &wlen); if (wp != NULL) { diff --git a/src/popupmenu.c b/src/popupmenu.c index 846dbc264d..b88c6531c1 100644 --- a/src/popupmenu.c +++ b/src/popupmenu.c @@ -1151,7 +1151,7 @@ split_message(char_u *mesg, pumitem_T **array) } } item->cells += ptr2cells(p); - p += MB_PTR2LEN(p); + p += mb_ptr2len(p); } item->bytelen = p - item->start; if (item->cells > max_cells) @@ -1195,7 +1195,7 @@ split_message(char_u *mesg, pumitem_T **array) { cells = item->indent * 2; for (p = item->start + skip; p < item->start + item->bytelen; - p += MB_PTR2LEN(p)) + p += mb_ptr2len(p)) if ((cells += ptr2cells(p)) > BALLOON_MIN_WIDTH) break; thislen = p - (item->start + skip); diff --git a/src/search.c b/src/search.c index 63171a92c0..a0ca8df622 100644 --- a/src/search.c +++ b/src/search.c @@ -2078,7 +2078,7 @@ findmatchlimit( find_mps_values(&initc, &findc, &backwards, FALSE); if (findc) break; - pos.col += MB_PTR2LEN(linep + pos.col); + pos.col += mb_ptr2len(linep + pos.col); } if (!findc) { @@ -2657,14 +2657,14 @@ showmatch( if (PTR2CHAR(p) == c && (curwin->w_p_rl ^ p_ri)) break; #endif - p += MB_PTR2LEN(p) + 1; + p += mb_ptr2len(p) + 1; if (PTR2CHAR(p) == c #ifdef FEAT_RIGHTLEFT && !(curwin->w_p_rl ^ p_ri) #endif ) break; - p += MB_PTR2LEN(p); + p += mb_ptr2len(p); if (*p == NUL) return; } @@ -5633,7 +5633,7 @@ exit_matched: && action == ACTION_EXPAND && !(compl_cont_status & CONT_SOL) && *startp != NUL - && *(p = startp + MB_PTR2LEN(startp)) != NUL) + && *(p = startp + mb_ptr2len(startp)) != NUL) goto search_line; } line_breakcheck(); diff --git a/src/spell.c b/src/spell.c index de478a3ed0..607bb392e5 100644 --- a/src/spell.c +++ b/src/spell.c @@ -2621,7 +2621,7 @@ spell_iswordp( if (has_mbyte) { - l = MB_PTR2LEN(p); + l = mb_ptr2len(p); s = p; if (l == 1) { diff --git a/src/spellsuggest.c b/src/spellsuggest.c index 41f4a73b17..a5f8b92fae 100644 --- a/src/spellsuggest.c +++ b/src/spellsuggest.c @@ -1782,7 +1782,7 @@ suggest_trie_walk( { int l; - l = MB_PTR2LEN(fword + sp->ts_fidx); + l = mb_ptr2len(fword + sp->ts_fidx); if (fword_ends) { // Copy the skipped character to preword. @@ -1937,7 +1937,7 @@ suggest_trie_walk( // Correct ts_fidx for the byte length of the // character (we didn't check that before). sp->ts_fidx = sp->ts_fcharstart - + MB_PTR2LEN( + + mb_ptr2len( fword + sp->ts_fcharstart); // For changing a composing character adjust // the score from SCORE_SUBST to @@ -2053,7 +2053,7 @@ suggest_trie_walk( if (has_mbyte) { c = mb_ptr2char(fword + sp->ts_fidx); - stack[depth].ts_fidx += MB_PTR2LEN(fword + sp->ts_fidx); + stack[depth].ts_fidx += mb_ptr2len(fword + sp->ts_fidx); if (enc_utf8 && utf_iscomposing(c)) stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP; else if (c == mb_ptr2char(fword + stack[depth].ts_fidx)) @@ -2266,9 +2266,9 @@ suggest_trie_walk( p = fword + sp->ts_fidx; if (has_mbyte) { - n = MB_PTR2LEN(p); + n = mb_ptr2len(p); c = mb_ptr2char(p + n); - mch_memmove(p + MB_PTR2LEN(p + n), p, n); + mch_memmove(p + mb_ptr2len(p + n), p, n); mb_char2bytes(c, p); } else @@ -2354,11 +2354,11 @@ suggest_trie_walk( p = fword + sp->ts_fidx; if (has_mbyte) { - n = MB_PTR2LEN(p); + n = mb_ptr2len(p); c2 = mb_ptr2char(p + n); - fl = MB_PTR2LEN(p + n); + fl = mb_ptr2len(p + n); c = mb_ptr2char(p + n + fl); - tl = MB_PTR2LEN(p + n + fl); + tl = mb_ptr2len(p + n + fl); mch_memmove(p + fl + tl, p, n); mb_char2bytes(c, p); mb_char2bytes(c2, p + tl); @@ -2427,10 +2427,10 @@ suggest_trie_walk( p = fword + sp->ts_fidx; if (has_mbyte) { - n = MB_PTR2LEN(p); - n += MB_PTR2LEN(p + n); + n = mb_ptr2len(p); + n += mb_ptr2len(p + n); c = mb_ptr2char(p + n); - tl = MB_PTR2LEN(p + n); + tl = mb_ptr2len(p + n); mch_memmove(p + tl, p, n); mb_char2bytes(c, p); } @@ -2489,9 +2489,9 @@ suggest_trie_walk( if (has_mbyte) { c = mb_ptr2char(p); - tl = MB_PTR2LEN(p); - n = MB_PTR2LEN(p + tl); - n += MB_PTR2LEN(p + tl + n); + tl = mb_ptr2len(p); + n = mb_ptr2len(p + tl); + n += mb_ptr2len(p + tl + n); mch_memmove(p, p + tl, n); mb_char2bytes(c, p + n); } diff --git a/src/terminal.c b/src/terminal.c index 52487a3cf3..42a80cd7dc 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -5431,7 +5431,7 @@ f_term_scrape(typval_T *argvars, typval_T *rettv) attrs = cellattr->attrs; fg = cellattr->fg; bg = cellattr->bg; - len = MB_PTR2LEN(p); + len = mb_ptr2len(p); mch_memmove(mbs, p, len); mbs[len] = NUL; p += len; diff --git a/src/version.c b/src/version.c index edbd0a717d..f668b9704e 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2120, /**/ 2119, /**/ From a27e1dcddc9e3914ab34b164f71c51b72903b00b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Oct 2019 22:27:36 +0200 Subject: [PATCH 19/67] patch 8.1.2121: mode is not updated when switching to terminal Problem: Mode is not updated when switching to terminal in Insert mode. Solution: Redraw the mode when entering a terminal window. (Jason Franklin) --- src/testdir/test_window_cmd.vim | 29 +++++++++++++++++++++++++++++ src/version.c | 2 ++ src/window.c | 3 +++ 3 files changed, 34 insertions(+) diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index ffe8f431d4..d958c36973 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -1,5 +1,7 @@ " Tests for window cmd (:wincmd, :split, :vsplit, :resize and etc...) +so check.vim + func Test_window_cmd_ls0_with_split() set ls=0 set splitbelow @@ -557,6 +559,33 @@ func Test_access_freed_mem() call assert_equal(&columns, winwidth(0)) endfunc +func Test_insert_cleared_on_switch_to_term() + CheckFeature terminal + + set showmode + terminal + wincmd p + + call feedkeys("i\", 'ntx') + redraw + + " The "-- (insert) --" indicator should be visible. + let chars = map(range(1, &columns), 'nr2char(screenchar(&lines, v:val))') + let str = trim(join(chars, '')) + call assert_equal('-- (insert) --', str) + + call feedkeys("\p", 'ntx') + redraw + + " The "-- (insert) --" indicator should have been cleared. + let chars = map(range(1, &columns), 'nr2char(screenchar(&lines, v:val))') + let str = trim(join(chars, '')) + call assert_equal('', str) + + set showmode& + %bw! +endfunc + func Test_visual_cleared_after_window_split() new | only! let smd_save = &showmode diff --git a/src/version.c b/src/version.c index f668b9704e..f4d0275344 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2121, /**/ 2120, /**/ diff --git a/src/window.c b/src/window.c index 6416e0187c..6174c02fd3 100644 --- a/src/window.c +++ b/src/window.c @@ -4654,6 +4654,9 @@ win_enter_ext( maketitle(); #endif curwin->w_redr_status = TRUE; + if (bt_terminal(wp->w_buffer)) + // terminal is likely in another mode + redraw_mode = TRUE; redraw_tabline = TRUE; if (restart_edit) redraw_later(VALID); /* causes status line redraw */ From fbbd102be0f017b316f483893a95e4e78c286c9b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Oct 2019 22:38:58 +0200 Subject: [PATCH 20/67] patch 8.1.2122: cannot build without terminal feature Problem: Cannot build without terminal feature. Solution: Add #ifdef. --- src/version.c | 2 ++ src/window.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/version.c b/src/version.c index f4d0275344..e207a37107 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2122, /**/ 2121, /**/ diff --git a/src/window.c b/src/window.c index 6174c02fd3..0fda9f05c6 100644 --- a/src/window.c +++ b/src/window.c @@ -4654,9 +4654,11 @@ win_enter_ext( maketitle(); #endif curwin->w_redr_status = TRUE; +#ifdef FEAT_TERMINAL if (bt_terminal(wp->w_buffer)) // terminal is likely in another mode redraw_mode = TRUE; +#endif redraw_tabline = TRUE; if (restart_edit) redraw_later(VALID); /* causes status line redraw */ From c3e555b22f24f93aabd31943c35a9228abb6ecb6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 8 Oct 2019 20:15:39 +0200 Subject: [PATCH 21/67] patch 8.1.2123: parsing CSI sequence is messy Problem: Parsing CSI sequence is messy. Solution: Generalize parsing a CSI sequence. --- src/term.c | 299 ++++++++++++++++++++++++++------------------------ src/version.c | 2 + 2 files changed, 158 insertions(+), 143 deletions(-) diff --git a/src/term.c b/src/term.c index 2319453fb7..f021a17ff7 100644 --- a/src/term.c +++ b/src/term.c @@ -4435,72 +4435,105 @@ check_termcode( # endif ) { - /* Check for some responses from the terminal starting with - * "[" or CSI: + char_u *argp = tp[0] == ESC ? tp + 2 : tp + 1; + + /* + * Check for responses from the terminal starting with {lead}: + * "[" or CSI followed by [0-9>?] * - * - Xterm version string: [>{x};{vers};{y}c - * Libvterm returns {x} == 0, {vers} == 100, {y} == 0. + * - Xterm version string: {lead}>{x};{vers};{y}c * Also eat other possible responses to t_RV, rxvt returns - * "[?1;2c". Also accept CSI instead of [. - * mrxvt has been reported to have "+" in the version. Assume - * the escape sequence ends with a letter or one of "{|}~". + * "{lead}?1;2c". * - * - Cursor position report: [{row};{col}R + * - Cursor position report: {lead}{row};{col}R * The final byte must be 'R'. It is used for checking the * ambiguous-width character state. * - * - window position reply: [3;{x};{y}t + * - window position reply: {lead}3;{x};{y}t + * + * - key with modifiers when modifyOtherKeys is enabled: + * {lead}27;{modifier};{key}~ + * {lead}{key};{modifier}u */ - char_u *argp = tp[0] == ESC ? tp + 2 : tp + 1; - - if ((*T_CRV != NUL || *T_U7 != NUL || did_request_winpos) - && ((tp[0] == ESC && len >= 3 && tp[1] == '[') + if (((tp[0] == ESC && len >= 3 && tp[1] == '[') || (tp[0] == CSI && len >= 2)) - && (VIM_ISDIGIT(*argp) || *argp == '>' || *argp == '?')) + && (VIM_ISDIGIT(*argp) || *argp == '>' || *argp == '?')) { - int col = 0; - int semicols = 0; - int row_char = NUL; + int first = -1; // optional char right after {lead} + int trail; // char that ends CSI sequence + int arg[3] = {-1, -1, -1}; // argument numbers + int argc; // number of arguments + char_u *ap = argp; + int csi_len; - extra = 0; - for (i = 2 + (tp[0] != CSI); i < len - && !(tp[i] >= '{' && tp[i] <= '~') - && !ASCII_ISALPHA(tp[i]); ++i) - if (tp[i] == ';' && ++semicols == 1) + // Check for non-digit after CSI. + if (!VIM_ISDIGIT(*ap)) + first = *ap++; + + // Find up to three argument numbers. + for (argc = 0; argc < 3; ) + { + if (ap >= tp + len) { - extra = i + 1; - row_char = tp[i - 1]; +not_enough: + LOG_TR(("Not enough characters for CSI sequence")); + return -1; } - if (i == len) - { - LOG_TR(("Not enough characters for CRV")); - return -1; + if (*ap == ';') + arg[argc++] = -1; // omitted number + else if (VIM_ISDIGIT(*ap)) + { + arg[argc] = 0; + for (;;) + { + if (ap >= tp + len) + goto not_enough; + if (!VIM_ISDIGIT(*ap)) + break; + arg[argc] = arg[argc] * 10 + (*ap - '0'); + ++ap; + } + ++argc; + } + if (*ap == ';') + ++ap; + else + break; } - if (extra > 0) - col = atoi((char *)tp + extra); + // mrxvt has been reported to have "+" in the version. Assume + // the escape sequence ends with a letter or one of "{|}~". + while (ap < tp + len + && !(*ap >= '{' && *ap <= '~') + && !ASCII_ISALPHA(*ap)) + ++ap; + if (ap >= tp + len) + goto not_enough; + trail = *ap; + csi_len = (int)(ap - tp) + 1; - /* Eat it when it has 2 arguments and ends in 'R'. Also when - * u7_status is not "sent", it may be from a previous Vim that - * just exited. But not for , it sends something - * similar, check for row and column to make sense. */ - if (semicols == 1 && tp[i] == 'R') + // Cursor position report: Eat it when there are 2 arguments + // and it ends in 'R'. Also when u7_status is not "sent", it + // may be from a previous Vim that just exited. But not for + // , it sends something similar, check for row and column + // to make sense. + if (first == -1 && argc == 2 && trail == 'R') { - if (row_char == '2' && col >= 2) + if (arg[0] == 2 && arg[1] >= 2) { char *aw = NULL; LOG_TR(("Received U7 status: %s", tp)); u7_status.tr_progress = STATUS_GOT; did_cursorhold = TRUE; - if (col == 2) + if (arg[1] == 2) aw = "single"; - else if (col == 3) + else if (arg[1] == 3) aw = "double"; if (aw != NULL && STRCMP(aw, p_ambw) != 0) { - /* Setting the option causes a screen redraw. Do - * that right away if possible, keeping any - * messages. */ + // Setting the option causes a screen redraw. Do + // that right away if possible, keeping any + // messages. set_option_value((char_u *)"ambw", 0L, (char_u *)aw, 0); # ifdef DEBUG_TERMRESPONSE @@ -4516,34 +4549,35 @@ check_termcode( } key_name[0] = (int)KS_EXTRA; key_name[1] = (int)KE_IGNORE; - slen = i + 1; + slen = csi_len; # ifdef FEAT_EVAL set_vim_var_string(VV_TERMU7RESP, tp, slen); # endif } - /* eat it when at least one digit and ending in 'c' */ - else if (*T_CRV != NUL && i > 2 + (tp[0] != CSI) - && tp[i] == 'c') + + // Version string: Eat it when there is at least one digit and + // it ends in 'c' + else if (*T_CRV != NUL && ap > argp + 1 && trail == 'c') { - int version = col; + int version = arg[1]; LOG_TR(("Received CRV response: %s", tp)); crv_status.tr_progress = STATUS_GOT; did_cursorhold = TRUE; - /* If this code starts with CSI, you can bet that the - * terminal uses 8-bit codes. */ + // If this code starts with CSI, you can bet that the + // terminal uses 8-bit codes. if (tp[0] == CSI) switch_to_8bit(); - /* rxvt sends its version number: "20703" is 2.7.3. - * Screen sends 40500. - * Ignore it for when the user has set 'term' to xterm, - * even though it's an rxvt. */ + // rxvt sends its version number: "20703" is 2.7.3. + // Screen sends 40500. + // Ignore it for when the user has set 'term' to xterm, + // even though it's an rxvt. if (version > 20000) version = 0; - if (tp[1 + (tp[0] != CSI)] == '>' && semicols == 2) + if (first == '>' && argc == 3) { int need_flush = FALSE; int is_iterm2 = FALSE; @@ -4551,10 +4585,10 @@ check_termcode( // mintty 2.9.5 sends 77;20905;0c. // (77 is ASCII 'M' for mintty.) - if (STRNCMP(tp + extra - 3, "77;", 3) == 0) + if (arg[0] == 77) is_mintty = TRUE; - /* if xterm version >= 141 try to get termcap codes */ + // if xterm version >= 141 try to get termcap codes if (version >= 141) { LOG_TR(("Enable checking for XT codes")); @@ -4563,13 +4597,12 @@ check_termcode( req_codes_from_term(); } - /* libvterm sends 0;100;0 */ - if (version == 100 - && STRNCMP(tp + extra - 2, "0;100;0c", 8) == 0) + // libvterm sends 0;100;0 + if (version == 100 && arg[0] == 0 && arg[2] == 0) { - /* If run from Vim $COLORS is set to the number of - * colors the terminal supports. Otherwise assume - * 256, libvterm supports even more. */ + // If run from Vim $COLORS is set to the number of + // colors the terminal supports. Otherwise assume + // 256, libvterm supports even more. if (mch_getenv((char_u *)"COLORS") == NULL) may_adjust_color_count(256); /* Libvterm can handle SGR mouse reporting. */ @@ -4581,56 +4614,54 @@ check_termcode( if (version == 95) { // Mac Terminal.app sends 1;95;0 - if (STRNCMP(tp + extra - 2, "1;95;0c", 7) == 0) + if (arg[0] == 1 && arg[2] == 0) { is_not_xterm = TRUE; is_mac_terminal = TRUE; } // iTerm2 sends 0;95;0 - if (STRNCMP(tp + extra - 2, "0;95;0c", 7) == 0) + else if (arg[0] == 0 && arg[2] == 0) is_iterm2 = TRUE; // old iTerm2 sends 0;95; - else if (STRNCMP(tp + extra - 2, "0;95;c", 6) == 0) + else if (arg[0] == 0 && arg[2] == -1) is_not_xterm = TRUE; } - /* Only set 'ttymouse' automatically if it was not set - * by the user already. */ + // Only set 'ttymouse' automatically if it was not set + // by the user already. if (!option_was_set((char_u *)"ttym")) { - /* Xterm version 277 supports SGR. Also support - * Terminal.app, iTerm2 and mintty. */ + // Xterm version 277 supports SGR. Also support + // Terminal.app, iTerm2 and mintty. if (version >= 277 || is_iterm2 || is_mac_terminal || is_mintty) set_option_value((char_u *)"ttym", 0L, (char_u *)"sgr", 0); - /* if xterm version >= 95 use mouse dragging */ + // if xterm version >= 95 use mouse dragging else if (version >= 95) set_option_value((char_u *)"ttym", 0L, (char_u *)"xterm2", 0); } - /* Detect terminals that set $TERM to something like - * "xterm-256colors" but are not fully xterm - * compatible. */ + // Detect terminals that set $TERM to something like + // "xterm-256colors" but are not fully xterm + // compatible. - /* Gnome terminal sends 1;3801;0, 1;4402;0 or 1;2501;0. - * xfce4-terminal sends 1;2802;0. - * screen sends 83;40500;0 - * Assuming any version number over 2500 is not an - * xterm (without the limit for rxvt and screen). */ - if (col >= 2500) + // Gnome terminal sends 1;3801;0, 1;4402;0 or 1;2501;0. + // xfce4-terminal sends 1;2802;0. + // screen sends 83;40500;0 + // Assuming any version number over 2500 is not an + // xterm (without the limit for rxvt and screen). + if (arg[1] >= 2500) is_not_xterm = TRUE; - /* PuTTY sends 0;136;0 - * vandyke SecureCRT sends 1;136;0 */ - if (version == 136 - && STRNCMP(tp + extra - 1, ";136;0c", 7) == 0) + // PuTTY sends 0;136;0 + // vandyke SecureCRT sends 1;136;0 + else if (version == 136 && arg[2] == 0) is_not_xterm = TRUE; - /* Konsole sends 0;115;0 */ - if (version == 115 - && STRNCMP(tp + extra - 2, "0;115;0c", 8) == 0) + // Konsole sends 0;115;0 + else if (version == 115 && arg[0] == 0 && arg[2] == 0) is_not_xterm = TRUE; // Xterm first responded to this request at patch level @@ -4638,11 +4669,11 @@ check_termcode( if (version < 95) is_not_xterm = TRUE; - /* Only request the cursor style if t_SH and t_RS are - * set. Only supported properly by xterm since version - * 279 (otherwise it returns 0x18). - * Not for Terminal.app, it can't handle t_RS, it - * echoes the characters to the screen. */ + // Only request the cursor style if t_SH and t_RS are + // set. Only supported properly by xterm since version + // 279 (otherwise it returns 0x18). + // Not for Terminal.app, it can't handle t_RS, it + // echoes the characters to the screen. if (rcs_status.tr_progress == STATUS_GET && version >= 279 && !is_not_xterm @@ -4655,9 +4686,9 @@ check_termcode( need_flush = TRUE; } - /* Only request the cursor blink mode if t_RC set. Not - * for Gnome terminal, it can't handle t_RC, it - * echoes the characters to the screen. */ + // Only request the cursor blink mode if t_RC set. Not + // for Gnome terminal, it can't handle t_RC, it + // echoes the characters to the screen. if (rbm_status.tr_progress == STATUS_GET && !is_not_xterm && *T_CRC != NUL) @@ -4671,7 +4702,7 @@ check_termcode( if (need_flush) out_flush(); } - slen = i + 1; + slen = csi_len; # ifdef FEAT_EVAL set_vim_var_string(VV_TERMRESPONSE, tp, slen); # endif @@ -4681,69 +4712,51 @@ check_termcode( key_name[1] = (int)KE_IGNORE; } - /* Check blinking cursor from xterm: - * {lead}?12;1$y set - * {lead}?12;2$y not set - * - * {lead} can be [ or CSI - */ + // Check blinking cursor from xterm: + // {lead}?12;1$y set + // {lead}?12;2$y not set + // + // {lead} can be [ or CSI else if (rbm_status.tr_progress == STATUS_SENT - && tp[(j = 1 + (tp[0] == ESC))] == '?' - && i == j + 6 - && tp[j + 1] == '1' - && tp[j + 2] == '2' - && tp[j + 3] == ';' - && tp[i - 1] == '$' - && tp[i] == 'y') + && first == '?' + && ap == argp + 6 + && arg[0] == 12 + && ap[-1] == '$' + && trail == 'y') { - initial_cursor_blink = (tp[j + 4] == '1'); + initial_cursor_blink = (arg[1] == '1'); rbm_status.tr_progress = STATUS_GOT; LOG_TR(("Received cursor blinking mode response: %s", tp)); key_name[0] = (int)KS_EXTRA; key_name[1] = (int)KE_IGNORE; - slen = i + 1; + slen = csi_len; # ifdef FEAT_EVAL set_vim_var_string(VV_TERMBLINKRESP, tp, slen); # endif } - /* - * Check for a window position response from the terminal: - * {lead}3;{x};{y}t - */ - else if (did_request_winpos - && ((len >= 4 && tp[0] == ESC && tp[1] == '[') - || (len >= 3 && tp[0] == CSI)) - && tp[(j = 1 + (tp[0] == ESC))] == '3' - && tp[j + 1] == ';') + // Check for a window position response from the terminal: + // {lead}3;{x};{y}t + else if (did_request_winpos && argc == 3 && arg[0] == 3 + && trail == 't') { - j += 2; - for (i = j; i < len && vim_isdigit(tp[i]); ++i) - ; - if (i < len && tp[i] == ';') - { - winpos_x = atoi((char *)tp + j); - j = i + 1; - for (i = j; i < len && vim_isdigit(tp[i]); ++i) - ; - if (i < len && tp[i] == 't') - { - winpos_y = atoi((char *)tp + j); - /* got finished code: consume it */ - key_name[0] = (int)KS_EXTRA; - key_name[1] = (int)KE_IGNORE; - slen = i + 1; + winpos_x = arg[1]; + winpos_y = arg[2]; + // got finished code: consume it + key_name[0] = (int)KS_EXTRA; + key_name[1] = (int)KE_IGNORE; + slen = csi_len; - if (--did_request_winpos <= 0) - winpos_status.tr_progress = STATUS_GOT; - } - } - if (i == len) - { - LOG_TR(("not enough characters for winpos")); - return -1; - } + if (--did_request_winpos <= 0) + winpos_status.tr_progress = STATUS_GOT; } + + // TODO: key with modifier: + // {lead}27;{modifier};{key}~ + // {lead}{key};{modifier}u + + // else: Unknown CSI sequence. We could drop it, but then the + // user can't create a map for it. } /* Check for fore/background color response from the terminal: diff --git a/src/version.c b/src/version.c index e207a37107..f07d0382ce 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2123, /**/ 2122, /**/ From 345f28df5482cd35f5fa74b06443376379f113b0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 8 Oct 2019 22:20:35 +0200 Subject: [PATCH 22/67] patch 8.1.2124: ruler is not updated if win_execute() moves cursor Problem: Ruler is not updated if win_execute() moves cursor. Solution: Update the status line. (closes #5022) --- src/evalwindow.c | 6 ++++++ src/testdir/test_execute_func.vim | 20 ++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 28 insertions(+) diff --git a/src/evalwindow.c b/src/evalwindow.c index 6df14c5927..76916936dc 100644 --- a/src/evalwindow.c +++ b/src/evalwindow.c @@ -655,12 +655,18 @@ f_win_execute(typval_T *argvars, typval_T *rettv) if (wp != NULL && tp != NULL) { + pos_T curpos = wp->w_cursor; + if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, TRUE) == OK) { check_cursor(); execute_common(argvars, rettv, 1); } restore_win_noblock(save_curwin, save_curtab, TRUE); + + // Update the status line if the cursor moved. + if (win_valid(wp) && !EQUAL_POS(curpos, wp->w_cursor)) + wp->w_redr_status = TRUE; } } diff --git a/src/testdir/test_execute_func.vim b/src/testdir/test_execute_func.vim index f81a86b6ac..2f02409dea 100644 --- a/src/testdir/test_execute_func.vim +++ b/src/testdir/test_execute_func.vim @@ -1,5 +1,7 @@ " test execute() +source view_util.vim + func NestedEval() let nested = execute('echo "nested\nlines"') echo 'got: "' . nested . '"' @@ -101,6 +103,24 @@ func Test_win_execute() bwipe! endfunc +func Test_win_execute_update_ruler() + enew + call setline(1, range(500)) + 20 + split + let winid = win_getid() + set ruler + wincmd w + let height = winheight(winid) + redraw + call assert_match('20,1', Screenline(height + 1)) + let line = win_execute(winid, 'call cursor(100, 1)') + redraw + call assert_match('100,1', Screenline(height + 1)) + + bwipe! +endfunc + func Test_win_execute_other_tab() let thiswin = win_getid() tabnew diff --git a/src/version.c b/src/version.c index f07d0382ce..514f3c929f 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2124, /**/ 2123, /**/ From b189295b72030f00c45c30d3daecf85d457221f8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 8 Oct 2019 23:26:50 +0200 Subject: [PATCH 23/67] patch 8.1.2125: fnamemodify() fails when repeating :e Problem: Fnamemodify() fails when repeating :e. Solution: Do not go before the tail. (Rob Pilling, closes #5024) --- src/filepath.c | 6 +++++- src/testdir/test_fnamemodify.vim | 28 ++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/filepath.c b/src/filepath.c index 3a06eec7b3..cf401dc6d5 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -563,7 +563,11 @@ repeat: } else // :r { - if (s > tail) // remove one extension + char_u *limit = *fnamep; + + if (limit < tail) + limit = tail; + if (s > limit) // remove one extension *fnamelen = (int)(s - *fnamep); } *usedlen += 2; diff --git a/src/testdir/test_fnamemodify.vim b/src/testdir/test_fnamemodify.vim index fd5f1efb82..a8324ab8a9 100644 --- a/src/testdir/test_fnamemodify.vim +++ b/src/testdir/test_fnamemodify.vim @@ -45,3 +45,31 @@ func Test_fnamemodify() let $HOME = save_home let &shell = save_shell endfunc + +func Test_fnamemodify_er() + call assert_equal("with", fnamemodify("path/to/file.with.extensions", ':e:e:r:r')) + + call assert_equal('c', fnamemodify('a.c', ':e')) + call assert_equal('c', fnamemodify('a.c', ':e:e')) + call assert_equal('c', fnamemodify('a.c', ':e:e:r')) + call assert_equal('c', fnamemodify('a.c', ':e:e:r:r')) + + call assert_equal('rb', fnamemodify('a.spec.rb', ':e:r')) + call assert_equal('rb', fnamemodify('a.spec.rb', ':e:r')) + call assert_equal('spec.rb', fnamemodify('a.spec.rb', ':e:e')) + call assert_equal('spec', fnamemodify('a.spec.rb', ':e:e:r')) + call assert_equal('spec', fnamemodify('a.spec.rb', ':e:e:r:r')) + call assert_equal('spec', fnamemodify('a.b.spec.rb', ':e:e:r')) + call assert_equal('b.spec', fnamemodify('a.b.spec.rb', ':e:e:e:r')) + call assert_equal('b', fnamemodify('a.b.spec.rb', ':e:e:e:r:r')) + + call assert_equal('spec', fnamemodify('a.b.spec.rb', ':r:e')) + call assert_equal('b', fnamemodify('a.b.spec.rb', ':r:r:e')) + + call assert_equal('c', fnamemodify('a.b.c.d.e', ':r:r:e')) + call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e')) + + " :e never includes the whole filename, so "a.b":e:e:e --> "b" + call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e')) + call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e:e')) +endfunc diff --git a/src/version.c b/src/version.c index 514f3c929f..a7a7869609 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2125, /**/ 2124, /**/ From 6bd1d7706766a7899904163e8fd55ea117fb1953 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 9 Oct 2019 22:01:25 +0200 Subject: [PATCH 24/67] patch 8.1.2126: viminfo not sufficiently tested Problem: Viminfo not sufficiently tested. Solution: Add more test cases. Clean up comments. (Yegappan Lakshmanan, closes #5032) --- src/search.c | 6 ++ src/structs.h | 12 ---- src/testdir/test_viminfo.vim | 64 +++++++++++++++++++-- src/version.c | 2 + src/viminfo.c | 106 ++++++++++++++++------------------- 5 files changed, 116 insertions(+), 74 deletions(-) diff --git a/src/search.c b/src/search.c index a0ca8df622..77c3aa4922 100644 --- a/src/search.c +++ b/src/search.c @@ -5783,12 +5783,18 @@ show_pat_in_path( #endif #ifdef FEAT_VIMINFO +/* + * Return the last used search pattern at "idx". + */ spat_T * get_spat(int idx) { return &spats[idx]; } +/* + * Return the last used search pattern index. + */ int get_spat_last_idx(void) { diff --git a/src/structs.h b/src/structs.h index 9e69001202..2d7c4fd884 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1133,18 +1133,6 @@ typedef struct int vc_fail; // fail for invalid char, don't use '?' } vimconv_T; -/* - * Structure used for reading from the viminfo file. - */ -typedef struct -{ - char_u *vir_line; // text of the current line - FILE *vir_fd; // file descriptor - vimconv_T vir_conv; // encoding conversion - int vir_version; // viminfo version detected or -1 - garray_T vir_barlines; // lines starting with | -} vir_T; - /* * Structure used for the command line history. */ diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim index 6e3116271e..7ea9f78da4 100644 --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -1,5 +1,7 @@ " Test for reading and writing .viminfo +source check.vim + function Test_viminfo_read_and_write() " First clear 'history', so that "hislen" is zero. Then set it again, " simulating Vim starting up. @@ -715,26 +717,78 @@ func Test_viminfo_large_register() rviminfo! Xviminfo call assert_equal(join(repeat(["sun is rising"], 200), "\n"), @r) call delete('Xviminfo') + let @r = '' let &viminfo = save_viminfo endfunc " Test for setting 'viminfofile' to NONE func Test_viminfofile_none() + let save_vif = &viminfofile set viminfofile=NONE wviminfo Xviminfo call assert_false(filereadable('Xviminfo')) call writefile([''], 'Xviminfo') call assert_fails('rviminfo Xviminfo', 'E195:') call delete('Xviminfo') + let &viminfofile = save_vif endfunc -" Test for an unwritable 'viminfo' file -func Test_viminfo_readonly() - if !has('unix') - return - endif +" Test for an unwritable and unreadble 'viminfo' file +func Test_viminfo_perm() + CheckUnix call writefile([''], 'Xviminfo') call setfperm('Xviminfo', 'r-x------') call assert_fails('wviminfo Xviminfo', 'E137:') + call setfperm('Xviminfo', '--x------') + call assert_fails('rviminfo Xviminfo', 'E195:') call delete('Xviminfo') endfunc + +" Test for writing to an existing viminfo file merges the file marks +func XTest_viminfo_marks_merge() + let save_viminfo = &viminfo + set viminfo&vim + set viminfo^=% + enew + %argdelete + %bwipe + + call writefile(repeat(['editor'], 10), 'Xbufa') + call writefile(repeat(['Vim'], 10), 'Xbufb') + + " set marks in buffers + call test_settime(10) + edit Xbufa + 4mark a + wviminfo Xviminfo + edit Xbufb + 4mark b + wviminfo Xviminfo + %bwipe + + " set marks in buffers again + call test_settime(20) + edit Xbufb + 6mark b + wviminfo Xviminfo + edit Xbufa + 6mark a + wviminfo Xviminfo + %bwipe + + " Load the buffer and check the marks + edit Xbufa + rviminfo! Xviminfo + call assert_equal(6, line("'a")) + edit Xbufb + rviminfo! Xviminfo + call assert_equal(6, line("'b")) + + " cleanup + %bwipe + call delete('Xviminfo') + call delete('Xbufa') + call delete('Xbufb') + call test_settime(0) + let &viminfo=save_viminfo +endfunc diff --git a/src/version.c b/src/version.c index a7a7869609..11419140f0 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2126, /**/ 2125, /**/ diff --git a/src/viminfo.c b/src/viminfo.c index 276e1b3bfc..b16282856b 100644 --- a/src/viminfo.c +++ b/src/viminfo.c @@ -14,6 +14,18 @@ #include "vim.h" #include "version.h" +/* + * Structure used for reading from the viminfo file. + */ +typedef struct +{ + char_u *vir_line; // text of the current line + FILE *vir_fd; // file descriptor + vimconv_T vir_conv; // encoding conversion + int vir_version; // viminfo version detected or -1 + garray_T vir_barlines; // lines starting with | +} vir_T; + #if defined(FEAT_VIMINFO) || defined(PROTO) static int viminfo_errcnt; @@ -822,11 +834,9 @@ write_viminfo_history(FILE *fp, int merge) if (num_saved > hislen) num_saved = hislen; - /* - * Merge typed and viminfo history: - * round 1: history of typed commands. - * round 2: history from recently read viminfo. - */ + // Merge typed and viminfo history: + // round 1: history of typed commands. + // round 2: history from recently read viminfo. for (round = 1; round <= 2; ++round) { if (round == 1) @@ -2671,16 +2681,14 @@ read_viminfo_barline(vir_T *virp, int got_encoding, int force, int writing) int i; int read_next = TRUE; - /* - * The format is: |{bartype},{value},... - * For a very long string: - * |{bartype},>{length of "{text}{text2}"} - * |<{text1} - * |<{text2},{value} - * For a long line not using a string - * |{bartype},{lots of values},> - * |<{value},{value} - */ + // The format is: |{bartype},{value},... + // For a very long string: + // |{bartype},>{length of "{text}{text2}"} + // |<{text1} + // |<{text2},{value} + // For a long line not using a string + // |{bartype},{lots of values},> + // |<{value},{value} if (*p == '<') { // Continuation line of an unrecognized item. @@ -3032,18 +3040,14 @@ write_viminfo(char_u *file, int forceit) } else { - /* - * There is an existing viminfo file. Create a temporary file to - * write the new viminfo into, in the same directory as the - * existing viminfo file, which will be renamed once all writing is - * successful. - */ + // There is an existing viminfo file. Create a temporary file to + // write the new viminfo into, in the same directory as the + // existing viminfo file, which will be renamed once all writing is + // successful. #ifdef UNIX - /* - * For Unix we check the owner of the file. It's not very nice to - * overwrite a user's viminfo file after a "su root", with a - * viminfo file that the user can't read. - */ + // For Unix we check the owner of the file. It's not very nice to + // overwrite a user's viminfo file after a "su root", with a + // viminfo file that the user can't read. st_old.st_dev = (dev_t)0; st_old.st_ino = 0; st_old.st_mode = 0600; @@ -3069,14 +3073,12 @@ write_viminfo(char_u *file, int forceit) hidden = mch_ishidden(fname); #endif - /* - * Make tempname, find one that does not exist yet. - * Beware of a race condition: If someone logs out and all Vim - * instances exit at the same time a temp file might be created between - * stat() and open(). Use mch_open() with O_EXCL to avoid that. - * May try twice: Once normal and once with shortname set, just in - * case somebody puts his viminfo file in an 8.3 filesystem. - */ + // Make tempname, find one that does not exist yet. + // Beware of a race condition: If someone logs out and all Vim + // instances exit at the same time a temp file might be created between + // stat() and open(). Use mch_open() with O_EXCL to avoid that. + // May try twice: Once normal and once with shortname set, just in + // case somebody puts his viminfo file in an 8.3 filesystem. for (;;) { int next_char = 'z'; @@ -3098,30 +3100,24 @@ write_viminfo(char_u *file, int forceit) if (tempname == NULL) // out of memory break; - /* - * Try a series of names. Change one character, just before - * the extension. This should also work for an 8.3 - * file name, when after adding the extension it still is - * the same file as the original. - */ + // Try a series of names. Change one character, just before + // the extension. This should also work for an 8.3 + // file name, when after adding the extension it still is + // the same file as the original. wp = tempname + STRLEN(tempname) - 5; if (wp < gettail(tempname)) // empty file name? wp = gettail(tempname); for (;;) { - /* - * Check if tempfile already exists. Never overwrite an - * existing file! - */ + // Check if tempfile already exists. Never overwrite an + // existing file! if (mch_stat((char *)tempname, &st_new) == 0) { #ifdef UNIX - /* - * Check if tempfile is same as original file. May happen - * when modname() gave the same file back. E.g. silly - * link, or file name-length reached. Try again with - * shortname set. - */ + // Check if tempfile is same as original file. May happen + // when modname() gave the same file back. E.g. silly + // link, or file name-length reached. Try again with + // shortname set. if (!shortname && st_new.st_dev == st_old.st_dev && st_new.st_ino == st_old.st_ino) { @@ -3199,10 +3195,8 @@ write_viminfo(char_u *file, int forceit) { stat_T tmp_st; - /* - * Make sure the original owner can read/write the tempfile and - * otherwise preserve permissions, making sure the group matches. - */ + // Make sure the original owner can read/write the tempfile and + // otherwise preserve permissions, making sure the group matches. if (mch_stat((char *)tempname, &tmp_st) >= 0) { if (st_old.st_uid != tmp_st.st_uid) @@ -3222,9 +3216,7 @@ write_viminfo(char_u *file, int forceit) #endif } - /* - * Check if the new viminfo file can be written to. - */ + // Check if the new viminfo file can be written to. if (fp_out == NULL) { semsg(_("E138: Can't write viminfo file %s!"), From 14c01f83487d5c53192297a710eda2b8a4ab17c9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 9 Oct 2019 22:53:08 +0200 Subject: [PATCH 25/67] patch 8.1.2127: the indent.c file is a bit big Problem: The indent.c file is a bit big. Solution: Move C-indent code a a new cindent.c file. Move other indent-related code to indent.c. (Yegappan Lakshmanan, closes #5031) --- Filelist | 2 + src/Make_cyg_ming.mak | 1 + src/Make_morph.mak | 1 + src/Make_mvc.mak | 4 + src/Make_vms.mms | 6 + src/Makefile | 10 + src/README.md | 3 +- src/change.c | 145 - src/cindent.c | 4133 +++++++++++++++++++++++++++ src/edit.c | 341 +-- src/evalfunc.c | 64 - src/ex_cmds.c | 215 -- src/globals.h | 2 + src/indent.c | 6132 +++++++++++------------------------------ src/misc1.c | 556 ---- src/ops.c | 107 - src/proto.h | 1 + src/proto/cindent.pro | 10 + src/proto/edit.pro | 2 +- src/proto/ex_cmds.pro | 1 - src/proto/indent.pro | 33 +- src/proto/misc1.pro | 9 - src/proto/ops.pro | 1 - src/userfunc.c | 2 +- src/version.c | 2 + 25 files changed, 5879 insertions(+), 5904 deletions(-) create mode 100644 src/cindent.c create mode 100644 src/proto/cindent.pro diff --git a/Filelist b/Filelist index dfc06245e5..92f76213af 100644 --- a/Filelist +++ b/Filelist @@ -25,6 +25,7 @@ SRC_ALL = \ src/change.c \ src/channel.c \ src/charset.c \ + src/cindent.c \ src/cmdexpand.c \ src/cmdhist.c \ src/crypt.c \ @@ -191,6 +192,7 @@ SRC_ALL = \ src/proto/change.pro \ src/proto/channel.pro \ src/proto/charset.pro \ + src/proto/cindent.pro \ src/proto/cmdexpand.pro \ src/proto/cmdhist.pro \ src/proto/crypt.pro \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 503761c54d..f04d11b188 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -712,6 +712,7 @@ OBJ = \ $(OUTDIR)/bufwrite.o \ $(OUTDIR)/change.o \ $(OUTDIR)/charset.o \ + $(OUTDIR)/cindent.o \ $(OUTDIR)/cmdexpand.o \ $(OUTDIR)/cmdhist.o \ $(OUTDIR)/crypt.o \ diff --git a/src/Make_morph.mak b/src/Make_morph.mak index 247a5788fe..bb5805c4d2 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -32,6 +32,7 @@ SRC = arabic.c \ bufwrite.c \ change.c \ charset.c \ + cindent.c \ cmdexpand.c \ cmdhist.c \ crypt.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index e853ae9b17..4fb045aff7 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -719,6 +719,7 @@ OBJ = \ $(OUTDIR)\bufwrite.obj \ $(OUTDIR)\change.obj \ $(OUTDIR)\charset.obj \ + $(OUTDIR)\cindent.obj \ $(OUTDIR)\cmdexpand.obj \ $(OUTDIR)\cmdhist.obj \ $(OUTDIR)\crypt.obj \ @@ -1464,6 +1465,8 @@ $(OUTDIR)/change.obj: $(OUTDIR) change.c $(INCL) $(OUTDIR)/charset.obj: $(OUTDIR) charset.c $(INCL) +$(OUTDIR)/cindent.obj: $(OUTDIR) cindent.c $(INCL) + $(OUTDIR)/cmdexpand.obj: $(OUTDIR) cmdexpand.c $(INCL) $(OUTDIR)/cmdhist.obj: $(OUTDIR) cmdhist.c $(INCL) @@ -1794,6 +1797,7 @@ proto.h: \ proto/bufwrite.pro \ proto/change.pro \ proto/charset.pro \ + proto/cindent.pro \ proto/cmdexpand.pro \ proto/cmdhist.pro \ proto/crypt.pro \ diff --git a/src/Make_vms.mms b/src/Make_vms.mms index 6046b5a235..3decf69116 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -318,6 +318,7 @@ SRC = \ bufwrite.c \ change.c \ charset.c \ + cindent.c \ cmdexpand.c \ cmdhist.c \ crypt.c \ @@ -420,6 +421,7 @@ OBJ = \ bufwrite.obj \ change.obj \ charset.obj \ + cindent.obj \ cmdexpand.obj \ cmdhist.obj \ crypt.obj \ @@ -700,6 +702,10 @@ charset.obj : charset.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ globals.h +cindent.obj : cindent.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h cmdexpand.obj : cmdexpand.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ diff --git a/src/Makefile b/src/Makefile index cae0e5ff9e..f169548556 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1585,6 +1585,7 @@ BASIC_SRC = \ buffer.c \ change.c \ charset.c \ + cindent.c \ cmdexpand.c \ cmdhist.c \ crypt.c \ @@ -1725,6 +1726,7 @@ OBJ_COMMON = \ objects/change.o \ objects/blob.o \ objects/blowfish.o \ + objects/cindent.o \ objects/cmdexpand.o \ objects/cmdhist.o \ objects/crypt.o \ @@ -1878,6 +1880,7 @@ PRO_AUTO = \ buffer.pro \ change.pro \ charset.pro \ + cindent.pro \ cmdexpand.pro \ cmdhist.pro \ crypt.pro \ @@ -3081,6 +3084,9 @@ objects/change.o: change.c objects/charset.o: charset.c $(CCC) -o $@ charset.c +objects/cindent.o: cindent.c + $(CCC) -o $@ cindent.c + objects/cmdexpand.o: cmdexpand.c $(CCC) -o $@ cmdexpand.c @@ -3621,6 +3627,10 @@ objects/charset.o: charset.c vim.h protodef.h auto/config.h feature.h os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h +objects/cindent.o: cindent.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h objects/cmdexpand.o: cmdexpand.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ diff --git a/src/README.md b/src/README.md index 8d20140851..5d17308425 100644 --- a/src/README.md +++ b/src/README.md @@ -29,6 +29,7 @@ blob.c | blob data type buffer.c | manipulating buffers (loaded files) bufwrite.c | writing a buffer to file change.c | handling changes to text +cindent.c | C and Lisp indentation cmdexpand.c | command-line completion cmdhist.c | command-line history debugger.c | vim script debugger @@ -46,7 +47,7 @@ findfile.c | search for files in 'path' fold.c | folding getchar.c | getting characters and key mapping highlight.c | syntax highlighting -indent.c | C and Lisp indentation +indent.c | text indentation insexpand.c | Insert mode completion mark.c | marks map.c | mapping and abbreviations diff --git a/src/change.c b/src/change.c index 0eb00c2836..caf9db0d71 100644 --- a/src/change.c +++ b/src/change.c @@ -1250,151 +1250,6 @@ del_bytes( return OK; } -/* - * Copy the indent from ptr to the current line (and fill to size) - * Leaves the cursor on the first non-blank in the line. - * Returns TRUE if the line was changed. - */ - static int -copy_indent(int size, char_u *src) -{ - char_u *p = NULL; - char_u *line = NULL; - char_u *s; - int todo; - int ind_len; - int line_len = 0; - int tab_pad; - int ind_done; - int round; -#ifdef FEAT_VARTABS - int ind_col; -#endif - - // Round 1: compute the number of characters needed for the indent - // Round 2: copy the characters. - for (round = 1; round <= 2; ++round) - { - todo = size; - ind_len = 0; - ind_done = 0; -#ifdef FEAT_VARTABS - ind_col = 0; -#endif - s = src; - - // Count/copy the usable portion of the source line - while (todo > 0 && VIM_ISWHITE(*s)) - { - if (*s == TAB) - { -#ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); -#else - tab_pad = (int)curbuf->b_p_ts - - (ind_done % (int)curbuf->b_p_ts); -#endif - // Stop if this tab will overshoot the target - if (todo < tab_pad) - break; - todo -= tab_pad; - ind_done += tab_pad; -#ifdef FEAT_VARTABS - ind_col += tab_pad; -#endif - } - else - { - --todo; - ++ind_done; -#ifdef FEAT_VARTABS - ++ind_col; -#endif - } - ++ind_len; - if (p != NULL) - *p++ = *s; - ++s; - } - - // Fill to next tabstop with a tab, if possible -#ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); -#else - tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); -#endif - if (todo >= tab_pad && !curbuf->b_p_et) - { - todo -= tab_pad; - ++ind_len; -#ifdef FEAT_VARTABS - ind_col += tab_pad; -#endif - if (p != NULL) - *p++ = TAB; - } - - // Add tabs required for indent - if (!curbuf->b_p_et) - { -#ifdef FEAT_VARTABS - for (;;) - { - tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, - curbuf->b_p_vts_array); - if (todo < tab_pad) - break; - todo -= tab_pad; - ++ind_len; - ind_col += tab_pad; - if (p != NULL) - *p++ = TAB; - } -#else - while (todo >= (int)curbuf->b_p_ts) - { - todo -= (int)curbuf->b_p_ts; - ++ind_len; - if (p != NULL) - *p++ = TAB; - } -#endif - } - - // Count/add spaces required for indent - while (todo > 0) - { - --todo; - ++ind_len; - if (p != NULL) - *p++ = ' '; - } - - if (p == NULL) - { - // Allocate memory for the result: the copied indent, new indent - // and the rest of the line. - line_len = (int)STRLEN(ml_get_curline()) + 1; - line = alloc(ind_len + line_len); - if (line == NULL) - return FALSE; - p = line; - } - } - - // Append the original line - mch_memmove(p, ml_get_curline(), (size_t)line_len); - - // Replace the line - ml_replace(curwin->w_cursor.lnum, line, FALSE); - - // Put the cursor after the indent. - curwin->w_cursor.col = ind_len; - return TRUE; -} - /* * open_line: Add a new line below or above the current line. * diff --git a/src/cindent.c b/src/cindent.c new file mode 100644 index 0000000000..dd416720a9 --- /dev/null +++ b/src/cindent.c @@ -0,0 +1,4133 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * cindent.c: C indentation related functions + * + * Many of C-indenting functions originally come from Eric Fischer. + * + * Below "XXX" means that this function may unlock the current line. + */ + +#include "vim.h" + +// values for the "lookfor" state +#define LOOKFOR_INITIAL 0 +#define LOOKFOR_IF 1 +#define LOOKFOR_DO 2 +#define LOOKFOR_CASE 3 +#define LOOKFOR_ANY 4 +#define LOOKFOR_TERM 5 +#define LOOKFOR_UNTERM 6 +#define LOOKFOR_SCOPEDECL 7 +#define LOOKFOR_NOBREAK 8 +#define LOOKFOR_CPP_BASECLASS 9 +#define LOOKFOR_ENUM_OR_INIT 10 +#define LOOKFOR_JS_KEY 11 +#define LOOKFOR_COMMA 12 + +#if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT) +/* + * Return TRUE if the string "line" starts with a word from 'cinwords'. + */ + int +cin_is_cinword(char_u *line) +{ + char_u *cinw; + char_u *cinw_buf; + int cinw_len; + int retval = FALSE; + int len; + + cinw_len = (int)STRLEN(curbuf->b_p_cinw) + 1; + cinw_buf = alloc(cinw_len); + if (cinw_buf != NULL) + { + line = skipwhite(line); + for (cinw = curbuf->b_p_cinw; *cinw; ) + { + len = copy_option_part(&cinw, cinw_buf, cinw_len, ","); + if (STRNCMP(line, cinw_buf, len) == 0 + && (!vim_iswordc(line[len]) || !vim_iswordc(line[len - 1]))) + { + retval = TRUE; + break; + } + } + vim_free(cinw_buf); + } + return retval; +} +#endif + +#if defined(FEAT_CINDENT) || defined(FEAT_SYN_HL) + +/* + * Skip to the end of a "string" and a 'c' character. + * If there is no string or character, return argument unmodified. + */ + static char_u * +skip_string(char_u *p) +{ + int i; + + // We loop, because strings may be concatenated: "date""time". + for ( ; ; ++p) + { + if (p[0] == '\'') // 'c' or '\n' or '\000' + { + if (!p[1]) // ' at end of line + break; + i = 2; + if (p[1] == '\\') // '\n' or '\000' + { + ++i; + while (vim_isdigit(p[i - 1])) // '\000' + ++i; + } + if (p[i] == '\'') // check for trailing ' + { + p += i; + continue; + } + } + else if (p[0] == '"') // start of string + { + for (++p; p[0]; ++p) + { + if (p[0] == '\\' && p[1] != NUL) + ++p; + else if (p[0] == '"') // end of string + break; + } + if (p[0] == '"') + continue; // continue for another string + } + else if (p[0] == 'R' && p[1] == '"') + { + // Raw string: R"[delim](...)[delim]" + char_u *delim = p + 2; + char_u *paren = vim_strchr(delim, '('); + + if (paren != NULL) + { + size_t delim_len = paren - delim; + + for (p += 3; *p; ++p) + if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 + && p[delim_len + 1] == '"') + { + p += delim_len + 1; + break; + } + if (p[0] == '"') + continue; // continue for another string + } + } + break; // no string found + } + if (!*p) + --p; // backup from NUL + return p; +} + +/* + * Find the start of a comment, not knowing if we are in a comment right now. + * Search starts at w_cursor.lnum and goes backwards. + * Return NULL when not inside a comment. + */ + static pos_T * +ind_find_start_comment(void) // XXX +{ + return find_start_comment(curbuf->b_ind_maxcomment); +} + + pos_T * +find_start_comment(int ind_maxcomment) // XXX +{ + pos_T *pos; + char_u *line; + char_u *p; + int cur_maxcomment = ind_maxcomment; + + for (;;) + { + pos = findmatchlimit(NULL, '*', FM_BACKWARD, cur_maxcomment); + if (pos == NULL) + break; + + // Check if the comment start we found is inside a string. + // If it is then restrict the search to below this line and try again. + line = ml_get(pos->lnum); + for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p) + p = skip_string(p); + if ((colnr_T)(p - line) <= pos->col) + break; + cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; + if (cur_maxcomment <= 0) + { + pos = NULL; + break; + } + } + return pos; +} + +/* + * Find the start of a raw string, not knowing if we are in one right now. + * Search starts at w_cursor.lnum and goes backwards. + * Return NULL when not inside a raw string. + */ + static pos_T * +find_start_rawstring(int ind_maxcomment) // XXX +{ + pos_T *pos; + char_u *line; + char_u *p; + int cur_maxcomment = ind_maxcomment; + + for (;;) + { + pos = findmatchlimit(NULL, 'R', FM_BACKWARD, cur_maxcomment); + if (pos == NULL) + break; + + // Check if the raw string start we found is inside a string. + // If it is then restrict the search to below this line and try again. + line = ml_get(pos->lnum); + for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p) + p = skip_string(p); + if ((colnr_T)(p - line) <= pos->col) + break; + cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; + if (cur_maxcomment <= 0) + { + pos = NULL; + break; + } + } + return pos; +} + +/* + * Find the start of a comment or raw string, not knowing if we are in a + * comment or raw string right now. + * Search starts at w_cursor.lnum and goes backwards. + * If is_raw is given and returns start of raw_string, sets it to true. + * Return NULL when not inside a comment or raw string. + * "CORS" -> Comment Or Raw String + */ + static pos_T * +ind_find_start_CORS(linenr_T *is_raw) // XXX +{ + static pos_T comment_pos_copy; + pos_T *comment_pos; + pos_T *rs_pos; + + comment_pos = find_start_comment(curbuf->b_ind_maxcomment); + if (comment_pos != NULL) + { + // Need to make a copy of the static pos in findmatchlimit(), + // calling find_start_rawstring() may change it. + comment_pos_copy = *comment_pos; + comment_pos = &comment_pos_copy; + } + rs_pos = find_start_rawstring(curbuf->b_ind_maxcomment); + + // If comment_pos is before rs_pos the raw string is inside the comment. + // If rs_pos is before comment_pos the comment is inside the raw string. + if (comment_pos == NULL || (rs_pos != NULL + && LT_POS(*rs_pos, *comment_pos))) + { + if (is_raw != NULL && rs_pos != NULL) + *is_raw = rs_pos->lnum; + return rs_pos; + } + return comment_pos; +} +#endif // FEAT_CINDENT || FEAT_SYN_HL + +#if defined(FEAT_CINDENT) || defined(PROTO) + +/* + * Return TRUE if C-indenting is on. + */ + int +cindent_on(void) +{ + return (!p_paste && (curbuf->b_p_cin +# ifdef FEAT_EVAL + || *curbuf->b_p_inde != NUL +# endif + )); +} + +// Find result cache for cpp_baseclass +typedef struct { + int found; + lpos_T lpos; +} cpp_baseclass_cache_T; + +/* + * Skip over white space and C comments within the line. + * Also skip over Perl/shell comments if desired. + */ + static char_u * +cin_skipcomment(char_u *s) +{ + while (*s) + { + char_u *prev_s = s; + + s = skipwhite(s); + + // Perl/shell # comment comment continues until eol. Require a space + // before # to avoid recognizing $#array. + if (curbuf->b_ind_hash_comment != 0 && s != prev_s && *s == '#') + { + s += STRLEN(s); + break; + } + if (*s != '/') + break; + ++s; + if (*s == '/') // slash-slash comment continues till eol + { + s += STRLEN(s); + break; + } + if (*s != '*') + break; + for (++s; *s; ++s) // skip slash-star comment + if (s[0] == '*' && s[1] == '/') + { + s += 2; + break; + } + } + return s; +} + +/* + * Return TRUE if there is no code at *s. White space and comments are + * not considered code. + */ + static int +cin_nocode(char_u *s) +{ + return *cin_skipcomment(s) == NUL; +} + +/* + * Recognize the start of a C or C++ comment. + */ + static int +cin_iscomment(char_u *p) +{ + return (p[0] == '/' && (p[1] == '*' || p[1] == '/')); +} + +/* + * Recognize the start of a "//" comment. + */ + static int +cin_islinecomment(char_u *p) +{ + return (p[0] == '/' && p[1] == '/'); +} + +/* + * Check previous lines for a "//" line comment, skipping over blank lines. + */ + static pos_T * +find_line_comment(void) // XXX +{ + static pos_T pos; + char_u *line; + char_u *p; + + pos = curwin->w_cursor; + while (--pos.lnum > 0) + { + line = ml_get(pos.lnum); + p = skipwhite(line); + if (cin_islinecomment(p)) + { + pos.col = (int)(p - line); + return &pos; + } + if (*p != NUL) + break; + } + return NULL; +} + +/* + * Return TRUE if "text" starts with "key:". + */ + static int +cin_has_js_key(char_u *text) +{ + char_u *s = skipwhite(text); + int quote = -1; + + if (*s == '\'' || *s == '"') + { + // can be 'key': or "key": + quote = *s; + ++s; + } + if (!vim_isIDc(*s)) // need at least one ID character + return FALSE; + + while (vim_isIDc(*s)) + ++s; + if (*s == quote) + ++s; + + s = cin_skipcomment(s); + + // "::" is not a label, it's C++ + return (*s == ':' && s[1] != ':'); +} + +/* + * Check if string matches "label:"; move to character after ':' if true. + * "*s" must point to the start of the label, if there is one. + */ + static int +cin_islabel_skip(char_u **s) +{ + if (!vim_isIDc(**s)) // need at least one ID character + return FALSE; + + while (vim_isIDc(**s)) + (*s)++; + + *s = cin_skipcomment(*s); + + // "::" is not a label, it's C++ + return (**s == ':' && *++*s != ':'); +} + +/* + * Recognize a "public/private/protected" scope declaration label. + */ + static int +cin_isscopedecl(char_u *s) +{ + int i; + + s = cin_skipcomment(s); + if (STRNCMP(s, "public", 6) == 0) + i = 6; + else if (STRNCMP(s, "protected", 9) == 0) + i = 9; + else if (STRNCMP(s, "private", 7) == 0) + i = 7; + else + return FALSE; + return (*(s = cin_skipcomment(s + i)) == ':' && s[1] != ':'); +} + +/* + * Recognize a preprocessor statement: Any line that starts with '#'. + */ + static int +cin_ispreproc(char_u *s) +{ + if (*skipwhite(s) == '#') + return TRUE; + return FALSE; +} + +/* + * Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a + * continuation line of a preprocessor statement. Decrease "*lnump" to the + * start and return the line in "*pp". + * Put the amount of indent in "*amount". + */ + static int +cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount) +{ + char_u *line = *pp; + linenr_T lnum = *lnump; + int retval = FALSE; + int candidate_amount = *amount; + + if (*line != NUL && line[STRLEN(line) - 1] == '\\') + candidate_amount = get_indent_lnum(lnum); + + for (;;) + { + if (cin_ispreproc(line)) + { + retval = TRUE; + *lnump = lnum; + break; + } + if (lnum == 1) + break; + line = ml_get(--lnum); + if (*line == NUL || line[STRLEN(line) - 1] != '\\') + break; + } + + if (lnum != *lnump) + *pp = ml_get(*lnump); + if (retval) + *amount = candidate_amount; + return retval; +} + + static int +cin_iselse( + char_u *p) +{ + if (*p == '}') // accept "} else" + p = cin_skipcomment(p + 1); + return (STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4])); +} + +/* + * Recognize a line that starts with '{' or '}', or ends with ';', ',', '{' or + * '}'. + * Don't consider "} else" a terminated line. + * If a line begins with an "else", only consider it terminated if no unmatched + * opening braces follow (handle "else { foo();" correctly). + * Return the character terminating the line (ending char's have precedence if + * both apply in order to determine initializations). + */ + static int +cin_isterminated( + char_u *s, + int incl_open, // include '{' at the end as terminator + int incl_comma) // recognize a trailing comma +{ + char_u found_start = 0; + unsigned n_open = 0; + int is_else = FALSE; + + s = cin_skipcomment(s); + + if (*s == '{' || (*s == '}' && !cin_iselse(s))) + found_start = *s; + + if (!found_start) + is_else = cin_iselse(s); + + while (*s) + { + // skip over comments, "" strings and 'c'haracters + s = skip_string(cin_skipcomment(s)); + if (*s == '}' && n_open > 0) + --n_open; + if ((!is_else || n_open == 0) + && (*s == ';' || *s == '}' || (incl_comma && *s == ',')) + && cin_nocode(s + 1)) + return *s; + else if (*s == '{') + { + if (incl_open && cin_nocode(s + 1)) + return *s; + else + ++n_open; + } + + if (*s) + s++; + } + return found_start; +} + +/* + * Return TRUE when "s" starts with "word" and then a non-ID character. + */ + static int +cin_starts_with(char_u *s, char *word) +{ + int l = (int)STRLEN(word); + + return (STRNCMP(s, word, l) == 0 && !vim_isIDc(s[l])); +} + +/* + * Recognize a "default" switch label. + */ + static int +cin_isdefault(char_u *s) +{ + return (STRNCMP(s, "default", 7) == 0 + && *(s = cin_skipcomment(s + 7)) == ':' + && s[1] != ':'); +} + +/* + * Recognize a switch label: "case .*:" or "default:". + */ + static int +cin_iscase( + char_u *s, + int strict) // Allow relaxed check of case statement for JS +{ + s = cin_skipcomment(s); + if (cin_starts_with(s, "case")) + { + for (s += 4; *s; ++s) + { + s = cin_skipcomment(s); + if (*s == ':') + { + if (s[1] == ':') // skip over "::" for C++ + ++s; + else + return TRUE; + } + if (*s == '\'' && s[1] && s[2] == '\'') + s += 2; // skip over ':' + else if (*s == '/' && (s[1] == '*' || s[1] == '/')) + return FALSE; // stop at comment + else if (*s == '"') + { + // JS etc. + if (strict) + return FALSE; // stop at string + else + return TRUE; + } + } + return FALSE; + } + + if (cin_isdefault(s)) + return TRUE; + return FALSE; +} + +/* + * Recognize a label: "label:". + * Note: curwin->w_cursor must be where we are looking for the label. + */ + static int +cin_islabel(void) // XXX +{ + char_u *s; + + s = cin_skipcomment(ml_get_curline()); + + // Exclude "default" from labels, since it should be indented + // like a switch label. Same for C++ scope declarations. + if (cin_isdefault(s)) + return FALSE; + if (cin_isscopedecl(s)) + return FALSE; + + if (cin_islabel_skip(&s)) + { + // Only accept a label if the previous line is terminated or is a case + // label. + pos_T cursor_save; + pos_T *trypos; + char_u *line; + + cursor_save = curwin->w_cursor; + while (curwin->w_cursor.lnum > 1) + { + --curwin->w_cursor.lnum; + + // If we're in a comment or raw string now, skip to the start of + // it. + curwin->w_cursor.col = 0; + if ((trypos = ind_find_start_CORS(NULL)) != NULL) // XXX + curwin->w_cursor = *trypos; + + line = ml_get_curline(); + if (cin_ispreproc(line)) // ignore #defines, #if, etc. + continue; + if (*(line = cin_skipcomment(line)) == NUL) + continue; + + curwin->w_cursor = cursor_save; + if (cin_isterminated(line, TRUE, FALSE) + || cin_isscopedecl(line) + || cin_iscase(line, TRUE) + || (cin_islabel_skip(&line) && cin_nocode(line))) + return TRUE; + return FALSE; + } + curwin->w_cursor = cursor_save; + return TRUE; // label at start of file??? + } + return FALSE; +} + +/* + * Return TRUE if string "s" ends with the string "find", possibly followed by + * white space and comments. Skip strings and comments. + * Ignore "ignore" after "find" if it's not NULL. + */ + static int +cin_ends_in(char_u *s, char_u *find, char_u *ignore) +{ + char_u *p = s; + char_u *r; + int len = (int)STRLEN(find); + + while (*p != NUL) + { + p = cin_skipcomment(p); + if (STRNCMP(p, find, len) == 0) + { + r = skipwhite(p + len); + if (ignore != NULL && STRNCMP(r, ignore, STRLEN(ignore)) == 0) + r = skipwhite(r + STRLEN(ignore)); + if (cin_nocode(r)) + return TRUE; + } + if (*p != NUL) + ++p; + } + return FALSE; +} + +/* + * Recognize structure initialization and enumerations: + * "[typedef] [static|public|protected|private] enum" + * "[typedef] [static|public|protected|private] = {" + */ + static int +cin_isinit(void) +{ + char_u *s; + static char *skip[] = {"static", "public", "protected", "private"}; + + s = cin_skipcomment(ml_get_curline()); + + if (cin_starts_with(s, "typedef")) + s = cin_skipcomment(s + 7); + + for (;;) + { + int i, l; + + for (i = 0; i < (int)(sizeof(skip) / sizeof(char *)); ++i) + { + l = (int)strlen(skip[i]); + if (cin_starts_with(s, skip[i])) + { + s = cin_skipcomment(s + l); + l = 0; + break; + } + } + if (l != 0) + break; + } + + if (cin_starts_with(s, "enum")) + return TRUE; + + if (cin_ends_in(s, (char_u *)"=", (char_u *)"{")) + return TRUE; + + return FALSE; +} + +// Maximum number of lines to search back for a "namespace" line. +#define FIND_NAMESPACE_LIM 20 + +/* + * Recognize a "namespace" scope declaration. + */ + static int +cin_is_cpp_namespace(char_u *s) +{ + char_u *p; + int has_name = FALSE; + int has_name_start = FALSE; + + s = cin_skipcomment(s); + if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) + { + p = cin_skipcomment(skipwhite(s + 9)); + while (*p != NUL) + { + if (VIM_ISWHITE(*p)) + { + has_name = TRUE; // found end of a name + p = cin_skipcomment(skipwhite(p)); + } + else if (*p == '{') + { + break; + } + else if (vim_iswordc(*p)) + { + has_name_start = TRUE; + if (has_name) + return FALSE; // word character after skipping past name + ++p; + } + else if (p[0] == ':' && p[1] == ':' && vim_iswordc(p[2])) + { + if (!has_name_start || has_name) + return FALSE; + // C++ 17 nested namespace + p += 3; + } + else + { + return FALSE; + } + } + return TRUE; + } + return FALSE; +} + +/* + * Recognize a `extern "C"` or `extern "C++"` linkage specifications. + */ + static int +cin_is_cpp_extern_c(char_u *s) +{ + char_u *p; + int has_string_literal = FALSE; + + s = cin_skipcomment(s); + if (STRNCMP(s, "extern", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6]))) + { + p = cin_skipcomment(skipwhite(s + 6)); + while (*p != NUL) + { + if (VIM_ISWHITE(*p)) + { + p = cin_skipcomment(skipwhite(p)); + } + else if (*p == '{') + { + break; + } + else if (p[0] == '"' && p[1] == 'C' && p[2] == '"') + { + if (has_string_literal) + return FALSE; + has_string_literal = TRUE; + p += 3; + } + else if (p[0] == '"' && p[1] == 'C' && p[2] == '+' && p[3] == '+' + && p[4] == '"') + { + if (has_string_literal) + return FALSE; + has_string_literal = TRUE; + p += 5; + } + else + { + return FALSE; + } + } + return has_string_literal ? TRUE : FALSE; + } + return FALSE; +} + +/* + * Return a pointer to the first non-empty non-comment character after a ':'. + * Return NULL if not found. + * case 234: a = b; + * ^ + */ + static char_u * +after_label(char_u *l) +{ + for ( ; *l; ++l) + { + if (*l == ':') + { + if (l[1] == ':') // skip over "::" for C++ + ++l; + else if (!cin_iscase(l + 1, FALSE)) + break; + } + else if (*l == '\'' && l[1] && l[2] == '\'') + l += 2; // skip over 'x' + } + if (*l == NUL) + return NULL; + l = cin_skipcomment(l + 1); + if (*l == NUL) + return NULL; + return l; +} + +/* + * Get indent of line "lnum", skipping a label. + * Return 0 if there is nothing after the label. + */ + static int +get_indent_nolabel (linenr_T lnum) // XXX +{ + char_u *l; + pos_T fp; + colnr_T col; + char_u *p; + + l = ml_get(lnum); + p = after_label(l); + if (p == NULL) + return 0; + + fp.col = (colnr_T)(p - l); + fp.lnum = lnum; + getvcol(curwin, &fp, &col, NULL, NULL); + return (int)col; +} + +/* + * Find indent for line "lnum", ignoring any case or jump label. + * Also return a pointer to the text (after the label) in "pp". + * label: if (asdf && asdfasdf) + * ^ + */ + static int +skip_label(linenr_T lnum, char_u **pp) +{ + char_u *l; + int amount; + pos_T cursor_save; + + cursor_save = curwin->w_cursor; + curwin->w_cursor.lnum = lnum; + l = ml_get_curline(); + // XXX + if (cin_iscase(l, FALSE) || cin_isscopedecl(l) || cin_islabel()) + { + amount = get_indent_nolabel(lnum); + l = after_label(ml_get_curline()); + if (l == NULL) // just in case + l = ml_get_curline(); + } + else + { + amount = get_indent(); + l = ml_get_curline(); + } + *pp = l; + + curwin->w_cursor = cursor_save; + return amount; +} + +/* + * Return the indent of the first variable name after a type in a declaration. + * int a, indent of "a" + * static struct foo b, indent of "b" + * enum bla c, indent of "c" + * Returns zero when it doesn't look like a declaration. + */ + static int +cin_first_id_amount(void) +{ + char_u *line, *p, *s; + int len; + pos_T fp; + colnr_T col; + + line = ml_get_curline(); + p = skipwhite(line); + len = (int)(skiptowhite(p) - p); + if (len == 6 && STRNCMP(p, "static", 6) == 0) + { + p = skipwhite(p + 6); + len = (int)(skiptowhite(p) - p); + } + if (len == 6 && STRNCMP(p, "struct", 6) == 0) + p = skipwhite(p + 6); + else if (len == 4 && STRNCMP(p, "enum", 4) == 0) + p = skipwhite(p + 4); + else if ((len == 8 && STRNCMP(p, "unsigned", 8) == 0) + || (len == 6 && STRNCMP(p, "signed", 6) == 0)) + { + s = skipwhite(p + len); + if ((STRNCMP(s, "int", 3) == 0 && VIM_ISWHITE(s[3])) + || (STRNCMP(s, "long", 4) == 0 && VIM_ISWHITE(s[4])) + || (STRNCMP(s, "short", 5) == 0 && VIM_ISWHITE(s[5])) + || (STRNCMP(s, "char", 4) == 0 && VIM_ISWHITE(s[4]))) + p = s; + } + for (len = 0; vim_isIDc(p[len]); ++len) + ; + if (len == 0 || !VIM_ISWHITE(p[len]) || cin_nocode(p)) + return 0; + + p = skipwhite(p + len); + fp.lnum = curwin->w_cursor.lnum; + fp.col = (colnr_T)(p - line); + getvcol(curwin, &fp, &col, NULL, NULL); + return (int)col; +} + +/* + * Return the indent of the first non-blank after an equal sign. + * char *foo = "here"; + * Return zero if no (useful) equal sign found. + * Return -1 if the line above "lnum" ends in a backslash. + * foo = "asdf\ + * asdf\ + * here"; + */ + static int +cin_get_equal_amount(linenr_T lnum) +{ + char_u *line; + char_u *s; + colnr_T col; + pos_T fp; + + if (lnum > 1) + { + line = ml_get(lnum - 1); + if (*line != NUL && line[STRLEN(line) - 1] == '\\') + return -1; + } + + line = s = ml_get(lnum); + while (*s != NUL && vim_strchr((char_u *)"=;{}\"'", *s) == NULL) + { + if (cin_iscomment(s)) // ignore comments + s = cin_skipcomment(s); + else + ++s; + } + if (*s != '=') + return 0; + + s = skipwhite(s + 1); + if (cin_nocode(s)) + return 0; + + if (*s == '"') // nice alignment for continued strings + ++s; + + fp.lnum = lnum; + fp.col = (colnr_T)(s - line); + getvcol(curwin, &fp, &col, NULL, NULL); + return (int)col; +} + +/* + * Skip strings, chars and comments until at or past "trypos". + * Return the column found. + */ + static int +cin_skip2pos(pos_T *trypos) +{ + char_u *line; + char_u *p; + char_u *new_p; + + p = line = ml_get(trypos->lnum); + while (*p && (colnr_T)(p - line) < trypos->col) + { + if (cin_iscomment(p)) + p = cin_skipcomment(p); + else + { + new_p = skip_string(p); + if (new_p == p) + ++p; + else + p = new_p; + } + } + return (int)(p - line); +} + + static pos_T * +find_match_char(int c, int ind_maxparen) // XXX +{ + pos_T cursor_save; + pos_T *trypos; + static pos_T pos_copy; + int ind_maxp_wk; + + cursor_save = curwin->w_cursor; + ind_maxp_wk = ind_maxparen; +retry: + if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) + { + // check if the ( is in a // comment + if ((colnr_T)cin_skip2pos(trypos) > trypos->col) + { + ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos->lnum); + if (ind_maxp_wk > 0) + { + curwin->w_cursor = *trypos; + curwin->w_cursor.col = 0; // XXX + goto retry; + } + trypos = NULL; + } + else + { + pos_T *trypos_wk; + + pos_copy = *trypos; // copy trypos, findmatch will change it + trypos = &pos_copy; + curwin->w_cursor = *trypos; + if ((trypos_wk = ind_find_start_CORS(NULL)) != NULL) // XXX + { + ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum + - trypos_wk->lnum); + if (ind_maxp_wk > 0) + { + curwin->w_cursor = *trypos_wk; + goto retry; + } + trypos = NULL; + } + } + } + curwin->w_cursor = cursor_save; + return trypos; +} + +/* + * Find the matching '(', ignoring it if it is in a comment. + * Return NULL if no match found. + */ + static pos_T * +find_match_paren(int ind_maxparen) // XXX +{ + return find_match_char('(', ind_maxparen); +} + +/* + * Set w_cursor.col to the column number of the last unmatched ')' or '{' in + * line "l". "l" must point to the start of the line. + */ + static int +find_last_paren(char_u *l, int start, int end) +{ + int i; + int retval = FALSE; + int open_count = 0; + + curwin->w_cursor.col = 0; // default is start of line + + for (i = 0; l[i] != NUL; i++) + { + i = (int)(cin_skipcomment(l + i) - l); // ignore parens in comments + i = (int)(skip_string(l + i) - l); // ignore parens in quotes + if (l[i] == start) + ++open_count; + else if (l[i] == end) + { + if (open_count > 0) + --open_count; + else + { + curwin->w_cursor.col = i; + retval = TRUE; + } + } + } + return retval; +} + +/* + * Recognize the basic picture of a function declaration -- it needs to + * have an open paren somewhere and a close paren at the end of the line and + * no semicolons anywhere. + * When a line ends in a comma we continue looking in the next line. + * "sp" points to a string with the line. When looking at other lines it must + * be restored to the line. When it's NULL fetch lines here. + * "first_lnum" is where we start looking. + * "min_lnum" is the line before which we will not be looking. + */ + static int +cin_isfuncdecl( + char_u **sp, + linenr_T first_lnum, + linenr_T min_lnum) +{ + char_u *s; + linenr_T lnum = first_lnum; + linenr_T save_lnum = curwin->w_cursor.lnum; + int retval = FALSE; + pos_T *trypos; + int just_started = TRUE; + + if (sp == NULL) + s = ml_get(lnum); + else + s = *sp; + + curwin->w_cursor.lnum = lnum; + if (find_last_paren(s, '(', ')') + && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) + { + lnum = trypos->lnum; + if (lnum < min_lnum) + { + curwin->w_cursor.lnum = save_lnum; + return FALSE; + } + + s = ml_get(lnum); + } + curwin->w_cursor.lnum = save_lnum; + + // Ignore line starting with #. + if (cin_ispreproc(s)) + return FALSE; + + while (*s && *s != '(' && *s != ';' && *s != '\'' && *s != '"') + { + if (cin_iscomment(s)) // ignore comments + s = cin_skipcomment(s); + else if (*s == ':') + { + if (*(s + 1) == ':') + s += 2; + else + // To avoid a mistake in the following situation: + // A::A(int a, int b) + // : a(0) // <--not a function decl + // , b(0) + // {... + return FALSE; + } + else + ++s; + } + if (*s != '(') + return FALSE; // ';', ' or " before any () or no '(' + + while (*s && *s != ';' && *s != '\'' && *s != '"') + { + if (*s == ')' && cin_nocode(s + 1)) + { + // ')' at the end: may have found a match + // Check for he previous line not to end in a backslash: + // #if defined(x) && {backslash} + // defined(y) + lnum = first_lnum - 1; + s = ml_get(lnum); + if (*s == NUL || s[STRLEN(s) - 1] != '\\') + retval = TRUE; + goto done; + } + if ((*s == ',' && cin_nocode(s + 1)) || s[1] == NUL || cin_nocode(s)) + { + int comma = (*s == ','); + + // ',' at the end: continue looking in the next line. + // At the end: check for ',' in the next line, for this style: + // func(arg1 + // , arg2) + for (;;) + { + if (lnum >= curbuf->b_ml.ml_line_count) + break; + s = ml_get(++lnum); + if (!cin_ispreproc(s)) + break; + } + if (lnum >= curbuf->b_ml.ml_line_count) + break; + // Require a comma at end of the line or a comma or ')' at the + // start of next line. + s = skipwhite(s); + if (!just_started && (!comma && *s != ',' && *s != ')')) + break; + just_started = FALSE; + } + else if (cin_iscomment(s)) // ignore comments + s = cin_skipcomment(s); + else + { + ++s; + just_started = FALSE; + } + } + +done: + if (lnum != first_lnum && sp != NULL) + *sp = ml_get(first_lnum); + + return retval; +} + + static int +cin_isif(char_u *p) +{ + return (STRNCMP(p, "if", 2) == 0 && !vim_isIDc(p[2])); +} + + static int +cin_isdo(char_u *p) +{ + return (STRNCMP(p, "do", 2) == 0 && !vim_isIDc(p[2])); +} + +/* + * Check if this is a "while" that should have a matching "do". + * We only accept a "while (condition) ;", with only white space between the + * ')' and ';'. The condition may be spread over several lines. + */ + static int +cin_iswhileofdo (char_u *p, linenr_T lnum) // XXX +{ + pos_T cursor_save; + pos_T *trypos; + int retval = FALSE; + + p = cin_skipcomment(p); + if (*p == '}') // accept "} while (cond);" + p = cin_skipcomment(p + 1); + if (cin_starts_with(p, "while")) + { + cursor_save = curwin->w_cursor; + curwin->w_cursor.lnum = lnum; + curwin->w_cursor.col = 0; + p = ml_get_curline(); + while (*p && *p != 'w') // skip any '}', until the 'w' of the "while" + { + ++p; + ++curwin->w_cursor.col; + } + if ((trypos = findmatchlimit(NULL, 0, 0, + curbuf->b_ind_maxparen)) != NULL + && *cin_skipcomment(ml_get_pos(trypos) + 1) == ';') + retval = TRUE; + curwin->w_cursor = cursor_save; + } + return retval; +} + +/* + * Check whether in "p" there is an "if", "for" or "while" before "*poffset". + * Return 0 if there is none. + * Otherwise return !0 and update "*poffset" to point to the place where the + * string was found. + */ + static int +cin_is_if_for_while_before_offset(char_u *line, int *poffset) +{ + int offset = *poffset; + + if (offset-- < 2) + return 0; + while (offset > 2 && VIM_ISWHITE(line[offset])) + --offset; + + offset -= 1; + if (!STRNCMP(line + offset, "if", 2)) + goto probablyFound; + + if (offset >= 1) + { + offset -= 1; + if (!STRNCMP(line + offset, "for", 3)) + goto probablyFound; + + if (offset >= 2) + { + offset -= 2; + if (!STRNCMP(line + offset, "while", 5)) + goto probablyFound; + } + } + return 0; + +probablyFound: + if (!offset || !vim_isIDc(line[offset - 1])) + { + *poffset = offset; + return 1; + } + return 0; +} + +/* + * Return TRUE if we are at the end of a do-while. + * do + * nothing; + * while (foo + * && bar); <-- here + * Adjust the cursor to the line with "while". + */ + static int +cin_iswhileofdo_end(int terminated) +{ + char_u *line; + char_u *p; + char_u *s; + pos_T *trypos; + int i; + + if (terminated != ';') // there must be a ';' at the end + return FALSE; + + p = line = ml_get_curline(); + while (*p != NUL) + { + p = cin_skipcomment(p); + if (*p == ')') + { + s = skipwhite(p + 1); + if (*s == ';' && cin_nocode(s + 1)) + { + // Found ");" at end of the line, now check there is "while" + // before the matching '('. XXX + i = (int)(p - line); + curwin->w_cursor.col = i; + trypos = find_match_paren(curbuf->b_ind_maxparen); + if (trypos != NULL) + { + s = cin_skipcomment(ml_get(trypos->lnum)); + if (*s == '}') // accept "} while (cond);" + s = cin_skipcomment(s + 1); + if (cin_starts_with(s, "while")) + { + curwin->w_cursor.lnum = trypos->lnum; + return TRUE; + } + } + + // Searching may have made "line" invalid, get it again. + line = ml_get_curline(); + p = line + i; + } + } + if (*p != NUL) + ++p; + } + return FALSE; +} + + static int +cin_isbreak(char_u *p) +{ + return (STRNCMP(p, "break", 5) == 0 && !vim_isIDc(p[5])); +} + +/* + * Find the position of a C++ base-class declaration or + * constructor-initialization. eg: + * + * class MyClass : + * baseClass <-- here + * class MyClass : public baseClass, + * anotherBaseClass <-- here (should probably lineup ??) + * MyClass::MyClass(...) : + * baseClass(...) <-- here (constructor-initialization) + * + * This is a lot of guessing. Watch out for "cond ? func() : foo". + */ + static int +cin_is_cpp_baseclass( + cpp_baseclass_cache_T *cached) // input and output +{ + lpos_T *pos = &cached->lpos; // find position + char_u *s; + int class_or_struct, lookfor_ctor_init, cpp_base_class; + linenr_T lnum = curwin->w_cursor.lnum; + char_u *line = ml_get_curline(); + + if (pos->lnum <= lnum) + return cached->found; // Use the cached result + + pos->col = 0; + + s = skipwhite(line); + if (*s == '#') // skip #define FOO x ? (x) : x + return FALSE; + s = cin_skipcomment(s); + if (*s == NUL) + return FALSE; + + cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE; + + // Search for a line starting with '#', empty, ending in ';' or containing + // '{' or '}' and start below it. This handles the following situations: + // a = cond ? + // func() : + // asdf; + // func::foo() + // : something + // {} + // Foo::Foo (int one, int two) + // : something(4), + // somethingelse(3) + // {} + while (lnum > 1) + { + line = ml_get(lnum - 1); + s = skipwhite(line); + if (*s == '#' || *s == NUL) + break; + while (*s != NUL) + { + s = cin_skipcomment(s); + if (*s == '{' || *s == '}' + || (*s == ';' && cin_nocode(s + 1))) + break; + if (*s != NUL) + ++s; + } + if (*s != NUL) + break; + --lnum; + } + + pos->lnum = lnum; + line = ml_get(lnum); + s = line; + for (;;) + { + if (*s == NUL) + { + if (lnum == curwin->w_cursor.lnum) + break; + // Continue in the cursor line. + line = ml_get(++lnum); + s = line; + } + if (s == line) + { + // don't recognize "case (foo):" as a baseclass + if (cin_iscase(s, FALSE)) + break; + s = cin_skipcomment(line); + if (*s == NUL) + continue; + } + + if (s[0] == '"' || (s[0] == 'R' && s[1] == '"')) + s = skip_string(s) + 1; + else if (s[0] == ':') + { + if (s[1] == ':') + { + // skip double colon. It can't be a constructor + // initialization any more + lookfor_ctor_init = FALSE; + s = cin_skipcomment(s + 2); + } + else if (lookfor_ctor_init || class_or_struct) + { + // we have something found, that looks like the start of + // cpp-base-class-declaration or constructor-initialization + cpp_base_class = TRUE; + lookfor_ctor_init = class_or_struct = FALSE; + pos->col = 0; + s = cin_skipcomment(s + 1); + } + else + s = cin_skipcomment(s + 1); + } + else if ((STRNCMP(s, "class", 5) == 0 && !vim_isIDc(s[5])) + || (STRNCMP(s, "struct", 6) == 0 && !vim_isIDc(s[6]))) + { + class_or_struct = TRUE; + lookfor_ctor_init = FALSE; + + if (*s == 'c') + s = cin_skipcomment(s + 5); + else + s = cin_skipcomment(s + 6); + } + else + { + if (s[0] == '{' || s[0] == '}' || s[0] == ';') + { + cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE; + } + else if (s[0] == ')') + { + // Constructor-initialization is assumed if we come across + // something like "):" + class_or_struct = FALSE; + lookfor_ctor_init = TRUE; + } + else if (s[0] == '?') + { + // Avoid seeing '() :' after '?' as constructor init. + return FALSE; + } + else if (!vim_isIDc(s[0])) + { + // if it is not an identifier, we are wrong + class_or_struct = FALSE; + lookfor_ctor_init = FALSE; + } + else if (pos->col == 0) + { + // it can't be a constructor-initialization any more + lookfor_ctor_init = FALSE; + + // the first statement starts here: lineup with this one... + if (cpp_base_class) + pos->col = (colnr_T)(s - line); + } + + // When the line ends in a comma don't align with it. + if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1)) + pos->col = 0; + + s = cin_skipcomment(s + 1); + } + } + + cached->found = cpp_base_class; + if (cpp_base_class) + pos->lnum = lnum; + return cpp_base_class; +} + + static int +get_baseclass_amount(int col) +{ + int amount; + colnr_T vcol; + pos_T *trypos; + + if (col == 0) + { + amount = get_indent(); + if (find_last_paren(ml_get_curline(), '(', ')') + && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) + amount = get_indent_lnum(trypos->lnum); // XXX + if (!cin_ends_in(ml_get_curline(), (char_u *)",", NULL)) + amount += curbuf->b_ind_cpp_baseclass; + } + else + { + curwin->w_cursor.col = col; + getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); + amount = (int)vcol; + } + if (amount < curbuf->b_ind_cpp_baseclass) + amount = curbuf->b_ind_cpp_baseclass; + return amount; +} + +/* + * Find the '{' at the start of the block we are in. + * Return NULL if no match found. + * Ignore a '{' that is in a comment, makes indenting the next three lines + * work. + */ +/* foo() */ +/* { */ +/* } */ + + static pos_T * +find_start_brace(void) // XXX +{ + pos_T cursor_save; + pos_T *trypos; + pos_T *pos; + static pos_T pos_copy; + + cursor_save = curwin->w_cursor; + while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP, 0)) != NULL) + { + pos_copy = *trypos; // copy pos_T, next findmatch will change it + trypos = &pos_copy; + curwin->w_cursor = *trypos; + pos = NULL; + // ignore the { if it's in a // or / * * / comment + if ((colnr_T)cin_skip2pos(trypos) == trypos->col + && (pos = ind_find_start_CORS(NULL)) == NULL) // XXX + break; + if (pos != NULL) + curwin->w_cursor.lnum = pos->lnum; + } + curwin->w_cursor = cursor_save; + return trypos; +} + +/* + * Find the matching '(', ignoring it if it is in a comment or before an + * unmatched {. + * Return NULL if no match found. + */ + static pos_T * +find_match_paren_after_brace (int ind_maxparen) // XXX +{ + pos_T *trypos = find_match_paren(ind_maxparen); + + if (trypos != NULL) + { + pos_T *tryposBrace = find_start_brace(); + + // If both an unmatched '(' and '{' is found. Ignore the '(' + // position if the '{' is further down. + if (tryposBrace != NULL + && (trypos->lnum != tryposBrace->lnum + ? trypos->lnum < tryposBrace->lnum + : trypos->col < tryposBrace->col)) + trypos = NULL; + } + return trypos; +} + +/* + * Return ind_maxparen corrected for the difference in line number between the + * cursor position and "startpos". This makes sure that searching for a + * matching paren above the cursor line doesn't find a match because of + * looking a few lines further. + */ + static int +corr_ind_maxparen(pos_T *startpos) +{ + long n = (long)startpos->lnum - (long)curwin->w_cursor.lnum; + + if (n > 0 && n < curbuf->b_ind_maxparen / 2) + return curbuf->b_ind_maxparen - (int)n; + return curbuf->b_ind_maxparen; +} + +/* + * Parse 'cinoptions' and set the values in "curbuf". + * Must be called when 'cinoptions', 'shiftwidth' and/or 'tabstop' changes. + */ + void +parse_cino(buf_T *buf) +{ + char_u *p; + char_u *l; + char_u *digits; + int n; + int divider; + int fraction = 0; + int sw = (int)get_sw_value(buf); + + // Set the default values. + + // Spaces from a block's opening brace the prevailing indent for that + // block should be. + buf->b_ind_level = sw; + + // Spaces from the edge of the line an open brace that's at the end of a + // line is imagined to be. + buf->b_ind_open_imag = 0; + + // Spaces from the prevailing indent for a line that is not preceded by + // an opening brace. + buf->b_ind_no_brace = 0; + + // Column where the first { of a function should be located }. + buf->b_ind_first_open = 0; + + // Spaces from the prevailing indent a leftmost open brace should be + // located. + buf->b_ind_open_extra = 0; + + // Spaces from the matching open brace (real location for one at the left + // edge; imaginary location from one that ends a line) the matching close + // brace should be located. + buf->b_ind_close_extra = 0; + + // Spaces from the edge of the line an open brace sitting in the leftmost + // column is imagined to be. + buf->b_ind_open_left_imag = 0; + + // Spaces jump labels should be shifted to the left if N is non-negative, + // otherwise the jump label will be put to column 1. + buf->b_ind_jump_label = -1; + + // Spaces from the switch() indent a "case xx" label should be located. + buf->b_ind_case = sw; + + // Spaces from the "case xx:" code after a switch() should be located. + buf->b_ind_case_code = sw; + + // Lineup break at end of case in switch() with case label. + buf->b_ind_case_break = 0; + + // Spaces from the class declaration indent a scope declaration label + // should be located. + buf->b_ind_scopedecl = sw; + + // Spaces from the scope declaration label code should be located. + buf->b_ind_scopedecl_code = sw; + + // Amount K&R-style parameters should be indented. + buf->b_ind_param = sw; + + // Amount a function type spec should be indented. + buf->b_ind_func_type = sw; + + // Amount a cpp base class declaration or constructor initialization + // should be indented. + buf->b_ind_cpp_baseclass = sw; + + // additional spaces beyond the prevailing indent a continuation line + // should be located. + buf->b_ind_continuation = sw; + + // Spaces from the indent of the line with an unclosed parentheses. + buf->b_ind_unclosed = sw * 2; + + // Spaces from the indent of the line with an unclosed parentheses, which + // itself is also unclosed. + buf->b_ind_unclosed2 = sw; + + // Suppress ignoring spaces from the indent of a line starting with an + // unclosed parentheses. + buf->b_ind_unclosed_noignore = 0; + + // If the opening paren is the last nonwhite character on the line, and + // b_ind_unclosed_wrapped is nonzero, use this indent relative to the outer + // context (for very long lines). + buf->b_ind_unclosed_wrapped = 0; + + // Suppress ignoring white space when lining up with the character after + // an unclosed parentheses. + buf->b_ind_unclosed_whiteok = 0; + + // Indent a closing parentheses under the line start of the matching + // opening parentheses. + buf->b_ind_matching_paren = 0; + + // Indent a closing parentheses under the previous line. + buf->b_ind_paren_prev = 0; + + // Extra indent for comments. + buf->b_ind_comment = 0; + + // Spaces from the comment opener when there is nothing after it. + buf->b_ind_in_comment = 3; + + // Boolean: if non-zero, use b_ind_in_comment even if there is something + // after the comment opener. + buf->b_ind_in_comment2 = 0; + + // Max lines to search for an open paren. + buf->b_ind_maxparen = 20; + + // Max lines to search for an open comment. + buf->b_ind_maxcomment = 70; + + // Handle braces for java code. + buf->b_ind_java = 0; + + // Not to confuse JS object properties with labels. + buf->b_ind_js = 0; + + // Handle blocked cases correctly. + buf->b_ind_keep_case_label = 0; + + // Handle C++ namespace. + buf->b_ind_cpp_namespace = 0; + + // Handle continuation lines containing conditions of if(), for() and + // while(). + buf->b_ind_if_for_while = 0; + + // indentation for # comments + buf->b_ind_hash_comment = 0; + + // Handle C++ extern "C" or "C++" + buf->b_ind_cpp_extern_c = 0; + + for (p = buf->b_p_cino; *p; ) + { + l = p++; + if (*p == '-') + ++p; + digits = p; // remember where the digits start + n = getdigits(&p); + divider = 0; + if (*p == '.') // ".5s" means a fraction + { + fraction = atol((char *)++p); + while (VIM_ISDIGIT(*p)) + { + ++p; + if (divider) + divider *= 10; + else + divider = 10; + } + } + if (*p == 's') // "2s" means two times 'shiftwidth' + { + if (p == digits) + n = sw; // just "s" is one 'shiftwidth' + else + { + n *= sw; + if (divider) + n += (sw * fraction + divider / 2) / divider; + } + ++p; + } + if (l[1] == '-') + n = -n; + + // When adding an entry here, also update the default 'cinoptions' in + // doc/indent.txt, and add explanation for it! + switch (*l) + { + case '>': buf->b_ind_level = n; break; + case 'e': buf->b_ind_open_imag = n; break; + case 'n': buf->b_ind_no_brace = n; break; + case 'f': buf->b_ind_first_open = n; break; + case '{': buf->b_ind_open_extra = n; break; + case '}': buf->b_ind_close_extra = n; break; + case '^': buf->b_ind_open_left_imag = n; break; + case 'L': buf->b_ind_jump_label = n; break; + case ':': buf->b_ind_case = n; break; + case '=': buf->b_ind_case_code = n; break; + case 'b': buf->b_ind_case_break = n; break; + case 'p': buf->b_ind_param = n; break; + case 't': buf->b_ind_func_type = n; break; + case '/': buf->b_ind_comment = n; break; + case 'c': buf->b_ind_in_comment = n; break; + case 'C': buf->b_ind_in_comment2 = n; break; + case 'i': buf->b_ind_cpp_baseclass = n; break; + case '+': buf->b_ind_continuation = n; break; + case '(': buf->b_ind_unclosed = n; break; + case 'u': buf->b_ind_unclosed2 = n; break; + case 'U': buf->b_ind_unclosed_noignore = n; break; + case 'W': buf->b_ind_unclosed_wrapped = n; break; + case 'w': buf->b_ind_unclosed_whiteok = n; break; + case 'm': buf->b_ind_matching_paren = n; break; + case 'M': buf->b_ind_paren_prev = n; break; + case ')': buf->b_ind_maxparen = n; break; + case '*': buf->b_ind_maxcomment = n; break; + case 'g': buf->b_ind_scopedecl = n; break; + case 'h': buf->b_ind_scopedecl_code = n; break; + case 'j': buf->b_ind_java = n; break; + case 'J': buf->b_ind_js = n; break; + case 'l': buf->b_ind_keep_case_label = n; break; + case '#': buf->b_ind_hash_comment = n; break; + case 'N': buf->b_ind_cpp_namespace = n; break; + case 'k': buf->b_ind_if_for_while = n; break; + case 'E': buf->b_ind_cpp_extern_c = n; break; + } + if (*p == ',') + ++p; + } +} + + static int +find_match(int lookfor, linenr_T ourscope) +{ + char_u *look; + pos_T *theirscope; + char_u *mightbeif; + int elselevel; + int whilelevel; + + if (lookfor == LOOKFOR_IF) + { + elselevel = 1; + whilelevel = 0; + } + else + { + elselevel = 0; + whilelevel = 1; + } + + curwin->w_cursor.col = 0; + + while (curwin->w_cursor.lnum > ourscope + 1) + { + curwin->w_cursor.lnum--; + curwin->w_cursor.col = 0; + + look = cin_skipcomment(ml_get_curline()); + if (cin_iselse(look) + || cin_isif(look) + || cin_isdo(look) // XXX + || cin_iswhileofdo(look, curwin->w_cursor.lnum)) + { + // if we've gone outside the braces entirely, + // we must be out of scope... + theirscope = find_start_brace(); // XXX + if (theirscope == NULL) + break; + + // and if the brace enclosing this is further + // back than the one enclosing the else, we're + // out of luck too. + if (theirscope->lnum < ourscope) + break; + + // and if they're enclosed in a *deeper* brace, + // then we can ignore it because it's in a + // different scope... + if (theirscope->lnum > ourscope) + continue; + + // if it was an "else" (that's not an "else if") + // then we need to go back to another if, so + // increment elselevel + look = cin_skipcomment(ml_get_curline()); + if (cin_iselse(look)) + { + mightbeif = cin_skipcomment(look + 4); + if (!cin_isif(mightbeif)) + ++elselevel; + continue; + } + + // if it was a "while" then we need to go back to + // another "do", so increment whilelevel. XXX + if (cin_iswhileofdo(look, curwin->w_cursor.lnum)) + { + ++whilelevel; + continue; + } + + // If it's an "if" decrement elselevel + look = cin_skipcomment(ml_get_curline()); + if (cin_isif(look)) + { + elselevel--; + // When looking for an "if" ignore "while"s that + // get in the way. + if (elselevel == 0 && lookfor == LOOKFOR_IF) + whilelevel = 0; + } + + // If it's a "do" decrement whilelevel + if (cin_isdo(look)) + whilelevel--; + + // if we've used up all the elses, then + // this must be the if that we want! + // match the indent level of that if. + if (elselevel <= 0 && whilelevel <= 0) + return OK; + } + } + return FAIL; +} + +/* + * Return the desired indent for C code. + * Return -1 if the indent should be left alone (inside a raw string). + */ + int +get_c_indent(void) +{ + pos_T cur_curpos; + int amount; + int scope_amount; + int cur_amount = MAXCOL; + colnr_T col; + char_u *theline; + char_u *linecopy; + pos_T *trypos; + pos_T *comment_pos; + pos_T *tryposBrace = NULL; + pos_T tryposCopy; + pos_T our_paren_pos; + char_u *start; + int start_brace; +#define BRACE_IN_COL0 1 // '{' is in column 0 +#define BRACE_AT_START 2 // '{' is at start of line +#define BRACE_AT_END 3 // '{' is at end of line + linenr_T ourscope; + char_u *l; + char_u *look; + char_u terminated; + int lookfor; + int whilelevel; + linenr_T lnum; + int n; + int iscase; + int lookfor_break; + int lookfor_cpp_namespace = FALSE; + int cont_amount = 0; // amount for continuation line + int original_line_islabel; + int added_to_amount = 0; + int js_cur_has_key = 0; + linenr_T raw_string_start = 0; + cpp_baseclass_cache_T cache_cpp_baseclass = { FALSE, { MAXLNUM, 0 } }; + + // make a copy, value is changed below + int ind_continuation = curbuf->b_ind_continuation; + + // remember where the cursor was when we started + cur_curpos = curwin->w_cursor; + + // if we are at line 1 zero indent is fine, right? + if (cur_curpos.lnum == 1) + return 0; + + // Get a copy of the current contents of the line. + // This is required, because only the most recent line obtained with + // ml_get is valid! + linecopy = vim_strsave(ml_get(cur_curpos.lnum)); + if (linecopy == NULL) + return 0; + + // In insert mode and the cursor is on a ')' truncate the line at the + // cursor position. We don't want to line up with the matching '(' when + // inserting new stuff. + // For unknown reasons the cursor might be past the end of the line, thus + // check for that. + if ((State & INSERT) + && curwin->w_cursor.col < (colnr_T)STRLEN(linecopy) + && linecopy[curwin->w_cursor.col] == ')') + linecopy[curwin->w_cursor.col] = NUL; + + theline = skipwhite(linecopy); + + // move the cursor to the start of the line + + curwin->w_cursor.col = 0; + + original_line_islabel = cin_islabel(); // XXX + + // If we are inside a raw string don't change the indent. + // Ignore a raw string inside a comment. + comment_pos = ind_find_start_comment(); + if (comment_pos != NULL) + { + // findmatchlimit() static pos is overwritten, make a copy + tryposCopy = *comment_pos; + comment_pos = &tryposCopy; + } + trypos = find_start_rawstring(curbuf->b_ind_maxcomment); + if (trypos != NULL && (comment_pos == NULL + || LT_POS(*trypos, *comment_pos))) + { + amount = -1; + goto laterend; + } + + // #defines and so on always go at the left when included in 'cinkeys'. + if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE))) + { + amount = curbuf->b_ind_hash_comment; + goto theend; + } + + // Is it a non-case label? Then that goes at the left margin too unless: + // - JS flag is set. + // - 'L' item has a positive value. + if (original_line_islabel && !curbuf->b_ind_js + && curbuf->b_ind_jump_label < 0) + { + amount = 0; + goto theend; + } + + // If we're inside a "//" comment and there is a "//" comment in a + // previous line, lineup with that one. + if (cin_islinecomment(theline) + && (trypos = find_line_comment()) != NULL) // XXX + { + // find how indented the line beginning the comment is + getvcol(curwin, trypos, &col, NULL, NULL); + amount = col; + goto theend; + } + + // If we're inside a comment and not looking at the start of the + // comment, try using the 'comments' option. + if (!cin_iscomment(theline) && comment_pos != NULL) // XXX + { + int lead_start_len = 2; + int lead_middle_len = 1; + char_u lead_start[COM_MAX_LEN]; // start-comment string + char_u lead_middle[COM_MAX_LEN]; // middle-comment string + char_u lead_end[COM_MAX_LEN]; // end-comment string + char_u *p; + int start_align = 0; + int start_off = 0; + int done = FALSE; + + // find how indented the line beginning the comment is + getvcol(curwin, comment_pos, &col, NULL, NULL); + amount = col; + *lead_start = NUL; + *lead_middle = NUL; + + p = curbuf->b_p_com; + while (*p != NUL) + { + int align = 0; + int off = 0; + int what = 0; + + while (*p != NUL && *p != ':') + { + if (*p == COM_START || *p == COM_END || *p == COM_MIDDLE) + what = *p++; + else if (*p == COM_LEFT || *p == COM_RIGHT) + align = *p++; + else if (VIM_ISDIGIT(*p) || *p == '-') + off = getdigits(&p); + else + ++p; + } + + if (*p == ':') + ++p; + (void)copy_option_part(&p, lead_end, COM_MAX_LEN, ","); + if (what == COM_START) + { + STRCPY(lead_start, lead_end); + lead_start_len = (int)STRLEN(lead_start); + start_off = off; + start_align = align; + } + else if (what == COM_MIDDLE) + { + STRCPY(lead_middle, lead_end); + lead_middle_len = (int)STRLEN(lead_middle); + } + else if (what == COM_END) + { + // If our line starts with the middle comment string, line it + // up with the comment opener per the 'comments' option. + if (STRNCMP(theline, lead_middle, lead_middle_len) == 0 + && STRNCMP(theline, lead_end, STRLEN(lead_end)) != 0) + { + done = TRUE; + if (curwin->w_cursor.lnum > 1) + { + // If the start comment string matches in the previous + // line, use the indent of that line plus offset. If + // the middle comment string matches in the previous + // line, use the indent of that line. XXX + look = skipwhite(ml_get(curwin->w_cursor.lnum - 1)); + if (STRNCMP(look, lead_start, lead_start_len) == 0) + amount = get_indent_lnum(curwin->w_cursor.lnum - 1); + else if (STRNCMP(look, lead_middle, + lead_middle_len) == 0) + { + amount = get_indent_lnum(curwin->w_cursor.lnum - 1); + break; + } + // If the start comment string doesn't match with the + // start of the comment, skip this entry. XXX + else if (STRNCMP(ml_get(comment_pos->lnum) + comment_pos->col, + lead_start, lead_start_len) != 0) + continue; + } + if (start_off != 0) + amount += start_off; + else if (start_align == COM_RIGHT) + amount += vim_strsize(lead_start) + - vim_strsize(lead_middle); + break; + } + + // If our line starts with the end comment string, line it up + // with the middle comment + if (STRNCMP(theline, lead_middle, lead_middle_len) != 0 + && STRNCMP(theline, lead_end, STRLEN(lead_end)) == 0) + { + amount = get_indent_lnum(curwin->w_cursor.lnum - 1); + // XXX + if (off != 0) + amount += off; + else if (align == COM_RIGHT) + amount += vim_strsize(lead_start) + - vim_strsize(lead_middle); + done = TRUE; + break; + } + } + } + + // If our line starts with an asterisk, line up with the + // asterisk in the comment opener; otherwise, line up + // with the first character of the comment text. + if (done) + ; + else if (theline[0] == '*') + amount += 1; + else + { + // If we are more than one line away from the comment opener, take + // the indent of the previous non-empty line. If 'cino' has "CO" + // and we are just below the comment opener and there are any + // white characters after it line up with the text after it; + // otherwise, add the amount specified by "c" in 'cino' + amount = -1; + for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; --lnum) + { + if (linewhite(lnum)) // skip blank lines + continue; + amount = get_indent_lnum(lnum); // XXX + break; + } + if (amount == -1) // use the comment opener + { + if (!curbuf->b_ind_in_comment2) + { + start = ml_get(comment_pos->lnum); + look = start + comment_pos->col + 2; // skip / and * + if (*look != NUL) // if something after it + comment_pos->col = (colnr_T)(skipwhite(look) - start); + } + getvcol(curwin, comment_pos, &col, NULL, NULL); + amount = col; + if (curbuf->b_ind_in_comment2 || *look == NUL) + amount += curbuf->b_ind_in_comment; + } + } + goto theend; + } + + // Are we looking at a ']' that has a match? + if (*skipwhite(theline) == ']' + && (trypos = find_match_char('[', curbuf->b_ind_maxparen)) != NULL) + { + // align with the line containing the '['. + amount = get_indent_lnum(trypos->lnum); + goto theend; + } + + // Are we inside parentheses or braces? XXX + if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL + && curbuf->b_ind_java == 0) + || (tryposBrace = find_start_brace()) != NULL + || trypos != NULL) + { + if (trypos != NULL && tryposBrace != NULL) + { + // Both an unmatched '(' and '{' is found. Use the one which is + // closer to the current cursor position, set the other to NULL. + if (trypos->lnum != tryposBrace->lnum + ? trypos->lnum < tryposBrace->lnum + : trypos->col < tryposBrace->col) + trypos = NULL; + else + tryposBrace = NULL; + } + + if (trypos != NULL) + { + // If the matching paren is more than one line away, use the indent of + // a previous non-empty line that matches the same paren. + if (theline[0] == ')' && curbuf->b_ind_paren_prev) + { + // Line up with the start of the matching paren line. + amount = get_indent_lnum(curwin->w_cursor.lnum - 1); // XXX + } + else + { + amount = -1; + our_paren_pos = *trypos; + for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) + { + l = skipwhite(ml_get(lnum)); + if (cin_nocode(l)) // skip comment lines + continue; + if (cin_ispreproc_cont(&l, &lnum, &amount)) + continue; // ignore #define, #if, etc. + curwin->w_cursor.lnum = lnum; + + // Skip a comment or raw string. XXX + if ((trypos = ind_find_start_CORS(NULL)) != NULL) + { + lnum = trypos->lnum + 1; + continue; + } + + // XXX + if ((trypos = find_match_paren( + corr_ind_maxparen(&cur_curpos))) != NULL + && trypos->lnum == our_paren_pos.lnum + && trypos->col == our_paren_pos.col) + { + amount = get_indent_lnum(lnum); // XXX + + if (theline[0] == ')') + { + if (our_paren_pos.lnum != lnum + && cur_amount > amount) + cur_amount = amount; + amount = -1; + } + break; + } + } + } + + // Line up with line where the matching paren is. XXX + // If the line starts with a '(' or the indent for unclosed + // parentheses is zero, line up with the unclosed parentheses. + if (amount == -1) + { + int ignore_paren_col = 0; + int is_if_for_while = 0; + + if (curbuf->b_ind_if_for_while) + { + // Look for the outermost opening parenthesis on this line + // and check whether it belongs to an "if", "for" or "while". + + pos_T cursor_save = curwin->w_cursor; + pos_T outermost; + char_u *line; + + trypos = &our_paren_pos; + do { + outermost = *trypos; + curwin->w_cursor.lnum = outermost.lnum; + curwin->w_cursor.col = outermost.col; + + trypos = find_match_paren(curbuf->b_ind_maxparen); + } while (trypos && trypos->lnum == outermost.lnum); + + curwin->w_cursor = cursor_save; + + line = ml_get(outermost.lnum); + + is_if_for_while = + cin_is_if_for_while_before_offset(line, &outermost.col); + } + + amount = skip_label(our_paren_pos.lnum, &look); + look = skipwhite(look); + if (*look == '(') + { + linenr_T save_lnum = curwin->w_cursor.lnum; + char_u *line; + int look_col; + + // Ignore a '(' in front of the line that has a match before + // our matching '('. + curwin->w_cursor.lnum = our_paren_pos.lnum; + line = ml_get_curline(); + look_col = (int)(look - line); + curwin->w_cursor.col = look_col + 1; + if ((trypos = findmatchlimit(NULL, ')', 0, + curbuf->b_ind_maxparen)) + != NULL + && trypos->lnum == our_paren_pos.lnum + && trypos->col < our_paren_pos.col) + ignore_paren_col = trypos->col + 1; + + curwin->w_cursor.lnum = save_lnum; + look = ml_get(our_paren_pos.lnum) + look_col; + } + if (theline[0] == ')' || (curbuf->b_ind_unclosed == 0 + && is_if_for_while == 0) + || (!curbuf->b_ind_unclosed_noignore && *look == '(' + && ignore_paren_col == 0)) + { + // If we're looking at a close paren, line up right there; + // otherwise, line up with the next (non-white) character. + // When b_ind_unclosed_wrapped is set and the matching paren is + // the last nonwhite character of the line, use either the + // indent of the current line or the indentation of the next + // outer paren and add b_ind_unclosed_wrapped (for very long + // lines). + if (theline[0] != ')') + { + cur_amount = MAXCOL; + l = ml_get(our_paren_pos.lnum); + if (curbuf->b_ind_unclosed_wrapped + && cin_ends_in(l, (char_u *)"(", NULL)) + { + // look for opening unmatched paren, indent one level + // for each additional level + n = 1; + for (col = 0; col < our_paren_pos.col; ++col) + { + switch (l[col]) + { + case '(': + case '{': ++n; + break; + + case ')': + case '}': if (n > 1) + --n; + break; + } + } + + our_paren_pos.col = 0; + amount += n * curbuf->b_ind_unclosed_wrapped; + } + else if (curbuf->b_ind_unclosed_whiteok) + our_paren_pos.col++; + else + { + col = our_paren_pos.col + 1; + while (VIM_ISWHITE(l[col])) + col++; + if (l[col] != NUL) // In case of trailing space + our_paren_pos.col = col; + else + our_paren_pos.col++; + } + } + + // Find how indented the paren is, or the character after it + // if we did the above "if". + if (our_paren_pos.col > 0) + { + getvcol(curwin, &our_paren_pos, &col, NULL, NULL); + if (cur_amount > (int)col) + cur_amount = col; + } + } + + if (theline[0] == ')' && curbuf->b_ind_matching_paren) + { + // Line up with the start of the matching paren line. + } + else if ((curbuf->b_ind_unclosed == 0 && is_if_for_while == 0) + || (!curbuf->b_ind_unclosed_noignore + && *look == '(' && ignore_paren_col == 0)) + { + if (cur_amount != MAXCOL) + amount = cur_amount; + } + else + { + // Add b_ind_unclosed2 for each '(' before our matching one, + // but ignore (void) before the line (ignore_paren_col). + col = our_paren_pos.col; + while ((int)our_paren_pos.col > ignore_paren_col) + { + --our_paren_pos.col; + switch (*ml_get_pos(&our_paren_pos)) + { + case '(': amount += curbuf->b_ind_unclosed2; + col = our_paren_pos.col; + break; + case ')': amount -= curbuf->b_ind_unclosed2; + col = MAXCOL; + break; + } + } + + // Use b_ind_unclosed once, when the first '(' is not inside + // braces + if (col == MAXCOL) + amount += curbuf->b_ind_unclosed; + else + { + curwin->w_cursor.lnum = our_paren_pos.lnum; + curwin->w_cursor.col = col; + if (find_match_paren_after_brace(curbuf->b_ind_maxparen) + != NULL) + amount += curbuf->b_ind_unclosed2; + else + { + if (is_if_for_while) + amount += curbuf->b_ind_if_for_while; + else + amount += curbuf->b_ind_unclosed; + } + } + // For a line starting with ')' use the minimum of the two + // positions, to avoid giving it more indent than the previous + // lines: + // func_long_name( if (x + // arg && yy + // ) ^ not here ) ^ not here + if (cur_amount < amount) + amount = cur_amount; + } + } + + // add extra indent for a comment + if (cin_iscomment(theline)) + amount += curbuf->b_ind_comment; + } + else + { + // We are inside braces, there is a { before this line at the position + // stored in tryposBrace. + // Make a copy of tryposBrace, it may point to pos_copy inside + // find_start_brace(), which may be changed somewhere. + tryposCopy = *tryposBrace; + tryposBrace = &tryposCopy; + trypos = tryposBrace; + ourscope = trypos->lnum; + start = ml_get(ourscope); + + // Now figure out how indented the line is in general. + // If the brace was at the start of the line, we use that; + // otherwise, check out the indentation of the line as + // a whole and then add the "imaginary indent" to that. + look = skipwhite(start); + if (*look == '{') + { + getvcol(curwin, trypos, &col, NULL, NULL); + amount = col; + if (*start == '{') + start_brace = BRACE_IN_COL0; + else + start_brace = BRACE_AT_START; + } + else + { + // That opening brace might have been on a continuation + // line. if so, find the start of the line. + curwin->w_cursor.lnum = ourscope; + + // Position the cursor over the rightmost paren, so that + // matching it will take us back to the start of the line. + lnum = ourscope; + if (find_last_paren(start, '(', ')') + && (trypos = find_match_paren(curbuf->b_ind_maxparen)) + != NULL) + lnum = trypos->lnum; + + // It could have been something like + // case 1: if (asdf && + // ldfd) { + // } + if ((curbuf->b_ind_js || curbuf->b_ind_keep_case_label) + && cin_iscase(skipwhite(ml_get_curline()), FALSE)) + amount = get_indent(); + else if (curbuf->b_ind_js) + amount = get_indent_lnum(lnum); + else + amount = skip_label(lnum, &l); + + start_brace = BRACE_AT_END; + } + + // For Javascript check if the line starts with "key:". + if (curbuf->b_ind_js) + js_cur_has_key = cin_has_js_key(theline); + + // If we're looking at a closing brace, that's where + // we want to be. otherwise, add the amount of room + // that an indent is supposed to be. + if (theline[0] == '}') + { + // they may want closing braces to line up with something + // other than the open brace. indulge them, if so. + amount += curbuf->b_ind_close_extra; + } + else + { + // If we're looking at an "else", try to find an "if" + // to match it with. + // If we're looking at a "while", try to find a "do" + // to match it with. + lookfor = LOOKFOR_INITIAL; + if (cin_iselse(theline)) + lookfor = LOOKFOR_IF; + else if (cin_iswhileofdo(theline, cur_curpos.lnum)) // XXX + lookfor = LOOKFOR_DO; + if (lookfor != LOOKFOR_INITIAL) + { + curwin->w_cursor.lnum = cur_curpos.lnum; + if (find_match(lookfor, ourscope) == OK) + { + amount = get_indent(); // XXX + goto theend; + } + } + + // We get here if we are not on an "while-of-do" or "else" (or + // failed to find a matching "if"). + // Search backwards for something to line up with. + // First set amount for when we don't find anything. + + // if the '{' is _really_ at the left margin, use the imaginary + // location of a left-margin brace. Otherwise, correct the + // location for b_ind_open_extra. + + if (start_brace == BRACE_IN_COL0) // '{' is in column 0 + { + amount = curbuf->b_ind_open_left_imag; + lookfor_cpp_namespace = TRUE; + } + else if (start_brace == BRACE_AT_START && + lookfor_cpp_namespace) // '{' is at start + { + + lookfor_cpp_namespace = TRUE; + } + else + { + if (start_brace == BRACE_AT_END) // '{' is at end of line + { + amount += curbuf->b_ind_open_imag; + + l = skipwhite(ml_get_curline()); + if (cin_is_cpp_namespace(l)) + amount += curbuf->b_ind_cpp_namespace; + else if (cin_is_cpp_extern_c(l)) + amount += curbuf->b_ind_cpp_extern_c; + } + else + { + // Compensate for adding b_ind_open_extra later. + amount -= curbuf->b_ind_open_extra; + if (amount < 0) + amount = 0; + } + } + + lookfor_break = FALSE; + + if (cin_iscase(theline, FALSE)) // it's a switch() label + { + lookfor = LOOKFOR_CASE; // find a previous switch() label + amount += curbuf->b_ind_case; + } + else if (cin_isscopedecl(theline)) // private:, ... + { + lookfor = LOOKFOR_SCOPEDECL; // class decl is this block + amount += curbuf->b_ind_scopedecl; + } + else + { + if (curbuf->b_ind_case_break && cin_isbreak(theline)) + // break; ... + lookfor_break = TRUE; + + lookfor = LOOKFOR_INITIAL; + // b_ind_level from start of block + amount += curbuf->b_ind_level; + } + scope_amount = amount; + whilelevel = 0; + + // Search backwards. If we find something we recognize, line up + // with that. + // + // If we're looking at an open brace, indent + // the usual amount relative to the conditional + // that opens the block. + curwin->w_cursor = cur_curpos; + for (;;) + { + curwin->w_cursor.lnum--; + curwin->w_cursor.col = 0; + + // If we went all the way back to the start of our scope, line + // up with it. + if (curwin->w_cursor.lnum <= ourscope) + { + // We reached end of scope: + // If looking for a enum or structure initialization + // go further back: + // If it is an initializer (enum xxx or xxx =), then + // don't add ind_continuation, otherwise it is a variable + // declaration: + // int x, + // here; <-- add ind_continuation + if (lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (curwin->w_cursor.lnum == 0 + || curwin->w_cursor.lnum + < ourscope - curbuf->b_ind_maxparen) + { + // nothing found (abuse curbuf->b_ind_maxparen as + // limit) assume terminated line (i.e. a variable + // initialization) + if (cont_amount > 0) + amount = cont_amount; + else if (!curbuf->b_ind_js) + amount += ind_continuation; + break; + } + + l = ml_get_curline(); + + // If we're in a comment or raw string now, skip to + // the start of it. + trypos = ind_find_start_CORS(NULL); + if (trypos != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + continue; + } + + // Skip preprocessor directives and blank lines. + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, + &amount)) + continue; + + if (cin_nocode(l)) + continue; + + terminated = cin_isterminated(l, FALSE, TRUE); + + // If we are at top level and the line looks like a + // function declaration, we are done + // (it's a variable declaration). + if (start_brace != BRACE_IN_COL0 + || !cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) + { + // if the line is terminated with another ',' + // it is a continued variable initialization. + // don't add extra indent. + // TODO: does not work, if a function + // declaration is split over multiple lines: + // cin_isfuncdecl returns FALSE then. + if (terminated == ',') + break; + + // if it es a enum declaration or an assignment, + // we are done. + if (terminated != ';' && cin_isinit()) + break; + + // nothing useful found + if (terminated == 0 || terminated == '{') + continue; + } + + if (terminated != ';') + { + // Skip parens and braces. Position the cursor + // over the rightmost paren, so that matching it + // will take us back to the start of the line. + // XXX + trypos = NULL; + if (find_last_paren(l, '(', ')')) + trypos = find_match_paren( + curbuf->b_ind_maxparen); + + if (trypos == NULL && find_last_paren(l, '{', '}')) + trypos = find_start_brace(); + + if (trypos != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + continue; + } + } + + // it's a variable declaration, add indentation + // like in + // int a, + // b; + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + } + else if (lookfor == LOOKFOR_UNTERM) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + } + else + { + if (lookfor != LOOKFOR_TERM + && lookfor != LOOKFOR_CPP_BASECLASS + && lookfor != LOOKFOR_COMMA) + { + amount = scope_amount; + if (theline[0] == '{') + { + amount += curbuf->b_ind_open_extra; + added_to_amount = curbuf->b_ind_open_extra; + } + } + + if (lookfor_cpp_namespace) + { + // Looking for C++ namespace, need to look further + // back. + if (curwin->w_cursor.lnum == ourscope) + continue; + + if (curwin->w_cursor.lnum == 0 + || curwin->w_cursor.lnum + < ourscope - FIND_NAMESPACE_LIM) + break; + + l = ml_get_curline(); + + // If we're in a comment or raw string now, skip + // to the start of it. + trypos = ind_find_start_CORS(NULL); + if (trypos != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + continue; + } + + // Skip preprocessor directives and blank lines. + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, + &amount)) + continue; + + // Finally the actual check for "namespace". + if (cin_is_cpp_namespace(l)) + { + amount += curbuf->b_ind_cpp_namespace + - added_to_amount; + break; + } + else if (cin_is_cpp_extern_c(l)) + { + amount += curbuf->b_ind_cpp_extern_c + - added_to_amount; + break; + } + + if (cin_nocode(l)) + continue; + } + } + break; + } + + // If we're in a comment or raw string now, skip to the start + // of it. XXX + if ((trypos = ind_find_start_CORS(&raw_string_start)) != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + continue; + } + + l = ml_get_curline(); + + // If this is a switch() label, may line up relative to that. + // If this is a C++ scope declaration, do the same. + iscase = cin_iscase(l, FALSE); + if (iscase || cin_isscopedecl(l)) + { + // we are only looking for cpp base class + // declaration/initialization any longer + if (lookfor == LOOKFOR_CPP_BASECLASS) + break; + + // When looking for a "do" we are not interested in + // labels. + if (whilelevel > 0) + continue; + + // case xx: + // c = 99 + <- this indent plus continuation + //-> here; + if (lookfor == LOOKFOR_UNTERM + || lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + break; + } + + // case xx: <- line up with this case + // x = 333; + // case yy: + if ( (iscase && lookfor == LOOKFOR_CASE) + || (iscase && lookfor_break) + || (!iscase && lookfor == LOOKFOR_SCOPEDECL)) + { + // Check that this case label is not for another + // switch() XXX + if ((trypos = find_start_brace()) == NULL + || trypos->lnum == ourscope) + { + amount = get_indent(); // XXX + break; + } + continue; + } + + n = get_indent_nolabel(curwin->w_cursor.lnum); // XXX + + // case xx: if (cond) <- line up with this if + // y = y + 1; + // -> s = 99; + // + // case xx: + // if (cond) <- line up with this line + // y = y + 1; + // -> s = 99; + if (lookfor == LOOKFOR_TERM) + { + if (n) + amount = n; + + if (!lookfor_break) + break; + } + + // case xx: x = x + 1; <- line up with this x + // -> y = y + 1; + // + // case xx: if (cond) <- line up with this if + // -> y = y + 1; + if (n) + { + amount = n; + l = after_label(ml_get_curline()); + if (l != NULL && cin_is_cinword(l)) + { + if (theline[0] == '{') + amount += curbuf->b_ind_open_extra; + else + amount += curbuf->b_ind_level + + curbuf->b_ind_no_brace; + } + break; + } + + // Try to get the indent of a statement before the switch + // label. If nothing is found, line up relative to the + // switch label. + // break; <- may line up with this line + // case xx: + // -> y = 1; + scope_amount = get_indent() + (iscase // XXX + ? curbuf->b_ind_case_code + : curbuf->b_ind_scopedecl_code); + lookfor = curbuf->b_ind_case_break + ? LOOKFOR_NOBREAK : LOOKFOR_ANY; + continue; + } + + // Looking for a switch() label or C++ scope declaration, + // ignore other lines, skip {}-blocks. + if (lookfor == LOOKFOR_CASE || lookfor == LOOKFOR_SCOPEDECL) + { + if (find_last_paren(l, '{', '}') + && (trypos = find_start_brace()) != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + } + continue; + } + + // Ignore jump labels with nothing after them. + if (!curbuf->b_ind_js && cin_islabel()) + { + l = after_label(ml_get_curline()); + if (l == NULL || cin_nocode(l)) + continue; + } + + // Ignore #defines, #if, etc. + // Ignore comment and empty lines. + // (need to get the line again, cin_islabel() may have + // unlocked it) + l = ml_get_curline(); + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount) + || cin_nocode(l)) + continue; + + // Are we at the start of a cpp base class declaration or + // constructor initialization? XXX + n = FALSE; + if (lookfor != LOOKFOR_TERM && curbuf->b_ind_cpp_baseclass > 0) + { + n = cin_is_cpp_baseclass(&cache_cpp_baseclass); + l = ml_get_curline(); + } + if (n) + { + if (lookfor == LOOKFOR_UNTERM) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + } + else if (theline[0] == '{') + { + // Need to find start of the declaration. + lookfor = LOOKFOR_UNTERM; + ind_continuation = 0; + continue; + } + else + // XXX + amount = get_baseclass_amount( + cache_cpp_baseclass.lpos.col); + break; + } + else if (lookfor == LOOKFOR_CPP_BASECLASS) + { + // only look, whether there is a cpp base class + // declaration or initialization before the opening brace. + if (cin_isterminated(l, TRUE, FALSE)) + break; + else + continue; + } + + // What happens next depends on the line being terminated. + // If terminated with a ',' only consider it terminating if + // there is another unterminated statement behind, eg: + // 123, + // sizeof + // here + // Otherwise check whether it is a enumeration or structure + // initialisation (not indented) or a variable declaration + // (indented). + terminated = cin_isterminated(l, FALSE, TRUE); + + if (js_cur_has_key) + { + js_cur_has_key = 0; // only check the first line + if (curbuf->b_ind_js && terminated == ',') + { + // For Javascript we might be inside an object: + // key: something, <- align with this + // key: something + // or: + // key: something + <- align with this + // something, + // key: something + lookfor = LOOKFOR_JS_KEY; + } + } + if (lookfor == LOOKFOR_JS_KEY && cin_has_js_key(l)) + { + amount = get_indent(); + break; + } + if (lookfor == LOOKFOR_COMMA) + { + if (tryposBrace != NULL && tryposBrace->lnum + >= curwin->w_cursor.lnum) + break; + if (terminated == ',') + // line below current line is the one that starts a + // (possibly broken) line ending in a comma. + break; + else + { + amount = get_indent(); + if (curwin->w_cursor.lnum - 1 == ourscope) + // line above is start of the scope, thus current + // line is the one that stars a (possibly broken) + // line ending in a comma. + break; + } + } + + if (terminated == 0 || (lookfor != LOOKFOR_UNTERM + && terminated == ',')) + { + if (lookfor != LOOKFOR_ENUM_OR_INIT && + (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[')) + amount += ind_continuation; + // if we're in the middle of a paren thing, + // go back to the line that starts it so + // we can get the right prevailing indent + // if ( foo && + // bar ) + + // Position the cursor over the rightmost paren, so that + // matching it will take us back to the start of the line. + // Ignore a match before the start of the block. + (void)find_last_paren(l, '(', ')'); + trypos = find_match_paren(corr_ind_maxparen(&cur_curpos)); + if (trypos != NULL && (trypos->lnum < tryposBrace->lnum + || (trypos->lnum == tryposBrace->lnum + && trypos->col < tryposBrace->col))) + trypos = NULL; + + // If we are looking for ',', we also look for matching + // braces. + if (trypos == NULL && terminated == ',' + && find_last_paren(l, '{', '}')) + trypos = find_start_brace(); + + if (trypos != NULL) + { + // Check if we are on a case label now. This is + // handled above. + // case xx: if ( asdf && + // asdf) + curwin->w_cursor = *trypos; + l = ml_get_curline(); + if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) + { + ++curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; + continue; + } + } + + // Skip over continuation lines to find the one to get the + // indent from + // char *usethis = "bla{backslash} + // bla", + // here; + if (terminated == ',') + { + while (curwin->w_cursor.lnum > 1) + { + l = ml_get(curwin->w_cursor.lnum - 1); + if (*l == NUL || l[STRLEN(l) - 1] != '\\') + break; + --curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; + } + } + + // Get indent and pointer to text for current line, + // ignoring any jump label. XXX + if (curbuf->b_ind_js) + cur_amount = get_indent(); + else + cur_amount = skip_label(curwin->w_cursor.lnum, &l); + // If this is just above the line we are indenting, and it + // starts with a '{', line it up with this line. + // while (not) + // -> { + // } + if (terminated != ',' && lookfor != LOOKFOR_TERM + && theline[0] == '{') + { + amount = cur_amount; + // Only add b_ind_open_extra when the current line + // doesn't start with a '{', which must have a match + // in the same line (scope is the same). Probably: + // { 1, 2 }, + // -> { 3, 4 } + if (*skipwhite(l) != '{') + amount += curbuf->b_ind_open_extra; + + if (curbuf->b_ind_cpp_baseclass && !curbuf->b_ind_js) + { + // have to look back, whether it is a cpp base + // class declaration or initialization + lookfor = LOOKFOR_CPP_BASECLASS; + continue; + } + break; + } + + // Check if we are after an "if", "while", etc. + // Also allow " } else". + if (cin_is_cinword(l) || cin_iselse(skipwhite(l))) + { + // Found an unterminated line after an if (), line up + // with the last one. + // if (cond) + // 100 + + // -> here; + if (lookfor == LOOKFOR_UNTERM + || lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + break; + } + + // If this is just above the line we are indenting, we + // are finished. + // while (not) + // -> here; + // Otherwise this indent can be used when the line + // before this is terminated. + // yyy; + // if (stat) + // while (not) + // xxx; + // -> here; + amount = cur_amount; + if (theline[0] == '{') + amount += curbuf->b_ind_open_extra; + if (lookfor != LOOKFOR_TERM) + { + amount += curbuf->b_ind_level + + curbuf->b_ind_no_brace; + break; + } + + // Special trick: when expecting the while () after a + // do, line up with the while() + // do + // x = 1; + // -> here + l = skipwhite(ml_get_curline()); + if (cin_isdo(l)) + { + if (whilelevel == 0) + break; + --whilelevel; + } + + // When searching for a terminated line, don't use the + // one between the "if" and the matching "else". + // Need to use the scope of this "else". XXX + // If whilelevel != 0 continue looking for a "do {". + if (cin_iselse(l) && whilelevel == 0) + { + // If we're looking at "} else", let's make sure we + // find the opening brace of the enclosing scope, + // not the one from "if () {". + if (*l == '}') + curwin->w_cursor.col = + (colnr_T)(l - ml_get_curline()) + 1; + + if ((trypos = find_start_brace()) == NULL + || find_match(LOOKFOR_IF, trypos->lnum) + == FAIL) + break; + } + } + + // If we're below an unterminated line that is not an + // "if" or something, we may line up with this line or + // add something for a continuation line, depending on + // the line before this one. + else + { + // Found two unterminated lines on a row, line up with + // the last one. + // c = 99 + + // 100 + + // -> here; + if (lookfor == LOOKFOR_UNTERM) + { + // When line ends in a comma add extra indent + if (terminated == ',') + amount += ind_continuation; + break; + } + + if (lookfor == LOOKFOR_ENUM_OR_INIT) + { + // Found two lines ending in ',', lineup with the + // lowest one, but check for cpp base class + // declaration/initialization, if it is an + // opening brace or we are looking just for + // enumerations/initializations. + if (terminated == ',') + { + if (curbuf->b_ind_cpp_baseclass == 0) + break; + + lookfor = LOOKFOR_CPP_BASECLASS; + continue; + } + + // Ignore unterminated lines in between, but + // reduce indent. + if (amount > cur_amount) + amount = cur_amount; + } + else + { + // Found first unterminated line on a row, may + // line up with this line, remember its indent + // 100 + + // -> here; + l = ml_get_curline(); + amount = cur_amount; + + n = (int)STRLEN(l); + if (terminated == ',' && (*skipwhite(l) == ']' + || (n >=2 && l[n - 2] == ']'))) + break; + + // If previous line ends in ',', check whether we + // are in an initialization or enum + // struct xxx = + // { + // sizeof a, + // 124 }; + // or a normal possible continuation line. + // but only, of no other statement has been found + // yet. + if (lookfor == LOOKFOR_INITIAL && terminated == ',') + { + if (curbuf->b_ind_js) + { + // Search for a line ending in a comma + // and line up with the line below it + // (could be the current line). + // some = [ + // 1, <- line up here + // 2, + // some = [ + // 3 + <- line up here + // 4 * + // 5, + // 6, + if (cin_iscomment(skipwhite(l))) + break; + lookfor = LOOKFOR_COMMA; + trypos = find_match_char('[', + curbuf->b_ind_maxparen); + if (trypos != NULL) + { + if (trypos->lnum + == curwin->w_cursor.lnum - 1) + { + // Current line is first inside + // [], line up with it. + break; + } + ourscope = trypos->lnum; + } + } + else + { + lookfor = LOOKFOR_ENUM_OR_INIT; + cont_amount = cin_first_id_amount(); + } + } + else + { + if (lookfor == LOOKFOR_INITIAL + && *l != NUL + && l[STRLEN(l) - 1] == '\\') + // XXX + cont_amount = cin_get_equal_amount( + curwin->w_cursor.lnum); + if (lookfor != LOOKFOR_TERM + && lookfor != LOOKFOR_JS_KEY + && lookfor != LOOKFOR_COMMA + && raw_string_start != curwin->w_cursor.lnum) + lookfor = LOOKFOR_UNTERM; + } + } + } + } + + // Check if we are after a while (cond); + // If so: Ignore until the matching "do". + else if (cin_iswhileofdo_end(terminated)) // XXX + { + // Found an unterminated line after a while ();, line up + // with the last one. + // while (cond); + // 100 + <- line up with this one + // -> here; + if (lookfor == LOOKFOR_UNTERM + || lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + break; + } + + if (whilelevel == 0) + { + lookfor = LOOKFOR_TERM; + amount = get_indent(); // XXX + if (theline[0] == '{') + amount += curbuf->b_ind_open_extra; + } + ++whilelevel; + } + + // We are after a "normal" statement. + // If we had another statement we can stop now and use the + // indent of that other statement. + // Otherwise the indent of the current statement may be used, + // search backwards for the next "normal" statement. + else + { + // Skip single break line, if before a switch label. It + // may be lined up with the case label. + if (lookfor == LOOKFOR_NOBREAK + && cin_isbreak(skipwhite(ml_get_curline()))) + { + lookfor = LOOKFOR_ANY; + continue; + } + + // Handle "do {" line. + if (whilelevel > 0) + { + l = cin_skipcomment(ml_get_curline()); + if (cin_isdo(l)) + { + amount = get_indent(); // XXX + --whilelevel; + continue; + } + } + + // Found a terminated line above an unterminated line. Add + // the amount for a continuation line. + // x = 1; + // y = foo + + // -> here; + // or + // int x = 1; + // int foo, + // -> here; + if (lookfor == LOOKFOR_UNTERM + || lookfor == LOOKFOR_ENUM_OR_INIT) + { + if (cont_amount > 0) + amount = cont_amount; + else + amount += ind_continuation; + break; + } + + // Found a terminated line above a terminated line or "if" + // etc. line. Use the amount of the line below us. + // x = 1; x = 1; + // if (asdf) y = 2; + // while (asdf) ->here; + // here; + // ->foo; + if (lookfor == LOOKFOR_TERM) + { + if (!lookfor_break && whilelevel == 0) + break; + } + + // First line above the one we're indenting is terminated. + // To know what needs to be done look further backward for + // a terminated line. + else + { + // position the cursor over the rightmost paren, so + // that matching it will take us back to the start of + // the line. Helps for: + // func(asdr, + // asdfasdf); + // here; +term_again: + l = ml_get_curline(); + if (find_last_paren(l, '(', ')') + && (trypos = find_match_paren( + curbuf->b_ind_maxparen)) != NULL) + { + // Check if we are on a case label now. This is + // handled above. + // case xx: if ( asdf && + // asdf) + curwin->w_cursor = *trypos; + l = ml_get_curline(); + if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) + { + ++curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; + continue; + } + } + + // When aligning with the case statement, don't align + // with a statement after it. + // case 1: { <-- don't use this { position + // stat; + // } + // case 2: + // stat; + // } + iscase = (curbuf->b_ind_keep_case_label + && cin_iscase(l, FALSE)); + + // Get indent and pointer to text for current line, + // ignoring any jump label. + amount = skip_label(curwin->w_cursor.lnum, &l); + + if (theline[0] == '{') + amount += curbuf->b_ind_open_extra; + // See remark above: "Only add b_ind_open_extra.." + l = skipwhite(l); + if (*l == '{') + amount -= curbuf->b_ind_open_extra; + lookfor = iscase ? LOOKFOR_ANY : LOOKFOR_TERM; + + // When a terminated line starts with "else" skip to + // the matching "if": + // else 3; + // indent this; + // Need to use the scope of this "else". XXX + // If whilelevel != 0 continue looking for a "do {". + if (lookfor == LOOKFOR_TERM + && *l != '}' + && cin_iselse(l) + && whilelevel == 0) + { + if ((trypos = find_start_brace()) == NULL + || find_match(LOOKFOR_IF, trypos->lnum) + == FAIL) + break; + continue; + } + + // If we're at the end of a block, skip to the start of + // that block. + l = ml_get_curline(); + if (find_last_paren(l, '{', '}') // XXX + && (trypos = find_start_brace()) != NULL) + { + curwin->w_cursor = *trypos; + // if not "else {" check for terminated again + // but skip block for "} else {" + l = cin_skipcomment(ml_get_curline()); + if (*l == '}' || !cin_iselse(l)) + goto term_again; + ++curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; + } + } + } + } + } + } + + // add extra indent for a comment + if (cin_iscomment(theline)) + amount += curbuf->b_ind_comment; + + // subtract extra left-shift for jump labels + if (curbuf->b_ind_jump_label > 0 && original_line_islabel) + amount -= curbuf->b_ind_jump_label; + + goto theend; + } + + // ok -- we're not inside any sort of structure at all! + // + // This means we're at the top level, and everything should + // basically just match where the previous line is, except + // for the lines immediately following a function declaration, + // which are K&R-style parameters and need to be indented. + // + // if our line starts with an open brace, forget about any + // prevailing indent and make sure it looks like the start + // of a function + + if (theline[0] == '{') + { + amount = curbuf->b_ind_first_open; + goto theend; + } + + // If the NEXT line is a function declaration, the current + // line needs to be indented as a function type spec. + // Don't do this if the current line looks like a comment or if the + // current line is terminated, ie. ends in ';', or if the current line + // contains { or }: "void f() {\n if (1)" + if (cur_curpos.lnum < curbuf->b_ml.ml_line_count + && !cin_nocode(theline) + && vim_strchr(theline, '{') == NULL + && vim_strchr(theline, '}') == NULL + && !cin_ends_in(theline, (char_u *)":", NULL) + && !cin_ends_in(theline, (char_u *)",", NULL) + && cin_isfuncdecl(NULL, cur_curpos.lnum + 1, + cur_curpos.lnum + 1) + && !cin_isterminated(theline, FALSE, TRUE)) + { + amount = curbuf->b_ind_func_type; + goto theend; + } + + // search backwards until we find something we recognize + amount = 0; + curwin->w_cursor = cur_curpos; + while (curwin->w_cursor.lnum > 1) + { + curwin->w_cursor.lnum--; + curwin->w_cursor.col = 0; + + l = ml_get_curline(); + + // If we're in a comment or raw string now, skip to the start + // of it. XXX + if ((trypos = ind_find_start_CORS(NULL)) != NULL) + { + curwin->w_cursor.lnum = trypos->lnum + 1; + curwin->w_cursor.col = 0; + continue; + } + + // Are we at the start of a cpp base class declaration or + // constructor initialization? XXX + n = FALSE; + if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') + { + n = cin_is_cpp_baseclass(&cache_cpp_baseclass); + l = ml_get_curline(); + } + if (n) + { + // XXX + amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col); + break; + } + + // Skip preprocessor directives and blank lines. + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) + continue; + + if (cin_nocode(l)) + continue; + + // If the previous line ends in ',', use one level of + // indentation: + // int foo, + // bar; + // do this before checking for '}' in case of eg. + // enum foobar + // { + // ... + // } foo, + // bar; + n = 0; + if (cin_ends_in(l, (char_u *)",", NULL) + || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) + { + // take us back to opening paren + if (find_last_paren(l, '(', ')') + && (trypos = find_match_paren( + curbuf->b_ind_maxparen)) != NULL) + curwin->w_cursor = *trypos; + + // For a line ending in ',' that is a continuation line go + // back to the first line with a backslash: + // char *foo = "bla{backslash} + // bla", + // here; + while (n == 0 && curwin->w_cursor.lnum > 1) + { + l = ml_get(curwin->w_cursor.lnum - 1); + if (*l == NUL || l[STRLEN(l) - 1] != '\\') + break; + --curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; + } + + amount = get_indent(); // XXX + + if (amount == 0) + amount = cin_first_id_amount(); + if (amount == 0) + amount = ind_continuation; + break; + } + + // If the line looks like a function declaration, and we're + // not in a comment, put it the left margin. + if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) // XXX + break; + l = ml_get_curline(); + + // Finding the closing '}' of a previous function. Put + // current line at the left margin. For when 'cino' has "fs". + if (*skipwhite(l) == '}') + break; + + // (matching {) + // If the previous line ends on '};' (maybe followed by + // comments) align at column 0. For example: + // char *string_array[] = { "foo", + // / * x * / "b};ar" }; / * foobar * / + if (cin_ends_in(l, (char_u *)"};", NULL)) + break; + + // If the previous line ends on '[' we are probably in an + // array constant: + // something = [ + // 234, <- extra indent + if (cin_ends_in(l, (char_u *)"[", NULL)) + { + amount = get_indent() + ind_continuation; + break; + } + + // Find a line only has a semicolon that belongs to a previous + // line ending in '}', e.g. before an #endif. Don't increase + // indent then. + if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) + { + pos_T curpos_save = curwin->w_cursor; + + while (curwin->w_cursor.lnum > 1) + { + look = ml_get(--curwin->w_cursor.lnum); + if (!(cin_nocode(look) || cin_ispreproc_cont( + &look, &curwin->w_cursor.lnum, &amount))) + break; + } + if (curwin->w_cursor.lnum > 0 + && cin_ends_in(look, (char_u *)"}", NULL)) + break; + + curwin->w_cursor = curpos_save; + } + + // If the PREVIOUS line is a function declaration, the current + // line (and the ones that follow) needs to be indented as + // parameters. + if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) + { + amount = curbuf->b_ind_param; + break; + } + + // If the previous line ends in ';' and the line before the + // previous line ends in ',' or '\', ident to column zero: + // int foo, + // bar; + // indent_to_0 here; + if (cin_ends_in(l, (char_u *)";", NULL)) + { + l = ml_get(curwin->w_cursor.lnum - 1); + if (cin_ends_in(l, (char_u *)",", NULL) + || (*l != NUL && l[STRLEN(l) - 1] == '\\')) + break; + l = ml_get_curline(); + } + + // Doesn't look like anything interesting -- so just + // use the indent of this line. + // + // Position the cursor over the rightmost paren, so that + // matching it will take us back to the start of the line. + find_last_paren(l, '(', ')'); + + if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) + curwin->w_cursor = *trypos; + amount = get_indent(); // XXX + break; + } + + // add extra indent for a comment + if (cin_iscomment(theline)) + amount += curbuf->b_ind_comment; + + // add extra indent if the previous line ended in a backslash: + // "asdfasdf{backslash} + // here"; + // char *foo = "asdf{backslash} + // here"; + if (cur_curpos.lnum > 1) + { + l = ml_get(cur_curpos.lnum - 1); + if (*l != NUL && l[STRLEN(l) - 1] == '\\') + { + cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1); + if (cur_amount > 0) + amount = cur_amount; + else if (cur_amount == 0) + amount += ind_continuation; + } + } + +theend: + if (amount < 0) + amount = 0; + +laterend: + // put the cursor back where it belongs + curwin->w_cursor = cur_curpos; + + vim_free(linecopy); + + return amount; +} + +/* + * return TRUE if 'cinkeys' contains the key "keytyped", + * when == '*': Only if key is preceded with '*' (indent before insert) + * when == '!': Only if key is preceded with '!' (don't insert) + * when == ' ': Only if key is not preceded with '*'(indent afterwards) + * + * "keytyped" can have a few special values: + * KEY_OPEN_FORW + * KEY_OPEN_BACK + * KEY_COMPLETE just finished completion. + * + * If line_is_empty is TRUE accept keys with '0' before them. + */ + int +in_cinkeys( + int keytyped, + int when, + int line_is_empty) +{ + char_u *look; + int try_match; + int try_match_word; + char_u *p; + char_u *line; + int icase; + int i; + + if (keytyped == NUL) + // Can happen with CTRL-Y and CTRL-E on a short line. + return FALSE; + +#ifdef FEAT_EVAL + if (*curbuf->b_p_inde != NUL) + look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys' + else +#endif + look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys' + while (*look) + { + // Find out if we want to try a match with this key, depending on + // 'when' and a '*' or '!' before the key. + switch (when) + { + case '*': try_match = (*look == '*'); break; + case '!': try_match = (*look == '!'); break; + default: try_match = (*look != '*'); break; + } + if (*look == '*' || *look == '!') + ++look; + + // If there is a '0', only accept a match if the line is empty. + // But may still match when typing last char of a word. + if (*look == '0') + { + try_match_word = try_match; + if (!line_is_empty) + try_match = FALSE; + ++look; + } + else + try_match_word = FALSE; + + // does it look like a control character? + if (*look == '^' +#ifdef EBCDIC + && (Ctrl_chr(look[1]) != 0) +#else + && look[1] >= '?' && look[1] <= '_' +#endif + ) + { + if (try_match && keytyped == Ctrl_chr(look[1])) + return TRUE; + look += 2; + } + // 'o' means "o" command, open forward. + // 'O' means "O" command, open backward. + else if (*look == 'o') + { + if (try_match && keytyped == KEY_OPEN_FORW) + return TRUE; + ++look; + } + else if (*look == 'O') + { + if (try_match && keytyped == KEY_OPEN_BACK) + return TRUE; + ++look; + } + + // 'e' means to check for "else" at start of line and just before the + // cursor. + else if (*look == 'e') + { + if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) + { + p = ml_get_curline(); + if (skipwhite(p) == p + curwin->w_cursor.col - 4 && + STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) + return TRUE; + } + ++look; + } + + // ':' only causes an indent if it is at the end of a label or case + // statement, or when it was before typing the ':' (to fix + // class::method for C++). + else if (*look == ':') + { + if (try_match && keytyped == ':') + { + p = ml_get_curline(); + if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel()) + return TRUE; + // Need to get the line again after cin_islabel(). + p = ml_get_curline(); + if (curwin->w_cursor.col > 2 + && p[curwin->w_cursor.col - 1] == ':' + && p[curwin->w_cursor.col - 2] == ':') + { + p[curwin->w_cursor.col - 1] = ' '; + i = (cin_iscase(p, FALSE) || cin_isscopedecl(p) + || cin_islabel()); + p = ml_get_curline(); + p[curwin->w_cursor.col - 1] = ':'; + if (i) + return TRUE; + } + } + ++look; + } + + + // Is it a key in <>, maybe? + else if (*look == '<') + { + if (try_match) + { + // make up some named keys , , , <0>, <>>, <<>, <*>, + // <:> and so that people can re-indent on o, O, e, 0, <, + // >, *, : and ! keys if they really really want to. + if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL + && keytyped == look[1]) + return TRUE; + + if (keytyped == get_special_key_code(look + 1)) + return TRUE; + } + while (*look && *look != '>') + look++; + while (*look == '>') + look++; + } + + // Is it a word: "=word"? + else if (*look == '=' && look[1] != ',' && look[1] != NUL) + { + ++look; + if (*look == '~') + { + icase = TRUE; + ++look; + } + else + icase = FALSE; + p = vim_strchr(look, ','); + if (p == NULL) + p = look + STRLEN(look); + if ((try_match || try_match_word) + && curwin->w_cursor.col >= (colnr_T)(p - look)) + { + int match = FALSE; + + if (keytyped == KEY_COMPLETE) + { + char_u *s; + + // Just completed a word, check if it starts with "look". + // search back for the start of a word. + line = ml_get_curline(); + if (has_mbyte) + { + char_u *n; + + for (s = line + curwin->w_cursor.col; s > line; s = n) + { + n = mb_prevptr(line, s); + if (!vim_iswordp(n)) + break; + } + } + else + for (s = line + curwin->w_cursor.col; s > line; --s) + if (!vim_iswordc(s[-1])) + break; + if (s + (p - look) <= line + curwin->w_cursor.col + && (icase + ? MB_STRNICMP(s, look, p - look) + : STRNCMP(s, look, p - look)) == 0) + match = TRUE; + } + else + // TODO: multi-byte + if (keytyped == (int)p[-1] || (icase && keytyped < 256 + && TOLOWER_LOC(keytyped) == TOLOWER_LOC((int)p[-1]))) + { + line = ml_get_cursor(); + if ((curwin->w_cursor.col == (colnr_T)(p - look) + || !vim_iswordc(line[-(p - look) - 1])) + && (icase + ? MB_STRNICMP(line - (p - look), look, p - look) + : STRNCMP(line - (p - look), look, p - look)) + == 0) + match = TRUE; + } + if (match && try_match_word && !try_match) + { + // "0=word": Check if there are only blanks before the + // word. + if (getwhitecols_curline() != + (int)(curwin->w_cursor.col - (p - look))) + match = FALSE; + } + if (match) + return TRUE; + } + look = p; + } + + // ok, it's a boring generic character. + else + { + if (try_match && *look == keytyped) + return TRUE; + if (*look != NUL) + ++look; + } + + // Skip over ", ". + look = skip_to_option_part(look); + } + return FALSE; +} + +/* + * Do C or expression indenting on the current line. + */ + void +do_c_expr_indent(void) +{ +# ifdef FEAT_EVAL + if (*curbuf->b_p_inde != NUL) + fixthisline(get_expr_indent); + else +# endif + fixthisline(get_c_indent); +} +#endif + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * "cindent(lnum)" function + */ + void +f_cindent(typval_T *argvars UNUSED, typval_T *rettv) +{ +# ifdef FEAT_CINDENT + pos_T pos; + linenr_T lnum; + + pos = curwin->w_cursor; + lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + { + curwin->w_cursor.lnum = lnum; + rettv->vval.v_number = get_c_indent(); + curwin->w_cursor = pos; + } + else +# endif + rettv->vval.v_number = -1; +} +#endif diff --git a/src/edit.c b/src/edit.c index 312095ed90..78f5cd74fe 100644 --- a/src/edit.c +++ b/src/edit.c @@ -37,7 +37,6 @@ static void check_spell_redraw(void); #endif static void stop_insert(pos_T *end_insert_pos, int esc, int nomove); static int echeck_abbr(int); -static void replace_join(int off); static void mb_replace_pop_ins(int cc); static void replace_flush(void); static void replace_do_bs(int limit_col); @@ -76,9 +75,6 @@ static int ins_tab(void); static int ins_digraph(void); #endif static int ins_ctrl_ey(int tc); -#ifdef FEAT_SMARTINDENT -static void ins_try_si(int c); -#endif #if defined(FEAT_EVAL) static char_u *do_insert_char_pre(int c); #endif @@ -97,8 +93,6 @@ static int did_restart_edit; /* "restart_edit" when calling edit() */ static int can_cindent; /* may do cindenting on this line */ #endif -static int old_indent = 0; /* for ^^D command in insert mode */ - #ifdef FEAT_RIGHTLEFT static int revins_on; /* reverse insert mode on */ static int revins_chars; /* how much to skip after edit */ @@ -1762,248 +1756,6 @@ undisplay_dollar(void) } } -/* - * Insert an indent (for or CTRL-T) or delete an indent (for CTRL-D). - * Keep the cursor on the same character. - * type == INDENT_INC increase indent (for CTRL-T or ) - * type == INDENT_DEC decrease indent (for CTRL-D) - * type == INDENT_SET set indent to "amount" - * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec). - */ - void -change_indent( - int type, - int amount, - int round, - int replaced, /* replaced character, put on replace stack */ - int call_changed_bytes) /* call changed_bytes() */ -{ - int vcol; - int last_vcol; - int insstart_less; /* reduction for Insstart.col */ - int new_cursor_col; - int i; - char_u *ptr; - int save_p_list; - int start_col; - colnr_T vc; - colnr_T orig_col = 0; /* init for GCC */ - char_u *new_line, *orig_line = NULL; /* init for GCC */ - - /* VREPLACE mode needs to know what the line was like before changing */ - if (State & VREPLACE_FLAG) - { - orig_line = vim_strsave(ml_get_curline()); /* Deal with NULL below */ - orig_col = curwin->w_cursor.col; - } - - /* for the following tricks we don't want list mode */ - save_p_list = curwin->w_p_list; - curwin->w_p_list = FALSE; - vc = getvcol_nolist(&curwin->w_cursor); - vcol = vc; - - /* - * For Replace mode we need to fix the replace stack later, which is only - * possible when the cursor is in the indent. Remember the number of - * characters before the cursor if it's possible. - */ - start_col = curwin->w_cursor.col; - - /* determine offset from first non-blank */ - new_cursor_col = curwin->w_cursor.col; - beginline(BL_WHITE); - new_cursor_col -= curwin->w_cursor.col; - - insstart_less = curwin->w_cursor.col; - - /* - * If the cursor is in the indent, compute how many screen columns the - * cursor is to the left of the first non-blank. - */ - if (new_cursor_col < 0) - vcol = get_indent() - vcol; - - if (new_cursor_col > 0) /* can't fix replace stack */ - start_col = -1; - - /* - * Set the new indent. The cursor will be put on the first non-blank. - */ - if (type == INDENT_SET) - (void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0); - else - { - int save_State = State; - - /* Avoid being called recursively. */ - if (State & VREPLACE_FLAG) - State = INSERT; - shift_line(type == INDENT_DEC, round, 1, call_changed_bytes); - State = save_State; - } - insstart_less -= curwin->w_cursor.col; - - /* - * Try to put cursor on same character. - * If the cursor is at or after the first non-blank in the line, - * compute the cursor column relative to the column of the first - * non-blank character. - * If we are not in insert mode, leave the cursor on the first non-blank. - * If the cursor is before the first non-blank, position it relative - * to the first non-blank, counted in screen columns. - */ - if (new_cursor_col >= 0) - { - /* - * When changing the indent while the cursor is touching it, reset - * Insstart_col to 0. - */ - if (new_cursor_col == 0) - insstart_less = MAXCOL; - new_cursor_col += curwin->w_cursor.col; - } - else if (!(State & INSERT)) - new_cursor_col = curwin->w_cursor.col; - else - { - /* - * Compute the screen column where the cursor should be. - */ - vcol = get_indent() - vcol; - curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); - - /* - * Advance the cursor until we reach the right screen column. - */ - vcol = last_vcol = 0; - new_cursor_col = -1; - ptr = ml_get_curline(); - while (vcol <= (int)curwin->w_virtcol) - { - last_vcol = vcol; - if (has_mbyte && new_cursor_col >= 0) - new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col); - else - ++new_cursor_col; - vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol); - } - vcol = last_vcol; - - /* - * May need to insert spaces to be able to position the cursor on - * the right screen column. - */ - if (vcol != (int)curwin->w_virtcol) - { - curwin->w_cursor.col = (colnr_T)new_cursor_col; - i = (int)curwin->w_virtcol - vcol; - ptr = alloc(i + 1); - if (ptr != NULL) - { - new_cursor_col += i; - ptr[i] = NUL; - while (--i >= 0) - ptr[i] = ' '; - ins_str(ptr); - vim_free(ptr); - } - } - - /* - * When changing the indent while the cursor is in it, reset - * Insstart_col to 0. - */ - insstart_less = MAXCOL; - } - - curwin->w_p_list = save_p_list; - - if (new_cursor_col <= 0) - curwin->w_cursor.col = 0; - else - curwin->w_cursor.col = (colnr_T)new_cursor_col; - curwin->w_set_curswant = TRUE; - changed_cline_bef_curs(); - - /* - * May have to adjust the start of the insert. - */ - if (State & INSERT) - { - if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) - { - if ((int)Insstart.col <= insstart_less) - Insstart.col = 0; - else - Insstart.col -= insstart_less; - } - if ((int)ai_col <= insstart_less) - ai_col = 0; - else - ai_col -= insstart_less; - } - - /* - * For REPLACE mode, may have to fix the replace stack, if it's possible. - * If the number of characters before the cursor decreased, need to pop a - * few characters from the replace stack. - * If the number of characters before the cursor increased, need to push a - * few NULs onto the replace stack. - */ - if (REPLACE_NORMAL(State) && start_col >= 0) - { - while (start_col > (int)curwin->w_cursor.col) - { - replace_join(0); /* remove a NUL from the replace stack */ - --start_col; - } - while (start_col < (int)curwin->w_cursor.col || replaced) - { - replace_push(NUL); - if (replaced) - { - replace_push(replaced); - replaced = NUL; - } - ++start_col; - } - } - - /* - * For VREPLACE mode, we also have to fix the replace stack. In this case - * it is always possible because we backspace over the whole line and then - * put it back again the way we wanted it. - */ - if (State & VREPLACE_FLAG) - { - /* If orig_line didn't allocate, just return. At least we did the job, - * even if you can't backspace. */ - if (orig_line == NULL) - return; - - /* Save new line */ - new_line = vim_strsave(ml_get_curline()); - if (new_line == NULL) - return; - - /* We only put back the new line up to the cursor */ - new_line[curwin->w_cursor.col] = NUL; - - /* Put back original line */ - ml_replace(curwin->w_cursor.lnum, orig_line, FALSE); - curwin->w_cursor.col = orig_col; - - /* Backspace from cursor to start of line */ - backspace_until_column(0); - - /* Insert new stuff into line again */ - ins_bytes(new_line); - - vim_free(new_line); - } -} - /* * Truncate the space at the end of a line. This is to be used only in an * insert mode. It handles fixing the replace stack for REPLACE and VREPLACE @@ -3840,7 +3592,7 @@ replace_pop(void) * Join the top two items on the replace stack. This removes to "off"'th NUL * encountered. */ - static void + void replace_join( int off) /* offset for which NUL to remove */ { @@ -6070,97 +5822,6 @@ ins_ctrl_ey(int tc) return c; } -#ifdef FEAT_SMARTINDENT -/* - * Try to do some very smart auto-indenting. - * Used when inserting a "normal" character. - */ - static void -ins_try_si(int c) -{ - pos_T *pos, old_pos; - char_u *ptr; - int i; - int temp; - - /* - * do some very smart indenting when entering '{' or '}' - */ - if (((did_si || can_si_back) && c == '{') || (can_si && c == '}')) - { - /* - * for '}' set indent equal to indent of line containing matching '{' - */ - if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) - { - old_pos = curwin->w_cursor; - /* - * If the matching '{' has a ')' immediately before it (ignoring - * white-space), then line up with the start of the line - * containing the matching '(' if there is one. This handles the - * case where an "if (..\n..) {" statement continues over multiple - * lines -- webb - */ - ptr = ml_get(pos->lnum); - i = pos->col; - if (i > 0) /* skip blanks before '{' */ - while (--i > 0 && VIM_ISWHITE(ptr[i])) - ; - curwin->w_cursor.lnum = pos->lnum; - curwin->w_cursor.col = i; - if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) - curwin->w_cursor = *pos; - i = get_indent(); - curwin->w_cursor = old_pos; - if (State & VREPLACE_FLAG) - change_indent(INDENT_SET, i, FALSE, NUL, TRUE); - else - (void)set_indent(i, SIN_CHANGED); - } - else if (curwin->w_cursor.col > 0) - { - /* - * when inserting '{' after "O" reduce indent, but not - * more than indent of previous line - */ - temp = TRUE; - if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1) - { - old_pos = curwin->w_cursor; - i = get_indent(); - while (curwin->w_cursor.lnum > 1) - { - ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum))); - - /* ignore empty lines and lines starting with '#'. */ - if (*ptr != '#' && *ptr != NUL) - break; - } - if (get_indent() >= i) - temp = FALSE; - curwin->w_cursor = old_pos; - } - if (temp) - shift_line(TRUE, FALSE, 1, TRUE); - } - } - - /* - * set indent of '#' always to 0 - */ - if (curwin->w_cursor.col > 0 && can_si && c == '#') - { - /* remember current indent for next line */ - old_indent = get_indent(); - (void)set_indent(0, SIN_CHANGED); - } - - /* Adjust ai_col, the char at this position can be deleted. */ - if (ai_col > curwin->w_cursor.col) - ai_col = curwin->w_cursor.col; -} -#endif - /* * Get the value that w_virtcol would have when 'list' is off. * Unless 'cpo' contains the 'L' flag. diff --git a/src/evalfunc.c b/src/evalfunc.c index 4a04696d6b..962c6c49bb 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -51,7 +51,6 @@ static void f_ceil(typval_T *argvars, typval_T *rettv); #endif static void f_changenr(typval_T *argvars, typval_T *rettv); static void f_char2nr(typval_T *argvars, typval_T *rettv); -static void f_cindent(typval_T *argvars, typval_T *rettv); static void f_col(typval_T *argvars, typval_T *rettv); static void f_confirm(typval_T *argvars, typval_T *rettv); static void f_copy(typval_T *argvars, typval_T *rettv); @@ -108,7 +107,6 @@ static void f_hlID(typval_T *argvars, typval_T *rettv); static void f_hlexists(typval_T *argvars, typval_T *rettv); static void f_hostname(typval_T *argvars, typval_T *rettv); static void f_iconv(typval_T *argvars, typval_T *rettv); -static void f_indent(typval_T *argvars, typval_T *rettv); static void f_index(typval_T *argvars, typval_T *rettv); static void f_input(typval_T *argvars, typval_T *rettv); static void f_inputdialog(typval_T *argvars, typval_T *rettv); @@ -128,7 +126,6 @@ static void f_libcall(typval_T *argvars, typval_T *rettv); static void f_libcallnr(typval_T *argvars, typval_T *rettv); static void f_line(typval_T *argvars, typval_T *rettv); static void f_line2byte(typval_T *argvars, typval_T *rettv); -static void f_lispindent(typval_T *argvars, typval_T *rettv); static void f_localtime(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT static void f_log(typval_T *argvars, typval_T *rettv); @@ -1491,29 +1488,6 @@ f_char2nr(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = tv_get_string(&argvars[0])[0]; } -/* - * "cindent(lnum)" function - */ - static void -f_cindent(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_CINDENT - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = tv_get_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - { - curwin->w_cursor.lnum = lnum; - rettv->vval.v_number = get_c_indent(); - curwin->w_cursor = pos; - } - else -#endif - rettv->vval.v_number = -1; -} - win_T * get_optional_window(typval_T *argvars, int idx) { @@ -3945,21 +3919,6 @@ f_iconv(typval_T *argvars UNUSED, typval_T *rettv) vim_free(to); } -/* - * "indent()" function - */ - static void -f_indent(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - - lnum = tv_get_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - rettv->vval.v_number = get_indent_lnum(lnum); - else - rettv->vval.v_number = -1; -} - /* * "index()" function */ @@ -4450,29 +4409,6 @@ f_line2byte(typval_T *argvars UNUSED, typval_T *rettv) #endif } -/* - * "lispindent(lnum)" function - */ - static void -f_lispindent(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_LISP - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = tv_get_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - { - curwin->w_cursor.lnum = lnum; - rettv->vval.v_number = get_lisp_indent(); - curwin->w_cursor = pos; - } - else -#endif - rettv->vval.v_number = -1; -} - /* * "localtime()" function */ diff --git a/src/ex_cmds.c b/src/ex_cmds.c index da01e9ddad..fc70e2cfef 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -660,221 +660,6 @@ sortend: emsg(_(e_interr)); } -/* - * ":retab". - */ - void -ex_retab(exarg_T *eap) -{ - linenr_T lnum; - int got_tab = FALSE; - long num_spaces = 0; - long num_tabs; - long len; - long col; - long vcol; - long start_col = 0; /* For start of white-space string */ - long start_vcol = 0; /* For start of white-space string */ - long old_len; - char_u *ptr; - char_u *new_line = (char_u *)1; /* init to non-NULL */ - int did_undo; /* called u_save for current line */ -#ifdef FEAT_VARTABS - int *new_vts_array = NULL; - char_u *new_ts_str; /* string value of tab argument */ -#else - int temp; - int new_ts; -#endif - int save_list; - linenr_T first_line = 0; /* first changed line */ - linenr_T last_line = 0; /* last changed line */ - - save_list = curwin->w_p_list; - curwin->w_p_list = 0; /* don't want list mode here */ - -#ifdef FEAT_VARTABS - new_ts_str = eap->arg; - if (!tabstop_set(eap->arg, &new_vts_array)) - return; - while (vim_isdigit(*(eap->arg)) || *(eap->arg) == ',') - ++(eap->arg); - - // This ensures that either new_vts_array and new_ts_str are freshly - // allocated, or new_vts_array points to an existing array and new_ts_str - // is null. - if (new_vts_array == NULL) - { - new_vts_array = curbuf->b_p_vts_array; - new_ts_str = NULL; - } - else - new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str); -#else - new_ts = getdigits(&(eap->arg)); - if (new_ts < 0) - { - emsg(_(e_positive)); - return; - } - if (new_ts == 0) - new_ts = curbuf->b_p_ts; -#endif - for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum) - { - ptr = ml_get(lnum); - col = 0; - vcol = 0; - did_undo = FALSE; - for (;;) - { - if (VIM_ISWHITE(ptr[col])) - { - if (!got_tab && num_spaces == 0) - { - /* First consecutive white-space */ - start_vcol = vcol; - start_col = col; - } - if (ptr[col] == ' ') - num_spaces++; - else - got_tab = TRUE; - } - else - { - if (got_tab || (eap->forceit && num_spaces > 1)) - { - /* Retabulate this string of white-space */ - - /* len is virtual length of white string */ - len = num_spaces = vcol - start_vcol; - num_tabs = 0; - if (!curbuf->b_p_et) - { -#ifdef FEAT_VARTABS - int t, s; - - tabstop_fromto(start_vcol, vcol, - curbuf->b_p_ts, new_vts_array, &t, &s); - num_tabs = t; - num_spaces = s; -#else - temp = new_ts - (start_vcol % new_ts); - if (num_spaces >= temp) - { - num_spaces -= temp; - num_tabs++; - } - num_tabs += num_spaces / new_ts; - num_spaces -= (num_spaces / new_ts) * new_ts; -#endif - } - if (curbuf->b_p_et || got_tab || - (num_spaces + num_tabs < len)) - { - if (did_undo == FALSE) - { - did_undo = TRUE; - if (u_save((linenr_T)(lnum - 1), - (linenr_T)(lnum + 1)) == FAIL) - { - new_line = NULL; /* flag out-of-memory */ - break; - } - } - - /* len is actual number of white characters used */ - len = num_spaces + num_tabs; - old_len = (long)STRLEN(ptr); - new_line = alloc(old_len - col + start_col + len + 1); - if (new_line == NULL) - break; - if (start_col > 0) - mch_memmove(new_line, ptr, (size_t)start_col); - mch_memmove(new_line + start_col + len, - ptr + col, (size_t)(old_len - col + 1)); - ptr = new_line + start_col; - for (col = 0; col < len; col++) - ptr[col] = (col < num_tabs) ? '\t' : ' '; - ml_replace(lnum, new_line, FALSE); - if (first_line == 0) - first_line = lnum; - last_line = lnum; - ptr = new_line; - col = start_col + len; - } - } - got_tab = FALSE; - num_spaces = 0; - } - if (ptr[col] == NUL) - break; - vcol += chartabsize(ptr + col, (colnr_T)vcol); - if (has_mbyte) - col += (*mb_ptr2len)(ptr + col); - else - ++col; - } - if (new_line == NULL) /* out of memory */ - break; - line_breakcheck(); - } - if (got_int) - emsg(_(e_interr)); - -#ifdef FEAT_VARTABS - // If a single value was given then it can be considered equal to - // either the value of 'tabstop' or the value of 'vartabstop'. - if (tabstop_count(curbuf->b_p_vts_array) == 0 - && tabstop_count(new_vts_array) == 1 - && curbuf->b_p_ts == tabstop_first(new_vts_array)) - ; /* not changed */ - else if (tabstop_count(curbuf->b_p_vts_array) > 0 - && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) - ; /* not changed */ - else - redraw_curbuf_later(NOT_VALID); -#else - if (curbuf->b_p_ts != new_ts) - redraw_curbuf_later(NOT_VALID); -#endif - if (first_line != 0) - changed_lines(first_line, 0, last_line + 1, 0L); - - curwin->w_p_list = save_list; /* restore 'list' */ - -#ifdef FEAT_VARTABS - if (new_ts_str != NULL) /* set the new tabstop */ - { - // If 'vartabstop' is in use or if the value given to retab has more - // than one tabstop then update 'vartabstop'. - int *old_vts_ary = curbuf->b_p_vts_array; - - if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) - { - set_string_option_direct((char_u *)"vts", -1, new_ts_str, - OPT_FREE|OPT_LOCAL, 0); - curbuf->b_p_vts_array = new_vts_array; - vim_free(old_vts_ary); - } - else - { - // 'vartabstop' wasn't in use and a single value was given to - // retab then update 'tabstop'. - curbuf->b_p_ts = tabstop_first(new_vts_array); - vim_free(new_vts_array); - } - vim_free(new_ts_str); - } -#else - curbuf->b_p_ts = new_ts; -#endif - coladvance(curwin->w_curswant); - - u_clearline(); -} - /* * :move command - move lines line1-line2 to line dest * diff --git a/src/globals.h b/src/globals.h index 105c451381..2611266a7d 100644 --- a/src/globals.h +++ b/src/globals.h @@ -843,6 +843,8 @@ EXTERN int can_si INIT(= FALSE); EXTERN int can_si_back INIT(= FALSE); #endif +EXTERN int old_indent INIT(= 0); // for ^^D command in insert mode + EXTERN pos_T saved_cursor // w_cursor before formatting text. #ifdef DO_INIT = {0, 0, 0} diff --git a/src/indent.c b/src/indent.c index dacafcbc02..4abe05b2d3 100644 --- a/src/indent.c +++ b/src/indent.c @@ -13,4451 +13,6 @@ #include "vim.h" -#if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT) - -static int cin_iscase(char_u *s, int strict); -static int cin_isscopedecl(char_u *s); - -/* - * Return TRUE if the string "line" starts with a word from 'cinwords'. - */ - int -cin_is_cinword(char_u *line) -{ - char_u *cinw; - char_u *cinw_buf; - int cinw_len; - int retval = FALSE; - int len; - - cinw_len = (int)STRLEN(curbuf->b_p_cinw) + 1; - cinw_buf = alloc(cinw_len); - if (cinw_buf != NULL) - { - line = skipwhite(line); - for (cinw = curbuf->b_p_cinw; *cinw; ) - { - len = copy_option_part(&cinw, cinw_buf, cinw_len, ","); - if (STRNCMP(line, cinw_buf, len) == 0 - && (!vim_iswordc(line[len]) || !vim_iswordc(line[len - 1]))) - { - retval = TRUE; - break; - } - } - vim_free(cinw_buf); - } - return retval; -} -#endif - -#if defined(FEAT_CINDENT) || defined(FEAT_SYN_HL) - -static char_u *skip_string(char_u *p); -static pos_T *find_start_rawstring(int ind_maxcomment); - -/* - * Find the start of a comment, not knowing if we are in a comment right now. - * Search starts at w_cursor.lnum and goes backwards. - * Return NULL when not inside a comment. - */ - static pos_T * -ind_find_start_comment(void) // XXX -{ - return find_start_comment(curbuf->b_ind_maxcomment); -} - - pos_T * -find_start_comment(int ind_maxcomment) // XXX -{ - pos_T *pos; - char_u *line; - char_u *p; - int cur_maxcomment = ind_maxcomment; - - for (;;) - { - pos = findmatchlimit(NULL, '*', FM_BACKWARD, cur_maxcomment); - if (pos == NULL) - break; - - // Check if the comment start we found is inside a string. - // If it is then restrict the search to below this line and try again. - line = ml_get(pos->lnum); - for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p) - p = skip_string(p); - if ((colnr_T)(p - line) <= pos->col) - break; - cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; - if (cur_maxcomment <= 0) - { - pos = NULL; - break; - } - } - return pos; -} - -/* - * Find the start of a comment or raw string, not knowing if we are in a - * comment or raw string right now. - * Search starts at w_cursor.lnum and goes backwards. - * If is_raw is given and returns start of raw_string, sets it to true. - * Return NULL when not inside a comment or raw string. - * "CORS" -> Comment Or Raw String - */ - static pos_T * -ind_find_start_CORS(linenr_T *is_raw) // XXX -{ - static pos_T comment_pos_copy; - pos_T *comment_pos; - pos_T *rs_pos; - - comment_pos = find_start_comment(curbuf->b_ind_maxcomment); - if (comment_pos != NULL) - { - // Need to make a copy of the static pos in findmatchlimit(), - // calling find_start_rawstring() may change it. - comment_pos_copy = *comment_pos; - comment_pos = &comment_pos_copy; - } - rs_pos = find_start_rawstring(curbuf->b_ind_maxcomment); - - // If comment_pos is before rs_pos the raw string is inside the comment. - // If rs_pos is before comment_pos the comment is inside the raw string. - if (comment_pos == NULL || (rs_pos != NULL - && LT_POS(*rs_pos, *comment_pos))) - { - if (is_raw != NULL && rs_pos != NULL) - *is_raw = rs_pos->lnum; - return rs_pos; - } - return comment_pos; -} - -/* - * Find the start of a raw string, not knowing if we are in one right now. - * Search starts at w_cursor.lnum and goes backwards. - * Return NULL when not inside a raw string. - */ - static pos_T * -find_start_rawstring(int ind_maxcomment) // XXX -{ - pos_T *pos; - char_u *line; - char_u *p; - int cur_maxcomment = ind_maxcomment; - - for (;;) - { - pos = findmatchlimit(NULL, 'R', FM_BACKWARD, cur_maxcomment); - if (pos == NULL) - break; - - // Check if the raw string start we found is inside a string. - // If it is then restrict the search to below this line and try again. - line = ml_get(pos->lnum); - for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p) - p = skip_string(p); - if ((colnr_T)(p - line) <= pos->col) - break; - cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; - if (cur_maxcomment <= 0) - { - pos = NULL; - break; - } - } - return pos; -} - -/* - * Skip to the end of a "string" and a 'c' character. - * If there is no string or character, return argument unmodified. - */ - static char_u * -skip_string(char_u *p) -{ - int i; - - // We loop, because strings may be concatenated: "date""time". - for ( ; ; ++p) - { - if (p[0] == '\'') // 'c' or '\n' or '\000' - { - if (!p[1]) // ' at end of line - break; - i = 2; - if (p[1] == '\\') // '\n' or '\000' - { - ++i; - while (vim_isdigit(p[i - 1])) // '\000' - ++i; - } - if (p[i] == '\'') // check for trailing ' - { - p += i; - continue; - } - } - else if (p[0] == '"') // start of string - { - for (++p; p[0]; ++p) - { - if (p[0] == '\\' && p[1] != NUL) - ++p; - else if (p[0] == '"') // end of string - break; - } - if (p[0] == '"') - continue; // continue for another string - } - else if (p[0] == 'R' && p[1] == '"') - { - // Raw string: R"[delim](...)[delim]" - char_u *delim = p + 2; - char_u *paren = vim_strchr(delim, '('); - - if (paren != NULL) - { - size_t delim_len = paren - delim; - - for (p += 3; *p; ++p) - if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 - && p[delim_len + 1] == '"') - { - p += delim_len + 1; - break; - } - if (p[0] == '"') - continue; // continue for another string - } - } - break; // no string found - } - if (!*p) - --p; // backup from NUL - return p; -} -#endif // FEAT_CINDENT || FEAT_SYN_HL - -#if defined(FEAT_CINDENT) || defined(PROTO) - -/* - * Return TRUE if C-indenting is on. - */ - int -cindent_on(void) -{ - return (!p_paste && (curbuf->b_p_cin -# ifdef FEAT_EVAL - || *curbuf->b_p_inde != NUL -# endif - )); -} - -// Find result cache for cpp_baseclass -typedef struct { - int found; - lpos_T lpos; -} cpp_baseclass_cache_T; - -/* - * Functions for C-indenting. - * Most of this originally comes from Eric Fischer. - */ -/* - * Below "XXX" means that this function may unlock the current line. - */ - -static int cin_isdefault(char_u *); -static int cin_ispreproc(char_u *); -static int cin_iscomment(char_u *); -static int cin_islinecomment(char_u *); -static int cin_isterminated(char_u *, int, int); -static int cin_iselse(char_u *); -static int cin_ends_in(char_u *, char_u *, char_u *); -static int cin_starts_with(char_u *s, char *word); -static pos_T *find_match_paren(int); -static pos_T *find_match_char(int c, int ind_maxparen); -static int find_last_paren(char_u *l, int start, int end); -static int find_match(int lookfor, linenr_T ourscope); - -/* - * Skip over white space and C comments within the line. - * Also skip over Perl/shell comments if desired. - */ - static char_u * -cin_skipcomment(char_u *s) -{ - while (*s) - { - char_u *prev_s = s; - - s = skipwhite(s); - - // Perl/shell # comment comment continues until eol. Require a space - // before # to avoid recognizing $#array. - if (curbuf->b_ind_hash_comment != 0 && s != prev_s && *s == '#') - { - s += STRLEN(s); - break; - } - if (*s != '/') - break; - ++s; - if (*s == '/') // slash-slash comment continues till eol - { - s += STRLEN(s); - break; - } - if (*s != '*') - break; - for (++s; *s; ++s) // skip slash-star comment - if (s[0] == '*' && s[1] == '/') - { - s += 2; - break; - } - } - return s; -} - -/* - * Return TRUE if there is no code at *s. White space and comments are - * not considered code. - */ - static int -cin_nocode(char_u *s) -{ - return *cin_skipcomment(s) == NUL; -} - -/* - * Check previous lines for a "//" line comment, skipping over blank lines. - */ - static pos_T * -find_line_comment(void) // XXX -{ - static pos_T pos; - char_u *line; - char_u *p; - - pos = curwin->w_cursor; - while (--pos.lnum > 0) - { - line = ml_get(pos.lnum); - p = skipwhite(line); - if (cin_islinecomment(p)) - { - pos.col = (int)(p - line); - return &pos; - } - if (*p != NUL) - break; - } - return NULL; -} - -/* - * Return TRUE if "text" starts with "key:". - */ - static int -cin_has_js_key(char_u *text) -{ - char_u *s = skipwhite(text); - int quote = -1; - - if (*s == '\'' || *s == '"') - { - // can be 'key': or "key": - quote = *s; - ++s; - } - if (!vim_isIDc(*s)) // need at least one ID character - return FALSE; - - while (vim_isIDc(*s)) - ++s; - if (*s == quote) - ++s; - - s = cin_skipcomment(s); - - // "::" is not a label, it's C++ - return (*s == ':' && s[1] != ':'); -} - -/* - * Check if string matches "label:"; move to character after ':' if true. - * "*s" must point to the start of the label, if there is one. - */ - static int -cin_islabel_skip(char_u **s) -{ - if (!vim_isIDc(**s)) // need at least one ID character - return FALSE; - - while (vim_isIDc(**s)) - (*s)++; - - *s = cin_skipcomment(*s); - - // "::" is not a label, it's C++ - return (**s == ':' && *++*s != ':'); -} - -/* - * Recognize a label: "label:". - * Note: curwin->w_cursor must be where we are looking for the label. - */ - static int -cin_islabel(void) // XXX -{ - char_u *s; - - s = cin_skipcomment(ml_get_curline()); - - // Exclude "default" from labels, since it should be indented - // like a switch label. Same for C++ scope declarations. - if (cin_isdefault(s)) - return FALSE; - if (cin_isscopedecl(s)) - return FALSE; - - if (cin_islabel_skip(&s)) - { - // Only accept a label if the previous line is terminated or is a case - // label. - pos_T cursor_save; - pos_T *trypos; - char_u *line; - - cursor_save = curwin->w_cursor; - while (curwin->w_cursor.lnum > 1) - { - --curwin->w_cursor.lnum; - - // If we're in a comment or raw string now, skip to the start of - // it. - curwin->w_cursor.col = 0; - if ((trypos = ind_find_start_CORS(NULL)) != NULL) // XXX - curwin->w_cursor = *trypos; - - line = ml_get_curline(); - if (cin_ispreproc(line)) // ignore #defines, #if, etc. - continue; - if (*(line = cin_skipcomment(line)) == NUL) - continue; - - curwin->w_cursor = cursor_save; - if (cin_isterminated(line, TRUE, FALSE) - || cin_isscopedecl(line) - || cin_iscase(line, TRUE) - || (cin_islabel_skip(&line) && cin_nocode(line))) - return TRUE; - return FALSE; - } - curwin->w_cursor = cursor_save; - return TRUE; // label at start of file??? - } - return FALSE; -} - -/* - * Recognize structure initialization and enumerations: - * "[typedef] [static|public|protected|private] enum" - * "[typedef] [static|public|protected|private] = {" - */ - static int -cin_isinit(void) -{ - char_u *s; - static char *skip[] = {"static", "public", "protected", "private"}; - - s = cin_skipcomment(ml_get_curline()); - - if (cin_starts_with(s, "typedef")) - s = cin_skipcomment(s + 7); - - for (;;) - { - int i, l; - - for (i = 0; i < (int)(sizeof(skip) / sizeof(char *)); ++i) - { - l = (int)strlen(skip[i]); - if (cin_starts_with(s, skip[i])) - { - s = cin_skipcomment(s + l); - l = 0; - break; - } - } - if (l != 0) - break; - } - - if (cin_starts_with(s, "enum")) - return TRUE; - - if (cin_ends_in(s, (char_u *)"=", (char_u *)"{")) - return TRUE; - - return FALSE; -} - -/* - * Recognize a switch label: "case .*:" or "default:". - */ - static int -cin_iscase( - char_u *s, - int strict) // Allow relaxed check of case statement for JS -{ - s = cin_skipcomment(s); - if (cin_starts_with(s, "case")) - { - for (s += 4; *s; ++s) - { - s = cin_skipcomment(s); - if (*s == ':') - { - if (s[1] == ':') // skip over "::" for C++ - ++s; - else - return TRUE; - } - if (*s == '\'' && s[1] && s[2] == '\'') - s += 2; // skip over ':' - else if (*s == '/' && (s[1] == '*' || s[1] == '/')) - return FALSE; // stop at comment - else if (*s == '"') - { - // JS etc. - if (strict) - return FALSE; // stop at string - else - return TRUE; - } - } - return FALSE; - } - - if (cin_isdefault(s)) - return TRUE; - return FALSE; -} - -/* - * Recognize a "default" switch label. - */ - static int -cin_isdefault(char_u *s) -{ - return (STRNCMP(s, "default", 7) == 0 - && *(s = cin_skipcomment(s + 7)) == ':' - && s[1] != ':'); -} - -/* - * Recognize a "public/private/protected" scope declaration label. - */ - static int -cin_isscopedecl(char_u *s) -{ - int i; - - s = cin_skipcomment(s); - if (STRNCMP(s, "public", 6) == 0) - i = 6; - else if (STRNCMP(s, "protected", 9) == 0) - i = 9; - else if (STRNCMP(s, "private", 7) == 0) - i = 7; - else - return FALSE; - return (*(s = cin_skipcomment(s + i)) == ':' && s[1] != ':'); -} - -// Maximum number of lines to search back for a "namespace" line. -#define FIND_NAMESPACE_LIM 20 - -/* - * Recognize a "namespace" scope declaration. - */ - static int -cin_is_cpp_namespace(char_u *s) -{ - char_u *p; - int has_name = FALSE; - int has_name_start = FALSE; - - s = cin_skipcomment(s); - if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) - { - p = cin_skipcomment(skipwhite(s + 9)); - while (*p != NUL) - { - if (VIM_ISWHITE(*p)) - { - has_name = TRUE; // found end of a name - p = cin_skipcomment(skipwhite(p)); - } - else if (*p == '{') - { - break; - } - else if (vim_iswordc(*p)) - { - has_name_start = TRUE; - if (has_name) - return FALSE; // word character after skipping past name - ++p; - } - else if (p[0] == ':' && p[1] == ':' && vim_iswordc(p[2])) - { - if (!has_name_start || has_name) - return FALSE; - // C++ 17 nested namespace - p += 3; - } - else - { - return FALSE; - } - } - return TRUE; - } - return FALSE; -} - -/* - * Recognize a `extern "C"` or `extern "C++"` linkage specifications. - */ - static int -cin_is_cpp_extern_c(char_u *s) -{ - char_u *p; - int has_string_literal = FALSE; - - s = cin_skipcomment(s); - if (STRNCMP(s, "extern", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6]))) - { - p = cin_skipcomment(skipwhite(s + 6)); - while (*p != NUL) - { - if (VIM_ISWHITE(*p)) - { - p = cin_skipcomment(skipwhite(p)); - } - else if (*p == '{') - { - break; - } - else if (p[0] == '"' && p[1] == 'C' && p[2] == '"') - { - if (has_string_literal) - return FALSE; - has_string_literal = TRUE; - p += 3; - } - else if (p[0] == '"' && p[1] == 'C' && p[2] == '+' && p[3] == '+' - && p[4] == '"') - { - if (has_string_literal) - return FALSE; - has_string_literal = TRUE; - p += 5; - } - else - { - return FALSE; - } - } - return has_string_literal ? TRUE : FALSE; - } - return FALSE; -} - -/* - * Return a pointer to the first non-empty non-comment character after a ':'. - * Return NULL if not found. - * case 234: a = b; - * ^ - */ - static char_u * -after_label(char_u *l) -{ - for ( ; *l; ++l) - { - if (*l == ':') - { - if (l[1] == ':') // skip over "::" for C++ - ++l; - else if (!cin_iscase(l + 1, FALSE)) - break; - } - else if (*l == '\'' && l[1] && l[2] == '\'') - l += 2; // skip over 'x' - } - if (*l == NUL) - return NULL; - l = cin_skipcomment(l + 1); - if (*l == NUL) - return NULL; - return l; -} - -/* - * Get indent of line "lnum", skipping a label. - * Return 0 if there is nothing after the label. - */ - static int -get_indent_nolabel (linenr_T lnum) // XXX -{ - char_u *l; - pos_T fp; - colnr_T col; - char_u *p; - - l = ml_get(lnum); - p = after_label(l); - if (p == NULL) - return 0; - - fp.col = (colnr_T)(p - l); - fp.lnum = lnum; - getvcol(curwin, &fp, &col, NULL, NULL); - return (int)col; -} - -/* - * Find indent for line "lnum", ignoring any case or jump label. - * Also return a pointer to the text (after the label) in "pp". - * label: if (asdf && asdfasdf) - * ^ - */ - static int -skip_label(linenr_T lnum, char_u **pp) -{ - char_u *l; - int amount; - pos_T cursor_save; - - cursor_save = curwin->w_cursor; - curwin->w_cursor.lnum = lnum; - l = ml_get_curline(); - // XXX - if (cin_iscase(l, FALSE) || cin_isscopedecl(l) || cin_islabel()) - { - amount = get_indent_nolabel(lnum); - l = after_label(ml_get_curline()); - if (l == NULL) // just in case - l = ml_get_curline(); - } - else - { - amount = get_indent(); - l = ml_get_curline(); - } - *pp = l; - - curwin->w_cursor = cursor_save; - return amount; -} - -/* - * Return the indent of the first variable name after a type in a declaration. - * int a, indent of "a" - * static struct foo b, indent of "b" - * enum bla c, indent of "c" - * Returns zero when it doesn't look like a declaration. - */ - static int -cin_first_id_amount(void) -{ - char_u *line, *p, *s; - int len; - pos_T fp; - colnr_T col; - - line = ml_get_curline(); - p = skipwhite(line); - len = (int)(skiptowhite(p) - p); - if (len == 6 && STRNCMP(p, "static", 6) == 0) - { - p = skipwhite(p + 6); - len = (int)(skiptowhite(p) - p); - } - if (len == 6 && STRNCMP(p, "struct", 6) == 0) - p = skipwhite(p + 6); - else if (len == 4 && STRNCMP(p, "enum", 4) == 0) - p = skipwhite(p + 4); - else if ((len == 8 && STRNCMP(p, "unsigned", 8) == 0) - || (len == 6 && STRNCMP(p, "signed", 6) == 0)) - { - s = skipwhite(p + len); - if ((STRNCMP(s, "int", 3) == 0 && VIM_ISWHITE(s[3])) - || (STRNCMP(s, "long", 4) == 0 && VIM_ISWHITE(s[4])) - || (STRNCMP(s, "short", 5) == 0 && VIM_ISWHITE(s[5])) - || (STRNCMP(s, "char", 4) == 0 && VIM_ISWHITE(s[4]))) - p = s; - } - for (len = 0; vim_isIDc(p[len]); ++len) - ; - if (len == 0 || !VIM_ISWHITE(p[len]) || cin_nocode(p)) - return 0; - - p = skipwhite(p + len); - fp.lnum = curwin->w_cursor.lnum; - fp.col = (colnr_T)(p - line); - getvcol(curwin, &fp, &col, NULL, NULL); - return (int)col; -} - -/* - * Return the indent of the first non-blank after an equal sign. - * char *foo = "here"; - * Return zero if no (useful) equal sign found. - * Return -1 if the line above "lnum" ends in a backslash. - * foo = "asdf\ - * asdf\ - * here"; - */ - static int -cin_get_equal_amount(linenr_T lnum) -{ - char_u *line; - char_u *s; - colnr_T col; - pos_T fp; - - if (lnum > 1) - { - line = ml_get(lnum - 1); - if (*line != NUL && line[STRLEN(line) - 1] == '\\') - return -1; - } - - line = s = ml_get(lnum); - while (*s != NUL && vim_strchr((char_u *)"=;{}\"'", *s) == NULL) - { - if (cin_iscomment(s)) // ignore comments - s = cin_skipcomment(s); - else - ++s; - } - if (*s != '=') - return 0; - - s = skipwhite(s + 1); - if (cin_nocode(s)) - return 0; - - if (*s == '"') // nice alignment for continued strings - ++s; - - fp.lnum = lnum; - fp.col = (colnr_T)(s - line); - getvcol(curwin, &fp, &col, NULL, NULL); - return (int)col; -} - -/* - * Recognize a preprocessor statement: Any line that starts with '#'. - */ - static int -cin_ispreproc(char_u *s) -{ - if (*skipwhite(s) == '#') - return TRUE; - return FALSE; -} - -/* - * Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a - * continuation line of a preprocessor statement. Decrease "*lnump" to the - * start and return the line in "*pp". - * Put the amount of indent in "*amount". - */ - static int -cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount) -{ - char_u *line = *pp; - linenr_T lnum = *lnump; - int retval = FALSE; - int candidate_amount = *amount; - - if (*line != NUL && line[STRLEN(line) - 1] == '\\') - candidate_amount = get_indent_lnum(lnum); - - for (;;) - { - if (cin_ispreproc(line)) - { - retval = TRUE; - *lnump = lnum; - break; - } - if (lnum == 1) - break; - line = ml_get(--lnum); - if (*line == NUL || line[STRLEN(line) - 1] != '\\') - break; - } - - if (lnum != *lnump) - *pp = ml_get(*lnump); - if (retval) - *amount = candidate_amount; - return retval; -} - -/* - * Recognize the start of a C or C++ comment. - */ - static int -cin_iscomment(char_u *p) -{ - return (p[0] == '/' && (p[1] == '*' || p[1] == '/')); -} - -/* - * Recognize the start of a "//" comment. - */ - static int -cin_islinecomment(char_u *p) -{ - return (p[0] == '/' && p[1] == '/'); -} - -/* - * Recognize a line that starts with '{' or '}', or ends with ';', ',', '{' or - * '}'. - * Don't consider "} else" a terminated line. - * If a line begins with an "else", only consider it terminated if no unmatched - * opening braces follow (handle "else { foo();" correctly). - * Return the character terminating the line (ending char's have precedence if - * both apply in order to determine initializations). - */ - static int -cin_isterminated( - char_u *s, - int incl_open, // include '{' at the end as terminator - int incl_comma) // recognize a trailing comma -{ - char_u found_start = 0; - unsigned n_open = 0; - int is_else = FALSE; - - s = cin_skipcomment(s); - - if (*s == '{' || (*s == '}' && !cin_iselse(s))) - found_start = *s; - - if (!found_start) - is_else = cin_iselse(s); - - while (*s) - { - // skip over comments, "" strings and 'c'haracters - s = skip_string(cin_skipcomment(s)); - if (*s == '}' && n_open > 0) - --n_open; - if ((!is_else || n_open == 0) - && (*s == ';' || *s == '}' || (incl_comma && *s == ',')) - && cin_nocode(s + 1)) - return *s; - else if (*s == '{') - { - if (incl_open && cin_nocode(s + 1)) - return *s; - else - ++n_open; - } - - if (*s) - s++; - } - return found_start; -} - -/* - * Recognize the basic picture of a function declaration -- it needs to - * have an open paren somewhere and a close paren at the end of the line and - * no semicolons anywhere. - * When a line ends in a comma we continue looking in the next line. - * "sp" points to a string with the line. When looking at other lines it must - * be restored to the line. When it's NULL fetch lines here. - * "first_lnum" is where we start looking. - * "min_lnum" is the line before which we will not be looking. - */ - static int -cin_isfuncdecl( - char_u **sp, - linenr_T first_lnum, - linenr_T min_lnum) -{ - char_u *s; - linenr_T lnum = first_lnum; - linenr_T save_lnum = curwin->w_cursor.lnum; - int retval = FALSE; - pos_T *trypos; - int just_started = TRUE; - - if (sp == NULL) - s = ml_get(lnum); - else - s = *sp; - - curwin->w_cursor.lnum = lnum; - if (find_last_paren(s, '(', ')') - && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) - { - lnum = trypos->lnum; - if (lnum < min_lnum) - { - curwin->w_cursor.lnum = save_lnum; - return FALSE; - } - - s = ml_get(lnum); - } - curwin->w_cursor.lnum = save_lnum; - - // Ignore line starting with #. - if (cin_ispreproc(s)) - return FALSE; - - while (*s && *s != '(' && *s != ';' && *s != '\'' && *s != '"') - { - if (cin_iscomment(s)) // ignore comments - s = cin_skipcomment(s); - else if (*s == ':') - { - if (*(s + 1) == ':') - s += 2; - else - // To avoid a mistake in the following situation: - // A::A(int a, int b) - // : a(0) // <--not a function decl - // , b(0) - // {... - return FALSE; - } - else - ++s; - } - if (*s != '(') - return FALSE; // ';', ' or " before any () or no '(' - - while (*s && *s != ';' && *s != '\'' && *s != '"') - { - if (*s == ')' && cin_nocode(s + 1)) - { - /* - * ')' at the end: may have found a match - * Check for he previous line not to end in a backslash: - * #if defined(x) && \ - * defined(y) - */ - lnum = first_lnum - 1; - s = ml_get(lnum); - if (*s == NUL || s[STRLEN(s) - 1] != '\\') - retval = TRUE; - goto done; - } - if ((*s == ',' && cin_nocode(s + 1)) || s[1] == NUL || cin_nocode(s)) - { - int comma = (*s == ','); - - // ',' at the end: continue looking in the next line. - // At the end: check for ',' in the next line, for this style: - // func(arg1 - // , arg2) - for (;;) - { - if (lnum >= curbuf->b_ml.ml_line_count) - break; - s = ml_get(++lnum); - if (!cin_ispreproc(s)) - break; - } - if (lnum >= curbuf->b_ml.ml_line_count) - break; - // Require a comma at end of the line or a comma or ')' at the - // start of next line. - s = skipwhite(s); - if (!just_started && (!comma && *s != ',' && *s != ')')) - break; - just_started = FALSE; - } - else if (cin_iscomment(s)) // ignore comments - s = cin_skipcomment(s); - else - { - ++s; - just_started = FALSE; - } - } - -done: - if (lnum != first_lnum && sp != NULL) - *sp = ml_get(first_lnum); - - return retval; -} - - static int -cin_isif(char_u *p) -{ - return (STRNCMP(p, "if", 2) == 0 && !vim_isIDc(p[2])); -} - - static int -cin_iselse( - char_u *p) -{ - if (*p == '}') // accept "} else" - p = cin_skipcomment(p + 1); - return (STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4])); -} - - static int -cin_isdo(char_u *p) -{ - return (STRNCMP(p, "do", 2) == 0 && !vim_isIDc(p[2])); -} - -/* - * Check if this is a "while" that should have a matching "do". - * We only accept a "while (condition) ;", with only white space between the - * ')' and ';'. The condition may be spread over several lines. - */ - static int -cin_iswhileofdo (char_u *p, linenr_T lnum) // XXX -{ - pos_T cursor_save; - pos_T *trypos; - int retval = FALSE; - - p = cin_skipcomment(p); - if (*p == '}') // accept "} while (cond);" - p = cin_skipcomment(p + 1); - if (cin_starts_with(p, "while")) - { - cursor_save = curwin->w_cursor; - curwin->w_cursor.lnum = lnum; - curwin->w_cursor.col = 0; - p = ml_get_curline(); - while (*p && *p != 'w') // skip any '}', until the 'w' of the "while" - { - ++p; - ++curwin->w_cursor.col; - } - if ((trypos = findmatchlimit(NULL, 0, 0, - curbuf->b_ind_maxparen)) != NULL - && *cin_skipcomment(ml_get_pos(trypos) + 1) == ';') - retval = TRUE; - curwin->w_cursor = cursor_save; - } - return retval; -} - -/* - * Check whether in "p" there is an "if", "for" or "while" before "*poffset". - * Return 0 if there is none. - * Otherwise return !0 and update "*poffset" to point to the place where the - * string was found. - */ - static int -cin_is_if_for_while_before_offset(char_u *line, int *poffset) -{ - int offset = *poffset; - - if (offset-- < 2) - return 0; - while (offset > 2 && VIM_ISWHITE(line[offset])) - --offset; - - offset -= 1; - if (!STRNCMP(line + offset, "if", 2)) - goto probablyFound; - - if (offset >= 1) - { - offset -= 1; - if (!STRNCMP(line + offset, "for", 3)) - goto probablyFound; - - if (offset >= 2) - { - offset -= 2; - if (!STRNCMP(line + offset, "while", 5)) - goto probablyFound; - } - } - return 0; - -probablyFound: - if (!offset || !vim_isIDc(line[offset - 1])) - { - *poffset = offset; - return 1; - } - return 0; -} - -/* - * Return TRUE if we are at the end of a do-while. - * do - * nothing; - * while (foo - * && bar); <-- here - * Adjust the cursor to the line with "while". - */ - static int -cin_iswhileofdo_end(int terminated) -{ - char_u *line; - char_u *p; - char_u *s; - pos_T *trypos; - int i; - - if (terminated != ';') // there must be a ';' at the end - return FALSE; - - p = line = ml_get_curline(); - while (*p != NUL) - { - p = cin_skipcomment(p); - if (*p == ')') - { - s = skipwhite(p + 1); - if (*s == ';' && cin_nocode(s + 1)) - { - // Found ");" at end of the line, now check there is "while" - // before the matching '('. XXX - i = (int)(p - line); - curwin->w_cursor.col = i; - trypos = find_match_paren(curbuf->b_ind_maxparen); - if (trypos != NULL) - { - s = cin_skipcomment(ml_get(trypos->lnum)); - if (*s == '}') // accept "} while (cond);" - s = cin_skipcomment(s + 1); - if (cin_starts_with(s, "while")) - { - curwin->w_cursor.lnum = trypos->lnum; - return TRUE; - } - } - - // Searching may have made "line" invalid, get it again. - line = ml_get_curline(); - p = line + i; - } - } - if (*p != NUL) - ++p; - } - return FALSE; -} - - static int -cin_isbreak(char_u *p) -{ - return (STRNCMP(p, "break", 5) == 0 && !vim_isIDc(p[5])); -} - -/* - * Find the position of a C++ base-class declaration or - * constructor-initialization. eg: - * - * class MyClass : - * baseClass <-- here - * class MyClass : public baseClass, - * anotherBaseClass <-- here (should probably lineup ??) - * MyClass::MyClass(...) : - * baseClass(...) <-- here (constructor-initialization) - * - * This is a lot of guessing. Watch out for "cond ? func() : foo". - */ - static int -cin_is_cpp_baseclass( - cpp_baseclass_cache_T *cached) // input and output -{ - lpos_T *pos = &cached->lpos; // find position - char_u *s; - int class_or_struct, lookfor_ctor_init, cpp_base_class; - linenr_T lnum = curwin->w_cursor.lnum; - char_u *line = ml_get_curline(); - - if (pos->lnum <= lnum) - return cached->found; // Use the cached result - - pos->col = 0; - - s = skipwhite(line); - if (*s == '#') // skip #define FOO x ? (x) : x - return FALSE; - s = cin_skipcomment(s); - if (*s == NUL) - return FALSE; - - cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE; - - // Search for a line starting with '#', empty, ending in ';' or containing - // '{' or '}' and start below it. This handles the following situations: - // a = cond ? - // func() : - // asdf; - // func::foo() - // : something - // {} - // Foo::Foo (int one, int two) - // : something(4), - // somethingelse(3) - // {} - while (lnum > 1) - { - line = ml_get(lnum - 1); - s = skipwhite(line); - if (*s == '#' || *s == NUL) - break; - while (*s != NUL) - { - s = cin_skipcomment(s); - if (*s == '{' || *s == '}' - || (*s == ';' && cin_nocode(s + 1))) - break; - if (*s != NUL) - ++s; - } - if (*s != NUL) - break; - --lnum; - } - - pos->lnum = lnum; - line = ml_get(lnum); - s = line; - for (;;) - { - if (*s == NUL) - { - if (lnum == curwin->w_cursor.lnum) - break; - // Continue in the cursor line. - line = ml_get(++lnum); - s = line; - } - if (s == line) - { - // don't recognize "case (foo):" as a baseclass - if (cin_iscase(s, FALSE)) - break; - s = cin_skipcomment(line); - if (*s == NUL) - continue; - } - - if (s[0] == '"' || (s[0] == 'R' && s[1] == '"')) - s = skip_string(s) + 1; - else if (s[0] == ':') - { - if (s[1] == ':') - { - // skip double colon. It can't be a constructor - // initialization any more - lookfor_ctor_init = FALSE; - s = cin_skipcomment(s + 2); - } - else if (lookfor_ctor_init || class_or_struct) - { - // we have something found, that looks like the start of - // cpp-base-class-declaration or constructor-initialization - cpp_base_class = TRUE; - lookfor_ctor_init = class_or_struct = FALSE; - pos->col = 0; - s = cin_skipcomment(s + 1); - } - else - s = cin_skipcomment(s + 1); - } - else if ((STRNCMP(s, "class", 5) == 0 && !vim_isIDc(s[5])) - || (STRNCMP(s, "struct", 6) == 0 && !vim_isIDc(s[6]))) - { - class_or_struct = TRUE; - lookfor_ctor_init = FALSE; - - if (*s == 'c') - s = cin_skipcomment(s + 5); - else - s = cin_skipcomment(s + 6); - } - else - { - if (s[0] == '{' || s[0] == '}' || s[0] == ';') - { - cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE; - } - else if (s[0] == ')') - { - // Constructor-initialization is assumed if we come across - // something like "):" - class_or_struct = FALSE; - lookfor_ctor_init = TRUE; - } - else if (s[0] == '?') - { - // Avoid seeing '() :' after '?' as constructor init. - return FALSE; - } - else if (!vim_isIDc(s[0])) - { - // if it is not an identifier, we are wrong - class_or_struct = FALSE; - lookfor_ctor_init = FALSE; - } - else if (pos->col == 0) - { - // it can't be a constructor-initialization any more - lookfor_ctor_init = FALSE; - - // the first statement starts here: lineup with this one... - if (cpp_base_class) - pos->col = (colnr_T)(s - line); - } - - // When the line ends in a comma don't align with it. - if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1)) - pos->col = 0; - - s = cin_skipcomment(s + 1); - } - } - - cached->found = cpp_base_class; - if (cpp_base_class) - pos->lnum = lnum; - return cpp_base_class; -} - - static int -get_baseclass_amount(int col) -{ - int amount; - colnr_T vcol; - pos_T *trypos; - - if (col == 0) - { - amount = get_indent(); - if (find_last_paren(ml_get_curline(), '(', ')') - && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) - amount = get_indent_lnum(trypos->lnum); // XXX - if (!cin_ends_in(ml_get_curline(), (char_u *)",", NULL)) - amount += curbuf->b_ind_cpp_baseclass; - } - else - { - curwin->w_cursor.col = col; - getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); - amount = (int)vcol; - } - if (amount < curbuf->b_ind_cpp_baseclass) - amount = curbuf->b_ind_cpp_baseclass; - return amount; -} - -/* - * Return TRUE if string "s" ends with the string "find", possibly followed by - * white space and comments. Skip strings and comments. - * Ignore "ignore" after "find" if it's not NULL. - */ - static int -cin_ends_in(char_u *s, char_u *find, char_u *ignore) -{ - char_u *p = s; - char_u *r; - int len = (int)STRLEN(find); - - while (*p != NUL) - { - p = cin_skipcomment(p); - if (STRNCMP(p, find, len) == 0) - { - r = skipwhite(p + len); - if (ignore != NULL && STRNCMP(r, ignore, STRLEN(ignore)) == 0) - r = skipwhite(r + STRLEN(ignore)); - if (cin_nocode(r)) - return TRUE; - } - if (*p != NUL) - ++p; - } - return FALSE; -} - -/* - * Return TRUE when "s" starts with "word" and then a non-ID character. - */ - static int -cin_starts_with(char_u *s, char *word) -{ - int l = (int)STRLEN(word); - - return (STRNCMP(s, word, l) == 0 && !vim_isIDc(s[l])); -} - -/* - * Skip strings, chars and comments until at or past "trypos". - * Return the column found. - */ - static int -cin_skip2pos(pos_T *trypos) -{ - char_u *line; - char_u *p; - char_u *new_p; - - p = line = ml_get(trypos->lnum); - while (*p && (colnr_T)(p - line) < trypos->col) - { - if (cin_iscomment(p)) - p = cin_skipcomment(p); - else - { - new_p = skip_string(p); - if (new_p == p) - ++p; - else - p = new_p; - } - } - return (int)(p - line); -} - -/* - * Find the '{' at the start of the block we are in. - * Return NULL if no match found. - * Ignore a '{' that is in a comment, makes indenting the next three lines - * work. - */ -/* foo() */ -/* { */ -/* } */ - - static pos_T * -find_start_brace(void) // XXX -{ - pos_T cursor_save; - pos_T *trypos; - pos_T *pos; - static pos_T pos_copy; - - cursor_save = curwin->w_cursor; - while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP, 0)) != NULL) - { - pos_copy = *trypos; // copy pos_T, next findmatch will change it - trypos = &pos_copy; - curwin->w_cursor = *trypos; - pos = NULL; - // ignore the { if it's in a // or / * * / comment - if ((colnr_T)cin_skip2pos(trypos) == trypos->col - && (pos = ind_find_start_CORS(NULL)) == NULL) // XXX - break; - if (pos != NULL) - curwin->w_cursor.lnum = pos->lnum; - } - curwin->w_cursor = cursor_save; - return trypos; -} - -/* - * Find the matching '(', ignoring it if it is in a comment. - * Return NULL if no match found. - */ - static pos_T * -find_match_paren(int ind_maxparen) // XXX -{ - return find_match_char('(', ind_maxparen); -} - - static pos_T * -find_match_char(int c, int ind_maxparen) // XXX -{ - pos_T cursor_save; - pos_T *trypos; - static pos_T pos_copy; - int ind_maxp_wk; - - cursor_save = curwin->w_cursor; - ind_maxp_wk = ind_maxparen; -retry: - if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) - { - // check if the ( is in a // comment - if ((colnr_T)cin_skip2pos(trypos) > trypos->col) - { - ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos->lnum); - if (ind_maxp_wk > 0) - { - curwin->w_cursor = *trypos; - curwin->w_cursor.col = 0; // XXX - goto retry; - } - trypos = NULL; - } - else - { - pos_T *trypos_wk; - - pos_copy = *trypos; // copy trypos, findmatch will change it - trypos = &pos_copy; - curwin->w_cursor = *trypos; - if ((trypos_wk = ind_find_start_CORS(NULL)) != NULL) // XXX - { - ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - - trypos_wk->lnum); - if (ind_maxp_wk > 0) - { - curwin->w_cursor = *trypos_wk; - goto retry; - } - trypos = NULL; - } - } - } - curwin->w_cursor = cursor_save; - return trypos; -} - -/* - * Find the matching '(', ignoring it if it is in a comment or before an - * unmatched {. - * Return NULL if no match found. - */ - static pos_T * -find_match_paren_after_brace (int ind_maxparen) // XXX -{ - pos_T *trypos = find_match_paren(ind_maxparen); - - if (trypos != NULL) - { - pos_T *tryposBrace = find_start_brace(); - - // If both an unmatched '(' and '{' is found. Ignore the '(' - // position if the '{' is further down. - if (tryposBrace != NULL - && (trypos->lnum != tryposBrace->lnum - ? trypos->lnum < tryposBrace->lnum - : trypos->col < tryposBrace->col)) - trypos = NULL; - } - return trypos; -} - -/* - * Return ind_maxparen corrected for the difference in line number between the - * cursor position and "startpos". This makes sure that searching for a - * matching paren above the cursor line doesn't find a match because of - * looking a few lines further. - */ - static int -corr_ind_maxparen(pos_T *startpos) -{ - long n = (long)startpos->lnum - (long)curwin->w_cursor.lnum; - - if (n > 0 && n < curbuf->b_ind_maxparen / 2) - return curbuf->b_ind_maxparen - (int)n; - return curbuf->b_ind_maxparen; -} - -/* - * Set w_cursor.col to the column number of the last unmatched ')' or '{' in - * line "l". "l" must point to the start of the line. - */ - static int -find_last_paren(char_u *l, int start, int end) -{ - int i; - int retval = FALSE; - int open_count = 0; - - curwin->w_cursor.col = 0; // default is start of line - - for (i = 0; l[i] != NUL; i++) - { - i = (int)(cin_skipcomment(l + i) - l); // ignore parens in comments - i = (int)(skip_string(l + i) - l); // ignore parens in quotes - if (l[i] == start) - ++open_count; - else if (l[i] == end) - { - if (open_count > 0) - --open_count; - else - { - curwin->w_cursor.col = i; - retval = TRUE; - } - } - } - return retval; -} - -/* - * Parse 'cinoptions' and set the values in "curbuf". - * Must be called when 'cinoptions', 'shiftwidth' and/or 'tabstop' changes. - */ - void -parse_cino(buf_T *buf) -{ - char_u *p; - char_u *l; - char_u *digits; - int n; - int divider; - int fraction = 0; - int sw = (int)get_sw_value(buf); - - // Set the default values. - - // Spaces from a block's opening brace the prevailing indent for that - // block should be. - buf->b_ind_level = sw; - - // Spaces from the edge of the line an open brace that's at the end of a - // line is imagined to be. - buf->b_ind_open_imag = 0; - - // Spaces from the prevailing indent for a line that is not preceded by - // an opening brace. - buf->b_ind_no_brace = 0; - - // Column where the first { of a function should be located }. - buf->b_ind_first_open = 0; - - // Spaces from the prevailing indent a leftmost open brace should be - // located. - buf->b_ind_open_extra = 0; - - // Spaces from the matching open brace (real location for one at the left - // edge; imaginary location from one that ends a line) the matching close - // brace should be located. - buf->b_ind_close_extra = 0; - - // Spaces from the edge of the line an open brace sitting in the leftmost - // column is imagined to be. - buf->b_ind_open_left_imag = 0; - - // Spaces jump labels should be shifted to the left if N is non-negative, - // otherwise the jump label will be put to column 1. - buf->b_ind_jump_label = -1; - - // Spaces from the switch() indent a "case xx" label should be located. - buf->b_ind_case = sw; - - // Spaces from the "case xx:" code after a switch() should be located. - buf->b_ind_case_code = sw; - - // Lineup break at end of case in switch() with case label. - buf->b_ind_case_break = 0; - - // Spaces from the class declaration indent a scope declaration label - // should be located. - buf->b_ind_scopedecl = sw; - - // Spaces from the scope declaration label code should be located. - buf->b_ind_scopedecl_code = sw; - - // Amount K&R-style parameters should be indented. - buf->b_ind_param = sw; - - // Amount a function type spec should be indented. - buf->b_ind_func_type = sw; - - // Amount a cpp base class declaration or constructor initialization - // should be indented. - buf->b_ind_cpp_baseclass = sw; - - // additional spaces beyond the prevailing indent a continuation line - // should be located. - buf->b_ind_continuation = sw; - - // Spaces from the indent of the line with an unclosed parentheses. - buf->b_ind_unclosed = sw * 2; - - // Spaces from the indent of the line with an unclosed parentheses, which - // itself is also unclosed. - buf->b_ind_unclosed2 = sw; - - // Suppress ignoring spaces from the indent of a line starting with an - // unclosed parentheses. - buf->b_ind_unclosed_noignore = 0; - - // If the opening paren is the last nonwhite character on the line, and - // b_ind_unclosed_wrapped is nonzero, use this indent relative to the outer - // context (for very long lines). - buf->b_ind_unclosed_wrapped = 0; - - // Suppress ignoring white space when lining up with the character after - // an unclosed parentheses. - buf->b_ind_unclosed_whiteok = 0; - - // Indent a closing parentheses under the line start of the matching - // opening parentheses. - buf->b_ind_matching_paren = 0; - - // Indent a closing parentheses under the previous line. - buf->b_ind_paren_prev = 0; - - // Extra indent for comments. - buf->b_ind_comment = 0; - - // Spaces from the comment opener when there is nothing after it. - buf->b_ind_in_comment = 3; - - // Boolean: if non-zero, use b_ind_in_comment even if there is something - // after the comment opener. - buf->b_ind_in_comment2 = 0; - - // Max lines to search for an open paren. - buf->b_ind_maxparen = 20; - - // Max lines to search for an open comment. - buf->b_ind_maxcomment = 70; - - // Handle braces for java code. - buf->b_ind_java = 0; - - // Not to confuse JS object properties with labels. - buf->b_ind_js = 0; - - // Handle blocked cases correctly. - buf->b_ind_keep_case_label = 0; - - // Handle C++ namespace. - buf->b_ind_cpp_namespace = 0; - - // Handle continuation lines containing conditions of if(), for() and - // while(). - buf->b_ind_if_for_while = 0; - - // indentation for # comments - buf->b_ind_hash_comment = 0; - - // Handle C++ extern "C" or "C++" - buf->b_ind_cpp_extern_c = 0; - - for (p = buf->b_p_cino; *p; ) - { - l = p++; - if (*p == '-') - ++p; - digits = p; // remember where the digits start - n = getdigits(&p); - divider = 0; - if (*p == '.') // ".5s" means a fraction - { - fraction = atol((char *)++p); - while (VIM_ISDIGIT(*p)) - { - ++p; - if (divider) - divider *= 10; - else - divider = 10; - } - } - if (*p == 's') // "2s" means two times 'shiftwidth' - { - if (p == digits) - n = sw; // just "s" is one 'shiftwidth' - else - { - n *= sw; - if (divider) - n += (sw * fraction + divider / 2) / divider; - } - ++p; - } - if (l[1] == '-') - n = -n; - - // When adding an entry here, also update the default 'cinoptions' in - // doc/indent.txt, and add explanation for it! - switch (*l) - { - case '>': buf->b_ind_level = n; break; - case 'e': buf->b_ind_open_imag = n; break; - case 'n': buf->b_ind_no_brace = n; break; - case 'f': buf->b_ind_first_open = n; break; - case '{': buf->b_ind_open_extra = n; break; - case '}': buf->b_ind_close_extra = n; break; - case '^': buf->b_ind_open_left_imag = n; break; - case 'L': buf->b_ind_jump_label = n; break; - case ':': buf->b_ind_case = n; break; - case '=': buf->b_ind_case_code = n; break; - case 'b': buf->b_ind_case_break = n; break; - case 'p': buf->b_ind_param = n; break; - case 't': buf->b_ind_func_type = n; break; - case '/': buf->b_ind_comment = n; break; - case 'c': buf->b_ind_in_comment = n; break; - case 'C': buf->b_ind_in_comment2 = n; break; - case 'i': buf->b_ind_cpp_baseclass = n; break; - case '+': buf->b_ind_continuation = n; break; - case '(': buf->b_ind_unclosed = n; break; - case 'u': buf->b_ind_unclosed2 = n; break; - case 'U': buf->b_ind_unclosed_noignore = n; break; - case 'W': buf->b_ind_unclosed_wrapped = n; break; - case 'w': buf->b_ind_unclosed_whiteok = n; break; - case 'm': buf->b_ind_matching_paren = n; break; - case 'M': buf->b_ind_paren_prev = n; break; - case ')': buf->b_ind_maxparen = n; break; - case '*': buf->b_ind_maxcomment = n; break; - case 'g': buf->b_ind_scopedecl = n; break; - case 'h': buf->b_ind_scopedecl_code = n; break; - case 'j': buf->b_ind_java = n; break; - case 'J': buf->b_ind_js = n; break; - case 'l': buf->b_ind_keep_case_label = n; break; - case '#': buf->b_ind_hash_comment = n; break; - case 'N': buf->b_ind_cpp_namespace = n; break; - case 'k': buf->b_ind_if_for_while = n; break; - case 'E': buf->b_ind_cpp_extern_c = n; break; - } - if (*p == ',') - ++p; - } -} - -/* - * Return the desired indent for C code. - * Return -1 if the indent should be left alone (inside a raw string). - */ - int -get_c_indent(void) -{ - pos_T cur_curpos; - int amount; - int scope_amount; - int cur_amount = MAXCOL; - colnr_T col; - char_u *theline; - char_u *linecopy; - pos_T *trypos; - pos_T *comment_pos; - pos_T *tryposBrace = NULL; - pos_T tryposCopy; - pos_T our_paren_pos; - char_u *start; - int start_brace; -#define BRACE_IN_COL0 1 // '{' is in column 0 -#define BRACE_AT_START 2 // '{' is at start of line -#define BRACE_AT_END 3 // '{' is at end of line - linenr_T ourscope; - char_u *l; - char_u *look; - char_u terminated; - int lookfor; -#define LOOKFOR_INITIAL 0 -#define LOOKFOR_IF 1 -#define LOOKFOR_DO 2 -#define LOOKFOR_CASE 3 -#define LOOKFOR_ANY 4 -#define LOOKFOR_TERM 5 -#define LOOKFOR_UNTERM 6 -#define LOOKFOR_SCOPEDECL 7 -#define LOOKFOR_NOBREAK 8 -#define LOOKFOR_CPP_BASECLASS 9 -#define LOOKFOR_ENUM_OR_INIT 10 -#define LOOKFOR_JS_KEY 11 -#define LOOKFOR_COMMA 12 - - int whilelevel; - linenr_T lnum; - int n; - int iscase; - int lookfor_break; - int lookfor_cpp_namespace = FALSE; - int cont_amount = 0; // amount for continuation line - int original_line_islabel; - int added_to_amount = 0; - int js_cur_has_key = 0; - linenr_T raw_string_start = 0; - cpp_baseclass_cache_T cache_cpp_baseclass = { FALSE, { MAXLNUM, 0 } }; - - // make a copy, value is changed below - int ind_continuation = curbuf->b_ind_continuation; - - // remember where the cursor was when we started - cur_curpos = curwin->w_cursor; - - // if we are at line 1 zero indent is fine, right? - if (cur_curpos.lnum == 1) - return 0; - - // Get a copy of the current contents of the line. - // This is required, because only the most recent line obtained with - // ml_get is valid! - linecopy = vim_strsave(ml_get(cur_curpos.lnum)); - if (linecopy == NULL) - return 0; - - // In insert mode and the cursor is on a ')' truncate the line at the - // cursor position. We don't want to line up with the matching '(' when - // inserting new stuff. - // For unknown reasons the cursor might be past the end of the line, thus - // check for that. - if ((State & INSERT) - && curwin->w_cursor.col < (colnr_T)STRLEN(linecopy) - && linecopy[curwin->w_cursor.col] == ')') - linecopy[curwin->w_cursor.col] = NUL; - - theline = skipwhite(linecopy); - - // move the cursor to the start of the line - - curwin->w_cursor.col = 0; - - original_line_islabel = cin_islabel(); // XXX - - // If we are inside a raw string don't change the indent. - // Ignore a raw string inside a comment. - comment_pos = ind_find_start_comment(); - if (comment_pos != NULL) - { - // findmatchlimit() static pos is overwritten, make a copy - tryposCopy = *comment_pos; - comment_pos = &tryposCopy; - } - trypos = find_start_rawstring(curbuf->b_ind_maxcomment); - if (trypos != NULL && (comment_pos == NULL - || LT_POS(*trypos, *comment_pos))) - { - amount = -1; - goto laterend; - } - - // #defines and so on always go at the left when included in 'cinkeys'. - if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE))) - { - amount = curbuf->b_ind_hash_comment; - goto theend; - } - - // Is it a non-case label? Then that goes at the left margin too unless: - // - JS flag is set. - // - 'L' item has a positive value. - if (original_line_islabel && !curbuf->b_ind_js - && curbuf->b_ind_jump_label < 0) - { - amount = 0; - goto theend; - } - - // If we're inside a "//" comment and there is a "//" comment in a - // previous line, lineup with that one. - if (cin_islinecomment(theline) - && (trypos = find_line_comment()) != NULL) // XXX - { - // find how indented the line beginning the comment is - getvcol(curwin, trypos, &col, NULL, NULL); - amount = col; - goto theend; - } - - // If we're inside a comment and not looking at the start of the - // comment, try using the 'comments' option. - if (!cin_iscomment(theline) && comment_pos != NULL) // XXX - { - int lead_start_len = 2; - int lead_middle_len = 1; - char_u lead_start[COM_MAX_LEN]; // start-comment string - char_u lead_middle[COM_MAX_LEN]; // middle-comment string - char_u lead_end[COM_MAX_LEN]; // end-comment string - char_u *p; - int start_align = 0; - int start_off = 0; - int done = FALSE; - - // find how indented the line beginning the comment is - getvcol(curwin, comment_pos, &col, NULL, NULL); - amount = col; - *lead_start = NUL; - *lead_middle = NUL; - - p = curbuf->b_p_com; - while (*p != NUL) - { - int align = 0; - int off = 0; - int what = 0; - - while (*p != NUL && *p != ':') - { - if (*p == COM_START || *p == COM_END || *p == COM_MIDDLE) - what = *p++; - else if (*p == COM_LEFT || *p == COM_RIGHT) - align = *p++; - else if (VIM_ISDIGIT(*p) || *p == '-') - off = getdigits(&p); - else - ++p; - } - - if (*p == ':') - ++p; - (void)copy_option_part(&p, lead_end, COM_MAX_LEN, ","); - if (what == COM_START) - { - STRCPY(lead_start, lead_end); - lead_start_len = (int)STRLEN(lead_start); - start_off = off; - start_align = align; - } - else if (what == COM_MIDDLE) - { - STRCPY(lead_middle, lead_end); - lead_middle_len = (int)STRLEN(lead_middle); - } - else if (what == COM_END) - { - // If our line starts with the middle comment string, line it - // up with the comment opener per the 'comments' option. - if (STRNCMP(theline, lead_middle, lead_middle_len) == 0 - && STRNCMP(theline, lead_end, STRLEN(lead_end)) != 0) - { - done = TRUE; - if (curwin->w_cursor.lnum > 1) - { - // If the start comment string matches in the previous - // line, use the indent of that line plus offset. If - // the middle comment string matches in the previous - // line, use the indent of that line. XXX - look = skipwhite(ml_get(curwin->w_cursor.lnum - 1)); - if (STRNCMP(look, lead_start, lead_start_len) == 0) - amount = get_indent_lnum(curwin->w_cursor.lnum - 1); - else if (STRNCMP(look, lead_middle, - lead_middle_len) == 0) - { - amount = get_indent_lnum(curwin->w_cursor.lnum - 1); - break; - } - // If the start comment string doesn't match with the - // start of the comment, skip this entry. XXX - else if (STRNCMP(ml_get(comment_pos->lnum) + comment_pos->col, - lead_start, lead_start_len) != 0) - continue; - } - if (start_off != 0) - amount += start_off; - else if (start_align == COM_RIGHT) - amount += vim_strsize(lead_start) - - vim_strsize(lead_middle); - break; - } - - // If our line starts with the end comment string, line it up - // with the middle comment - if (STRNCMP(theline, lead_middle, lead_middle_len) != 0 - && STRNCMP(theline, lead_end, STRLEN(lead_end)) == 0) - { - amount = get_indent_lnum(curwin->w_cursor.lnum - 1); - // XXX - if (off != 0) - amount += off; - else if (align == COM_RIGHT) - amount += vim_strsize(lead_start) - - vim_strsize(lead_middle); - done = TRUE; - break; - } - } - } - - // If our line starts with an asterisk, line up with the - // asterisk in the comment opener; otherwise, line up - // with the first character of the comment text. - if (done) - ; - else if (theline[0] == '*') - amount += 1; - else - { - // If we are more than one line away from the comment opener, take - // the indent of the previous non-empty line. If 'cino' has "CO" - // and we are just below the comment opener and there are any - // white characters after it line up with the text after it; - // otherwise, add the amount specified by "c" in 'cino' - amount = -1; - for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; --lnum) - { - if (linewhite(lnum)) // skip blank lines - continue; - amount = get_indent_lnum(lnum); // XXX - break; - } - if (amount == -1) // use the comment opener - { - if (!curbuf->b_ind_in_comment2) - { - start = ml_get(comment_pos->lnum); - look = start + comment_pos->col + 2; // skip / and * - if (*look != NUL) // if something after it - comment_pos->col = (colnr_T)(skipwhite(look) - start); - } - getvcol(curwin, comment_pos, &col, NULL, NULL); - amount = col; - if (curbuf->b_ind_in_comment2 || *look == NUL) - amount += curbuf->b_ind_in_comment; - } - } - goto theend; - } - - // Are we looking at a ']' that has a match? - if (*skipwhite(theline) == ']' - && (trypos = find_match_char('[', curbuf->b_ind_maxparen)) != NULL) - { - // align with the line containing the '['. - amount = get_indent_lnum(trypos->lnum); - goto theend; - } - - // Are we inside parentheses or braces? XXX - if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL - && curbuf->b_ind_java == 0) - || (tryposBrace = find_start_brace()) != NULL - || trypos != NULL) - { - if (trypos != NULL && tryposBrace != NULL) - { - // Both an unmatched '(' and '{' is found. Use the one which is - // closer to the current cursor position, set the other to NULL. - if (trypos->lnum != tryposBrace->lnum - ? trypos->lnum < tryposBrace->lnum - : trypos->col < tryposBrace->col) - trypos = NULL; - else - tryposBrace = NULL; - } - - if (trypos != NULL) - { - // If the matching paren is more than one line away, use the indent of - // a previous non-empty line that matches the same paren. - if (theline[0] == ')' && curbuf->b_ind_paren_prev) - { - // Line up with the start of the matching paren line. - amount = get_indent_lnum(curwin->w_cursor.lnum - 1); // XXX - } - else - { - amount = -1; - our_paren_pos = *trypos; - for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) - { - l = skipwhite(ml_get(lnum)); - if (cin_nocode(l)) // skip comment lines - continue; - if (cin_ispreproc_cont(&l, &lnum, &amount)) - continue; // ignore #define, #if, etc. - curwin->w_cursor.lnum = lnum; - - // Skip a comment or raw string. XXX - if ((trypos = ind_find_start_CORS(NULL)) != NULL) - { - lnum = trypos->lnum + 1; - continue; - } - - // XXX - if ((trypos = find_match_paren( - corr_ind_maxparen(&cur_curpos))) != NULL - && trypos->lnum == our_paren_pos.lnum - && trypos->col == our_paren_pos.col) - { - amount = get_indent_lnum(lnum); // XXX - - if (theline[0] == ')') - { - if (our_paren_pos.lnum != lnum - && cur_amount > amount) - cur_amount = amount; - amount = -1; - } - break; - } - } - } - - // Line up with line where the matching paren is. XXX - // If the line starts with a '(' or the indent for unclosed - // parentheses is zero, line up with the unclosed parentheses. - if (amount == -1) - { - int ignore_paren_col = 0; - int is_if_for_while = 0; - - if (curbuf->b_ind_if_for_while) - { - // Look for the outermost opening parenthesis on this line - // and check whether it belongs to an "if", "for" or "while". - - pos_T cursor_save = curwin->w_cursor; - pos_T outermost; - char_u *line; - - trypos = &our_paren_pos; - do { - outermost = *trypos; - curwin->w_cursor.lnum = outermost.lnum; - curwin->w_cursor.col = outermost.col; - - trypos = find_match_paren(curbuf->b_ind_maxparen); - } while (trypos && trypos->lnum == outermost.lnum); - - curwin->w_cursor = cursor_save; - - line = ml_get(outermost.lnum); - - is_if_for_while = - cin_is_if_for_while_before_offset(line, &outermost.col); - } - - amount = skip_label(our_paren_pos.lnum, &look); - look = skipwhite(look); - if (*look == '(') - { - linenr_T save_lnum = curwin->w_cursor.lnum; - char_u *line; - int look_col; - - // Ignore a '(' in front of the line that has a match before - // our matching '('. - curwin->w_cursor.lnum = our_paren_pos.lnum; - line = ml_get_curline(); - look_col = (int)(look - line); - curwin->w_cursor.col = look_col + 1; - if ((trypos = findmatchlimit(NULL, ')', 0, - curbuf->b_ind_maxparen)) - != NULL - && trypos->lnum == our_paren_pos.lnum - && trypos->col < our_paren_pos.col) - ignore_paren_col = trypos->col + 1; - - curwin->w_cursor.lnum = save_lnum; - look = ml_get(our_paren_pos.lnum) + look_col; - } - if (theline[0] == ')' || (curbuf->b_ind_unclosed == 0 - && is_if_for_while == 0) - || (!curbuf->b_ind_unclosed_noignore && *look == '(' - && ignore_paren_col == 0)) - { - // If we're looking at a close paren, line up right there; - // otherwise, line up with the next (non-white) character. - // When b_ind_unclosed_wrapped is set and the matching paren is - // the last nonwhite character of the line, use either the - // indent of the current line or the indentation of the next - // outer paren and add b_ind_unclosed_wrapped (for very long - // lines). - if (theline[0] != ')') - { - cur_amount = MAXCOL; - l = ml_get(our_paren_pos.lnum); - if (curbuf->b_ind_unclosed_wrapped - && cin_ends_in(l, (char_u *)"(", NULL)) - { - // look for opening unmatched paren, indent one level - // for each additional level - n = 1; - for (col = 0; col < our_paren_pos.col; ++col) - { - switch (l[col]) - { - case '(': - case '{': ++n; - break; - - case ')': - case '}': if (n > 1) - --n; - break; - } - } - - our_paren_pos.col = 0; - amount += n * curbuf->b_ind_unclosed_wrapped; - } - else if (curbuf->b_ind_unclosed_whiteok) - our_paren_pos.col++; - else - { - col = our_paren_pos.col + 1; - while (VIM_ISWHITE(l[col])) - col++; - if (l[col] != NUL) // In case of trailing space - our_paren_pos.col = col; - else - our_paren_pos.col++; - } - } - - // Find how indented the paren is, or the character after it - // if we did the above "if". - if (our_paren_pos.col > 0) - { - getvcol(curwin, &our_paren_pos, &col, NULL, NULL); - if (cur_amount > (int)col) - cur_amount = col; - } - } - - if (theline[0] == ')' && curbuf->b_ind_matching_paren) - { - // Line up with the start of the matching paren line. - } - else if ((curbuf->b_ind_unclosed == 0 && is_if_for_while == 0) - || (!curbuf->b_ind_unclosed_noignore - && *look == '(' && ignore_paren_col == 0)) - { - if (cur_amount != MAXCOL) - amount = cur_amount; - } - else - { - // Add b_ind_unclosed2 for each '(' before our matching one, - // but ignore (void) before the line (ignore_paren_col). - col = our_paren_pos.col; - while ((int)our_paren_pos.col > ignore_paren_col) - { - --our_paren_pos.col; - switch (*ml_get_pos(&our_paren_pos)) - { - case '(': amount += curbuf->b_ind_unclosed2; - col = our_paren_pos.col; - break; - case ')': amount -= curbuf->b_ind_unclosed2; - col = MAXCOL; - break; - } - } - - // Use b_ind_unclosed once, when the first '(' is not inside - // braces - if (col == MAXCOL) - amount += curbuf->b_ind_unclosed; - else - { - curwin->w_cursor.lnum = our_paren_pos.lnum; - curwin->w_cursor.col = col; - if (find_match_paren_after_brace(curbuf->b_ind_maxparen) - != NULL) - amount += curbuf->b_ind_unclosed2; - else - { - if (is_if_for_while) - amount += curbuf->b_ind_if_for_while; - else - amount += curbuf->b_ind_unclosed; - } - } - // For a line starting with ')' use the minimum of the two - // positions, to avoid giving it more indent than the previous - // lines: - // func_long_name( if (x - // arg && yy - // ) ^ not here ) ^ not here - if (cur_amount < amount) - amount = cur_amount; - } - } - - // add extra indent for a comment - if (cin_iscomment(theline)) - amount += curbuf->b_ind_comment; - } - else - { - // We are inside braces, there is a { before this line at the position - // stored in tryposBrace. - // Make a copy of tryposBrace, it may point to pos_copy inside - // find_start_brace(), which may be changed somewhere. - tryposCopy = *tryposBrace; - tryposBrace = &tryposCopy; - trypos = tryposBrace; - ourscope = trypos->lnum; - start = ml_get(ourscope); - - // Now figure out how indented the line is in general. - // If the brace was at the start of the line, we use that; - // otherwise, check out the indentation of the line as - // a whole and then add the "imaginary indent" to that. - look = skipwhite(start); - if (*look == '{') - { - getvcol(curwin, trypos, &col, NULL, NULL); - amount = col; - if (*start == '{') - start_brace = BRACE_IN_COL0; - else - start_brace = BRACE_AT_START; - } - else - { - // That opening brace might have been on a continuation - // line. if so, find the start of the line. - curwin->w_cursor.lnum = ourscope; - - // Position the cursor over the rightmost paren, so that - // matching it will take us back to the start of the line. - lnum = ourscope; - if (find_last_paren(start, '(', ')') - && (trypos = find_match_paren(curbuf->b_ind_maxparen)) - != NULL) - lnum = trypos->lnum; - - // It could have been something like - // case 1: if (asdf && - // ldfd) { - // } - if ((curbuf->b_ind_js || curbuf->b_ind_keep_case_label) - && cin_iscase(skipwhite(ml_get_curline()), FALSE)) - amount = get_indent(); - else if (curbuf->b_ind_js) - amount = get_indent_lnum(lnum); - else - amount = skip_label(lnum, &l); - - start_brace = BRACE_AT_END; - } - - // For Javascript check if the line starts with "key:". - if (curbuf->b_ind_js) - js_cur_has_key = cin_has_js_key(theline); - - // If we're looking at a closing brace, that's where - // we want to be. otherwise, add the amount of room - // that an indent is supposed to be. - if (theline[0] == '}') - { - // they may want closing braces to line up with something - // other than the open brace. indulge them, if so. - amount += curbuf->b_ind_close_extra; - } - else - { - // If we're looking at an "else", try to find an "if" - // to match it with. - // If we're looking at a "while", try to find a "do" - // to match it with. - lookfor = LOOKFOR_INITIAL; - if (cin_iselse(theline)) - lookfor = LOOKFOR_IF; - else if (cin_iswhileofdo(theline, cur_curpos.lnum)) // XXX - lookfor = LOOKFOR_DO; - if (lookfor != LOOKFOR_INITIAL) - { - curwin->w_cursor.lnum = cur_curpos.lnum; - if (find_match(lookfor, ourscope) == OK) - { - amount = get_indent(); // XXX - goto theend; - } - } - - // We get here if we are not on an "while-of-do" or "else" (or - // failed to find a matching "if"). - // Search backwards for something to line up with. - // First set amount for when we don't find anything. - - // if the '{' is _really_ at the left margin, use the imaginary - // location of a left-margin brace. Otherwise, correct the - // location for b_ind_open_extra. - - if (start_brace == BRACE_IN_COL0) // '{' is in column 0 - { - amount = curbuf->b_ind_open_left_imag; - lookfor_cpp_namespace = TRUE; - } - else if (start_brace == BRACE_AT_START && - lookfor_cpp_namespace) // '{' is at start - { - - lookfor_cpp_namespace = TRUE; - } - else - { - if (start_brace == BRACE_AT_END) // '{' is at end of line - { - amount += curbuf->b_ind_open_imag; - - l = skipwhite(ml_get_curline()); - if (cin_is_cpp_namespace(l)) - amount += curbuf->b_ind_cpp_namespace; - else if (cin_is_cpp_extern_c(l)) - amount += curbuf->b_ind_cpp_extern_c; - } - else - { - // Compensate for adding b_ind_open_extra later. - amount -= curbuf->b_ind_open_extra; - if (amount < 0) - amount = 0; - } - } - - lookfor_break = FALSE; - - if (cin_iscase(theline, FALSE)) // it's a switch() label - { - lookfor = LOOKFOR_CASE; // find a previous switch() label - amount += curbuf->b_ind_case; - } - else if (cin_isscopedecl(theline)) // private:, ... - { - lookfor = LOOKFOR_SCOPEDECL; // class decl is this block - amount += curbuf->b_ind_scopedecl; - } - else - { - if (curbuf->b_ind_case_break && cin_isbreak(theline)) - // break; ... - lookfor_break = TRUE; - - lookfor = LOOKFOR_INITIAL; - // b_ind_level from start of block - amount += curbuf->b_ind_level; - } - scope_amount = amount; - whilelevel = 0; - - // Search backwards. If we find something we recognize, line up - // with that. - // - // If we're looking at an open brace, indent - // the usual amount relative to the conditional - // that opens the block. - curwin->w_cursor = cur_curpos; - for (;;) - { - curwin->w_cursor.lnum--; - curwin->w_cursor.col = 0; - - // If we went all the way back to the start of our scope, line - // up with it. - if (curwin->w_cursor.lnum <= ourscope) - { - // We reached end of scope: - // If looking for a enum or structure initialization - // go further back: - // If it is an initializer (enum xxx or xxx =), then - // don't add ind_continuation, otherwise it is a variable - // declaration: - // int x, - // here; <-- add ind_continuation - if (lookfor == LOOKFOR_ENUM_OR_INIT) - { - if (curwin->w_cursor.lnum == 0 - || curwin->w_cursor.lnum - < ourscope - curbuf->b_ind_maxparen) - { - // nothing found (abuse curbuf->b_ind_maxparen as - // limit) assume terminated line (i.e. a variable - // initialization) - if (cont_amount > 0) - amount = cont_amount; - else if (!curbuf->b_ind_js) - amount += ind_continuation; - break; - } - - l = ml_get_curline(); - - // If we're in a comment or raw string now, skip to - // the start of it. - trypos = ind_find_start_CORS(NULL); - if (trypos != NULL) - { - curwin->w_cursor.lnum = trypos->lnum + 1; - curwin->w_cursor.col = 0; - continue; - } - - // Skip preprocessor directives and blank lines. - if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, - &amount)) - continue; - - if (cin_nocode(l)) - continue; - - terminated = cin_isterminated(l, FALSE, TRUE); - - // If we are at top level and the line looks like a - // function declaration, we are done - // (it's a variable declaration). - if (start_brace != BRACE_IN_COL0 - || !cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) - { - // if the line is terminated with another ',' - // it is a continued variable initialization. - // don't add extra indent. - // TODO: does not work, if a function - // declaration is split over multiple lines: - // cin_isfuncdecl returns FALSE then. - if (terminated == ',') - break; - - // if it es a enum declaration or an assignment, - // we are done. - if (terminated != ';' && cin_isinit()) - break; - - // nothing useful found - if (terminated == 0 || terminated == '{') - continue; - } - - if (terminated != ';') - { - // Skip parens and braces. Position the cursor - // over the rightmost paren, so that matching it - // will take us back to the start of the line. - // XXX - trypos = NULL; - if (find_last_paren(l, '(', ')')) - trypos = find_match_paren( - curbuf->b_ind_maxparen); - - if (trypos == NULL && find_last_paren(l, '{', '}')) - trypos = find_start_brace(); - - if (trypos != NULL) - { - curwin->w_cursor.lnum = trypos->lnum + 1; - curwin->w_cursor.col = 0; - continue; - } - } - - // it's a variable declaration, add indentation - // like in - // int a, - // b; - if (cont_amount > 0) - amount = cont_amount; - else - amount += ind_continuation; - } - else if (lookfor == LOOKFOR_UNTERM) - { - if (cont_amount > 0) - amount = cont_amount; - else - amount += ind_continuation; - } - else - { - if (lookfor != LOOKFOR_TERM - && lookfor != LOOKFOR_CPP_BASECLASS - && lookfor != LOOKFOR_COMMA) - { - amount = scope_amount; - if (theline[0] == '{') - { - amount += curbuf->b_ind_open_extra; - added_to_amount = curbuf->b_ind_open_extra; - } - } - - if (lookfor_cpp_namespace) - { - // Looking for C++ namespace, need to look further - // back. - if (curwin->w_cursor.lnum == ourscope) - continue; - - if (curwin->w_cursor.lnum == 0 - || curwin->w_cursor.lnum - < ourscope - FIND_NAMESPACE_LIM) - break; - - l = ml_get_curline(); - - // If we're in a comment or raw string now, skip - // to the start of it. - trypos = ind_find_start_CORS(NULL); - if (trypos != NULL) - { - curwin->w_cursor.lnum = trypos->lnum + 1; - curwin->w_cursor.col = 0; - continue; - } - - // Skip preprocessor directives and blank lines. - if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, - &amount)) - continue; - - // Finally the actual check for "namespace". - if (cin_is_cpp_namespace(l)) - { - amount += curbuf->b_ind_cpp_namespace - - added_to_amount; - break; - } - else if (cin_is_cpp_extern_c(l)) - { - amount += curbuf->b_ind_cpp_extern_c - - added_to_amount; - break; - } - - if (cin_nocode(l)) - continue; - } - } - break; - } - - // If we're in a comment or raw string now, skip to the start - // of it. XXX - if ((trypos = ind_find_start_CORS(&raw_string_start)) != NULL) - { - curwin->w_cursor.lnum = trypos->lnum + 1; - curwin->w_cursor.col = 0; - continue; - } - - l = ml_get_curline(); - - // If this is a switch() label, may line up relative to that. - // If this is a C++ scope declaration, do the same. - iscase = cin_iscase(l, FALSE); - if (iscase || cin_isscopedecl(l)) - { - // we are only looking for cpp base class - // declaration/initialization any longer - if (lookfor == LOOKFOR_CPP_BASECLASS) - break; - - // When looking for a "do" we are not interested in - // labels. - if (whilelevel > 0) - continue; - - // case xx: - // c = 99 + <- this indent plus continuation - //-> here; - if (lookfor == LOOKFOR_UNTERM - || lookfor == LOOKFOR_ENUM_OR_INIT) - { - if (cont_amount > 0) - amount = cont_amount; - else - amount += ind_continuation; - break; - } - - // case xx: <- line up with this case - // x = 333; - // case yy: - if ( (iscase && lookfor == LOOKFOR_CASE) - || (iscase && lookfor_break) - || (!iscase && lookfor == LOOKFOR_SCOPEDECL)) - { - // Check that this case label is not for another - // switch() XXX - if ((trypos = find_start_brace()) == NULL - || trypos->lnum == ourscope) - { - amount = get_indent(); // XXX - break; - } - continue; - } - - n = get_indent_nolabel(curwin->w_cursor.lnum); // XXX - - // case xx: if (cond) <- line up with this if - // y = y + 1; - // -> s = 99; - // - // case xx: - // if (cond) <- line up with this line - // y = y + 1; - // -> s = 99; - if (lookfor == LOOKFOR_TERM) - { - if (n) - amount = n; - - if (!lookfor_break) - break; - } - - // case xx: x = x + 1; <- line up with this x - // -> y = y + 1; - // - // case xx: if (cond) <- line up with this if - // -> y = y + 1; - if (n) - { - amount = n; - l = after_label(ml_get_curline()); - if (l != NULL && cin_is_cinword(l)) - { - if (theline[0] == '{') - amount += curbuf->b_ind_open_extra; - else - amount += curbuf->b_ind_level - + curbuf->b_ind_no_brace; - } - break; - } - - // Try to get the indent of a statement before the switch - // label. If nothing is found, line up relative to the - // switch label. - // break; <- may line up with this line - // case xx: - // -> y = 1; - scope_amount = get_indent() + (iscase // XXX - ? curbuf->b_ind_case_code - : curbuf->b_ind_scopedecl_code); - lookfor = curbuf->b_ind_case_break - ? LOOKFOR_NOBREAK : LOOKFOR_ANY; - continue; - } - - // Looking for a switch() label or C++ scope declaration, - // ignore other lines, skip {}-blocks. - if (lookfor == LOOKFOR_CASE || lookfor == LOOKFOR_SCOPEDECL) - { - if (find_last_paren(l, '{', '}') - && (trypos = find_start_brace()) != NULL) - { - curwin->w_cursor.lnum = trypos->lnum + 1; - curwin->w_cursor.col = 0; - } - continue; - } - - // Ignore jump labels with nothing after them. - if (!curbuf->b_ind_js && cin_islabel()) - { - l = after_label(ml_get_curline()); - if (l == NULL || cin_nocode(l)) - continue; - } - - // Ignore #defines, #if, etc. - // Ignore comment and empty lines. - // (need to get the line again, cin_islabel() may have - // unlocked it) - l = ml_get_curline(); - if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount) - || cin_nocode(l)) - continue; - - // Are we at the start of a cpp base class declaration or - // constructor initialization? XXX - n = FALSE; - if (lookfor != LOOKFOR_TERM && curbuf->b_ind_cpp_baseclass > 0) - { - n = cin_is_cpp_baseclass(&cache_cpp_baseclass); - l = ml_get_curline(); - } - if (n) - { - if (lookfor == LOOKFOR_UNTERM) - { - if (cont_amount > 0) - amount = cont_amount; - else - amount += ind_continuation; - } - else if (theline[0] == '{') - { - // Need to find start of the declaration. - lookfor = LOOKFOR_UNTERM; - ind_continuation = 0; - continue; - } - else - // XXX - amount = get_baseclass_amount( - cache_cpp_baseclass.lpos.col); - break; - } - else if (lookfor == LOOKFOR_CPP_BASECLASS) - { - // only look, whether there is a cpp base class - // declaration or initialization before the opening brace. - if (cin_isterminated(l, TRUE, FALSE)) - break; - else - continue; - } - - // What happens next depends on the line being terminated. - // If terminated with a ',' only consider it terminating if - // there is another unterminated statement behind, eg: - // 123, - // sizeof - // here - // Otherwise check whether it is a enumeration or structure - // initialisation (not indented) or a variable declaration - // (indented). - terminated = cin_isterminated(l, FALSE, TRUE); - - if (js_cur_has_key) - { - js_cur_has_key = 0; // only check the first line - if (curbuf->b_ind_js && terminated == ',') - { - // For Javascript we might be inside an object: - // key: something, <- align with this - // key: something - // or: - // key: something + <- align with this - // something, - // key: something - lookfor = LOOKFOR_JS_KEY; - } - } - if (lookfor == LOOKFOR_JS_KEY && cin_has_js_key(l)) - { - amount = get_indent(); - break; - } - if (lookfor == LOOKFOR_COMMA) - { - if (tryposBrace != NULL && tryposBrace->lnum - >= curwin->w_cursor.lnum) - break; - if (terminated == ',') - // line below current line is the one that starts a - // (possibly broken) line ending in a comma. - break; - else - { - amount = get_indent(); - if (curwin->w_cursor.lnum - 1 == ourscope) - // line above is start of the scope, thus current - // line is the one that stars a (possibly broken) - // line ending in a comma. - break; - } - } - - if (terminated == 0 || (lookfor != LOOKFOR_UNTERM - && terminated == ',')) - { - if (lookfor != LOOKFOR_ENUM_OR_INIT && - (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[')) - amount += ind_continuation; - // if we're in the middle of a paren thing, - // go back to the line that starts it so - // we can get the right prevailing indent - // if ( foo && - // bar ) - - // Position the cursor over the rightmost paren, so that - // matching it will take us back to the start of the line. - // Ignore a match before the start of the block. - (void)find_last_paren(l, '(', ')'); - trypos = find_match_paren(corr_ind_maxparen(&cur_curpos)); - if (trypos != NULL && (trypos->lnum < tryposBrace->lnum - || (trypos->lnum == tryposBrace->lnum - && trypos->col < tryposBrace->col))) - trypos = NULL; - - // If we are looking for ',', we also look for matching - // braces. - if (trypos == NULL && terminated == ',' - && find_last_paren(l, '{', '}')) - trypos = find_start_brace(); - - if (trypos != NULL) - { - // Check if we are on a case label now. This is - // handled above. - // case xx: if ( asdf && - // asdf) - curwin->w_cursor = *trypos; - l = ml_get_curline(); - if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) - { - ++curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; - continue; - } - } - - /* - * Skip over continuation lines to find the one to get the - * indent from - * char *usethis = "bla\ - * bla", - * here; - */ - if (terminated == ',') - { - while (curwin->w_cursor.lnum > 1) - { - l = ml_get(curwin->w_cursor.lnum - 1); - if (*l == NUL || l[STRLEN(l) - 1] != '\\') - break; - --curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; - } - } - - // Get indent and pointer to text for current line, - // ignoring any jump label. XXX - if (curbuf->b_ind_js) - cur_amount = get_indent(); - else - cur_amount = skip_label(curwin->w_cursor.lnum, &l); - // If this is just above the line we are indenting, and it - // starts with a '{', line it up with this line. - // while (not) - // -> { - // } - if (terminated != ',' && lookfor != LOOKFOR_TERM - && theline[0] == '{') - { - amount = cur_amount; - // Only add b_ind_open_extra when the current line - // doesn't start with a '{', which must have a match - // in the same line (scope is the same). Probably: - // { 1, 2 }, - // -> { 3, 4 } - if (*skipwhite(l) != '{') - amount += curbuf->b_ind_open_extra; - - if (curbuf->b_ind_cpp_baseclass && !curbuf->b_ind_js) - { - // have to look back, whether it is a cpp base - // class declaration or initialization - lookfor = LOOKFOR_CPP_BASECLASS; - continue; - } - break; - } - - // Check if we are after an "if", "while", etc. - // Also allow " } else". - if (cin_is_cinword(l) || cin_iselse(skipwhite(l))) - { - // Found an unterminated line after an if (), line up - // with the last one. - // if (cond) - // 100 + - // -> here; - if (lookfor == LOOKFOR_UNTERM - || lookfor == LOOKFOR_ENUM_OR_INIT) - { - if (cont_amount > 0) - amount = cont_amount; - else - amount += ind_continuation; - break; - } - - // If this is just above the line we are indenting, we - // are finished. - // while (not) - // -> here; - // Otherwise this indent can be used when the line - // before this is terminated. - // yyy; - // if (stat) - // while (not) - // xxx; - // -> here; - amount = cur_amount; - if (theline[0] == '{') - amount += curbuf->b_ind_open_extra; - if (lookfor != LOOKFOR_TERM) - { - amount += curbuf->b_ind_level - + curbuf->b_ind_no_brace; - break; - } - - // Special trick: when expecting the while () after a - // do, line up with the while() - // do - // x = 1; - // -> here - l = skipwhite(ml_get_curline()); - if (cin_isdo(l)) - { - if (whilelevel == 0) - break; - --whilelevel; - } - - // When searching for a terminated line, don't use the - // one between the "if" and the matching "else". - // Need to use the scope of this "else". XXX - // If whilelevel != 0 continue looking for a "do {". - if (cin_iselse(l) && whilelevel == 0) - { - // If we're looking at "} else", let's make sure we - // find the opening brace of the enclosing scope, - // not the one from "if () {". - if (*l == '}') - curwin->w_cursor.col = - (colnr_T)(l - ml_get_curline()) + 1; - - if ((trypos = find_start_brace()) == NULL - || find_match(LOOKFOR_IF, trypos->lnum) - == FAIL) - break; - } - } - - // If we're below an unterminated line that is not an - // "if" or something, we may line up with this line or - // add something for a continuation line, depending on - // the line before this one. - else - { - // Found two unterminated lines on a row, line up with - // the last one. - // c = 99 + - // 100 + - // -> here; - if (lookfor == LOOKFOR_UNTERM) - { - // When line ends in a comma add extra indent - if (terminated == ',') - amount += ind_continuation; - break; - } - - if (lookfor == LOOKFOR_ENUM_OR_INIT) - { - // Found two lines ending in ',', lineup with the - // lowest one, but check for cpp base class - // declaration/initialization, if it is an - // opening brace or we are looking just for - // enumerations/initializations. - if (terminated == ',') - { - if (curbuf->b_ind_cpp_baseclass == 0) - break; - - lookfor = LOOKFOR_CPP_BASECLASS; - continue; - } - - // Ignore unterminated lines in between, but - // reduce indent. - if (amount > cur_amount) - amount = cur_amount; - } - else - { - // Found first unterminated line on a row, may - // line up with this line, remember its indent - // 100 + - // -> here; - l = ml_get_curline(); - amount = cur_amount; - - n = (int)STRLEN(l); - if (terminated == ',' && (*skipwhite(l) == ']' - || (n >=2 && l[n - 2] == ']'))) - break; - - // If previous line ends in ',', check whether we - // are in an initialization or enum - // struct xxx = - // { - // sizeof a, - // 124 }; - // or a normal possible continuation line. - // but only, of no other statement has been found - // yet. - if (lookfor == LOOKFOR_INITIAL && terminated == ',') - { - if (curbuf->b_ind_js) - { - // Search for a line ending in a comma - // and line up with the line below it - // (could be the current line). - // some = [ - // 1, <- line up here - // 2, - // some = [ - // 3 + <- line up here - // 4 * - // 5, - // 6, - if (cin_iscomment(skipwhite(l))) - break; - lookfor = LOOKFOR_COMMA; - trypos = find_match_char('[', - curbuf->b_ind_maxparen); - if (trypos != NULL) - { - if (trypos->lnum - == curwin->w_cursor.lnum - 1) - { - // Current line is first inside - // [], line up with it. - break; - } - ourscope = trypos->lnum; - } - } - else - { - lookfor = LOOKFOR_ENUM_OR_INIT; - cont_amount = cin_first_id_amount(); - } - } - else - { - if (lookfor == LOOKFOR_INITIAL - && *l != NUL - && l[STRLEN(l) - 1] == '\\') - // XXX - cont_amount = cin_get_equal_amount( - curwin->w_cursor.lnum); - if (lookfor != LOOKFOR_TERM - && lookfor != LOOKFOR_JS_KEY - && lookfor != LOOKFOR_COMMA - && raw_string_start != curwin->w_cursor.lnum) - lookfor = LOOKFOR_UNTERM; - } - } - } - } - - // Check if we are after a while (cond); - // If so: Ignore until the matching "do". - else if (cin_iswhileofdo_end(terminated)) // XXX - { - // Found an unterminated line after a while ();, line up - // with the last one. - // while (cond); - // 100 + <- line up with this one - // -> here; - if (lookfor == LOOKFOR_UNTERM - || lookfor == LOOKFOR_ENUM_OR_INIT) - { - if (cont_amount > 0) - amount = cont_amount; - else - amount += ind_continuation; - break; - } - - if (whilelevel == 0) - { - lookfor = LOOKFOR_TERM; - amount = get_indent(); // XXX - if (theline[0] == '{') - amount += curbuf->b_ind_open_extra; - } - ++whilelevel; - } - - // We are after a "normal" statement. - // If we had another statement we can stop now and use the - // indent of that other statement. - // Otherwise the indent of the current statement may be used, - // search backwards for the next "normal" statement. - else - { - // Skip single break line, if before a switch label. It - // may be lined up with the case label. - if (lookfor == LOOKFOR_NOBREAK - && cin_isbreak(skipwhite(ml_get_curline()))) - { - lookfor = LOOKFOR_ANY; - continue; - } - - // Handle "do {" line. - if (whilelevel > 0) - { - l = cin_skipcomment(ml_get_curline()); - if (cin_isdo(l)) - { - amount = get_indent(); // XXX - --whilelevel; - continue; - } - } - - // Found a terminated line above an unterminated line. Add - // the amount for a continuation line. - // x = 1; - // y = foo + - // -> here; - // or - // int x = 1; - // int foo, - // -> here; - if (lookfor == LOOKFOR_UNTERM - || lookfor == LOOKFOR_ENUM_OR_INIT) - { - if (cont_amount > 0) - amount = cont_amount; - else - amount += ind_continuation; - break; - } - - // Found a terminated line above a terminated line or "if" - // etc. line. Use the amount of the line below us. - // x = 1; x = 1; - // if (asdf) y = 2; - // while (asdf) ->here; - // here; - // ->foo; - if (lookfor == LOOKFOR_TERM) - { - if (!lookfor_break && whilelevel == 0) - break; - } - - // First line above the one we're indenting is terminated. - // To know what needs to be done look further backward for - // a terminated line. - else - { - // position the cursor over the rightmost paren, so - // that matching it will take us back to the start of - // the line. Helps for: - // func(asdr, - // asdfasdf); - // here; -term_again: - l = ml_get_curline(); - if (find_last_paren(l, '(', ')') - && (trypos = find_match_paren( - curbuf->b_ind_maxparen)) != NULL) - { - // Check if we are on a case label now. This is - // handled above. - // case xx: if ( asdf && - // asdf) - curwin->w_cursor = *trypos; - l = ml_get_curline(); - if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) - { - ++curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; - continue; - } - } - - // When aligning with the case statement, don't align - // with a statement after it. - // case 1: { <-- don't use this { position - // stat; - // } - // case 2: - // stat; - // } - iscase = (curbuf->b_ind_keep_case_label - && cin_iscase(l, FALSE)); - - // Get indent and pointer to text for current line, - // ignoring any jump label. - amount = skip_label(curwin->w_cursor.lnum, &l); - - if (theline[0] == '{') - amount += curbuf->b_ind_open_extra; - // See remark above: "Only add b_ind_open_extra.." - l = skipwhite(l); - if (*l == '{') - amount -= curbuf->b_ind_open_extra; - lookfor = iscase ? LOOKFOR_ANY : LOOKFOR_TERM; - - // When a terminated line starts with "else" skip to - // the matching "if": - // else 3; - // indent this; - // Need to use the scope of this "else". XXX - // If whilelevel != 0 continue looking for a "do {". - if (lookfor == LOOKFOR_TERM - && *l != '}' - && cin_iselse(l) - && whilelevel == 0) - { - if ((trypos = find_start_brace()) == NULL - || find_match(LOOKFOR_IF, trypos->lnum) - == FAIL) - break; - continue; - } - - // If we're at the end of a block, skip to the start of - // that block. - l = ml_get_curline(); - if (find_last_paren(l, '{', '}') // XXX - && (trypos = find_start_brace()) != NULL) - { - curwin->w_cursor = *trypos; - // if not "else {" check for terminated again - // but skip block for "} else {" - l = cin_skipcomment(ml_get_curline()); - if (*l == '}' || !cin_iselse(l)) - goto term_again; - ++curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; - } - } - } - } - } - } - - // add extra indent for a comment - if (cin_iscomment(theline)) - amount += curbuf->b_ind_comment; - - // subtract extra left-shift for jump labels - if (curbuf->b_ind_jump_label > 0 && original_line_islabel) - amount -= curbuf->b_ind_jump_label; - - goto theend; - } - - // ok -- we're not inside any sort of structure at all! - // - // This means we're at the top level, and everything should - // basically just match where the previous line is, except - // for the lines immediately following a function declaration, - // which are K&R-style parameters and need to be indented. - // - // if our line starts with an open brace, forget about any - // prevailing indent and make sure it looks like the start - // of a function - - if (theline[0] == '{') - { - amount = curbuf->b_ind_first_open; - goto theend; - } - - // If the NEXT line is a function declaration, the current - // line needs to be indented as a function type spec. - // Don't do this if the current line looks like a comment or if the - // current line is terminated, ie. ends in ';', or if the current line - // contains { or }: "void f() {\n if (1)" - if (cur_curpos.lnum < curbuf->b_ml.ml_line_count - && !cin_nocode(theline) - && vim_strchr(theline, '{') == NULL - && vim_strchr(theline, '}') == NULL - && !cin_ends_in(theline, (char_u *)":", NULL) - && !cin_ends_in(theline, (char_u *)",", NULL) - && cin_isfuncdecl(NULL, cur_curpos.lnum + 1, - cur_curpos.lnum + 1) - && !cin_isterminated(theline, FALSE, TRUE)) - { - amount = curbuf->b_ind_func_type; - goto theend; - } - - // search backwards until we find something we recognize - amount = 0; - curwin->w_cursor = cur_curpos; - while (curwin->w_cursor.lnum > 1) - { - curwin->w_cursor.lnum--; - curwin->w_cursor.col = 0; - - l = ml_get_curline(); - - // If we're in a comment or raw string now, skip to the start - // of it. XXX - if ((trypos = ind_find_start_CORS(NULL)) != NULL) - { - curwin->w_cursor.lnum = trypos->lnum + 1; - curwin->w_cursor.col = 0; - continue; - } - - // Are we at the start of a cpp base class declaration or - // constructor initialization? XXX - n = FALSE; - if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') - { - n = cin_is_cpp_baseclass(&cache_cpp_baseclass); - l = ml_get_curline(); - } - if (n) - { - // XXX - amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col); - break; - } - - // Skip preprocessor directives and blank lines. - if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) - continue; - - if (cin_nocode(l)) - continue; - - // If the previous line ends in ',', use one level of - // indentation: - // int foo, - // bar; - // do this before checking for '}' in case of eg. - // enum foobar - // { - // ... - // } foo, - // bar; - n = 0; - if (cin_ends_in(l, (char_u *)",", NULL) - || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) - { - // take us back to opening paren - if (find_last_paren(l, '(', ')') - && (trypos = find_match_paren( - curbuf->b_ind_maxparen)) != NULL) - curwin->w_cursor = *trypos; - - /* - * For a line ending in ',' that is a continuation line go - * back to the first line with a backslash: - * char *foo = "bla\ - * bla", - * here; - */ - while (n == 0 && curwin->w_cursor.lnum > 1) - { - l = ml_get(curwin->w_cursor.lnum - 1); - if (*l == NUL || l[STRLEN(l) - 1] != '\\') - break; - --curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; - } - - amount = get_indent(); // XXX - - if (amount == 0) - amount = cin_first_id_amount(); - if (amount == 0) - amount = ind_continuation; - break; - } - - // If the line looks like a function declaration, and we're - // not in a comment, put it the left margin. - if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) // XXX - break; - l = ml_get_curline(); - - // Finding the closing '}' of a previous function. Put - // current line at the left margin. For when 'cino' has "fs". - if (*skipwhite(l) == '}') - break; - - // (matching {) - // If the previous line ends on '};' (maybe followed by - // comments) align at column 0. For example: - // char *string_array[] = { "foo", - // / * x * / "b};ar" }; / * foobar * / - if (cin_ends_in(l, (char_u *)"};", NULL)) - break; - - // If the previous line ends on '[' we are probably in an - // array constant: - // something = [ - // 234, <- extra indent - if (cin_ends_in(l, (char_u *)"[", NULL)) - { - amount = get_indent() + ind_continuation; - break; - } - - // Find a line only has a semicolon that belongs to a previous - // line ending in '}', e.g. before an #endif. Don't increase - // indent then. - if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) - { - pos_T curpos_save = curwin->w_cursor; - - while (curwin->w_cursor.lnum > 1) - { - look = ml_get(--curwin->w_cursor.lnum); - if (!(cin_nocode(look) || cin_ispreproc_cont( - &look, &curwin->w_cursor.lnum, &amount))) - break; - } - if (curwin->w_cursor.lnum > 0 - && cin_ends_in(look, (char_u *)"}", NULL)) - break; - - curwin->w_cursor = curpos_save; - } - - // If the PREVIOUS line is a function declaration, the current - // line (and the ones that follow) needs to be indented as - // parameters. - if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) - { - amount = curbuf->b_ind_param; - break; - } - - // If the previous line ends in ';' and the line before the - // previous line ends in ',' or '\', ident to column zero: - // int foo, - // bar; - // indent_to_0 here; - if (cin_ends_in(l, (char_u *)";", NULL)) - { - l = ml_get(curwin->w_cursor.lnum - 1); - if (cin_ends_in(l, (char_u *)",", NULL) - || (*l != NUL && l[STRLEN(l) - 1] == '\\')) - break; - l = ml_get_curline(); - } - - // Doesn't look like anything interesting -- so just - // use the indent of this line. - // - // Position the cursor over the rightmost paren, so that - // matching it will take us back to the start of the line. - find_last_paren(l, '(', ')'); - - if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) - curwin->w_cursor = *trypos; - amount = get_indent(); // XXX - break; - } - - // add extra indent for a comment - if (cin_iscomment(theline)) - amount += curbuf->b_ind_comment; - - /* - * add extra indent if the previous line ended in a backslash: - * "asdfasdf\ - * here"; - * char *foo = "asdf\ - * here"; - */ - if (cur_curpos.lnum > 1) - { - l = ml_get(cur_curpos.lnum - 1); - if (*l != NUL && l[STRLEN(l) - 1] == '\\') - { - cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1); - if (cur_amount > 0) - amount = cur_amount; - else if (cur_amount == 0) - amount += ind_continuation; - } - } - -theend: - if (amount < 0) - amount = 0; - -laterend: - // put the cursor back where it belongs - curwin->w_cursor = cur_curpos; - - vim_free(linecopy); - - return amount; -} - - static int -find_match(int lookfor, linenr_T ourscope) -{ - char_u *look; - pos_T *theirscope; - char_u *mightbeif; - int elselevel; - int whilelevel; - - if (lookfor == LOOKFOR_IF) - { - elselevel = 1; - whilelevel = 0; - } - else - { - elselevel = 0; - whilelevel = 1; - } - - curwin->w_cursor.col = 0; - - while (curwin->w_cursor.lnum > ourscope + 1) - { - curwin->w_cursor.lnum--; - curwin->w_cursor.col = 0; - - look = cin_skipcomment(ml_get_curline()); - if (cin_iselse(look) - || cin_isif(look) - || cin_isdo(look) // XXX - || cin_iswhileofdo(look, curwin->w_cursor.lnum)) - { - // if we've gone outside the braces entirely, - // we must be out of scope... - theirscope = find_start_brace(); // XXX - if (theirscope == NULL) - break; - - // and if the brace enclosing this is further - // back than the one enclosing the else, we're - // out of luck too. - if (theirscope->lnum < ourscope) - break; - - // and if they're enclosed in a *deeper* brace, - // then we can ignore it because it's in a - // different scope... - if (theirscope->lnum > ourscope) - continue; - - // if it was an "else" (that's not an "else if") - // then we need to go back to another if, so - // increment elselevel - look = cin_skipcomment(ml_get_curline()); - if (cin_iselse(look)) - { - mightbeif = cin_skipcomment(look + 4); - if (!cin_isif(mightbeif)) - ++elselevel; - continue; - } - - // if it was a "while" then we need to go back to - // another "do", so increment whilelevel. XXX - if (cin_iswhileofdo(look, curwin->w_cursor.lnum)) - { - ++whilelevel; - continue; - } - - // If it's an "if" decrement elselevel - look = cin_skipcomment(ml_get_curline()); - if (cin_isif(look)) - { - elselevel--; - // When looking for an "if" ignore "while"s that - // get in the way. - if (elselevel == 0 && lookfor == LOOKFOR_IF) - whilelevel = 0; - } - - // If it's a "do" decrement whilelevel - if (cin_isdo(look)) - whilelevel--; - - // if we've used up all the elses, then - // this must be the if that we want! - // match the indent level of that if. - if (elselevel <= 0 && whilelevel <= 0) - return OK; - } - } - return FAIL; -} - -# if defined(FEAT_EVAL) || defined(PROTO) -/* - * Get indent level from 'indentexpr'. - */ - int -get_expr_indent(void) -{ - int indent = -1; - char_u *inde_copy; - pos_T save_pos; - colnr_T save_curswant; - int save_set_curswant; - int save_State; - int use_sandbox = was_set_insecurely((char_u *)"indentexpr", - OPT_LOCAL); - - // Save and restore cursor position and curswant, in case it was changed - // via :normal commands - save_pos = curwin->w_cursor; - save_curswant = curwin->w_curswant; - save_set_curswant = curwin->w_set_curswant; - set_vim_var_nr(VV_LNUM, curwin->w_cursor.lnum); - if (use_sandbox) - ++sandbox; - ++textlock; - - // Need to make a copy, the 'indentexpr' option could be changed while - // evaluating it. - inde_copy = vim_strsave(curbuf->b_p_inde); - if (inde_copy != NULL) - { - indent = (int)eval_to_number(inde_copy); - vim_free(inde_copy); - } - - if (use_sandbox) - --sandbox; - --textlock; - - // Restore the cursor position so that 'indentexpr' doesn't need to. - // Pretend to be in Insert mode, allow cursor past end of line for "o" - // command. - save_State = State; - State = INSERT; - curwin->w_cursor = save_pos; - curwin->w_curswant = save_curswant; - curwin->w_set_curswant = save_set_curswant; - check_cursor(); - State = save_State; - - // If there is an error, just keep the current indent. - if (indent < 0) - indent = get_indent(); - - return indent; -} -# endif - -/* - * return TRUE if 'cinkeys' contains the key "keytyped", - * when == '*': Only if key is preceded with '*' (indent before insert) - * when == '!': Only if key is preceded with '!' (don't insert) - * when == ' ': Only if key is not preceded with '*'(indent afterwards) - * - * "keytyped" can have a few special values: - * KEY_OPEN_FORW - * KEY_OPEN_BACK - * KEY_COMPLETE just finished completion. - * - * If line_is_empty is TRUE accept keys with '0' before them. - */ - int -in_cinkeys( - int keytyped, - int when, - int line_is_empty) -{ - char_u *look; - int try_match; - int try_match_word; - char_u *p; - char_u *line; - int icase; - int i; - - if (keytyped == NUL) - // Can happen with CTRL-Y and CTRL-E on a short line. - return FALSE; - -#ifdef FEAT_EVAL - if (*curbuf->b_p_inde != NUL) - look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys' - else -#endif - look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys' - while (*look) - { - // Find out if we want to try a match with this key, depending on - // 'when' and a '*' or '!' before the key. - switch (when) - { - case '*': try_match = (*look == '*'); break; - case '!': try_match = (*look == '!'); break; - default: try_match = (*look != '*'); break; - } - if (*look == '*' || *look == '!') - ++look; - - // If there is a '0', only accept a match if the line is empty. - // But may still match when typing last char of a word. - if (*look == '0') - { - try_match_word = try_match; - if (!line_is_empty) - try_match = FALSE; - ++look; - } - else - try_match_word = FALSE; - - // does it look like a control character? - if (*look == '^' -#ifdef EBCDIC - && (Ctrl_chr(look[1]) != 0) -#else - && look[1] >= '?' && look[1] <= '_' -#endif - ) - { - if (try_match && keytyped == Ctrl_chr(look[1])) - return TRUE; - look += 2; - } - // 'o' means "o" command, open forward. - // 'O' means "O" command, open backward. - else if (*look == 'o') - { - if (try_match && keytyped == KEY_OPEN_FORW) - return TRUE; - ++look; - } - else if (*look == 'O') - { - if (try_match && keytyped == KEY_OPEN_BACK) - return TRUE; - ++look; - } - - // 'e' means to check for "else" at start of line and just before the - // cursor. - else if (*look == 'e') - { - if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) - { - p = ml_get_curline(); - if (skipwhite(p) == p + curwin->w_cursor.col - 4 && - STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) - return TRUE; - } - ++look; - } - - // ':' only causes an indent if it is at the end of a label or case - // statement, or when it was before typing the ':' (to fix - // class::method for C++). - else if (*look == ':') - { - if (try_match && keytyped == ':') - { - p = ml_get_curline(); - if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel()) - return TRUE; - // Need to get the line again after cin_islabel(). - p = ml_get_curline(); - if (curwin->w_cursor.col > 2 - && p[curwin->w_cursor.col - 1] == ':' - && p[curwin->w_cursor.col - 2] == ':') - { - p[curwin->w_cursor.col - 1] = ' '; - i = (cin_iscase(p, FALSE) || cin_isscopedecl(p) - || cin_islabel()); - p = ml_get_curline(); - p[curwin->w_cursor.col - 1] = ':'; - if (i) - return TRUE; - } - } - ++look; - } - - - // Is it a key in <>, maybe? - else if (*look == '<') - { - if (try_match) - { - // make up some named keys , , , <0>, <>>, <<>, <*>, - // <:> and so that people can re-indent on o, O, e, 0, <, - // >, *, : and ! keys if they really really want to. - if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL - && keytyped == look[1]) - return TRUE; - - if (keytyped == get_special_key_code(look + 1)) - return TRUE; - } - while (*look && *look != '>') - look++; - while (*look == '>') - look++; - } - - // Is it a word: "=word"? - else if (*look == '=' && look[1] != ',' && look[1] != NUL) - { - ++look; - if (*look == '~') - { - icase = TRUE; - ++look; - } - else - icase = FALSE; - p = vim_strchr(look, ','); - if (p == NULL) - p = look + STRLEN(look); - if ((try_match || try_match_word) - && curwin->w_cursor.col >= (colnr_T)(p - look)) - { - int match = FALSE; - - if (keytyped == KEY_COMPLETE) - { - char_u *s; - - // Just completed a word, check if it starts with "look". - // search back for the start of a word. - line = ml_get_curline(); - if (has_mbyte) - { - char_u *n; - - for (s = line + curwin->w_cursor.col; s > line; s = n) - { - n = mb_prevptr(line, s); - if (!vim_iswordp(n)) - break; - } - } - else - for (s = line + curwin->w_cursor.col; s > line; --s) - if (!vim_iswordc(s[-1])) - break; - if (s + (p - look) <= line + curwin->w_cursor.col - && (icase - ? MB_STRNICMP(s, look, p - look) - : STRNCMP(s, look, p - look)) == 0) - match = TRUE; - } - else - // TODO: multi-byte - if (keytyped == (int)p[-1] || (icase && keytyped < 256 - && TOLOWER_LOC(keytyped) == TOLOWER_LOC((int)p[-1]))) - { - line = ml_get_cursor(); - if ((curwin->w_cursor.col == (colnr_T)(p - look) - || !vim_iswordc(line[-(p - look) - 1])) - && (icase - ? MB_STRNICMP(line - (p - look), look, p - look) - : STRNCMP(line - (p - look), look, p - look)) - == 0) - match = TRUE; - } - if (match && try_match_word && !try_match) - { - // "0=word": Check if there are only blanks before the - // word. - if (getwhitecols_curline() != - (int)(curwin->w_cursor.col - (p - look))) - match = FALSE; - } - if (match) - return TRUE; - } - look = p; - } - - // ok, it's a boring generic character. - else - { - if (try_match && *look == keytyped) - return TRUE; - if (*look != NUL) - ++look; - } - - // Skip over ", ". - look = skip_to_option_part(look); - } - return FALSE; -} -#endif // FEAT_CINDENT - -#if defined(FEAT_LISP) || defined(PROTO) - - static int -lisp_match(char_u *p) -{ - char_u buf[LSIZE]; - int len; - char_u *word = *curbuf->b_p_lw != NUL ? curbuf->b_p_lw : p_lispwords; - - while (*word != NUL) - { - (void)copy_option_part(&word, buf, LSIZE, ","); - len = (int)STRLEN(buf); - if (STRNCMP(buf, p, len) == 0 && p[len] == ' ') - return TRUE; - } - return FALSE; -} - -/* - * When 'p' is present in 'cpoptions, a Vi compatible method is used. - * The incompatible newer method is quite a bit better at indenting - * code in lisp-like languages than the traditional one; it's still - * mostly heuristics however -- Dirk van Deun, dirk@rave.org - * - * TODO: - * Findmatch() should be adapted for lisp, also to make showmatch - * work correctly: now (v5.3) it seems all C/C++ oriented: - * - it does not recognize the #\( and #\) notations as character literals - * - it doesn't know about comments starting with a semicolon - * - it incorrectly interprets '(' as a character literal - * All this messes up get_lisp_indent in some rare cases. - * Update from Sergey Khorev: - * I tried to fix the first two issues. - */ - int -get_lisp_indent(void) -{ - pos_T *pos, realpos, paren; - int amount; - char_u *that; - colnr_T col; - colnr_T firsttry; - int parencount, quotecount; - int vi_lisp; - - // Set vi_lisp to use the vi-compatible method - vi_lisp = (vim_strchr(p_cpo, CPO_LISP) != NULL); - - realpos = curwin->w_cursor; - curwin->w_cursor.col = 0; - - if ((pos = findmatch(NULL, '(')) == NULL) - pos = findmatch(NULL, '['); - else - { - paren = *pos; - pos = findmatch(NULL, '['); - if (pos == NULL || LT_POSP(pos, &paren)) - pos = &paren; - } - if (pos != NULL) - { - // Extra trick: Take the indent of the first previous non-white - // line that is at the same () level. - amount = -1; - parencount = 0; - - while (--curwin->w_cursor.lnum >= pos->lnum) - { - if (linewhite(curwin->w_cursor.lnum)) - continue; - for (that = ml_get_curline(); *that != NUL; ++that) - { - if (*that == ';') - { - while (*(that + 1) != NUL) - ++that; - continue; - } - if (*that == '\\') - { - if (*(that + 1) != NUL) - ++that; - continue; - } - if (*that == '"' && *(that + 1) != NUL) - { - while (*++that && *that != '"') - { - // skipping escaped characters in the string - if (*that == '\\') - { - if (*++that == NUL) - break; - if (that[1] == NUL) - { - ++that; - break; - } - } - } - } - if (*that == '(' || *that == '[') - ++parencount; - else if (*that == ')' || *that == ']') - --parencount; - } - if (parencount == 0) - { - amount = get_indent(); - break; - } - } - - if (amount == -1) - { - curwin->w_cursor.lnum = pos->lnum; - curwin->w_cursor.col = pos->col; - col = pos->col; - - that = ml_get_curline(); - - if (vi_lisp && get_indent() == 0) - amount = 2; - else - { - char_u *line = that; - - amount = 0; - while (*that && col) - { - amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount); - col--; - } - - // Some keywords require "body" indenting rules (the - // non-standard-lisp ones are Scheme special forms): - // - // (let ((a 1)) instead (let ((a 1)) - // (...)) of (...)) - - if (!vi_lisp && (*that == '(' || *that == '[') - && lisp_match(that + 1)) - amount += 2; - else - { - that++; - amount++; - firsttry = amount; - - while (VIM_ISWHITE(*that)) - { - amount += lbr_chartabsize(line, that, (colnr_T)amount); - ++that; - } - - if (*that && *that != ';') // not a comment line - { - // test *that != '(' to accommodate first let/do - // argument if it is more than one line - if (!vi_lisp && *that != '(' && *that != '[') - firsttry++; - - parencount = 0; - quotecount = 0; - - if (vi_lisp - || (*that != '"' - && *that != '\'' - && *that != '#' - && (*that < '0' || *that > '9'))) - { - while (*that - && (!VIM_ISWHITE(*that) - || quotecount - || parencount) - && (!((*that == '(' || *that == '[') - && !quotecount - && !parencount - && vi_lisp))) - { - if (*that == '"') - quotecount = !quotecount; - if ((*that == '(' || *that == '[') - && !quotecount) - ++parencount; - if ((*that == ')' || *that == ']') - && !quotecount) - --parencount; - if (*that == '\\' && *(that+1) != NUL) - amount += lbr_chartabsize_adv( - line, &that, (colnr_T)amount); - amount += lbr_chartabsize_adv( - line, &that, (colnr_T)amount); - } - } - while (VIM_ISWHITE(*that)) - { - amount += lbr_chartabsize( - line, that, (colnr_T)amount); - that++; - } - if (!*that || *that == ';') - amount = firsttry; - } - } - } - } - } - else - amount = 0; // no matching '(' or '[' found, use zero indent - - curwin->w_cursor = realpos; - - return amount; -} -#endif // FEAT_LISP - -#if defined(FEAT_CINDENT) || defined(PROTO) -/* - * Do C or expression indenting on the current line. - */ - void -do_c_expr_indent(void) -{ -# ifdef FEAT_EVAL - if (*curbuf->b_p_inde != NUL) - fixthisline(get_expr_indent); - else -# endif - fixthisline(get_c_indent); -} -#endif - -#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO) -/* - * Re-indent the current line, based on the current contents of it and the - * surrounding lines. Fixing the cursor position seems really easy -- I'm very - * confused what all the part that handles Control-T is doing that I'm not. - * "get_the_indent" should be get_c_indent, get_expr_indent or get_lisp_indent. - */ - - void -fixthisline(int (*get_the_indent)(void)) -{ - int amount = get_the_indent(); - - if (amount >= 0) - { - change_indent(INDENT_SET, amount, FALSE, 0, TRUE); - if (linewhite(curwin->w_cursor.lnum)) - did_ai = TRUE; // delete the indent if the line stays empty - } -} - - void -fix_indent(void) -{ - if (p_paste) - return; -# ifdef FEAT_LISP - if (curbuf->b_p_lisp && curbuf->b_p_ai) - fixthisline(get_lisp_indent); -# endif -# if defined(FEAT_LISP) && defined(FEAT_CINDENT) - else -# endif -# ifdef FEAT_CINDENT - if (cindent_on()) - do_c_expr_indent(); -# endif -} - -#endif - #if defined(FEAT_VARTABS) || defined(PROTO) /* @@ -4696,7 +251,7 @@ tabstop_fromto( /* * See if two tabstop arrays contain the same values. */ - int + static int tabstop_eq(int *ts1, int *ts2) { int t; @@ -4715,7 +270,7 @@ tabstop_eq(int *ts1, int *ts2) return TRUE; } -#if defined(FEAT_BEVAL) || defined(PROTO) +# if defined(FEAT_BEVAL) || defined(PROTO) /* * Copy a tabstop array, allocating space for the new array. */ @@ -4733,7 +288,7 @@ tabstop_copy(int *oldts) newts[t] = oldts[t]; return newts; } -#endif +# endif /* * Return a count of the number of tabstops. @@ -4799,11 +354,11 @@ get_sw_value_indent(buf_T *buf) get_sw_value_col(buf_T *buf, colnr_T col UNUSED) { return buf->b_p_sw ? buf->b_p_sw : - #ifdef FEAT_VARTABS +#ifdef FEAT_VARTABS tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array); - #else +#else buf->b_p_ts; - #endif +#endif } /* @@ -4815,3 +370,1678 @@ get_sts_value(void) { return curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts; } + +/* + * Count the size (in window cells) of the indent in the current line. + */ + int +get_indent(void) +{ +#ifdef FEAT_VARTABS + return get_indent_str_vtab(ml_get_curline(), (int)curbuf->b_p_ts, + curbuf->b_p_vts_array, FALSE); +#else + return get_indent_str(ml_get_curline(), (int)curbuf->b_p_ts, FALSE); +#endif +} + +/* + * Count the size (in window cells) of the indent in line "lnum". + */ + int +get_indent_lnum(linenr_T lnum) +{ +#ifdef FEAT_VARTABS + return get_indent_str_vtab(ml_get(lnum), (int)curbuf->b_p_ts, + curbuf->b_p_vts_array, FALSE); +#else + return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts, FALSE); +#endif +} + +#if defined(FEAT_FOLDING) || defined(PROTO) +/* + * Count the size (in window cells) of the indent in line "lnum" of buffer + * "buf". + */ + int +get_indent_buf(buf_T *buf, linenr_T lnum) +{ +# ifdef FEAT_VARTABS + return get_indent_str_vtab(ml_get_buf(buf, lnum, FALSE), + (int)curbuf->b_p_ts, buf->b_p_vts_array, FALSE); +# else + return get_indent_str(ml_get_buf(buf, lnum, FALSE), (int)buf->b_p_ts, FALSE); +# endif +} +#endif + +/* + * count the size (in window cells) of the indent in line "ptr", with + * 'tabstop' at "ts" + */ + int +get_indent_str( + char_u *ptr, + int ts, + int list) // if TRUE, count only screen size for tabs +{ + int count = 0; + + for ( ; *ptr; ++ptr) + { + if (*ptr == TAB) + { + if (!list || lcs_tab1) // count a tab for what it is worth + count += ts - (count % ts); + else + // In list mode, when tab is not set, count screen char width + // for Tab, displays: ^I + count += ptr2cells(ptr); + } + else if (*ptr == ' ') + ++count; // count a space for one + else + break; + } + return count; +} + +#ifdef FEAT_VARTABS +/* + * Count the size (in window cells) of the indent in line "ptr", using + * variable tabstops. + * if "list" is TRUE, count only screen size for tabs. + */ + int +get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list) +{ + int count = 0; + + for ( ; *ptr; ++ptr) + { + if (*ptr == TAB) // count a tab for what it is worth + { + if (!list || lcs_tab1) + count += tabstop_padding(count, ts, vts); + else + // In list mode, when tab is not set, count screen char width + // for Tab, displays: ^I + count += ptr2cells(ptr); + } + else if (*ptr == ' ') + ++count; // count a space for one + else + break; + } + return count; +} +#endif + +/* + * Set the indent of the current line. + * Leaves the cursor on the first non-blank in the line. + * Caller must take care of undo. + * "flags": + * SIN_CHANGED: call changed_bytes() if the line was changed. + * SIN_INSERT: insert the indent in front of the line. + * SIN_UNDO: save line for undo before changing it. + * Returns TRUE if the line was changed. + */ + int +set_indent( + int size, // measured in spaces + int flags) +{ + char_u *p; + char_u *newline; + char_u *oldline; + char_u *s; + int todo; + int ind_len; // measured in characters + int line_len; + int doit = FALSE; + int ind_done = 0; // measured in spaces +#ifdef FEAT_VARTABS + int ind_col = 0; +#endif + int tab_pad; + int retval = FALSE; + int orig_char_len = -1; // number of initial whitespace chars when + // 'et' and 'pi' are both set + + // First check if there is anything to do and compute the number of + // characters needed for the indent. + todo = size; + ind_len = 0; + p = oldline = ml_get_curline(); + + // Calculate the buffer size for the new indent, and check to see if it + // isn't already set + + // if 'expandtab' isn't set: use TABs; if both 'expandtab' and + // 'preserveindent' are set count the number of characters at the + // beginning of the line to be copied + if (!curbuf->b_p_et || (!(flags & SIN_INSERT) && curbuf->b_p_pi)) + { + // If 'preserveindent' is set then reuse as much as possible of + // the existing indent structure for the new indent + if (!(flags & SIN_INSERT) && curbuf->b_p_pi) + { + ind_done = 0; + + // count as many characters as we can use + while (todo > 0 && VIM_ISWHITE(*p)) + { + if (*p == TAB) + { +#ifdef FEAT_VARTABS + tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, + curbuf->b_p_vts_array); +#else + tab_pad = (int)curbuf->b_p_ts + - (ind_done % (int)curbuf->b_p_ts); +#endif + // stop if this tab will overshoot the target + if (todo < tab_pad) + break; + todo -= tab_pad; + ++ind_len; + ind_done += tab_pad; + } + else + { + --todo; + ++ind_len; + ++ind_done; + } + ++p; + } + +#ifdef FEAT_VARTABS + // These diverge from this point. + ind_col = ind_done; +#endif + // Set initial number of whitespace chars to copy if we are + // preserving indent but expandtab is set + if (curbuf->b_p_et) + orig_char_len = ind_len; + + // Fill to next tabstop with a tab, if possible +#ifdef FEAT_VARTABS + tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, + curbuf->b_p_vts_array); +#else + tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); +#endif + if (todo >= tab_pad && orig_char_len == -1) + { + doit = TRUE; + todo -= tab_pad; + ++ind_len; + // ind_done += tab_pad; +#ifdef FEAT_VARTABS + ind_col += tab_pad; +#endif + } + } + + // count tabs required for indent +#ifdef FEAT_VARTABS + for (;;) + { + tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, + curbuf->b_p_vts_array); + if (todo < tab_pad) + break; + if (*p != TAB) + doit = TRUE; + else + ++p; + todo -= tab_pad; + ++ind_len; + ind_col += tab_pad; + } +#else + while (todo >= (int)curbuf->b_p_ts) + { + if (*p != TAB) + doit = TRUE; + else + ++p; + todo -= (int)curbuf->b_p_ts; + ++ind_len; + // ind_done += (int)curbuf->b_p_ts; + } +#endif + } + // count spaces required for indent + while (todo > 0) + { + if (*p != ' ') + doit = TRUE; + else + ++p; + --todo; + ++ind_len; + // ++ind_done; + } + + // Return if the indent is OK already. + if (!doit && !VIM_ISWHITE(*p) && !(flags & SIN_INSERT)) + return FALSE; + + // Allocate memory for the new line. + if (flags & SIN_INSERT) + p = oldline; + else + p = skipwhite(p); + line_len = (int)STRLEN(p) + 1; + + // If 'preserveindent' and 'expandtab' are both set keep the original + // characters and allocate accordingly. We will fill the rest with spaces + // after the if (!curbuf->b_p_et) below. + if (orig_char_len != -1) + { + newline = alloc(orig_char_len + size - ind_done + line_len); + if (newline == NULL) + return FALSE; + todo = size - ind_done; + ind_len = orig_char_len + todo; // Set total length of indent in + // characters, which may have been + // undercounted until now + p = oldline; + s = newline; + while (orig_char_len > 0) + { + *s++ = *p++; + orig_char_len--; + } + + // Skip over any additional white space (useful when newindent is less + // than old) + while (VIM_ISWHITE(*p)) + ++p; + + } + else + { + todo = size; + newline = alloc(ind_len + line_len); + if (newline == NULL) + return FALSE; + s = newline; + } + + // Put the characters in the new line. + // if 'expandtab' isn't set: use TABs + if (!curbuf->b_p_et) + { + // If 'preserveindent' is set then reuse as much as possible of + // the existing indent structure for the new indent + if (!(flags & SIN_INSERT) && curbuf->b_p_pi) + { + p = oldline; + ind_done = 0; + + while (todo > 0 && VIM_ISWHITE(*p)) + { + if (*p == TAB) + { +#ifdef FEAT_VARTABS + tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, + curbuf->b_p_vts_array); +#else + tab_pad = (int)curbuf->b_p_ts + - (ind_done % (int)curbuf->b_p_ts); +#endif + // stop if this tab will overshoot the target + if (todo < tab_pad) + break; + todo -= tab_pad; + ind_done += tab_pad; + } + else + { + --todo; + ++ind_done; + } + *s++ = *p++; + } + + // Fill to next tabstop with a tab, if possible +#ifdef FEAT_VARTABS + tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, + curbuf->b_p_vts_array); +#else + tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); +#endif + if (todo >= tab_pad) + { + *s++ = TAB; + todo -= tab_pad; +#ifdef FEAT_VARTABS + ind_done += tab_pad; +#endif + } + + p = skipwhite(p); + } + +#ifdef FEAT_VARTABS + for (;;) + { + tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, + curbuf->b_p_vts_array); + if (todo < tab_pad) + break; + *s++ = TAB; + todo -= tab_pad; + ind_done += tab_pad; + } +#else + while (todo >= (int)curbuf->b_p_ts) + { + *s++ = TAB; + todo -= (int)curbuf->b_p_ts; + } +#endif + } + while (todo > 0) + { + *s++ = ' '; + --todo; + } + mch_memmove(s, p, (size_t)line_len); + + // Replace the line (unless undo fails). + if (!(flags & SIN_UNDO) || u_savesub(curwin->w_cursor.lnum) == OK) + { + ml_replace(curwin->w_cursor.lnum, newline, FALSE); + if (flags & SIN_CHANGED) + changed_bytes(curwin->w_cursor.lnum, 0); + + // Correct saved cursor position if it is in this line. + if (saved_cursor.lnum == curwin->w_cursor.lnum) + { + if (saved_cursor.col >= (colnr_T)(p - oldline)) + // cursor was after the indent, adjust for the number of + // bytes added/removed + saved_cursor.col += ind_len - (colnr_T)(p - oldline); + else if (saved_cursor.col >= (colnr_T)(s - newline)) + // cursor was in the indent, and is now after it, put it back + // at the start of the indent (replacing spaces with TAB) + saved_cursor.col = (colnr_T)(s - newline); + } +#ifdef FEAT_TEXT_PROP + { + int added = ind_len - (colnr_T)(p - oldline); + + // When increasing indent this behaves like spaces were inserted at + // the old indent, when decreasing indent it behaves like spaces + // were deleted at the new indent. + adjust_prop_columns(curwin->w_cursor.lnum, + (colnr_T)(added > 0 ? (p - oldline) : ind_len), added, 0); + } +#endif + retval = TRUE; + } + else + vim_free(newline); + + curwin->w_cursor.col = ind_len; + return retval; +} + +/* + * Return the indent of the current line after a number. Return -1 if no + * number was found. Used for 'n' in 'formatoptions': numbered list. + * Since a pattern is used it can actually handle more than numbers. + */ + int +get_number_indent(linenr_T lnum) +{ + colnr_T col; + pos_T pos; + + regmatch_T regmatch; + int lead_len = 0; // length of comment leader + + if (lnum > curbuf->b_ml.ml_line_count) + return -1; + pos.lnum = 0; + + // In format_lines() (i.e. not insert mode), fo+=q is needed too... + if ((State & INSERT) || has_format_option(FO_Q_COMS)) + lead_len = get_leader_len(ml_get(lnum), NULL, FALSE, TRUE); + + regmatch.regprog = vim_regcomp(curbuf->b_p_flp, RE_MAGIC); + if (regmatch.regprog != NULL) + { + regmatch.rm_ic = FALSE; + + // vim_regexec() expects a pointer to a line. This lets us + // start matching for the flp beyond any comment leader... + if (vim_regexec(®match, ml_get(lnum) + lead_len, (colnr_T)0)) + { + pos.lnum = lnum; + pos.col = (colnr_T)(*regmatch.endp - ml_get(lnum)); + pos.coladd = 0; + } + vim_regfree(regmatch.regprog); + } + + if (pos.lnum == 0 || *ml_get_pos(&pos) == NUL) + return -1; + getvcol(curwin, &pos, &col, NULL, NULL); + return (int)col; +} + +#if defined(FEAT_LINEBREAK) || defined(PROTO) +/* + * Return appropriate space number for breakindent, taking influencing + * parameters into account. Window must be specified, since it is not + * necessarily always the current one. + */ + int +get_breakindent_win( + win_T *wp, + char_u *line) // start of the line +{ + static int prev_indent = 0; // cached indent value + static long prev_ts = 0L; // cached tabstop value + static char_u *prev_line = NULL; // cached pointer to line + static varnumber_T prev_tick = 0; // changedtick of cached value +# ifdef FEAT_VARTABS + static int *prev_vts = NULL; // cached vartabs values +# endif + int bri = 0; + // window width minus window margin space, i.e. what rests for text + const int eff_wwidth = wp->w_width + - ((wp->w_p_nu || wp->w_p_rnu) + && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) + ? number_width(wp) + 1 : 0); + + // used cached indent, unless pointer or 'tabstop' changed + if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts + || prev_tick != CHANGEDTICK(wp->w_buffer) +# ifdef FEAT_VARTABS + || prev_vts != wp->w_buffer->b_p_vts_array +# endif + ) + { + prev_line = line; + prev_ts = wp->w_buffer->b_p_ts; + prev_tick = CHANGEDTICK(wp->w_buffer); +# ifdef FEAT_VARTABS + prev_vts = wp->w_buffer->b_p_vts_array; + prev_indent = get_indent_str_vtab(line, + (int)wp->w_buffer->b_p_ts, + wp->w_buffer->b_p_vts_array, wp->w_p_list); +# else + prev_indent = get_indent_str(line, + (int)wp->w_buffer->b_p_ts, wp->w_p_list); +# endif + } + bri = prev_indent + wp->w_p_brishift; + + // indent minus the length of the showbreak string + if (wp->w_p_brisbr) + bri -= vim_strsize(p_sbr); + + // Add offset for number column, if 'n' is in 'cpoptions' + bri += win_col_off2(wp); + + // never indent past left window margin + if (bri < 0) + bri = 0; + // always leave at least bri_min characters on the left, + // if text width is sufficient + else if (bri > eff_wwidth - wp->w_p_brimin) + bri = (eff_wwidth - wp->w_p_brimin < 0) + ? 0 : eff_wwidth - wp->w_p_brimin; + + return bri; +} +#endif + +/* + * When extra == 0: Return TRUE if the cursor is before or on the first + * non-blank in the line. + * When extra == 1: Return TRUE if the cursor is before the first non-blank in + * the line. + */ + int +inindent(int extra) +{ + char_u *ptr; + colnr_T col; + + for (col = 0, ptr = ml_get_curline(); VIM_ISWHITE(*ptr); ++col) + ++ptr; + if (col >= curwin->w_cursor.col + extra) + return TRUE; + else + return FALSE; +} + +#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO) +/* + * op_reindent - handle reindenting a block of lines. + */ + void +op_reindent(oparg_T *oap, int (*how)(void)) +{ + long i; + char_u *l; + int amount; + linenr_T first_changed = 0; + linenr_T last_changed = 0; + linenr_T start_lnum = curwin->w_cursor.lnum; + + // Don't even try when 'modifiable' is off. + if (!curbuf->b_p_ma) + { + emsg(_(e_modifiable)); + return; + } + + for (i = oap->line_count; --i >= 0 && !got_int; ) + { + // it's a slow thing to do, so give feedback so there's no worry that + // the computer's just hung. + + if (i > 1 + && (i % 50 == 0 || i == oap->line_count - 1) + && oap->line_count > p_report) + smsg(_("%ld lines to indent... "), i); + + // Be vi-compatible: For lisp indenting the first line is not + // indented, unless there is only one line. +# ifdef FEAT_LISP + if (i != oap->line_count - 1 || oap->line_count == 1 + || how != get_lisp_indent) +# endif + { + l = skipwhite(ml_get_curline()); + if (*l == NUL) // empty or blank line + amount = 0; + else + amount = how(); // get the indent for this line + + if (amount >= 0 && set_indent(amount, SIN_UNDO)) + { + // did change the indent, call changed_lines() later + if (first_changed == 0) + first_changed = curwin->w_cursor.lnum; + last_changed = curwin->w_cursor.lnum; + } + } + ++curwin->w_cursor.lnum; + curwin->w_cursor.col = 0; // make sure it's valid + } + + // put cursor on first non-blank of indented line + curwin->w_cursor.lnum = start_lnum; + beginline(BL_SOL | BL_FIX); + + // Mark changed lines so that they will be redrawn. When Visual + // highlighting was present, need to continue until the last line. When + // there is no change still need to remove the Visual highlighting. + if (last_changed != 0) + changed_lines(first_changed, 0, + oap->is_VIsual ? start_lnum + oap->line_count : + last_changed + 1, 0L); + else if (oap->is_VIsual) + redraw_curbuf_later(INVERTED); + + if (oap->line_count > p_report) + { + i = oap->line_count - (i + 1); + smsg(NGETTEXT("%ld line indented ", + "%ld lines indented ", i), i); + } + // set '[ and '] marks + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; +} +#endif // defined(FEAT_LISP) || defined(FEAT_CINDENT) + +#if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT) || defined(PROTO) +/* + * Return TRUE if lines starting with '#' should be left aligned. + */ + int +preprocs_left(void) +{ + return +# ifdef FEAT_SMARTINDENT +# ifdef FEAT_CINDENT + (curbuf->b_p_si && !curbuf->b_p_cin) || +# else + curbuf->b_p_si +# endif +# endif +# ifdef FEAT_CINDENT + (curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE) + && curbuf->b_ind_hash_comment == 0) +# endif + ; +} +#endif + +#ifdef FEAT_SMARTINDENT +/* + * Try to do some very smart auto-indenting. + * Used when inserting a "normal" character. + */ + void +ins_try_si(int c) +{ + pos_T *pos, old_pos; + char_u *ptr; + int i; + int temp; + + // do some very smart indenting when entering '{' or '}' + if (((did_si || can_si_back) && c == '{') || (can_si && c == '}')) + { + // for '}' set indent equal to indent of line containing matching '{' + if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) + { + old_pos = curwin->w_cursor; + // If the matching '{' has a ')' immediately before it (ignoring + // white-space), then line up with the start of the line + // containing the matching '(' if there is one. This handles the + // case where an "if (..\n..) {" statement continues over multiple + // lines -- webb + ptr = ml_get(pos->lnum); + i = pos->col; + if (i > 0) // skip blanks before '{' + while (--i > 0 && VIM_ISWHITE(ptr[i])) + ; + curwin->w_cursor.lnum = pos->lnum; + curwin->w_cursor.col = i; + if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) + curwin->w_cursor = *pos; + i = get_indent(); + curwin->w_cursor = old_pos; + if (State & VREPLACE_FLAG) + change_indent(INDENT_SET, i, FALSE, NUL, TRUE); + else + (void)set_indent(i, SIN_CHANGED); + } + else if (curwin->w_cursor.col > 0) + { + // when inserting '{' after "O" reduce indent, but not + // more than indent of previous line + temp = TRUE; + if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1) + { + old_pos = curwin->w_cursor; + i = get_indent(); + while (curwin->w_cursor.lnum > 1) + { + ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum))); + + // ignore empty lines and lines starting with '#'. + if (*ptr != '#' && *ptr != NUL) + break; + } + if (get_indent() >= i) + temp = FALSE; + curwin->w_cursor = old_pos; + } + if (temp) + shift_line(TRUE, FALSE, 1, TRUE); + } + } + + // set indent of '#' always to 0 + if (curwin->w_cursor.col > 0 && can_si && c == '#') + { + // remember current indent for next line + old_indent = get_indent(); + (void)set_indent(0, SIN_CHANGED); + } + + // Adjust ai_col, the char at this position can be deleted. + if (ai_col > curwin->w_cursor.col) + ai_col = curwin->w_cursor.col; +} +#endif + +/* + * Insert an indent (for or CTRL-T) or delete an indent (for CTRL-D). + * Keep the cursor on the same character. + * type == INDENT_INC increase indent (for CTRL-T or ) + * type == INDENT_DEC decrease indent (for CTRL-D) + * type == INDENT_SET set indent to "amount" + * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec). + */ + void +change_indent( + int type, + int amount, + int round, + int replaced, // replaced character, put on replace stack + int call_changed_bytes) // call changed_bytes() +{ + int vcol; + int last_vcol; + int insstart_less; // reduction for Insstart.col + int new_cursor_col; + int i; + char_u *ptr; + int save_p_list; + int start_col; + colnr_T vc; + colnr_T orig_col = 0; // init for GCC + char_u *new_line, *orig_line = NULL; // init for GCC + + // VREPLACE mode needs to know what the line was like before changing + if (State & VREPLACE_FLAG) + { + orig_line = vim_strsave(ml_get_curline()); // Deal with NULL below + orig_col = curwin->w_cursor.col; + } + + // for the following tricks we don't want list mode + save_p_list = curwin->w_p_list; + curwin->w_p_list = FALSE; + vc = getvcol_nolist(&curwin->w_cursor); + vcol = vc; + + // For Replace mode we need to fix the replace stack later, which is only + // possible when the cursor is in the indent. Remember the number of + // characters before the cursor if it's possible. + start_col = curwin->w_cursor.col; + + // determine offset from first non-blank + new_cursor_col = curwin->w_cursor.col; + beginline(BL_WHITE); + new_cursor_col -= curwin->w_cursor.col; + + insstart_less = curwin->w_cursor.col; + + // If the cursor is in the indent, compute how many screen columns the + // cursor is to the left of the first non-blank. + if (new_cursor_col < 0) + vcol = get_indent() - vcol; + + if (new_cursor_col > 0) // can't fix replace stack + start_col = -1; + + // Set the new indent. The cursor will be put on the first non-blank. + if (type == INDENT_SET) + (void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0); + else + { + int save_State = State; + + // Avoid being called recursively. + if (State & VREPLACE_FLAG) + State = INSERT; + shift_line(type == INDENT_DEC, round, 1, call_changed_bytes); + State = save_State; + } + insstart_less -= curwin->w_cursor.col; + + // Try to put cursor on same character. + // If the cursor is at or after the first non-blank in the line, + // compute the cursor column relative to the column of the first + // non-blank character. + // If we are not in insert mode, leave the cursor on the first non-blank. + // If the cursor is before the first non-blank, position it relative + // to the first non-blank, counted in screen columns. + if (new_cursor_col >= 0) + { + // When changing the indent while the cursor is touching it, reset + // Insstart_col to 0. + if (new_cursor_col == 0) + insstart_less = MAXCOL; + new_cursor_col += curwin->w_cursor.col; + } + else if (!(State & INSERT)) + new_cursor_col = curwin->w_cursor.col; + else + { + // Compute the screen column where the cursor should be. + vcol = get_indent() - vcol; + curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); + + // Advance the cursor until we reach the right screen column. + vcol = last_vcol = 0; + new_cursor_col = -1; + ptr = ml_get_curline(); + while (vcol <= (int)curwin->w_virtcol) + { + last_vcol = vcol; + if (has_mbyte && new_cursor_col >= 0) + new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col); + else + ++new_cursor_col; + vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol); + } + vcol = last_vcol; + + // May need to insert spaces to be able to position the cursor on + // the right screen column. + if (vcol != (int)curwin->w_virtcol) + { + curwin->w_cursor.col = (colnr_T)new_cursor_col; + i = (int)curwin->w_virtcol - vcol; + ptr = alloc(i + 1); + if (ptr != NULL) + { + new_cursor_col += i; + ptr[i] = NUL; + while (--i >= 0) + ptr[i] = ' '; + ins_str(ptr); + vim_free(ptr); + } + } + + // When changing the indent while the cursor is in it, reset + // Insstart_col to 0. + insstart_less = MAXCOL; + } + + curwin->w_p_list = save_p_list; + + if (new_cursor_col <= 0) + curwin->w_cursor.col = 0; + else + curwin->w_cursor.col = (colnr_T)new_cursor_col; + curwin->w_set_curswant = TRUE; + changed_cline_bef_curs(); + + // May have to adjust the start of the insert. + if (State & INSERT) + { + if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) + { + if ((int)Insstart.col <= insstart_less) + Insstart.col = 0; + else + Insstart.col -= insstart_less; + } + if ((int)ai_col <= insstart_less) + ai_col = 0; + else + ai_col -= insstart_less; + } + + // For REPLACE mode, may have to fix the replace stack, if it's possible. + // If the number of characters before the cursor decreased, need to pop a + // few characters from the replace stack. + // If the number of characters before the cursor increased, need to push a + // few NULs onto the replace stack. + if (REPLACE_NORMAL(State) && start_col >= 0) + { + while (start_col > (int)curwin->w_cursor.col) + { + replace_join(0); // remove a NUL from the replace stack + --start_col; + } + while (start_col < (int)curwin->w_cursor.col || replaced) + { + replace_push(NUL); + if (replaced) + { + replace_push(replaced); + replaced = NUL; + } + ++start_col; + } + } + + // For VREPLACE mode, we also have to fix the replace stack. In this case + // it is always possible because we backspace over the whole line and then + // put it back again the way we wanted it. + if (State & VREPLACE_FLAG) + { + // If orig_line didn't allocate, just return. At least we did the job, + // even if you can't backspace. + if (orig_line == NULL) + return; + + // Save new line + new_line = vim_strsave(ml_get_curline()); + if (new_line == NULL) + return; + + // We only put back the new line up to the cursor + new_line[curwin->w_cursor.col] = NUL; + + // Put back original line + ml_replace(curwin->w_cursor.lnum, orig_line, FALSE); + curwin->w_cursor.col = orig_col; + + // Backspace from cursor to start of line + backspace_until_column(0); + + // Insert new stuff into line again + ins_bytes(new_line); + + vim_free(new_line); + } +} + +/* + * Copy the indent from ptr to the current line (and fill to size) + * Leaves the cursor on the first non-blank in the line. + * Returns TRUE if the line was changed. + */ + int +copy_indent(int size, char_u *src) +{ + char_u *p = NULL; + char_u *line = NULL; + char_u *s; + int todo; + int ind_len; + int line_len = 0; + int tab_pad; + int ind_done; + int round; +#ifdef FEAT_VARTABS + int ind_col; +#endif + + // Round 1: compute the number of characters needed for the indent + // Round 2: copy the characters. + for (round = 1; round <= 2; ++round) + { + todo = size; + ind_len = 0; + ind_done = 0; +#ifdef FEAT_VARTABS + ind_col = 0; +#endif + s = src; + + // Count/copy the usable portion of the source line + while (todo > 0 && VIM_ISWHITE(*s)) + { + if (*s == TAB) + { +#ifdef FEAT_VARTABS + tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, + curbuf->b_p_vts_array); +#else + tab_pad = (int)curbuf->b_p_ts + - (ind_done % (int)curbuf->b_p_ts); +#endif + // Stop if this tab will overshoot the target + if (todo < tab_pad) + break; + todo -= tab_pad; + ind_done += tab_pad; +#ifdef FEAT_VARTABS + ind_col += tab_pad; +#endif + } + else + { + --todo; + ++ind_done; +#ifdef FEAT_VARTABS + ++ind_col; +#endif + } + ++ind_len; + if (p != NULL) + *p++ = *s; + ++s; + } + + // Fill to next tabstop with a tab, if possible +#ifdef FEAT_VARTABS + tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, + curbuf->b_p_vts_array); +#else + tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); +#endif + if (todo >= tab_pad && !curbuf->b_p_et) + { + todo -= tab_pad; + ++ind_len; +#ifdef FEAT_VARTABS + ind_col += tab_pad; +#endif + if (p != NULL) + *p++ = TAB; + } + + // Add tabs required for indent + if (!curbuf->b_p_et) + { +#ifdef FEAT_VARTABS + for (;;) + { + tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, + curbuf->b_p_vts_array); + if (todo < tab_pad) + break; + todo -= tab_pad; + ++ind_len; + ind_col += tab_pad; + if (p != NULL) + *p++ = TAB; + } +#else + while (todo >= (int)curbuf->b_p_ts) + { + todo -= (int)curbuf->b_p_ts; + ++ind_len; + if (p != NULL) + *p++ = TAB; + } +#endif + } + + // Count/add spaces required for indent + while (todo > 0) + { + --todo; + ++ind_len; + if (p != NULL) + *p++ = ' '; + } + + if (p == NULL) + { + // Allocate memory for the result: the copied indent, new indent + // and the rest of the line. + line_len = (int)STRLEN(ml_get_curline()) + 1; + line = alloc(ind_len + line_len); + if (line == NULL) + return FALSE; + p = line; + } + } + + // Append the original line + mch_memmove(p, ml_get_curline(), (size_t)line_len); + + // Replace the line + ml_replace(curwin->w_cursor.lnum, line, FALSE); + + // Put the cursor after the indent. + curwin->w_cursor.col = ind_len; + return TRUE; +} + +/* + * ":retab". + */ + void +ex_retab(exarg_T *eap) +{ + linenr_T lnum; + int got_tab = FALSE; + long num_spaces = 0; + long num_tabs; + long len; + long col; + long vcol; + long start_col = 0; /* For start of white-space string */ + long start_vcol = 0; /* For start of white-space string */ + long old_len; + char_u *ptr; + char_u *new_line = (char_u *)1; /* init to non-NULL */ + int did_undo; /* called u_save for current line */ +#ifdef FEAT_VARTABS + int *new_vts_array = NULL; + char_u *new_ts_str; /* string value of tab argument */ +#else + int temp; + int new_ts; +#endif + int save_list; + linenr_T first_line = 0; /* first changed line */ + linenr_T last_line = 0; /* last changed line */ + + save_list = curwin->w_p_list; + curwin->w_p_list = 0; /* don't want list mode here */ + +#ifdef FEAT_VARTABS + new_ts_str = eap->arg; + if (!tabstop_set(eap->arg, &new_vts_array)) + return; + while (vim_isdigit(*(eap->arg)) || *(eap->arg) == ',') + ++(eap->arg); + + // This ensures that either new_vts_array and new_ts_str are freshly + // allocated, or new_vts_array points to an existing array and new_ts_str + // is null. + if (new_vts_array == NULL) + { + new_vts_array = curbuf->b_p_vts_array; + new_ts_str = NULL; + } + else + new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str); +#else + new_ts = getdigits(&(eap->arg)); + if (new_ts < 0) + { + emsg(_(e_positive)); + return; + } + if (new_ts == 0) + new_ts = curbuf->b_p_ts; +#endif + for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum) + { + ptr = ml_get(lnum); + col = 0; + vcol = 0; + did_undo = FALSE; + for (;;) + { + if (VIM_ISWHITE(ptr[col])) + { + if (!got_tab && num_spaces == 0) + { + /* First consecutive white-space */ + start_vcol = vcol; + start_col = col; + } + if (ptr[col] == ' ') + num_spaces++; + else + got_tab = TRUE; + } + else + { + if (got_tab || (eap->forceit && num_spaces > 1)) + { + /* Retabulate this string of white-space */ + + /* len is virtual length of white string */ + len = num_spaces = vcol - start_vcol; + num_tabs = 0; + if (!curbuf->b_p_et) + { +#ifdef FEAT_VARTABS + int t, s; + + tabstop_fromto(start_vcol, vcol, + curbuf->b_p_ts, new_vts_array, &t, &s); + num_tabs = t; + num_spaces = s; +#else + temp = new_ts - (start_vcol % new_ts); + if (num_spaces >= temp) + { + num_spaces -= temp; + num_tabs++; + } + num_tabs += num_spaces / new_ts; + num_spaces -= (num_spaces / new_ts) * new_ts; +#endif + } + if (curbuf->b_p_et || got_tab || + (num_spaces + num_tabs < len)) + { + if (did_undo == FALSE) + { + did_undo = TRUE; + if (u_save((linenr_T)(lnum - 1), + (linenr_T)(lnum + 1)) == FAIL) + { + new_line = NULL; /* flag out-of-memory */ + break; + } + } + + /* len is actual number of white characters used */ + len = num_spaces + num_tabs; + old_len = (long)STRLEN(ptr); + new_line = alloc(old_len - col + start_col + len + 1); + if (new_line == NULL) + break; + if (start_col > 0) + mch_memmove(new_line, ptr, (size_t)start_col); + mch_memmove(new_line + start_col + len, + ptr + col, (size_t)(old_len - col + 1)); + ptr = new_line + start_col; + for (col = 0; col < len; col++) + ptr[col] = (col < num_tabs) ? '\t' : ' '; + ml_replace(lnum, new_line, FALSE); + if (first_line == 0) + first_line = lnum; + last_line = lnum; + ptr = new_line; + col = start_col + len; + } + } + got_tab = FALSE; + num_spaces = 0; + } + if (ptr[col] == NUL) + break; + vcol += chartabsize(ptr + col, (colnr_T)vcol); + if (has_mbyte) + col += (*mb_ptr2len)(ptr + col); + else + ++col; + } + if (new_line == NULL) /* out of memory */ + break; + line_breakcheck(); + } + if (got_int) + emsg(_(e_interr)); + +#ifdef FEAT_VARTABS + // If a single value was given then it can be considered equal to + // either the value of 'tabstop' or the value of 'vartabstop'. + if (tabstop_count(curbuf->b_p_vts_array) == 0 + && tabstop_count(new_vts_array) == 1 + && curbuf->b_p_ts == tabstop_first(new_vts_array)) + ; /* not changed */ + else if (tabstop_count(curbuf->b_p_vts_array) > 0 + && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) + ; /* not changed */ + else + redraw_curbuf_later(NOT_VALID); +#else + if (curbuf->b_p_ts != new_ts) + redraw_curbuf_later(NOT_VALID); +#endif + if (first_line != 0) + changed_lines(first_line, 0, last_line + 1, 0L); + + curwin->w_p_list = save_list; /* restore 'list' */ + +#ifdef FEAT_VARTABS + if (new_ts_str != NULL) /* set the new tabstop */ + { + // If 'vartabstop' is in use or if the value given to retab has more + // than one tabstop then update 'vartabstop'. + int *old_vts_ary = curbuf->b_p_vts_array; + + if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) + { + set_string_option_direct((char_u *)"vts", -1, new_ts_str, + OPT_FREE|OPT_LOCAL, 0); + curbuf->b_p_vts_array = new_vts_array; + vim_free(old_vts_ary); + } + else + { + // 'vartabstop' wasn't in use and a single value was given to + // retab then update 'tabstop'. + curbuf->b_p_ts = tabstop_first(new_vts_array); + vim_free(new_vts_array); + } + vim_free(new_ts_str); + } +#else + curbuf->b_p_ts = new_ts; +#endif + coladvance(curwin->w_curswant); + + u_clearline(); +} + +#if (defined(FEAT_CINDENT) && defined(FEAT_EVAL)) || defined(PROTO) +/* + * Get indent level from 'indentexpr'. + */ + int +get_expr_indent(void) +{ + int indent = -1; + char_u *inde_copy; + pos_T save_pos; + colnr_T save_curswant; + int save_set_curswant; + int save_State; + int use_sandbox = was_set_insecurely((char_u *)"indentexpr", + OPT_LOCAL); + + // Save and restore cursor position and curswant, in case it was changed + // via :normal commands + save_pos = curwin->w_cursor; + save_curswant = curwin->w_curswant; + save_set_curswant = curwin->w_set_curswant; + set_vim_var_nr(VV_LNUM, curwin->w_cursor.lnum); + if (use_sandbox) + ++sandbox; + ++textlock; + + // Need to make a copy, the 'indentexpr' option could be changed while + // evaluating it. + inde_copy = vim_strsave(curbuf->b_p_inde); + if (inde_copy != NULL) + { + indent = (int)eval_to_number(inde_copy); + vim_free(inde_copy); + } + + if (use_sandbox) + --sandbox; + --textlock; + + // Restore the cursor position so that 'indentexpr' doesn't need to. + // Pretend to be in Insert mode, allow cursor past end of line for "o" + // command. + save_State = State; + State = INSERT; + curwin->w_cursor = save_pos; + curwin->w_curswant = save_curswant; + curwin->w_set_curswant = save_set_curswant; + check_cursor(); + State = save_State; + + // If there is an error, just keep the current indent. + if (indent < 0) + indent = get_indent(); + + return indent; +} +#endif + +#if defined(FEAT_LISP) || defined(PROTO) + + static int +lisp_match(char_u *p) +{ + char_u buf[LSIZE]; + int len; + char_u *word = *curbuf->b_p_lw != NUL ? curbuf->b_p_lw : p_lispwords; + + while (*word != NUL) + { + (void)copy_option_part(&word, buf, LSIZE, ","); + len = (int)STRLEN(buf); + if (STRNCMP(buf, p, len) == 0 && p[len] == ' ') + return TRUE; + } + return FALSE; +} + +/* + * When 'p' is present in 'cpoptions, a Vi compatible method is used. + * The incompatible newer method is quite a bit better at indenting + * code in lisp-like languages than the traditional one; it's still + * mostly heuristics however -- Dirk van Deun, dirk@rave.org + * + * TODO: + * Findmatch() should be adapted for lisp, also to make showmatch + * work correctly: now (v5.3) it seems all C/C++ oriented: + * - it does not recognize the #\( and #\) notations as character literals + * - it doesn't know about comments starting with a semicolon + * - it incorrectly interprets '(' as a character literal + * All this messes up get_lisp_indent in some rare cases. + * Update from Sergey Khorev: + * I tried to fix the first two issues. + */ + int +get_lisp_indent(void) +{ + pos_T *pos, realpos, paren; + int amount; + char_u *that; + colnr_T col; + colnr_T firsttry; + int parencount, quotecount; + int vi_lisp; + + // Set vi_lisp to use the vi-compatible method + vi_lisp = (vim_strchr(p_cpo, CPO_LISP) != NULL); + + realpos = curwin->w_cursor; + curwin->w_cursor.col = 0; + + if ((pos = findmatch(NULL, '(')) == NULL) + pos = findmatch(NULL, '['); + else + { + paren = *pos; + pos = findmatch(NULL, '['); + if (pos == NULL || LT_POSP(pos, &paren)) + pos = &paren; + } + if (pos != NULL) + { + // Extra trick: Take the indent of the first previous non-white + // line that is at the same () level. + amount = -1; + parencount = 0; + + while (--curwin->w_cursor.lnum >= pos->lnum) + { + if (linewhite(curwin->w_cursor.lnum)) + continue; + for (that = ml_get_curline(); *that != NUL; ++that) + { + if (*that == ';') + { + while (*(that + 1) != NUL) + ++that; + continue; + } + if (*that == '\\') + { + if (*(that + 1) != NUL) + ++that; + continue; + } + if (*that == '"' && *(that + 1) != NUL) + { + while (*++that && *that != '"') + { + // skipping escaped characters in the string + if (*that == '\\') + { + if (*++that == NUL) + break; + if (that[1] == NUL) + { + ++that; + break; + } + } + } + } + if (*that == '(' || *that == '[') + ++parencount; + else if (*that == ')' || *that == ']') + --parencount; + } + if (parencount == 0) + { + amount = get_indent(); + break; + } + } + + if (amount == -1) + { + curwin->w_cursor.lnum = pos->lnum; + curwin->w_cursor.col = pos->col; + col = pos->col; + + that = ml_get_curline(); + + if (vi_lisp && get_indent() == 0) + amount = 2; + else + { + char_u *line = that; + + amount = 0; + while (*that && col) + { + amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount); + col--; + } + + // Some keywords require "body" indenting rules (the + // non-standard-lisp ones are Scheme special forms): + // + // (let ((a 1)) instead (let ((a 1)) + // (...)) of (...)) + + if (!vi_lisp && (*that == '(' || *that == '[') + && lisp_match(that + 1)) + amount += 2; + else + { + that++; + amount++; + firsttry = amount; + + while (VIM_ISWHITE(*that)) + { + amount += lbr_chartabsize(line, that, (colnr_T)amount); + ++that; + } + + if (*that && *that != ';') // not a comment line + { + // test *that != '(' to accommodate first let/do + // argument if it is more than one line + if (!vi_lisp && *that != '(' && *that != '[') + firsttry++; + + parencount = 0; + quotecount = 0; + + if (vi_lisp + || (*that != '"' + && *that != '\'' + && *that != '#' + && (*that < '0' || *that > '9'))) + { + while (*that + && (!VIM_ISWHITE(*that) + || quotecount + || parencount) + && (!((*that == '(' || *that == '[') + && !quotecount + && !parencount + && vi_lisp))) + { + if (*that == '"') + quotecount = !quotecount; + if ((*that == '(' || *that == '[') + && !quotecount) + ++parencount; + if ((*that == ')' || *that == ']') + && !quotecount) + --parencount; + if (*that == '\\' && *(that+1) != NUL) + amount += lbr_chartabsize_adv( + line, &that, (colnr_T)amount); + amount += lbr_chartabsize_adv( + line, &that, (colnr_T)amount); + } + } + while (VIM_ISWHITE(*that)) + { + amount += lbr_chartabsize( + line, that, (colnr_T)amount); + that++; + } + if (!*that || *that == ';') + amount = firsttry; + } + } + } + } + } + else + amount = 0; // no matching '(' or '[' found, use zero indent + + curwin->w_cursor = realpos; + + return amount; +} +#endif // FEAT_LISP + +#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO) +/* + * Re-indent the current line, based on the current contents of it and the + * surrounding lines. Fixing the cursor position seems really easy -- I'm very + * confused what all the part that handles Control-T is doing that I'm not. + * "get_the_indent" should be get_c_indent, get_expr_indent or get_lisp_indent. + */ + + void +fixthisline(int (*get_the_indent)(void)) +{ + int amount = get_the_indent(); + + if (amount >= 0) + { + change_indent(INDENT_SET, amount, FALSE, 0, TRUE); + if (linewhite(curwin->w_cursor.lnum)) + did_ai = TRUE; // delete the indent if the line stays empty + } +} + + void +fix_indent(void) +{ + if (p_paste) + return; +# ifdef FEAT_LISP + if (curbuf->b_p_lisp && curbuf->b_p_ai) + fixthisline(get_lisp_indent); +# endif +# if defined(FEAT_LISP) && defined(FEAT_CINDENT) + else +# endif +# ifdef FEAT_CINDENT + if (cindent_on()) + do_c_expr_indent(); +# endif +} +#endif + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * "indent()" function + */ + void +f_indent(typval_T *argvars, typval_T *rettv) +{ + linenr_T lnum; + + lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + rettv->vval.v_number = get_indent_lnum(lnum); + else + rettv->vval.v_number = -1; +} + +/* + * "lispindent(lnum)" function + */ + void +f_lispindent(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_LISP + pos_T pos; + linenr_T lnum; + + pos = curwin->w_cursor; + lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + { + curwin->w_cursor.lnum = lnum; + rettv->vval.v_number = get_lisp_indent(); + curwin->w_cursor = pos; + } + else +#endif + rettv->vval.v_number = -1; +} +#endif diff --git a/src/misc1.c b/src/misc1.c index d43de06c16..d359ffd978 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -24,542 +24,6 @@ // All user names (for ~user completion as done by shell). static garray_T ga_users; -/* - * Count the size (in window cells) of the indent in the current line. - */ - int -get_indent(void) -{ -#ifdef FEAT_VARTABS - return get_indent_str_vtab(ml_get_curline(), (int)curbuf->b_p_ts, - curbuf->b_p_vts_array, FALSE); -#else - return get_indent_str(ml_get_curline(), (int)curbuf->b_p_ts, FALSE); -#endif -} - -/* - * Count the size (in window cells) of the indent in line "lnum". - */ - int -get_indent_lnum(linenr_T lnum) -{ -#ifdef FEAT_VARTABS - return get_indent_str_vtab(ml_get(lnum), (int)curbuf->b_p_ts, - curbuf->b_p_vts_array, FALSE); -#else - return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts, FALSE); -#endif -} - -#if defined(FEAT_FOLDING) || defined(PROTO) -/* - * Count the size (in window cells) of the indent in line "lnum" of buffer - * "buf". - */ - int -get_indent_buf(buf_T *buf, linenr_T lnum) -{ -#ifdef FEAT_VARTABS - return get_indent_str_vtab(ml_get_buf(buf, lnum, FALSE), - (int)curbuf->b_p_ts, buf->b_p_vts_array, FALSE); -#else - return get_indent_str(ml_get_buf(buf, lnum, FALSE), (int)buf->b_p_ts, FALSE); -#endif -} -#endif - -/* - * count the size (in window cells) of the indent in line "ptr", with - * 'tabstop' at "ts" - */ - int -get_indent_str( - char_u *ptr, - int ts, - int list) /* if TRUE, count only screen size for tabs */ -{ - int count = 0; - - for ( ; *ptr; ++ptr) - { - if (*ptr == TAB) - { - if (!list || lcs_tab1) /* count a tab for what it is worth */ - count += ts - (count % ts); - else - /* In list mode, when tab is not set, count screen char width - * for Tab, displays: ^I */ - count += ptr2cells(ptr); - } - else if (*ptr == ' ') - ++count; /* count a space for one */ - else - break; - } - return count; -} - -#ifdef FEAT_VARTABS -/* - * Count the size (in window cells) of the indent in line "ptr", using - * variable tabstops. - * if "list" is TRUE, count only screen size for tabs. - */ - int -get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list) -{ - int count = 0; - - for ( ; *ptr; ++ptr) - { - if (*ptr == TAB) /* count a tab for what it is worth */ - { - if (!list || lcs_tab1) - count += tabstop_padding(count, ts, vts); - else - /* In list mode, when tab is not set, count screen char width - * for Tab, displays: ^I */ - count += ptr2cells(ptr); - } - else if (*ptr == ' ') - ++count; /* count a space for one */ - else - break; - } - return count; -} -#endif - -/* - * Set the indent of the current line. - * Leaves the cursor on the first non-blank in the line. - * Caller must take care of undo. - * "flags": - * SIN_CHANGED: call changed_bytes() if the line was changed. - * SIN_INSERT: insert the indent in front of the line. - * SIN_UNDO: save line for undo before changing it. - * Returns TRUE if the line was changed. - */ - int -set_indent( - int size, /* measured in spaces */ - int flags) -{ - char_u *p; - char_u *newline; - char_u *oldline; - char_u *s; - int todo; - int ind_len; /* measured in characters */ - int line_len; - int doit = FALSE; - int ind_done = 0; /* measured in spaces */ -#ifdef FEAT_VARTABS - int ind_col = 0; -#endif - int tab_pad; - int retval = FALSE; - int orig_char_len = -1; /* number of initial whitespace chars when - 'et' and 'pi' are both set */ - - /* - * First check if there is anything to do and compute the number of - * characters needed for the indent. - */ - todo = size; - ind_len = 0; - p = oldline = ml_get_curline(); - - /* Calculate the buffer size for the new indent, and check to see if it - * isn't already set */ - - /* if 'expandtab' isn't set: use TABs; if both 'expandtab' and - * 'preserveindent' are set count the number of characters at the - * beginning of the line to be copied */ - if (!curbuf->b_p_et || (!(flags & SIN_INSERT) && curbuf->b_p_pi)) - { - /* If 'preserveindent' is set then reuse as much as possible of - * the existing indent structure for the new indent */ - if (!(flags & SIN_INSERT) && curbuf->b_p_pi) - { - ind_done = 0; - - /* count as many characters as we can use */ - while (todo > 0 && VIM_ISWHITE(*p)) - { - if (*p == TAB) - { -#ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); -#else - tab_pad = (int)curbuf->b_p_ts - - (ind_done % (int)curbuf->b_p_ts); -#endif - /* stop if this tab will overshoot the target */ - if (todo < tab_pad) - break; - todo -= tab_pad; - ++ind_len; - ind_done += tab_pad; - } - else - { - --todo; - ++ind_len; - ++ind_done; - } - ++p; - } - -#ifdef FEAT_VARTABS - /* These diverge from this point. */ - ind_col = ind_done; -#endif - /* Set initial number of whitespace chars to copy if we are - * preserving indent but expandtab is set */ - if (curbuf->b_p_et) - orig_char_len = ind_len; - - /* Fill to next tabstop with a tab, if possible */ -#ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); -#else - tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); -#endif - if (todo >= tab_pad && orig_char_len == -1) - { - doit = TRUE; - todo -= tab_pad; - ++ind_len; - /* ind_done += tab_pad; */ -#ifdef FEAT_VARTABS - ind_col += tab_pad; -#endif - } - } - - /* count tabs required for indent */ -#ifdef FEAT_VARTABS - for (;;) - { - tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, - curbuf->b_p_vts_array); - if (todo < tab_pad) - break; - if (*p != TAB) - doit = TRUE; - else - ++p; - todo -= tab_pad; - ++ind_len; - ind_col += tab_pad; - } -#else - while (todo >= (int)curbuf->b_p_ts) - { - if (*p != TAB) - doit = TRUE; - else - ++p; - todo -= (int)curbuf->b_p_ts; - ++ind_len; - /* ind_done += (int)curbuf->b_p_ts; */ - } -#endif - } - /* count spaces required for indent */ - while (todo > 0) - { - if (*p != ' ') - doit = TRUE; - else - ++p; - --todo; - ++ind_len; - /* ++ind_done; */ - } - - /* Return if the indent is OK already. */ - if (!doit && !VIM_ISWHITE(*p) && !(flags & SIN_INSERT)) - return FALSE; - - /* Allocate memory for the new line. */ - if (flags & SIN_INSERT) - p = oldline; - else - p = skipwhite(p); - line_len = (int)STRLEN(p) + 1; - - /* If 'preserveindent' and 'expandtab' are both set keep the original - * characters and allocate accordingly. We will fill the rest with spaces - * after the if (!curbuf->b_p_et) below. */ - if (orig_char_len != -1) - { - newline = alloc(orig_char_len + size - ind_done + line_len); - if (newline == NULL) - return FALSE; - todo = size - ind_done; - ind_len = orig_char_len + todo; /* Set total length of indent in - * characters, which may have been - * undercounted until now */ - p = oldline; - s = newline; - while (orig_char_len > 0) - { - *s++ = *p++; - orig_char_len--; - } - - /* Skip over any additional white space (useful when newindent is less - * than old) */ - while (VIM_ISWHITE(*p)) - ++p; - - } - else - { - todo = size; - newline = alloc(ind_len + line_len); - if (newline == NULL) - return FALSE; - s = newline; - } - - /* Put the characters in the new line. */ - /* if 'expandtab' isn't set: use TABs */ - if (!curbuf->b_p_et) - { - /* If 'preserveindent' is set then reuse as much as possible of - * the existing indent structure for the new indent */ - if (!(flags & SIN_INSERT) && curbuf->b_p_pi) - { - p = oldline; - ind_done = 0; - - while (todo > 0 && VIM_ISWHITE(*p)) - { - if (*p == TAB) - { -#ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); -#else - tab_pad = (int)curbuf->b_p_ts - - (ind_done % (int)curbuf->b_p_ts); -#endif - /* stop if this tab will overshoot the target */ - if (todo < tab_pad) - break; - todo -= tab_pad; - ind_done += tab_pad; - } - else - { - --todo; - ++ind_done; - } - *s++ = *p++; - } - - /* Fill to next tabstop with a tab, if possible */ -#ifdef FEAT_VARTABS - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); -#else - tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts); -#endif - if (todo >= tab_pad) - { - *s++ = TAB; - todo -= tab_pad; -#ifdef FEAT_VARTABS - ind_done += tab_pad; -#endif - } - - p = skipwhite(p); - } - -#ifdef FEAT_VARTABS - for (;;) - { - tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, - curbuf->b_p_vts_array); - if (todo < tab_pad) - break; - *s++ = TAB; - todo -= tab_pad; - ind_done += tab_pad; - } -#else - while (todo >= (int)curbuf->b_p_ts) - { - *s++ = TAB; - todo -= (int)curbuf->b_p_ts; - } -#endif - } - while (todo > 0) - { - *s++ = ' '; - --todo; - } - mch_memmove(s, p, (size_t)line_len); - - // Replace the line (unless undo fails). - if (!(flags & SIN_UNDO) || u_savesub(curwin->w_cursor.lnum) == OK) - { - ml_replace(curwin->w_cursor.lnum, newline, FALSE); - if (flags & SIN_CHANGED) - changed_bytes(curwin->w_cursor.lnum, 0); - - // Correct saved cursor position if it is in this line. - if (saved_cursor.lnum == curwin->w_cursor.lnum) - { - if (saved_cursor.col >= (colnr_T)(p - oldline)) - // cursor was after the indent, adjust for the number of - // bytes added/removed - saved_cursor.col += ind_len - (colnr_T)(p - oldline); - else if (saved_cursor.col >= (colnr_T)(s - newline)) - // cursor was in the indent, and is now after it, put it back - // at the start of the indent (replacing spaces with TAB) - saved_cursor.col = (colnr_T)(s - newline); - } -#ifdef FEAT_TEXT_PROP - { - int added = ind_len - (colnr_T)(p - oldline); - - // When increasing indent this behaves like spaces were inserted at - // the old indent, when decreasing indent it behaves like spaces - // were deleted at the new indent. - adjust_prop_columns(curwin->w_cursor.lnum, - (colnr_T)(added > 0 ? (p - oldline) : ind_len), added, 0); - } -#endif - retval = TRUE; - } - else - vim_free(newline); - - curwin->w_cursor.col = ind_len; - return retval; -} - -/* - * Return the indent of the current line after a number. Return -1 if no - * number was found. Used for 'n' in 'formatoptions': numbered list. - * Since a pattern is used it can actually handle more than numbers. - */ - int -get_number_indent(linenr_T lnum) -{ - colnr_T col; - pos_T pos; - - regmatch_T regmatch; - int lead_len = 0; /* length of comment leader */ - - if (lnum > curbuf->b_ml.ml_line_count) - return -1; - pos.lnum = 0; - - /* In format_lines() (i.e. not insert mode), fo+=q is needed too... */ - if ((State & INSERT) || has_format_option(FO_Q_COMS)) - lead_len = get_leader_len(ml_get(lnum), NULL, FALSE, TRUE); - - regmatch.regprog = vim_regcomp(curbuf->b_p_flp, RE_MAGIC); - if (regmatch.regprog != NULL) - { - regmatch.rm_ic = FALSE; - - /* vim_regexec() expects a pointer to a line. This lets us - * start matching for the flp beyond any comment leader... */ - if (vim_regexec(®match, ml_get(lnum) + lead_len, (colnr_T)0)) - { - pos.lnum = lnum; - pos.col = (colnr_T)(*regmatch.endp - ml_get(lnum)); - pos.coladd = 0; - } - vim_regfree(regmatch.regprog); - } - - if (pos.lnum == 0 || *ml_get_pos(&pos) == NUL) - return -1; - getvcol(curwin, &pos, &col, NULL, NULL); - return (int)col; -} - -#if defined(FEAT_LINEBREAK) || defined(PROTO) -/* - * Return appropriate space number for breakindent, taking influencing - * parameters into account. Window must be specified, since it is not - * necessarily always the current one. - */ - int -get_breakindent_win( - win_T *wp, - char_u *line) /* start of the line */ -{ - static int prev_indent = 0; /* cached indent value */ - static long prev_ts = 0L; /* cached tabstop value */ - static char_u *prev_line = NULL; /* cached pointer to line */ - static varnumber_T prev_tick = 0; /* changedtick of cached value */ -#ifdef FEAT_VARTABS - static int *prev_vts = NULL; /* cached vartabs values */ -#endif - int bri = 0; - /* window width minus window margin space, i.e. what rests for text */ - const int eff_wwidth = wp->w_width - - ((wp->w_p_nu || wp->w_p_rnu) - && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) - ? number_width(wp) + 1 : 0); - - /* used cached indent, unless pointer or 'tabstop' changed */ - if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts - || prev_tick != CHANGEDTICK(wp->w_buffer) -#ifdef FEAT_VARTABS - || prev_vts != wp->w_buffer->b_p_vts_array -#endif - ) - { - prev_line = line; - prev_ts = wp->w_buffer->b_p_ts; - prev_tick = CHANGEDTICK(wp->w_buffer); -#ifdef FEAT_VARTABS - prev_vts = wp->w_buffer->b_p_vts_array; - prev_indent = get_indent_str_vtab(line, - (int)wp->w_buffer->b_p_ts, - wp->w_buffer->b_p_vts_array, wp->w_p_list); -#else - prev_indent = get_indent_str(line, - (int)wp->w_buffer->b_p_ts, wp->w_p_list); -#endif - } - bri = prev_indent + wp->w_p_brishift; - - /* indent minus the length of the showbreak string */ - if (wp->w_p_brisbr) - bri -= vim_strsize(p_sbr); - - /* Add offset for number column, if 'n' is in 'cpoptions' */ - bri += win_col_off2(wp); - - /* never indent past left window margin */ - if (bri < 0) - bri = 0; - /* always leave at least bri_min characters on the left, - * if text width is sufficient */ - else if (bri > eff_wwidth - wp->w_p_brimin) - bri = (eff_wwidth - wp->w_p_brimin < 0) - ? 0 : eff_wwidth - wp->w_p_brimin; - - return bri; -} -#endif - /* * get_leader_len() returns the length in bytes of the prefix of the given * string which introduces a comment. If this string is not a comment then @@ -1075,26 +539,6 @@ pchar_cursor(int c) + curwin->w_cursor.col) = c; } -/* - * When extra == 0: Return TRUE if the cursor is before or on the first - * non-blank in the line. - * When extra == 1: Return TRUE if the cursor is before the first non-blank in - * the line. - */ - int -inindent(int extra) -{ - char_u *ptr; - colnr_T col; - - for (col = 0, ptr = ml_get_curline(); VIM_ISWHITE(*ptr); ++col) - ++ptr; - if (col >= curwin->w_cursor.col + extra) - return TRUE; - else - return FALSE; -} - /* * Skip to next part of an option argument: Skip space and comma. */ diff --git a/src/ops.c b/src/ops.c index aa6858492a..a008a9d48b 100644 --- a/src/ops.c +++ b/src/ops.c @@ -590,90 +590,6 @@ block_insert( State = oldstate; } -#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO) -/* - * op_reindent - handle reindenting a block of lines. - */ - static void -op_reindent(oparg_T *oap, int (*how)(void)) -{ - long i; - char_u *l; - int amount; - linenr_T first_changed = 0; - linenr_T last_changed = 0; - linenr_T start_lnum = curwin->w_cursor.lnum; - - /* Don't even try when 'modifiable' is off. */ - if (!curbuf->b_p_ma) - { - emsg(_(e_modifiable)); - return; - } - - for (i = oap->line_count; --i >= 0 && !got_int; ) - { - /* it's a slow thing to do, so give feedback so there's no worry that - * the computer's just hung. */ - - if (i > 1 - && (i % 50 == 0 || i == oap->line_count - 1) - && oap->line_count > p_report) - smsg(_("%ld lines to indent... "), i); - - /* - * Be vi-compatible: For lisp indenting the first line is not - * indented, unless there is only one line. - */ -#ifdef FEAT_LISP - if (i != oap->line_count - 1 || oap->line_count == 1 - || how != get_lisp_indent) -#endif - { - l = skipwhite(ml_get_curline()); - if (*l == NUL) /* empty or blank line */ - amount = 0; - else - amount = how(); /* get the indent for this line */ - - if (amount >= 0 && set_indent(amount, SIN_UNDO)) - { - /* did change the indent, call changed_lines() later */ - if (first_changed == 0) - first_changed = curwin->w_cursor.lnum; - last_changed = curwin->w_cursor.lnum; - } - } - ++curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; /* make sure it's valid */ - } - - /* put cursor on first non-blank of indented line */ - curwin->w_cursor.lnum = start_lnum; - beginline(BL_SOL | BL_FIX); - - /* Mark changed lines so that they will be redrawn. When Visual - * highlighting was present, need to continue until the last line. When - * there is no change still need to remove the Visual highlighting. */ - if (last_changed != 0) - changed_lines(first_changed, 0, - oap->is_VIsual ? start_lnum + oap->line_count : - last_changed + 1, 0L); - else if (oap->is_VIsual) - redraw_curbuf_later(INVERTED); - - if (oap->line_count > p_report) - { - i = oap->line_count - (i + 1); - smsg(NGETTEXT("%ld line indented ", - "%ld lines indented ", i), i); - } - /* set '[ and '] marks */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; -} -#endif /* defined(FEAT_LISP) || defined(FEAT_CINDENT) */ - /* * Stuff a string into the typeahead buffer, such that edit() will insert it * literally ("literally" TRUE) or interpret is as typed characters. @@ -1917,29 +1833,6 @@ adjust_cursor_eol(void) } } -#if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT) || defined(PROTO) -/* - * Return TRUE if lines starting with '#' should be left aligned. - */ - int -preprocs_left(void) -{ - return -# ifdef FEAT_SMARTINDENT -# ifdef FEAT_CINDENT - (curbuf->b_p_si && !curbuf->b_p_cin) || -# else - curbuf->b_p_si -# endif -# endif -# ifdef FEAT_CINDENT - (curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE) - && curbuf->b_ind_hash_comment == 0) -# endif - ; -} -#endif - /* * If "process" is TRUE and the line begins with a comment leader (possibly * after some white space), return a pointer to the text after it. Put a boolean diff --git a/src/proto.h b/src/proto.h index 625e8aa472..ad12159e51 100644 --- a/src/proto.h +++ b/src/proto.h @@ -67,6 +67,7 @@ extern int _stricoll(char *a, char *b); # include "bufwrite.pro" # include "change.pro" # include "charset.pro" +# include "cindent.pro" # include "cmdexpand.pro" # include "cmdhist.pro" # include "if_cscope.pro" diff --git a/src/proto/cindent.pro b/src/proto/cindent.pro new file mode 100644 index 0000000000..7de2ab3e1f --- /dev/null +++ b/src/proto/cindent.pro @@ -0,0 +1,10 @@ +/* cindent.c */ +int cin_is_cinword(char_u *line); +pos_T *find_start_comment(int ind_maxcomment); +int cindent_on(void); +void parse_cino(buf_T *buf); +int get_c_indent(void); +int in_cinkeys(int keytyped, int when, int line_is_empty); +void do_c_expr_indent(void); +void f_cindent(typval_T *argvars, typval_T *rettv); +/* vim: set ft=c : */ diff --git a/src/proto/edit.pro b/src/proto/edit.pro index f1ab20500f..bc69b41e83 100644 --- a/src/proto/edit.pro +++ b/src/proto/edit.pro @@ -8,7 +8,6 @@ int prompt_curpos_editable(void); void edit_unputchar(void); void display_dollar(colnr_T col); void undisplay_dollar(void); -void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes); void truncate_spaces(char_u *line); void backspace_until_column(int col); int get_literal(void); @@ -30,6 +29,7 @@ char_u *get_last_insert(void); char_u *get_last_insert_save(void); void replace_push(int c); int replace_push_mb(char_u *p); +void replace_join(int off); int hkmap(int c); int bracketed_paste(paste_mode_T mode, int drop, garray_T *gap); void ins_scroll(void); diff --git a/src/proto/ex_cmds.pro b/src/proto/ex_cmds.pro index 298de8fb97..a4d60ba166 100644 --- a/src/proto/ex_cmds.pro +++ b/src/proto/ex_cmds.pro @@ -2,7 +2,6 @@ void do_ascii(exarg_T *eap); void ex_align(exarg_T *eap); void ex_sort(exarg_T *eap); -void ex_retab(exarg_T *eap); int do_move(linenr_T line1, linenr_T line2, linenr_T dest); void ex_copy(linenr_T line1, linenr_T line2, linenr_T n); void free_prev_shellcmd(void); diff --git a/src/proto/indent.pro b/src/proto/indent.pro index b9c071c958..2aed373a61 100644 --- a/src/proto/indent.pro +++ b/src/proto/indent.pro @@ -1,21 +1,9 @@ /* indent.c */ -int cin_is_cinword(char_u *line); -pos_T *find_start_comment(int ind_maxcomment); -int cindent_on(void); -void parse_cino(buf_T *buf); -int get_c_indent(void); -int get_expr_indent(void); -int in_cinkeys(int keytyped, int when, int line_is_empty); -int get_lisp_indent(void); -void do_c_expr_indent(void); -void fixthisline(int (*get_the_indent)(void)); -void fix_indent(void); int tabstop_set(char_u *var, int **array); int tabstop_padding(colnr_T col, int ts_arg, int *vts); int tabstop_at(colnr_T col, int ts, int *vts); colnr_T tabstop_start(colnr_T col, int ts, int *vts); void tabstop_fromto(colnr_T start_col, colnr_T end_col, int ts_arg, int *vts, int *ntabs, int *nspcs); -int tabstop_eq(int *ts1, int *ts2); int *tabstop_copy(int *oldts); int tabstop_count(int *ts); int tabstop_first(int *ts); @@ -23,4 +11,25 @@ long get_sw_value(buf_T *buf); long get_sw_value_indent(buf_T *buf); long get_sw_value_col(buf_T *buf, colnr_T col); long get_sts_value(void); +int get_indent(void); +int get_indent_lnum(linenr_T lnum); +int get_indent_buf(buf_T *buf, linenr_T lnum); +int get_indent_str(char_u *ptr, int ts, int list); +int get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list); +int set_indent(int size, int flags); +int get_number_indent(linenr_T lnum); +int get_breakindent_win(win_T *wp, char_u *line); +int inindent(int extra); +void op_reindent(oparg_T *oap, int (*how)(void)); +int preprocs_left(void); +void ins_try_si(int c); +void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes); +int copy_indent(int size, char_u *src); +void ex_retab(exarg_T *eap); +int get_expr_indent(void); +int get_lisp_indent(void); +void fixthisline(int (*get_the_indent)(void)); +void fix_indent(void); +void f_indent(typval_T *argvars, typval_T *rettv); +void f_lispindent(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro index 8d60f665ae..1a06d7ed7f 100644 --- a/src/proto/misc1.pro +++ b/src/proto/misc1.pro @@ -1,12 +1,4 @@ /* misc1.c */ -int get_indent(void); -int get_indent_lnum(linenr_T lnum); -int get_indent_buf(buf_T *buf, linenr_T lnum); -int get_indent_str(char_u *ptr, int ts, int list); -int get_indent_str_vtab(char_u *ptr, int ts, int *vts, int list); -int set_indent(int size, int flags); -int get_number_indent(linenr_T lnum); -int get_breakindent_win(win_T *wp, char_u *line); int get_leader_len(char_u *line, char_u **flags, int backward, int include_space); int get_last_leader_offset(char_u *line, char_u **flags); int plines(linenr_T lnum); @@ -19,7 +11,6 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last); int gchar_pos(pos_T *pos); int gchar_cursor(void); void pchar_cursor(int c); -int inindent(int extra); char_u *skip_to_option_part(char_u *p); void check_status(buf_T *buf); int ask_yesno(char_u *str, int direct); diff --git a/src/proto/ops.pro b/src/proto/ops.pro index f4e7de7a26..a958cf3282 100644 --- a/src/proto/ops.pro +++ b/src/proto/ops.pro @@ -12,7 +12,6 @@ int swapchar(int op_type, pos_T *pos); void op_insert(oparg_T *oap, long count1); int op_change(oparg_T *oap); void adjust_cursor_eol(void); -int preprocs_left(void); char_u *skip_comment(char_u *line, int process, int include_space, int *is_comment); int do_join(long count, int insert_space, int save_undo, int use_formatoptions, int setmark); int fex_format(linenr_T lnum, long count, int c); diff --git a/src/userfunc.c b/src/userfunc.c index a6ac29ece8..a77521290f 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -8,7 +8,7 @@ */ /* - * eval.c: User defined function support + * userfunc.c: User defined function support */ #include "vim.h" diff --git a/src/version.c b/src/version.c index 11419140f0..06454efc70 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2127, /**/ 2126, /**/ From 93268054428fe3a6bbe3f89d2def2fec4eabcf5f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 10 Oct 2019 13:22:54 +0200 Subject: [PATCH 26/67] patch 8.1.2128: renamed libvterm sources makes merging difficult Problem: Renamed libvterm sources makes merging difficult. Solution: Rename back to the original name and only rename the .o files. Also clean the libvterm build artifacts. (James McCoy, closes #5027) --- src/Make_cyg_ming.mak | 20 ++++----- src/Make_mvc.mak | 49 ++++++++++++--------- src/Makefile | 44 +++++++++--------- src/auto/configure | 4 +- src/configure.ac | 4 +- src/libvterm/src/{termmouse.c => mouse.c} | 0 src/libvterm/src/{termscreen.c => screen.c} | 0 src/version.c | 2 + 8 files changed, 67 insertions(+), 56 deletions(-) rename src/libvterm/src/{termmouse.c => mouse.c} (100%) rename src/libvterm/src/{termscreen.c => screen.c} (100%) diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index f04d11b188..24c1338504 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -869,15 +869,15 @@ endif ifeq ($(TERMINAL),yes) OBJ += $(OUTDIR)/terminal.o \ - $(OUTDIR)/encoding.o \ - $(OUTDIR)/keyboard.o \ - $(OUTDIR)/termmouse.o \ - $(OUTDIR)/parser.o \ - $(OUTDIR)/pen.o \ - $(OUTDIR)/termscreen.o \ - $(OUTDIR)/state.o \ - $(OUTDIR)/unicode.o \ - $(OUTDIR)/vterm.o + $(OUTDIR)/vterm_encoding.o \ + $(OUTDIR)/vterm_keyboard.o \ + $(OUTDIR)/vterm_mouse.o \ + $(OUTDIR)/vterm_parser.o \ + $(OUTDIR)/vterm_pen.o \ + $(OUTDIR)/vterm_screen.o \ + $(OUTDIR)/vterm_state.o \ + $(OUTDIR)/vterm_unicode.o \ + $(OUTDIR)/vterm_vterm.o endif ifeq ($(SOUND),yes) @@ -1206,7 +1206,7 @@ CCCTERM = $(CC) -c $(CFLAGS) -Ilibvterm/include -DINLINE="" \ -DWCWIDTH_FUNCTION=utf_uint2cells \ -DGET_SPECIAL_PTY_TYPE_FUNCTION=get_special_pty_type -$(OUTDIR)/%.o : libvterm/src/%.c $(TERM_DEPS) +$(OUTDIR)/vterm_%.o : libvterm/src/%.c $(TERM_DEPS) $(CCCTERM) $< -o $@ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 4fb045aff7..f867b3abd1 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -363,15 +363,15 @@ TERMINAL = no !if "$(TERMINAL)" == "yes" TERM_OBJ = \ $(OBJDIR)/terminal.obj \ - $(OBJDIR)/encoding.obj \ - $(OBJDIR)/keyboard.obj \ - $(OBJDIR)/termmouse.obj \ - $(OBJDIR)/parser.obj \ - $(OBJDIR)/pen.obj \ - $(OBJDIR)/termscreen.obj \ - $(OBJDIR)/state.obj \ - $(OBJDIR)/unicode.obj \ - $(OBJDIR)/vterm.obj + $(OBJDIR)/vterm_encoding.obj \ + $(OBJDIR)/vterm_keyboard.obj \ + $(OBJDIR)/vterm_mouse.obj \ + $(OBJDIR)/vterm_parser.obj \ + $(OBJDIR)/vterm_pen.obj \ + $(OBJDIR)/vterm_screen.obj \ + $(OBJDIR)/vterm_state.obj \ + $(OBJDIR)/vterm_unicode.obj \ + $(OBJDIR)/vterm_vterm.obj TERM_DEFS = -DFEAT_TERMINAL TERM_DEPS = \ libvterm/include/vterm.h \ @@ -1743,27 +1743,32 @@ CCCTERM = $(CC) $(CFLAGS) -Ilibvterm/include -DINLINE="" \ -DGET_SPECIAL_PTY_TYPE_FUNCTION=get_special_pty_type \ -D_CRT_SECURE_NO_WARNINGS -# Create a default rule for libvterm. -{libvterm/src/}.c{$(OUTDIR)/}.obj:: - $(CCCTERM) -Fo$(OUTDIR)/ $< +$(OUTDIR)/vterm_encoding.obj: $(OUTDIR) libvterm/src/encoding.c $(TERM_DEPS) + $(CCCTERM) /Fo$@ $< -$(OUTDIR)/encoding.obj: $(OUTDIR) libvterm/src/encoding.c $(TERM_DEPS) +$(OUTDIR)/vterm_keyboard.obj: $(OUTDIR) libvterm/src/keyboard.c $(TERM_DEPS) + $(CCCTERM) /Fo$@ $< -$(OUTDIR)/keyboard.obj: $(OUTDIR) libvterm/src/keyboard.c $(TERM_DEPS) +$(OUTDIR)/vterm_mouse.obj: $(OUTDIR) libvterm/src/mouse.c $(TERM_DEPS) + $(CCCTERM) /Fo$@ $< -$(OUTDIR)/termmouse.obj: $(OUTDIR) libvterm/src/termmouse.c $(TERM_DEPS) +$(OUTDIR)/vterm_parser.obj: $(OUTDIR) libvterm/src/parser.c $(TERM_DEPS) + $(CCCTERM) /Fo$@ $< -$(OUTDIR)/parser.obj: $(OUTDIR) libvterm/src/parser.c $(TERM_DEPS) +$(OUTDIR)/vterm_pen.obj: $(OUTDIR) libvterm/src/pen.c $(TERM_DEPS) + $(CCCTERM) /Fo$@ $< -$(OUTDIR)/pen.obj: $(OUTDIR) libvterm/src/pen.c $(TERM_DEPS) +$(OUTDIR)/vterm_screen.obj: $(OUTDIR) libvterm/src/screen.c $(TERM_DEPS) + $(CCCTERM) /Fo$@ $< -$(OUTDIR)/termscreen.obj: $(OUTDIR) libvterm/src/termscreen.c $(TERM_DEPS) +$(OUTDIR)/vterm_state.obj: $(OUTDIR) libvterm/src/state.c $(TERM_DEPS) + $(CCCTERM) /Fo$@ $< -$(OUTDIR)/state.obj: $(OUTDIR) libvterm/src/state.c $(TERM_DEPS) +$(OUTDIR)/vterm_unicode.obj: $(OUTDIR) libvterm/src/unicode.c $(TERM_DEPS) + $(CCCTERM) /Fo$@ $< -$(OUTDIR)/unicode.obj: $(OUTDIR) libvterm/src/unicode.c $(TERM_DEPS) - -$(OUTDIR)/vterm.obj: $(OUTDIR) libvterm/src/vterm.c $(TERM_DEPS) +$(OUTDIR)/vterm_vterm.obj: $(OUTDIR) libvterm/src/vterm.c $(TERM_DEPS) + $(CCCTERM) /Fo$@ $< # $CFLAGS may contain backslashes and double quotes, escape them both. diff --git a/src/Makefile b/src/Makefile index f169548556..c08446339c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2860,6 +2860,7 @@ clean celan: testclean -rm -f runtime pixmaps -rm -rf $(APPDIR) -rm -rf mzscheme_base.c + -rm -rf libvterm/.libs libterm/t/.libs libvterm/src/*.o libvterm/src/*.lo libvterm/t/*.o libvterm/t/*.lo libvterm/t/harness libvterm/libvterm.la if test -d $(PODIR); then \ cd $(PODIR); $(MAKE) prefix=$(DESTDIR)$(prefix) clean; \ fi @@ -3453,36 +3454,39 @@ objects/channel.o: channel.c Makefile: @echo The name of the makefile MUST be "Makefile" (with capital M)!!!! +# Build rules for libvterm. Putting them here allows for adding compilation +# options specific for Vim. Since the .o files go into objects/ we do need to +# prefix vterm_ to avoid name clashes. CCCTERM = $(CCC_NF) $(VTERM_CFLAGS) $(ALL_CFLAGS) -DINLINE="" \ -DVSNPRINTF=vim_vsnprintf \ -DIS_COMBINING_FUNCTION=utf_iscomposing_uint \ -DWCWIDTH_FUNCTION=utf_uint2cells -objects/encoding.o: libvterm/src/encoding.c $(TERM_DEPS) +objects/vterm_encoding.o: libvterm/src/encoding.c $(TERM_DEPS) $(CCCTERM) -o $@ libvterm/src/encoding.c -objects/keyboard.o: libvterm/src/keyboard.c $(TERM_DEPS) +objects/vterm_keyboard.o: libvterm/src/keyboard.c $(TERM_DEPS) $(CCCTERM) -o $@ libvterm/src/keyboard.c -objects/termmouse.o: libvterm/src/termmouse.c $(TERM_DEPS) - $(CCCTERM) -o $@ libvterm/src/termmouse.c +objects/vterm_mouse.o: libvterm/src/mouse.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/mouse.c -objects/parser.o: libvterm/src/parser.c $(TERM_DEPS) +objects/vterm_parser.o: libvterm/src/parser.c $(TERM_DEPS) $(CCCTERM) -o $@ libvterm/src/parser.c -objects/pen.o: libvterm/src/pen.c $(TERM_DEPS) +objects/vterm_pen.o: libvterm/src/pen.c $(TERM_DEPS) $(CCCTERM) -o $@ libvterm/src/pen.c -objects/termscreen.o: libvterm/src/termscreen.c $(TERM_DEPS) - $(CCCTERM) -o $@ libvterm/src/termscreen.c +objects/vterm_screen.o: libvterm/src/screen.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/screen.c -objects/state.o: libvterm/src/state.c $(TERM_DEPS) +objects/vterm_state.o: libvterm/src/state.c $(TERM_DEPS) $(CCCTERM) -o $@ libvterm/src/state.c -objects/unicode.o: libvterm/src/unicode.c $(TERM_DEPS) +objects/vterm_unicode.o: libvterm/src/unicode.c $(TERM_DEPS) $(CCCTERM) -o $@ libvterm/src/unicode.c -objects/vterm.o: libvterm/src/vterm.c $(TERM_DEPS) +objects/vterm_vterm.o: libvterm/src/vterm.c $(TERM_DEPS) $(CCCTERM) -o $@ libvterm/src/vterm.c CCCDIFF = $(CCC_NF) $(ALL_CFLAGS) @@ -4111,27 +4115,27 @@ objects/channel.o: channel.c vim.h protodef.h auto/config.h feature.h os_unix.h proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h objects/gui_gtk_gresources.o: auto/gui_gtk_gresources.c -objects/encoding.o: libvterm/src/encoding.c libvterm/src/vterm_internal.h \ +objects/vterm_encoding.o: libvterm/src/encoding.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ libvterm/src/encoding/DECdrawing.inc libvterm/src/encoding/uk.inc -objects/keyboard.o: libvterm/src/keyboard.c libvterm/src/vterm_internal.h \ +objects/vterm_keyboard.o: libvterm/src/keyboard.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ libvterm/src/utf8.h -objects/termmouse.o: libvterm/src/termmouse.c libvterm/src/vterm_internal.h \ +objects/vterm_mouse.o: libvterm/src/mouse.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ libvterm/src/utf8.h -objects/parser.o: libvterm/src/parser.c libvterm/src/vterm_internal.h \ +objects/vterm_parser.o: libvterm/src/parser.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h -objects/pen.o: libvterm/src/pen.c libvterm/src/vterm_internal.h \ +objects/vterm_pen.o: libvterm/src/pen.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h -objects/state.o: libvterm/src/state.c libvterm/src/vterm_internal.h \ +objects/vterm_state.o: libvterm/src/state.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h -objects/termscreen.o: libvterm/src/termscreen.c libvterm/src/vterm_internal.h \ +objects/vterm_screen.o: libvterm/src/screen.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ libvterm/src/rect.h libvterm/src/utf8.h -objects/unicode.o: libvterm/src/unicode.c libvterm/src/vterm_internal.h \ +objects/vterm_unicode.o: libvterm/src/unicode.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h -objects/vterm.o: libvterm/src/vterm.c libvterm/src/vterm_internal.h \ +objects/vterm_vterm.o: libvterm/src/vterm.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ libvterm/src/utf8.h objects/xdiffi.o: xdiff/xdiffi.c xdiff/xinclude.h auto/config.h \ diff --git a/src/auto/configure b/src/auto/configure index c9b370cc7f..1c00773f23 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -7907,9 +7907,9 @@ fi if test "$enable_terminal" = "yes" -a "$enable_channel" = "yes"; then $as_echo "#define FEAT_TERMINAL 1" >>confdefs.h - TERM_SRC="libvterm/src/encoding.c libvterm/src/keyboard.c libvterm/src/mouse.c libvterm/src/parser.c libvterm/src/pen.c libvterm/src/termscreen.c libvterm/src/state.c libvterm/src/unicode.c libvterm/src/vterm.c" + TERM_SRC="libvterm/src/encoding.c libvterm/src/keyboard.c libvterm/src/mouse.c libvterm/src/parser.c libvterm/src/pen.c libvterm/src/creen.c libvterm/src/state.c libvterm/src/unicode.c libvterm/src/vterm.c" - TERM_OBJ="objects/encoding.o objects/keyboard.o objects/termmouse.o objects/parser.o objects/pen.o objects/termscreen.o objects/state.o objects/unicode.o objects/vterm.o" + TERM_OBJ="objects/vterm_encoding.o objects/vterm_keyboard.o objects/vterm_mouse.o objects/vterm_parser.o objects/vterm_pen.o objects/vterm_screen.o objects/vterm_state.o objects/vterm_unicode.o objects/vterm_vterm.o" fi diff --git a/src/configure.ac b/src/configure.ac index 97753a6552..3ad223b398 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -2104,9 +2104,9 @@ else fi if test "$enable_terminal" = "yes" -a "$enable_channel" = "yes"; then AC_DEFINE(FEAT_TERMINAL) - TERM_SRC="libvterm/src/encoding.c libvterm/src/keyboard.c libvterm/src/mouse.c libvterm/src/parser.c libvterm/src/pen.c libvterm/src/termscreen.c libvterm/src/state.c libvterm/src/unicode.c libvterm/src/vterm.c" + TERM_SRC="libvterm/src/encoding.c libvterm/src/keyboard.c libvterm/src/mouse.c libvterm/src/parser.c libvterm/src/pen.c libvterm/src/creen.c libvterm/src/state.c libvterm/src/unicode.c libvterm/src/vterm.c" AC_SUBST(TERM_SRC) - TERM_OBJ="objects/encoding.o objects/keyboard.o objects/termmouse.o objects/parser.o objects/pen.o objects/termscreen.o objects/state.o objects/unicode.o objects/vterm.o" + TERM_OBJ="objects/vterm_encoding.o objects/vterm_keyboard.o objects/vterm_mouse.o objects/vterm_parser.o objects/vterm_pen.o objects/vterm_screen.o objects/vterm_state.o objects/vterm_unicode.o objects/vterm_vterm.o" AC_SUBST(TERM_OBJ) fi diff --git a/src/libvterm/src/termmouse.c b/src/libvterm/src/mouse.c similarity index 100% rename from src/libvterm/src/termmouse.c rename to src/libvterm/src/mouse.c diff --git a/src/libvterm/src/termscreen.c b/src/libvterm/src/screen.c similarity index 100% rename from src/libvterm/src/termscreen.c rename to src/libvterm/src/screen.c diff --git a/src/version.c b/src/version.c index 06454efc70..7ad0acd936 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2128, /**/ 2127, /**/ From 1ac41a5c1352306942344777d2ba86dccd84ffad Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 10 Oct 2019 13:30:12 +0200 Subject: [PATCH 27/67] patch 8.1.2129: using hard coded executable path in test Problem: Using hard coded executable path in test. Solution: Use v:progpath. Use $VIMRUNTIME instead of "runtime". (James McCoy, closes #5025) --- src/testdir/test49.vim | 4 ++-- src/testdir/test_compiler.vim | 6 +++--- src/testdir/test_spell.vim | 10 +++++----- src/version.c | 2 ++ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/testdir/test49.vim b/src/testdir/test49.vim index bd6051461b..b6f6f6b5dc 100644 --- a/src/testdir/test49.vim +++ b/src/testdir/test49.vim @@ -1,6 +1,6 @@ " Vim script language tests " Author: Servatius Brandt -" Last Change: 2019 May 24 +" Last Change: 2019 Oct 08 "------------------------------------------------------------------------------- " Test environment {{{1 @@ -456,7 +456,7 @@ function ExtraVim(...) " messing up the user's viminfo file. let redirect = a:0 ? \ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : "" - exec "!echo '" . debug_quits . "q' | ../vim -u NONE -N -Xes" . redirect . + exec "!echo '" . debug_quits . "q' | " .. v:progpath .. " -u NONE -N -Xes" . redirect . \ " -c 'debuggreedy|set viminfo+=nviminfo'" . \ " -c 'let ExtraVimBegin = " . extra_begin . "'" . \ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints . diff --git a/src/testdir/test_compiler.vim b/src/testdir/test_compiler.vim index 40d3cdbdae..06522d77ec 100644 --- a/src/testdir/test_compiler.vim +++ b/src/testdir/test_compiler.vim @@ -39,9 +39,9 @@ endfunc func Test_compiler_without_arg() let a=split(execute('compiler')) - call assert_match('^.*runtime/compiler/ant.vim$', a[0]) - call assert_match('^.*runtime/compiler/bcc.vim$', a[1]) - call assert_match('^.*runtime/compiler/xmlwf.vim$', a[-1]) + call assert_match($VIMRUNTIME .. '/compiler/ant.vim$', a[0]) + call assert_match($VIMRUNTIME .. '/compiler/bcc.vim$', a[1]) + call assert_match($VIMRUNTIME .. '/compiler/xmlwf.vim$', a[-1]) endfunc func Test_compiler_completion() diff --git a/src/testdir/test_spell.vim b/src/testdir/test_spell.vim index a44d95534f..523126652d 100644 --- a/src/testdir/test_spell.vim +++ b/src/testdir/test_spell.vim @@ -130,18 +130,18 @@ func Test_spellinfo() new set enc=latin1 spell spelllang=en - call assert_match("^\nfile: .*/runtime/spell/en.latin1.spl\n$", execute('spellinfo')) + call assert_match("^\nfile: " .. $VIMRUNTIME .. "/spell/en.latin1.spl\n$", execute('spellinfo')) set enc=cp1250 spell spelllang=en - call assert_match("^\nfile: .*/runtime/spell/en.ascii.spl\n$", execute('spellinfo')) + call assert_match("^\nfile: " .. $VIMRUNTIME .. "/spell/en.ascii.spl\n$", execute('spellinfo')) set enc=utf-8 spell spelllang=en - call assert_match("^\nfile: .*/runtime/spell/en.utf-8.spl\n$", execute('spellinfo')) + call assert_match("^\nfile: " .. $VIMRUNTIME .. "/spell/en.utf-8.spl\n$", execute('spellinfo')) set enc=latin1 spell spelllang=en_us,en_nz call assert_match("^\n" . - \ "file: .*/runtime/spell/en.latin1.spl\n" . - \ "file: .*/runtime/spell/en.latin1.spl\n$", execute('spellinfo')) + \ "file: " .. $VIMRUNTIME .. "/spell/en.latin1.spl\n" . + \ "file: " .. $VIMRUNTIME .. "/spell/en.latin1.spl\n$", execute('spellinfo')) set spell spelllang= call assert_fails('spellinfo', 'E756:') diff --git a/src/version.c b/src/version.c index 7ad0acd936..f9a30285d4 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2129, /**/ 2128, /**/ From 073e779640d089445c7289393db94d1ceb4ddb17 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 10 Oct 2019 13:39:08 +0200 Subject: [PATCH 28/67] patch 8.1.2130: MSVC build fails Problem: MSVC build fails. Solution: Add the source file name explicitly. --- src/Make_mvc.mak | 18 +++++++++--------- src/version.c | 2 ++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index f867b3abd1..2a0a1a1dae 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -1744,31 +1744,31 @@ CCCTERM = $(CC) $(CFLAGS) -Ilibvterm/include -DINLINE="" \ -D_CRT_SECURE_NO_WARNINGS $(OUTDIR)/vterm_encoding.obj: $(OUTDIR) libvterm/src/encoding.c $(TERM_DEPS) - $(CCCTERM) /Fo$@ $< + $(CCCTERM) /Fo$@ libvterm/src/encoding.c $(OUTDIR)/vterm_keyboard.obj: $(OUTDIR) libvterm/src/keyboard.c $(TERM_DEPS) - $(CCCTERM) /Fo$@ $< + $(CCCTERM) /Fo$@ libvterm/src/keyboard.c $(OUTDIR)/vterm_mouse.obj: $(OUTDIR) libvterm/src/mouse.c $(TERM_DEPS) - $(CCCTERM) /Fo$@ $< + $(CCCTERM) /Fo$@ libvterm/src/mouse.c $(OUTDIR)/vterm_parser.obj: $(OUTDIR) libvterm/src/parser.c $(TERM_DEPS) - $(CCCTERM) /Fo$@ $< + $(CCCTERM) /Fo$@ libvterm/src/parser.c $(OUTDIR)/vterm_pen.obj: $(OUTDIR) libvterm/src/pen.c $(TERM_DEPS) - $(CCCTERM) /Fo$@ $< + $(CCCTERM) /Fo$@ libvterm/src/pen.c $(OUTDIR)/vterm_screen.obj: $(OUTDIR) libvterm/src/screen.c $(TERM_DEPS) - $(CCCTERM) /Fo$@ $< + $(CCCTERM) /Fo$@ libvterm/src/screen.c $(OUTDIR)/vterm_state.obj: $(OUTDIR) libvterm/src/state.c $(TERM_DEPS) - $(CCCTERM) /Fo$@ $< + $(CCCTERM) /Fo$@ libvterm/src/state.c $(OUTDIR)/vterm_unicode.obj: $(OUTDIR) libvterm/src/unicode.c $(TERM_DEPS) - $(CCCTERM) /Fo$@ $< + $(CCCTERM) /Fo$@ libvterm/src/unicode.c $(OUTDIR)/vterm_vterm.obj: $(OUTDIR) libvterm/src/vterm.c $(TERM_DEPS) - $(CCCTERM) /Fo$@ $< + $(CCCTERM) /Fo$@ libvterm/src/vterm.c # $CFLAGS may contain backslashes and double quotes, escape them both. diff --git a/src/version.c b/src/version.c index f9a30285d4..a0646485c2 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2130, /**/ 2129, /**/ From c25e702deec74771e49f6c2df4cda7a1f97d0c1e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 10 Oct 2019 14:08:26 +0200 Subject: [PATCH 29/67] patch 8.1.2131: MSVC tests fail Problem: MSVC tests fail. Solution: Replace backslashes with slashes. --- src/testdir/test_compiler.vim | 9 +++++---- src/testdir/test_spell.vim | 11 ++++++----- src/version.c | 2 ++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/testdir/test_compiler.vim b/src/testdir/test_compiler.vim index 06522d77ec..6bb602717f 100644 --- a/src/testdir/test_compiler.vim +++ b/src/testdir/test_compiler.vim @@ -38,10 +38,11 @@ func Test_compiler() endfunc func Test_compiler_without_arg() - let a=split(execute('compiler')) - call assert_match($VIMRUNTIME .. '/compiler/ant.vim$', a[0]) - call assert_match($VIMRUNTIME .. '/compiler/bcc.vim$', a[1]) - call assert_match($VIMRUNTIME .. '/compiler/xmlwf.vim$', a[-1]) + let runtime = substitute($VIMRUNTIME, '\\', '/', 'g') + let a = split(execute('compiler')) + call assert_match(runtime .. '/compiler/ant.vim$', a[0]) + call assert_match(runtime .. '/compiler/bcc.vim$', a[1]) + call assert_match(runtime .. '/compiler/xmlwf.vim$', a[-1]) endfunc func Test_compiler_completion() diff --git a/src/testdir/test_spell.vim b/src/testdir/test_spell.vim index 523126652d..9cbfaa50e8 100644 --- a/src/testdir/test_spell.vim +++ b/src/testdir/test_spell.vim @@ -128,20 +128,21 @@ endfunc func Test_spellinfo() new + let runtime = substitute($VIMRUNTIME, '\\', '/', 'g') set enc=latin1 spell spelllang=en - call assert_match("^\nfile: " .. $VIMRUNTIME .. "/spell/en.latin1.spl\n$", execute('spellinfo')) + call assert_match("^\nfile: " .. runtime .. "/spell/en.latin1.spl\n$", execute('spellinfo')) set enc=cp1250 spell spelllang=en - call assert_match("^\nfile: " .. $VIMRUNTIME .. "/spell/en.ascii.spl\n$", execute('spellinfo')) + call assert_match("^\nfile: " .. runtime .. "/spell/en.ascii.spl\n$", execute('spellinfo')) set enc=utf-8 spell spelllang=en - call assert_match("^\nfile: " .. $VIMRUNTIME .. "/spell/en.utf-8.spl\n$", execute('spellinfo')) + call assert_match("^\nfile: " .. runtime .. "/spell/en.utf-8.spl\n$", execute('spellinfo')) set enc=latin1 spell spelllang=en_us,en_nz call assert_match("^\n" . - \ "file: " .. $VIMRUNTIME .. "/spell/en.latin1.spl\n" . - \ "file: " .. $VIMRUNTIME .. "/spell/en.latin1.spl\n$", execute('spellinfo')) + \ "file: " .. runtime .. "/spell/en.latin1.spl\n" . + \ "file: " .. runtime.. "/spell/en.latin1.spl\n$", execute('spellinfo')) set spell spelllang= call assert_fails('spellinfo', 'E756:') diff --git a/src/version.c b/src/version.c index a0646485c2..2dc9f9bf1e 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2131, /**/ 2130, /**/ From a129974bc71fcb86e05a29387bcaba9aae2f296a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 10 Oct 2019 16:36:00 +0200 Subject: [PATCH 30/67] patch 8.1.2132: MS-Windows: screen mess when not recognizing insider build Problem: MS-Windows: screen mess when not recognizing insider build. Solution: Always move the cursor to the first column first. (Nobuhiro Takasaki, closes #5036) --- src/os_win32.c | 6 +++--- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/os_win32.c b/src/os_win32.c index ce035570db..9badbed41f 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -5868,9 +5868,9 @@ gotoxy( else { // Move the cursor to the left edge of the screen to prevent screen - // destruction. Insider build bug. - if (conpty_type == 3) - vtp_printf("\033[%d;%dH", g_coord.Y + 1, 1); + // destruction. Insider build bug. Always enabled because it's cheap + // and avoids mistakes with recognizing the build. + vtp_printf("\033[%d;%dH", g_coord.Y + 1, 1); vtp_printf("\033[%d;%dH", y, x); diff --git a/src/version.c b/src/version.c index 2dc9f9bf1e..4e0af78a45 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2132, /**/ 2131, /**/ From 07282f01da06c158bab4787adc89ec15d7eeb202 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 10 Oct 2019 16:46:17 +0200 Subject: [PATCH 31/67] patch 8.1.2133: some tests fail when run as root Problem: Some tests fail when run as root. Solution: Add CheckNotRoot and use it. (James McCoy, closes #5020) --- src/testdir/check.vim | 10 ++++++++++ src/testdir/shared.vim | 9 +++++++++ src/testdir/test_rename.vim | 4 +++- src/testdir/test_swap.vim | 21 +++++++++++++-------- src/testdir/test_terminal.vim | 3 +-- src/testdir/test_viminfo.vim | 1 + src/version.c | 2 ++ 7 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/testdir/check.vim b/src/testdir/check.vim index f176f83329..6c3b1be4ff 100644 --- a/src/testdir/check.vim +++ b/src/testdir/check.vim @@ -1,3 +1,5 @@ +source shared.vim + " Command to check for the presence of a feature. command -nargs=1 CheckFeature call CheckFeature() func CheckFeature(name) @@ -102,3 +104,11 @@ func CheckNotGui() throw 'Skipped: only works in the terminal' endif endfunc + +" Command to check that test is not running as root +command CheckNotRoot call CheckNotRoot() +func CheckNotRoot() + if IsRoot() + throw 'Skipped: cannot run test as root' + endif +endfunc diff --git a/src/testdir/shared.vim b/src/testdir/shared.vim index cf15a4cc41..78f204c10e 100644 --- a/src/testdir/shared.vim +++ b/src/testdir/shared.vim @@ -325,3 +325,12 @@ func RunVimPiped(before, after, arguments, pipecmd) endif return 1 endfunc + +func IsRoot() + if !has('unix') + return v:false + elseif $USER == 'root' || system('id -un') =~ '\' + return v:true + endif + return v:false +endfunc diff --git a/src/testdir/test_rename.vim b/src/testdir/test_rename.vim index 3887fcfabf..5359b84923 100644 --- a/src/testdir/test_rename.vim +++ b/src/testdir/test_rename.vim @@ -1,5 +1,7 @@ " Test rename() +source shared.vim + func Test_rename_file_to_file() call writefile(['foo'], 'Xrename1') @@ -81,7 +83,7 @@ func Test_rename_copy() call assert_equal(0, rename('Xrenamedir/Xrenamefile', 'Xrenamefile')) - if !has('win32') + if !has('win32') && !IsRoot() " On Windows, the source file is removed despite " its directory being made not writable. call assert_equal(['foo'], readfile('Xrenamedir/Xrenamefile')) diff --git a/src/testdir/test_swap.vim b/src/testdir/test_swap.vim index 6e01ad00da..aa67b430e6 100644 --- a/src/testdir/test_swap.vim +++ b/src/testdir/test_swap.vim @@ -1,5 +1,7 @@ " Tests for the swap feature +source shared.vim + func s:swapname() return trim(execute('swapname')) endfunc @@ -196,14 +198,17 @@ func Test_swapfile_delete() quit call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t')) - " Write the swapfile with a modified PID, now it will be automatically - " deleted. Process one should never be Vim. - let swapfile_bytes[24:27] = 0z01000000 - call writefile(swapfile_bytes, swapfile_name) - let s:swapname = '' - split XswapfileText - quit - call assert_equal('', s:swapname) + " This test won't work as root because root can successfully run kill(1, 0) + if !IsRoot() + " Write the swapfile with a modified PID, now it will be automatically + " deleted. Process one should never be Vim. + let swapfile_bytes[24:27] = 0z01000000 + call writefile(swapfile_bytes, swapfile_name) + let s:swapname = '' + split XswapfileText + quit + call assert_equal('', s:swapname) + endif " Now set the modified flag, the swap file will not be deleted let swapfile_bytes[28 + 80 + 899] = 0x55 diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index 0041965f30..7e8ef763b4 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -570,7 +570,7 @@ func Test_terminal_cwd_failure() " Case 3: Directory exists but is not accessible. " Skip this for root, it will be accessible anyway. - if $USER != 'root' + if !IsRoot() call mkdir('XdirNoAccess', '', '0600') " return early if the directory permissions could not be set properly if getfperm('XdirNoAccess')[2] == 'x' @@ -1353,7 +1353,6 @@ endfunc func Test_terminal_api_call() CheckRunVimInTerminal -call ch_logfile('logfile', 'w') unlet! g:called_bufnum unlet! g:called_arg diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim index 7ea9f78da4..a9dc8fd31a 100644 --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -736,6 +736,7 @@ endfunc " Test for an unwritable and unreadble 'viminfo' file func Test_viminfo_perm() CheckUnix + CheckNotRoot call writefile([''], 'Xviminfo') call setfperm('Xviminfo', 'r-x------') call assert_fails('wviminfo Xviminfo', 'E137:') diff --git a/src/version.c b/src/version.c index 4e0af78a45..acbcc2d258 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2133, /**/ 2132, /**/ From 6a0299d8f4c7a64c64d60a6bb39cfe6eaf892247 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 10 Oct 2019 21:14:03 +0200 Subject: [PATCH 32/67] patch 8.1.2134: modifier keys are not always recognized Problem: Modifier keys are not always recognized. Solution: Handle key codes generated by xterm with modifyOtherKeys set. Add this to libvterm so we can debug it. --- src/getchar.c | 19 +++ src/globals.h | 2 + src/libvterm/include/vterm.h | 1 + src/libvterm/src/keyboard.c | 11 ++ src/libvterm/src/state.c | 5 + src/libvterm/src/vterm_internal.h | 1 + src/term.c | 189 ++++++++++++++++++++---------- src/terminal.c | 20 +++- src/version.c | 2 + 9 files changed, 185 insertions(+), 65 deletions(-) diff --git a/src/getchar.c b/src/getchar.c index 85ceddeb4c..ecd6bdcd04 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -1733,6 +1733,25 @@ vgetc(void) case K_XRIGHT: c = K_RIGHT; break; } + if (!no_reduce_keys) + { + // A modifier was not used for a mapping, apply it to ASCII + // keys. + if ((mod_mask & MOD_MASK_CTRL) + && ((c >= '`' && c <= 0x7f) + || (c >= '@' && c <= '_'))) + { + c &= 0x1f; + mod_mask &= ~MOD_MASK_CTRL; + } + if ((mod_mask & (MOD_MASK_META | MOD_MASK_ALT)) + && c >= 0 && c <= 127) + { + c += 0x80; + mod_mask &= ~MOD_MASK_META; + } + } + // For a multi-byte character get all the bytes and return the // converted character. // Note: This will loop until enough bytes are received! diff --git a/src/globals.h b/src/globals.h index 2611266a7d..d790c82e78 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1006,6 +1006,8 @@ EXTERN int no_mapping INIT(= FALSE); // currently no mapping allowed EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed EXTERN int allow_keys INIT(= FALSE); // allow key codes when no_mapping // is set +EXTERN int no_reduce_keys INIT(= FALSE); // do not apply Ctrl, Shift and Alt + // to the key EXTERN int no_u_sync INIT(= 0); // Don't call u_sync() #ifdef FEAT_EVAL EXTERN int u_sync_once INIT(= 0); // Call u_sync() once when evaluating diff --git a/src/libvterm/include/vterm.h b/src/libvterm/include/vterm.h index 02ea91cd59..28d0a10f52 100644 --- a/src/libvterm/include/vterm.h +++ b/src/libvterm/include/vterm.h @@ -200,6 +200,7 @@ size_t vterm_output_get_buffer_remaining(const VTerm *vt); size_t vterm_output_read(VTerm *vt, char *buffer, size_t len); +int vterm_is_modify_other_keys(VTerm *vt); void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod); void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod); diff --git a/src/libvterm/src/keyboard.c b/src/libvterm/src/keyboard.c index 62338c638d..95b962ed64 100644 --- a/src/libvterm/src/keyboard.c +++ b/src/libvterm/src/keyboard.c @@ -4,10 +4,21 @@ #include "utf8.h" +int vterm_is_modify_other_keys(VTerm *vt) +{ + return vt->state->mode.modify_other_keys; +} + + void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod) { int needs_CSIu; + if (vt->state->mode.modify_other_keys && mod != 0) { + vterm_push_output_sprintf_ctrl(vt, C1_CSI, "27;%d;%d~", mod+1, c); + return; + } + // The shift modifier is never important for Unicode characters // apart from Space if(c != ' ') diff --git a/src/libvterm/src/state.c b/src/libvterm/src/state.c index 1f7ac02de9..6b2583af99 100644 --- a/src/libvterm/src/state.c +++ b/src/libvterm/src/state.c @@ -1334,6 +1334,11 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha vterm_state_setpen(state, args, argcount); break; + case LEADER('>', 0x6d): // xterm resource modifyOtherKeys + if (argcount == 2 && args[0] == 4) + state->mode.modify_other_keys = args[1] == 2; + break; + case 0x6e: // DSR - ECMA-48 8.3.35 case LEADER('?', 0x6e): // DECDSR val = CSI_ARG_OR(args[0], 0); diff --git a/src/libvterm/src/vterm_internal.h b/src/libvterm/src/vterm_internal.h index 5b6198bdc0..e2b8b159be 100644 --- a/src/libvterm/src/vterm_internal.h +++ b/src/libvterm/src/vterm_internal.h @@ -124,6 +124,7 @@ struct VTermState unsigned int leftrightmargin:1; unsigned int bracketpaste:1; unsigned int report_focus:1; + unsigned int modify_other_keys:1; } mode; VTermEncodingInstance encoding[4], encoding_utf8; diff --git a/src/term.c b/src/term.c index f021a17ff7..27def15c6a 100644 --- a/src/term.c +++ b/src/term.c @@ -4198,6 +4198,99 @@ is_mouse_topline(win_T *wp) } #endif +/* + * Put "string[new_slen]" in typebuf, or in "buf[bufsize]" if "buf" is not NULL. + * Remove "slen" bytes. + * Returns FAIL for error. + */ + static int +put_string_in_typebuf( + int offset, + int slen, + char_u *string, + int new_slen, + char_u *buf, + int bufsize, + int *buflen) +{ + int extra = new_slen - slen; + + string[new_slen] = NUL; + if (buf == NULL) + { + if (extra < 0) + // remove matched chars, taking care of noremap + del_typebuf(-extra, offset); + else if (extra > 0) + // insert the extra space we need + ins_typebuf(string + slen, REMAP_YES, offset, FALSE, FALSE); + + // Careful: del_typebuf() and ins_typebuf() may have reallocated + // typebuf.tb_buf[]! + mch_memmove(typebuf.tb_buf + typebuf.tb_off + offset, string, + (size_t)new_slen); + } + else + { + if (extra < 0) + // remove matched characters + mch_memmove(buf + offset, buf + offset - extra, + (size_t)(*buflen + offset + extra)); + else if (extra > 0) + { + // Insert the extra space we need. If there is insufficient + // space return -1. + if (*buflen + extra + new_slen >= bufsize) + return FAIL; + mch_memmove(buf + offset + extra, buf + offset, + (size_t)(*buflen - offset)); + } + mch_memmove(buf + offset, string, (size_t)new_slen); + *buflen = *buflen + extra + new_slen; + } + return OK; +} + +/* + * Decode a modifier number as xterm provides it into MOD_MASK bits. + */ + static int +decode_modifiers(int n) +{ + int code = n - 1; + int modifiers = 0; + + if (code & 1) + modifiers |= MOD_MASK_SHIFT; + if (code & 2) + modifiers |= MOD_MASK_ALT; + if (code & 4) + modifiers |= MOD_MASK_CTRL; + if (code & 8) + modifiers |= MOD_MASK_META; + return modifiers; +} + + static int +modifiers2keycode(int modifiers, int *key, char_u *string) +{ + int new_slen = 0; + + if (modifiers != 0) + { + // Some keys have the modifier included. Need to handle that here to + // make mappings work. + *key = simplify_key(*key, &modifiers); + if (modifiers != 0) + { + string[new_slen++] = K_SPECIAL; + string[new_slen++] = (int)KS_MODIFIER; + string[new_slen++] = modifiers; + } + } + return new_slen; +} + /* * Check if typebuf.tb_buf[] contains a terminal key code. * Check from typebuf.tb_buf[typebuf.tb_off] to typebuf.tb_buf[typebuf.tb_off @@ -4229,8 +4322,7 @@ check_termcode( int modifiers; char_u *modifiers_start = NULL; int key; - int new_slen; - int extra; + int new_slen; // Length of what will replace the termcode char_u string[MAX_KEY_CODE_LEN + 1]; int i, j; int idx = 0; @@ -4401,16 +4493,9 @@ check_termcode( modifiers_start = tp + slen - 2; - /* Match! Convert modifier bits. */ - n = atoi((char *)modifiers_start) - 1; - if (n & 1) - modifiers |= MOD_MASK_SHIFT; - if (n & 2) - modifiers |= MOD_MASK_ALT; - if (n & 4) - modifiers |= MOD_MASK_CTRL; - if (n & 8) - modifiers |= MOD_MASK_META; + // Match! Convert modifier bits. + n = atoi((char *)modifiers_start); + modifiers |= decode_modifiers(n); slen = j; } @@ -4751,9 +4836,32 @@ not_enough: winpos_status.tr_progress = STATUS_GOT; } - // TODO: key with modifier: + // Key with modifier: // {lead}27;{modifier};{key}~ // {lead}{key};{modifier}u + else if ((arg[0] == 27 && argc == 3 && trail == '~') + || (argc == 2 && trail == 'u')) + { + if (trail == 'u') + key = arg[0]; + else + key = arg[2]; + + // insert modifiers with KS_MODIFIER + modifiers = decode_modifiers(arg[1]); + new_slen = modifiers2keycode(modifiers, &key, string); + slen = csi_len; + + if (has_mbyte) + new_slen += (*mb_char2bytes)(key, string + new_slen); + else + string[new_slen++] = key; + + if (put_string_in_typebuf(offset, slen, string, new_slen, + buf, bufsize, buflen) == FAIL) + return -1; + return len + new_slen - slen + offset; + } // else: Unknown CSI sequence. We could drop it, but then the // user can't create a map for it. @@ -5138,19 +5246,7 @@ not_enough: /* * Add any modifier codes to our string. */ - new_slen = 0; /* Length of what will replace the termcode */ - if (modifiers != 0) - { - /* Some keys have the modifier included. Need to handle that here - * to make mappings work. */ - key = simplify_key(key, &modifiers); - if (modifiers != 0) - { - string[new_slen++] = K_SPECIAL; - string[new_slen++] = (int)KS_MODIFIER; - string[new_slen++] = modifiers; - } - } + new_slen = modifiers2keycode(modifiers, &key, string); /* Finally, add the special key code to our string */ key_name[0] = KEY2TERMCAP0(key); @@ -5176,43 +5272,10 @@ not_enough: string[new_slen++] = key_name[0]; string[new_slen++] = key_name[1]; } - string[new_slen] = NUL; - extra = new_slen - slen; - if (buf == NULL) - { - if (extra < 0) - /* remove matched chars, taking care of noremap */ - del_typebuf(-extra, offset); - else if (extra > 0) - /* insert the extra space we need */ - ins_typebuf(string + slen, REMAP_YES, offset, FALSE, FALSE); - - /* - * Careful: del_typebuf() and ins_typebuf() may have reallocated - * typebuf.tb_buf[]! - */ - mch_memmove(typebuf.tb_buf + typebuf.tb_off + offset, string, - (size_t)new_slen); - } - else - { - if (extra < 0) - /* remove matched characters */ - mch_memmove(buf + offset, buf + offset - extra, - (size_t)(*buflen + offset + extra)); - else if (extra > 0) - { - /* Insert the extra space we need. If there is insufficient - * space return -1. */ - if (*buflen + extra + new_slen >= bufsize) - return -1; - mch_memmove(buf + offset + extra, buf + offset, - (size_t)(*buflen - offset)); - } - mch_memmove(buf + offset, string, (size_t)new_slen); - *buflen = *buflen + extra + new_slen; - } - return retval == 0 ? (len + extra + offset) : retval; + if (put_string_in_typebuf(offset, slen, string, new_slen, + buf, bufsize, buflen) == FAIL) + return -1; + return retval == 0 ? (len + new_slen - slen + offset) : retval; } #ifdef FEAT_TERMRESPONSE diff --git a/src/terminal.c b/src/terminal.c index 42a80cd7dc..a992b444dc 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -1371,11 +1371,13 @@ term_convert_key(term_T *term, int c, char *buf) break; } + // add modifiers for the typed key + mod |= mod_mask; + /* * Convert special keys to vterm keys: * - Write keys to vterm: vterm_keyboard_key() * - Write output to channel. - * TODO: use mod_mask */ if (key != VTERM_KEY_NONE) /* Special key, let vterm convert it. */ @@ -1902,15 +1904,21 @@ term_vgetc() { int c; int save_State = State; + int modify_other_keys = + vterm_is_modify_other_keys(curbuf->b_term->tl_vterm); State = TERMINAL; got_int = FALSE; #ifdef MSWIN ctrl_break_was_pressed = FALSE; #endif + if (modify_other_keys) + ++no_reduce_keys; c = vgetc(); got_int = FALSE; State = save_State; + if (modify_other_keys) + --no_reduce_keys; return c; } @@ -2255,6 +2263,7 @@ term_win_entered() terminal_loop(int blocking) { int c; + int raw_c; int termwinkey = 0; int ret; #ifdef UNIX @@ -2307,6 +2316,13 @@ terminal_loop(int blocking) if (c == K_IGNORE) continue; + // vgetc may not include CTRL in the key when modify_other_keys is set. + raw_c = c; + if ((mod_mask & MOD_MASK_CTRL) + && ((c >= '`' && c <= 0x7f) + || (c >= '@' && c <= '_'))) + c &= 0x1f; + #ifdef UNIX /* * The shell or another program may change the tty settings. Getting @@ -2417,7 +2433,7 @@ terminal_loop(int blocking) c = wc; } # endif - if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK) + if (send_keys_to_term(curbuf->b_term, raw_c, TRUE) != OK) { if (c == K_MOUSEMOVE) /* We are sure to come back here, don't reset the cursor color diff --git a/src/version.c b/src/version.c index acbcc2d258..7289c9bb6d 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2134, /**/ 2133, /**/ From 00eab7f128ecfef699fec30988e5b1cc72378617 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 10 Oct 2019 21:49:28 +0200 Subject: [PATCH 33/67] patch 8.1.2135: with modifyOtherKeys Alt-a does not work properly Problem: With modifyOtherKeys Alt-a does not work properly. Solution: Remove the ALT modifier. Get multi-byte after applying ALT. --- src/getchar.c | 38 +++++++++++++++++++------------------- src/version.c | 2 ++ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/getchar.c b/src/getchar.c index ecd6bdcd04..0e4e3c3157 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -1733,25 +1733,6 @@ vgetc(void) case K_XRIGHT: c = K_RIGHT; break; } - if (!no_reduce_keys) - { - // A modifier was not used for a mapping, apply it to ASCII - // keys. - if ((mod_mask & MOD_MASK_CTRL) - && ((c >= '`' && c <= 0x7f) - || (c >= '@' && c <= '_'))) - { - c &= 0x1f; - mod_mask &= ~MOD_MASK_CTRL; - } - if ((mod_mask & (MOD_MASK_META | MOD_MASK_ALT)) - && c >= 0 && c <= 127) - { - c += 0x80; - mod_mask &= ~MOD_MASK_META; - } - } - // For a multi-byte character get all the bytes and return the // converted character. // Note: This will loop until enough bytes are received! @@ -1787,6 +1768,25 @@ vgetc(void) c = (*mb_ptr2char)(buf); } + if (!no_reduce_keys) + { + // A modifier was not used for a mapping, apply it to ASCII + // keys. + if ((mod_mask & MOD_MASK_CTRL) + && ((c >= '`' && c <= 0x7f) + || (c >= '@' && c <= '_'))) + { + c &= 0x1f; + mod_mask &= ~MOD_MASK_CTRL; + } + if ((mod_mask & (MOD_MASK_META | MOD_MASK_ALT)) + && c >= 0 && c <= 127) + { + c += 0x80; + mod_mask &= ~(MOD_MASK_META|MOD_MASK_ALT); + } + } + break; } } diff --git a/src/version.c b/src/version.c index 7289c9bb6d..f22db55478 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2135, /**/ 2134, /**/ From ec66c41d84e574baf8009dbc0bd088d2bc5b2421 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 11 Oct 2019 21:19:13 +0200 Subject: [PATCH 34/67] patch 8.1.2136: using freed memory with autocmd from fuzzer Problem: using freed memory with autocmd from fuzzer. (Dhiraj Mishra, Dominique Pelle) Solution: Avoid using "wp" after autocommands. (closes #5041) --- src/testdir/test_autocmd.vim | 8 ++++++++ src/version.c | 2 ++ src/window.c | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index fe77bf5786..1e53fe4639 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -2288,3 +2288,11 @@ func Test_autocmd_CmdWinEnter() call StopVimInTerminal(buf) call delete(filename) endfunc + +func Test_autocmd_was_using_freed_memory() + pedit xx + n x + au WinEnter * quit + split + au! WinEnter +endfunc diff --git a/src/version.c b/src/version.c index f22db55478..46bd1b19c6 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2136, /**/ 2135, /**/ diff --git a/src/window.c b/src/window.c index 0fda9f05c6..fb8a5a7ede 100644 --- a/src/window.c +++ b/src/window.c @@ -4641,6 +4641,7 @@ win_enter_ext( #ifdef FEAT_JOB_CHANNEL entering_window(curwin); #endif + // Careful: autocommands may close the window and make "wp" invalid if (trigger_new_autocmds) apply_autocmds(EVENT_WINNEW, NULL, NULL, FALSE, curbuf); if (trigger_enter_autocmds) @@ -4655,7 +4656,7 @@ win_enter_ext( #endif curwin->w_redr_status = TRUE; #ifdef FEAT_TERMINAL - if (bt_terminal(wp->w_buffer)) + if (bt_terminal(curwin->w_buffer)) // terminal is likely in another mode redraw_mode = TRUE; #endif From 1a4cbb19ded7ad6cb161f2797e0414032d1f9b5c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Oct 2019 13:25:44 +0200 Subject: [PATCH 35/67] patch 8.1.2137: parsing the termresponse is not tested Problem: Parsing the termresponse is not tested. Solution: Add a first test. (related to #5042) --- src/testdir/test_termcodes.vim | 24 +++++++++++++++++++++--- src/version.c | 2 ++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index 6174723173..22e7a04de1 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -779,9 +779,9 @@ func Test_term_rgb_response() endfunc " This only checks if the sequence is recognized. -" This must be last, because it has side effects to xterm properties. -" TODO: check that the values were parsed properly -func Test_xx_term_style_response() +" This must be after other tests, because it has side effects to xterm +" properties. +func Test_xx01_term_style_response() " Termresponse is only parsed when t_RV is not empty. set t_RV=x @@ -797,6 +797,24 @@ func Test_xx_term_style_response() set t_RV= endfunc +" This checks the libvterm version response. +" This must be after other tests, because it has side effects to xterm +" properties. +" TODO: check other terminals response +func Test_xx02_libvterm_response() + " Termresponse is only parsed when t_RV is not empty. + set t_RV=x + set ttymouse=xterm + call test_option_not_set('ttymouse') + + let seq = "\[>0;100;0c" + call feedkeys(seq, 'Lx!') + call assert_equal(seq, v:termresponse) + call assert_equal('sgr', &ttymouse) + + set t_RV= +endfunc + func Test_get_termcode() try let k1 = &t_k1 diff --git a/src/version.c b/src/version.c index 46bd1b19c6..a6a1398386 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2137, /**/ 2136, /**/ From 4cdbed33e467bdfed9aa90dc0f96642c91db32bb Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Oct 2019 15:02:47 +0200 Subject: [PATCH 36/67] patch 8.1.2138: including the build number in the Win32 binary is confusing Problem: Including the build number in the Win32 binary is confusing. Solution: Only use the patchlevel. --- src/version.c | 2 ++ src/vim.rc | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/version.c b/src/version.c index a6a1398386..9cb3a01568 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2138, /**/ 2137, /**/ diff --git a/src/vim.rc b/src/vim.rc index 726cb96409..3307c8e51e 100644 --- a/src/vim.rc +++ b/src/vim.rc @@ -58,8 +58,8 @@ CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "gvim.exe.mnf" // VS_VERSION_INFO VERSIONINFO - FILEVERSION VIM_VERSION_MAJOR,VIM_VERSION_MINOR,VIM_VERSION_BUILD,VIM_VERSION_PATCHLEVEL - PRODUCTVERSION VIM_VERSION_MAJOR,VIM_VERSION_MINOR,VIM_VERSION_BUILD,VIM_VERSION_PATCHLEVEL + FILEVERSION VIM_VERSION_MAJOR,VIM_VERSION_MINOR,VIM_VERSION_PATCHLEVEL + PRODUCTVERSION VIM_VERSION_MAJOR,VIM_VERSION_MINOR,VIM_VERSION_PATCHLEVEL FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #if VIM_VERSION_PATCHLEVEL > 0 @@ -91,7 +91,7 @@ BEGIN BEGIN VALUE "CompanyName", "Vim Developers\0" VALUE "FileDescription", "Vi Improved - A Text Editor\0" - VALUE "FileVersion", VIM_VERSION_MAJOR_STR ", " VIM_VERSION_MINOR_STR ", " VIM_VERSION_BUILD_STR ", " VIM_VERSION_PATCHLEVEL_STR "\0" + VALUE "FileVersion", VIM_VERSION_MAJOR_STR ", " VIM_VERSION_MINOR_STR ", " VIM_VERSION_PATCHLEVEL_STR "\0" VALUE "InternalName", "VIM\0" VALUE "LegalCopyright", "Copyright \251 1996\0" VALUE "LegalTrademarks", "Vim\0" @@ -103,7 +103,7 @@ BEGIN VALUE "OriginalFilename", "vim.exe\0" #endif VALUE "ProductName", "Vim\0" - VALUE "ProductVersion", VIM_VERSION_MAJOR_STR ", " VIM_VERSION_MINOR_STR ", " VIM_VERSION_BUILD_STR ", " VIM_VERSION_PATCHLEVEL_STR "\0" + VALUE "ProductVersion", VIM_VERSION_MAJOR_STR ", " VIM_VERSION_MINOR_STR ", " VIM_VERSION_PATCHLEVEL_STR "\0" END END BLOCK "VarFileInfo" From 18a79a68413365cd7672728d54615ca708764e23 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Oct 2019 15:36:11 +0200 Subject: [PATCH 37/67] patch 8.1.2139: the modifyOtherKeys codes are not tested Problem: The modifyOtherKeys codes are not tested. Solution: Add a test case. --- src/testdir/test_termcodes.vim | 64 ++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 2 files changed, 66 insertions(+) diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index 22e7a04de1..0d157aeb42 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -845,3 +845,67 @@ func Test_get_termcode() set ttybuiltin endfunc + +func GetEscCodeCSI27(key, modifier) + let key = printf("%d", char2nr(a:key)) + let mod = printf("%d", a:modifier) + return "\[27;" .. mod .. ';' .. key .. '~' +endfunc + +func GetEscCodeCSIu(key, modifier) + let key = printf("%d", char2nr(a:key)) + let mod = printf("%d", a:modifier) + return "\[" .. key .. ';' .. mod .. 'u' +endfunc + +" This checks the CSI sequences when in modifyOtherKeys mode. +" The mode doesn't need to be enabled, the codes are always detected. +func RunTest_modifyOtherKeys(func) + new + set timeoutlen=20 + + " Shift-X is send as 'X' with the shift modifier + call feedkeys('a' .. a:func('X', 2) .. "\", 'Lx!') + call assert_equal('X', getline(1)) + + " Ctrl-i is Tab + call setline(1, '') + call feedkeys('a' .. a:func('i', 5) .. "\", 'Lx!') + call assert_equal("\t", getline(1)) + + " Ctrl-I is also Tab + call setline(1, '') + call feedkeys('a' .. a:func('I', 5) .. "\", 'Lx!') + call assert_equal("\t", getline(1)) + + " Alt-x is ø + call setline(1, '') + call feedkeys('a' .. a:func('x', 3) .. "\", 'Lx!') + call assert_equal("ø", getline(1)) + + " Meta-x is also ø + call setline(1, '') + call feedkeys('a' .. a:func('x', 9) .. "\", 'Lx!') + call assert_equal("ø", getline(1)) + + " Alt-X is Ø + call setline(1, '') + call feedkeys('a' .. a:func('X', 3) .. "\", 'Lx!') + call assert_equal("Ø", getline(1)) + + " Meta-X is ø + call setline(1, '') + call feedkeys('a' .. a:func('X', 9) .. "\", 'Lx!') + call assert_equal("Ø", getline(1)) + + bwipe! + set timeoutlen& +endfunc + +func Test_modifyOtherKeys_CSI27() + call RunTest_modifyOtherKeys(function('GetEscCodeCSI27')) +endfunc + +func Test_modifyOtherKeys_CSIu() + call RunTest_modifyOtherKeys(function('GetEscCodeCSIu')) +endfunc diff --git a/src/version.c b/src/version.c index 9cb3a01568..87b3a33996 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2139, /**/ 2138, /**/ From ceba3dd5187788e09f65bd41b07b40f6f9aab953 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Oct 2019 16:12:54 +0200 Subject: [PATCH 38/67] patch 8.1.2140: "gk" and "gj" do not work correctly in number column Problem: "gk" and "gj" do not work correctly in number column. Solution: Allow for a negative "curswant". (Zach Wegner, closes #4969) --- src/misc2.c | 12 +++++++----- src/normal.c | 17 ++++++++++++----- src/testdir/test_normal.vim | 25 +++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/misc2.c b/src/misc2.c index da274e6036..3261437db2 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -119,10 +119,11 @@ getvpos(pos_T *pos, colnr_T wcol) static int coladvance2( pos_T *pos, - int addspaces, /* change the text to achieve our goal? */ - int finetune, /* change char offset for the exact column */ - colnr_T wcol) /* column to move to */ + int addspaces, // change the text to achieve our goal? + int finetune, // change char offset for the exact column + colnr_T wcol_arg) // column to move to (can be negative) { + colnr_T wcol = wcol_arg; int idx; char_u *ptr; char_u *line; @@ -136,7 +137,7 @@ coladvance2( one_more = (State & INSERT) || restart_edit != NUL || (VIsual_active && *p_sel != 'o') - || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL) ; + || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL); line = ml_get_buf(curbuf, pos->lnum, FALSE); if (wcol >= MAXCOL) @@ -206,6 +207,7 @@ coladvance2( if (virtual_active() && addspaces + && wcol >= 0 && ((col != wcol && col != wcol + 1) || csize > 1)) { /* 'virtualedit' is set: The difference between wcol and col is @@ -305,7 +307,7 @@ coladvance2( if (has_mbyte) mb_adjustpos(curbuf, pos); - if (col < wcol) + if (wcol < 0 || col < wcol) return FAIL; return OK; } diff --git a/src/normal.c b/src/normal.c index 1fddeae07a..50f7b3185d 100644 --- a/src/normal.c +++ b/src/normal.c @@ -2499,17 +2499,18 @@ nv_screengo(oparg_T *oap, int dir, long dist) n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1; else n = width1; - if (curwin->w_curswant > (colnr_T)n + 1) - curwin->w_curswant -= ((curwin->w_curswant - n) / width2 + 1) - * width2; + if (curwin->w_curswant >= (colnr_T)n) + curwin->w_curswant = n - 1; } while (dist--) { if (dir == BACKWARD) { - if ((long)curwin->w_curswant > width2) - // move back within line + if ((long)curwin->w_curswant >= width1) + // Move back within the line. This can give a negative value + // for w_curswant if width1 < width2 (with cpoptions+=n), + // which will get clipped to column 0. curwin->w_curswant -= width2; else { @@ -2557,6 +2558,12 @@ nv_screengo(oparg_T *oap, int dir, long dist) } curwin->w_cursor.lnum++; curwin->w_curswant %= width2; + // Check if the cursor has moved below the number display + // when width1 < width2 (with cpoptions+=n). Subtract width2 + // to get a negative value for w_curswant, which will get + // clipped to column 0. + if (curwin->w_curswant >= width1) + curwin->w_curswant -= width2; linelen = linetabsize(ml_get_curline()); } } diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim index dcfa929c8a..da9c004a84 100644 --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -2654,4 +2654,29 @@ func Test_normal_gk() call assert_equal(95, virtcol('.')) bw! bw! + + " needs 80 column new window + new + vert 80new + set number + set numberwidth=10 + set cpoptions+=n + put =[repeat('0',90), repeat('1',90)] + norm! 075l + call assert_equal(76, col('.')) + norm! gk + call assert_equal(1, col('.')) + norm! gk + call assert_equal(76, col('.')) + norm! gk + call assert_equal(1, col('.')) + norm! gj + call assert_equal(76, col('.')) + norm! gj + call assert_equal(1, col('.')) + norm! gj + call assert_equal(76, col('.')) + bw! + bw! + set cpoptions& number& numberwidth& endfunc diff --git a/src/version.c b/src/version.c index 87b3a33996..2ddecaf034 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2140, /**/ 2139, /**/ From e8070987c6ca9b1e14c5305707c6d29c8e58e7c4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Oct 2019 17:07:06 +0200 Subject: [PATCH 39/67] patch 8.1.2141: :tselect has an extra hit-enter prompt Problem: :tselect has an extra hit-enter prompt. Solution: Do not set need_wait_return when only moving the cursor. (closes #5040) --- src/message.c | 15 ++++++------ src/testdir/dumps/Test_tselect_1.dump | 10 ++++++++ src/testdir/test_tagjump.vim | 33 +++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 src/testdir/dumps/Test_tselect_1.dump diff --git a/src/message.c b/src/message.c index dd5f3f519b..19defaa1f8 100644 --- a/src/message.c +++ b/src/message.c @@ -1980,15 +1980,14 @@ msg_puts_attr_len(char *str, int maxlen, int attr) attr &= ~MSG_HIST; } - /* - * When writing something to the screen after it has scrolled, requires a - * wait-return prompt later. Needed when scrolling, resetting - * need_wait_return after some prompt, and then outputting something - * without scrolling - */ - if (msg_scrolled != 0 && !msg_scrolled_ign) + // When writing something to the screen after it has scrolled, requires a + // wait-return prompt later. Needed when scrolling, resetting + // need_wait_return after some prompt, and then outputting something + // without scrolling + // Not needed when only using CR to move the cursor. + if (msg_scrolled != 0 && !msg_scrolled_ign && STRCMP(str, "\r") != 0) need_wait_return = TRUE; - msg_didany = TRUE; /* remember that something was outputted */ + msg_didany = TRUE; // remember that something was outputted /* * If there is no valid screen, use fprintf so we can see error messages. diff --git a/src/testdir/dumps/Test_tselect_1.dump b/src/testdir/dumps/Test_tselect_1.dump new file mode 100644 index 0000000000..cdaee45776 --- /dev/null +++ b/src/testdir/dumps/Test_tselect_1.dump @@ -0,0 +1,10 @@ +>i+0#00e0003#ffffff0|n|t| +0#0000000&|m|a|i|n|(|)| @39 +|v+0#00e0003&|o|i|d| +0#0000000&|t|e|s|t|(|)| @38 +|~+0#4040ff13&| @48 +|~| @48 +|~| @48 +|~| @48 +|~| @48 +|~| @48 +|~| @48 +|"+0#0000000&|X|t|e|s|t|.|c|"| |2|L|,| |2|3|C| @14|1|,|1| @10|A|l@1| diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index 6d47020a52..64a0a28caf 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -1,5 +1,8 @@ " Tests for tagjump (tags and special searches) +source check.vim +source screendump.vim + " SEGV occurs in older versions. (At least 7.4.1748 or older) func Test_ptag_with_notagstack() set notagstack @@ -473,4 +476,34 @@ func Test_tag_line_toolong() let &verbose = old_vbs endfunc +" Check that using :tselect does not run into the hit-enter prompt. +" Requires a terminal to trigger that prompt. +func Test_tselect() + CheckScreendump + + call writefile([ + \ 'main Xtest.h /^void test();$/;" f', + \ 'main Xtest.c /^int main()$/;" f', + \ 'main Xtest.x /^void test()$/;" f', + \ ], 'Xtags') + cal writefile([ + \ 'int main()', + \ 'void test()', + \ ], 'Xtest.c') + + let lines =<< trim [SCRIPT] + set tags=Xtags + [SCRIPT] + call writefile(lines, 'XTest_tselect') + let buf = RunVimInTerminal('-S XTest_tselect', {'rows': 10, 'cols': 50}) + + call term_wait(buf, 100) + call term_sendkeys(buf, ":tselect main\2\") + call VerifyScreenDump(buf, 'Test_tselect_1', {}) + + call delete('Xtags') + call delete('Xtest.c') + call delete('XTest_tselect') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 2ddecaf034..c92eb26a10 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2141, /**/ 2140, /**/ From d1e2f3984ae0b4e22ba6977eedcf05285819eea9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Oct 2019 18:22:50 +0200 Subject: [PATCH 40/67] patch 8.1.2142: some key mappings do not work with modifyOtherKeys Problem: Some key mappings do not work with modifyOtherKeys. Solution: Remove the Shift modifier if it is already included in the key. --- src/term.c | 11 +++++++++- src/testdir/test_termcodes.vim | 39 ++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/term.c b/src/term.c index 27def15c6a..e30506bc42 100644 --- a/src/term.c +++ b/src/term.c @@ -4847,8 +4847,17 @@ not_enough: else key = arg[2]; - // insert modifiers with KS_MODIFIER modifiers = decode_modifiers(arg[1]); + + // Some keys already have Shift included, pass them as + // normal keys. + if (modifiers == MOD_MASK_SHIFT + && ((key >= '@' && key <= 'Z') + || key == '^' || key == '_' + || (key >= '{' && key <= '~'))) + modifiers = 0; + + // insert modifiers with KS_MODIFIER new_slen = modifiers2keycode(modifiers, &key, string); slen = csi_len; diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index 0d157aeb42..1df9f7bf11 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -909,3 +909,42 @@ endfunc func Test_modifyOtherKeys_CSIu() call RunTest_modifyOtherKeys(function('GetEscCodeCSIu')) endfunc + +func RunTest_mapping_shift(key, func) + call setline(1, '') + if a:key == '|' + exe 'inoremap \| xyz' + else + exe 'inoremap ' .. a:key .. ' xyz' + endif + call feedkeys('a' .. a:func(a:key, 2) .. "\", 'Lx!') + call assert_equal("xyz", getline(1)) + if a:key == '|' + exe 'iunmap \|' + else + exe 'iunmap ' .. a:key + endif +endfunc + +func RunTest_mapping_works_with_shift(func) + new + set timeoutlen=20 + + call RunTest_mapping_shift('@', a:func) + call RunTest_mapping_shift('A', a:func) + call RunTest_mapping_shift('Z', a:func) + call RunTest_mapping_shift('^', a:func) + call RunTest_mapping_shift('_', a:func) + call RunTest_mapping_shift('{', a:func) + call RunTest_mapping_shift('|', a:func) + call RunTest_mapping_shift('}', a:func) + call RunTest_mapping_shift('~', a:func) + + bwipe! + set timeoutlen& +endfunc + +func Test_mapping_works_with_shift() + call RunTest_mapping_works_with_shift(function('GetEscCodeCSI27')) + call RunTest_mapping_works_with_shift(function('GetEscCodeCSIu')) +endfunc diff --git a/src/version.c b/src/version.c index c92eb26a10..74126d2e24 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2142, /**/ 2141, /**/ From 4facea310c2788c88f021b262658b847381a50a8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Oct 2019 20:17:40 +0200 Subject: [PATCH 41/67] patch 8.1.2143: cannot see each command even when 'verbose' is set Problem: Cannot see each command even when 'verbose' is set. Solution: List each command when 'verbose' is at least 16. --- src/ex_docmd.c | 47 +++++++++++++------- src/testdir/dumps/Test_verbose_option_1.dump | 12 +++++ src/testdir/test_cmdline.vim | 23 ++++++++++ src/testdir/test_tagjump.vim | 1 + src/version.c | 2 + 5 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 src/testdir/dumps/Test_verbose_option_1.dump diff --git a/src/ex_docmd.c b/src/ex_docmd.c index a4a8756354..f4c327140d 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -558,6 +558,27 @@ do_exmode( msg_scroll = save_msg_scroll; } +/* + * Print the executed command for when 'verbose' is set. + * When "lnum" is 0 only print the command. + */ + static void +msg_verbose_cmd(linenr_T lnum, char_u *cmd) +{ + ++no_wait_return; + verbose_enter_scroll(); + + if (lnum == 0) + smsg(_("Executing: %s"), cmd); + else + smsg(_("line %ld: %s"), (long)lnum, cmd); + if (msg_silent == 0) + msg_puts("\n"); // don't overwrite this + + verbose_leave_scroll(); + --no_wait_return; +} + /* * Execute a simple command line. Used for translated commands like "*". */ @@ -944,18 +965,7 @@ do_cmdline( } if (p_verbose >= 15 && sourcing_name != NULL) - { - ++no_wait_return; - verbose_enter_scroll(); - - smsg(_("line %ld: %s"), - (long)sourcing_lnum, cmdline_copy); - if (msg_silent == 0) - msg_puts("\n"); /* don't overwrite this */ - - verbose_leave_scroll(); - --no_wait_return; - } + msg_verbose_cmd(sourcing_lnum, cmdline_copy); /* * 2. Execute one '|' separated command. @@ -1666,6 +1676,9 @@ do_one_cmd( if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!') goto doend; + if (p_verbose >= 16) + msg_verbose_cmd(0, *cmdlinep); + /* * 1. Skip comment lines and leading white space and colons. * 2. Handle command modifiers. @@ -8534,9 +8547,9 @@ ex_folddo(exarg_T *eap) { linenr_T lnum; -#ifdef FEAT_CLIPBOARD +# ifdef FEAT_CLIPBOARD start_global_changes(); -#endif +# endif /* First set the marks for all lines closed/open. */ for (lnum = eap->line1; lnum <= eap->line2; ++lnum) @@ -8546,9 +8559,9 @@ ex_folddo(exarg_T *eap) /* Execute the command on the marked lines. */ global_exe(eap->arg); ml_clearmarked(); /* clear rest of the marks */ -#ifdef FEAT_CLIPBOARD +# ifdef FEAT_CLIPBOARD end_global_changes(); -#endif +# endif } #endif @@ -8566,7 +8579,7 @@ is_loclist_cmd(int cmdidx) } #endif -# if defined(FEAT_TIMERS) || defined(PROTO) +#if defined(FEAT_TIMERS) || defined(PROTO) int get_pressedreturn(void) { diff --git a/src/testdir/dumps/Test_verbose_option_1.dump b/src/testdir/dumps/Test_verbose_option_1.dump new file mode 100644 index 0000000000..192d1028c8 --- /dev/null +++ b/src/testdir/dumps/Test_verbose_option_1.dump @@ -0,0 +1,12 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|E+0#0000000&|x|e|c|u|t|i|n|g|:| |D|o|S|o|m|e|t|h|i|n|g| @52 +|E|x|e|c|u|t|i|n|g|:| |e|c|h|o| |'|h|e|l@1|o|'| |||s|e|t| |t|s|=|4| |||l|e|t| |v| |=| |'|1|2|3|'| |||e|c|h|o| |v| @18 +|h|e|l@1|o| @69 +|E|x|e|c|u|t|i|n|g|:| |s|e|t| |t|s|=|4| |||l|e|t| |v| |=| |'|1|2|3|'| |||e|c|h|o| |v| @32 +|E|x|e|c|u|t|i|n|g|:| |l|e|t| |v| |=| |'|1|2|3|'| |||e|c|h|o| |v| @42 +|E|x|e|c|u|t|i|n|g|:| |e|c|h|o| |v| @57 +|1|2|3| @71 +|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 98768b5097..55f35e7dd6 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -1,5 +1,8 @@ " Tests for editing the command line. +source check.vim +source screendump.vim + func Test_complete_tab() call writefile(['testfile'], 'Xtestfile') call feedkeys(":e Xtest\t\r", "tx") @@ -687,6 +690,26 @@ func Test_verbosefile() call delete('Xlog') endfunc +func Test_verbose_option() + CheckScreendump + + let lines =<< trim [SCRIPT] + command DoSomething echo 'hello' |set ts=4 |let v = '123' |echo v + call feedkeys("\r", 't') " for the hit-enter prompt + set verbose=20 + [SCRIPT] + call writefile(lines, 'XTest_verbose') + + let buf = RunVimInTerminal('-S XTest_verbose', {'rows': 12}) + call term_wait(buf, 100) + call term_sendkeys(buf, ":DoSomething\") + call VerifyScreenDump(buf, 'Test_verbose_option_1', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_verbose') +endfunc + func Test_setcmdpos() func InsertTextAtPos(text, pos) call assert_equal(0, setcmdpos(a:pos)) diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index 64a0a28caf..81d3e80a1c 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -501,6 +501,7 @@ func Test_tselect() call term_sendkeys(buf, ":tselect main\2\") call VerifyScreenDump(buf, 'Test_tselect_1', {}) + call StopVimInTerminal(buf) call delete('Xtags') call delete('Xtest.c') call delete('XTest_tselect') diff --git a/src/version.c b/src/version.c index 74126d2e24..615d24e141 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2143, /**/ 2142, /**/ From 171a921b51101c1261040d28a8147c8829b675d3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Oct 2019 21:08:59 +0200 Subject: [PATCH 42/67] patch 8.1.2144: side effects when using t_ti to enable modifyOtherKeys Problem: Side effects when using t_ti to enable modifyOtherKeys. Solution: Add t_TI and t_TE. --- runtime/doc/term.txt | 29 ++++++++++++++++++----------- src/optiondefs.h | 2 ++ src/term.c | 3 +++ src/term.h | 8 ++++++-- src/version.c | 2 ++ 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index 79debe3a1e..133d0e991b 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -1,4 +1,4 @@ -*term.txt* For Vim version 8.1. Last change: 2019 May 07 +*term.txt* For Vim version 8.1. Last change: 2019 Oct 12 VIM REFERENCE MANUAL by Bram Moolenaar @@ -80,14 +80,19 @@ can do this best in your .vimrc. Example: > < *raw-terminal-mode* For normal editing the terminal will be put into "raw" mode. The strings -defined with 't_ti' and 't_ks' will be sent to the terminal. Normally this -puts the terminal in a state where the termcap codes are valid and activates -the cursor and function keys. When Vim exits the terminal will be put back -into the mode it was before Vim started. The strings defined with 't_te' and -'t_ke' will be sent to the terminal. On the Amiga, with commands that execute -an external command (e.g., "!!"), the terminal will be put into Normal mode -for a moment. This means that you can stop the output to the screen by -hitting a printing key. Output resumes when you hit . +defined with 't_ti', 't_TI' and 't_ks' will be sent to the terminal. Normally +this puts the terminal in a state where the termcap codes are valid and +activates the cursor and function keys. +When Vim exits the terminal will be put back into the mode it was before Vim +started. The strings defined with 't_te', 't_TE' and 't_ke' will be sent to +the terminal. On the Amiga, with commands that execute an external command +(e.g., "!!"), the terminal will be put into Normal mode for a moment. This +means that you can stop the output to the screen by hitting a printing key. +Output resumes when you hit . + +Note: When 't_ti' is not empty, Vim assumes that it causes switching to the +alternate screen. This may slightly change what happens when executing a +shell command or exiting Vim. To avoid this use 't_TI' and 't_TE'. *xterm-bracketed-paste* When the 't_BE' option is set then 't_BE' will be sent to the @@ -297,8 +302,8 @@ OUTPUT CODES *terminal-output-codes* t_se standout end *t_se* *'t_se'* t_so standout mode *t_so* *'t_so'* t_sr scroll reverse (backward) *t_sr* *'t_sr'* - t_te out of "termcap" mode *t_te* *'t_te'* - t_ti put terminal in "termcap" mode *t_ti* *'t_ti'* + t_te end of "termcap" mode *t_te* *'t_te'* + t_ti put terminal into "termcap" mode *t_ti* *'t_ti'* t_ts set window title start (to status line) *t_ts* *'t_ts'* t_ue underline end *t_ue* *'t_ue'* t_us underline mode *t_us* *'t_us'* @@ -359,6 +364,8 @@ Added by Vim (there are no standard codes for these): t_RT restore window title from stack *t_RT* *'t_RT'* t_Si save icon text to stack *t_Si* *'t_Si'* t_Ri restore icon text from stack *t_Ri* *'t_Ri'* + t_TE end of "raw" mode *t_TE* *'t_TE'* + t_TI put terminal into "raw" mode *t_TI* *'t_TI'* Some codes have a start, middle and end part. The start and end are defined by the termcap option, the middle part is text. diff --git a/src/optiondefs.h b/src/optiondefs.h index 3745d07ced..47f17ed392 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -2969,7 +2969,9 @@ static struct vimoption options[] = p_term("t_ST", T_CST) p_term("t_Te", T_STE) p_term("t_te", T_TE) + p_term("t_TE", T_CTE) p_term("t_ti", T_TI) + p_term("t_TI", T_CTI) p_term("t_Ts", T_STS) p_term("t_ts", T_TS) p_term("t_u7", T_U7) diff --git a/src/term.c b/src/term.c index e30506bc42..8ab5f42710 100644 --- a/src/term.c +++ b/src/term.c @@ -1624,6 +1624,7 @@ get_term_entries(int *height, int *width) {KS_CM, "cm"}, {KS_SR, "sr"}, {KS_CRI,"RI"}, {KS_VB, "vb"}, {KS_KS, "ks"}, {KS_KE, "ke"}, {KS_TI, "ti"}, {KS_TE, "te"}, + {KS_CTI, "TI"}, {KS_CTE, "TE"}, {KS_BC, "bc"}, {KS_CSB,"Sb"}, {KS_CSF,"Sf"}, {KS_CAB,"AB"}, {KS_CAF,"AF"}, {KS_LE, "le"}, {KS_ND, "nd"}, {KS_OP, "op"}, {KS_CRV, "RV"}, @@ -3462,6 +3463,7 @@ starttermcap(void) if (full_screen && !termcap_active) { out_str(T_TI); /* start termcap mode */ + out_str(T_CTI); /* start "raw" mode */ out_str(T_KS); /* start "keypad transmit" mode */ out_str(T_BE); /* enable bracketed paste mode */ out_flush(); @@ -3517,6 +3519,7 @@ stoptermcap(void) out_flush(); termcap_active = FALSE; cursor_on(); /* just in case it is still off */ + out_str(T_CTE); /* stop "raw" mode */ out_str(T_TE); /* stop termcap mode */ screen_start(); /* don't know where cursor is now */ out_flush(); diff --git a/src/term.h b/src/term.h index 219a2083d2..0791f460b4 100644 --- a/src/term.h +++ b/src/term.h @@ -65,7 +65,9 @@ enum SpecialKey KS_KS, /* put term in "keypad transmit" mode */ KS_KE, /* out of "keypad transmit" mode */ KS_TI, /* put terminal in termcap mode */ - KS_TE, /* out of termcap mode */ + KS_CTI, /* put terminal in "raw" mode */ + KS_TE, /* end of termcap mode */ + KS_CTE, /* end of "raw" mode */ KS_BC, /* backspace character (cursor left) */ KS_CCS, /* cur is relative to scroll region */ KS_CCO, /* number of colors */ @@ -164,7 +166,9 @@ extern char_u *(term_strings[]); /* current terminal strings */ #define T_KS (TERM_STR(KS_KS)) /* put term in "keypad transmit" mode */ #define T_KE (TERM_STR(KS_KE)) /* out of "keypad transmit" mode */ #define T_TI (TERM_STR(KS_TI)) /* put terminal in termcap mode */ -#define T_TE (TERM_STR(KS_TE)) /* out of termcap mode */ +#define T_CTI (TERM_STR(KS_CTI)) /* put terminal in "raw" mode */ +#define T_TE (TERM_STR(KS_TE)) /* end of termcap mode */ +#define T_CTE (TERM_STR(KS_CTE)) /* end of "raw" mode */ #define T_BC (TERM_STR(KS_BC)) /* backspace character */ #define T_CCS (TERM_STR(KS_CCS)) /* cur is relative to scroll region */ #define T_CCO (TERM_STR(KS_CCO)) /* number of colors */ diff --git a/src/version.c b/src/version.c index 615d24e141..8ad31a09c2 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2144, /**/ 2143, /**/ From 459fd785e4a8d044147a3f83a5fca8748528aa84 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 13 Oct 2019 16:43:39 +0200 Subject: [PATCH 43/67] patch 8.1.2145: cannot map when modifyOtherKeys is enabled Problem: Cannot map when modifyOtherKeys is enabled. Solution: Add the mapping twice, both with modifier and as 0x08. Use only the first one when modifyOtherKeys has been detected. --- src/eval.c | 3 +- src/getchar.c | 5 +- src/globals.h | 4 + src/gui_mac.c | 6 +- src/gui_w32.c | 2 +- src/highlight.c | 3 +- src/if_ole.cpp | 2 +- src/main.c | 2 +- src/map.c | 632 ++++++++++++++++++--------------- src/menu.c | 3 +- src/misc2.c | 57 ++- src/option.c | 2 +- src/proto/misc2.pro | 6 +- src/proto/term.pro | 2 +- src/structs.h | 2 + src/term.c | 45 ++- src/terminal.c | 10 +- src/testdir/test_termcodes.vim | 92 ++++- src/usercmd.c | 2 +- src/version.c | 2 + src/vim.h | 6 + 21 files changed, 543 insertions(+), 345 deletions(-) diff --git a/src/eval.c b/src/eval.c index 0fe8fd34aa..ace1e013f5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3526,7 +3526,8 @@ get_string_tv(char_u **arg, typval_T *rettv, int evaluate) break; /* Special key, e.g.: "\" */ - case '<': extra = trans_special(&p, name, TRUE, TRUE); + case '<': extra = trans_special(&p, name, TRUE, TRUE, + TRUE, NULL); if (extra != 0) { name += extra; diff --git a/src/getchar.c b/src/getchar.c index 0e4e3c3157..475bfca8e1 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -52,7 +52,7 @@ static int typeahead_char = 0; /* typeahead char that's not flushed */ */ static int block_redo = FALSE; -static int KeyNoremap = 0; /* remapping flags */ +static int KeyNoremap = 0; // remapping flags /* * Variables used by vgetorpeek() and flush_buffers(). @@ -1771,7 +1771,7 @@ vgetc(void) if (!no_reduce_keys) { // A modifier was not used for a mapping, apply it to ASCII - // keys. + // keys. Shift would already have been applied. if ((mod_mask & MOD_MASK_CTRL) && ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_'))) @@ -2240,6 +2240,7 @@ handle_mapping( // Skip ":lmap" mappings if keys were mapped. if (mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State) + && !(mp->m_simplified && seenModifyOtherKeys) && ((mp->m_mode & LANGMAP) == 0 || typebuf.tb_maplen == 0)) { #ifdef FEAT_LANGMAP diff --git a/src/globals.h b/src/globals.h index d790c82e78..014fca2ad4 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1002,6 +1002,10 @@ EXTERN int ex_no_reprint INIT(= FALSE); // no need to print after z or p EXTERN int reg_recording INIT(= 0); // register for recording or zero EXTERN int reg_executing INIT(= 0); // register being executed or zero +// Set when a modifyOtherKeys sequence was seen, then simplified mappings will +// no longer be used. +EXTERN int seenModifyOtherKeys INIT(= FALSE); + EXTERN int no_mapping INIT(= FALSE); // currently no mapping allowed EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed EXTERN int allow_keys INIT(= FALSE); // allow key codes when no_mapping diff --git a/src/gui_mac.c b/src/gui_mac.c index b43ed8506f..185cdee28e 100644 --- a/src/gui_mac.c +++ b/src/gui_mac.c @@ -2177,7 +2177,8 @@ gui_mac_unicode_key_event( key_char = simplify_key(key_char, (int *)&vimModifiers); /* Interpret META, include SHIFT, etc. */ - key_char = extract_modifiers(key_char, (int *)&vimModifiers); + key_char = extract_modifiers(key_char, (int *)&vimModifiers, + TRUE, NULL); if (key_char == CSI) key_char = K_CSI; @@ -4772,7 +4773,8 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx) char_u *p_actext; p_actext = menu->actext; - key = find_special_key(&p_actext, &modifiers, FALSE, FALSE, FALSE); + key = find_special_key(&p_actext, &modifiers, FALSE, FALSE, FALSE, + TRUE, NULL); if (*p_actext != 0) key = 0; /* error: trailing text */ /* find_special_key() returns a keycode with as many of the diff --git a/src/gui_w32.c b/src/gui_w32.c index 61b09e1395..ae47873e4e 100644 --- a/src/gui_w32.c +++ b/src/gui_w32.c @@ -850,7 +850,7 @@ _OnSysChar( modifiers &= ~MOD_MASK_SHIFT; /* Interpret the ALT key as making the key META, include SHIFT, etc. */ - ch = extract_modifiers(ch, &modifiers); + ch = extract_modifiers(ch, &modifiers, TRUE, NULL); if (ch == CSI) ch = K_CSI; diff --git a/src/highlight.c b/src/highlight.c index 0fdd93a267..1333362dbd 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -1417,7 +1417,8 @@ do_highlight( */ for (p = arg, off = 0; off < 100 - 6 && *p; ) { - len = trans_special(&p, buf + off, FALSE, FALSE); + len = trans_special(&p, buf + off, FALSE, FALSE, + TRUE, NULL); if (len > 0) // recognized special char off += len; else // copy as normal char diff --git a/src/if_ole.cpp b/src/if_ole.cpp index cb643e5461..34ce232660 100644 --- a/src/if_ole.cpp +++ b/src/if_ole.cpp @@ -330,7 +330,7 @@ CVim::SendKeys(BSTR keys) } /* Translate key codes like */ - str = replace_termcodes((char_u *)buffer, &ptr, FALSE, TRUE, FALSE); + str = replace_termcodes((char_u *)buffer, &ptr, REPTERM_DO_LT, NULL); /* If ptr was set, then a new buffer was allocated, * so we can free the old one. diff --git a/src/main.c b/src/main.c index 33ac89c097..5545cc48a6 100644 --- a/src/main.c +++ b/src/main.c @@ -4339,7 +4339,7 @@ server_to_input_buf(char_u *str) * sequence is recognised - needed for a real backslash. */ p_cpo = (char_u *)"Bk"; - str = replace_termcodes((char_u *)str, &ptr, FALSE, TRUE, FALSE); + str = replace_termcodes((char_u *)str, &ptr, REPTERM_DO_LT, NULL); p_cpo = cpo_save; if (*ptr != NUL) /* trailing CTRL-V results in nothing */ diff --git a/src/map.c b/src/map.c index df8cbedb1f..00f4608b07 100644 --- a/src/map.c +++ b/src/map.c @@ -256,18 +256,15 @@ do_map( char_u *p; int n; int len = 0; // init for GCC - char_u *newstr; int hasarg; int haskey; - int did_it = FALSE; - int did_local = FALSE; - int round; + int do_print; + int keyround; char_u *keys_buf = NULL; + char_u *alt_keys_buf = NULL; char_u *arg_buf = NULL; int retval = 0; int do_backslash; - int hash; - int new_hash; mapblock_T **abbr_table; mapblock_T **map_table; int unique = FALSE; @@ -277,6 +274,7 @@ do_map( #ifdef FEAT_EVAL int expr = FALSE; #endif + int did_simplify = FALSE; int noremap; char_u *orig_rhs; @@ -375,6 +373,7 @@ do_map( rhs = p; hasarg = (*rhs != NUL); haskey = (*keys != NUL); + do_print = !haskey || (maptype != 1 && !hasarg); // check for :unmap without argument if (maptype == 1 && !haskey) @@ -389,373 +388,427 @@ do_map( // replace_termcodes() may move the result to allocated memory, which // needs to be freed later (*keys_buf and *arg_buf). // replace_termcodes() also removes CTRL-Vs and sometimes backslashes. + // If something like is simplified to 0x08 then mark it as simplified + // and also add a n entry with a modifier, which will work when + // modifyOtherKeys is working. if (haskey) - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, special); + { + char_u *new_keys; + int flags = REPTERM_FROM_PART | REPTERM_DO_LT; + + if (special) + flags |= REPTERM_SPECIAL; + new_keys = replace_termcodes(keys, &keys_buf, flags, &did_simplify); + if (did_simplify) + (void)replace_termcodes(keys, &alt_keys_buf, + flags | REPTERM_NO_SIMPLIFY, NULL); + keys = new_keys; + } orig_rhs = rhs; if (hasarg) { if (STRICMP(rhs, "") == 0) // "" means nothing rhs = (char_u *)""; else - rhs = replace_termcodes(rhs, &arg_buf, FALSE, TRUE, special); + rhs = replace_termcodes(rhs, &arg_buf, + REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL); } - // check arguments and translate function keys - if (haskey) + /* + * The following is done twice if we have two versions of keys: + * "alt_keys_buf" is not NULL. + */ + for (keyround = 1; keyround <= 2; ++keyround) { - len = (int)STRLEN(keys); - if (len > MAXMAPLEN) // maximum length of MAXMAPLEN chars + int did_it = FALSE; + int did_local = FALSE; + int round; + int hash; + int new_hash; + + if (keyround == 2) { - retval = 1; - goto theend; + if (alt_keys_buf == NULL) + break; + keys = alt_keys_buf; } + else if (alt_keys_buf != NULL && do_print) + // when printing always use the not-simplified map + keys = alt_keys_buf; - if (abbrev && maptype != 1) + // check arguments and translate function keys + if (haskey) { - // If an abbreviation ends in a keyword character, the - // rest must be all keyword-char or all non-keyword-char. - // Otherwise we won't be able to find the start of it in a - // vi-compatible way. - if (has_mbyte) + len = (int)STRLEN(keys); + if (len > MAXMAPLEN) // maximum length of MAXMAPLEN chars { - int first, last; - int same = -1; - - first = vim_iswordp(keys); - last = first; - p = keys + (*mb_ptr2len)(keys); - n = 1; - while (p < keys + len) - { - ++n; // nr of (multi-byte) chars - last = vim_iswordp(p); // type of last char - if (same == -1 && last != first) - same = n - 1; // count of same char type - p += (*mb_ptr2len)(p); - } - if (last && n > 2 && same >= 0 && same < n - 1) - { - retval = 1; - goto theend; - } + retval = 1; + goto theend; } - else if (vim_iswordc(keys[len - 1])) // ends in keyword char + + if (abbrev && maptype != 1) + { + // If an abbreviation ends in a keyword character, the + // rest must be all keyword-char or all non-keyword-char. + // Otherwise we won't be able to find the start of it in a + // vi-compatible way. + if (has_mbyte) + { + int first, last; + int same = -1; + + first = vim_iswordp(keys); + last = first; + p = keys + (*mb_ptr2len)(keys); + n = 1; + while (p < keys + len) + { + ++n; // nr of (multi-byte) chars + last = vim_iswordp(p); // type of last char + if (same == -1 && last != first) + same = n - 1; // count of same char type + p += (*mb_ptr2len)(p); + } + if (last && n > 2 && same >= 0 && same < n - 1) + { + retval = 1; + goto theend; + } + } + else if (vim_iswordc(keys[len - 1])) + // ends in keyword char for (n = 0; n < len - 2; ++n) if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2])) { retval = 1; goto theend; } - // An abbreviation cannot contain white space. - for (n = 0; n < len; ++n) - if (VIM_ISWHITE(keys[n])) - { - retval = 1; - goto theend; - } + // An abbreviation cannot contain white space. + for (n = 0; n < len; ++n) + if (VIM_ISWHITE(keys[n])) + { + retval = 1; + goto theend; + } + } } - } - if (haskey && hasarg && abbrev) // if we will add an abbreviation - no_abbr = FALSE; // reset flag that indicates there are + if (haskey && hasarg && abbrev) // if we will add an abbreviation + no_abbr = FALSE; // reset flag that indicates there are // no abbreviations - if (!haskey || (maptype != 1 && !hasarg)) - msg_start(); + if (do_print) + msg_start(); - // Check if a new local mapping wasn't already defined globally. - if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1) - { - // need to loop over all global hash lists - for (hash = 0; hash < 256 && !got_int; ++hash) + // Check if a new local mapping wasn't already defined globally. + if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1) { - if (abbrev) + // need to loop over all global hash lists + for (hash = 0; hash < 256 && !got_int; ++hash) { - if (hash != 0) // there is only one abbreviation list - break; - mp = first_abbr; - } - else - mp = maphash[hash]; - for ( ; mp != NULL && !got_int; mp = mp->m_next) - { - // check entries with the same mode - if ((mp->m_mode & mode) != 0 - && mp->m_keylen == len - && unique - && STRNCMP(mp->m_keys, keys, (size_t)len) == 0) + if (abbrev) { - if (abbrev) - semsg(_("E224: global abbreviation already exists for %s"), - mp->m_keys); - else - semsg(_("E225: global mapping already exists for %s"), - mp->m_keys); - retval = 5; - goto theend; + if (hash != 0) // there is only one abbreviation list + break; + mp = first_abbr; + } + else + mp = maphash[hash]; + for ( ; mp != NULL && !got_int; mp = mp->m_next) + { + // check entries with the same mode + if ((mp->m_mode & mode) != 0 + && mp->m_keylen == len + && unique + && STRNCMP(mp->m_keys, keys, (size_t)len) == 0) + { + if (abbrev) + semsg(_( + "E224: global abbreviation already exists for %s"), + mp->m_keys); + else + semsg(_( + "E225: global mapping already exists for %s"), + mp->m_keys); + retval = 5; + goto theend; + } } } } - } - // When listing global mappings, also list buffer-local ones here. - if (map_table != curbuf->b_maphash && !hasarg && maptype != 1) - { - // need to loop over all global hash lists - for (hash = 0; hash < 256 && !got_int; ++hash) + // When listing global mappings, also list buffer-local ones here. + if (map_table != curbuf->b_maphash && !hasarg && maptype != 1) { - if (abbrev) + // need to loop over all global hash lists + for (hash = 0; hash < 256 && !got_int; ++hash) { - if (hash != 0) // there is only one abbreviation list - break; - mp = curbuf->b_first_abbr; - } - else - mp = curbuf->b_maphash[hash]; - for ( ; mp != NULL && !got_int; mp = mp->m_next) - { - // check entries with the same mode - if ((mp->m_mode & mode) != 0) + if (abbrev) { - if (!haskey) // show all entries + if (hash != 0) // there is only one abbreviation list + break; + mp = curbuf->b_first_abbr; + } + else + mp = curbuf->b_maphash[hash]; + for ( ; mp != NULL && !got_int; mp = mp->m_next) + { + // check entries with the same mode + if ((mp->m_mode & mode) != 0) { - showmap(mp, TRUE); - did_local = TRUE; - } - else - { - n = mp->m_keylen; - if (STRNCMP(mp->m_keys, keys, - (size_t)(n < len ? n : len)) == 0) + if (!haskey) // show all entries { showmap(mp, TRUE); did_local = TRUE; } + else + { + n = mp->m_keylen; + if (STRNCMP(mp->m_keys, keys, + (size_t)(n < len ? n : len)) == 0) + { + showmap(mp, TRUE); + did_local = TRUE; + } + } } } } } - } - // Find an entry in the maphash[] list that matches. - // For :unmap we may loop two times: once to try to unmap an entry with a - // matching 'from' part, a second time, if the first fails, to unmap an - // entry with a matching 'to' part. This was done to allow ":ab foo bar" - // to be unmapped by typing ":unab foo", where "foo" will be replaced by - // "bar" because of the abbreviation. - for (round = 0; (round == 0 || maptype == 1) && round <= 1 - && !did_it && !got_int; ++round) - { - // need to loop over all hash lists - for (hash = 0; hash < 256 && !got_int; ++hash) + // Find an entry in the maphash[] list that matches. + // For :unmap we may loop two times: once to try to unmap an entry with + // a matching 'from' part, a second time, if the first fails, to unmap + // an entry with a matching 'to' part. This was done to allow ":ab foo + // bar" to be unmapped by typing ":unab foo", where "foo" will be + // replaced by "bar" because of the abbreviation. + for (round = 0; (round == 0 || maptype == 1) && round <= 1 + && !did_it && !got_int; ++round) { - if (abbrev) - { - if (hash > 0) // there is only one abbreviation list - break; - mpp = abbr_table; - } - else - mpp = &(map_table[hash]); - for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) + // need to loop over all hash lists + for (hash = 0; hash < 256 && !got_int; ++hash) { + if (abbrev) + { + if (hash > 0) // there is only one abbreviation list + break; + mpp = abbr_table; + } + else + mpp = &(map_table[hash]); + for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) + { - if (!(mp->m_mode & mode)) // skip entries with wrong mode - { - mpp = &(mp->m_next); - continue; - } - if (!haskey) // show all entries - { - showmap(mp, map_table != maphash); - did_it = TRUE; - } - else // do we have a match? - { - if (round) // second round: Try unmap "rhs" string + if (!(mp->m_mode & mode)) // skip entries with wrong mode { - n = (int)STRLEN(mp->m_str); - p = mp->m_str; + mpp = &(mp->m_next); + continue; } - else + if (!haskey) // show all entries { - n = mp->m_keylen; - p = mp->m_keys; + showmap(mp, map_table != maphash); + did_it = TRUE; } - if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0) + else // do we have a match? { - if (maptype == 1) // delete entry + if (round) // second round: Try unmap "rhs" string { - // Only accept a full match. For abbreviations we - // ignore trailing space when matching with the - // "lhs", since an abbreviation can't have - // trailing space. - if (n != len && (!abbrev || round || n > len + n = (int)STRLEN(mp->m_str); + p = mp->m_str; + } + else + { + n = mp->m_keylen; + p = mp->m_keys; + } + if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0) + { + if (maptype == 1) + { + // Delete entry. + // Only accept a full match. For abbreviations + // we ignore trailing space when matching with + // the "lhs", since an abbreviation can't have + // trailing space. + if (n != len && (!abbrev || round || n > len || *skipwhite(keys + n) != NUL)) + { + mpp = &(mp->m_next); + continue; + } + // We reset the indicated mode bits. If nothing + // is left the entry is deleted below. + mp->m_mode &= ~mode; + did_it = TRUE; // remember we did something + } + else if (!hasarg) // show matching entry + { + showmap(mp, map_table != maphash); + did_it = TRUE; + } + else if (n != len) // new entry is ambiguous { mpp = &(mp->m_next); continue; } - // We reset the indicated mode bits. If nothing is - // left the entry is deleted below. - mp->m_mode &= ~mode; - did_it = TRUE; // remember we did something - } - else if (!hasarg) // show matching entry - { - showmap(mp, map_table != maphash); - did_it = TRUE; - } - else if (n != len) // new entry is ambiguous - { - mpp = &(mp->m_next); - continue; - } - else if (unique) - { - if (abbrev) - semsg(_("E226: abbreviation already exists for %s"), - p); - else - semsg(_("E227: mapping already exists for %s"), p); - retval = 5; - goto theend; - } - else // new rhs for existing entry - { - mp->m_mode &= ~mode; // remove mode bits - if (mp->m_mode == 0 && !did_it) // reuse entry + else if (unique) { - newstr = vim_strsave(rhs); - if (newstr == NULL) + if (abbrev) + semsg(_( + "E226: abbreviation already exists for %s"), + p); + else + semsg(_( + "E227: mapping already exists for %s"), + p); + retval = 5; + goto theend; + } + else + { + // new rhs for existing entry + mp->m_mode &= ~mode; // remove mode bits + if (mp->m_mode == 0 && !did_it) // reuse entry { - retval = 4; // no mem - goto theend; - } - vim_free(mp->m_str); - mp->m_str = newstr; - vim_free(mp->m_orig_str); - mp->m_orig_str = vim_strsave(orig_rhs); - mp->m_noremap = noremap; - mp->m_nowait = nowait; - mp->m_silent = silent; - mp->m_mode = mode; + char_u *newstr = vim_strsave(rhs); + + if (newstr == NULL) + { + retval = 4; // no mem + goto theend; + } + vim_free(mp->m_str); + mp->m_str = newstr; + vim_free(mp->m_orig_str); + mp->m_orig_str = vim_strsave(orig_rhs); + mp->m_noremap = noremap; + mp->m_nowait = nowait; + mp->m_silent = silent; + mp->m_mode = mode; + mp->m_simplified = + did_simplify && keyround == 1; #ifdef FEAT_EVAL - mp->m_expr = expr; - mp->m_script_ctx = current_sctx; - mp->m_script_ctx.sc_lnum += sourcing_lnum; + mp->m_expr = expr; + mp->m_script_ctx = current_sctx; + mp->m_script_ctx.sc_lnum += sourcing_lnum; #endif - did_it = TRUE; + did_it = TRUE; + } + } + if (mp->m_mode == 0) // entry can be deleted + { + map_free(mpp); + continue; // continue with *mpp + } + + // May need to put this entry into another hash + // list. + new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); + if (!abbrev && new_hash != hash) + { + *mpp = mp->m_next; + mp->m_next = map_table[new_hash]; + map_table[new_hash] = mp; + + continue; // continue with *mpp } } - if (mp->m_mode == 0) // entry can be deleted - { - map_free(mpp); - continue; // continue with *mpp - } - - // May need to put this entry into another hash list. - new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); - if (!abbrev && new_hash != hash) - { - *mpp = mp->m_next; - mp->m_next = map_table[new_hash]; - map_table[new_hash] = mp; - - continue; // continue with *mpp - } } + mpp = &(mp->m_next); } - mpp = &(mp->m_next); } } - } - if (maptype == 1) // delete entry - { - if (!did_it) - retval = 2; // no match - else if (*keys == Ctrl_C) + if (maptype == 1) + { + // delete entry + if (!did_it) + retval = 2; // no match + else if (*keys == Ctrl_C) + { + // If CTRL-C has been unmapped, reuse it for Interrupting. + if (map_table == curbuf->b_maphash) + curbuf->b_mapped_ctrl_c &= ~mode; + else + mapped_ctrl_c &= ~mode; + } + continue; + } + + if (!haskey || !hasarg) + { + // print entries + if (!did_it && !did_local) + { + if (abbrev) + msg(_("No abbreviation found")); + else + msg(_("No mapping found")); + } + goto theend; // listing finished + } + + if (did_it) + continue; // have added the new entry already + + // Get here when adding a new entry to the maphash[] list or abbrlist. + mp = ALLOC_ONE(mapblock_T); + if (mp == NULL) + { + retval = 4; // no mem + goto theend; + } + + // If CTRL-C has been mapped, don't always use it for Interrupting. + if (*keys == Ctrl_C) { - // If CTRL-C has been unmapped, reuse it for Interrupting. if (map_table == curbuf->b_maphash) - curbuf->b_mapped_ctrl_c &= ~mode; + curbuf->b_mapped_ctrl_c |= mode; else - mapped_ctrl_c &= ~mode; + mapped_ctrl_c |= mode; } - goto theend; - } - if (!haskey || !hasarg) // print entries - { - if (!did_it && !did_local) + mp->m_keys = vim_strsave(keys); + mp->m_str = vim_strsave(rhs); + mp->m_orig_str = vim_strsave(orig_rhs); + if (mp->m_keys == NULL || mp->m_str == NULL) { - if (abbrev) - msg(_("No abbreviation found")); - else - msg(_("No mapping found")); + vim_free(mp->m_keys); + vim_free(mp->m_str); + vim_free(mp->m_orig_str); + vim_free(mp); + retval = 4; // no mem + goto theend; } - goto theend; // listing finished - } - - if (did_it) // have added the new entry already - goto theend; - - // Get here when adding a new entry to the maphash[] list or abbrlist. - mp = ALLOC_ONE(mapblock_T); - if (mp == NULL) - { - retval = 4; // no mem - goto theend; - } - - // If CTRL-C has been mapped, don't always use it for Interrupting. - if (*keys == Ctrl_C) - { - if (map_table == curbuf->b_maphash) - curbuf->b_mapped_ctrl_c |= mode; - else - mapped_ctrl_c |= mode; - } - - mp->m_keys = vim_strsave(keys); - mp->m_str = vim_strsave(rhs); - mp->m_orig_str = vim_strsave(orig_rhs); - if (mp->m_keys == NULL || mp->m_str == NULL) - { - vim_free(mp->m_keys); - vim_free(mp->m_str); - vim_free(mp->m_orig_str); - vim_free(mp); - retval = 4; // no mem - goto theend; - } - mp->m_keylen = (int)STRLEN(mp->m_keys); - mp->m_noremap = noremap; - mp->m_nowait = nowait; - mp->m_silent = silent; - mp->m_mode = mode; + mp->m_keylen = (int)STRLEN(mp->m_keys); + mp->m_noremap = noremap; + mp->m_nowait = nowait; + mp->m_silent = silent; + mp->m_mode = mode; + mp->m_simplified = did_simplify && keyround == 1; #ifdef FEAT_EVAL - mp->m_expr = expr; - mp->m_script_ctx = current_sctx; - mp->m_script_ctx.sc_lnum += sourcing_lnum; + mp->m_expr = expr; + mp->m_script_ctx = current_sctx; + mp->m_script_ctx.sc_lnum += sourcing_lnum; #endif - // add the new entry in front of the abbrlist or maphash[] list - if (abbrev) - { - mp->m_next = *abbr_table; - *abbr_table = mp; - } - else - { - n = MAP_HASH(mp->m_mode, mp->m_keys[0]); - mp->m_next = map_table[n]; - map_table[n] = mp; + // add the new entry in front of the abbrlist or maphash[] list + if (abbrev) + { + mp->m_next = *abbr_table; + *abbr_table = mp; + } + else + { + n = MAP_HASH(mp->m_mode, mp->m_keys[0]); + mp->m_next = map_table[n]; + map_table[n] = mp; + } } theend: vim_free(keys_buf); + vim_free(alt_keys_buf); vim_free(arg_buf); return retval; } @@ -934,7 +987,7 @@ map_to_exists(char_u *str, char_u *modechars, int abbr) char_u *buf; int retval; - rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE); + rhs = replace_termcodes(str, &buf, REPTERM_DO_LT, NULL); retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr); vim_free(buf); @@ -2036,7 +2089,8 @@ get_maparg(typval_T *argvars, typval_T *rettv, int exact) mode = get_map_mode(&which, 0); - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE); + keys = replace_termcodes(keys, &keys_buf, + REPTERM_FROM_PART | REPTERM_DO_LT, NULL); rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local); vim_free(keys_buf); diff --git a/src/menu.c b/src/menu.c index 4096a0571c..e55cab6758 100644 --- a/src/menu.c +++ b/src/menu.c @@ -372,7 +372,8 @@ ex_menu( else if (modes & MENU_TIP_MODE) map_buf = NULL; /* Menu tips are plain text. */ else - map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE, special); + map_to = replace_termcodes(map_to, &map_buf, + REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL); menuarg.modes = modes; #ifdef FEAT_TOOLBAR menuarg.iconfile = icon; diff --git a/src/misc2.c b/src/misc2.c index 3261437db2..67fa8b5eed 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -2696,12 +2696,15 @@ trans_special( char_u **srcp, char_u *dst, int keycode, // prefer key code, e.g. K_DEL instead of DEL - int in_string) // TRUE when inside a double quoted string + int in_string, // TRUE when inside a double quoted string + int simplify, // simplify and + int *did_simplify) // found or { int modifiers = 0; int key; - key = find_special_key(srcp, &modifiers, keycode, FALSE, in_string); + key = find_special_key(srcp, &modifiers, keycode, FALSE, in_string, + simplify, did_simplify); if (key == 0) return 0; @@ -2753,9 +2756,11 @@ special_to_buf(int key, int modifiers, int keycode, char_u *dst) find_special_key( char_u **srcp, int *modp, - int keycode, /* prefer key code, e.g. K_DEL instead of DEL */ - int keep_x_key, /* don't translate xHome to Home key */ - int in_string) /* TRUE in string, double quote is escaped */ + int keycode, // prefer key code, e.g. K_DEL instead of DEL + int keep_x_key, // don't translate xHome to Home key + int in_string, // TRUE in string, double quote is escaped + int simplify, // simplify and + int *did_simplify) // found or { char_u *last_dash; char_u *end_of_name; @@ -2835,7 +2840,8 @@ find_special_key( && VIM_ISDIGIT(last_dash[6])) { /* or or */ - vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, TRUE); + vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, + &n, 0, TRUE); if (l == 0) { emsg(_(e_invarg)); @@ -2885,11 +2891,10 @@ find_special_key( key = DEL; } - /* - * Normal Key with modifier: Try to make a single byte code. - */ + // Normal Key with modifier: Try to make a single byte code. if (!IS_SPECIAL(key)) - key = extract_modifiers(key, &modifiers); + key = extract_modifiers(key, &modifiers, + simplify, did_simplify); *modp = modifiers; *srcp = end_of_name; @@ -2903,26 +2908,37 @@ find_special_key( /* * Try to include modifiers in the key. * Changes "Shift-a" to 'A', "Alt-A" to 0xc0, etc. + * When "simplify" is FALSE don't do Ctrl and Alt. + * When "simplify" is TRUE and Ctrl or Alt is removed from modifiers set + * "did_simplify" when it's not NULL. */ int -extract_modifiers(int key, int *modp) +extract_modifiers(int key, int *modp, int simplify, int *did_simplify) { int modifiers = *modp; #ifdef MACOS_X - /* Command-key really special, no fancynest */ + // Command-key really special, no fancynest if (!(modifiers & MOD_MASK_CMD)) #endif if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { key = TOUPPER_ASC(key); - modifiers &= ~MOD_MASK_SHIFT; + // With and we keep the shift modifier. + // With and we don't keep the shift modifier. + if (simplify || modifiers == MOD_MASK_SHIFT) + modifiers &= ~MOD_MASK_SHIFT; } - if ((modifiers & MOD_MASK_CTRL) + + // and mean the same thing, always use "H" + if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key)) + key = TOUPPER_ASC(key); + + if (simplify && (modifiers & MOD_MASK_CTRL) #ifdef EBCDIC - /* * TODO: EBCDIC Better use: - * && (Ctrl_chr(key) || key == '?') - * ??? */ + // TODO: EBCDIC Better use: + // && (Ctrl_chr(key) || key == '?') + // ??? && strchr("?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_", key) != NULL #else @@ -2935,16 +2951,21 @@ extract_modifiers(int key, int *modp) /* is */ if (key == 0) key = K_ZERO; + if (did_simplify != NULL) + *did_simplify = TRUE; } + #ifdef MACOS_X /* Command-key really special, no fancynest */ if (!(modifiers & MOD_MASK_CMD)) #endif - if ((modifiers & MOD_MASK_ALT) && key < 0x80 + if (simplify && (modifiers & MOD_MASK_ALT) && key < 0x80 && !enc_dbcs) // avoid creating a lead byte { key |= 0x80; modifiers &= ~MOD_MASK_ALT; /* remove the META modifier */ + if (did_simplify != NULL) + *did_simplify = TRUE; } *modp = modifiers; diff --git a/src/option.c b/src/option.c index 60c1141b6a..898068b416 100644 --- a/src/option.c +++ b/src/option.c @@ -4495,7 +4495,7 @@ find_key_option(char_u *arg_arg, int has_lt) { --arg; /* put arg at the '<' */ modifiers = 0; - key = find_special_key(&arg, &modifiers, TRUE, TRUE, FALSE); + key = find_special_key(&arg, &modifiers, TRUE, TRUE, FALSE, TRUE, NULL); if (modifiers) /* can't handle modifiers here */ key = 0; } diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro index bcdcd3147c..a52b462804 100644 --- a/src/proto/misc2.pro +++ b/src/proto/misc2.pro @@ -67,10 +67,10 @@ void append_ga_line(garray_T *gap); int simplify_key(int key, int *modifiers); int handle_x_keys(int key); char_u *get_special_key_name(int c, int modifiers); -int trans_special(char_u **srcp, char_u *dst, int keycode, int in_string); +int trans_special(char_u **srcp, char_u *dst, int keycode, int in_string, int simplify, int *did_simplify); int special_to_buf(int key, int modifiers, int keycode, char_u *dst); -int find_special_key(char_u **srcp, int *modp, int keycode, int keep_x_key, int in_string); -int extract_modifiers(int key, int *modp); +int find_special_key(char_u **srcp, int *modp, int keycode, int keep_x_key, int in_string, int simplify, int *did_simplify); +int extract_modifiers(int key, int *modp, int simplify, int *did_simplify); int find_special_key_in_table(int c); int get_special_key_code(char_u *name); char_u *get_key_name(int i); diff --git a/src/proto/term.pro b/src/proto/term.pro index a7a051c34a..b29eace07a 100644 --- a/src/proto/term.pro +++ b/src/proto/term.pro @@ -67,7 +67,7 @@ int is_mouse_topline(win_T *wp); int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen); void term_get_fg_color(char_u *r, char_u *g, char_u *b); void term_get_bg_color(char_u *r, char_u *g, char_u *b); -char_u *replace_termcodes(char_u *from, char_u **bufp, int from_part, int do_lt, int special); +char_u *replace_termcodes(char_u *from, char_u **bufp, int flags, int *did_simplify); void show_termcodes(void); int show_one_termcode(char_u *name, char_u *code, int printit); char_u *translate_mapping(char_u *str); diff --git a/src/structs.h b/src/structs.h index 2d7c4fd884..7c0bfce8fd 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1172,6 +1172,8 @@ struct mapblock char_u *m_orig_str; // rhs as entered by the user int m_keylen; // strlen(m_keys) int m_mode; // valid mode + int m_simplified; // m_keys was simplified, do not use this map + // if seenModifyOtherKeys is TRUE int m_noremap; // if non-zero no re-mapping for m_str char m_silent; // used, don't echo commands char m_nowait; // used diff --git a/src/term.c b/src/term.c index 8ab5f42710..2158fe37fe 100644 --- a/src/term.c +++ b/src/term.c @@ -4845,6 +4845,7 @@ not_enough: else if ((arg[0] == 27 && argc == 3 && trail == '~') || (argc == 2 && trail == 'u')) { + seenModifyOtherKeys = TRUE; if (trail == 'u') key = arg[0]; else @@ -4853,13 +4854,20 @@ not_enough: modifiers = decode_modifiers(arg[1]); // Some keys already have Shift included, pass them as - // normal keys. + // normal keys. Not when Ctrl is also used, because + // and are different. if (modifiers == MOD_MASK_SHIFT && ((key >= '@' && key <= 'Z') || key == '^' || key == '_' || (key >= '{' && key <= '~'))) modifiers = 0; + // When used with Ctrl we always make a letter upper case, + // so that mapping and are the same. Typing + // also uses "H" but modifier is different. + if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key)) + key = TOUPPER_ASC(key); + // insert modifiers with KS_MODIFIER new_slen = modifiers2keycode(modifiers, &key, string); slen = csi_len; @@ -5340,18 +5348,26 @@ term_get_bg_color(char_u *r, char_u *g, char_u *b) * pointer to it is returned. If something fails *bufp is set to NULL and from * is returned. * - * CTRL-V characters are removed. When "from_part" is TRUE, a trailing CTRL-V - * is included, otherwise it is removed (for ":map xx ^V", maps xx to - * nothing). When 'cpoptions' does not contain 'B', a backslash can be used - * instead of a CTRL-V. + * CTRL-V characters are removed. When "flags" has REPTERM_FROM_PART, a + * trailing CTRL-V is included, otherwise it is removed (for ":map xx ^V", maps + * xx to nothing). When 'cpoptions' does not contain 'B', a backslash can be + * used instead of a CTRL-V. + * + * Flags: + * REPTERM_FROM_PART see above + * REPTERM_DO_LT also translate + * REPTERM_SPECIAL always accept notation + * REPTERM_NO_SIMPLIFY do not simplify to 0x08 and set 8th bit for + * + * "did_simplify" is set when some or code was simplified, unless + * it is NULL. */ char_u * replace_termcodes( char_u *from, char_u **bufp, - int from_part, - int do_lt, /* also translate */ - int special) /* always accept notation */ + int flags, + int *did_simplify) { int i; int slen; @@ -5364,7 +5380,8 @@ replace_termcodes( char_u *result; /* buffer for resulting string */ do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); - do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL) || special; + do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL) + || (flags & REPTERM_SPECIAL); do_key_code = (vim_strchr(p_cpo, CPO_KEYCODE) == NULL); /* @@ -5383,7 +5400,7 @@ replace_termcodes( /* * Check for #n at start only: function key n */ - if (from_part && src[0] == '#' && VIM_ISDIGIT(src[1])) /* function key */ + if ((flags & REPTERM_FROM_PART) && src[0] == '#' && VIM_ISDIGIT(src[1])) { result[dlen++] = K_SPECIAL; result[dlen++] = 'k'; @@ -5403,7 +5420,8 @@ replace_termcodes( * If 'cpoptions' does not contain '<', check for special key codes, * like "" */ - if (do_special && (do_lt || STRNCMP(src, "", 4) != 0)) + if (do_special && ((flags & REPTERM_DO_LT) + || STRNCMP(src, "", 4) != 0)) { #ifdef FEAT_EVAL /* @@ -5429,7 +5447,8 @@ replace_termcodes( } #endif - slen = trans_special(&src, result + dlen, TRUE, FALSE); + slen = trans_special(&src, result + dlen, TRUE, FALSE, + (flags & REPTERM_NO_SIMPLIFY) == 0, did_simplify); if (slen) { dlen += slen; @@ -5509,7 +5528,7 @@ replace_termcodes( ++src; /* skip CTRL-V or backslash */ if (*src == NUL) { - if (from_part) + if (flags & REPTERM_FROM_PART) result[dlen++] = key; break; } diff --git a/src/terminal.c b/src/terminal.c index a992b444dc..7b2d43bf25 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -772,7 +772,8 @@ ex_terminal(exarg_T *eap) p = skiptowhite(cmd); *p = NUL; - keys = replace_termcodes(ep + 1, &buf, TRUE, TRUE, TRUE); + keys = replace_termcodes(ep + 1, &buf, + REPTERM_FROM_PART | REPTERM_DO_LT | REPTERM_SPECIAL, NULL); opt.jo_set2 |= JO2_EOF_CHARS; opt.jo_eof_chars = vim_strsave(keys); vim_free(buf); @@ -1372,7 +1373,12 @@ term_convert_key(term_T *term, int c, char *buf) } // add modifiers for the typed key - mod |= mod_mask; + if (mod_mask & MOD_MASK_SHIFT) + mod |= VTERM_MOD_SHIFT; + if (mod_mask & MOD_MASK_CTRL) + mod |= VTERM_MOD_CTRL; + if (mod_mask & (MOD_MASK_ALT | MOD_MASK_META)) + mod |= VTERM_MOD_ALT; /* * Convert special keys to vterm keys: diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index 1df9f7bf11..8fe1850ebe 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -862,7 +862,7 @@ endfunc " The mode doesn't need to be enabled, the codes are always detected. func RunTest_modifyOtherKeys(func) new - set timeoutlen=20 + set timeoutlen=10 " Shift-X is send as 'X' with the shift modifier call feedkeys('a' .. a:func('X', 2) .. "\", 'Lx!') @@ -902,11 +902,8 @@ func RunTest_modifyOtherKeys(func) set timeoutlen& endfunc -func Test_modifyOtherKeys_CSI27() +func Test_modifyOtherKeys_basic() call RunTest_modifyOtherKeys(function('GetEscCodeCSI27')) -endfunc - -func Test_modifyOtherKeys_CSIu() call RunTest_modifyOtherKeys(function('GetEscCodeCSIu')) endfunc @@ -928,7 +925,7 @@ endfunc func RunTest_mapping_works_with_shift(func) new - set timeoutlen=20 + set timeoutlen=10 call RunTest_mapping_shift('@', a:func) call RunTest_mapping_shift('A', a:func) @@ -944,7 +941,88 @@ func RunTest_mapping_works_with_shift(func) set timeoutlen& endfunc -func Test_mapping_works_with_shift() +func Test_mapping_works_with_shift_plain() call RunTest_mapping_works_with_shift(function('GetEscCodeCSI27')) call RunTest_mapping_works_with_shift(function('GetEscCodeCSIu')) endfunc + +func RunTest_mapping_mods(map, key, func, code) + call setline(1, '') + exe 'inoremap ' .. a:map .. ' xyz' + call feedkeys('a' .. a:func(a:key, a:code) .. "\", 'Lx!') + call assert_equal("xyz", getline(1)) + exe 'iunmap ' .. a:map +endfunc + +func RunTest_mapping_works_with_mods(func, mods, code) + new + set timeoutlen=10 + + if a:mods !~ 'S' + " Shift by itself has no effect + call RunTest_mapping_mods('<' .. a:mods .. '-@>', '@', a:func, a:code) + endif + call RunTest_mapping_mods('<' .. a:mods .. '-A>', 'A', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-Z>', 'Z', a:func, a:code) + if a:mods !~ 'S' + " with Shift code is always upper case + call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'a', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'z', a:func, a:code) + endif + if a:mods != 'A' + " with Alt code is not in upper case + call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'A', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'Z', a:func, a:code) + endif + call RunTest_mapping_mods('<' .. a:mods .. '-á>', 'á', a:func, a:code) + if a:mods !~ 'S' + " Shift by itself has no effect + call RunTest_mapping_mods('<' .. a:mods .. '-^>', '^', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-_>', '_', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-{>', '{', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-\|>', '|', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-}>', '}', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-~>', '~', a:func, a:code) + endif + + bwipe! + set timeoutlen& +endfunc + +func Test_mapping_works_with_shift() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S', 2) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S', 2) +endfunc + +func Test_mapping_works_with_ctrl() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C', 5) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C', 5) +endfunc + +func Test_mapping_works_with_shift_ctrl() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S', 6) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S', 6) +endfunc + +" Below we also test the "u" code with Alt, This works, but libvterm would not +" send the Alt key like this but by prefixing an Esc. + +func Test_mapping_works_with_alt() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'A', 3) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'A', 3) +endfunc + +func Test_mapping_works_with_shift_alt() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S-A', 4) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S-A', 4) +endfunc + +func Test_mapping_works_with_ctrl_alt() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-A', 7) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-A', 7) +endfunc + +func Test_mapping_works_with_shift_ctrl_alt() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S-A', 8) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S-A', 8) +endfunc diff --git a/src/usercmd.c b/src/usercmd.c index c6bf7fe343..b48f157d62 100644 --- a/src/usercmd.c +++ b/src/usercmd.c @@ -868,7 +868,7 @@ uc_add_command( char_u *rep_buf = NULL; garray_T *gap; - replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE); + replace_termcodes(rep, &rep_buf, 0, NULL); if (rep_buf == NULL) { // Can't replace termcodes - try using the string as is diff --git a/src/version.c b/src/version.c index 8ad31a09c2..18509fd0da 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2145, /**/ 2144, /**/ diff --git a/src/vim.h b/src/vim.h index 1d479c6925..5a977a8afe 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2633,4 +2633,10 @@ long elapsed(DWORD start_tick); #define CLIP_ZINDEX 32000 +// Flags for replace_termcodes() +#define REPTERM_FROM_PART 1 +#define REPTERM_DO_LT 2 +#define REPTERM_SPECIAL 4 +#define REPTERM_NO_SIMPLIFY 8 + #endif // VIM__H From 1e7b52ad8a5e34b9e7a2d12a09765d76d446cd5a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 13 Oct 2019 16:59:08 +0200 Subject: [PATCH 44/67] patch 8.1.2146: build failure Problem: Build failure. Solution: Include omitted changed file. --- src/optionstr.c | 3 ++- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/optionstr.c b/src/optionstr.c index 925ea592dd..a5b5b55a57 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -1892,7 +1892,8 @@ did_set_string_option( { if (*p_pt) { - (void)replace_termcodes(p_pt, &p, TRUE, TRUE, FALSE); + (void)replace_termcodes(p_pt, &p, + REPTERM_FROM_PART | REPTERM_DO_LT, NULL); if (p != NULL) { if (new_value_alloced) diff --git a/src/version.c b/src/version.c index 18509fd0da..a6a9439fe8 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2146, /**/ 2145, /**/ From 53efb18530cc3940d7af2ea338947783ea5495ed Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 13 Oct 2019 19:49:26 +0200 Subject: [PATCH 45/67] patch 8.1.2147: crash when allocating memory fails Problem: Crash when allocating memory fails. (Zu-Ming Jiang) Solution: Check that 'spellcapcheck' is not NULL. (closes #5048) --- src/spell.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/spell.c b/src/spell.c index 607bb392e5..04e4af601f 100644 --- a/src/spell.c +++ b/src/spell.c @@ -4367,7 +4367,7 @@ compile_cap_prog(synblock_T *synblock) regprog_T *rp = synblock->b_cap_prog; char_u *re; - if (*synblock->b_p_spc == NUL) + if (synblock->b_p_spc == NULL || *synblock->b_p_spc == NUL) synblock->b_cap_prog = NULL; else { diff --git a/src/version.c b/src/version.c index a6a9439fe8..3dc64cc08d 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2147, /**/ 2146, /**/ From 6aa7523b9642a752ab879131d4b159635207d9f2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 13 Oct 2019 21:01:34 +0200 Subject: [PATCH 46/67] patch 8.1.2148: no test for right click extending Visual area Problem: No test for right click extending Visual area. Solution: Add a test. (Dominique Pelle, closes #5018) --- src/testdir/test_termcodes.vim | 87 +++++++++++++++++++++++++++++++++- src/version.c | 2 + 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index 8fe1850ebe..1a10d62577 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -72,6 +72,14 @@ func MouseMiddleClick(row, col) endif endfunc +func MouseRightClick(row, col) + if &ttymouse ==# 'dec' + call DecEscapeCode(6, 1, a:row, a:col) + else + call TerminalEscapeCode(2, a:row, a:col, 'M') + endif +endfunc + func MouseCtrlLeftClick(row, col) let ctrl = 0x10 call TerminalEscapeCode(0 + ctrl, a:row, a:col, 'M') @@ -101,7 +109,11 @@ func MouseMiddleRelease(row, col) endfunc func MouseRightRelease(row, col) - call TerminalEscapeCode(3, a:row, a:col, 'm') + if &ttymouse ==# 'dec' + call DecEscapeCode(7, 0, a:row, a:col) + else + call TerminalEscapeCode(3, a:row, a:col, 'm') + endif endfunc func MouseLeftDrag(row, col) @@ -148,6 +160,79 @@ func Test_term_mouse_left_click() bwipe! endfunc +func Test_xterm_mouse_right_click_extends_visual() + if has('mac') + throw "Skipped: test right click in visual mode does not work on macOs (why?)" + endif + let save_mouse = &mouse + let save_term = &term + let save_ttymouse = &ttymouse + call test_override('no_query_mouse', 1) + set mouse=a term=xterm + + for visual_mode in ["v", "V", "\"] + for ttymouse_val in s:ttymouse_values + s:ttymouse_dec + let msg = 'visual=' .. visual_mode .. ' ttymouse=' .. ttymouse_val + exe 'set ttymouse=' .. ttymouse_val + + call setline(1, repeat([repeat('-', 7)], 7)) + call MouseLeftClick(4, 4) + call MouseLeftRelease(4, 4) + exe "norm! " .. visual_mode + + " Right click extends top left of visual area. + call MouseRightClick(2, 2) + call MouseRightRelease(2, 2) + + " Right click extends bottom bottom right of visual area. + call MouseRightClick(6, 6) + call MouseRightRelease(6, 6) + norm! r1gv + + " Right click shrinks top left of visual area. + call MouseRightClick(3, 3) + call MouseRightRelease(3, 3) + + " Right click shrinks bottom right of visual area. + call MouseRightClick(5, 5) + call MouseRightRelease(5, 5) + norm! r2 + + if visual_mode ==# 'v' + call assert_equal(['-------', + \ '-111111', + \ '1122222', + \ '2222222', + \ '2222211', + \ '111111-', + \ '-------'], getline(1, '$'), msg) + elseif visual_mode ==# 'V' + call assert_equal(['-------', + \ '1111111', + \ '2222222', + \ '2222222', + \ '2222222', + \ '1111111', + \ '-------'], getline(1, '$'), msg) + else + call assert_equal(['-------', + \ '-11111-', + \ '-12221-', + \ '-12221-', + \ '-12221-', + \ '-11111-', + \ '-------'], getline(1, '$'), msg) + endif + endfor + endfor + + let &mouse = save_mouse + let &term = save_term + let &ttymouse = save_ttymouse + call test_override('no_query_mouse', 0) + bwipe! +endfunc + " Test that jumps to help tag and jumps back. func Test_xterm_mouse_ctrl_click() let save_mouse = &mouse diff --git a/src/version.c b/src/version.c index 3dc64cc08d..cf6c773719 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2148, /**/ 2147, /**/ From e3a22cb1ba057381be3e645479a537f8032f119f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 14 Oct 2019 22:01:57 +0200 Subject: [PATCH 47/67] patch 8.1.2149: crash when running out of memory very early Problem: Crash when running out of memory very early. Solution: Do not use IObuff when it's NULL. (closes #5052) --- src/message.c | 112 +++++++++++++++++++++++++++++++++++++------------- src/version.c | 2 + 2 files changed, 85 insertions(+), 29 deletions(-) diff --git a/src/message.c b/src/message.c index 19defaa1f8..fddb28e2b8 100644 --- a/src/message.c +++ b/src/message.c @@ -356,34 +356,61 @@ int vim_snprintf(char *str, size_t str_m, const char *fmt, ...); int smsg(const char *s, ...) { - va_list arglist; + if (IObuff == NULL) + { + // Very early in initialisation and already something wrong, just + // give the raw message so the user at least gets a hint. + return msg((char *)s); + } + else + { + va_list arglist; - va_start(arglist, s); - vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist); - va_end(arglist); - return msg((char *)IObuff); + va_start(arglist, s); + vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist); + va_end(arglist); + return msg((char *)IObuff); + } } int smsg_attr(int attr, const char *s, ...) { - va_list arglist; + if (IObuff == NULL) + { + // Very early in initialisation and already something wrong, just + // give the raw message so the user at least gets a hint. + return msg_attr((char *)s, attr); + } + else + { + va_list arglist; - va_start(arglist, s); - vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist); - va_end(arglist); - return msg_attr((char *)IObuff, attr); + va_start(arglist, s); + vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist); + va_end(arglist); + return msg_attr((char *)IObuff, attr); + } } int smsg_attr_keep(int attr, const char *s, ...) { - va_list arglist; + if (IObuff == NULL) + { + // Very early in initialisation and already something wrong, just + // give the raw message so the user at least gets a hint. + return msg_attr_keep((char *)s, attr, TRUE); + } + else + { + va_list arglist; - va_start(arglist, s); - vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist); - va_end(arglist); - return msg_attr_keep((char *)IObuff, attr, TRUE); + va_start(arglist, s); + vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist); + va_end(arglist); + return msg_attr_keep((char *)IObuff, attr, TRUE); + } } #endif @@ -723,17 +750,26 @@ emsg(char *s) int semsg(const char *s, ...) { - /* Skip this if not giving error messages at the moment. */ + // Skip this if not giving error messages at the moment. if (!emsg_not_now()) { - va_list ap; + if (IObuff == NULL) + { + // Very early in initialisation and already something wrong, just + // give the raw message so the user at least gets a hint. + return emsg_core((char_u *)s); + } + else + { + va_list ap; - va_start(ap, s); - vim_vsnprintf((char *)IObuff, IOSIZE, s, ap); - va_end(ap); - return emsg_core(IObuff); + va_start(ap, s); + vim_vsnprintf((char *)IObuff, IOSIZE, s, ap); + va_end(ap); + return emsg_core(IObuff); + } } - return TRUE; /* no error messages at the moment */ + return TRUE; // no error messages at the moment } #endif @@ -764,12 +800,21 @@ siemsg(const char *s, ...) { if (!emsg_not_now()) { - va_list ap; + if (IObuff == NULL) + { + // Very early in initialisation and already something wrong, just + // give the raw message so the user at least gets a hint. + emsg_core((char_u *)s); + } + else + { + va_list ap; - va_start(ap, s); - vim_vsnprintf((char *)IObuff, IOSIZE, s, ap); - va_end(ap); - emsg_core(IObuff); + va_start(ap, s); + vim_vsnprintf((char *)IObuff, IOSIZE, s, ap); + va_end(ap); + emsg_core(IObuff); + } } # ifdef ABORT_ON_INTERNAL_ERROR abort(); @@ -3506,8 +3551,17 @@ give_warning(char_u *message, int hl) void give_warning2(char_u *message, char_u *a1, int hl) { - vim_snprintf((char *)IObuff, IOSIZE, (char *)message, a1); - give_warning(IObuff, hl); + if (IObuff == NULL) + { + // Very early in initialisation and already something wrong, just give + // the raw message so the user at least gets a hint. + give_warning((char_u *)message, hl); + } + else + { + vim_snprintf((char *)IObuff, IOSIZE, (char *)message, a1); + give_warning(IObuff, hl); + } } #endif diff --git a/src/version.c b/src/version.c index cf6c773719..3fc889e91d 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2149, /**/ 2148, /**/ From 03b00476eed43dbc25222549c00b3af28e79b350 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 14 Oct 2019 22:22:03 +0200 Subject: [PATCH 48/67] patch 8.1.2150: no test for 'ttymouse' set from xterm version response Problem: No test for 'ttymouse' set from xterm version response. Solution: Test the three possible values. --- src/testdir/test_termcodes.vim | 39 ++++++++++++++++++++++++++++++++-- src/version.c | 2 ++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index 1a10d62577..0a4170e38b 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -885,13 +885,12 @@ endfunc " This checks the libvterm version response. " This must be after other tests, because it has side effects to xterm " properties. -" TODO: check other terminals response func Test_xx02_libvterm_response() " Termresponse is only parsed when t_RV is not empty. set t_RV=x + set ttymouse=xterm call test_option_not_set('ttymouse') - let seq = "\[>0;100;0c" call feedkeys(seq, 'Lx!') call assert_equal(seq, v:termresponse) @@ -900,6 +899,42 @@ func Test_xx02_libvterm_response() set t_RV= endfunc +" This checks the xterm version response. +" This must be after other tests, because it has side effects to xterm +" properties. +func Test_xx03_xterm_response() + " Termresponse is only parsed when t_RV is not empty. + set t_RV=x + + " xterm < 95: "xterm" (actually unmodified) + set ttymouse=xterm + call test_option_not_set('ttymouse') + let seq = "\[>0;94;0c" + call feedkeys(seq, 'Lx!') + call assert_equal(seq, v:termresponse) + call assert_equal('xterm', &ttymouse) + + " xterm >= 95 < 277 "xterm2" + set ttymouse=xterm + call test_option_not_set('ttymouse') + let seq = "\[>0;267;0c" + call feedkeys(seq, 'Lx!') + call assert_equal(seq, v:termresponse) + call assert_equal('xterm2', &ttymouse) + + " xterm >= 277: "sgr" + set ttymouse=xterm + call test_option_not_set('ttymouse') + let seq = "\[>0;277;0c" + call feedkeys(seq, 'Lx!') + call assert_equal(seq, v:termresponse) + call assert_equal('sgr', &ttymouse) + + set t_RV= +endfunc + +" TODO: check other terminals response + func Test_get_termcode() try let k1 = &t_k1 diff --git a/src/version.c b/src/version.c index 3fc889e91d..a8dc708142 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2150, /**/ 2149, /**/ From 3c8cd4a1dcbc34d8818a2a38b1d1e4755da9edc2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 14 Oct 2019 22:26:20 +0200 Subject: [PATCH 49/67] patch 8.1.2151: state test is a bit flaky Problem: State test is a bit flaky. Solution: Add to the list of flaky tests. --- src/testdir/runtest.vim | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/testdir/runtest.vim b/src/testdir/runtest.vim index e1ea7f1e68..a8eb5e23b0 100644 --- a/src/testdir/runtest.vim +++ b/src/testdir/runtest.vim @@ -341,6 +341,7 @@ let s:flaky_tests = [ \ 'Test_raw_one_time_callback()', \ 'Test_reltime()', \ 'Test_server_crash()', + \ 'Test_state()', \ 'Test_term_mouse_double_click_to_create_tab()', \ 'Test_terminal_ansicolors_default()', \ 'Test_terminal_ansicolors_func()', diff --git a/src/version.c b/src/version.c index a8dc708142..c1c3442164 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2151, /**/ 2150, /**/ From 27fc8cab227e30f649f52e74efd58ad56d21e9bb Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 15 Oct 2019 22:23:37 +0200 Subject: [PATCH 50/67] patch 8.1.2152: problems navigating tags file on MacOS Catalina Problem: Problems navigating tags file on MacOS Catalina. Solution: Use fseek instead of lseek. (John Lamb, fixes #5061) --- src/tag.c | 22 +++++++++++----------- src/version.c | 2 ++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/tag.c b/src/tag.c index 284f2c1a02..75ff026c55 100644 --- a/src/tag.c +++ b/src/tag.c @@ -2198,23 +2198,23 @@ line_read_in: #endif #ifdef FEAT_TAG_BINS - /* - * When starting a binary search, get the size of the file and - * compute the first offset. - */ + // When starting a binary search, get the size of the file and + // compute the first offset. if (state == TS_BINARY) { - /* Get the tag file size (don't use mch_fstat(), it's not - * portable). */ - if ((filesize = vim_lseek(fileno(fp), - (off_T)0L, SEEK_END)) <= 0) + if (vim_fseek(fp, 0L, SEEK_END) != 0) + // can't seek, don't use binary search state = TS_LINEAR; else { - vim_lseek(fileno(fp), (off_T)0L, SEEK_SET); + // Get the tag file size (don't use mch_fstat(), it's + // not portable). Don't use lseek(), it doesn't work + // properly on MacOS Catalina. + filesize = vim_ftell(fp); + vim_fseek(fp, 0L, SEEK_SET); - /* Calculate the first read offset in the file. Start - * the search in the middle of the file. */ + // Calculate the first read offset in the file. Start + // the search in the middle of the file. search_info.low_offset = 0; search_info.low_char = 0; search_info.high_offset = filesize; diff --git a/src/version.c b/src/version.c index c1c3442164..e21f5c468b 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2152, /**/ 2151, /**/ From 3439028c8909aaa71ffe612a7191babdfe07c04c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 14:38:26 +0200 Subject: [PATCH 51/67] patch 8.1.2153: combining text property and syntax highlight is wrong Problem: Combining text property and syntax highlight is wrong. (Nick Jensen) Solution: Compute the syntax highlight attribute much earlier. (closes #5057) --- src/drawline.c | 174 +++++++++------------ src/testdir/dumps/Test_textprop_syn_1.dump | 6 + src/testdir/test_textprop.vim | 35 ++++- src/version.c | 2 + 4 files changed, 110 insertions(+), 107 deletions(-) create mode 100644 src/testdir/dumps/Test_textprop_syn_1.dump diff --git a/src/drawline.c b/src/drawline.c index ea7764ea62..4477b3f0c4 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -307,6 +307,7 @@ win_line( #endif #ifdef FEAT_SPELL int has_spell = FALSE; // this buffer has spell checking + int can_spell; # define SPWORDLEN 150 char_u nextline[SPWORDLEN * 2];// text with start of the next line int nextlinecol = 0; // column where nextline[] starts @@ -747,6 +748,9 @@ win_line( win_attr = wcr_attr; area_highlighting = TRUE; } + if (vi_attr != 0 && win_attr != 0) + vi_attr = hl_combine_attr(win_attr, vi_attr); + #ifdef FEAT_TEXT_PROP if (WIN_IS_POPUP(wp)) screen_line_flags |= SLF_POPUP; @@ -1281,11 +1285,7 @@ win_line( break; } - if (draw_state == WL_LINE && (area_highlighting -#ifdef FEAT_SPELL - || has_spell -#endif - )) + if (draw_state == WL_LINE && (area_highlighting || extra_check)) { // handle Visual or match highlighting in this line if (vcol == fromcol @@ -1397,6 +1397,70 @@ win_line( } #endif + if (extra_check) + { +#ifdef FEAT_TERMINAL + if (get_term_attr) + { + syntax_attr = term_get_attr(wp->w_buffer, lnum, vcol); + + if (!attr_pri) + char_attr = syntax_attr; + else + char_attr = hl_combine_attr(syntax_attr, char_attr); + } +#endif + +#ifdef FEAT_SYN_HL + // Get syntax attribute. + if (has_syntax) + { + // Get the syntax attribute for the character. If there + // is an error, disable syntax highlighting. + save_did_emsg = did_emsg; + did_emsg = FALSE; + + v = (long)(ptr - line); + can_spell = TRUE; + syntax_attr = get_syntax_attr((colnr_T)v, +# ifdef FEAT_SPELL + has_spell ? &can_spell : +# endif + NULL, FALSE); + + // combine syntax attribute with 'wincolor' + if (syntax_attr != 0 && win_attr != 0) + syntax_attr = hl_combine_attr(win_attr, syntax_attr); + + if (did_emsg) + { + wp->w_s->b_syn_error = TRUE; + has_syntax = FALSE; + syntax_attr = 0; + } + else + did_emsg = save_did_emsg; +# ifdef SYN_TIME_LIMIT + if (wp->w_s->b_syn_slow) + has_syntax = FALSE; +# endif + + // Need to get the line again, a multi-line regexp may + // have made it invalid. + line = ml_get_buf(wp->w_buffer, lnum, FALSE); + ptr = line + v; +# ifdef FEAT_CONCEAL + // no concealing past the end of the line, it interferes + // with line highlighting + if (*ptr == NUL) + syntax_flags = 0; + else + syntax_flags = get_syntax_info(&syntax_seqnr); +# endif + } +#endif + } + // Decide which of the highlight attributes to use. attr_pri = TRUE; #ifdef LINE_ATTR @@ -1420,7 +1484,12 @@ win_line( { // Use line_attr when not in the Visual or 'incsearch' area // (area_attr may be 0 when "noinvcur" is set). - char_attr = line_attr; +# ifdef FEAT_SYN_HL + if (has_syntax) + char_attr = hl_combine_attr(syntax_attr, line_attr); + else +# endif + char_attr = line_attr; attr_pri = FALSE; } #else @@ -1741,99 +1810,6 @@ win_line( if (extra_check) { -#ifdef FEAT_SPELL - int can_spell = TRUE; -#endif - -#ifdef FEAT_TERMINAL - if (get_term_attr) - { - syntax_attr = term_get_attr(wp->w_buffer, lnum, vcol); - - if (!attr_pri) - char_attr = syntax_attr; - else - char_attr = hl_combine_attr(syntax_attr, char_attr); - } -#endif - -#ifdef FEAT_SYN_HL - // Get syntax attribute, unless still at the start of the line - // (double-wide char that doesn't fit). - v = (long)(ptr - line); - if (has_syntax && v > 0) - { - // Get the syntax attribute for the character. If there - // is an error, disable syntax highlighting. - save_did_emsg = did_emsg; - did_emsg = FALSE; - - syntax_attr = get_syntax_attr((colnr_T)v - 1, -# ifdef FEAT_SPELL - has_spell ? &can_spell : -# endif - NULL, FALSE); - - if (did_emsg) - { - wp->w_s->b_syn_error = TRUE; - has_syntax = FALSE; - syntax_attr = 0; - } - else - did_emsg = save_did_emsg; - - // combine syntax attribute with 'wincolor' - if (win_attr != 0) - syntax_attr = hl_combine_attr(win_attr, syntax_attr); - -# ifdef SYN_TIME_LIMIT - if (wp->w_s->b_syn_slow) - has_syntax = FALSE; -# endif - - // Need to get the line again, a multi-line regexp may - // have made it invalid. - line = ml_get_buf(wp->w_buffer, lnum, FALSE); - ptr = line + v; - -# ifdef FEAT_TEXT_PROP - // Text properties overrule syntax highlighting or combine. - if (text_prop_attr == 0 || text_prop_combine) -# endif - { - int comb_attr = syntax_attr; -# ifdef FEAT_TEXT_PROP - comb_attr = hl_combine_attr(text_prop_attr, comb_attr); -# endif - if (!attr_pri) - { -#ifdef FEAT_SYN_HL - if (cul_attr) - char_attr = hl_combine_attr( - comb_attr, cul_attr); - else -#endif - if (line_attr) - char_attr = hl_combine_attr( - comb_attr, line_attr); - else - char_attr = comb_attr; - } - else - char_attr = hl_combine_attr(comb_attr, char_attr); - } -# ifdef FEAT_CONCEAL - // no concealing past the end of the line, it interferes - // with line highlighting - if (c == NUL) - syntax_flags = 0; - else - syntax_flags = get_syntax_info(&syntax_seqnr); -# endif - } -#endif - #ifdef FEAT_SPELL // Check spelling (unless at the end of the line). // Only do this when there is no syntax highlighting, the diff --git a/src/testdir/dumps/Test_textprop_syn_1.dump b/src/testdir/dumps/Test_textprop_syn_1.dump new file mode 100644 index 0000000000..6f5aff523f --- /dev/null +++ b/src/testdir/dumps/Test_textprop_syn_1.dump @@ -0,0 +1,6 @@ +>(+0(ffff15|a+0#e000e06#ffffff0|b|c|)+0#0000000#40ffff15| +0&#ffffff0@69 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|1|,|1| @10|A|l@1| diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index aede59b2d6..54cf5f3c7d 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -652,9 +652,10 @@ endfunc " screenshot test with textprop highlighting func Test_textprop_screenshot_various() + CheckScreendump " The Vim running in the terminal needs to use utf-8. - if !CanRunVimInTerminal() || g:orig_encoding != 'utf-8' - throw 'Skipped: cannot make screendumps or not using utf-8' + if g:orig_encoding != 'utf-8' + throw 'Skipped: not using utf-8' endif call writefile([ \ "call setline(1, [" @@ -750,9 +751,7 @@ endfunc " screenshot test with Visual block mode operations func Test_textprop_screenshot_visual() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump " Delete two columns while text props are three chars wide. call RunTestVisualBlock(2, '01') @@ -762,9 +761,7 @@ func Test_textprop_screenshot_visual() endfunc func Test_textprop_after_tab() - if !CanRunVimInTerminal() - throw 'Skipped: cannot make screendumps' - endif + CheckScreendump let lines =<< trim END call setline(1, [ @@ -785,6 +782,28 @@ func Test_textprop_after_tab() call delete('XtestPropTab') endfunc +func Test_textprop_with_syntax() + CheckScreendump + + let lines =<< trim END + call setline(1, [ + \ "(abc)", + \ ]) + syn match csParens "[()]" display + hi! link csParens MatchParen + + call prop_type_add('TPTitle', #{ highlight: 'Title' }) + call prop_add(1, 2, #{type: 'TPTitle', end_col: 5}) + END + call writefile(lines, 'XtestPropSyn') + let buf = RunVimInTerminal('-S XtestPropSyn', {'rows': 6}) + call VerifyScreenDump(buf, 'Test_textprop_syn_1', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XtestPropSyn') +endfunc + " Adding a text property to a new buffer should not fail func Test_textprop_empty_buffer() call prop_type_add('comment', {'highlight': 'Search'}) diff --git a/src/version.c b/src/version.c index e21f5c468b..33929149dc 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2153, /**/ 2152, /**/ From 1142a31b8c44c4e7dbf28a83ae52995113b37917 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 14:51:39 +0200 Subject: [PATCH 52/67] patch 8.1.2154: quickfix window height wrong when there is a tabline Problem: Quickfix window height wrong when there is a tabline. (Daniel Hahler) Solution: Take the tabline height into account. (closes #5058) --- src/quickfix.c | 4 ++-- src/testdir/test_quickfix.vim | 12 ++++++++++++ src/version.c | 2 ++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/quickfix.c b/src/quickfix.c index 136c472e18..36456ed4ff 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -4046,8 +4046,8 @@ qf_goto_cwindow(qf_info_T *qi, int resize, int sz, int vertsplit) if (sz != win->w_width) win_setwidth(sz); } - else if (sz != win->w_height - && win->w_height + win->w_status_height < cmdline_row) + else if (sz != win->w_height && win->w_height + + win->w_status_height + tabline_height() < cmdline_row) win_setheight(sz); } diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index e0ab2b3aa2..84cdb6c916 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -297,6 +297,18 @@ func Test_copenHeight() quit endfunc +func Test_copenHeight_tabline() + set tabline=foo showtabline=2 + copen + wincmd H + let height = winheight(0) + copen 10 + call assert_equal(height, winheight(0)) + quit + set tabline& showtabline& +endfunc + + " Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile " commands. func XfileTests(cchar) diff --git a/src/version.c b/src/version.c index 33929149dc..165756a298 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2154, /**/ 2153, /**/ From a3817730c0a38ed6371b38b6db2e08547ecc674d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 16:31:44 +0200 Subject: [PATCH 53/67] patch 8.1.2155: in a terminal window 'cursorlineopt' does not work properly Problem: In a terminal window 'cursorlineopt' does not work properly. Solution: Check the 'cursorlineopt' value. (closes #5055) --- src/drawline.c | 10 ++----- src/testdir/dumps/Test_terminal_normal_1.dump | 8 ++++++ src/testdir/dumps/Test_terminal_normal_2.dump | 8 ++++++ src/testdir/dumps/Test_terminal_normal_3.dump | 8 ++++++ src/testdir/test_terminal.vim | 27 +++++++++++++++++++ src/version.c | 2 ++ 6 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 src/testdir/dumps/Test_terminal_normal_1.dump create mode 100644 src/testdir/dumps/Test_terminal_normal_2.dump create mode 100644 src/testdir/dumps/Test_terminal_normal_3.dump diff --git a/src/drawline.c b/src/drawline.c index 4477b3f0c4..1f02eef2a4 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1401,14 +1401,7 @@ win_line( { #ifdef FEAT_TERMINAL if (get_term_attr) - { syntax_attr = term_get_attr(wp->w_buffer, lnum, vcol); - - if (!attr_pri) - char_attr = syntax_attr; - else - char_attr = hl_combine_attr(syntax_attr, char_attr); - } #endif #ifdef FEAT_SYN_HL @@ -2295,7 +2288,8 @@ win_line( if (win_attr != 0) { char_attr = win_attr; - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) + if (wp->w_p_cul && lnum == wp->w_cursor.lnum + && wp->w_p_culopt_flags != CULOPT_NBR) { if (!cul_screenline || (vcol >= left_curline_col && vcol <= right_curline_col)) diff --git a/src/testdir/dumps/Test_terminal_normal_1.dump b/src/testdir/dumps/Test_terminal_normal_1.dump new file mode 100644 index 0000000000..471419a6d6 --- /dev/null +++ b/src/testdir/dumps/Test_terminal_normal_1.dump @@ -0,0 +1,8 @@ +| +0#af5f00255#ffffff0@1|1| |1+0#0000000&@4| @65 +| +0#af5f00255&@1|2| |1+0#0000000&@3|2| @65 +| +8#af5f00255&@1|3| >1+8#0000000&@3|3| @65 +| +0#af5f00255&@1|4| |1+0#0000000&@3|4| @65 +| +0#af5f00255&@1|5| |1+0#0000000&@3|5| @65 +| +0#af5f00255&@1|6| |1+0#0000000&@3|6| @65 +| +0#af5f00255&@1|7| |1+0#0000000&@3|7| @65 +|:|s|e|t| |n|u|m|b|e|r| |c|u|r|s|o|r|l|i|n|e| |c|u|l|o|p|t|=|b|o|t|h| @22|3|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_terminal_normal_2.dump b/src/testdir/dumps/Test_terminal_normal_2.dump new file mode 100644 index 0000000000..b255d893fc --- /dev/null +++ b/src/testdir/dumps/Test_terminal_normal_2.dump @@ -0,0 +1,8 @@ +| +0#af5f00255#ffffff0@1|1| |1+0#0000000&@4| @65 +| +0#af5f00255&@1|2| |1+0#0000000&@3|2| @65 +| +8#af5f00255&@1|3| >1+0#0000000&@3|3| @65 +| +0#af5f00255&@1|4| |1+0#0000000&@3|4| @65 +| +0#af5f00255&@1|5| |1+0#0000000&@3|5| @65 +| +0#af5f00255&@1|6| |1+0#0000000&@3|6| @65 +| +0#af5f00255&@1|7| |1+0#0000000&@3|7| @65 +|:|s|e|t| |c|u|l|o|p|t|=|n|u|m|b|e|r| @38|3|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_terminal_normal_3.dump b/src/testdir/dumps/Test_terminal_normal_3.dump new file mode 100644 index 0000000000..aa82fe8598 --- /dev/null +++ b/src/testdir/dumps/Test_terminal_normal_3.dump @@ -0,0 +1,8 @@ +| +0#af5f00255#ffffff0@1|1| |1+0#0000000&@4| @65 +| +0#af5f00255&@1|2| |1+0#0000000&@3|2| @65 +| +0#af5f00255&@1|3| >1+8#0000000&@3|3| @65 +| +0#af5f00255&@1|4| |1+0#0000000&@3|4| @65 +| +0#af5f00255&@1|5| |1+0#0000000&@3|5| @65 +| +0#af5f00255&@1|6| |1+0#0000000&@3|6| @65 +| +0#af5f00255&@1|7| |1+0#0000000&@3|7| @65 +|:|s|e|t| |c|u|l|o|p|t|=|l|i|n|e| @40|3|,|1| @10|T|o|p| diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index 7e8ef763b4..383dfedf71 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -1906,6 +1906,33 @@ func Test_terminal_switch_mode() bwipe! endfunc +func Test_terminal_normal_mode() + CheckRunVimInTerminal + + " Run Vim in a terminal and open a terminal window to run Vim in. + let lines =<< trim END + call setline(1, range(11111, 11122)) + 3 + END + call writefile(lines, 'XtermNormal') + let buf = RunVimInTerminal('-S XtermNormal', {'rows': 8}) + call term_wait(buf) + + call term_sendkeys(buf, "\N") + call term_sendkeys(buf, ":set number cursorline culopt=both\r") + call VerifyScreenDump(buf, 'Test_terminal_normal_1', {}) + + call term_sendkeys(buf, ":set culopt=number\r") + call VerifyScreenDump(buf, 'Test_terminal_normal_2', {}) + + call term_sendkeys(buf, ":set culopt=line\r") + call VerifyScreenDump(buf, 'Test_terminal_normal_3', {}) + + call term_sendkeys(buf, "a:q!\:q\:q\") + call StopVimInTerminal(buf) + call delete('XtermNormal') +endfunc + func Test_terminal_hidden_and_close() CheckUnix diff --git a/src/version.c b/src/version.c index 165756a298..6de34d4712 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2155, /**/ 2154, /**/ From 9115c611dbd3e4abab583eade6888421f66077e7 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 16:57:06 +0200 Subject: [PATCH 54/67] patch 8.1.2156: first character after Tab is not highlighted Problem: First character after Tab is not highlighted. Solution: Remember the syntax attribute for a column. --- src/drawline.c | 14 ++++++++++++-- src/testdir/dumps/Test_syntax_c_01.dump | 10 +++++----- src/testdir/test_syntax.vim | 8 ++++---- src/version.c | 2 ++ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/drawline.c b/src/drawline.c index 1f02eef2a4..15761bf22a 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -289,6 +289,8 @@ win_line( #ifdef FEAT_SYN_HL int vcol_save_attr = 0; // saved attr for 'cursorcolumn' int syntax_attr = 0; // attributes desired by syntax + int prev_syntax_col = -1; // column of prev_syntax_attr + int prev_syntax_attr = 0; // syntax_attr at prev_syntax_col int has_syntax = FALSE; // this buffer has syntax highl. int save_did_emsg; int draw_color_col = FALSE; // highlight colorcolumn @@ -1414,12 +1416,20 @@ win_line( did_emsg = FALSE; v = (long)(ptr - line); - can_spell = TRUE; - syntax_attr = get_syntax_attr((colnr_T)v, + if (v == prev_syntax_col) + // at same column again + syntax_attr = prev_syntax_attr; + else + { + can_spell = TRUE; + syntax_attr = get_syntax_attr((colnr_T)v, # ifdef FEAT_SPELL has_spell ? &can_spell : # endif NULL, FALSE); + prev_syntax_col = v; + prev_syntax_attr = syntax_attr; + } // combine syntax attribute with 'wincolor' if (syntax_attr != 0 && win_attr != 0) diff --git a/src/testdir/dumps/Test_syntax_c_01.dump b/src/testdir/dumps/Test_syntax_c_01.dump index 2fea5d6157..54e34a6035 100644 --- a/src/testdir/dumps/Test_syntax_c_01.dump +++ b/src/testdir/dumps/Test_syntax_c_01.dump @@ -12,9 +12,9 @@ |}| @73 @3|s+0#00e0003&|t|a|t|i|c| +0#0000000&|v+0#00e0003&|o|i|d| +0#0000000&@60 |m|y|F|u|n|c|t|i|o|n|(|c+0#00e0003&|o|n|s|t| +0#0000000&|d+0#00e0003&|o|u|b|l|e| +0#0000000&|c|o|u|n|t|,| |s+0#00e0003&|t|r|u|c|t| +0#0000000&|n|o|t|h|i|n|g|,| |l+0#00e0003&|o|n|g| +0#0000000&|t|h|e|r|e|)| |{| @14 -@2|/+0#0000e05&@1| |1+0#e000002&|2|3|:+0#0000e05&| |n|o|t|h|i|n|g| |t|o| |r|e|a|d| |h|e|r|e| +0#0000000&@44 -@2|f+0#af5f00255&|o|r| +0#0000000&|(|i+0#00e0003&|n|t| +0#0000000&|i| |=| |0+0#e000002&|;+0#0000000&| |i| |<| |c|o|u|n|t|;| |+@1|i|)| |{| @39 -@4|b+0#af5f00255&|r|e|a|k|;+0#0000000&| @64 -@2|}| @71 +@1| +0#0000e05&@6|/@1| |1+0#e000002&|2|3|:+0#0000e05&| |n|o|t|h|i|n|g| |t|o| |r|e|a|d| |h|e|r|e| +0#0000000&@38 +@1| +0#af5f00255&@6|f|o|r| +0#0000000&|(|i+0#00e0003&|n|t| +0#0000000&|i| |=| |0+0#e000002&|;+0#0000000&| |i| |<| |c|o|u|n|t|;| |+@1|i|)| |{| @33 +@11|b+0#af5f00255&|r|e|a|k|;+0#0000000&| @57 +@8|}| @65 |}| @73 -|"|X|t|e|s|t|.|c|"| |1|9|L|,| |3|6|4|C| @37|1|,|1| @10|A|l@1| +|"|X|t|e|s|t|.|c|"| |1|9|L|,| |3|6|1|C| @37|1|,|1| @10|A|l@1| diff --git a/src/testdir/test_syntax.vim b/src/testdir/test_syntax.vim index 0479901ce0..c7d6f2db85 100644 --- a/src/testdir/test_syntax.vim +++ b/src/testdir/test_syntax.vim @@ -551,10 +551,10 @@ func Test_syntax_c() \ '}', \ ' static void', \ 'myFunction(const double count, struct nothing, long there) {', - \ ' // 123: nothing to read here', - \ ' for (int i = 0; i < count; ++i) {', - \ ' break;', - \ ' }', + \ "\t// 123: nothing to read here", + \ "\tfor (int i = 0; i < count; ++i) {", + \ "\t break;", + \ "\t}", \ '}', \ ], 'Xtest.c') diff --git a/src/version.c b/src/version.c index 6de34d4712..573b5ccda5 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2156, /**/ 2155, /**/ From 8f7b29f033305fafc237c8e56e4b04172fd62e32 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 17:15:18 +0200 Subject: [PATCH 55/67] patch 8.1.2157: libvterm source files missing from distribution Problem: Libvterm source files missing from distribution. Solution: Rename source files. (closes #5065) --- Filelist | 4 ++-- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Filelist b/Filelist index 92f76213af..e79cbb791e 100644 --- a/Filelist +++ b/Filelist @@ -296,11 +296,11 @@ SRC_ALL = \ src/libvterm/src/encoding/uk.inc \ src/libvterm/src/encoding/uk.tbl \ src/libvterm/src/keyboard.c \ - src/libvterm/src/termmouse.c \ + src/libvterm/src/mouse.c \ src/libvterm/src/parser.c \ src/libvterm/src/pen.c \ src/libvterm/src/rect.h \ - src/libvterm/src/termscreen.c \ + src/libvterm/src/screen.c \ src/libvterm/src/state.c \ src/libvterm/src/unicode.c \ src/libvterm/src/utf8.h \ diff --git a/src/version.c b/src/version.c index 573b5ccda5..f8602f7a66 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2157, /**/ 2156, /**/ From 17efc7fa05daea1e916a25620c71a5626b7f298d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 18:11:31 +0200 Subject: [PATCH 56/67] patch 8.1.2158: terminal attributes missing in Terminal-normal mode Problem: Terminal attributes missing in Terminal-normal mode. Solution: Use "syntax_attr". --- src/drawline.c | 2 +- src/testdir/dumps/Test_terminal_dumpload.dump | 15 +++++++++++++++ src/testdir/test_terminal.vim | 14 ++++++++++++++ src/version.c | 2 ++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/testdir/dumps/Test_terminal_dumpload.dump diff --git a/src/drawline.c b/src/drawline.c index 15761bf22a..d702e9eb8a 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1517,7 +1517,7 @@ win_line( else #endif #ifdef FEAT_SYN_HL - if (has_syntax) + if (has_syntax || get_term_attr) char_attr = syntax_attr; else #endif diff --git a/src/testdir/dumps/Test_terminal_dumpload.dump b/src/testdir/dumps/Test_terminal_dumpload.dump new file mode 100644 index 0000000000..7d2e1b05ee --- /dev/null +++ b/src/testdir/dumps/Test_terminal_dumpload.dump @@ -0,0 +1,15 @@ +>1+0&#ffffff0| @73 +|2| |╔+0fd7ff255|═@11|╗| +0&#ffffff0@5|╔+0&#dadada255|═@11|╗+0a8a8a255| +0&#ffffff0@5|x+0fd7ff255@13| +0&#ffffff0@2|#+0fd7ff255|x@11|#| +0&#ffffff0@1 +|3| |║+0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|║+0#0000000#5fd7ff255| +0&#ffffff0@5|║+0&#a8a8a8255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|║+0#0000000#8a8a8a255| +0&#ffffff0@5|x+0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|x+0#0000000#5fd7ff255| +0&#ffffff0@2|x+0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|x+0#0000000#5fd7ff255| +0&#ffffff0@1 +|4| |╚+0fd7ff255|═@11|╝| +0&#ffffff0@5|║+0&#a8a8a8255|a+0#0000001#ffd7ff255|n|d| |m|o|r|e| @3|║+0#0000000#8a8a8a255| +0&#ffffff0@5|x+0fd7ff255|l+0#0000001#ffd7ff255|i|n|e|s| |o|n|l|y| @1|x+0#0000000#5fd7ff255| +0&#ffffff0@2|x+0fd7ff255|w+0#0000001#ffd7ff255|i|t|h| |c|o|r|n|e|r|s|x+0#0000000#5fd7ff255| +0&#ffffff0@1 +|5| @20|╚+0�|═@11|╝| +0&#ffffff0@5|x+0fd7ff255@13| +0&#ffffff0@2|#+0fd7ff255|x@11|#| +0&#ffffff0@1 +|6| |4+0fd7ff255|0@11|5| +0&#ffffff0@58 +|7| |3+0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r|1+0#0000000#5fd7ff255| +0&#ffffff0@5| +0fd7ff255@13| +0&#ffffff0@38 +|8| |3+0fd7ff255|w+0#0000001#ffd7ff255|i|t|h| |n|u|m|b|e|r|s|1+0#0000000#5fd7ff255| +0&#ffffff0@5| +0fd7ff255|h+0#0000001#ffd7ff255|e|l@1|o| |b|o|r|d|e|r| +0#0000000#5fd7ff255| +0&#ffffff0@38 +|9| |7+0fd7ff255|2@11|6| +0&#ffffff0@5| +0fd7ff255|j+0#0000001#ffd7ff255|u|s|t| |b|l|a|n|k|s| | +0#0000000#5fd7ff255| +0&#ffffff0@38 +|1|0| @19| +0fd7ff255@13| +0&#ffffff0@38 +|1@1| @72 +|d+2#ffffff16#00e0003|u|m|p| |d|i|f@1| |d|u|m|p|s|/|T|e|s|t|_|p|o|p|u|p|w|i|n|_|2@1|.|d|u|m|p| |[|f|i|n|i|s|h|e|d|]| @8|1|,|1| @11|T|o|p +| +0#0000000#ffffff0@74 +|[+1&&|N|o| |N|a|m|e|]| @47|0|,|0|-|1| @9|A|l@1 +| +0&&@74 diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index 383dfedf71..c4d3e528c6 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -1140,6 +1140,20 @@ func Test_terminal_dumpload() quit endfunc +func Test_terminal_dumpload_dump() + CheckRunVimInTerminal + + let lines =<< trim END + call term_dumpload('dumps/Test_popupwin_22.dump', #{term_rows: 12}) + END + call writefile(lines, 'XtermDumpload') + let buf = RunVimInTerminal('-S XtermDumpload', #{rows: 15}) + call VerifyScreenDump(buf, 'Test_terminal_dumpload', {}) + + call StopVimInTerminal(buf) + call delete('XtermDumpload') +endfunc + func Test_terminal_dumpdiff() call assert_equal(1, winnr('$')) eval 'dumps/Test_popup_command_01.dump'->term_dumpdiff('dumps/Test_popup_command_02.dump') diff --git a/src/version.c b/src/version.c index f8602f7a66..2042cb0f40 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2158, /**/ 2157, /**/ From fafb4b18cd4aa5897537f53003b31bb83d7362df Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 18:34:57 +0200 Subject: [PATCH 57/67] patch 8.1.2159: some mappings are listed twice Problem: Some mappings are listed twice. Solution: Skip mappings duplicated for modifyOtherKeys. (closes #5064) --- src/map.c | 19 +++++++++++++------ src/testdir/test_mapping.vim | 16 ++++++++++++++++ src/version.c | 2 ++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/map.c b/src/map.c index 00f4608b07..74fc60c1fc 100644 --- a/src/map.c +++ b/src/map.c @@ -554,7 +554,7 @@ do_map( for ( ; mp != NULL && !got_int; mp = mp->m_next) { // check entries with the same mode - if ((mp->m_mode & mode) != 0) + if (!mp->m_simplified && (mp->m_mode & mode) != 0) { if (!haskey) // show all entries { @@ -599,15 +599,19 @@ do_map( for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) { - if (!(mp->m_mode & mode)) // skip entries with wrong mode + if ((mp->m_mode & mode) == 0) { + // skip entries with wrong mode mpp = &(mp->m_next); continue; } if (!haskey) // show all entries { - showmap(mp, map_table != maphash); - did_it = TRUE; + if (!mp->m_simplified) + { + showmap(mp, map_table != maphash); + did_it = TRUE; + } } else // do we have a match? { @@ -643,8 +647,11 @@ do_map( } else if (!hasarg) // show matching entry { - showmap(mp, map_table != maphash); - did_it = TRUE; + if (!mp->m_simplified) + { + showmap(mp, map_table != maphash); + did_it = TRUE; + } } else if (n != len) // new entry is ambiguous { diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim index 93a4d4b857..f62d5755ec 100644 --- a/src/testdir/test_mapping.vim +++ b/src/testdir/test_mapping.vim @@ -442,3 +442,19 @@ func Test_error_in_map_expr() call delete('Xtest.vim') exe buf .. 'bwipe!' endfunc + +func Test_list_mappings() + inoremap CtrlM + inoremap AltS + inoremap ShiftSlash + call assert_equal([ + \ 'i * ShiftSlash', + \ 'i * AltS', + \ 'i * CtrlM', + \], execute('imap')->trim()->split("\n")) + iunmap + iunmap + call assert_equal(['i * ShiftSlash'], execute('imap')->trim()->split("\n")) + iunmap + call assert_equal(['No mapping found'], execute('imap')->trim()->split("\n")) +endfunc diff --git a/src/version.c b/src/version.c index 2042cb0f40..91cffb330d 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2159, /**/ 2158, /**/ From a6cc5beb1705359828a45b91402c34640f4e756a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 18:42:26 +0200 Subject: [PATCH 58/67] patch 8.1.2160: cannot build with +syntax but without +terminal Problem: Cannot build with +syntax but without +terminal. Solution: Add #ifdef. --- src/drawline.c | 8 ++++++-- src/version.c | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/drawline.c b/src/drawline.c index d702e9eb8a..1415a3177f 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1517,8 +1517,12 @@ win_line( else #endif #ifdef FEAT_SYN_HL - if (has_syntax || get_term_attr) - char_attr = syntax_attr; + if (has_syntax +# ifdef FEAT_TERMINAL + || get_term_attr +# endif + ) + char_attr = syntax_attr; else #endif char_attr = 0; diff --git a/src/version.c b/src/version.c index 91cffb330d..9135fe73c9 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2160, /**/ 2159, /**/ From 4bd88d568a81d37df69dc3cf8cdd8d9dbb4011b7 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 19:27:26 +0200 Subject: [PATCH 59/67] patch 8.1.2161: mapping test fails Problem: Mapping test fails. Solution: Run the test separately. --- src/testdir/Make_all.mak | 1 + src/testdir/test_alot.vim | 1 - src/version.c | 2 ++ 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 68f26de162..c22ce3bd6f 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -375,6 +375,7 @@ NEW_TESTS_RES = \ test_makeencoding.res \ test_man.res \ test_maparg.res \ + test_mapping.res \ test_marks.res \ test_matchadd_conceal.res \ test_memory_usage.res \ diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim index e3ce1e123f..216ff6c7ef 100644 --- a/src/testdir/test_alot.vim +++ b/src/testdir/test_alot.vim @@ -38,7 +38,6 @@ source test_join.vim source test_jumps.vim source test_lambda.vim source test_lispwords.vim -source test_mapping.vim source test_match.vim source test_menu.vim source test_messages.vim diff --git a/src/version.c b/src/version.c index 9135fe73c9..f84340d3cb 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2161, /**/ 2160, /**/ From 4e03933726e3698d962bf7dacdd27f306a4c5086 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 21:13:35 +0200 Subject: [PATCH 60/67] patch 8.1.2162: popup resize test is flaky Problem: Popup resize test is flaky. (Christian Brabandt) Solution: Add the function to the list of flaky tests. --- src/testdir/runtest.vim | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/testdir/runtest.vim b/src/testdir/runtest.vim index a8eb5e23b0..e22557d5a2 100644 --- a/src/testdir/runtest.vim +++ b/src/testdir/runtest.vim @@ -340,6 +340,7 @@ let s:flaky_tests = [ \ 'Test_quotestar()', \ 'Test_raw_one_time_callback()', \ 'Test_reltime()', + \ 'Test_popup_and_window_resize()', \ 'Test_server_crash()', \ 'Test_state()', \ 'Test_term_mouse_double_click_to_create_tab()', diff --git a/src/version.c b/src/version.c index f84340d3cb..347e0d5844 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2162, /**/ 2161, /**/ From b2fe1d676f28af92989a842d4e8708dddf157b3d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 21:33:40 +0200 Subject: [PATCH 61/67] patch 8.1.2163: cannot build with +spell but without +syntax Problem: Cannot build with +spell but without +syntax. Solution: Add #ifdef. (John Marriott) --- src/drawline.c | 2 ++ src/version.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/drawline.c b/src/drawline.c index 1415a3177f..9a4ff7dc5d 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1421,7 +1421,9 @@ win_line( syntax_attr = prev_syntax_attr; else { +# ifdef FEAT_SPELL can_spell = TRUE; +# endif syntax_attr = get_syntax_attr((colnr_T)v, # ifdef FEAT_SPELL has_spell ? &can_spell : diff --git a/src/version.c b/src/version.c index 347e0d5844..cfd54a19cc 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2163, /**/ 2162, /**/ From 7b3d93966709998011e2eb3b84414ff454161b37 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 22:17:07 +0200 Subject: [PATCH 62/67] patch 8.1.2164: stuck when using "j" in a popupwin with popup_filter_menu Problem: Stuck when using "j" in a popupwin with popup_filter_menu if a line wraps. Solution: Check the cursor line is visible. (closes #4577) --- src/popupwin.c | 11 ++++++- src/testdir/dumps/Test_popupwin_wrap_1.dump | 10 +++++++ src/testdir/dumps/Test_popupwin_wrap_2.dump | 10 +++++++ src/testdir/test_popupwin.vim | 32 +++++++++++++++++++++ src/version.c | 2 ++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/testdir/dumps/Test_popupwin_wrap_1.dump create mode 100644 src/testdir/dumps/Test_popupwin_wrap_2.dump diff --git a/src/popupwin.c b/src/popupwin.c index 6b0b383b08..f9c127a6c4 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -535,7 +535,7 @@ check_highlight(dict_T *dict, char *name, char_u **pval) } /* - * Scroll to show the line with the cursor. This assumes lines don't wrap. + * Scroll to show the line with the cursor. */ static void popup_show_curline(win_T *wp) @@ -550,6 +550,11 @@ popup_show_curline(win_T *wp) wp->w_topline = 1; else if (wp->w_topline > wp->w_buffer->b_ml.ml_line_count) wp->w_topline = wp->w_buffer->b_ml.ml_line_count; + while (wp->w_topline < wp->w_cursor.lnum + && wp->w_topline < wp->w_buffer->b_ml.ml_line_count + && plines_m_win(wp, wp->w_topline, wp->w_cursor.lnum) + > wp->w_height) + ++wp->w_topline; } // Don't use "firstline" now. @@ -1041,6 +1046,7 @@ popup_adjust_position(win_T *wp) linenr_T lnum; int wrapped = 0; int maxwidth; + int used_maxwidth = FALSE; int maxspace; int center_vert = FALSE; int center_hor = FALSE; @@ -1208,6 +1214,7 @@ popup_adjust_position(win_T *wp) ++wrapped; len -= maxwidth; wp->w_width = maxwidth; + used_maxwidth = TRUE; } } else if (len > maxwidth @@ -1259,6 +1266,8 @@ popup_adjust_position(win_T *wp) { ++right_extra; ++extra_width; + if (used_maxwidth) + maxwidth -= 2; // try to show the scrollbar } minwidth = wp->w_minwidth; diff --git a/src/testdir/dumps/Test_popupwin_wrap_1.dump b/src/testdir/dumps/Test_popupwin_wrap_1.dump new file mode 100644 index 0000000000..5643dc75c6 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_wrap_1.dump @@ -0,0 +1,10 @@ +>1+0&#ffffff0| @73 +|2| @73 +|╔+0#0000001#ffd7ff255|═@73 +|║| |o+0&#e0e0e08|n|e| @67| +0&#ffd7ff255| +0#0000000#0000001 +|║+0#0000001#ffd7ff255| |a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d| | +0#0000000#0000001 +|║+0#0000001#ffd7ff255| |f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s| @52| +0#0000000#a8a8a8255 +|╚+0#0000001#ffd7ff255|═@73 +|8+0#0000000#ffffff0| @73 +|9| @73 +@57|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_popupwin_wrap_2.dump b/src/testdir/dumps/Test_popupwin_wrap_2.dump new file mode 100644 index 0000000000..3a02fc5c95 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_wrap_2.dump @@ -0,0 +1,10 @@ +>1+0&#ffffff0| @73 +|2| @73 +|╔+0#0000001#ffd7ff255|═@73 +|║| |a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d| | +0#0000000#a8a8a8255 +|║+0#0000001#ffd7ff255| |f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s| @52| +0#0000000#0000001 +|║+0#0000001#ffd7ff255| |t+0&#e0e0e08|h|r|e@1| @65| +0&#ffd7ff255| +0#0000000#0000001 +|╚+0#0000001#ffd7ff255|═@73 +|8+0#0000000#ffffff0| @73 +|9| @73 +@57|1|,|1| @10|T|o|p| diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 771db9a69e..e84527bf7f 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -509,6 +509,38 @@ func Test_popup_close_with_mouse() call delete('XtestPopupClose') endfunction +func Test_popup_menu_wrap() + CheckScreendump + + let lines =<< trim END + call setline(1, range(1, 20)) + call popup_create([ + \ 'one', + \ 'asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfas', + \ 'three', + \ 'four', + \ ], #{ + \ pos: "botleft", + \ border: [], + \ padding: [0,1,0,1], + \ maxheight: 3, + \ cursorline: 1, + \ filter: 'popup_filter_menu', + \ }) + END + call writefile(lines, 'XtestPopupWrap') + let buf = RunVimInTerminal('-S XtestPopupWrap', #{rows: 10}) + call VerifyScreenDump(buf, 'Test_popupwin_wrap_1', {}) + + call term_sendkeys(buf, "jj") + call VerifyScreenDump(buf, 'Test_popupwin_wrap_2', {}) + + " clean up + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) + call delete('XtestPopupWrap') +endfunction + func Test_popup_with_mask() CheckScreendump diff --git a/src/version.c b/src/version.c index cfd54a19cc..f6a3817da6 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2164, /**/ 2163, /**/ From 4f2f61a014e80217a2d6ac476c8f94e250a3d0ff Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 22:27:49 +0200 Subject: [PATCH 63/67] patch 8.1.2165: mapping test fails on Mac Problem: Mapping test fails on Mac. Solution: Remove the default Mac mapping. --- src/testdir/test_mapping.vim | 3 +++ src/version.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim index f62d5755ec..d3ae615411 100644 --- a/src/testdir/test_mapping.vim +++ b/src/testdir/test_mapping.vim @@ -444,6 +444,9 @@ func Test_error_in_map_expr() endfunc func Test_list_mappings() + " Remove default Mac mapping + silent! iunmap + inoremap CtrlM inoremap AltS inoremap ShiftSlash diff --git a/src/version.c b/src/version.c index f6a3817da6..e7ba706b43 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2165, /**/ 2164, /**/ From 2e693a88b24dc6b12883fad78ff2cb9cd4469c98 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 22:35:02 +0200 Subject: [PATCH 64/67] Update runtime files. --- nsis/lang/simpchinese.nsi | 36 ++++----- runtime/defaults.vim | 2 +- runtime/doc/change.txt | 2 +- runtime/doc/eval.txt | 139 +++++++++++++++++++++------------ runtime/doc/if_perl.txt | 10 +-- runtime/doc/if_ruby.txt | 10 +-- runtime/doc/indent.txt | 6 +- runtime/doc/message.txt | 2 +- runtime/doc/options.txt | 9 +-- runtime/doc/pi_netrw.txt | 8 +- runtime/doc/popup.txt | 12 +-- runtime/doc/syntax.txt | 6 +- runtime/doc/tags | 5 +- runtime/doc/terminal.txt | 8 +- runtime/doc/todo.txt | 96 +++++++++++------------ runtime/doc/vi_diff.txt | 5 +- runtime/filetype.vim | 2 +- runtime/indent/testdir/yaml.in | 14 ++++ runtime/indent/testdir/yaml.ok | 14 ++++ runtime/indent/yaml.vim | 4 +- runtime/syntax/named.vim | 4 +- 21 files changed, 229 insertions(+), 165 deletions(-) create mode 100644 runtime/indent/testdir/yaml.in create mode 100644 runtime/indent/testdir/yaml.ok diff --git a/nsis/lang/simpchinese.nsi b/nsis/lang/simpchinese.nsi index 389adc503e..a6875a79cc 100644 --- a/nsis/lang/simpchinese.nsi +++ b/nsis/lang/simpchinese.nsi @@ -5,7 +5,7 @@ # # Locale ID : 2052 # fileencoding : UTF-8 -# Author : Guopeng Wen +# Author : Guopeng Wen, David Liu !insertmacro MUI_LANGUAGE "SimpChinese" @@ -112,7 +112,7 @@ LangString str_desc_plugin ${LANG_SIMPCHINESE} \ LangString str_section_plugin_home ${LANG_SIMPCHINESE} \ "私有插件目录" LangString str_desc_plugin_home ${LANG_SIMPCHINESE} \ - "Create plugin directories in HOME directory." + "在主目录创建私有插件目录。" LangString str_section_plugin_vim ${LANG_SIMPCHINESE} \ "公共插件目录" @@ -141,14 +141,14 @@ LangString str_desc_rm_exe ${LANG_SIMPCHINESE} \ "删除 Vim 的所有执行文件及脚本。" LangString str_ungroup_plugin ${LANG_SIMPCHINESE} \ - "Remove plugin directories" + "移除插件目录" LangString str_desc_rm_plugin ${LANG_SIMPCHINESE} \ - "Remove the plugin directories if they are empty." + "移除插件目录(如果目录为空)。" LangString str_unsection_plugin_home ${LANG_SIMPCHINESE} \ "私有插件目录" LangString str_desc_rm_plugin_home ${LANG_SIMPCHINESE} \ - "Remove the plugin directories from HOME directory." + "从主目录中移除私有插件目录。" LangString str_unsection_plugin_vim ${LANG_SIMPCHINESE} \ "公共插件目录" @@ -241,37 +241,37 @@ LangString str_msg_unregistering ${LANG_SIMPCHINESE} \ LangString str_vimrc_page_title ${LANG_SIMPCHINESE} \ "设置 _vimrc" LangString str_vimrc_page_subtitle ${LANG_SIMPCHINESE} \ - "选择键盘、鼠标和增强选项。" + "选择键盘、鼠标和扩展设置。" LangString str_msg_compat_title ${LANG_SIMPCHINESE} \ "Vi / Vim 行为" LangString str_msg_compat_desc ${LANG_SIMPCHINESE} \ - "&Compatibility and enhancements" + "兼容性与扩展(&B)" LangString str_msg_compat_vi ${LANG_SIMPCHINESE} \ - "Vi compatible" + "原始 Vi" LangString str_msg_compat_vim ${LANG_SIMPCHINESE} \ - "Vim original" + "原始 Vim" LangString str_msg_compat_defaults ${LANG_SIMPCHINESE} \ - "Vim with some enhancements (load defaults.vim)" + "Vim 原始版本和部分扩展 (加载 defaults.vim)" LangString str_msg_compat_all ${LANG_SIMPCHINESE} \ - "Vim with all enhancements (load vimrc_example.vim) (Default)" + "Vim 原始版本和所有扩展 (加载 vimrc_example.vim) (缺省)" LangString str_msg_keymap_title ${LANG_SIMPCHINESE} \ "键盘映射" LangString str_msg_keymap_desc ${LANG_SIMPCHINESE} \ - "&Remap a few keys for Windows (Ctrl-V, Ctrl-C, Ctrl-A, Ctrl-S, Ctrl-F, etc)" + "为 Windows 映射按键(&R) (例如:Ctrl-V, Ctrl-C, Ctrl-A, Ctrl-S, Ctrl-F 等)" LangString str_msg_keymap_default ${LANG_SIMPCHINESE} \ - "Do not remap keys (Default)" + "不映射按键 (缺省)" LangString str_msg_keymap_windows ${LANG_SIMPCHINESE} \ - "Remap a few keys" + "映射一些按键" LangString str_msg_mouse_title ${LANG_SIMPCHINESE} \ "鼠标" LangString str_msg_mouse_desc ${LANG_SIMPCHINESE} \ - "&Behavior of right and left buttons" + "左键和右键行为(&B)" LangString str_msg_mouse_default ${LANG_SIMPCHINESE} \ - "Right: popup menu, Left: visual mode (Default)" + "右键:弹出菜单, 左键:可视化模式 (缺省)" LangString str_msg_mouse_windows ${LANG_SIMPCHINESE} \ - "Right: popup menu, Left: select mode (Windows)" + "右键:弹出菜单, 左键:选择模式 (Windows)" LangString str_msg_mouse_unix ${LANG_SIMPCHINESE} \ - "Right: extends selection, Left: visual mode (Unix)" + "右键: 扩展选择, 左键:可视化模式 (Unix)" diff --git a/runtime/defaults.vim b/runtime/defaults.vim index e8a0ff40fc..255b67b470 100644 --- a/runtime/defaults.vim +++ b/runtime/defaults.vim @@ -1,7 +1,7 @@ " The default vimrc file. " " Maintainer: Bram Moolenaar -" Last change: 2019 Feb 18 +" Last change: 2019 Sep 28 " " This is loaded if no vimrc file was found. " Except when Vim is run with "-u NONE" or "-C". diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 542d97217a..9b8155ca91 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1,4 +1,4 @@ -*change.txt* For Vim version 8.1. Last change: 2019 Sep 27 +*change.txt* For Vim version 8.1. Last change: 2019 Sep 28 VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 927651ce02..09310e627e 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.1. Last change: 2019 Sep 27 +*eval.txt* For Vim version 8.1. Last change: 2019 Oct 13 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2880,7 +2880,7 @@ win_id2tabwin({expr}) List get tab and window nr from window ID win_id2win({expr}) Number get window nr from window ID win_screenpos({nr}) List get screen position of window {nr} win_splitmove({nr}, {target} [, {options}]) - none move window {nr} to split of {target} + Number move window {nr} to split of {target} winbufnr({nr}) Number buffer number of window {nr} wincol() Number window column of the cursor winheight({nr}) Number height of window {nr} @@ -2974,6 +2974,9 @@ append({lnum}, {text}) *append()* appendbufline({expr}, {lnum}, {text}) *appendbufline()* Like |append()| but append the text in buffer {expr}. + This function works only for loaded buffers. First call + |bufload()| if needed. + For the use of {expr}, see |bufname()|. {lnum} is used like with |append()|. Note that using |line()| @@ -3540,8 +3543,8 @@ complete({startcol}, {matches}) *complete()* *E785* < This isn't very useful, but it shows how it works. Note that an empty string is returned to avoid a zero being inserted. - Can also be used as a |method|, the second argument is passed - in: > + Can also be used as a |method|, the base is passed as the + second argument: > GetMatches()->complete(col('.')) complete_add({expr}) *complete_add()* @@ -3676,7 +3679,7 @@ confirm({msg} [, {choices} [, {default} [, {type}]]]) Can also be used as a |method|in: > BuildMessage()->confirm("&Yes\n&No") - +< *copy()* copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't different from using {expr} directly. @@ -3871,6 +3874,9 @@ deletebufline({expr}, {first} [, {last}]) *deletebufline()* If {last} is omitted then delete line {first} only. On success 0 is returned, on failure 1 is returned. + This function works only for loaded buffers. First call + |bufload()| if needed. + For the use of {expr}, see |bufname()| above. {first} and {last} are used like with |getline()|. Note that @@ -3879,7 +3885,7 @@ deletebufline({expr}, {first} [, {last}]) *deletebufline()* Can also be used as a |method|: > GetBuffer()->deletebufline(1) - +< *did_filetype()* did_filetype() Returns |TRUE| when autocommands are being executed and the FileType event has been triggered at least once. Can be used @@ -4040,7 +4046,7 @@ exepath({expr}) *exepath()* Can also be used as a |method|: > GetCommand()->exepath() - +< *exists()* exists({expr}) The result is a Number, which is |TRUE| if {expr} is defined, zero otherwise. @@ -4566,7 +4572,7 @@ foldlevel({lnum}) *foldlevel()* Can also be used as a |method|: > GetLnum()->foldlevel() - +< *foldtext()* foldtext() Returns a String, to be displayed for a closed fold. This is the default function used for the 'foldtext' option and should @@ -5848,7 +5854,7 @@ histadd({history}, {item}) *histadd()* :let date=input("Enter date: ") < This function is not available in the |sandbox|. - Can also be used as a |method|, the base is used for the + Can also be used as a |method|, the base is passed as the second argument: > GetHistory()->histadd('search') @@ -6376,8 +6382,8 @@ libcall({libname}, {funcname}, {argument}) Examples: > :echo libcall("libc.so", "getenv", "HOME") -< Can also be used as a |method|, where the base is passed as - the argument to the called function: > +< Can also be used as a |method|, the base is passed as the + third argument: > GetValue()->libcall("libc.so", "getenv") < *libcallnr()* @@ -6391,8 +6397,8 @@ libcallnr({libname}, {funcname}, {argument}) :call libcallnr("libc.so", "printf", "Hello World!\n") :call libcallnr("libc.so", "sleep", 10) < - Can also be used as a |method|, where the base is passed as - the argument to the called function: > + Can also be used as a |method|, the base is passed as the + third argument: > GetValue()->libcallnr("libc.so", "printf") < @@ -6543,8 +6549,8 @@ listener_add({callback} [, {buf}]) *listener_add()* The {callback} is also not invoked when the buffer is unloaded, use the |BufUnload| autocmd event for that. - Can also be used as a |method|, where the base is passed as - the second argument, the buffer: > + Can also be used as a |method|, the base is passed as the + second argument: > GetBuffer()->listener_add(callback) listener_flush([{buf}]) *listener_flush()* @@ -7014,6 +7020,7 @@ matchstrpos({expr}, {pat} [, {start} [, {count}]]) *matchstrpos()* Can also be used as a |method|: > GetText()->matchstrpos('word') +< *max()* max({expr}) Return the maximum value of all items in {expr}. {expr} can be a list or a dictionary. For a dictionary, @@ -8270,20 +8277,31 @@ serverlist() *serverlist()* :echo serverlist() < setbufline({expr}, {lnum}, {text}) *setbufline()* - Set line {lnum} to {text} in buffer {expr}. To insert - lines use |append()|. Any text properties in {lnum} are - cleared. + Set line {lnum} to {text} in buffer {expr}. This works like + |setline()| for the specified buffer. + + This function works only for loaded buffers. First call + |bufload()| if needed. + + To insert lines use |appendbufline()|. + Any text properties in {lnum} are cleared. + + {text} can be a string to set one line, or a list of strings + to set multiple lines. If the list extends below the last + line then those lines are added. For the use of {expr}, see |bufname()| above. {lnum} is used like with |setline()|. - This works like |setline()| for the specified buffer. + When {lnum} is just below the last line the {text} will be + added below the last line. When {expr} is not a valid buffer, the buffer is not loaded or {lnum} is not valid then 1 is returned. On success 0 is returned. - Can also be used as a |method|: > + Can also be used as a |method|, the base is passed as the + third argument: > GetText()->setbufline(buf, lnum) setbufvar({expr}, {varname}, {val}) *setbufvar()* @@ -8299,7 +8317,8 @@ setbufvar({expr}, {varname}, {val}) *setbufvar()* :call setbufvar("todo", "myvar", "foobar") < This function is not available in the |sandbox|. - Can also be used as a |method|: > + Can also be used as a |method|, the base is passed as the + third argument: > GetValue()->setbufvar(buf, varname) setcharsearch({dict}) *setcharsearch()* @@ -8348,7 +8367,8 @@ setenv({name}, {val}) *setenv()* When {val} is |v:null| the environment variable is deleted. See also |expr-env|. - Can also be used as a |method|, passing the value as the base: > + Can also be used as a |method|, the base is passed as the + second argument: > GetPath()->setenv('PATH') setfperm({fname}, {mode}) *setfperm()* *chmod* @@ -8379,7 +8399,7 @@ setline({lnum}, {text}) *setline()* {lnum} is used like with |getline()|. When {lnum} is just below the last line the {text} will be - added as a new line. + added below the last line. If this succeeds, 0 is returned. If this fails (most likely because {lnum} is invalid) 1 is returned. @@ -8397,7 +8417,8 @@ setline({lnum}, {text}) *setline()* < Note: The '[ and '] marks are not set. - Can also be used as a |method|, passing the text as the base: > + Can also be used as a |method|, the base is passed as the + second argument: > GetText()->setline(lnum) setloclist({nr}, {list} [, {action} [, {what}]]) *setloclist()* @@ -8646,7 +8667,8 @@ settabvar({tabnr}, {varname}, {val}) *settabvar()* Tabs are numbered starting with one. This function is not available in the |sandbox|. - Can also be used as a |method|, the base is used as the value: > + Can also be used as a |method|, the base is passed as the + third argument: > GetValue()->settabvar(tab, name) settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()* @@ -8667,7 +8689,8 @@ settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()* :call settabwinvar(3, 2, "myvar", "foobar") < This function is not available in the |sandbox|. - Can also be used as a |method|, the base is used as the value: > + Can also be used as a |method|, the base is passed as the + fourth argument: > GetValue()->settabvar(tab, winnr, name) settagstack({nr}, {dict} [, {action}]) *settagstack()* @@ -8701,7 +8724,8 @@ settagstack({nr}, {dict} [, {action}]) *settagstack()* call settagstack(1003, stack) unlet stack < - Can also be used as a |method|, the base is used as the Dict: > + Can also be used as a |method|, the base is passed as the + second argument: > GetStack()->settagstack(winnr) setwinvar({winnr}, {varname}, {val}) *setwinvar()* @@ -8710,7 +8734,8 @@ setwinvar({winnr}, {varname}, {val}) *setwinvar()* :call setwinvar(1, "&list", 0) :call setwinvar(2, "myvar", "foobar") -< Can also be used as a |method|, the base is used as the value: > +< Can also be used as a |method|, the base is passed as the + third argument: > GetValue()->setwinvar(winnr, name) sha256({string}) *sha256()* @@ -9078,8 +9103,8 @@ state([{what}]) *state()* e.g. after |f| a Insert mode autocomplete active x executing an autocommand - w blocked on waiting, e.g. ch_evalexpr() and - ch_read(), ch_readraw() when reading json. + w blocked on waiting, e.g. ch_evalexpr(), ch_read() and + ch_readraw() when reading json. S not triggering SafeState or SafeStateAgain c callback invoked, including timer (repeats for recursiveness up to "ccc") @@ -9130,7 +9155,7 @@ str2nr({expr} [, {base} [, {quoted}]]) *str2nr()* When {base} is omitted base 10 is used. This also means that a leading zero doesn't cause octal conversion to be used, as with the default String to Number conversion. Example: > - let nr = str2nr('123') + let nr = str2nr('0123') < When {base} is 16 a leading "0x" or "0X" is ignored. With a different base the result will be zero. Similarly, when @@ -9247,6 +9272,7 @@ stridx({haystack}, {needle} [, {start}]) *stridx()* Can also be used as a |method|: > GetHaystack()->stridx(needle) +< *string()* string({expr}) Return {expr} converted to a String. If {expr} is a Number, Float, String, Blob or a composition of them, then the result @@ -10142,8 +10168,8 @@ win_execute({id}, {command} [, {silent}]) *win_execute()* Not all commands are allowed in popup windows. When window {id} does not exist then no error is given. - Can also be used as a |method|, the base is used for the - command: > + Can also be used as a |method|, the base is passed as the + second argument: > GetCommand()->win_execute(winid) win_findbuf({bufnr}) *win_findbuf()* @@ -10461,11 +10487,11 @@ xor({expr}, {expr}) *xor()* to a number. A List, Dict or Float argument causes an error. Example: > :let bits = xor(bits, 0x80) -< Can also be used as a |method|: > +< + Can also be used as a |method|: > :let bits = bits->xor(0x80) < - *feature-list* There are four types of features: 1. Features that are only supported when they have been enabled when Vim @@ -11338,21 +11364,22 @@ This does NOT work: > *:let=<<* *:let-heredoc* *E990* *E991* *E172* *E221* -:let {var-name} =<< [trim] {marker} +:let {var-name} =<< [trim] {endmarker} text... text... -{marker} +{endmarker} Set internal variable {var-name} to a List containing - the lines of text bounded by the string {marker}. - {marker} must not contain white space. - {marker} cannot start with a lower case character. - The last line should end only with the {marker} string - without any other character. Watch out for white - space after {marker}! + the lines of text bounded by the string {endmarker}. + {endmarker} must not contain white space. + {endmarker} cannot start with a lower case character. + The last line should end only with the {endmarker} + string without any other character. Watch out for + white space after {endmarker}! Without "trim" any white space characters in the lines of text are preserved. If "trim" is specified before - {marker}, then indentation is stripped so you can do: > + {endmarker}, then indentation is stripped so you can + do: > let text =<< trim END if ok echo 'done' @@ -11366,23 +11393,31 @@ text... non-empty text line is stripped from the input lines. All leading indentation exactly matching the leading indentation before `let` is stripped from the line - containing {marker}. Note that the difference between - space and tab matters here. + containing {endmarker}. Note that the difference + between space and tab matters here. If {var-name} didn't exist yet, it is created. Cannot be followed by another command, but can be followed by a comment. + To avoid line continuation to be applied, consider + adding 'C' to 'cpoptions': > + set cpo+=C + let var =<< END + \ leading backslash + END + set cpo-=C +< Examples: > let var1 =<< END - Sample text 1 - Sample text 2 - Sample text 3 - END + Sample text 1 + Sample text 2 + Sample text 3 + END let data =<< trim DATA - 1 2 3 4 - 5 6 7 8 + 1 2 3 4 + 5 6 7 8 DATA < *E121* diff --git a/runtime/doc/if_perl.txt b/runtime/doc/if_perl.txt index bee9b88bf4..a34e7c944d 100644 --- a/runtime/doc/if_perl.txt +++ b/runtime/doc/if_perl.txt @@ -55,14 +55,14 @@ The ActiveState one should work, Strawberry Perl is a good alternative. working: > :perl VIM::Msg("Hello") -:pe[rl] << [endpattern] +:pe[rl] << [endmarker] {script} -{endpattern} +{endmarker} Execute Perl script {script}. - The {endpattern} after {script} must NOT be preceded - by any white space. + The {endmarker} after {script} must NOT be preceded by + any white space. - If [endpattern] is omitted, it defaults to a dot '.' + If [endmarker] is omitted, it defaults to a dot '.' like for the |:append| and |:insert| commands. Using '.' helps when inside a function, because "$i;" looks like the start of an |:insert| command to Vim. diff --git a/runtime/doc/if_ruby.txt b/runtime/doc/if_ruby.txt index 0eca0b6b66..0950e02aa5 100644 --- a/runtime/doc/if_ruby.txt +++ b/runtime/doc/if_ruby.txt @@ -28,14 +28,14 @@ downloading Ruby there. :rub[y] {cmd} Execute Ruby command {cmd}. A command to try it out: > :ruby print "Hello" -:rub[y] << [endpattern] +:rub[y] << [endmarker] {script} -{endpattern} +{endmarker} Execute Ruby script {script}. - The {endpattern} after {script} must NOT be preceded - by any white space. + The {endmarker} after {script} must NOT be preceded by + any white space. - If [endpattern] is omitted, it defaults to a dot '.' + If [endmarker] is omitted, it defaults to a dot '.' like for the |:append| and |:insert| commands. This form of the |:ruby| command is mainly useful for diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt index 36c49b8ba1..e7f9af9217 100644 --- a/runtime/doc/indent.txt +++ b/runtime/doc/indent.txt @@ -949,10 +949,12 @@ Function call arguments will indent 1 extra level. For two-space indentation: > ------------- *PHP_IndentFunctionDeclarationParameters* -Extra indentation levels to add to arguments in multi-line function definitions. > +Extra indentation levels to add to arguments in multi-line function +definitions. > let g:PHP_IndentFunctionDeclarationParameters = 1 -Function arguments in declarations will indent 1 extra level. For two-space indentation: > +Function arguments in declarations will indent 1 extra level. For two-space +indentation: > function call_the_thing( $with_this, diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index 4a7edca493..c4e08f27fc 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -480,7 +480,7 @@ try to solve the memory shortage. To stay on the safe side, exit Vim and start again. If this happens while Vim is still initializing, editing files is very -unlikely to work, therefore Vim will exit with value 123. +unlikely to work, therefore Vim will exit with value 123. Buffers are only partly kept in memory, thus editing a very large file is unlikely to cause an out-of-memory situation. Undo information is completely diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index cb65535bb8..34fb484f07 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 8.1. Last change: 2019 Sep 26 +*options.txt* For Vim version 8.1. Last change: 2019 Sep 28 VIM REFERENCE MANUAL by Bram Moolenaar @@ -856,7 +856,7 @@ A jump table for the options with a short description can be found at |Q_op|. :set background& < Vim will guess the value. In the GUI this should work correctly, in other cases Vim might not be able to guess the right value. - If the GUI supports a dark them, you can use the "d" flag in + If the GUI supports a dark theme, you can use the "d" flag in 'guioptions', see 'go-d'. When the |t_RB| option is set, Vim will use it to request the background @@ -1877,7 +1877,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'completeslash'* *'csl'* 'completeslash' 'csl' string (default: "") local to buffer - {not in Vi} {only for MS-Windows} + {only for MS-Windows} When this option is set it overrules 'shellslash' for completion: - When this option is set to "slash", a forward slash is used for path completion in insert mode. This is useful when editing HTML tag, or @@ -1917,7 +1917,7 @@ A jump table for the options with a short description can be found at |Q_op|. completion in a popup window. Only works in combination with "menu" or "menuone". Overrides "preview". See |'completepopup'| for specifying properties. - {only works when compiled with the +textprop feature} + {only works when compiled with the |+textprop| feature} noinsert Do not insert any text for a match until the user selects a match from the menu. Only works in combination with @@ -2470,7 +2470,6 @@ A jump table for the options with a short description can be found at |Q_op|. *'cursorlineopt'* *'culopt'* 'cursorlineopt' 'culopt' string (default: "number,line") local to window - {not in Vi} {not available when compiled without the |+syntax| feature} Comma separated list of settings for how 'cursorline' is displayed. diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 9a75a95f23..9c8db6bd05 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1,4 +1,4 @@ -*pi_netrw.txt* For Vim version 8.1. Last change: 2019 Jul 17 +*pi_netrw.txt* For Vim version 8.1. Last change: 2019 Oct 10 ------------------------------------------------ NETRW REFERENCE MANUAL by Charles E. Campbell @@ -3512,7 +3512,7 @@ Example: Clear netrw's marked file list via a mapping on gu > - Click "Add..." - Set External Editor (adjust path as needed, include the quotes and !.! at the end): - "c:\Program Files\Vim\vim70\gvim.exe" !.! + "c:\Program Files\Vim\vim81\gvim.exe" !.! - Check that the filetype in the box below is {asterisk}.{asterisk} (all files), or whatever types you want (cec: change {asterisk} to * ; I had to @@ -3762,8 +3762,8 @@ by obtaining a copy of the latest (often developmental) netrw at: The script is typically installed on systems as something like: > - /usr/local/share/vim/vim7x/plugin/netrwPlugin.vim - /usr/local/share/vim/vim7x/autoload/netrw.vim + /usr/local/share/vim/vim8x/plugin/netrwPlugin.vim + /usr/local/share/vim/vim8x/autoload/netrw.vim (see output of :echo &rtp) < which is loaded automatically at startup (assuming :set nocp). If you diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt index 04fa0a6786..a438ce64e9 100644 --- a/runtime/doc/popup.txt +++ b/runtime/doc/popup.txt @@ -361,7 +361,7 @@ popup_getpos({id}) *popup_getpos()* core_width width of the text box in screen cells core_height height of the text box in screen cells firstline line of the buffer at top (1 unless scrolled) - (not the value of the "firstline" property) + (not the value of the "firstline" property) scrollbar non-zero if a scrollbar is displayed visible one if the popup is displayed, zero if hidden Note that these are the actual screen positions. They differ @@ -744,22 +744,22 @@ is inserted or deleted. The popup functions like a tooltip. These steps are needed to make this work: - Define a text property type, it defines the name. > - call prop_type_add('popupMarker', {}) + call prop_type_add('popupMarker', {}) - Place a text property at the desired text: > let lnum = {line of the text} let col = {start column of the text} let len = {length of the text} let propId = {arbitrary but unique number} - call prop_add(lnum, col, #{ + call prop_add(lnum, col, #{ \ length: len, \ type: 'popupMarker', \ id: propId, \ }) - Create a popup: > - let winid = popup_create('the text', #{ - \ pos: 'botleft', + let winid = popup_create('the text', #{ + \ pos: 'botleft', \ textprop: 'popupMarker', \ textpropid: propId, \ border: [], @@ -799,7 +799,7 @@ Some hints: a click, as in the example above, helps for that. - If the text property is removed the popup is closed. Use something like this: > - call prop_remove(#{type: 'popupMarker', id: propId}) + call prop_remove(#{type: 'popupMarker', id: propId}) POPUP FILTER *popup-filter* diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 6365e3622c..08a532c770 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 8.1. Last change: 2019 Sep 19 +*syntax.txt* For Vim version 8.1. Last change: 2019 Oct 04 VIM REFERENCE MANUAL by Bram Moolenaar @@ -60,8 +60,8 @@ If the VIM environment variable is not set, Vim will try to find the path in another way (see |$VIMRUNTIME|). Usually this works just fine. If it doesn't, try setting the VIM environment variable to the directory where the Vim stuff is located. For example, if your syntax files -are in the "/usr/vim/vim50/syntax" directory, set $VIMRUNTIME to -"/usr/vim/vim50". You must do this in the shell, before starting Vim. +are in the "/usr/vim/vim81/syntax" directory, set $VIMRUNTIME to +"/usr/vim/vim81". You must do this in the shell, before starting Vim. This command also sources the |menu.vim| script when the GUI is running or will start soon. See |'go-M'| about avoiding that. diff --git a/runtime/doc/tags b/runtime/doc/tags index 091c968138..1e8b1cbb68 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -1016,6 +1016,8 @@ $VIM_POSIX vi_diff.txt /*$VIM_POSIX* 't_Sb' term.txt /*'t_Sb'* 't_Sf' term.txt /*'t_Sf'* 't_Si' term.txt /*'t_Si'* +'t_TE' term.txt /*'t_TE'* +'t_TI' term.txt /*'t_TI'* 't_Te' term.txt /*'t_Te'* 't_Ts' term.txt /*'t_Ts'* 't_VS' term.txt /*'t_VS'* @@ -8087,7 +8089,6 @@ new-vimgrep version7.txt /*new-vimgrep* new-virtedit version6.txt /*new-virtedit* news intro.txt /*news* nextnonblank() eval.txt /*nextnonblank()* -nice todo.txt /*nice* no-eval-feature eval.txt /*no-eval-feature* no-type-checking eval.txt /*no-type-checking* no_buffers_menu gui.txt /*no_buffers_menu* @@ -9104,6 +9105,8 @@ t_ST term.txt /*t_ST* t_Sb term.txt /*t_Sb* t_Sf term.txt /*t_Sf* t_Si term.txt /*t_Si* +t_TE term.txt /*t_TE* +t_TI term.txt /*t_TI* t_Te term.txt /*t_Te* t_Ts term.txt /*t_Ts* t_VS term.txt /*t_VS* diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index ab26631e27..fc5031dd46 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -496,7 +496,7 @@ term_dumpload({filename} [, {options}]) For {options} see |term_dumpdiff()|. Can also be used as a |method|: > - GetFilename()-> term_dumpload() + GetFilename()->term_dumpload() < *term_dumpwrite()* term_dumpwrite({buf}, {filename} [, {options}]) @@ -514,7 +514,7 @@ term_dumpwrite({buf}, {filename} [, {options}]) Can also be used as a |method|, the base is used for the file name: > - GetFilename()-> term_dumpwrite(bufnr) + GetFilename()->term_dumpwrite(bufnr) term_getaltscreen({buf}) *term_getaltscreen()* Returns 1 if the terminal of {buf} is using the alternate @@ -863,8 +863,6 @@ term_start({cmd} [, {options}]) *term_start()* Can also be used as a |method|: > GetCommand()->term_start() -< {only available when compiled with the |+terminal| feature} - term_wait({buf} [, {time}]) *term_wait()* Wait for pending updates of {buf} to be handled. @@ -1243,7 +1241,7 @@ gdb: *:Break* set a breakpoint at the cursor position :Break {position} - set a breakpoint at the specified position + set a breakpoint at the specified position *:Clear* delete the breakpoint at the cursor position *:Step* execute the gdb "step" command diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index 59a2f9d3cd..47d59e95e3 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 8.1. Last change: 2019 Sep 27 +*todo.txt* For Vim version 8.1. Last change: 2019 Oct 16 VIM REFERENCE MANUAL by Bram Moolenaar @@ -47,7 +47,10 @@ Popup windows: - Use popup (or popup menu) for command line completion - Implement flip option - Why does 'nrformats' leak from the popup window buffer??? - Happens in Test_simple_popup() at the second screendump. + Happens in Test_simple_popup() at: + call VerifyScreenDump(buf, 'Test_popupwin_04a', {}) + Only when this line is in defaults.vim: + set nrformats-=octal - For the "moved" property also include mouse movement? - Make redrawing more efficient and avoid flicker: - put popup menu also in popup_mask? @@ -76,7 +79,7 @@ Text properties: See comment at top of src/textprop.c. Then :%s?foo should take the first match above the cursor line. Prompt buffer: -- Add a command line history. +- Add a command line history, using up/down keys. #5010 - delay next prompt until plugin gives OK? - add prompt_addtext({buf}, {expr}) none add text to a prompt buffer @@ -134,6 +137,21 @@ E279, E290, E292, E362, E366, E450, E451, E452, E453, E454, E460, E489, E491, E565, E578, E610, E611, E653, E654, E856, E857, E860, E861, E863, E889, E900 +Try out enabling modifyOtherKeys in xterm: + CSI > 4 ; 2 m +Need to disable when going to cooked mode: + CSI > 4 ; m +Known problems: +- CTRL-V key inserts Esc sequence + +Patch to skip tests that don't work when run as root. (James McCoy, #5020) +Or just bail out completely? + +Patch to test right click. (Dominique Pelle, #5018) + +Python output doesn't stop when got_int is set. #5053 +Check got_int in write_output() in if_py_both.h? + Running test_gui and test_gui_init with Motif sometimes kills the window manager. Problem with Motif? Now test_gui crashes in submenu_change(). Athena is OK. @@ -141,8 +159,13 @@ Motif: Build on Ubuntu can't enter any text in dialog text fields. Improve running tests on MS-Windows: #4922 -Patch to properly break CJK lines: #3875 -Ready to include now? +In a function these two lines are different: + let [a, b, c] =<< trim END fails + let [a,b,c] =<< trim END works +issue #5051 + +Patch to properly break CJK lines: Anton Kochkov, #3875 +Should be ready to include now. Remove check for cmd_silent when calling search_stat()? (Gary Johnson) @@ -159,10 +182,13 @@ Patch to sort buffers on b_last_used time. (Andy Massimino, #4722) Patch to highlight the line number differently below the cursor line. (Shaun Brady, #624) +Patch to add more tests for cmd.exe: #4928 + Patch to fix session file when using multiple tabs. (Jason Franklin, 2019 May 20) Also put :argadd commands at the start for all buffers, so that their order remains equal? Then %argdel to clean it up. Do try this with 'hidden' set. +Also #4994: window-local options not always restored, related to using :badd. Completion mixes results from the current buffer with tags and other files. Happens when typing CTRL-N while still search for results. E.g., type "b_" in @@ -176,12 +202,17 @@ Ready to include now? Adding "10" to 'spellsuggest' causes spell suggestions to become very slow. (#4087) +":helptags ALL" should skip directories where "tags" cannot be written. +(Matěj Cepl, #5026) + ":bnext" in a help buffer is supposed to go to the next help buffer, but it goes to any buffer, and then :bnext skips help buffers, since they are unlisted. (#4478) Patch to fix using zero sc_sid. (#4877) +Enable 'termbidi' if $VTE_VERSION >= 5703 ? + Universal solution to detect if t_RS is working, using cursor position. Koichi Iwamoto, #2126 @@ -212,8 +243,6 @@ Patch to remove FORTIFY_SOURCE also from CPPFLAGS. (Benedikt Morbach, #2786) Patch from Namsh to allow building with both XIM and hangulin. (2019 Aug 29) -Patch to fix redirect of shell on MS-Windows. (Yasuhiro Matsumoto, #2054) - When using :packadd files under "later" are not used, which is inconsistent with packages under "start". (xtal8, #1994) @@ -243,6 +272,14 @@ Patch to configure BUILD_DATE for reproducible builds. (James McCoy, #513) Patch to add MODIFIED_BY to MSVC build file. (Chen Lei, 2016 Nov 24, #1275) +xterm should be able to pass focus changes to Vim, so that Vim can check for +buffers that changed. Perhaps in misc.c, function selectwindow(). +Xterm 224 supports it! +Patch to make FocusGained and FocusLost work in modern terminals. (Hayaki +Saito, 2013 Apr 24) Update 2016 Aug 12. +Also see issue #609. +We could add the enable/disable sequences to t_ti/t_te or t_ks/t_ke. + Check_external_diff() is used too often. (Daniel Hahler, #4800) Incorrect formatting with autoindent. (Sebastian Gniazdowski, #4909) @@ -297,6 +334,8 @@ Williams, 2018 Oct 30) "exepath('bin/cmd')" does not work while ":!bin/cmd" does work. (Daniel Hahler, #4710) and executable('bin/cmd') returns 1 +Error drawing the number column when 'cursorline' is set. (#3893) + Problem with :tlmenu: Detach item added with all modes? Issue #3563. The quoting of the [command] argument of :terminal is not clearly documented. @@ -1604,14 +1643,6 @@ compatible with Vim spell files. The old files can no longer be downloaded. Spell checking: Add a feature to only consider two spaces after a dot to start a new sentence. Don't give the capitalization error when there is one space. -xterm should be able to pass focus changes to Vim, so that Vim can check for -buffers that changed. Perhaps in misc.c, function selectwindow(). -Xterm 224 supports it! -Patch to make FocusGained and FocusLost work in modern terminals. (Hayaki -Saito, 2013 Apr 24) Update 2016 Aug 12. -Also see issue #609. -We could add the enable/disable sequences to t_ti/t_te or t_ks/t_ke. - Idea: For a window in the middle (has window above and below it), use right-mouse-drag on the status line to move a window up/down without changing its height? It's like dragging the status bar above it at the same time. @@ -2155,10 +2186,6 @@ Looks like only bash can do it. (Yakov Lerner) Cscope "cs add" stopped working somewhat before 7.2.438. (Gary Johnson, 2010 Jun 29) Caused by 7.2.433? -I often see pasted text (from Firefox, to Vim in xterm) appear twice. -Also, Vim in xterm sometimes loses copy/paste ability (probably after running -an external command). - Jumplist doesn't work properly in Insert mode? (Jean Johner, 2010 Mar 20) Problem with transparent cmdline. Also: Terminal title is wrong with @@ -2754,10 +2781,6 @@ If the variable "g:x#y#z" exists completion after ":echo g:x#" doesn't work. Feature request: Command to go to previous tab, like what CTRL-W p does for windows. (Adam George) -F1 - F4 in an xterm produce a different escape sequence when used with a -modifier key. Need to catch three different sequences. Use K_ZF1, like -K_ZHOME? (Dickey, 2007 Dec 2) - In debug mode, using CTRL-R = to evaluate a function causes stepping through the function. (Hari Krishna Dara, 2006 Jun 28) @@ -2892,12 +2915,6 @@ Setting 'background' resets the Normal background color: This is undesired, 'background' is supposed to tell Vim what the background color is, not reset it. -Linux distributions: -- Suggest compiling xterm with --enable-tcap-query, so that nr of colors is - known to Vim. 88 colors instead of 16 works better. See ":help - xfree-xterm". -- Suggest including bare "vi" and "vim" with X11, syntax, etc. - Completion menu: For a wrapping line, completing a long file name, only the start of the path is shown in the menu. Should move the menu to the right to show more text of the completions. Shorten the items that don't fit in the @@ -2912,12 +2929,6 @@ the buffer is displayed. (Antonios Tsakiridis) When ":cn" moves to an error in the same line the message isn't shortened. Only skip shortening for ":cc"? -Write "making vim work better" for the docs (mostly pointers): *nice* - - sourcing $VIMRUNTIME/vimrc_example.vim - - setting 'mouse' to "a" - - getting colors in xterm - - compiling Vim with X11, GUI, etc. - Problem with ":call" and dictionary function. Hari Krishna Dara, Charles Campbell 2006 Jul 06. @@ -3011,11 +3022,6 @@ When 'encoding' is utf-8 typing text at the end of the line causes previously typed characters to be redrawn. Caused by patch 7.1.329. (Tyler Spivey, 2008 Sep 3, 11) -When Vim in an xterm owns the selection and the user does ":shell" Vim doesn't -respond to selection requests. Invoking XtDisownSelection() before executing -the shell doesn't help. Would require forking and doing a message loop, like -what happens for the GUI. - ":vimgrep" does not recognize a recursive symlink. Is it possible to detect this, at least for Unix (using device/inode)? @@ -3104,11 +3110,6 @@ Awaiting updated patches: use the patch that keeps using HLF_8 if HLF_WS has not been given values. Add section in help files for these highlight groups? -8 "fg" and "bg" don't work in an xterm. Get default colors from xterm - with an ESC sequence. - xterm can send colors for many things. E.g. for the cursor: - ]12;? - Can use this to get the background color and restore the colors on exit. 7 Add "DefaultFG" and "DefaultBG" for the colors of the menu. (Marcin Dalecki has a patch for Motif and Carbon) - Add possibility to highlight specific columns (for Fortran). Or put a @@ -3702,7 +3703,6 @@ Macintosh: 8 Xterm sends ^[[H for and ^[[F for in some mode. Also recognize these keys? Mostly useful for xterm simulators, like gnometerm. See http://dickey.his.com/xterm/xterm.faq.html#xterm_pc_style. -8 For xterm also recognize keypad up/down/left/right and insert. 8 '[ and '] should be set to start/end of line when using a linewise operator (e.g., ":w"). 8 CTRL-A can't handle big "long" numbers, they become negative. Check for @@ -3717,10 +3717,6 @@ Macintosh: filesystem, an illegal file name may be created: ".vim". 8 For each buffer that is opened, the viminfo file is opened and read to check for file marks. This can be slow. -7 In xterm, recognize both vt100 and vt220 cursor keys. Change - add_termcode() to not remove an existing entry for a name, when it's - needed. - Need a generic solution to recognize different codes for the same key. 8 Core dump within signal function: gdb doesn't show stack backtrace! Option to skip catch_signals()? 9 Repeating a "cw" with "." doesn't work if the text was pasted from the diff --git a/runtime/doc/vi_diff.txt b/runtime/doc/vi_diff.txt index 8872103b19..82f6274345 100644 --- a/runtime/doc/vi_diff.txt +++ b/runtime/doc/vi_diff.txt @@ -1,4 +1,4 @@ -*vi_diff.txt* For Vim version 8.1. Last change: 2019 Sep 27 +*vi_diff.txt* For Vim version 8.1. Last change: 2019 Oct 12 VIM REFERENCE MANUAL by Bram Moolenaar @@ -64,6 +64,9 @@ w300 number (default 23) *'w300'* w1200 number (default 23) *'w1200'* w9600 number (default 23) *'w9600'* +Vi did not allow for changing the termcap entries, you would have to exit Vi, +edit the termcap entry and try again. Vim has the |terminal-options|. + ============================================================================== 3. Limits *limits* diff --git a/runtime/filetype.vim b/runtime/filetype.vim index fce4825882..9af37ad2a8 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar -" Last Change: 2019 Sep 27 +" Last Change: 2019 Oct 04 " Listen very carefully, I will say this only once if exists("did_load_filetypes") diff --git a/runtime/indent/testdir/yaml.in b/runtime/indent/testdir/yaml.in new file mode 100644 index 0000000000..e3d77e2543 --- /dev/null +++ b/runtime/indent/testdir/yaml.in @@ -0,0 +1,14 @@ +# vim: set ft=yaml sw=2 et : + +# START_INDENT +map1: +sub1: +- list item +map2: +- another list +# END_INDENT + +# START_INDENT +map: &anchor +map: val +# END_INDENT diff --git a/runtime/indent/testdir/yaml.ok b/runtime/indent/testdir/yaml.ok new file mode 100644 index 0000000000..b97b2e5896 --- /dev/null +++ b/runtime/indent/testdir/yaml.ok @@ -0,0 +1,14 @@ +# vim: set ft=yaml sw=2 et : + +# START_INDENT +map1: + sub1: + - list item +map2: + - another list +# END_INDENT + +# START_INDENT +map: &anchor +map: val +# END_INDENT diff --git a/runtime/indent/yaml.vim b/runtime/indent/yaml.vim index 3eb16f845d..9621b2b6ed 100644 --- a/runtime/indent/yaml.vim +++ b/runtime/indent/yaml.vim @@ -1,7 +1,7 @@ " Vim indent file " Language: YAML " Maintainer: Nikolai Pavlov -" Last Change: 2017 Jun 13 +" Last Change: 2019 Sep 28 " Only load this indent file when no other was loaded. if exists('b:did_indent') @@ -29,7 +29,7 @@ function s:FindPrevLessIndentedLine(lnum, ...) let curindent = a:0 ? a:1 : indent(a:lnum) while prevlnum \&& indent(prevlnum) >= curindent - \&& getline(prevlnum) =~# '^\s*#' + \&& getline(prevlnum) !~# '^\s*#' let prevlnum = prevnonblank(prevlnum-1) endwhile return prevlnum diff --git a/runtime/syntax/named.vim b/runtime/syntax/named.vim index 210d387ece..292d1b2bbf 100644 --- a/runtime/syntax/named.vim +++ b/runtime/syntax/named.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: BIND configuration file " Maintainer: Nick Hibma -" Last Change: 2007-01-30 +" Last Change: 2019 Oct 08 " Filenames: named.conf, rndc.conf " Location: http://www.van-laarhoven.org/vim/syntax/named.vim " @@ -54,7 +54,7 @@ syn match namedIntIdent contained /"\=\k\+"\=/ nextgroup=namedIntSection skipwhi syn region namedSection contained start=+{+ end=+};+ contains=namedSection,namedIntKeyword " --- IntSection: section that does not contain other sections -syn region namedIntSection contained start=+{+ end=+}+ contains=namedIntKeyword,namedError +syn region namedIntSection contained start=+{+ end=+}+ contains=namedIntKeyword,namedError,namedComment " --- IntKeyword: keywords contained within `{ ... }' sections only " + these keywords are contained within `key' and `acl' sections From 4a4981b7cd57b0b2289ed4f9d621ef4d90d767f0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 22:36:11 +0200 Subject: [PATCH 65/67] patch 8.1.2166: rubyeval() not tested as a method Problem: Rubyeval() not tested as a method. Solution: Change a test case. --- src/testdir/test_ruby.vim | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_ruby.vim b/src/testdir/test_ruby.vim index f679bbd5f4..8f83a30408 100644 --- a/src/testdir/test_ruby.vim +++ b/src/testdir/test_ruby.vim @@ -46,7 +46,7 @@ func Test_set_cursor() " Check that movement after setting cursor position keeps current column. normal j call assert_equal([2, 6], [line('.'), col('.')]) - call assert_equal([2, 5], rubyeval('$curwin.cursor')) + call assert_equal([2, 5], '$curwin.cursor'->rubyeval()) call assert_fails('ruby $curwin.cursor = [1]', \ 'ArgumentError: array length must be 2') diff --git a/src/version.c b/src/version.c index e7ba706b43..c7d0ace489 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2166, /**/ 2165, /**/ From 2559a47823a6a7827631f2e6a0176d7afce2721c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 23:33:12 +0200 Subject: [PATCH 66/67] patch 8.1.2167: mapping test fails on MS-Windows Problem: Mapping test fails on MS-Windows. Solution: Remove all the existing Insert-mode mappings. --- src/testdir/test_mapping.vim | 4 ++-- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim index d3ae615411..4ba3645981 100644 --- a/src/testdir/test_mapping.vim +++ b/src/testdir/test_mapping.vim @@ -444,8 +444,8 @@ func Test_error_in_map_expr() endfunc func Test_list_mappings() - " Remove default Mac mapping - silent! iunmap + " Remove default mappings + imapclear inoremap CtrlM inoremap AltS diff --git a/src/version.c b/src/version.c index c7d0ace489..3bcd910c13 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2167, /**/ 2166, /**/ From b1ba9abcb385b0a5355788a7eefef78ec68d2f65 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Oct 2019 23:34:42 +0200 Subject: [PATCH 67/67] patch 8.1.2168: heredoc assignment not skipped in if block Problem: Heredoc assignment not skipped in if block. Solution: Check if "skip" is set. (closes #5063) --- src/evalvars.c | 9 ++++++--- src/testdir/test_let.vim | 8 ++++++++ src/version.c | 2 ++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/evalvars.c b/src/evalvars.c index 08a9cc2e14..eb198fdf9f 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -756,10 +756,13 @@ ex_let_const(exarg_T *eap, int is_const) if (l != NULL) { rettv_list_set(&rettv, l); - op[0] = '='; - op[1] = NUL; - (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count, + if (!eap->skip) + { + op[0] = '='; + op[1] = NUL; + (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count, is_const, op); + } clear_tv(&rettv); } } diff --git a/src/testdir/test_let.vim b/src/testdir/test_let.vim index 3bb5748947..64d178c675 100644 --- a/src/testdir/test_let.vim +++ b/src/testdir/test_let.vim @@ -295,4 +295,12 @@ E app END call assert_equal(['something', 'app'], var1) + + let check = [] + if 0 + let check =<< trim END + from heredoc + END + endif + call assert_equal([], check) endfunc diff --git a/src/version.c b/src/version.c index 3bcd910c13..4f0e6faa5c 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2168, /**/ 2167, /**/