diff options
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r-- | src/nvim/ex_docmd.c | 278 |
1 files changed, 166 insertions, 112 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 34497eb212..4524026e3f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -99,7 +99,7 @@ static garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL }; #define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i]) // Whether a command index indicates a user command. -# define IS_USER_CMDIDX(idx) ((int)(idx) < 0) +#define IS_USER_CMDIDX(idx) ((int)(idx) < 0) // Struct for storing a line inside a while/for loop typedef struct { @@ -197,8 +197,8 @@ void do_exmode(void) exmode_active = true; State = NORMAL; - /* When using ":global /pat/ visual" and then "Q" we return to continue - * the :global command. */ + // When using ":global /pat/ visual" and then "Q" we return to continue + // the :global command. if (global_busy) { return; } @@ -231,8 +231,8 @@ void do_exmode(void) EMSG(_(e_emptybuf)); } else { if (ex_pressedreturn) { - /* go up one line, to overwrite the ":<CR>" line, so the - * output doesn't contain empty lines. */ + // go up one line, to overwrite the ":<CR>" line, so the + // output doesn't contain empty lines. msg_row = prev_msg_row; if (prev_msg_row == Rows - 1) { msg_row--; @@ -304,6 +304,7 @@ int do_cmdline_cmd(const char *cmd) /// DOCMD_KEYTYPED - Don't reset KeyTyped. /// DOCMD_EXCRESET - Reset the exception environment (used for debugging). /// DOCMD_KEEPLINE - Store first typed line (for repeating with "."). +/// DOCMD_PREVIEW - During 'inccommand' preview. /// /// @param cookie argument for fgetline() /// @@ -373,8 +374,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) ++ex_nesting_level; } - /* Get the function or script name and the address where the next breakpoint - * line and the debug tick for a function or script are stored. */ + // Get the function or script name and the address where the next breakpoint + // line and the debug tick for a function or script are stored. if (getline_is_func) { fname = func_name(real_cookie); breakpoint = func_breakpoint(real_cookie); @@ -499,11 +500,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) } if (cstack.cs_looplevel > 0) { - /* Inside a while/for loop we need to store the lines and use them - * again. Pass a different "fgetline" function to do_one_cmd() - * below, so that it stores lines in or reads them from - * "lines_ga". Makes it possible to define a function inside a - * while/for loop. */ + // Inside a while/for loop we need to store the lines and use them + // again. Pass a different "fgetline" function to do_one_cmd() + // below, so that it stores lines in or reads them from + // "lines_ga". Makes it possible to define a function inside a + // while/for loop. cmd_getline = get_loop_line; cmd_cookie = (void *)&cmd_loop_cookie; cmd_loop_cookie.lines_gap = &lines_ga; @@ -606,13 +607,13 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) recursive--; // Ignore trailing '|'-separated commands in preview-mode ('inccommand'). - if (State & CMDPREVIEW) { + if ((State & CMDPREVIEW) && (flags & DOCMD_PREVIEW)) { next_cmdline = NULL; } if (cmd_cookie == (void *)&cmd_loop_cookie) { - /* Use "current_line" from "cmd_loop_cookie", it may have been - * incremented when defining a function. */ + // Use "current_line" from "cmd_loop_cookie", it may have been + // incremented when defining a function. current_line = cmd_loop_cookie.current_line; } @@ -670,8 +671,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) cstack.cs_lflags |= CSL_HAD_LOOP; line_breakcheck(); // check if CTRL-C typed - /* Check for the next breakpoint at or after the ":while" - * or ":for". */ + // Check for the next breakpoint at or after the ":while" + // or ":for". if (breakpoint != NULL) { *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), fname, @@ -723,8 +724,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY; } - /* Update global "trylevel" for recursive calls to do_cmdline() from - * within this loop. */ + // Update global "trylevel" for recursive calls to do_cmdline() from + // within this loop. trylevel = initial_trylevel + cstack.cs_trylevel; // If the outermost try conditional (across function calls and sourced @@ -805,9 +806,9 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) trylevel = initial_trylevel; } - /* If a missing ":endtry", ":endwhile", ":endfor", or ":endif" or a memory - * lack was reported above and the error message is to be converted to an - * exception, do this now after rewinding the cstack. */ + // If a missing ":endtry", ":endwhile", ":endfor", or ":endif" or a memory + // lack was reported above and the error message is to be converted to an + // exception, do this now after rewinding the cstack. do_errthrow(&cstack, getline_equal(fgetline, cookie, get_func_line) ? (char_u *)"endfunction" : (char_u *)NULL); @@ -1013,9 +1014,9 @@ int getline_equal(LineGetter fgetline, void *cookie, LineGetter func) LineGetter gp; struct loop_cookie *cp; - /* When "fgetline" is "get_loop_line()" use the "cookie" to find the - * function that's originally used to obtain the lines. This may be - * nested several levels. */ + // When "fgetline" is "get_loop_line()" use the "cookie" to find the + // function that's originally used to obtain the lines. This may be + // nested several levels. gp = fgetline; cp = (struct loop_cookie *)cookie; while (gp == get_loop_line) { @@ -1029,14 +1030,14 @@ int getline_equal(LineGetter fgetline, void *cookie, LineGetter func) /// getline function. Otherwise return "cookie". /// /// @param cookie argument for fgetline() -void * getline_cookie(LineGetter fgetline, void *cookie) +void *getline_cookie(LineGetter fgetline, void *cookie) { LineGetter gp; struct loop_cookie *cp; - /* When "fgetline" is "get_loop_line()" use the "cookie" to find the - * cookie that's originally used to obtain the lines. This may be nested - * several levels. */ + // When "fgetline" is "get_loop_line()" use the "cookie" to find the + // cookie that's originally used to obtain the lines. This may be nested + // several levels. gp = fgetline; cp = (struct loop_cookie *)cookie; while (gp == get_loop_line) { @@ -1249,8 +1250,8 @@ static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite) /// This function may be called recursively! /// /// @param cookie argument for fgetline() -static char_u * do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline, - void *cookie) +static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline, + void *cookie) { char_u *p; linenr_T lnum; @@ -2624,9 +2625,9 @@ static char_u *find_command(exarg_T *eap, int *full) } len = (int)(p - eap->cmd); if (*eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) { - /* Check for ":dl", ":dell", etc. to ":deletel": that's - * :delete with the 'l' flag. Same for 'p'. */ - for (i = 0; i < len; ++i) { + // Check for ":dl", ":dell", etc. to ":deletel": that's + // :delete with the 'l' flag. Same for 'p'. + for (i = 0; i < len; i++) { if (eap->cmd[i] != ((char_u *)"delete")[i]) { break; } @@ -2904,7 +2905,7 @@ int cmd_exists(const char *const name) /// probably won't change that much -- webb. /// /// @param buff buffer for command string -const char * set_one_cmd_context(expand_T *xp, const char *buff) +const char *set_one_cmd_context(expand_T *xp, const char *buff) { size_t len = 0; exarg_T ea; @@ -4434,8 +4435,8 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) continue; } - /* Wildcards won't be expanded below, the replacement is taken - * literally. But do expand "~/file", "~user/file" and "$HOME/file". */ + // Wildcards won't be expanded below, the replacement is taken + // literally. But do expand "~/file", "~user/file" and "$HOME/file". if (vim_strchr(repl, '$') != NULL || vim_strchr(repl, '~') != NULL) { char_u *l = repl; @@ -4462,8 +4463,8 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) && !(eap->argt & EX_NOSPC)) { char_u *l; #ifdef BACKSLASH_IN_FILENAME - /* Don't escape a backslash here, because rem_backslash() doesn't - * remove it later. */ + // Don't escape a backslash here, because rem_backslash() doesn't + // remove it later. static char_u *nobslash = (char_u *)" \t\"|"; # define ESCAPE_CHARS nobslash #else @@ -5636,7 +5637,7 @@ static void ex_command(exarg_T *eap) uint32_t argt = 0; long def = -1; int flags = 0; - int compl = EXPAND_NOTHING; + int compl = EXPAND_NOTHING; char_u *compl_arg = NULL; cmd_addr_T addr_type_arg = ADDR_NONE; int has_attr = (eap->arg[0] == '-'); @@ -5696,7 +5697,7 @@ void ex_comclear(exarg_T *eap) uc_clear(&curbuf->b_ucmds); } -static void free_ucmd(ucmd_T * cmd) { +static void free_ucmd(ucmd_T *cmd) { xfree(cmd->uc_name); xfree(cmd->uc_rep); xfree(cmd->uc_compl_arg); @@ -6948,7 +6949,7 @@ static void ex_goto(exarg_T *eap) */ void alist_clear(alist_T *al) { -# define FREE_AENTRY_FNAME(arg) xfree(arg->ae_fname) +#define FREE_AENTRY_FNAME(arg) xfree(arg->ae_fname) GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME); } @@ -7204,8 +7205,8 @@ void ex_splitview(exarg_T *eap) } } else if (win_split(eap->addr_count > 0 ? (int)eap->line2 : 0, *eap->cmd == 'v' ? WSP_VERT : 0) != FAIL) { - /* Reset 'scrollbind' when editing another file, but keep it when - * doing ":split" without arguments. */ + // Reset 'scrollbind' when editing another file, but keep it when + // doing ":split" without arguments. if (*eap->arg != NUL ) { RESET_BINDING(curwin); @@ -7401,8 +7402,8 @@ static void ex_find(exarg_T *eap) fname = find_file_in_path(eap->arg, STRLEN(eap->arg), FNAME_MESS, TRUE, curbuf->b_ffname); if (eap->addr_count > 0) { - /* Repeat finding the file "count" times. This matters when it - * appears several times in the path. */ + // Repeat finding the file "count" times. This matters when it + // appears several times in the path. count = eap->line2; while (fname != NULL && --count > 0) { xfree(fname); @@ -7500,16 +7501,16 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) // After a split we can use an existing buffer. + (old_curwin != NULL ? ECMD_OLDBUF : 0) + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0) - + (eap->cmdidx == CMD_balt ? ECMD_ALTBUF : 0) - , old_curwin == NULL ? curwin : NULL) == FAIL) { + + (eap->cmdidx == CMD_balt ? ECMD_ALTBUF : 0), + old_curwin == NULL ? curwin : NULL) == FAIL) { // Editing the file failed. If the window was split, close it. if (old_curwin != NULL) { need_hide = (curbufIsChanged() && curbuf->b_nwindows <= 1); if (!need_hide || buf_hide(curbuf)) { cleanup_T cs; - /* Reset the error/interrupt/exception state here so that - * aborting() returns FALSE when closing a window. */ + // Reset the error/interrupt/exception state here so that + // aborting() returns FALSE when closing a window. enter_cleanup(&cs); win_close(curwin, !need_hide && !buf_hide(curbuf)); @@ -7674,8 +7675,8 @@ static void ex_read(exarg_T *eap) } } else { if (empty && exmode_active) { - /* Delete the empty line that remains. Historically ex does - * this but vi doesn't. */ + // Delete the empty line that remains. Historically ex does + // this but vi doesn't. if (eap->line2 == 0) { lnum = curbuf->b_ml.ml_line_count; } else { @@ -7706,6 +7707,21 @@ void free_cd_dir(void) #endif +// Get the previous directory for the given chdir scope. +static char_u *get_prevdir(CdScope scope) +{ + switch (scope) { + case kCdScopeTabpage: + return curtab->tp_prevdir; + break; + case kCdScopeWindow: + return curwin->w_prevdir; + break; + default: + return prev_dir; + } +} + /// Deal with the side effects of changing the current directory. /// /// @param scope Scope of the function call (global, tab or window). @@ -7715,14 +7731,15 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) XFREE_CLEAR(curwin->w_localdir); // Overwrite the tab-local CWD for :cd, :tcd. - if (scope >= kCdScopeTab) { + if (scope >= kCdScopeTabpage) { XFREE_CLEAR(curtab->tp_localdir); } if (scope < kCdScopeGlobal) { + char_u *pdir = get_prevdir(scope); // If still in global directory, set CWD as the global directory. - if (globaldir == NULL && prev_dir != NULL) { - globaldir = vim_strsave(prev_dir); + if (globaldir == NULL && pdir != NULL) { + globaldir = vim_strsave(pdir); } } @@ -7735,7 +7752,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) // We are now in the global directory, no need to remember its name. XFREE_CLEAR(globaldir); break; - case kCdScopeTab: + case kCdScopeTabpage: curtab->tp_localdir = (char_u *)xstrdup(cwd); break; case kCdScopeWindow: @@ -7748,59 +7765,92 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) shorten_fnames(true); if (trigger_dirchanged) { - do_autocmd_dirchanged(cwd, scope, false); + do_autocmd_dirchanged(cwd, scope, kCdCauseManual); } } -/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`. -void ex_cd(exarg_T *eap) +/// Change directory function used by :cd/:tcd/:lcd Ex commands and the chdir() function. +/// @param new_dir The directory to change to. +/// @param scope Scope of the function call (global, tab or window). +/// @return true if the directory is successfully changed. +bool changedir_func(char_u *new_dir, CdScope scope) { - char_u *new_dir; char_u *tofree; + char_u *pdir = NULL; + bool retval = false; - new_dir = eap->arg; -#if !defined(UNIX) - // for non-UNIX ":cd" means: print current directory - if (*new_dir == NUL) { - ex_pwd(NULL); - } else -#endif - { - if (allbuf_locked()) { - return; - } + if (new_dir == NULL || allbuf_locked()) { + return false; + } - // ":cd -": Change to previous directory - if (STRCMP(new_dir, "-") == 0) { - if (prev_dir == NULL) { - EMSG(_("E186: No previous directory")); - return; - } - new_dir = prev_dir; + // ":cd -": Change to previous directory + if (STRCMP(new_dir, "-") == 0) { + pdir = get_prevdir(scope); + if (pdir == NULL) { + EMSG(_("E186: No previous directory")); + return false; } + new_dir = pdir; + } - // Save current directory for next ":cd -" - tofree = prev_dir; - if (os_dirname(NameBuff, MAXPATHL) == OK) { - prev_dir = vim_strsave(NameBuff); - } else { - prev_dir = NULL; - } + // Free the previous directory + tofree = get_prevdir(scope); + + if (os_dirname(NameBuff, MAXPATHL) == OK) { + pdir = vim_strsave(NameBuff); + } else { + pdir = NULL; + } + + switch (scope) { + case kCdScopeTabpage: + curtab->tp_prevdir = pdir; + break; + case kCdScopeWindow: + curwin->w_prevdir = pdir; + break; + default: + prev_dir = pdir; + } #if defined(UNIX) - // On Unix ":cd" means: go to home directory. - if (*new_dir == NUL) { - // Use NameBuff for home directory name. - expand_env((char_u *)"$HOME", NameBuff, MAXPATHL); - new_dir = NameBuff; - } + // On Unix ":cd" means: go to home directory. + if (*new_dir == NUL) { + // Use NameBuff for home directory name. + expand_env((char_u *)"$HOME", NameBuff, MAXPATHL); + new_dir = NameBuff; + } #endif - CdScope scope = kCdScopeGlobal; // Depends on command invoked + if (vim_chdir(new_dir) == 0) { + bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0; + post_chdir(scope, dir_differs); + retval = true; + } else { + EMSG(_(e_failed)); + } + xfree(tofree); + + return retval; +} + +/// ":cd", ":tcd", ":lcd", ":chdir", "tchdir" and ":lchdir". +void ex_cd(exarg_T *eap) +{ + char_u *new_dir; + new_dir = eap->arg; +#if !defined(UNIX) && !defined(VMS) + // for non-UNIX ":cd" means: print current directory + if (*new_dir == NUL) { + ex_pwd(NULL); + } else +#endif + { + CdScope scope = kCdScopeGlobal; switch (eap->cmdidx) { case CMD_tcd: case CMD_tchdir: - scope = kCdScopeTab; + scope = kCdScopeTabpage; break; case CMD_lcd: case CMD_lchdir: @@ -7809,18 +7859,12 @@ void ex_cd(exarg_T *eap) default: break; } - - if (vim_chdir(new_dir)) { - EMSG(_(e_failed)); - } else { - post_chdir(scope, true); + if (changedir_func(new_dir, scope)) { // Echo the new current directory if the command was typed. if (KeyTyped || p_verbose >= 5) { ex_pwd(eap); } } - - xfree(tofree); } } @@ -7833,7 +7877,17 @@ static void ex_pwd(exarg_T *eap) #ifdef BACKSLASH_IN_FILENAME slash_adjust(NameBuff); #endif - msg(NameBuff); + if (p_verbose > 0) { + char *context = "global"; + if (curwin->w_localdir != NULL) { + context = "window"; + } else if (curtab->tp_localdir != NULL) { + context = "tabpage"; + } + smsg("[%s] %s", context, (char *)NameBuff); + } else { + msg(NameBuff); + } } else { EMSG(_("E187: Unknown")); } @@ -8297,8 +8351,8 @@ static void ex_redir(exarg_T *eap) } } - /* Make sure redirection is not off. Can happen for cmdline completion - * that indirectly invokes a command to catch its output. */ + // Make sure redirection is not off. Can happen for cmdline completion + // that indirectly invokes a command to catch its output. if (redir_fd != NULL || redir_reg || redir_vname) { redir_off = false; @@ -9432,14 +9486,14 @@ static void ex_filetype(exarg_T *eap) } if (STRCMP(arg, "on") == 0 || STRCMP(arg, "detect") == 0) { if (*arg == 'o' || !filetype_detect) { - source_runtime((char_u *)FILETYPE_FILE, DIP_ALL); + source_runtime(FILETYPE_FILE, DIP_ALL); filetype_detect = kTrue; if (plugin) { - source_runtime((char_u *)FTPLUGIN_FILE, DIP_ALL); + source_runtime(FTPLUGIN_FILE, DIP_ALL); filetype_plugin = kTrue; } if (indent) { - source_runtime((char_u *)INDENT_FILE, DIP_ALL); + source_runtime(INDENT_FILE, DIP_ALL); filetype_indent = kTrue; } } @@ -9450,15 +9504,15 @@ static void ex_filetype(exarg_T *eap) } else if (STRCMP(arg, "off") == 0) { if (plugin || indent) { if (plugin) { - source_runtime((char_u *)FTPLUGOF_FILE, DIP_ALL); + source_runtime(FTPLUGOF_FILE, DIP_ALL); filetype_plugin = kFalse; } if (indent) { - source_runtime((char_u *)INDOFF_FILE, DIP_ALL); + source_runtime(INDOFF_FILE, DIP_ALL); filetype_indent = kFalse; } } else { - source_runtime((char_u *)FTOFF_FILE, DIP_ALL); + source_runtime(FTOFF_FILE, DIP_ALL); filetype_detect = kFalse; } } else { @@ -9470,15 +9524,15 @@ static void ex_filetype(exarg_T *eap) void filetype_maybe_enable(void) { if (filetype_detect == kNone) { - source_runtime((char_u *)FILETYPE_FILE, true); + source_runtime(FILETYPE_FILE, true); filetype_detect = kTrue; } if (filetype_plugin == kNone) { - source_runtime((char_u *)FTPLUGIN_FILE, true); + source_runtime(FTPLUGIN_FILE, true); filetype_plugin = kTrue; } if (filetype_indent == kNone) { - source_runtime((char_u *)INDENT_FILE, true); + source_runtime(INDENT_FILE, true); filetype_indent = kTrue; } } |