feat(defaults): session data in $XDG_STATE_HOME #15583

See: 4f2884e16d

- Move session persistent data to $XDG_STATE_HOME Change 'directory',
  'backupdir', 'undodir', 'viewdir' and 'shadafile' default location to
  $XDG_STATE_HOME/nvim.
- Move logs to $XDG_STATE_HOME, too.
- Add stdpath('log') support.

Fixes: #14805
This commit is contained in:
Ivan 2021-09-06 20:35:34 +03:00 committed by Justin M. Keyes
parent a1b663cce8
commit 78a1e6bc00
15 changed files with 303 additions and 224 deletions

View File

@ -188,7 +188,7 @@ loading plugins is also skipped.
Use Use
.Ar shada .Ar shada
instead of the default instead of the default
.Pa ~/.local/share/nvim/shada/main.shada . .Pa ~/.local/state/nvim/shada/main.shada .
If If
.Ar shada .Ar shada
is is
@ -326,7 +326,7 @@ Print version information and exit.
.Sh ENVIRONMENT .Sh ENVIRONMENT
.Bl -tag -width Fl .Bl -tag -width Fl
.It Ev NVIM_LOG_FILE .It Ev NVIM_LOG_FILE
Low-level log file, usually found at ~/.cache/nvim/log. Low-level log file, usually found at ~/.local/state/nvim/log.
:help $NVIM_LOG_FILE :help $NVIM_LOG_FILE
.It Ev VIM .It Ev VIM
Used to locate user files, such as init.vim. Used to locate user files, such as init.vim.
@ -340,12 +340,20 @@ Path to the user-local configuration directory, see
Defaults to Defaults to
.Pa ~/.config . .Pa ~/.config .
:help xdg :help xdg
.It Ev XDG_DATA_HOME .It Ev XDG_STATE_HOME
Like Like
.Ev XDG_CONFIG_HOME , .Ev XDG_CONFIG_HOME ,
but used to store data not generally edited by the user, but used to store data not generally edited by the user,
namely swap, backup, and ShaDa files. namely swap, backup, and ShaDa files.
Defaults to Defaults to
.Pa ~/.local/state .
:help xdg
.It Ev XDG_DATA_HOME
Like
.Ev XDG_CONFIG_HOME ,
but used to store data not generally edited by the user,
things like runtime files.
Defaults to
.Pa ~/.local/share . .Pa ~/.local/share .
:help xdg :help xdg
.It Ev VIMINIT .It Ev VIMINIT

View File

@ -7465,14 +7465,17 @@ stdpath({what}) *stdpath()* *E6100*
directories. directories.
{what} Type Description ~ {what} Type Description ~
cache String Cache directory. Arbitrary temporary cache String Cache directory: arbitrary temporary
storage for plugins, etc. storage for plugins, etc.
config String User configuration directory. The config String User configuration directory. |init.vim|
|init.vim| is stored here. is stored here.
config_dirs List Additional configuration directories. config_dirs List Other configuration directories.
data String User data directory. The |shada-file| data String User data directory. The |shada-file|
is stored here. is stored here.
data_dirs List Additional data directories. data_dirs List Other data directories.
log String Logs directory (for use by plugins too).
state String Session state directory: storage for file
drafts, undo history, shada, etc.
Example: > Example: >
:echo stdpath("config") :echo stdpath("config")

View File

