diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 2afb22bb43..ae21bc47ca 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -55,6 +55,10 @@ NEW FEATURES *news-features* The following new APIs or features were added. +• A new environment variable named NVIM_APPNAME enables configuring the + directories where Neovim should find its configuration and state files. See + `:help $NVIM_APPNAME` . + • |nvim_open_win()| now accepts a relative `mouse` option to open a floating win relative to the mouse. Note that the mouse doesn't update frequently without setting `vim.o.mousemoveevent = true` diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 5e0718c3bb..67a88913fb 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1385,6 +1385,18 @@ STATE DIRECTORY (DEFAULT) ~ Note: Throughout the user manual these defaults are used as placeholders, e.g. "~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config". +NVIM_APPNAME *$NVIM_APPNAME* +The XDG directories used by Nvim can be further configured by setting the +`$NVIM_APPNAME` environment variable. This variable controls the directory +Neovim will look for (and auto-create) in the various XDG parent directories. +For example, setting `$NVIM_APPNAME` to "neovim" before running Neovim will +result in Neovim looking for configuration files in `$XDG_CONFIG_HOME/neovim` +instead of `$XDG_CONFIG_HOME/nvim`. + +Note: Similarly to the $XDG environment variables, when +`$XDG_CONFIG_HOME/nvim` is mentionned, it should be understood as +`$XDG_CONFIG_HOME/$NVIM_APPNAME`. + LOG FILE *$NVIM_LOG_FILE* *E5430* Besides 'debug' and 'verbose', Nvim keeps a general log file for internal debugging, plugins and RPC clients. > diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 92a8ffd646..ccea4f4951 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5368,13 +5368,14 @@ void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) return; } const void *iter = NULL; + const char *appname = get_appname(); do { size_t dir_len; const char *dir; iter = vim_env_iter(ENV_SEPCHAR, dirs, iter, &dir, &dir_len); if (dir != NULL && dir_len > 0) { char *dir_with_nvim = xmemdupz(dir, dir_len); - dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true); + dir_with_nvim = concat_fnames_realloc(dir_with_nvim, appname, true); tv_list_append_string(list, dir_with_nvim, (ssize_t)strlen(dir_with_nvim)); xfree(dir_with_nvim); } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index a3cdd2bb23..755b8e0834 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5181,7 +5181,10 @@ static void vim_mktempdir(void) // "/tmp/" exists, now try to create "/tmp/nvim./". add_pathsep(tmp); - xstrlcat(tmp, "nvim.", sizeof(tmp)); + + const char *appname = get_appname(); + xstrlcat(tmp, appname, sizeof(tmp)); + xstrlcat(tmp, ".", sizeof(tmp)); xstrlcat(tmp, user, sizeof(tmp)); (void)os_mkdir(tmp, 0700); // Always create, to avoid a race. bool owned = os_file_owned(tmp); diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 1d75c208be..c6a0a4d8cc 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -91,13 +91,14 @@ char *server_address_new(const char *name) { static uint32_t count = 0; char fmt[ADDRESS_MAX_SIZE]; + const char *appname = get_appname(); #ifdef MSWIN int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32, - name ? name : "nvim", os_get_pid(), count++); + name ? name : appname, os_get_pid(), count++); #else char *dir = stdpaths_get_xdg_var(kXDGRuntimeDir); int r = snprintf(fmt, sizeof(fmt), "%s/%s.%" PRIu64 ".%" PRIu32, - dir, name ? name : "nvim", os_get_pid(), count++); + dir, name ? name : appname, os_get_pid(), count++); xfree(dir); #endif if ((size_t)r >= sizeof(fmt)) { diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index 6b07b6ef70..5235828f7a 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -57,6 +57,18 @@ static const char *const xdg_defaults[] = { #endif }; +/// Get the value of $NVIM_APPNAME or "nvim" if not set. +/// +/// @return $NVIM_APPNAME value +const char *get_appname(void) +{ + const char *env_val = os_getenv("NVIM_APPNAME"); + if (env_val == NULL || *env_val == '\0') { + env_val = "nvim"; + } + return env_val; +} + /// Return XDG variable value /// /// @param[in] idx XDG variable to use. @@ -100,25 +112,28 @@ char *stdpaths_get_xdg_var(const XDGVarType idx) /// Return Nvim-specific XDG directory subpath. /// -/// Windows: Uses "…/nvim-data" for kXDGDataHome to avoid storing +/// Windows: Uses "…/$NVIM_APPNAME-data" for kXDGDataHome to avoid storing /// configuration and data files in the same path. #4403 /// /// @param[in] idx XDG directory to use. /// -/// @return [allocated] "{xdg_directory}/nvim" +/// @return [allocated] "{xdg_directory}/$NVIM_APPNAME" char *get_xdg_home(const XDGVarType idx) FUNC_ATTR_WARN_UNUSED_RESULT { char *dir = stdpaths_get_xdg_var(idx); + const char *appname = get_appname(); + size_t appname_len = strlen(appname); + assert(appname_len < (IOSIZE - sizeof("-data"))); + if (dir) { + xstrlcpy(IObuff, appname, appname_len + 1); #if defined(MSWIN) - dir = concat_fnames_realloc(dir, - ((idx == kXDGDataHome - || idx == kXDGStateHome) ? "nvim-data" : "nvim"), - true); -#else - dir = concat_fnames_realloc(dir, "nvim", true); + if (idx == kXDGDataHome || idx == kXDGStateHome) { + STRCAT(IObuff, "-data"); + } #endif + dir = concat_fnames_realloc(dir, IObuff, true); #ifdef BACKSLASH_IN_FILENAME slash_adjust(dir); @@ -131,7 +146,7 @@ char *get_xdg_home(const XDGVarType idx) /// /// @param[in] fname New component of the path. /// -/// @return [allocated] `$XDG_CACHE_HOME/nvim/{fname}` +/// @return [allocated] `$XDG_CACHE_HOME/$NVIM_APPNAME/{fname}` char *stdpaths_user_cache_subpath(const char *fname) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { @@ -142,7 +157,7 @@ char *stdpaths_user_cache_subpath(const char *fname) /// /// @param[in] fname New component of the path. /// -/// @return [allocated] `$XDG_CONFIG_HOME/nvim/{fname}` +/// @return [allocated] `$XDG_CONFIG_HOME/$NVIM_APPNAME/{fname}` char *stdpaths_user_conf_subpath(const char *fname) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { @@ -153,7 +168,7 @@ char *stdpaths_user_conf_subpath(const char *fname) /// /// @param[in] fname New component of the path. /// -/// @return [allocated] `$XDG_DATA_HOME/nvim/{fname}` +/// @return [allocated] `$XDG_DATA_HOME/$NVIM_APPNAME/{fname}` char *stdpaths_user_data_subpath(const char *fname) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { @@ -166,7 +181,7 @@ char *stdpaths_user_data_subpath(const char *fname) /// @param[in] trailing_pathseps Amount of trailing path separators to add. /// @param[in] escape_commas If true, all commas will be escaped. /// -/// @return [allocated] `$XDG_STATE_HOME/nvim/{fname}`. +/// @return [allocated] `$XDG_STATE_HOME/$NVIM_APPNAME/{fname}`. char *stdpaths_user_state_subpath(const char *fname, const size_t trailing_pathseps, const bool escape_commas) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 9dc213d718..3487a8d7a2 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -1435,7 +1435,6 @@ static inline size_t compute_double_env_sep_len(const char *const val, const siz return ret; } -#define NVIM_SIZE (sizeof("nvim") - 1) /// Add directories to a ENV_SEPCHAR-separated array from a colon-separated one /// /// Commas are escaped in process. To each item PATHSEP "nvim" is appended in @@ -1464,6 +1463,8 @@ static inline char *add_env_sep_dirs(char *dest, const char *const val, const ch return dest; } const void *iter = NULL; + const char *appname = get_appname(); + const size_t appname_len = strlen(appname); do { size_t dir_len; const char *dir; @@ -1474,8 +1475,8 @@ static inline char *add_env_sep_dirs(char *dest, const char *const val, const ch if (!after_pathsep(dest - 1, dest)) { *dest++ = PATHSEP; } - memmove(dest, "nvim", NVIM_SIZE); - dest += NVIM_SIZE; + memmove(dest, appname, appname_len); + dest += appname_len; if (suf1 != NULL) { *dest++ = PATHSEP; memmove(dest, suf1, len1); @@ -1529,14 +1530,18 @@ static inline char *add_dir(char *dest, const char *const dir, const size_t dir_ if (!after_pathsep(dest - 1, dest)) { *dest++ = PATHSEP; } + const char *appname = get_appname(); + size_t appname_len = strlen(appname); + assert(appname_len < (IOSIZE - sizeof("-data"))); + xstrlcpy(IObuff, appname, appname_len + 1); #if defined(MSWIN) - size_t size = (type == kXDGDataHome ? sizeof("nvim-data") - 1 : NVIM_SIZE); - memmove(dest, (type == kXDGDataHome ? "nvim-data" : "nvim"), size); - dest += size; -#else - memmove(dest, "nvim", NVIM_SIZE); - dest += NVIM_SIZE; + if (type == kXDGDataHome || type == kXDGStateHome) { + STRCAT(IObuff, "-data"); + appname_len += 5; + } #endif + xstrlcpy(dest, IObuff, appname_len + 1); + dest += appname_len; if (suf1 != NULL) { *dest++ = PATHSEP; memmove(dest, suf1, len1); @@ -1596,16 +1601,17 @@ char *runtimepath_default(bool clean_arg) size_t config_len = 0; size_t vimruntime_len = 0; size_t libdir_len = 0; + const char *appname = get_appname(); + size_t appname_len = strlen(appname); if (data_home != NULL) { data_len = strlen(data_home); - if (data_len != 0) { + size_t nvim_data_size = appname_len; #if defined(MSWIN) - size_t nvim_size = (sizeof("nvim-data") - 1); -#else - size_t nvim_size = NVIM_SIZE; + nvim_data_size += sizeof("-data"); #endif + if (data_len != 0) { rtp_size += ((data_len + memcnt(data_home, ',', data_len) - + nvim_size + 1 + SITE_SIZE + 1 + + nvim_data_size + 1 + SITE_SIZE + 1 + !after_pathsep(data_home, data_home + data_len)) * 2 + AFTER_SIZE + 1); } @@ -1614,7 +1620,7 @@ char *runtimepath_default(bool clean_arg) config_len = strlen(config_home); if (config_len != 0) { rtp_size += ((config_len + memcnt(config_home, ',', config_len) - + NVIM_SIZE + 1 + + appname_len + 1 + !after_pathsep(config_home, config_home + config_len)) * 2 + AFTER_SIZE + 1); } @@ -1632,9 +1638,9 @@ char *runtimepath_default(bool clean_arg) } } rtp_size += compute_double_env_sep_len(data_dirs, - NVIM_SIZE + 1 + SITE_SIZE + 1, + appname_len + 1 + SITE_SIZE + 1, AFTER_SIZE + 1); - rtp_size += compute_double_env_sep_len(config_dirs, NVIM_SIZE + 1, + rtp_size += compute_double_env_sep_len(config_dirs, appname_len + 1, AFTER_SIZE + 1); char *rtp = NULL; if (rtp_size == 0) { @@ -1675,7 +1681,6 @@ freeall: return rtp; } -#undef NVIM_SIZE static void cmd_source(char *fname, exarg_T *eap) { diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index 84ec43f4cb..4242b6e493 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -566,8 +566,12 @@ end) describe('stdpath()', function() -- Windows appends 'nvim-data' instead of just 'nvim' to prevent collisions -- due to XDG_CONFIG_HOME, XDG_DATA_HOME and XDG_STATE_HOME being the same. - local datadir = is_os('win') and 'nvim-data' or 'nvim' - local statedir = is_os('win') and 'nvim-data' or 'nvim' + local function maybe_data(name) + return is_os('win') and name .. '-data' or name + end + + local datadir = maybe_data('nvim') + local statedir = maybe_data('nvim') local env_sep = is_os('win') and ';' or ':' it('acceptance', function() @@ -583,6 +587,24 @@ describe('stdpath()', function() assert_alive() -- Check for crash. #8393 end) + it('reacts to #NVIM_APPNAME', function() + local appname = "NVIM_APPNAME_TEST____________________________________" .. + "______________________________________________________________________" + clear({env={ NVIM_APPNAME=appname }}) + eq(appname, funcs.fnamemodify(funcs.stdpath('config'), ':t')) + eq(appname, funcs.fnamemodify(funcs.stdpath('cache'), ':t')) + eq(maybe_data(appname), funcs.fnamemodify(funcs.stdpath('log'), ':t')) + eq(maybe_data(appname), funcs.fnamemodify(funcs.stdpath('data'), ':t')) + eq(maybe_data(appname), funcs.fnamemodify(funcs.stdpath('state'), ':t')) + -- config_dirs and data_dirs are empty on windows, so don't check them on + -- that platform + if not is_os('win') then + eq(appname, funcs.fnamemodify(funcs.stdpath('config_dirs')[1], ':t')) + eq(appname, funcs.fnamemodify(funcs.stdpath('data_dirs')[1], ':t')) + end + assert_alive() -- Check for crash. #8393 + end) + context('returns a String', function() describe('with "config"' , function ()