(15-08-2020, 04:38 PM)phillbush Wrote: I'm working on xprompt
That's pretty cool!
I was intrigued about how to generate a list that would be compatible with your program based on some shell completion. I wasn't able to extract it out of zsh so I went with bash completion, and here's the result:
I started out with a list of all the programs in the
$PATH.
Then I've used the following tool called
complete from bash completion system to extract additional programs that have completion:
Code:
# complete | grep -o -P '(\-o (\w+))|(-F _?\w+ \w+)' | sed 's/-F _\?_\?\w\+ //g;s/-o //' | sort | uniq > all_available_completion
I then searched for quite a while to find a way to pass a certain string to a completion system to let it spit back the possible next word. I've found my solution in
this blog post. I'll copy the script here:
Code:
#
# Author: Brian Beffa <brbsix@gmail.com>
# Original source: https://brbsix.github.io/2015/11/29/accessing-tab-completion-programmatically-in-bash/
# License: LGPLv3 (http://www.gnu.org/licenses/lgpl-3.0.txt)
#
#https://brbsix.github.io/2015/11/29/accessing-tab-completion-programmatically-in-bash/
get_completions(){
local completion COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS COMPREPLY=()
# load bash-completion if necessary
declare -F _completion_loader &>/dev/null || {
for f in /usr/share/bash-completion/completions/*; do source $f; done
source /usr/share/bash-completion/helpers/gst
source /usr/share/bash-completion/bash_completion
}
COMP_LINE=$*
COMP_POINT=${#COMP_LINE}
eval set -- "$@"
COMP_WORDS=("$@")
# add '' to COMP_WORDS if the last character of the command line is a space
[[ ${COMP_LINE[@]: -1} = ' ' ]] && COMP_WORDS+=('')
# index of the last word
COMP_CWORD=$(( ${#COMP_WORDS[@]} - 1 ))
# determine completion function
completion=$(complete -p "$1" 2>/dev/null | awk '{print $(NF-1)}')
# run _completion_loader only if necessary
[[ -n $completion ]] || {
# load completion
_completion_loader "$1"
# detect completion
completion=$(complete -p "$1" 2>/dev/null | awk '{print $(NF-1)}')
}
# ensure completion was detected
[[ -n $completion ]] || return 1
# execute completion function
"$completion"
# print completions to stdout
printf '%s\n' "${COMPREPLY[@]}" | LC_ALL=C sort
}
The last piece is a script that would loop through all the available programs and generate the input file for xprompt. I wrote it in Perl:
Code:
use strict;
use warnings;
my $filename = $ARGV[0];
open (my $fh, '<', $filename);
while (my $row = <$fh>) {
chomp $row;
my $args = `bash -c "source $ARGV[1] && get_completions '$row ' 2> /dev/null"`;
my @split_args = split /\n/, $args;
print "$row\n";
for my $arg (@split_args) {
print "\t$arg\n";
}
}
The first argument being the location of the file with all the programs, and the second argument the location of the get_completions function script to get sourced.
Be sure to run this in a directory that will stay empty while the script runs, otherwise you may get a completion that contains the current files in the directory. So output to another directory that isn't the current one.
The result is a file that's quite big, with 540K lines. xprompt still works perfectly though!
EDIT:
And here's a script to add man page information:
Code:
use strict;
use warnings;
use Data::Dumper;
my $filename = 'all_programs_with_args';
open (my $fh, '<', $filename);
sub begins_with
{
return substr($_[0], 0, length($_[1])) eq $_[1];
}
while (my $row = <$fh>) {
chomp $row;
if (begins_with($row, "\t")) {
print "$row\n";
} else {
my $description = `man -f $row | grep -P '(1)|(6)|(7)|(8)' | head -n 1 | sed 's/.*- //' 2> /dev/null`;
print("$row\t$description\n");
}
}
You can also remove empty lines with the following, in case the file gets polluted: