Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for XDG Base Directory Specification #1282

Merged
merged 19 commits into from
Jun 1, 2018
Merged

Conversation

mark64
Copy link
Contributor

@mark64 mark64 commented May 6, 2018

This makes it possible to keep your home directory free from fzf dotfiles. One thing to note is that this does not handle the case where a ~/.fzf.bash file already exists. I can include that if desired.

install Outdated
@@ -9,6 +9,13 @@ update_config=2
binary_arch=
allow_legacy=
shells="bash zsh fish"
config_dir=~/.config/fzf
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't want to suddenly change the default directory after 4 and a half years. Please change it back to ~.
(Also note that Travis CI build is failing because of the change.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reverted the default directory to ~/, but also added a check to see if the user manually created a ~/.config/fzf directory, which I hope is fine since some users want their dotfiles in ~/.confg but don't want to set extra environment variables.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me.

install Outdated
fish_dir=~/.config/fish
if [[ -v XDG_CONFIG_HOME ]] && [ ! -z $XDG_CONFIG_HOME ] && [ -d $XDG_CONFIG_HOME ]; then
config_dir=$XDG_CONFIG_HOME/fzf
fish_dir=$XDG_CONFIG_HOME/fish
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use 2-space indentation throughout the code.

uninstall Outdated
fish_dir=~/.config/fish
if [[ -v XDG_CONFIG_HOME ]] && [ ! -z $XDG_CONFIG_HOME ] && [ -d $XDG_CONFIG_HOME ]; then
config_dir=$XDG_CONFIG_HOME/fzf
fish_dir=$XDG_CONFIG_HOME/fish
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation.

install Outdated
@@ -9,6 +9,13 @@ update_config=2
binary_arch=
allow_legacy=
shells="bash zsh fish"
config_dir=~/.config/fzf
fish_dir=~/.config/fish
if [[ -v XDG_CONFIG_HOME ]] && [ ! -z $XDG_CONFIG_HOME ] && [ -d $XDG_CONFIG_HOME ]; then
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-z seems unnecessary. We can write [[ -v XDG_CONFIG_HOME ]] && [[ -d "$XDG_CONFIG_HOME" ]]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-z does seems unnecessary, but the XDG base dir spec allows for that env var to be empty, in which case it should be ignored and code should use a default directory (normally ~/.config).

From here

$XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, thanks.

install Outdated
@@ -9,6 +9,15 @@ update_config=2
binary_arch=
allow_legacy=
shells="bash zsh fish"
config_base=~/.
fish_dir=~/.config/fish
Copy link
Owner

@junegunn junegunn May 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No slash at the end. Actually, I don't like requiring $fish_dir to have a trailing slash, and I would much prefer $fish_dir/functions over ${fish_dir}functions as it's cleaner and less bug-prone. I can see that it's necessary for $config_base, so we have either ~/.fzf.bash or ~/.config/fzf/fzf.bash but we don't have to do the same for $fish_dir. Maybe we can rename $config_base to $config_prefix, to make the difference more explicit ("prefix" vs. "dir").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@junegunn junegunn added this to the 0.17.4 milestone May 13, 2018
@junegunn
Copy link
Owner

Thanks for the update, there's one more problem.

Before the patch:

Update /Users/jg/.bashrc:
  - [ -f ~/.fzf.bash ] && source ~/.fzf.bash
    - Already exists: line #559

Update /Users/jg/.zshrc:
  - [ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
    - Already exists: line #86

and after the patch

Update /Users/jg/.bashrc:
  - [ -f /Users/jg/.fzf.bash ] && source /Users/jg/.fzf.bash
    + Added

Update /Users/jg/.zshrc:
  - [ -f /Users/jg/.fzf.zsh ] && source /Users/jg/.fzf.zsh
    + Added

You see the difference? And I end up with both versions of the line in my .bashrc

[ -f ~/.fzf.bash ] && source ~/.fzf.bash

[ -f /Users/jg/.fzf.bash ] && source /Users/jg/.fzf.bash

This is not desirable, and I have noticed that most users prefer the former as it can be used with different usernames.

The following patch seems to fix the problem. Please let me know if you have a better idea.

diff --git a/install b/install
index d131c82..ee851ad 100755
--- a/install
+++ b/install
@@ -9,14 +9,14 @@ update_config=2
 binary_arch=
 allow_legacy=
 shells="bash zsh fish"
-config_prefix=~/.
+config_prefix="~/."
 fish_dir=~/.config/fish
 if [[ -v XDG_CONFIG_HOME ]] && [[ ! -z $XDG_CONFIG_HOME ]] && [[ -d $XDG_CONFIG_HOME ]]; then
   config_prefix=$XDG_CONFIG_HOME/fzf/
   fish_dir=$XDG_CONFIG_HOME/fish
   mkdir -p $config_prefix
 elif [[ -d ~/.config/fzf ]]; then
-  config_prefix=~/.config/fzf/
+  config_prefix="~/.config/fzf/"
 fi
 
 help() {
@@ -262,7 +262,8 @@ for shell in $shells; do
     fzf_key_bindings="# $fzf_key_bindings"
   fi
 
-  cat > $src << EOF
+  eval "src=\"$src\""
+  cat > "$src" << EOF
 # Setup fzf
 # ---------
 if [[ ! "\$PATH" == *$fzf_base/bin* ]]; then

@mark64
Copy link
Contributor Author

mark64 commented May 14, 2018

Good point, thanks for the patch!

@junegunn
Copy link
Owner

Thanks, one more thing though, can you make the code compatible with the paths with whitespaces? We didn't have the problem before, but now we have to consider such unusual cases.

export XDG_CONFIG_HOME="/tmp/foo bar"
mkdir -p "$XDG_CONFIG_HOME"
./install --all

@junegunn
Copy link
Owner

  1. So we're disallowing $XDG_CONFIG_HOME with spaces. Is it officially forbidden by the spec? Or are we just ignoring the case to simplify the code?
  2. If a user temporarily sets $XDG_CONFIG_HOME just before running the script, the added line will refer to it, but the variable may not be available on other sessions, which can be a source of confusion. One way to avoid such a situation is to provide a default value for $XDG_CONFIG_HOME; i.e. config_prefix='${XDG_CONFIG_HOME:-~/.config}/fzf/', but this can be an overkill. I'm a bit torn on this idea.
  3. Need to quote config_prefix in uninstall script
  4. Let's say a user already installed fzf without $XDG_CONFIG_HOME, then decided to use $XDG_CONFIG_HOME. If he reruns the install script, the configuration file will have two lines for loading fzf.
[ -f ~/.fzf.bash ] && source ~/.fzf.bash

[ -f $XDG_CONFIG_HOME/fzf/fzf.bash ] && source $XDG_CONFIG_HOME/fzf/fzf.bash

We can consider avoiding XDG version if ~/.fzf.bash already exists (which means this is not a fresh-install).

@mark64
Copy link
Contributor Author

mark64 commented May 19, 2018

  1. Not forbidden, my bad. I assumed you were referring to the possibility of specifying 2, space-separated directory, not an actual directory with spaces in the name. I now quote the mkdir line properly.

  2. I didn't add it in the latest commit, but I could do config_prefix="\${XDG_CONFIG_HOME:-${XDG_CONFIG_HOME}}/fzf/". It is quite verbose and a little ugly

  3. Done.

  4. Done.

@junegunn
Copy link
Owner

Thanks, but the space issue still lurks in a number of places.

rm -f ~/.fzf.bash
export XDG_CONFIG_HOME="/tmp/foo bar"
mkdir -p "$XDG_CONFIG_HOME"
./install --all
Downloading bin/fzf ...
  - Already exists
  - Checking fzf executable ... 0.17.3

Generate ${XDG_CONFIG_HOME:-/tmp/foo bar}/fzf/fzf.bash ... ./install: line 267: $src: ambiguous redirect
OK
Generate ${XDG_CONFIG_HOME:-/tmp/foo bar}/fzf/fzf.zsh ... ./install: line 267: $src: ambiguous redirect
OK
Update fish_user_paths ... OK
./install: line 296: [: /tmp/foo: binary operator expected
Symlink /tmp/foo bar/fish/functions/fzf_key_bindings.fish ... ln: /tmp/foo bar/fish/functions/fzf_key_bindings.fish: No such file or directory
Failed

Update /Users/jg/.bashrc:
  - [ -f ${XDG_CONFIG_HOME:-/tmp/foo bar}/fzf/fzf.bash ] && source ${XDG_CONFIG_HOME:-/tmp/foo bar}/fzf/fzf.bash
    + Added

Update /Users/jg/.zshrc:
  - [ -f ${XDG_CONFIG_HOME:-/tmp/foo bar}/fzf/fzf.zsh ] && source ${XDG_CONFIG_HOME:-/tmp/foo bar}/fzf/fzf.zsh
    + Added

Create /tmp/foo bar/fish/functions/fish_user_key_bindings.fish:
    function fish_user_key_bindings
./install: line 353: /tmp/foo bar/fish/functions/fish_user_key_bindings.fish: No such file or directory
      fzf_key_bindings
./install: line 353: /tmp/foo bar/fish/functions/fish_user_key_bindings.fish: No such file or directory
    end
./install: line 353: /tmp/foo bar/fish/functions/fish_user_key_bindings.fish: No such file or directory

Finished. Restart your shell or reload config file.
   source ~/.bashrc  # bash
   source ~/.zshrc   # zsh
   fzf_key_bindings  # fish

Use uninstall script to remove fzf.

@junegunn
Copy link
Owner

Also note that [ -f $XDG_CONFIG_HOME/fzf/fzf.bash ] needs quoting if $XDG_CONFIG_HOME contains whitespaces.

export XDG_CONFIG_HOME="/tmp/foo bar"
mkdir -p "$XDG_CONFIG_HOME"
touch $XDG_CONFIG_HOME/baz
[ -f $XDG_CONFIG_HOME/baz ] && echo 1
# bash: [: /tmp/foo: binary operator expected

@junegunn
Copy link
Owner

junegunn commented May 28, 2018

Sorry for the delay, I just pushed commits that should fix the whitespace issue.

Tested with the following script:

rm -f ~/.fzf.bash
export XDG_CONFIG_HOME="/tmp/foo bar"
mkdir -p "$XDG_CONFIG_HOME"
./install --all
./uninstall

unset XDG_CONFIG_HOME
rm -rf ~/.config/fzf
./install --all
./uninstall

mkdir -p ~/.config/fzf
./install --all
./uninstall

Now, bashrc can have three versions of the line depending on the environment variables of the shell that runs the install script.

  1. [ -f ~/.fzf.bash ] && source ~/.fzf.bash
  2. [ -f "$XDG_CONFIG_HOME"/fzf/fzf.bash ] && source "$XDG_CONFIG_HOME"/fzf/fzf.bash
  3. [ -f ~/.config/fzf/fzf.bash ] && source ~/.config/fzf/fzf.bash

And I'm not sure if this is a good decision as it can be confusing. Maybe we should ditch the third case?

@junegunn
Copy link
Owner

junegunn commented May 28, 2018

Another option would be to combine the latter two:

[ -f "${XDG_CONFIG_HOME:-$HOME/.config}"/fzf/fzf.bash ] && source "${XDG_CONFIG_HOME:-$HOME/.config}"/fzf/fzf.bash

Doesn't look good though.

@mark64
Copy link
Contributor Author

mark64 commented May 28, 2018

The third case should be handled, and though its verbose I think your suggestion to do [ -f "${XDG_CONFIG_HOME:-$HOME/.config}"/fzf/fzf.bash ] && source "${XDG_CONFIG_HOME:-$HOME/.config}"/fzf/fzf.bash is the best way to cover 2 and 3 together as this is the exact use case for that feature.

I'm adding a commit using that solution. It makes the xdg-compliant directory the default if ~/.fzf.bash is not found. I'm not sure how you feel about that, so I'd like comments. By the time the use runs this install script the ~/.config directory likely exists, so checking for its presence won't yield useful information. Forcing users to set the XDG_CONFIG_HOME variable, at least during installation, is an option, but wouldn't fully follow the spec.

@junegunn
Copy link
Owner

The test script I posted above doesn't pass with the latest commit.

@junegunn
Copy link
Owner

junegunn commented May 28, 2018

To be precise, I'm seeing the following error messages during the process:

Generate "${XDG_CONFIG_HOME:-~/.config}"/fzf/fzf.bash ... ./install: line 267: bar/fzf/fzf.bash: No such file or directory
OK
Generate "${XDG_CONFIG_HOME:-~/.config}"/fzf/fzf.zsh ... ./install: line 267: bar/fzf/fzf.zsh: No such file or directory
OK
Update fish_user_paths ... Failed

@junegunn
Copy link
Owner

Hmm, I'm thinking again if changing the default directory on fresh installation is a good call, because there are many users who have [ -f ~/.fzf.bash ] && source ~/.fzf.bash committed to their dotfiles repositories. See:

https://github.com/search?q=%5B+-f+~%2F.fzf.bash+%5D+%26%26+source+~%2F.fzf.bash&type=Code

They will expect fzf installer to create the file on that particular path.

How about we add --xdg flag for explicitly selecting $XDG_CONFIG_HOME over the default?

install Outdated
eval 'config_dir="$config_prefix"'
eval "config_dir=$config_dir"
eval "config_dir=$config_dir"
echo $config_dir
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Echo for debugging?

uninstall Outdated
eval 'config_dir="$config_prefix"'
eval "config_dir=$config_dir"
eval "config_dir=$config_dir"
echo $config_dir
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

mark64 and others added 2 commits May 29, 2018 18:31
eval doesn't work well with XDG_CONFIG_HOME with whitespaces and it can
be a security hole as bash will inadvertently execute any command
injected in the variable.

    FOO="b ls"
    eval "a=$FOO"

Also, replace tilde with $HOME to simplify the expansion.
@junegunn
Copy link
Owner

Pushed a fix that removes evals. See the commit message for the details.

Also I had to change ${XDG_CONFIG_HOME:-~/.config} to ${XDG_CONFIG_HOME:-$HOME/.config}, as the tilde doesn't expand inside double quotes.

mkdir -p ~/.config
unset XDG_CONFIG_HOME
[ -d "${XDG_CONFIG_HOME:-~/.config}" ] && echo found || echo not found
  # not found
[ -d ${XDG_CONFIG_HOME:-~/.config} ] && echo found || echo not found
  # found
[ -d "${XDG_CONFIG_HOME:-$HOME/.config}" ] && echo found || echo not found
  # found

Please review the change and let me know if there's anything else you want to address. I'll merge this if everything's okay.

@mark64
Copy link
Contributor Author

mark64 commented May 31, 2018

That looks good. Works for me

@junegunn junegunn merged commit 2ff1908 into junegunn:master Jun 1, 2018
@junegunn
Copy link
Owner

junegunn commented Jun 1, 2018

Merged, thanks!

@ANGkeith
Copy link

ANGkeith commented Dec 28, 2019

Hi, I am still new to this XDG Base Directory thing.

Just curious, why is the fzf installed into the XDG_CONFIG_HOME directory instead of ~/.local/lib directory ?

@dlagg
Copy link

dlagg commented Jul 5, 2020

Here another newbie. I have set XDG_CONFIG_HOME to .config , and created, .config/fzf. Should it store dotfiles there without setting any env var? I have to reinstall fzf?

@Futarimiti
Copy link

Futarimiti commented Jul 12, 2023

Another newbie here with the same wonder. Has someone come up to solve it eventually?

Edit: never mind, just found it here:

The shell init files will be installed to XDG_CONFIG_HOME/fzf if the installation script is called with --xdg for example /usr/local/opt/fzf/install --xdg.

@lucaspar
Copy link

@junegunn any chance we can have the --xdg to be the default behavior when XDG_CONFIG_HOME is set?

I could help with making this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants