# HG changeset patch # User Christian Brabandt # Date 1713128403 -7200 # Node ID 2d7721a4d21365be3413223551c3a2300deaaa4a # Parent 2418c55e3ba68ced1a2cbc6b8c5ee3afe405dd90 patch 9.1.0327: No support for using $XDG_CONFIG_HOME Commit: https://github.com/vim/vim/commit/c9df1fb35a1866901c32df37dd39c8b39dbdb64a Author: Luca Saccarola Date: Sun Apr 14 22:53:22 2024 +0200 patch 9.1.0327: No support for using $XDG_CONFIG_HOME Problem: No support for using $XDG_CONFIG_HOME Solution: optionally source $XDG_CONFIG_HOME/vim/vimrc (Luca Saccarola) fixes: #2034 closes: #14182 Signed-off-by: Luca Saccarola Signed-off-by: Christian Brabandt diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1,4 +1,4 @@ -*starting.txt* For Vim version 9.1. Last change: 2024 Mar 13 +*starting.txt* For Vim version 9.1. Last change: 2024 Apr 14 VIM REFERENCE MANUAL by Bram Moolenaar @@ -811,7 +811,8 @@ 3. Execute Ex commands, from environment name. Also see |vimrc-intro|. Places for your personal initializations: - Unix $HOME/.vimrc or $HOME/.vim/vimrc + Unix $HOME/.vimrc, $HOME/.vim/vimrc + or $XDG_CONFIG_HOME/vim/vimrc MS-Windows $HOME/_vimrc, $HOME/vimfiles/vimrc or $VIM/_vimrc Amiga s:.vimrc, home:.vimrc, home:vimfiles:vimrc @@ -853,15 +854,16 @@ 3. Execute Ex commands, from environment I The environment variable VIMINIT (see also |compatible-default|) (*) The value of $VIMINIT is used as an Ex command line. II The user vimrc file(s): - "$HOME/.vimrc" (for Unix) (*) - "$HOME/.vim/vimrc" (for Unix) (*) - "s:.vimrc" (for Amiga) (*) - "home:.vimrc" (for Amiga) (*) - "home:vimfiles:vimrc" (for Amiga) (*) - "$VIM/.vimrc" (for Amiga) (*) - "$HOME/_vimrc" (for Win32) (*) - "$HOME/vimfiles/vimrc" (for Win32) (*) - "$VIM/_vimrc" (for Win32) (*) + "$HOME/.vimrc" (for Unix) (*) + "$HOME/.vim/vimrc" (for Unix) (*) + "$HOME/.config/vim/vimrc" (for Unix) (*) + "s:.vimrc" (for Amiga) (*) + "home:.vimrc" (for Amiga) (*) + "home:vimfiles:vimrc" (for Amiga) (*) + "$VIM/.vimrc" (for Amiga) (*) + "$HOME/_vimrc" (for Win32) (*) + "$HOME/vimfiles/vimrc" (for Win32) (*) + "$VIM/_vimrc" (for Win32) (*) "$HOME/config/settings/vim/vimrc" (for Haiku) (*) Note: For Unix and Amiga, when ".vimrc" does not exist, @@ -1085,6 +1087,44 @@ defaults.vim from your .vimrc, first unl example above. + *xdg-base-dir* *$XDG_CONFIG_HOME* +XDG Base Directory Specification ~ + +The XDG Base Directory Specification aims to define a standard location for +configuration files used by applications. This is mainly done to prevent +the legacy behavior of dumping everything into the users home directory. +The specification can be found online at +https://specifications.freedesktop.org/basedir-spec/latest/ + +The location of this standard configuration directory is configurable by the +user, using environment variable but should also give fallback in case those +variables weren't set. + +This is a not an exhaustive list of those directories: + Environment var default location Description ~ + `$XDG_CACHE_HOME` $HOME/.cache Ephemiral data files + `$XDG_CONFIG_HOME` $HOME/.config Configuration files + `$XDG_DATA_HOME` $HOME/.local/share Persistent data files + `$XDG_STATE_HOME` $HOME/.local/state State data files + +Vim will only care of the `$XDG_CONFIG_HOME` directory, the others are not +(yet) used for its various configuration and state files. + + *xdg-vimrc* +Vim, on Unix systems, will look at `$XDG_CONFIG_HOME/vim/vimrc` for its +configuration (see |vimrc|) but it will source it only if no other +initialization file is found in `$HOME` or `$HOME/.vim` (thus making this +feature backward compatible). However, if you want to migrate to use +`$XDG_CONFIG_HOME/vim/` directory, you will have to move away your `~/.vimrc` +and `~/.vim/vimrc` file. + + *xdg-runtime* +When the |xdg-vimrc| is used the |'runtimepath'| will be modified accordingly +to respect the |xdg-base-dir|: > + + "$XDG_CONFIG_HOME/vim,$VIMRUNTIME,/after,$XDG_CONFIG_HOME/vim/after" +< + Avoiding trojan horses ~ *trojan-horse* While reading the "vimrc" or the "exrc" file in the current directory, some diff --git a/runtime/doc/tags b/runtime/doc/tags --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -12,6 +12,7 @@ $VIM-use version5.txt /*$VIM-use* $VIMRUNTIME starting.txt /*$VIMRUNTIME* $VIM_POSIX vi_diff.txt /*$VIM_POSIX* +$XDG_CONFIG_HOME starting.txt /*$XDG_CONFIG_HOME* $quote eval.txt /*$quote* % motion.txt /*%* %:. cmdline.txt /*%:.* @@ -11371,6 +11372,9 @@ x11-clientserver remote.txt /*x11-client x11-cut-buffer gui_x11.txt /*x11-cut-buffer* x11-selection gui_x11.txt /*x11-selection* xattr editing.txt /*xattr* +xdg-base-dir starting.txt /*xdg-base-dir* +xdg-runtime starting.txt /*xdg-runtime* +xdg-vimrc starting.txt /*xdg-vimrc* xf86conf.vim syntax.txt /*xf86conf.vim* xfontset mbyte.txt /*xfontset* xfree-xterm syntax.txt /*xfree-xterm* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -41543,6 +41543,8 @@ and is a work in progress. Support for Wayland UI. +Support for the XDG Desktop Specification |xdg-base-dir| + Vim9 script ----------- Add support for internal builtin functions with vim9 objects, see diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -3276,6 +3276,10 @@ source_startup_scripts(mparm_T *parmp) && do_source((char_u *)USR_VIMRC_FILE2, TRUE, DOSO_VIMRC, NULL) == FAIL #endif +#ifdef XDG_VIMRC_FILE + && do_source((char_u *)XDG_VIMRC_FILE, TRUE, + DOSO_VIMRC, NULL) == FAIL +#endif #ifdef USR_VIMRC_FILE3 && do_source((char_u *)USR_VIMRC_FILE3, TRUE, DOSO_VIMRC, NULL) == FAIL diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -364,6 +364,66 @@ set_init_clean_rtp(void) } #endif +#ifdef UNIX +/* + * Change 'runtimepath' and 'packdir' to '$XDG_CONFIG_HOME/vim' if the only + * vimrc found is located in '$XDG_CONFIG_HOME/vim/vimrc'. + * In case the '$XDG_CONFIG_HOME' variable is not set, '$HOME/.config' is used + * as a fallback as is defined in the XDG base dir specification: + * + */ + static void +set_init_xdg_rtp(void) +{ + int opt_idx; + int has_xdg_env = TRUE; + int should_free_xdg_dir = FALSE; + char_u *vimrc1 = NULL; + char_u *vimrc2 = NULL; + char_u *xdg_dir = NULL; + char_u *xdg_rtp = NULL; + char_u *vimrc_xdg = NULL; + + vimrc1 = expand_env_save((char_u *)USR_VIMRC_FILE); + vimrc2 = expand_env_save((char_u *)USR_VIMRC_FILE2); + + xdg_dir = mch_getenv("XDG_CONFIG_HOME"); + if (!xdg_dir) + { + xdg_dir = expand_env_save((char_u *)"~/.config"); + should_free_xdg_dir = TRUE; + has_xdg_env = FALSE; + } + vimrc_xdg = concat_fnames(xdg_dir, (char_u *)"vim/vimrc", TRUE); + + if (file_is_readable(vimrc1) || file_is_readable(vimrc2) || + !file_is_readable(vimrc_xdg)) + goto theend; + + xdg_rtp = has_xdg_env ? (char_u *)XDG_RUNTIMEPATH + : (char_u *)XDG_RUNTIMEPATH_FB; + + if ((opt_idx = findoption((char_u *)"runtimepath")) < 0) + goto theend; + + options[opt_idx].def_val[VI_DEFAULT] = xdg_rtp; + p_rtp = xdg_rtp; + + if ((opt_idx = findoption((char_u *)"packpath")) < 0) + goto theend; + + options[opt_idx].def_val[VI_DEFAULT] = xdg_rtp; + p_pp = xdg_rtp; + +theend: + vim_free(vimrc1); + vim_free(vimrc2); + vim_free(vimrc_xdg); + if (should_free_xdg_dir) + vim_free(xdg_dir); +} +#endif + /* * Expand environment variables and things like "~" for the defaults. * If option_expand() returns non-NULL the variable is expanded. This can @@ -588,6 +648,7 @@ set_init_1(int clean_arg) set_options_default(0); #ifdef UNIX + set_init_xdg_rtp(); set_init_restricted_mode(); #endif diff --git a/src/os_unix.h b/src/os_unix.h --- a/src/os_unix.h +++ b/src/os_unix.h @@ -236,7 +236,7 @@ typedef struct dsc$descriptor DESC; # ifdef VMS # define USR_VIMRC_FILE "sys$login:.vimrc" # else -# define USR_VIMRC_FILE "$HOME/.vimrc" +# define USR_VIMRC_FILE "~/.vimrc" # endif #endif @@ -249,6 +249,12 @@ typedef struct dsc$descriptor DESC; # endif #endif +#ifndef XDG_VIMRC_FILE +# define XDG_VIMRC_FILE mch_getenv("XDG_CONFIG_HOME") \ + ? (char_u *)"$XDG_CONFIG_HOME/vim/vimrc" \ + : (char_u *)"~/.config/vim/vimrc" +#endif + #if !defined(USR_VIMRC_FILE3) && defined(VMS) # define USR_VIMRC_FILE3 "sys$login:_vimrc" #endif @@ -349,13 +355,19 @@ typedef struct dsc$descriptor DESC; # ifdef RUNTIME_GLOBAL # ifdef RUNTIME_GLOBAL_AFTER # define DFLT_RUNTIMEPATH "~/.vim," RUNTIME_GLOBAL ",$VIMRUNTIME," RUNTIME_GLOBAL_AFTER ",~/.vim/after" +# define XDG_RUNTIMEPATH "$XDG_CONFIG_HOME/vim," RUNTIME_GLOBAL ",$VIMRUNTIME," RUNTIME_GLOBAL_AFTER "/after,$XDG_CONFIG_HOME/vim/after" +# define XDG_RUNTIMEPATH_FB "~/.config/vim," RUNTIME_GLOBAL ",$VIMRUNTIME," RUNTIME_GLOBAL_AFTER "/after,~/.config/vim/after" # define CLEAN_RUNTIMEPATH RUNTIME_GLOBAL ",$VIMRUNTIME," RUNTIME_GLOBAL_AFTER # else # define DFLT_RUNTIMEPATH "~/.vim," RUNTIME_GLOBAL ",$VIMRUNTIME," RUNTIME_GLOBAL "/after,~/.vim/after" +# define XDG_RUNTIMEPATH "$XDG_CONFIG_HOME/vim," RUNTIME_GLOBAL ",$VIMRUNTIME," RUNTIME_GLOBAL "/after,$XDG_CONFIG_HOME/vim/after" +# define XDG_RUNTIMEPATH_FB "~/.config/vim," RUNTIME_GLOBAL ",$VIMRUNTIME," RUNTIME_GLOBAL "/after,~/.config/vim/after" # define CLEAN_RUNTIMEPATH RUNTIME_GLOBAL ",$VIMRUNTIME," RUNTIME_GLOBAL "/after" # endif # else # define DFLT_RUNTIMEPATH "~/.vim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,~/.vim/after" +# define XDG_RUNTIMEPATH "$XDG_CONFIG_HOME/vim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,$XDG_CONFIG_HOME/vim/after" +# define XDG_RUNTIMEPATH_FB "~/.config/vim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,~/.config/vim/after" # define CLEAN_RUNTIMEPATH "$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after" # endif # endif diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -331,6 +331,7 @@ NEW_TESTS = \ test_wnext \ test_wordcount \ test_writefile \ + test_xdg \ test_xxd \ test_alot_latin \ test_alot_utf8 \ @@ -564,6 +565,7 @@ NEW_TESTS_RES = \ test_winfixbuf.res \ test_wordcount.res \ test_writefile.res \ + test_xdg.res \ test_xxd.res \ test_alot_latin.res \ test_alot_utf8.res \ diff --git a/src/testdir/dumps/Test_xdg_1.dump b/src/testdir/dumps/Test_xdg_1.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_xdg_1.dump @@ -0,0 +1,20 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|r+0#0000000&|c|_|o|n|e| @16|o|n|e| @48 +|r|c| @20|.|v|i|m|r|c| @45 +|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35 diff --git a/src/testdir/dumps/Test_xdg_2.dump b/src/testdir/dumps/Test_xdg_2.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_xdg_2.dump @@ -0,0 +1,20 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|r+0#0000000&|c|_|t|w|o| @16|t|w|o| @48 +|r|c| @20|.|v|i|m|/|v|i|m|r|c| @41 +|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35 diff --git a/src/testdir/dumps/Test_xdg_3.dump b/src/testdir/dumps/Test_xdg_3.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_xdg_3.dump @@ -0,0 +1,20 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|r+0#0000000&|c|_|t|h|r|e@1| @14|t|h|r|e@1| @46 +|r|c| @20|.|c|o|n|f|i|g|/|v|i|m|/|v|i|m|r|c| @34 +|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35 diff --git a/src/testdir/dumps/Test_xdg_4.dump b/src/testdir/dumps/Test_xdg_4.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_xdg_4.dump @@ -0,0 +1,20 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|r+0#0000000&|c|_|f|o|u|r| @15|f|o|u|r| @47 +|r|c| @20|x|d|g|/|v|i|m|/|v|i|m|r|c| @38 +|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35 diff --git a/src/testdir/test_xdg.vim b/src/testdir/test_xdg.vim new file mode 100644 --- /dev/null +++ b/src/testdir/test_xdg.vim @@ -0,0 +1,140 @@ +" Tests for the XDG feature + +source check.vim +CheckFeature terminal + +source shared.vim +source screendump.vim +source mouse.vim +source term_util.vim + +func s:get_rcs() + let rcs = { + \ 'file1': { 'path': '~/.vimrc' }, + \ 'file2': { 'path': '~/.vim/vimrc' }, + \ 'xdg': { 'path': exists('$XDG_CONFIG_HOME') ? '$XDG_CONFIG_HOME' : "~/.config" }, + \} + for v in values(rcs) + let v.exists = filereadable(expand(v.path)) + endfor + return rcs +endfunc + +func Test_xdg_rc_detection() + CheckUnix + let rc = s:get_rcs() + let before =<< trim CODE + call writefile([expand('$MYVIMRC')], "XMY_VIMRC") + quit! + CODE + call RunVim(before, [], "") + let my_rc = readfile("XMY_VIMRC") + if rc.file1.exists + call assert_equal(rc.file1.path, my_rc) + elseif !rc.file1.exists && rc.file2.exists + call assert_equal(rc.file2.path, my_rc) + elseif !rc.file1.exists && !rc.file2.exists && rc.xdg.exists + call assert_equal(rc.xdg.path, my_rc) + endif + call delete("XMY_VIMRC") +endfunc + +func Test_xdg_runtime_files() + " This tests, that the initialization file from + " ~/.vimrc, ~/.vim/vimrc and ~/.config/vim/vimrc (or + " $XDG_HOMECONFIG/vim/vimrc) are sourced in that order + CheckUnix + call mkdir(expand('~/.vim/'), 'pD') + call mkdir(expand('~/.config/vim/'), 'pD') + call mkdir(expand('~/xdg/vim/'), 'pD') + + let rc1=expand('~/.vimrc') + let rc2=expand('~/.vim/vimrc') + let rc3=expand('~/.config/vim/vimrc') + let rc4=expand('~/xdg/vim/vimrc') + + " g:rc_one|two|three|four is to verify, that the other + " init files are not source + " g:rc is to verify which rc file has been loaded. + let file1 =<< trim CODE + let g:rc_one = 'one' + let g:rc = '.vimrc' + CODE + let file2 =<< trim CODE + let g:rc_two = 'two' + let g:rc = '.vim/vimrc' + CODE + let file3 =<< trim CODE + let g:rc_three = 'three' + let g:rc = '.config/vim/vimrc' + CODE + let file4 =<< trim CODE + let g:rc_four = 'four' + let g:rc = 'xdg/vim/vimrc' + CODE + call writefile(file1, rc1) + call writefile(file2, rc2) + call writefile(file3, rc3) + call writefile(file4, rc4) + + let rows = 20 + let buf = RunVimInTerminal('', #{rows: rows, no_clean: 1}) + call TermWait(buf) + call term_sendkeys(buf, ":echo \$MYVIMRC\") + call WaitForAssert({-> assert_match('XfakeHOME/\.vimrc', term_getline(buf, rows))}) + call term_sendkeys(buf, ":call filter(g:, {idx, _ -> idx =~ '^rc'})\") + call TermWait(buf) + call term_sendkeys(buf, ":redraw!\") + call TermWait(buf) + call term_sendkeys(buf, ":let g:\") + call VerifyScreenDump(buf, 'Test_xdg_1', {}) + call StopVimInTerminal(buf) + call delete(rc1) + bw + + let buf = RunVimInTerminal('', #{rows: rows, no_clean: 1}) + call TermWait(buf) + call term_sendkeys(buf, ":echo \$MYVIMRC\") + call WaitForAssert({-> assert_match('XfakeHOME/\.vim/vimrc', term_getline(buf, rows))}) + call term_sendkeys(buf, ":call filter(g:, {idx, _ -> idx =~ '^rc'})\") + call TermWait(buf) + call term_sendkeys(buf, ":redraw!\") + call TermWait(buf) + call term_sendkeys(buf, ":let g:\") + call VerifyScreenDump(buf, 'Test_xdg_2', {}) + call StopVimInTerminal(buf) + call delete(rc2) + bw + + let buf = RunVimInTerminal('', #{rows: rows, no_clean: 1}) + call TermWait(buf) + call term_sendkeys(buf, ":echo \$MYVIMRC\") + call WaitForAssert({-> assert_match('XfakeHOME/\.config/vim/vimrc', term_getline(buf, rows))}) + call term_sendkeys(buf, ":call filter(g:, {idx, _ -> idx =~ '^rc'})\") + call TermWait(buf) + call term_sendkeys(buf, ":redraw!\") + call TermWait(buf) + call term_sendkeys(buf, ":let g:\") + call VerifyScreenDump(buf, 'Test_xdg_3', {}) + call StopVimInTerminal(buf) + call delete(rc3) + bw + + let $XDG_CONFIG_HOME=expand('~/xdg/') + let buf = RunVimInTerminal('', #{rows: rows, no_clean: 1}) + call TermWait(buf) + call term_sendkeys(buf, ":redraw!\") + call TermWait(buf) + call term_sendkeys(buf, ":echo \$MYVIMRC\") + call WaitForAssert({-> assert_match('xdg/vim/vimrc', term_getline(buf, rows))}) + call term_sendkeys(buf, ":call filter(g:, {idx, _ -> idx =~ '^rc'})\") + call TermWait(buf) + call term_sendkeys(buf, ":let g:\") + call VerifyScreenDump(buf, 'Test_xdg_4', {}) + call StopVimInTerminal(buf) + call delete(rc4) + bw + unlet $XDG_CONFIG_HOME +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 327, +/**/ 326, /**/ 325,