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

add a "mcd" (menu change directory) command #2847

Closed
krader1961 opened this issue Mar 23, 2016 · 14 comments
Closed

add a "mcd" (menu change directory) command #2847

krader1961 opened this issue Mar 23, 2016 · 14 comments
Assignees
Milestone

Comments

@krader1961
Copy link
Contributor

@krader1961 krader1961 commented Mar 23, 2016

I've never liked the prevd/nextd model for navigating my cd history. And the pushd/popd model, while easier to reason about, is quite limiting. So long ago I wrote a mcd (menu change directory) function for ksh (Korn shell). When I switched to zsh a few years ago I ported that function. Now that I'm using fish full-time I decided it was time to write a fish version of mcd. I'm opening this issue to see if enough people think it is useful enough to be included in the standard fish package.

Below is the function. You can put this in ~/.config/fish/functions/mcd.fish to autoload it or put it in another file and explicitly source it. Here is an example of what it looks like:

screenshot 2016-03-22 18 47 19

Notice that this can also effectively replace the dirh command since, if you just press [enter] or [ctrl-D] at the prompt, it does nothing other than show you your recent cd history.

I've removed the first version of the function that was in my original post. It's been replaced by the updated version in the attachment below.

@krader1961 krader1961 added this to the fish-tank milestone Mar 23, 2016
@anordal
Copy link
Contributor

@anordal anordal commented Mar 23, 2016

Looks more fishy than prevd/nextd/histd — all in one command. 👍
This is actually my main problem with prevd/nextd/histd (at least was, in the beginning).

I tried it out — won't go back to prevd/nextd/histd I think. But some comments:

  • A separate issue, but set_color -b brcyan silently failed in Konsole (tried both TERM=xterm and TERM=konsole). Surprising, since Konsole supports 24bit color codes, e.g. printf '%s%s%s\n' \e'[48;2;250;80;0;30m' habanero (set_color normal). In the Linux console (TERM=linux), it failed with "Color not valid in TERM = linux: brcyan". Replacing brcyan with cyan worked in both terminals.
  • With a bright background color, consider setting the foreground color to black (in case it is white, as on a black terminal).
  • There can be duplicates, which are not so useful in a menu. Maybe deduplication makes sense to take care of at a lower level (#2842)?
  • I'm a bit clumsy entering numbers. How about alphabetical numbering? But I would love to navigate such a menu with arrow keys. Alternatively, the Alt+left/right trick should have shown a menu.

I also use the similar "jump" function I mentioned in #1969. They complement each other, as it uses bookmarks instead of history — sometimes, the directory you want is not in your bookmarks, and sometimes, it's not in your history. It differs a bit in user interface too by taking arguments instead of being menu driven (I find that suitable for bookmarks because they are static).

One feature you might consider stealing from "jump" is the ability to edit (at the prompt) the selected item. I think it would make more sense in a menu driven interface.

Loading

@krader1961
Copy link
Contributor Author

@krader1961 krader1961 commented Mar 23, 2016

The set_color failure is probably because you're using fish 2.2.0. The "bright" variants were introduced by me last November in commit 0a0acc8. I've changed the code to use existing fish_color_* vars. Also, if $PWD appears in the history it is now highlighted the same way the dirh command does it.

In the new version below I've de-duped the history and added the ability to select by letter or number. I've also changed the logic so the most recently visited directory is always selected by entering "a" or "1" rather than it being an essentially random number that depends on the state of the cd history. This also means you can get the effect of prevd 2 by running mcd and always pressing "b" or "2" (if prevd did duplicate elimination that is).

Being able to pick an entry by tabbing through the list or using arrow keys is something we can add when issue #2805 to expose the fish pager as a callable widget is implemented.

mcd.fish.txt

Loading

@krader1961 krader1961 self-assigned this Mar 23, 2016
@krader1961
Copy link
Contributor Author

@krader1961 krader1961 commented Mar 24, 2016

Another tweak: Don't assume a given fish_color_* var exists.

mcd.fish.txt

Loading

@anordal
Copy link
Contributor

@anordal anordal commented Mar 24, 2016

You're a mind reader, that's more than I asked for. Perfect!

Regarding brcyan, that color doesn't work even as of your "limit size of cd history to 25 directories" — a commit that evidently works.

Loading

@krader1961
Copy link
Contributor Author

@krader1961 krader1961 commented Mar 24, 2016

I can reproduce the set_color issue if I set $TERM to linux or xterm. It works fine if $TERM is xterm-256color. The problem is that those two term types and konsole define the number of supported colors as eight (e.g., infocmp -I linux | grep colors#). Try linux-16color and konsole-256color.

Loading

@floam
Copy link
Member

@floam floam commented Apr 2, 2016

It's too bad this won't do anything on a new shell session, especially since iTerm and Terminal.app give me the impression of continuing a sesison with my previous directory restored and there being some scrollback. This will be nice once you can use the pager.

Perhaps you should enhance fish's history builtin to accommodate use cases such as this - I think that's why it is (intended to be) YAML and makes an effort to record some extra data already. I rarely used the directory history built in to fish but see myself using this more. Obviously both could benefit there.

Loading

@anordal
Copy link
Contributor

@anordal anordal commented Apr 2, 2016

too bad this won't do anything on a new shell session

Have you considered bookmarks?

Loading

@jrobeson
Copy link

@jrobeson jrobeson commented Apr 25, 2016

@krader1961 : can you put this mcd command in a gist, that way it's easier to stay up to date with it.

Loading

@s4code
Copy link

@s4code s4code commented Apr 29, 2016

I’ve implemented an alternative solution to that problem. I called that command "go-back”. It uses complete to generate menu. go-back is not intended to be used directly, I mean that you should bind it to a key. go-back uses leading numbers to sort completions in the right order.

fish-go-back

When it's called with an argument, it strips the leading number from this string and passes the result to cd command. Otherwise it prints the list of visited directories.

function go-back --description "Prints the visited directories"
    if count $argv > /dev/null
        set -l string (type -t string ^ /dev/null)
        if test "$string" = builtin
            cd (string replace -r '^\d+:' '' -- $argv[1])
        else
            cd (printf "%s\n" $argv[1] | sed -r 's/^[0-9]+://')
        end
        return
    end

    set -l alldirs $dirprev $dirnext
    set -l dirhist
    for dir in $alldirs[-1..1]
        if test -d "$dir" -a ! \( $dir = $PWD \)
            if not contains -- $dir $dirhist
                set dirhist $dirhist $dir
                echo (count $dirhist):$dir
            end
        end
    end
end

complete -c go-back -x -a "(go-back)"

And finally the function that should be bound to a key. When that function is called first time it replaces the command line with the string “ go-back “ and calls complete, but only when the command line is empty. Note the leading space, go-back doesn’t pollute your history. When it’s called second time it acts like an “Enter” key.

function __fish_go-back
    if commandline --search-mode
        return
    end

    set -l cmd (commandline -po)
    if count $cmd > /dev/null
        if test "$cmd[1]" = "go-back"
            commandline -f execute
            if commandline --paging-mode
                commandline -f execute
            end
        end
        return
    end

    set -l dirhist (go-back)
    if test -n "$dirhist"
        commandline -r " go-back "
        commandline -f complete down-line
        return
    end

    printf "<directory history is empty>"
    printf "\n%.0s" (fish_prompt)
    commandline -f repaint
end

bind \eh '__fish_go-back'

I bound this function to "Alt+h" (my keybindings are very different from defaults). So when I need to go back I just hold down the “Alt” key and press “h” twice.

What do you guys think about this?

P.S.
I can’t find a better place for this code than my fish_user_key_bindings.fish :)
Here is a gist if someone needs it.

Loading

@floam
Copy link
Member

@floam floam commented Sep 7, 2016

Very cool @s4code - just happened upon this.

Loading

@krader1961 krader1961 removed this from the fish-tank milestone Nov 17, 2016
@krader1961 krader1961 removed this from the fish-tank milestone Nov 17, 2016
@krader1961 krader1961 added the RFC label Nov 17, 2016
@nhooyr nhooyr mentioned this issue Jan 5, 2017
2 tasks
@mouchtaris
Copy link

@mouchtaris mouchtaris commented Jan 16, 2017

@krader1961 could you put this script in a gist, so contributions and updates are easier to track?

Loading

@krader1961
Copy link
Contributor Author

@krader1961 krader1961 commented Jan 17, 2017

could you put this script in a gist, so contributions and updates are easier to track?

I could but I would never update it so doing that seems rather pointless. On the other hand my current version is slightly better than what I originally posted. For example, you can now select via letter (with a always taking you to the previous directory):

$ mcd
 c  3)  ~/bittorrent
 b  2)  ~/VMware Machines
 a  1)  ~
