aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_docmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r--src/nvim/ex_docmd.c237
1 files changed, 145 insertions, 92 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 16a9b78c6a..f649266b9f 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -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--;
@@ -374,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);
@@ -500,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;
@@ -612,8 +612,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
}
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;
}
@@ -671,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,
@@ -724,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
@@ -806,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);
@@ -1014,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) {
@@ -1035,9 +1035,9 @@ 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) {
@@ -2625,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;
}
@@ -4435,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;
@@ -4463,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
@@ -7205,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);
@@ -7402,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);
@@ -7509,8 +7509,8 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
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));
@@ -7675,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 {
@@ -7707,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).
@@ -7716,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);
}
}
@@ -7736,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:
@@ -7749,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:
@@ -7810,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);
}
}
@@ -7834,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"));
}
@@ -8298,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;