How thefuck alias handle command line arguments


TLDR it’s not, thefuck uses functions instead aliases starting from 3.16.

Initially, thefuck alias wasn’t working with command line arguments, if history changing was disabled they were just passed to corrected command. So it was just like:

➜ git br
git: 'br' is not a git command. See 'git --help'.

Did you mean one of these?
	branch
	var
➜ fuck --help
git branch [enter/↑/↓/ctrl+c]
GIT-BRANCH(1)                                          Git Manual                                          GIT-BRANCH(1)

NAME
       git-branch - List, create, or delete branches

SYNOPSIS
       git branch [--color[=<when>] | --no-color] [-r | -a]
               [--list] [-v [--abbrev=<length> | --no-abbrev]]
               [--column[=<options>] | --no-column]
               [(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>]
               [--points-at <object>] [<pattern>...]
...

If history changing was enabled it was less dramatic, it was executing git branch, but it was putting git branch --help in history.

Also there was a lot of feature requests, that can’t be implemented without proper command line arguments supports, like #614 and #531.

So, why there was a problem? Let’s examine old zsh alias, for simplicity with disabled history changing:

alias fuck='TF_CMD=$(
    TF_ALIAS=fuck
    PYTHONIOENCODING=utf-8
    TF_SHELL_ALIASES=$(alias)
    thefuck $(fc -ln -1 | tail -n 1)
) && eval $TF_CMD'

In a few words it was working like:

  • get previous command with fc -ln -1 | tail -n 1 (git br);
  • run thefuck with it (thefuck git br);
  • put result in TF_CMD (TF_CMD=git branch);
  • run the command with eval (eval git branch).

So when fuck was called with arguments like fuck --help it was working like:

TF_CMD=$(
    TF_ALIAS=fuck
    PYTHONIOENCODING=utf-8
    TF_SHELL_ALIASES=$(alias)
    thefuck $(fc -ln -1 | tail -n 1)
) && eval $TF_CMD --help

So in the last step eval git branch --help was called.

As everyone know, bash and zsh don’t allow command line arguments in the middle of alias. So I just replaced aliases with functions.

And there’s another problem – how to distinguish previous command arguments with fuck arguments, and the solution is a dead simple – just use some placeholder, like THEFUCK_ARGUMENT_PLACEHOLDER, between the previous command and fuck arguments.

So now zsh “alias” (with history changing) looks like:

fuck () {
    TF_PREVIOUS=$(fc -ln -1 | tail -n 1);
    TF_CMD=$(
        TF_ALIAS=fuck
        TF_SHELL_ALIASES=$(alias)
        PYTHONIOENCODING=utf-8
        thefuck $TF_PREVIOUS THEFUCK_ARGUMENT_PLACEHOLDER $*
    ) && eval $TF_CMD;
    test -n "$TF_CMD" && print -s $TF_CMD
}

It looks almost the same, but more readable and support nice stuff like:

➜ git br
git: 'br' is not a git command. See 'git --help'.

Did you mean one of these?
	branch
	var
➜ fuck -y
git branch
  614-repeate-option
  620-command-line-arguments
...

➜ hit brunch
➜ fuck -r
git brunch [enter/↑/↓/ctrl+c]
git: 'brunch' is not a git command. See 'git --help'.

Did you mean this?
	branch
git branch [enter/↑/↓/ctrl+c]
  614-repeate-option
  620-command-line-arguments
...

As a drawback, this feature is shell-specific and currently implemented only for bash and zsh. So users of fish, PowerShell and tcsh at this moment can’t use command line arguments with fuck.



comments powered by Disqus