@ -841,7 +841,7 @@ A jump table for the options with a short description can be found at |Q_op|.
again not rename the file. again not rename the file.
*'backupdir'* *'bdir'* *'backupdir'* *'bdir'*
'backupdir' 'bdir' string (default ".,$XDG_DATA_HOME/nvim/backup//") 'backupdir' 'bdir' string (default ".,$XDG_STATE_HOME/nvim/backup//")
global global
List of directories for the backup file, separated with commas. List of directories for the backup file, separated with commas.
- The backup file will be created in the first directory in the list - The backup file will be created in the first directory in the list
@ -2063,7 +2063,7 @@ A jump table for the options with a short description can be found at |Q_op|.
{char2}. See |digraphs|. {char2}. See |digraphs|.
*'directory'* *'dir'* *'directory'* *'dir'*
'directory' 'dir' string (default "$XDG_DATA_HOME/nvim/swap//") 'directory' 'dir' string (default "$XDG_STATE_HOME/nvim/swap//")
global global
List of directory names for the swap file, separated with commas. List of directory names for the swap file, separated with commas.
@ -3502,7 +3502,7 @@ A jump table for the options with a short description can be found at |Q_op|.
option. For '@' only characters up to 255 are used. option. For '@' only characters up to 255 are used.
Careful: If you change this option, it might break expanding Careful: If you change this option, it might break expanding
environment variables. E.g., when '/' is included and Vim tries to environment variables. E.g., when '/' is included and Vim tries to
expand "$HOME/.local/share/nvim/shada/main.shada". Maybe you should expand "$HOME/.local/state/nvim/shada/main.shada". Maybe you should
change 'iskeyword' instead. change 'iskeyword' instead.
*'iskeyword'* *'isk'* *'iskeyword'* *'isk'*
@ -4942,9 +4942,12 @@ A jump table for the options with a short description can be found at |Q_op|.
but are not part of the Nvim distribution. XDG_DATA_DIRS defaults but are not part of the Nvim distribution. XDG_DATA_DIRS defaults
to /usr/local/share/:/usr/share/, so system administrators are to /usr/local/share/:/usr/share/, so system administrators are
expected to install site plugins to /usr/share/nvim/site. expected to install site plugins to /usr/share/nvim/site.
5. $VIMRUNTIME, for files distributed with Neovim. 5. Applications state home directory, for files that contain your
session state (eg. backupdir, viewdir, undodir, etc).
Given by `stdpath("state")`. |$XDG_STATE_HOME|
6. $VIMRUNTIME, for files distributed with Neovim.
*after-directory* *after-directory*
6, 7, 8, 9. In after/ subdirectories of 1, 2, 3 and 4, with reverse 7, 8, 9, 10. In after/ subdirectories of 1, 2, 3 and 4, with reverse
ordering. This is for preferences to overrule or add to the ordering. This is for preferences to overrule or add to the
distributed defaults or system-wide settings (rarely needed). distributed defaults or system-wide settings (rarely needed).
@ -6623,7 +6626,7 @@ A jump table for the options with a short description can be found at |Q_op|.
'ttyfast' 'tf' Removed. |vim-differences| 'ttyfast' 'tf' Removed. |vim-differences|
*'undodir'* *'udir'* *E5003* *'undodir'* *'udir'* *E5003*
'undodir' 'udir' string (default "$XDG_DATA_HOME/nvim/undo//") 'undodir' 'udir' string (default "$XDG_STATE_HOME/nvim/undo//")
global global
List of directory names for undo files, separated with commas. List of directory names for undo files, separated with commas.
See 'backupdir' for details of the format. See 'backupdir' for details of the format.
@ -6786,7 +6789,7 @@ A jump table for the options with a short description can be found at |Q_op|.
displayed when 'verbosefile' is set. displayed when 'verbosefile' is set.
*'viewdir'* *'vdir'* *'viewdir'* *'vdir'*
'viewdir' 'vdir' string (default: "$XDG_DATA_HOME/nvim/view//") 'viewdir' 'vdir' string (default: "$XDG_STATE_HOME/nvim/view//")
global global
Name of the directory where to store files for |:mkview|. Name of the directory where to store files for |:mkview|.
This option cannot be set from a |modeline| or in the |sandbox|, for This option cannot be set from a |modeline| or in the |sandbox|, for

View File

@ -1044,8 +1044,8 @@ even if other entries (with known name/type/etc) are merged. |shada-merging|
SHADA FILE NAME *shada-file-name* SHADA FILE NAME *shada-file-name*
- Default name of the |shada| file is: - Default name of the |shada| file is:
Unix: "$XDG_DATA_HOME/nvim/shada/main.shada" Unix: "$XDG_STATE_HOME/nvim/shada/main.shada"
Windows: "$XDG_DATA_HOME/nvim-data/shada/main.shada" Windows: "$XDG_STATE_HOME/nvim-data/shada/main.shada"
See also |base-directories|. See also |base-directories|.
- To choose a different file name you can use: - To choose a different file name you can use:
- The "n" flag in the 'shada' option. - The "n" flag in the 'shada' option.
@ -1135,7 +1135,7 @@ running) you have additional options:
When you get error "E929: All .tmp.X files exist, When you get error "E929: All .tmp.X files exist,
cannot write ShaDa file!" check that no old temp files cannot write ShaDa file!" check that no old temp files
were left behind (e.g. were left behind (e.g.
~/.local/share/nvim/shada/main.shada.tmp*). ~/.local/state/nvim/shada/main.shada.tmp*).
Note: Executing :wshada will reset all |'quote| marks. Note: Executing :wshada will reset all |'quote| marks.
@ -1329,8 +1329,8 @@ paths.
*base-directories* *xdg* *base-directories* *xdg*
The "base" (root) directories conform to the XDG Base Directory Specification. The "base" (root) directories conform to the XDG Base Directory Specification.
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
The $XDG_CONFIG_HOME and $XDG_DATA_HOME environment variables are used if they The $XDG_CONFIG_HOME, $XDG_DATA_HOME and $XDG_STATE_HOME environment variables
exist, otherwise default values (listed below) are used. are used if they exist, otherwise default values (listed below) are used.
CONFIG DIRECTORY (DEFAULT) ~ CONFIG DIRECTORY (DEFAULT) ~
*$XDG_CONFIG_HOME* Nvim: stdpath("config") *$XDG_CONFIG_HOME* Nvim: stdpath("config")
@ -1342,6 +1342,11 @@ DATA DIRECTORY (DEFAULT) ~
Unix: ~/.local/share ~/.local/share/nvim Unix: ~/.local/share ~/.local/share/nvim
Windows: ~/AppData/Local ~/AppData/Local/nvim-data Windows: ~/AppData/Local ~/AppData/Local/nvim-data
STATE DIRECTORY (DEFAULT) ~
*$XDG_STATE_HOME* Nvim: stdpath("state")
Unix: ~/.local/state ~/.local/state/nvim
Windows: ~/AppData/Local ~/AppData/Local/nvim-data
Note: Throughout the user manual these defaults are used as placeholders, e.g. Note: Throughout the user manual these defaults are used as placeholders, e.g.
"~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config". "~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config".
@ -1349,7 +1354,7 @@ LOG FILE *$NVIM_LOG_FILE*
Besides 'debug' and 'verbose', Nvim keeps a general log file for internal Besides 'debug' and 'verbose', Nvim keeps a general log file for internal
debugging, plugins and RPC clients. > debugging, plugins and RPC clients. >
:echo $NVIM_LOG_FILE :echo $NVIM_LOG_FILE
By default, the file is located at stdpath('cache')/log unless that path By default, the file is located at stdpath('log')/log unless that path
is inaccessible or if $NVIM_LOG_FILE was set before |startup|. is inaccessible or if $NVIM_LOG_FILE was set before |startup|.

