xprompt: a dmenu rip-off with contextual completion - Programming On Unix

Users browsing this thread: 2 Guest(s)
venam
Administrators
(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:
Code:
sed '/^\s*$/d' "$@"


Messages In This Thread
RE: What are you working on? - by movq - 16-08-2020, 03:54 AM
RE: What are you working on? - by venam - 16-08-2020, 07:34 AM
RE: What are you working on? - by seninha - 16-08-2020, 09:48 AM
RE: What are you working on? - by venam - 16-08-2020, 10:03 AM