zsh-users / zsh-autosuggestions

Fish-like autosuggestions for zsh
MIT License
31.33k stars 1.87k forks source link

An idea for a new feature – querying via tags #373

Open psprint opened 6 years ago

psprint commented 6 years ago

Suppose you enter following example commands:

file=state; cp "answer/plugins/$file" model/plugins ## test:cplg
file=sice; cp "answer/$file" "model/" ## test:cmain

Then, on command line, you could enter:

% test:cplg  # would propose the first command
% :cplg # as above
% cmain:test # would propose the second command
% :test # would propose both commands, somehow

The background is: I'm updating my tests for zplugin and have to copy files from the-answer (test result) paths to the-model (model of correct test result) paths, for 50 tests, multiple times per test. And this way I thought that summoning commands via tags is a neat solution. I can imagine this being handy not only in such specific situations. E.g., I have a "make / configure / make install" command that I moderate-often summon in Zshell source tree. It now has little discomfort, summoning via entering just "make " is not enough, I have always to verify that it's the exact command I want, not some variation of it. With the tags, I would do:

% make clean; CPPFLAGS=-I/usr/local/include CFLAGS="-g -Wall -O0" LDFLAGS=-L/usr/local/lib ./configure --disable-gdbm && make && make install ## zsh:O0
% make clean; CPPFLAGS=-I/usr/local/include CFLAGS="-g -Wall -O2" LDFLAGS=-L/usr/local/lib ./configure && make && make install ## zsh:O2:gdbm

To then summon via "O2:zsh", "O2:gdbm". Handy?

Here is a pattern that for query "zp*" searches for such tag (e.g. :zplg:, for some zplugin-related commands)

print -rl -- ${(M)history[@]:#*\#\# (zp[^:]#:(#c0,1)*|*[^:]#:zp[^:]#:(#c0,1)*)}

Try entering following commands and invoking the above print -rl ...:

ls ## zplg
ls ## yeszp:yes2:zplg
ls ## zplg:yes
ls ## test:zplg:yes

The search seems to be fast (60k entries in $history).

One more thing: searching via substrings in path-like arguments to commands. You query ":#tes" and obtain all commands like "cd ./test && make", "rm -f ~/github/project.git/test/.gitignore", etc.

The pattern – first go two that are a presentation version, not actually working, but easy to parse:

results=( ${(M)history[@]:#*(*/*tes|tes*/*|*/*tes*/*)*} ) # / on the left OR / on the right OR on both sides
results=( ${(M)history[@]:#*(*/*tes|*/*file|tes*/*|file*/*|*/*tes*/*|*/*file*/*)*} ) # the same but for 2 keywords `test' and `file'

I spare my time and will get back to this, so some things might be redundant. Now the actual pattern, which uses [^:]# in place of *, which you can run to search your history for references to paths with "tes" in them:

print -rl -- ${(M)history[@]:#*([^[:space:]]#/[^[:space:]]#tes|tes[^[:space:]]#/[^[:space:]]#|[^[:space:]]#/[^[:space:]]#tes[^[:space:]]#/[^[:space:]]#)*}

The problem: it takes 3.4 sec to run. But with the async architecture of autosuggestions, it might be possible? To query via tag and path-substring user would enter: zplg:#tes, i.e. #-character would enable path-substring search.

ericfreese commented 6 years ago

For your first example (test:cplg), do you have an idea for how the proposed suggestion would be shown? Currently, the plugin only supports suggestions that begin with the content in the $BUFFER and renders the remainder of the suggestion using $POSTDISPLAY. Changing this would probably be a lot of work.

ericfreese commented 6 years ago

I wonder if incremental search using ^R would work for you? You may be able to type the content of the tag and have it select the right command

psprint commented 6 years ago

I was thinking about suggesting like it's currently done but appending whole matching command. But this suggests a solution: why not show suggestions under the prompt, in $POSTDISPLAY, when detected tag-search? To show is simple, but then some navigation is also needed, hmm.

Not sure about what exactly ^R you were thinking, but I like autosuggestions far more than my ^R plugin history-search-multi-word, even though it allows to query with multiple keywords and has syntax highlighting of matched commands. That said, I have to revert to ^R on regular basis, when I cannot hit the right beginning of command for zsh-autosuggestions summoning. Tag-search would limit the number of times I have to use the ^R.

ericfreese commented 6 years ago

why not show suggestions under the prompt, in $POSTDISPLAY, when detected tag-search? To show is simple, but then some navigation is also needed, hmm.

Yes, as a prerequisite for this (and #62) we would need a way to gather (via suggestion strategies) and display multiple suggestions and allow the user to navigate between them.

If we were to implement multiple suggestions, I would want it to be disabled by default so that the default behavior is as close as possible to what you get with Fish shell.

Maybe someone can work up a proof-of-concept implementation that looks something like what you get from a browser omnibar?

screenshot-2018-10-11t16 34 10-06 00

psprint commented 6 years ago

Showing multiple suggestions is essentially what a friend asks me for some 1.5 year – to make history-search-mutli-word active all the time. All this is probably very close to the problematic-to-setup auto-fu. So three projects would somewhat meet in the same point. I can just tell that multiple suggestions under the prompt are very risky, I know this from using HSMW, it's a level(s?)-below the comfort of seeing a command summoned and concatenated after cursor (current zsh-autosuggestions behavior), it probably requires to interrupt current focus and to enter browsing multiple lines text, what requires different strong focus.

I once almost started a new project aiming at the permanent under-prompt suggestions, to make a fresh auto-fu alternative, and I was planning to continuously keep session inside .recursive-edit sub-session, where one can act on POSTDISPLAY (I think that's not fully possible in normal top-level session, but on other hand if the contents for POSTDISPLAY is set from a Zle widget, then why wouldn't this work, hmm; I just don't remember details of why this method worked so well with HSMW, but overall .recursive-edit is like running an application on top of Zsh), use custom keymap like HSMW does by zle .recursive-edit -K hsmw_kmap, and somehow handle pressing enter and normally run commands. I just did a proof of concept code to always enter .recursive-edit: https://github.com/zdharma/prompt-box/blob/master/prompt-box.plugin.zsh, i.e. I've checked that zle-line-init can immediately enter .recursive-edit keeping user separated from top-level "session".

psprint commented 6 years ago

I've started a non-public plugin that implements the idea. It's better to just see it, so here's asciinema link: https://asciinema.org/a/207856

It works the same for searching tags. The video shows well working asynchronicity based on <() subst (with the command true workaround). There's searching for filepath-nodes, but tags work the same. So I've got this quite implemented already, but it will be a Patreon-only plugin. I can share the regular expressions used:

For in-directory nodes and then for tags:

            for tag in $tags; do
                if [[ "$tag" = @* ]]; then
                    tag=${${tag#@}//\*/[^/$' \t\n']#}
                    search_pattern="(*/${tag}/*|*/${tag}([[:blank:]]|(#e))*|*([[:blank:]]|(#s))${tag}/*)"
                    ...
                else
                    search_pattern="\#\#(*:|[[:blank:]]#)${tag//\*/[^:]#}(:*|[[:blank:]]#(#e))"