Re: [dev] [wmii] wimenu custom completion

From: LuX <>
Date: Tue, 17 Aug 2010 02:15:13 +0200


@Arian Kuschki: Thank you too, for the .inputrc trick.

On Sun, 15 Aug 2010 18:51:55 -0400, Kris Maglione wrote:
> Yes, I already fixed that problem when I made it into an example
> file for distribution. Attached.

I have a few unessential remarks, if you allow me:

- Although I don't understand this, it seems that the last 'tail -1' is
  no longer needed.

- Since you replaced the call to dirname by a 'sub' command just after
  the comment '# Strip the trailing filename', it happens that when I
  type 'ls /' in the input area, it is the content of the $HOME
  directory instead of / which is displayed by wimenu. Here is a fix:

              # Strip the trailing filename
              if(match(str, "^/[^/]*$"))
                  str = "/" str
              sub("(/|^)[^/]*$", "", str)

- I have read somewhere that /dev/shm was a good place to put files
  used by programs to discuss one with each others. This would apply
  to "fifo" in this case. I don't know if you have an opinion on this.

- I liked more the previous behaviour, when the script ends by sending
  the input string to stdout (like wimenu does) so that it can can be
  processed by some independent script like for example this one:


    noterm=$(grep Exec= /usr/share/applications/*.desktop /usr/local/share/applications/*.desktop | sed -e 's:.*Exec=\s*::' -e 's:^[^\ ]*/::' | grep -E "^$cmd(\ |$)")

    if [ "$args" != "${args% &}" -o "$noterm" != "" ]; then
        exec $cmd $args &
    elif [ "$cmd" != "" ]; then
        Terminal -e "${SHELL:-sh} -c \"$cmd $args ; ${SHELL:-sh}\"" &

In addition I enjoyed adding another minor feature: when a list of
options has been declared in the script for the initial command of the
input line, every time a '-' is typed at the beginning of a new
argument in a line starting with that command, this list of options is
displayed instead of a list of files. I find it convenient for some
commands, like 'lp' or 'pdfnup' which accept many options, useful to
me but that I do not use to remember.

In case other people would be interested I attach the code below with
my favourite options for lp, as an example.

Note: It would be better to declare the 'opt' array outside this
script, in a sort of configuration file, but I don't know how to do


# This script will launch wimenu and provide command
# completion for the first argument and filename completion
# for each following argument, and pass the result to stdout.
# Program name completion requires that a program list already
# exist in $(wmiir namespace)/.proglist

mkfifo $fifo 2>/dev/null

script=$(cat <<'!'
    BEGIN {
        progs = "cat $(wmiir namespace)/.proglist"
        # Favorite options for some programs
        opt["lp"] = "-o media=a4\n-o landscape\n-o sides=two-sided-long-edge\nsides=two-sided-short-edge\n-o number-up=N\n"

        # Print the first set of completions to wimenu’s fifo
        print read(progs) >fifo

    # Process the input and provide the completions
        # Skip the trailing part of the command.
        # If there is none, this is the result.
        if (!getline rest) {

        if (!match($0, /.*[ \t]/))
            # First argument, provide the program list
            update(0, progs)
        else {
            # Set the offset to the location of the last
            # space, and save that part of the completion
            offset = RLENGTH
            str = substr($0, offset + 1)

            # If we are completing a sub-directory, adjust
            # the offset to the position of the last /
            if (match(str, ".*/"))
                offset += RLENGTH

            # If the last component of the path begins with
            # a ., include hidden files
            arg = ""
            if(match(str, "(^|/)\\.[^/]*$"))
                arg = "-A"

            # Substitute ~/ for $HOME/
            sub("^~/", ENVIRON["HOME"] "/", str)

            # Strip the trailing filename
            if(match(str, "^/[^/]*$"))
                str = "/" str
            sub("(/|^)[^/]*$", "", str)

            # If the last argument starts with a -, list
            # options declared in opt instead of files
            lscmd = "ls " arg quote(str)
            if(match($0, "\\ -[^\\ ]*$")) {
                lsopt = opt[gensub(/\ .*/, "", 1)]
                lscmd = "echo -n \"" lsopt "\""

            update(offset, lscmd)

    # Push out a new set of completions
    function update(offset, cmd) {
        # Only push out the completion if the offset or the
        # ls command has changed. The behavior will be the
        # same regardless, but this is a minor optimization
        if (offset != loffset || cmd != lcmd) {
            loffset = offset
            lcmd = cmd

            cmpl = read(cmd)
            print offset >fifo
            print cmpl >fifo

    # Quote a string. This should work in any Bourne
    # or POSIX compatible shell.
    function quote(str) {
        if (!match(str, /[\[\](){}$'"^#~!&;*?|<>]/))
            return str
        gsub(/\\/, "'\\\\'", str)
        gsub(/'/, "'\\''", str)
        return "'" str "'"

    # Read the output of a command and return it
    function read(cmd) {
        if (cmd in cache)
            return cache[cmd]
        res = ""
        while (cmd | getline)
            res = res quote($0) "\n"
        return cache[cmd] = res
wimenu -h "$HOME/.bash_history" -n 5000 -c "$@" <$fifo | awk -v "fifo=$fifo" "$script"
Received on Tue Aug 17 2010 - 02:15:13 CEST

This archive was generated by hypermail 2.2.0 : Tue Aug 17 2010 - 02:24:02 CEST