View File

@ -352,12 +352,12 @@ another session.
this yourself then. Example: > this yourself then. Example: >
:mksession! ~/.config/nvim/secret.vim :mksession! ~/.config/nvim/secret.vim
:wshada! ~/.local/share/nvim/shada/secret.shada :wshada! ~/.local/state/nvim/shada/secret.shada
And to restore this again: > And to restore this again: >
:source ~/.config/nvim/secret.vim :source ~/.config/nvim/secret.vim
:rshada! ~/.local/share/nvim/shada/secret.shada :rshada! ~/.local/state/nvim/shada/secret.shada
============================================================================== ==============================================================================
*21.5* Views *21.5* Views

View File

@ -17,7 +17,7 @@ centralized reference of the differences.
- Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for your |config|. - Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for your |config|.
- Use `$XDG_CONFIG_HOME/nvim` instead of `.vim` to store configuration files. - Use `$XDG_CONFIG_HOME/nvim` instead of `.vim` to store configuration files.
- Use `$XDG_DATA_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent - Use `$XDG_STATE_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent
session information. |shada| session information. |shada|
============================================================================== ==============================================================================
@ -32,12 +32,12 @@ centralized reference of the differences.
- 'autoread' is enabled - 'autoread' is enabled
- 'background' defaults to "dark" (unless set automatically by the terminal/UI) - 'background' defaults to "dark" (unless set automatically by the terminal/UI)
- 'backspace' defaults to "indent,eol,start" - 'backspace' defaults to "indent,eol,start"
- 'backupdir' defaults to .,~/.local/share/nvim/backup// (|xdg|), auto-created - 'backupdir' defaults to .,~/.local/state/nvim/backup// (|xdg|), auto-created
- 'belloff' defaults to "all" - 'belloff' defaults to "all"
- 'compatible' is always disabled - 'compatible' is always disabled
- 'complete' excludes "i" - 'complete' excludes "i"
- 'cscopeverbose' is enabled - 'cscopeverbose' is enabled
- 'directory' defaults to ~/.local/share/nvim/swap// (|xdg|), auto-created - 'directory' defaults to ~/.local/state/nvim/swap// (|xdg|), auto-created
- 'display' defaults to "lastline,msgsep" - 'display' defaults to "lastline,msgsep"
- 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding) - 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding)
- 'fillchars' defaults (in effect) to "vert:│,fold:·,sep:│" - 'fillchars' defaults (in effect) to "vert:│,fold:·,sep:│"
@ -65,7 +65,7 @@ centralized reference of the differences.
- 'tags' defaults to "./tags;,tags" - 'tags' defaults to "./tags;,tags"
- 'ttimeoutlen' defaults to 50 - 'ttimeoutlen' defaults to 50
- 'ttyfast' is always set - 'ttyfast' is always set
- 'undodir' defaults to ~/.local/share/nvim/undo// (|xdg|), auto-created - 'undodir' defaults to ~/.local/state/nvim/undo// (|xdg|), auto-created
- 'viewoptions' includes "unix,slash", excludes "options" - 'viewoptions' includes "unix,slash", excludes "options"
- 'viminfo' includes "!" - 'viminfo' includes "!"
- 'wildmenu' is enabled - 'wildmenu' is enabled

View File

@ -25,12 +25,12 @@ do
local function path_join(...) local function path_join(...)
return table.concat(vim.tbl_flatten({ ... }), path_sep) return table.concat(vim.tbl_flatten({ ... }), path_sep)
end end
local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log') local logfilename = path_join(vim.fn.stdpath('log'), 'lsp.log')
-- TODO: Ideally the directory should be created in open_logfile(), right -- TODO: Ideally the directory should be created in open_logfile(), right
-- before opening the log file, but open_logfile() can be called from libuv -- before opening the log file, but open_logfile() can be called from libuv
-- callbacks, where using fn.mkdir() is not allowed. -- callbacks, where using fn.mkdir() is not allowed.
vim.fn.mkdir(vim.fn.stdpath('cache'), 'p') vim.fn.mkdir(vim.fn.stdpath('log'), 'p')
--- Returns the log filename. --- Returns the log filename.
---@returns (string) log filename ---@returns (string) log filename

View File

@ -38,7 +38,7 @@ alternate file (e.g. stderr) use `LOG_CALLSTACK_TO_FILE(FILE*)`. Requires
Many log messages have a shared prefix, such as "UI" or "RPC". Use the shell to Many log messages have a shared prefix, such as "UI" or "RPC". Use the shell to
filter the log, e.g. at DEBUG level you might want to exclude UI messages: filter the log, e.g. at DEBUG level you might want to exclude UI messages:
tail -F ~/.cache/nvim/log | cat -v | stdbuf -o0 grep -v UI | stdbuf -o0 tee -a log tail -F ~/.local/state/nvim/log | cat -v | stdbuf -o0 grep -v UI | stdbuf -o0 tee -a log
Build with ASAN Build with ASAN
--------------- ---------------

View File

@ -9842,6 +9842,10 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = get_xdg_home(kXDGDataHome); rettv->vval.v_string = get_xdg_home(kXDGDataHome);
} else if (strequal(p, "cache")) { } else if (strequal(p, "cache")) {
rettv->vval.v_string = get_xdg_home(kXDGCacheHome); rettv->vval.v_string = get_xdg_home(kXDGCacheHome);
} else if (strequal(p, "state")) {
rettv->vval.v_string = get_xdg_home(kXDGStateHome);
} else if (strequal(p, "log")) {
rettv->vval.v_string = get_xdg_home(kXDGStateHome);
} else if (strequal(p, "config_dirs")) { } else if (strequal(p, "config_dirs")) {
get_xdg_var_list(kXDGConfigDirs, rettv); get_xdg_var_list(kXDGConfigDirs, rettv);
} else if (strequal(p, "data_dirs")) { } else if (strequal(p, "data_dirs")) {

View File

@ -51,7 +51,7 @@ static bool log_try_create(char *fname)
/// Initializes path to log file. Sets $NVIM_LOG_FILE if empty. /// Initializes path to log file. Sets $NVIM_LOG_FILE if empty.
/// ///
/// Tries $NVIM_LOG_FILE, or falls back to $XDG_CACHE_HOME/nvim/log. Path to log /// Tries $NVIM_LOG_FILE, or falls back to $XDG_STATE_HOME/nvim/log. Path to log
/// file is cached, so only the first call has effect, unless first call was not /// file is cached, so only the first call has effect, unless first call was not
/// successful. Failed initialization indicates either a bug in expand_env() /// successful. Failed initialization indicates either a bug in expand_env()
/// or both $NVIM_LOG_FILE and $HOME environment variables are undefined. /// or both $NVIM_LOG_FILE and $HOME environment variables are undefined.
@ -69,16 +69,16 @@ static bool log_path_init(void)
|| log_file_path[0] == '\0' || log_file_path[0] == '\0'
|| os_isdir((char_u *)log_file_path) || os_isdir((char_u *)log_file_path)
|| !log_try_create(log_file_path)) { || !log_try_create(log_file_path)) {
// Make kXDGCacheHome if it does not exist. // Make kXDGStateHome if it does not exist.
char *cachehome = get_xdg_home(kXDGCacheHome); char *loghome = get_xdg_home(kXDGStateHome);
char *failed_dir = NULL; char *failed_dir = NULL;
bool log_dir_failure = false; bool log_dir_failure = false;
if (!os_isdir((char_u *)cachehome)) { if (!os_isdir((char_u *)loghome)) {
log_dir_failure = (os_mkdir_recurse(cachehome, 0700, &failed_dir) != 0); log_dir_failure = (os_mkdir_recurse(loghome, 0700, &failed_dir) != 0);
} }
XFREE_CLEAR(cachehome); XFREE_CLEAR(loghome);
// Invalid $NVIM_LOG_FILE or failed to expand; fall back to default. // Invalid $NVIM_LOG_FILE or failed to expand; fall back to default.
char *defaultpath = stdpaths_user_cache_subpath("log"); char *defaultpath = stdpaths_user_state_subpath("log", 0, true);
size_t len = xstrlcpy(log_file_path, defaultpath, size); size_t len = xstrlcpy(log_file_path, defaultpath, size);
xfree(defaultpath); xfree(defaultpath);
// Fall back to .nvimlog // Fall back to .nvimlog

View File

@ -491,17 +491,17 @@ void set_init_1(bool clean_arg)
#endif #endif
false); false);
char *backupdir = stdpaths_user_data_subpath("backup", 2, true); char *backupdir = stdpaths_user_state_subpath("backup", 2, true);
const size_t backupdir_len = strlen(backupdir); const size_t backupdir_len = strlen(backupdir);
backupdir = xrealloc(backupdir, backupdir_len + 3); backupdir = xrealloc(backupdir, backupdir_len + 3);
memmove(backupdir + 2, backupdir, backupdir_len + 1); memmove(backupdir + 2, backupdir, backupdir_len + 1);
memmove(backupdir, ".,", 2); memmove(backupdir, ".,", 2);
set_string_default("backupdir", backupdir, true); set_string_default("backupdir", backupdir, true);
set_string_default("viewdir", stdpaths_user_data_subpath("view", 2, true), set_string_default("viewdir", stdpaths_user_state_subpath("view", 2, true),
true); true);
set_string_default("directory", stdpaths_user_data_subpath("swap", 2, true), set_string_default("directory", stdpaths_user_state_subpath("swap", 2, true),
true); true);
set_string_default("undodir", stdpaths_user_data_subpath("undo", 2, true), set_string_default("undodir", stdpaths_user_state_subpath("undo", 2, true),
true); true);
// Set default for &runtimepath. All necessary expansions are performed in // Set default for &runtimepath. All necessary expansions are performed in
// this function. // this function.

View File

