bash completion directory style partial suggestion printing - bash

I am working on a script called to that bookmarks file system locations in bash (with limited support for zsh). You can find the source here: https://github.com/resultsreturned/to
The script supports tab completion for all operations.
Say you have "bookmarked" the location of a folder:
mara#andromeda:~/bin/android-sdk-linux$ to -b android
The script then allows you to access subfolders of the bookmark, like so:
mara#andromeda:~$ to android/docs/
mara#andromeda:~/bin/android-sdk-linux/docs$
Pressing tab twice will give suggestions:
mara#andromeda:~$ to android/s<TAB><TAB>
android/samples/ android/sd-card/ android/sources/ android/system-images/
However, I would rather that the suggestions print only the portion of the path that is not currently input into the buffer. For example, the cd command:
mara#andromeda:~/bin$ cd android-sdk-linux/s<TAB><TAB>
samples/ sd-card/ sources/ system-images/
Note how the suggestions only contain the directory level that is currently being input.
So the question is, how do you control the way bash prints suggestions? Is it possible to do this independently of the completion wordlist (for compgen/complete)? Is there some way to define a regex that would process the completion wordlist for printing as suggestions?

Since feature requests to mark a comment as an answer remain declined, I copy the above solution here.
So the solution turns out to be using -o filenames when invoking complete. Showing only the substrings of COMPREPLY bash completion options to the user – resultsreturned

Related

Detect prompt with xterm.js