Select directory by letter or number:

I think I should just merge my script. It would then be something people automatically get when installing fish and they can open issues to request changes just like they would do for any other aspect of fish. Also, while @s4code's solution is clever I am not a fan of binding this to a key as opposed to making it an explicit command to mimic cd.

Loading

@pgan002
Copy link

@pgan002 pgan002 commented May 27, 2017

Modality in interfaces is problematic in general. One way to avoid modality is to select the index using a modifier key. Unfortunately, pressing ^1 generates the character as 1.

Another way is substring filtering: the user types a string and Fish shows only path names having that string as a substring or the beginning of a sub-path. The user would not have to read or think about the index number of the desired path name, only the name of the directory. This is not currently supported by Fish's menu completion, which is modal, but I think it would be a good idea.

Ideally, we would integrate this previous paths interaction together with selecting a current subdirectory, as an extension of the menu for the cd function. That would make the interaction more general and more discoverable. Example:

> cd ~/some/long/directory
> cd ~/another/long/directory
> cd ~/some/other/directory
> cd ~
> cd <Tab><Tab>
another    bar    foo    mydir
another/long/directory
some/long/directory
some/other/directory
> cd <Down><Down><Down><Down>
another    bar    foo    mydir
another/long/directory
some/long/directory
[some/other/directory]

<Enter>

> cd some/other/directory

This requires more strokes than index selection on average, but probably less cognitive overhead, because of (1) no index numbers and (2) always using the same command for changing directory. If Fish supported menu filtering, the user could do:

> cd <Tab>
another    bar    foo    mydir
another/long/directory
some/long/directory
some/other/directory
> cd <Tab>o
some/[o]ther/directory

<Right>

> cd some/other/directory

Here, Fish filtered matching subpaths, not substrings.

Loading

@krader1961 krader1961 added this to the fish-future milestone Jun 21, 2017
@krader1961
Copy link
Contributor Author

@krader1961 krader1961 commented Jul 5, 2017

@pgan002 makes a good point. We can do that by adding a completion script for the mcd command. We can't modify the completion behavior for cd because doing so isn't backward compatible and too likely to annoy existing fish users. But we can implement both modal and modeless behavior for the mcd command. With modeless behavior achieved via mcd completions that leverage the fish pager. And modal behavior achieved by simply typing mcd and pressing [Enter] with no args. That should have been obvious to me so consider this a face palm. 😄 Thanks for the idea, @pgan002.

Note that I ended up naming the command cdh to avoid conflicts with the mcd command that might be installed on the user's system.

Loading

@krader1961 krader1961 removed the RFC label Jul 5, 2017
@krader1961 krader1961 added this to the fish 2.7.0 milestone Jul 5, 2017
@krader1961 krader1961 removed this from the fish-future milestone Jul 5, 2017
@krader1961 krader1961 closed this in 8cc4639 Jul 5, 2017
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 17, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

7 participants