@ -14,6 +14,7 @@ static const char *xdg_env_vars[] = {
[kXDGConfigHome] = "XDG_CONFIG_HOME", [kXDGConfigHome] = "XDG_CONFIG_HOME",
[kXDGDataHome] = "XDG_DATA_HOME", [kXDGDataHome] = "XDG_DATA_HOME",
[kXDGCacheHome] = "XDG_CACHE_HOME", [kXDGCacheHome] = "XDG_CACHE_HOME",
[kXDGStateHome] = "XDG_STATE_HOME",
[kXDGRuntimeDir] = "XDG_RUNTIME_DIR", [kXDGRuntimeDir] = "XDG_RUNTIME_DIR",
[kXDGConfigDirs] = "XDG_CONFIG_DIRS", [kXDGConfigDirs] = "XDG_CONFIG_DIRS",
[kXDGDataDirs] = "XDG_DATA_DIRS", [kXDGDataDirs] = "XDG_DATA_DIRS",
@ -24,6 +25,7 @@ static const char *const xdg_defaults_env_vars[] = {
[kXDGConfigHome] = "LOCALAPPDATA", [kXDGConfigHome] = "LOCALAPPDATA",
[kXDGDataHome] = "LOCALAPPDATA", [kXDGDataHome] = "LOCALAPPDATA",
[kXDGCacheHome] = "TEMP", [kXDGCacheHome] = "TEMP",
[kXDGStateHome] = "LOCALAPPDATA",
[kXDGRuntimeDir] = NULL, [kXDGRuntimeDir] = NULL,
[kXDGConfigDirs] = NULL, [kXDGConfigDirs] = NULL,
[kXDGDataDirs] = NULL, [kXDGDataDirs] = NULL,
@ -38,6 +40,7 @@ static const char *const xdg_defaults[] = {
[kXDGConfigHome] = "~\\AppData\\Local", [kXDGConfigHome] = "~\\AppData\\Local",
[kXDGDataHome] = "~\\AppData\\Local", [kXDGDataHome] = "~\\AppData\\Local",
[kXDGCacheHome] = "~\\AppData\\Local\\Temp", [kXDGCacheHome] = "~\\AppData\\Local\\Temp",
[kXDGStateHome] = "~\\AppData\\Local",
[kXDGRuntimeDir] = NULL, [kXDGRuntimeDir] = NULL,
[kXDGConfigDirs] = NULL, [kXDGConfigDirs] = NULL,
[kXDGDataDirs] = NULL, [kXDGDataDirs] = NULL,
@ -45,6 +48,7 @@ static const char *const xdg_defaults[] = {
[kXDGConfigHome] = "~/.config", [kXDGConfigHome] = "~/.config",
[kXDGDataHome] = "~/.local/share", [kXDGDataHome] = "~/.local/share",
[kXDGCacheHome] = "~/.cache", [kXDGCacheHome] = "~/.cache",
[kXDGStateHome] = "~/.local/state",
[kXDGRuntimeDir] = NULL, [kXDGRuntimeDir] = NULL,
[kXDGConfigDirs] = "/etc/xdg/", [kXDGConfigDirs] = "/etc/xdg/",
[kXDGDataDirs] = "/usr/local/share/:/usr/share/", [kXDGDataDirs] = "/usr/local/share/:/usr/share/",
@ -133,15 +137,26 @@ char *stdpaths_user_conf_subpath(const char *fname)
/// Return subpath of $XDG_DATA_HOME /// Return subpath of $XDG_DATA_HOME
/// ///
/// @param[in] fname New component of the path. /// @param[in] fname New component of the path.
///
/// @return [allocated] `$XDG_DATA_HOME/nvim/{fname}`
char *stdpaths_user_data_subpath(const char *fname)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
return concat_fnames_realloc(get_xdg_home(kXDGDataHome), fname, true);
}
/// Return subpath of $XDG_STATE_HOME
///
/// @param[in] fname New component of the path.
/// @param[in] trailing_pathseps Amount of trailing path separators to add. /// @param[in] trailing_pathseps Amount of trailing path separators to add.
/// @param[in] escape_commas If true, all commas will be escaped. /// @param[in] escape_commas If true, all commas will be escaped.
/// ///
/// @return [allocated] `$XDG_DATA_HOME/nvim/{fname}`. /// @return [allocated] `$XDG_STATE_HOME/nvim/{fname}`.
char *stdpaths_user_data_subpath(const char *fname, const size_t trailing_pathseps, char *stdpaths_user_state_subpath(const char *fname, const size_t trailing_pathseps,
const bool escape_commas) const bool escape_commas)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{ {
char *ret = concat_fnames_realloc(get_xdg_home(kXDGDataHome), fname, true); char *ret = concat_fnames_realloc(get_xdg_home(kXDGStateHome), fname, true);
const size_t len = strlen(ret); const size_t len = strlen(ret);
const size_t numcommas = (escape_commas ? memcnt(ret, ',', len) : 0); const size_t numcommas = (escape_commas ? memcnt(ret, ',', len) : 0);
if (numcommas || trailing_pathseps) { if (numcommas || trailing_pathseps) {

View File

@ -7,6 +7,7 @@ typedef enum {
kXDGConfigHome, ///< XDG_CONFIG_HOME kXDGConfigHome, ///< XDG_CONFIG_HOME
kXDGDataHome, ///< XDG_DATA_HOME kXDGDataHome, ///< XDG_DATA_HOME
kXDGCacheHome, ///< XDG_CACHE_HOME kXDGCacheHome, ///< XDG_CACHE_HOME
kXDGStateHome, ///< XDG_STATE_HOME
kXDGRuntimeDir, ///< XDG_RUNTIME_DIR kXDGRuntimeDir, ///< XDG_RUNTIME_DIR
kXDGConfigDirs, ///< XDG_CONFIG_DIRS kXDGConfigDirs, ///< XDG_CONFIG_DIRS
kXDGDataDirs, ///< XDG_DATA_DIRS kXDGDataDirs, ///< XDG_DATA_DIRS

View File

@ -1447,7 +1447,7 @@ static const char *shada_get_default_file(void)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_WARN_UNUSED_RESULT
{ {
if (default_shada_file == NULL) { if (default_shada_file == NULL) {
char *shada_dir = stdpaths_user_data_subpath("shada", 0, false); char *shada_dir = stdpaths_user_state_subpath("shada", 0, false);
default_shada_file = concat_fnames_realloc(shada_dir, "main.shada", true); default_shada_file = concat_fnames_realloc(shada_dir, "main.shada", true);
} }
return default_shada_file; return default_shada_file;

View File

@ -163,7 +163,7 @@ describe('startup defaults', function()
end) end)
it("'shadafile' ('viminfofile')", function() it("'shadafile' ('viminfofile')", function()
local env = {XDG_DATA_HOME='Xtest-userdata', XDG_CONFIG_HOME='Xtest-userconfig'} local env = {XDG_DATA_HOME='Xtest-userdata', XDG_STATE_HOME='Xtest-userstate', XDG_CONFIG_HOME='Xtest-userconfig'}
clear{args={}, args_rm={'-i'}, env=env} clear{args={}, args_rm={'-i'}, env=env}
-- Default 'shadafile' is empty. -- Default 'shadafile' is empty.
-- This means use the default location. :help shada-file-name -- This means use the default location. :help shada-file-name
@ -178,7 +178,7 @@ describe('startup defaults', function()
clear{args={}, args_rm={'-i'}, env=env} clear{args={}, args_rm={'-i'}, env=env}
eq({ f }, eval('v:oldfiles')) eq({ f }, eval('v:oldfiles'))
os.remove('Xtest-foo') os.remove('Xtest-foo')
rmdir('Xtest-userdata') rmdir('Xtest-userstate')
-- Handles viminfo/viminfofile as alias for shada/shadafile. -- Handles viminfo/viminfofile as alias for shada/shadafile.
eq('\n shadafile=', eval('execute("set shadafile?")')) eq('\n shadafile=', eval('execute("set shadafile?")'))
@ -206,7 +206,7 @@ describe('startup defaults', function()
describe('$NVIM_LOG_FILE', function() describe('$NVIM_LOG_FILE', function()
local xdgdir = 'Xtest-startup-xdg-logpath' local xdgdir = 'Xtest-startup-xdg-logpath'
local xdgcachedir = xdgdir..'/nvim' local xdgstatedir = xdgdir..'/nvim'
after_each(function() after_each(function()
os.remove('Xtest-logpath') os.remove('Xtest-logpath')
rmdir(xdgdir) rmdir(xdgdir)
@ -218,21 +218,21 @@ describe('startup defaults', function()
}}) }})
eq('Xtest-logpath', eval('$NVIM_LOG_FILE')) eq('Xtest-logpath', eval('$NVIM_LOG_FILE'))
end) end)
it('defaults to stdpath("cache")/log if empty', function() it('defaults to stdpath("log")/log if empty', function()
eq(true, mkdir(xdgdir) and mkdir(xdgcachedir)) eq(true, mkdir(xdgdir) and mkdir(xdgstatedir))
clear({env={ clear({env={
XDG_CACHE_HOME=xdgdir, XDG_STATE_HOME=xdgdir,
NVIM_LOG_FILE='', -- Empty is invalid. NVIM_LOG_FILE='', -- Empty is invalid.
}}) }})
eq(xdgcachedir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/')) eq(xdgstatedir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
end) end)
it('defaults to stdpath("cache")/log if invalid', function() it('defaults to stdpath("log")/log if invalid', function()
eq(true, mkdir(xdgdir) and mkdir(xdgcachedir)) eq(true, mkdir(xdgdir) and mkdir(xdgstatedir))
clear({env={ clear({env={
XDG_CACHE_HOME=xdgdir, XDG_STATE_HOME=xdgdir,
NVIM_LOG_FILE='.', -- Any directory is invalid. NVIM_LOG_FILE='.', -- Any directory is invalid.
}}) }})
eq(xdgcachedir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/')) eq(xdgstatedir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
end) end)
end) end)
end) end)
@ -264,6 +264,7 @@ describe('XDG-based defaults', function()
XDG_CONFIG_HOME=nil, XDG_CONFIG_HOME=nil,
XDG_DATA_HOME=nil, XDG_DATA_HOME=nil,
XDG_CACHE_HOME=nil, XDG_CACHE_HOME=nil,
XDG_STATE_HOME=nil,
XDG_RUNTIME_DIR=nil, XDG_RUNTIME_DIR=nil,
XDG_CONFIG_DIRS=nil, XDG_CONFIG_DIRS=nil,
XDG_DATA_DIRS=nil, XDG_DATA_DIRS=nil,
@ -293,6 +294,7 @@ describe('XDG-based defaults', function()
local env_sep = iswin() and ';' or ':' local env_sep = iswin() and ';' or ':'
local data_dir = iswin() and 'nvim-data' or 'nvim' local data_dir = iswin() and 'nvim-data' or 'nvim'
local state_dir = iswin() and 'nvim-data' or 'nvim'
local root_path = iswin() and 'C:' or '' local root_path = iswin() and 'C:' or ''
describe('with too long XDG variables', function() describe('with too long XDG variables', function()
@ -303,6 +305,7 @@ describe('XDG-based defaults', function()
.. env_sep.. root_path .. ('/b'):rep(2048) .. env_sep.. root_path .. ('/b'):rep(2048)
.. (env_sep .. root_path .. '/c'):rep(512)), .. (env_sep .. root_path .. '/c'):rep(512)),
XDG_DATA_HOME=(root_path .. ('/X'):rep(4096)), XDG_DATA_HOME=(root_path .. ('/X'):rep(4096)),
XDG_STATE_HOME=(root_path .. ('/X'):rep(4096)),
XDG_DATA_DIRS=(root_path .. ('/A'):rep(2048) XDG_DATA_DIRS=(root_path .. ('/A'):rep(2048)
.. env_sep .. root_path .. ('/B'):rep(2048) .. env_sep .. root_path .. ('/B'):rep(2048)
.. (env_sep .. root_path .. '/C'):rep(512)), .. (env_sep .. root_path .. '/C'):rep(512)),
@ -355,13 +358,13 @@ describe('XDG-based defaults', function()
.. ',' .. root_path .. ('/a'):rep(2048) .. '/nvim/after' .. ',' .. root_path .. ('/a'):rep(2048) .. '/nvim/after'
.. ',' .. root_path .. ('/x'):rep(4096) .. '/nvim/after' .. ',' .. root_path .. ('/x'):rep(4096) .. '/nvim/after'
):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/')) ):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/'))
eq('.,' .. root_path .. ('/X'):rep(4096).. '/' .. data_dir .. '/backup//', eq('.,' .. root_path .. ('/X'):rep(4096).. '/' .. state_dir .. '/backup//',
(meths.get_option('backupdir'):gsub('\\', '/'))) (meths.get_option('backupdir'):gsub('\\', '/')))
eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/swap//', eq(root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/swap//',
(meths.get_option('directory')):gsub('\\', '/')) (meths.get_option('directory')):gsub('\\', '/'))
eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/undo//', eq(root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/undo//',
(meths.get_option('undodir')):gsub('\\', '/')) (meths.get_option('undodir')):gsub('\\', '/'))
eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/view//', eq(root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/view//',
(meths.get_option('viewdir')):gsub('\\', '/')) (meths.get_option('viewdir')):gsub('\\', '/'))
end) end)
end) end)
@ -372,6 +375,7 @@ describe('XDG-based defaults', function()
XDG_CONFIG_HOME='$XDG_DATA_HOME', XDG_CONFIG_HOME='$XDG_DATA_HOME',
XDG_CONFIG_DIRS='$XDG_DATA_DIRS', XDG_CONFIG_DIRS='$XDG_DATA_DIRS',
XDG_DATA_HOME='$XDG_CONFIG_HOME', XDG_DATA_HOME='$XDG_CONFIG_HOME',
XDG_STATE_HOME='$XDG_CONFIG_HOME',
XDG_DATA_DIRS='$XDG_CONFIG_DIRS', XDG_DATA_DIRS='$XDG_CONFIG_DIRS',
}}) }})
end) end)
@ -405,13 +409,13 @@ describe('XDG-based defaults', function()
.. ',$XDG_DATA_DIRS/nvim/after' .. ',$XDG_DATA_DIRS/nvim/after'
.. ',$XDG_DATA_HOME/nvim/after' .. ',$XDG_DATA_HOME/nvim/after'
):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/')) ):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/'))
eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup//'), eq(('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'),
meths.get_option('backupdir'):gsub('\\', '/')) meths.get_option('backupdir'):gsub('\\', '/'))
eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/swap//'), eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'),
meths.get_option('directory'):gsub('\\', '/')) meths.get_option('directory'):gsub('\\', '/'))
eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo//'), eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'),
meths.get_option('undodir'):gsub('\\', '/')) meths.get_option('undodir'):gsub('\\', '/'))
eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view//'), eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'),
meths.get_option('viewdir'):gsub('\\', '/')) meths.get_option('viewdir'):gsub('\\', '/'))
meths.command('set all&') meths.command('set all&')
eq(('$XDG_DATA_HOME/nvim' eq(('$XDG_DATA_HOME/nvim'
@ -425,13 +429,13 @@ describe('XDG-based defaults', function()
.. ',$XDG_DATA_DIRS/nvim/after' .. ',$XDG_DATA_DIRS/nvim/after'
.. ',$XDG_DATA_HOME/nvim/after' .. ',$XDG_DATA_HOME/nvim/after'
):gsub('\\', '/'), (meths.get_option('runtimepath')):gsub('\\', '/')) ):gsub('\\', '/'), (meths.get_option('runtimepath')):gsub('\\', '/'))
eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup//'), eq(('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'),
meths.get_option('backupdir'):gsub('\\', '/')) meths.get_option('backupdir'):gsub('\\', '/'))
eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/swap//'), eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'),
meths.get_option('directory'):gsub('\\', '/')) meths.get_option('directory'):gsub('\\', '/'))
eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo//'), eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'),
meths.get_option('undodir'):gsub('\\', '/')) meths.get_option('undodir'):gsub('\\', '/'))
eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view//'), eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'),
meths.get_option('viewdir'):gsub('\\', '/')) meths.get_option('viewdir'):gsub('\\', '/'))
end) end)
end) end)
@ -442,6 +446,7 @@ describe('XDG-based defaults', function()
XDG_CONFIG_HOME=', , ,', XDG_CONFIG_HOME=', , ,',
XDG_CONFIG_DIRS=',-,-,' .. env_sep .. '-,-,-', XDG_CONFIG_DIRS=',-,-,' .. env_sep .. '-,-,-',
XDG_DATA_HOME=',=,=,', XDG_DATA_HOME=',=,=,',
XDG_STATE_HOME=',=,=,',
XDG_DATA_DIRS=',≡,≡,' .. env_sep .. '≡,≡,≡', XDG_DATA_DIRS=',≡,≡,' .. env_sep .. '≡,≡,≡',
}}) }})
end) end)
@ -484,13 +489,13 @@ describe('XDG-based defaults', function()
.. ',\\,-\\,-\\,' .. path_sep ..'nvim' .. path_sep ..'after' .. ',\\,-\\,-\\,' .. path_sep ..'nvim' .. path_sep ..'after'
.. ',\\, \\, \\,' .. path_sep ..'nvim' .. path_sep ..'after' .. ',\\, \\, \\,' .. path_sep ..'nvim' .. path_sep ..'after'
), meths.get_option('runtimepath')) ), meths.get_option('runtimepath'))
eq('.,\\,=\\,=\\,' .. path_sep .. data_dir .. '' .. path_sep ..'backup' .. (path_sep):rep(2), eq('.,\\,=\\,=\\,' .. path_sep .. state_dir .. '' .. path_sep ..'backup' .. (path_sep):rep(2),
meths.get_option('backupdir')) meths.get_option('backupdir'))
eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'swap' .. (path_sep):rep(2), eq('\\,=\\,=\\,' .. path_sep ..'' .. state_dir .. '' .. path_sep ..'swap' .. (path_sep):rep(2),
meths.get_option('directory')) meths.get_option('directory'))
eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'undo' .. (path_sep):rep(2), eq('\\,=\\,=\\,' .. path_sep ..'' .. state_dir .. '' .. path_sep ..'undo' .. (path_sep):rep(2),
meths.get_option('undodir')) meths.get_option('undodir'))
eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'view' .. (path_sep):rep(2), eq('\\,=\\,=\\,' .. path_sep ..'' .. state_dir .. '' .. path_sep ..'view' .. (path_sep):rep(2),
meths.get_option('viewdir')) meths.get_option('viewdir'))
end) end)
end) end)
@ -499,8 +504,9 @@ end)
describe('stdpath()', function() describe('stdpath()', function()
-- Windows appends 'nvim-data' instead of just 'nvim' to prevent collisions -- Windows appends 'nvim-data' instead of just 'nvim' to prevent collisions
-- due to XDG_CONFIG_HOME and XDG_DATA_HOME being the same. -- due to XDG_CONFIG_HOME, XDG_DATA_HOME and XDG_STATE_HOME being the same.
local datadir = iswin() and 'nvim-data' or 'nvim' local datadir = iswin() and 'nvim-data' or 'nvim'
local statedir = iswin() and 'nvim-data' or 'nvim'
local env_sep = iswin() and ';' or ':' local env_sep = iswin() and ';' or ':'
it('acceptance', function() it('acceptance', function()
@ -509,6 +515,7 @@ describe('stdpath()', function()
eq('nvim', funcs.fnamemodify(funcs.stdpath('cache'), ':t')) eq('nvim', funcs.fnamemodify(funcs.stdpath('cache'), ':t'))
eq('nvim', funcs.fnamemodify(funcs.stdpath('config'), ':t')) eq('nvim', funcs.fnamemodify(funcs.stdpath('config'), ':t'))
eq(datadir, funcs.fnamemodify(funcs.stdpath('data'), ':t')) eq(datadir, funcs.fnamemodify(funcs.stdpath('data'), ':t'))
eq(statedir, funcs.fnamemodify(funcs.stdpath('state'), ':t'))
eq('table', type(funcs.stdpath('config_dirs'))) eq('table', type(funcs.stdpath('config_dirs')))
eq('table', type(funcs.stdpath('data_dirs'))) eq('table', type(funcs.stdpath('data_dirs')))
assert_alive() -- Check for crash. #8393 assert_alive() -- Check for crash. #8393
@ -582,6 +589,39 @@ describe('stdpath()', function()
end) end)
end) end)
describe('with "state"' , function ()
it('knows XDG_STATE_HOME', function()
clear({env={
XDG_STATE_HOME=alter_slashes('/home/docwhat/.local'),
}})
eq(alter_slashes('/home/docwhat/.local/'..statedir), funcs.stdpath('state'))
end)
it('handles changes during runtime', function()
clear({env={
XDG_STATE_HOME=alter_slashes('/home/original'),
}})
eq(alter_slashes('/home/original/'..statedir), funcs.stdpath('state'))
command("let $XDG_STATE_HOME='"..alter_slashes('/home/new').."'")
eq(alter_slashes('/home/new/'..statedir), funcs.stdpath('state'))
end)
it("doesn't expand $VARIABLES", function()
clear({env={
XDG_STATE_HOME='$VARIABLES',
VARIABLES='this-should-not-happen',
}})
eq(alter_slashes('$VARIABLES/'..statedir), funcs.stdpath('state'))
end)
it("doesn't expand ~/", function()
clear({env={
XDG_STATE_HOME=alter_slashes('~/frobnitz'),
}})
eq(alter_slashes('~/frobnitz/'..statedir), funcs.stdpath('state'))
end)
end)
describe('with "cache"' , function () describe('with "cache"' , function ()
it('knows XDG_CACHE_HOME', function() it('knows XDG_CACHE_HOME', function()
clear({env={ clear({env={