TLDR: I want know how to detect from the output of a shell (e.g. zsh, bash) the location of the prompts (e.g. user#machine /etc % ).
Details
I have made a working shell frontend in the browser based on xtermjs. It is now equivalent feature-wise to e.g. the default macOS terminal application with zsh, bash and powershell. In a nutshell, it works by executing a shell process (e.g. zsh) as the child of a parent process that pipes the input/output from/to the browser via web sockets.
I want now to step up and implement a "collapse" functionality that hides the output of the selected commands in the history (like Visual Studio Code does now).
To this end, I need to detect the location of the prompts from the terminal output: the collapse function would then hide the characters between two consecutive prompts.
I know I can use the approaches below:
detect the prompt with a regular expression (I would need to parse the PS1 variable)
inject some special character sequence before and after the prompt (e.g. in variable PS1)
But both do not seem very robust, and may not work with some specific command interpreter. I could not find yet the location where this functionality is implemented in the source code of Visual Studio Code.
My question is: is there a robust way to achieve this functionality for at least zsh, bash and powershell (it is fine if it is specific to xterm.js)
Edit 1
This SO question is related: ANSI escape sequence for collapsing/folding text (maybe hierarchically)
It links to this interesting thread: https://github.com/PerBothner/DomTerm/issues/54
It appears that DomTerm uses escapes sequences at folding points (my solution 2).
Yet I don't see how to inject them into the terminal, besides hacking the PS1 env var.
Edit 2
While parsing iTerm's documentation I found out that it takes advantage of the hooks provided by the shell (e.g. for zsh) in order to print some special escape sequence at various locations, including before showing the prompt.
For example, in zsh, I can print string "🐮" before each prompt be executing precmd() { echo '🐮' }. Then when I execute e.g. ls I get
$> ls
[...]
🐮
$>
There is a more extensive explanation of the various available hooks for various shells here.
It looks like PowerShell uses a very different system though.

Tab completion ignoring some files in Bash/Debian

I am using tab completion in a standard Debian install with Bash and I have some files being ignored. For example, if I have the files:
index.php
index.php.a
If I type vim i then tab it immediately selects "index.php " (see space after file name). Normally, it would just complete up to "index.php" and give me the option to type something else after.
Why is it behaving differently in this situation?
Update
Some commands such as "cp" seem to handle the tab completion just fine, so maybe it is vim looking for specific file extensions?
The bash-completion package uses the function _filedir_xspec to complete vim. That function in general completes filenames, but excludes certain patterns depending on which command it is completing.
For vim, the exclusion pattern starts like this:
_install_xspec '*.#([ao]|so|so.!(conf|*/*) ...
I.e., among other things, files ending in .a should be ignored. The thinking behind that is probably that these are often created as backup copies and you probably don't want to edit them.
If you want to override this behaviour, you can add your own completions into ~/.bash_completion; for example, to get vim to complete on all filenames, use this:
complete -f vim
which will make vim tab completion default to the built-in file completion bevahiour.

Is it possible to have separate bash completion functions for separate commands which happen to share the same name?

I have two separate scripts with the same filename, in different paths, for different projects:
/home/me/projects/alpha/bin/hithere and /home/me/projects/beta/bin/hithere.
Correspondingly, I have two separate bash completion scripts, because the proper completions differ for each of the scripts. In the completion scripts, the "complete" command is run for each completion specifying the full name of the script in question, i.e.
complete -F _alpha_hithere_completion /home/me/projects/alpha/bin/hithere
However, only the most-recently-run script seems to have an effect, regardless of which actual version of hithere is invoked: it seems that bash completion only cares about the filename of the command and disregards path information.
Is there any way to change this behavior so that I can have these two independent scripts with the same name, each with different completion functions?
Please note that I'm not interested in a solution which requires alpha to know about beta, or which would require a third component to know about either of them--that would defeat the purpose in my case.
The Bash manual describes the lookup process for completions:
If the command word is a full pathname, a compspec for the full pathname is searched for first. If no compspec is found for the full pathname, an attempt is made to find a compspec for the portion following the final slash. If those searches do not result in a compspec, any compspec defined with the -D option to complete is used as the default.
So the full path is used by complete, but only if you invoke the command via its full path. As for getting completions to work using just the short name, I think your only option (judging from the spec) is going to be some sort of dynamic hook that determines which completion function to invoke based on the $PWD - I don't see any evidence that Bash supports overloading a completion name like you're envisioning.
Yes, this is possible. But it's a bit tricky. I am using this for a future scripting concept I am developing: All scripts have the same name as they are build scripts, but still bash completion can do its job.
For this I use a two step process: First of all I place a main script in ~/.config/bash_completion.d. This script is designed to cover all scripts of the particular shared script name. I configured ~/.bashrc in order to load that bash completion file for these scripts.
The script will obtain the full file path of the particular script file I want to have bash completion for. From this path I generate an identifier. For this identifier there exists a file that provides actual bash completion data. So if bash completion is performed the bash completion function from the main bash completion script will check for that file and load it's content. Then it will continue with regular bash completion operation.
If you have two scripts with the same name you will have two different identifiers as those scripts share the same name but have different paths. Therefore two different configurations for bash completion can be used.
This concept works like a charm.
NOTE: I will update this answer soon providing some source code.

Bash alias autocompletion that considers only *.foo files

Whenever I want to start editing my LaTeX sources by launching, e.g., the command vim pdeOptAff.tex, I find that bash the (tab)-autocompletion is still tedious because the folder typically contains a bunch of files with the same name but different endings:
$ ls
pdaeOptAff.aux pdaeOptAff.out pdaeOptAff.tex.latexmain
pdaeOptAff.bbl pdaeOptAff.pdf pdaeOptAff.toc
pdaeOptAff.blg pdaeOptAff.synctex.gz
pdaeOptAff.log pdaeOptAff.tex
Since I always want to open the the ~.tex file, I would love to have a filter that makes autocomplete expand only the ~.tex files.
In addition, I have an alias defined: alias g='gvim --remote-silent'. So this filter, in particular, should work for the alias.
Try:
complete -f -X '!*.tex' g
Then, you can type:
g <TAB>
And get the completions. Note: this doesn't complete directories. Someone else may have a better solution using compgen.
There's a helpful autocomplete guide on The Linux Documentation Project too.

bash tab completion filter out options

I would like to make tab completion in bash a bit more intelligent.
Let's say I have a folder with a src file .lisp, and a compiled version of that file .fasl. I would like to type vi filename [tab tab], and the .lisp autocompletes as the only option. That is, it's not likely that I want vim to open a compiled binary, so don't have it in the list of autocomplete options to cycle through.
Is there a way that I can keep a flat list of extensions that autocomplete ignores, or somehow customize it for vim, so that autocomplete ignores only particular file extensions when a bash command starts with vi ...
Any ideas are appreciated.
Thanks!
From man bash:
FIGNORE
A colon-separated list of suffixes to ignore when performing
filename completion. A filename whose suffix matches one of the entries in FIGNORE is excluded from the
list of matched filenames. A sample value is ".o:~" (Quoting is
needed when assigning a value to this variable, which contains
tildes).
So, for your example this can be set in your .bashrc file with
FIGNORE=".o:~:.fasl"
or, if you want to keep any other site-wide settings:
FIGNORE=".o:~:.fasl:$FIGNORE"
The bash complete command seems to be what you want.
Here is a Linux Journal link to 'complete' command video. and here is the follow-up page More on Using the Bash Complete Command
The links explain it quite well, and here is a related SO Question/Answer: Bash autocompletion across a symbolic link

Resources