diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2016-04-21 03:15:52 -0400 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2016-04-21 03:15:52 -0400 |
commit | 5ebffaae4e6c08630b705b5994a509a08cbecce6 (patch) | |
tree | f73be82fddf61e925c09f6ba906751961bdb2824 /src | |
parent | cef624ee9e14bf15598219d96f65a6e4932eb935 (diff) | |
parent | 89e6973fe2b57316817877f64f0be5ebc5a25b7f (diff) | |
download | rneovim-5ebffaae4e6c08630b705b5994a509a08cbecce6.tar.gz rneovim-5ebffaae4e6c08630b705b5994a509a08cbecce6.tar.bz2 rneovim-5ebffaae4e6c08630b705b5994a509a08cbecce6.zip |
Merge #3229 ':tcd'
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 2 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 10 | ||||
-rw-r--r-- | src/nvim/eval.c | 245 | ||||
-rw-r--r-- | src/nvim/ex_cmds.lua | 12 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 81 | ||||
-rw-r--r-- | src/nvim/ex_docmd.h | 12 | ||||
-rw-r--r-- | src/nvim/window.c | 22 |
7 files changed, 330 insertions, 54 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 10110b0f62..243806b3f1 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -291,7 +291,7 @@ void vim_change_directory(String dir, Error *err) return; } - post_chdir(false); + post_chdir(kCdScopeGlobal); try_end(err); } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 86e63eb52c..ecd13d5ee0 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -816,10 +816,12 @@ struct tabpage_S { was set */ diff_T *tp_first_diff; buf_T *(tp_diffbuf[DB_COUNT]); - int tp_diff_invalid; /* list of diffs is outdated */ - frame_T *(tp_snapshot[SNAP_COUNT]); /* window layout snapshots */ - dictitem_T tp_winvar; /* variable for "t:" Dictionary */ - dict_T *tp_vars; /* internal variables, local to tab page */ + int tp_diff_invalid; ///< list of diffs is outdated */ + frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots + dictitem_T tp_winvar; ///< variable for "t:" Dictionary + dict_T *tp_vars; ///< internal variables, local to tab page + char_u *localdir; ///< Absolute path of local directory or + ///< NULL }; /* diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b51480afc6..300e1c677e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6656,7 +6656,7 @@ static struct fst { } functions[] = { { "abs", 1, 1, f_abs }, - { "acos", 1, 1, f_acos }, // WJMc + { "acos", 1, 1, f_acos }, // WJMc { "add", 2, 2, f_add }, { "and", 2, 2, f_and }, { "append", 2, 2, f_append }, @@ -6673,9 +6673,9 @@ static struct fst { { "browse", 4, 4, f_browse }, { "browsedir", 2, 2, f_browsedir }, { "bufexists", 1, 1, f_bufexists }, - { "buffer_exists", 1, 1, f_bufexists }, // obsolete - { "buffer_name", 1, 1, f_bufname }, // obsolete - { "buffer_number", 1, 1, f_bufnr }, // obsolete + { "buffer_exists", 1, 1, f_bufexists }, // obsolete + { "buffer_name", 1, 1, f_bufname }, // obsolete + { "buffer_number", 1, 1, f_bufnr }, // obsolete { "buflisted", 1, 1, f_buflisted }, { "bufloaded", 1, 1, f_bufloaded }, { "bufname", 1, 1, f_bufname }, @@ -6719,7 +6719,7 @@ static struct fst { { "expand", 1, 3, f_expand }, { "extend", 2, 3, f_extend }, { "feedkeys", 1, 2, f_feedkeys }, - { "file_readable", 1, 1, f_filereadable }, // obsolete + { "file_readable", 1, 1, f_filereadable }, // obsolete { "filereadable", 1, 1, f_filereadable }, { "filewritable", 1, 1, f_filewritable }, { "filter", 2, 2, f_filter }, @@ -6749,7 +6749,7 @@ static struct fst { { "getcmdtype", 0, 0, f_getcmdtype }, { "getcmdwintype", 0, 0, f_getcmdwintype }, { "getcurpos", 0, 0, f_getcurpos }, - { "getcwd", 0, 0, f_getcwd }, + { "getcwd", 0, 2, f_getcwd }, { "getfontname", 0, 1, f_getfontname }, { "getfperm", 1, 1, f_getfperm }, { "getfsize", 1, 1, f_getfsize }, @@ -6773,7 +6773,7 @@ static struct fst { { "globpath", 2, 5, f_globpath }, { "has", 1, 1, f_has }, { "has_key", 2, 2, f_has_key }, - { "haslocaldir", 0, 0, f_haslocaldir }, + { "haslocaldir", 0, 2, f_haslocaldir }, { "hasmapto", 1, 3, f_hasmapto }, { "highlightID", 1, 1, f_hlID }, // obsolete { "highlight_exists", 1, 1, f_hlexists }, // obsolete @@ -9758,22 +9758,143 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv) rettv->vval.v_string[0] = cmdwin_type; } -/* - * "getcwd()" function - */ +/// `getcwd([{win}[, {tab}]])` function +/// +/// Every scope not specified implies the currently selected scope object. +/// +/// @pre The arguments must be of type number. +/// @pre There may not be more than two arguments. +/// @pre An argument may not be -1 if preceding arguments are not all -1. +/// +/// @post The return value will be a string. static void f_getcwd(typval_T *argvars, typval_T *rettv) { - char_u *cwd; + // Possible scope of working directory to return. + CdScope scope = kCdScopeWindow; + + // Numbers of the scope objects (window, tab) we want the working directory + // of. A `-1` means to skip this scope, a `0` means the current object. + int scope_number[] = { + [kCdScopeWindow] = 0, // Number of window to look at. + [kCdScopeTab ] = 0, // Number of tab to look at. + }; + + char_u *cwd = NULL; // Current working directory to print + char_u *from = NULL; // The original string to copy + + tabpage_T *tp = curtab; // The tabpage to look at. + win_T *win = curwin; // The window to look at. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; + + // Pre-conditions and scope extraction together + for (int i = 0; i < kCdScopeGlobal; i++) { + if (argvars[i].v_type == VAR_UNKNOWN) { + break; + } + if (argvars[i].v_type != VAR_NUMBER) { + EMSG(_(e_invarg)); + return; + } + scope_number[i] = argvars[i].vval.v_number; + // The scope is the current iteration step. + scope = i; + + if (scope_number[i] < -1) { + EMSG(_(e_invarg)); + return; + } + } + + // Allocate and initialize the string to return. cwd = xmalloc(MAXPATHL); - if (os_dirname(cwd, MAXPATHL) != FAIL) { - rettv->vval.v_string = vim_strsave(cwd); + + // Get the scope and numbers from the arguments + for (int i = 0; i < MAX_CD_SCOPE; i++) { + // If there is no argument there are no more scopes after it, break out. + if (argvars[i].v_type == VAR_UNKNOWN) { + break; + } + scope_number[i] = argvars[i].vval.v_number; + // The scope is the current iteration step. + scope = i; + // It is an error for the scope number to be less than `-1`. + if (scope_number[i] < -1) { + EMSG(_(e_invarg)); + goto end; + } + } + + // If the deepest scope number is `-1` advance the scope. + if (scope_number[scope] < 0) { + scope++; + } + + // Find the tabpage by number + if (scope_number[kCdScopeTab] == -1) { + tp = NULL; + } else if (scope_number[kCdScopeTab] > 0) { + tp = find_tabpage(scope_number[kCdScopeTab]); + if (!tp) { + EMSG(_("E5000: Cannot find tab number.")); + goto end; + } + } + + // Find the window in `tp` by number, `NULL` if none. + if (scope_number[kCdScopeWindow] == -1) { + win = NULL; + } else if (scope_number[kCdScopeWindow] >= 0) { + if (!tp) { + EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0.")); + goto end; + } + + if (scope_number[kCdScopeWindow] > 0) { + win = find_win_by_nr(&argvars[0], curtab); + if (!win) { + EMSG(_("E5002: Cannot find window number.")); + goto end; + } + } + } + + switch (scope) { + case kCdScopeWindow: + from = win->w_localdir; + if (from) { + break; + } + case kCdScopeTab: // FALLTHROUGH + from = tp->localdir; + if (from) { + break; + } + case kCdScopeGlobal: // FALLTHROUGH + // The `globaldir` variable is not always set. + if (globaldir) { + from = globaldir; + } else { + // Copy the OS path directly into output string and jump to the end. + if (os_dirname(cwd, MAXPATHL) == FAIL) { + EMSG(_("E41: Could not display path.")); + goto end; + } + } + break; + } + + if (from) { + xstrlcpy((char *)cwd, (char *)from, MAXPATHL); + } + + rettv->vval.v_string = vim_strsave(cwd); #ifdef BACKSLASH_IN_FILENAME - slash_adjust(rettv->vval.v_string); + slash_adjust(rettv->vval.v_string); #endif - } + +end: xfree(cwd); } @@ -10593,12 +10714,98 @@ static void f_has_key(typval_T *argvars, typval_T *rettv) get_tv_string(&argvars[1]), -1) != NULL; } -/* - * "haslocaldir()" function - */ +/// `haslocaldir([{win}[, {tab}]])` function +/// +/// Returns `1` if the scope object has a local directory, `0` otherwise. If a +/// scope object is not specified the current one is implied. This function +/// share a lot of code with `f_getcwd`. +/// +/// @pre The arguments must be of type number. +/// @pre There may not be more than two arguments. +/// @pre An argument may not be -1 if preceding arguments are not all -1. +/// +/// @post The return value will be either the number `1` or `0`. static void f_haslocaldir(typval_T *argvars, typval_T *rettv) { - rettv->vval.v_number = (curwin->w_localdir != NULL); + // Possible scope of working directory to return. + CdScope scope = kCdScopeWindow; + + // Numbers of the scope objects (window, tab) we want the working directory + // of. A `-1` means to skip this scope, a `0` means the current object. + int scope_number[] = { + [kCdScopeWindow] = 0, // Number of window to look at. + [kCdScopeTab ] = 0, // Number of tab to look at. + }; + + tabpage_T *tp = curtab; // The tabpage to look at. + win_T *win = curwin; // The window to look at. + + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + // Pre-conditions and scope extraction together + for (int i = 0; i < kCdScopeGlobal; i++) { + if (argvars[i].v_type == VAR_UNKNOWN) { + break; + } + if (argvars[i].v_type != VAR_NUMBER) { + EMSG(_(e_invarg)); + return; + } + scope_number[i] = argvars[i].vval.v_number; + // The scope is the current iteration step. + scope = i; + if (scope_number[i] < -1) { + EMSG(_(e_invarg)); + return; + } + } + + // It the deepest scope number is `-1` advance the scope by one. + if (scope_number[scope] < 0) { + ++scope; + } + + // Find the tabpage by number + if (scope_number[kCdScopeTab] == -1) { + tp = NULL; + } else if (scope_number[kCdScopeTab] > 0) { + tp = find_tabpage(scope_number[kCdScopeTab]); + if (!tp) { + EMSG(_("5000: Cannot find tab number.")); + return; + } + } + + // Find the window in `tp` by number, `NULL` if none. + if (scope_number[kCdScopeWindow] == -1) { + win = NULL; + } else if (scope_number[kCdScopeWindow] >= 0) { + if (!tp) { + EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0.")); + return; + } + + if (scope_number[kCdScopeWindow] > 0) { + win = find_win_by_nr(&argvars[0], curtab); + if (!win) { + EMSG(_("E5002: Cannot find window number.")); + return; + } + } + } + + switch (scope) { + case kCdScopeWindow: + rettv->vval.v_number = win->w_localdir ? 1 : 0; + break; + case kCdScopeTab: + rettv->vval.v_number = tp->localdir ? 1 : 0; + break; + case kCdScopeGlobal: + assert(0); + break; + } } /* diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 6c8835b5c5..04fd88cc8d 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2575,6 +2575,18 @@ return { func='ex_copymove', }, { + command='tcd', + flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), + addr_type=ADDR_LINES, + func='ex_cd', + }, + { + command='tchdir', + flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), + addr_type=ADDR_LINES, + func='ex_cd', + }, + { command='tNext', flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), addr_type=ADDR_LINES, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6391e023c3..2d316cb106 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2958,8 +2958,11 @@ set_one_cmd_context ( case CMD_chdir: case CMD_lcd: case CMD_lchdir: - if (xp->xp_context == EXPAND_FILES) + case CMD_tcd: + case CMD_tchdir: + if (xp->xp_context == EXPAND_FILES) { xp->xp_context = EXPAND_DIRECTORIES; + } break; case CMD_help: xp->xp_context = EXPAND_HELP; @@ -6814,36 +6817,55 @@ void free_cd_dir(void) #endif -/* - * Deal with the side effects of changing the current directory. - * When "local" is TRUE then this was after an ":lcd" command. - */ -void post_chdir(int local) +/// Deal with the side effects of changing the current directory. +/// +/// @param scope Scope of the function call (global, tab or window). +void post_chdir(CdScope scope) { + // The local directory of the current window is always overwritten. xfree(curwin->w_localdir); curwin->w_localdir = NULL; - if (local) { - /* If still in global directory, need to remember current - * directory as global directory. */ - if (globaldir == NULL && prev_dir != NULL) + + // Overwrite the local directory of the current tab page for `cd` and `tcd` + if (scope >= kCdScopeTab) { + xfree(curtab->localdir); + curtab->localdir = NULL; + } + + if (scope < kCdScopeGlobal) { + // If still in global directory, need to remember current directory as + // global directory. + if (globaldir == NULL && prev_dir != NULL) { globaldir = vim_strsave(prev_dir); - /* Remember this local directory for the window. */ - if (os_dirname(NameBuff, MAXPATHL) == OK) - curwin->w_localdir = vim_strsave(NameBuff); - } else { - /* We are now in the global directory, no need to remember its - * name. */ + } + } + + switch (scope) { + case kCdScopeGlobal: + // We are now in the global directory, no need to remember its name. xfree(globaldir); globaldir = NULL; + break; + case kCdScopeTab: + // Remember this local directory for the tab page. + if (os_dirname(NameBuff, MAXPATHL) == OK) { + curtab->localdir = vim_strsave(NameBuff); + } + break; + case kCdScopeWindow: + // Remember this local directory for the window. + if (os_dirname(NameBuff, MAXPATHL) == OK) { + curwin->w_localdir = vim_strsave(NameBuff); + } + break; } shorten_fnames(TRUE); } -/* - * ":cd", ":lcd", ":chdir" and ":lchdir". - */ + +/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`. void ex_cd(exarg_T *eap) { char_u *new_dir; @@ -6884,10 +6906,25 @@ void ex_cd(exarg_T *eap) new_dir = NameBuff; } #endif - if (new_dir == NULL || vim_chdir(new_dir)) + if (vim_chdir(new_dir)) { EMSG(_(e_failed)); - else { - post_chdir(eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir); + } else { + CdScope scope = kCdScopeGlobal; // Depends on command invoked + + switch (eap->cmdidx) { + case CMD_tcd: + case CMD_tchdir: + scope = kCdScopeTab; + break; + case CMD_lcd: + case CMD_lchdir: + scope = kCdScopeWindow; + break; + default: + break; + } + + post_chdir(scope); /* Echo the new current directory if the command was typed. */ if (KeyTyped || p_verbose >= 5) diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index a5a4edbbbf..7af3ee233c 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -19,6 +19,18 @@ #define EXMODE_NORMAL 1 #define EXMODE_VIM 2 +/// The scope of a command. +/// +/// The lower a number, the deeper the scope. +typedef enum { + kCdScopeWindow, ///< Affects one window. + kCdScopeTab, ///< Affects one tab page. + kCdScopeGlobal, ///< Affects the entire instance of NeoVim. +} CdScope; + +/// Last `:cd` scope defined. +#define MAX_CD_SCOPE kCdScopeGlobal + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_docmd.h.generated.h" #endif diff --git a/src/nvim/window.c b/src/nvim/window.c index 1b8c953596..93a7a58678 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2948,7 +2948,7 @@ void free_tabpage(tabpage_T *tp) unref_var_dict(tp->tp_vars); - + xfree(tp->localdir); // Free tab-local working directory xfree(tp); } @@ -3560,18 +3560,24 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri curwin->w_cursor.coladd = 0; changed_line_abv_curs(); /* assume cursor position needs updating */ - if (curwin->w_localdir != NULL) { - /* Window has a local directory: Save current directory as global - * directory (unless that was done already) and change to the local - * directory. */ + // The new directory is either the local directory of the window, of the tab + // or NULL. + char_u *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->localdir; + + if (new_dir) { + // Window/tab has a local directory: Save current directory as global + // directory (unless that was done already) and change to the local + // directory. if (globaldir == NULL) { char_u cwd[MAXPATHL]; - if (os_dirname(cwd, MAXPATHL) == OK) + if (os_dirname(cwd, MAXPATHL) == OK) { globaldir = vim_strsave(cwd); + } + } + if (os_chdir((char *)new_dir) == 0) { + shorten_fnames(true); } - if (os_chdir((char *)curwin->w_localdir) == 0) - shorten_fnames(TRUE); } else if (globaldir != NULL) { /* Window doesn't have a local directory and we are not in the global * directory: Change to the global directory. */ |