How to add entry to fish shell history?

4.4k views Asked by At

In my fish function, I'm evaluating constructed commandline via eval (commandline), specifically - I'm looking for some file name from fzf, and then analyze if commandline was prepended with vim. If it was - instead of returning vim filename to press enter afterwards, I just evaluate it, as I showed before.

The thing is that if I evaluate it, instead of pressing enter manually, it is not going to history - e.g. I can not see it as previous command by pressing up.

I tried set -x history (commandline) $history after eval, but it shows me an error set: Tried to change the read-only variable “history”

Is there a way to manually add custom string (in my case commandline buffer) to history? thx.

2

There are 2 answers

1
Brett Y On

history --merge does not merge history in chronological order #2312. So even if we manually add the command to ~/.config/fish/fish_history we will no be able to simply press up to see it (although you will get to it eventually if you press up enough).

To get around this we can make a copy of fish_history then call builtin history --clear which will clear both fish's internal history and the history file. We then restore the history file from our copy, append our command and call history --merge to merge the history file with the now empty internal history.

function evalh
    eval $argv

    # backup history file
    cp ~/.config/fish/fish_history /tmp/fish_history.tmp

    # clear internal history and history file (using builtin means we don't get a prompt)
    builtin history --clear

    # restore history file
    cp /tmp/fish_history.tmp ~/.config/fish/fish_history

    # append our command 
    echo "- cmd:" $argv >> ~/.config/fish/fish_history
    echo "  when:" (date "+%s") >> ~/.config/fish/fish_history

    # merge history file with (empty) internal history
    history --merge
end
3
rperce On

Instead of using eval, you can use the -f option on commandline to inject readline functions---specifically, execute.

For example, I wrote a function substitute that performs an analogous action to bash's ^foo^bar syntax:

function substitute
    set -l last $history[1]
    switch (count $argv)
        case 3
            if test $argv[3] = 'g'
                commandline (echo $last | sed "s/$argv[1]/$argv[2]/g")
                commandline -f execute
            else
                echo "Syntax: substitute <find> <replace> [g]"
            end
        case 2
            commandline (echo $last | sed "s/$argv[1]/$argv[2]/")
            commandline -f execute
        case '*'
            echo "Syntax: substitute <find> <replace> [g]"
    end
end

Which in my config.fish is aliased to s:

alias s substitute

Usage looks like this (manual presses of the Enter key are replaced with the ⏎ character):

~> echo hi hi hi⏎
hi hi hi
~> s hi bye⏎
~> echo bye hi hi
bye hi hi
~> s hi bye g⏎
~> echo bye bye bye
bye bye bye

Note that, though it can't be displayed here, the commandlines created by substitute in this fashion are not syntax-highlighted.

It is either a bug or a feature that the s foo bar lines appear in your history in this context as well. I consider it a feature, so have not bothered fixing it.