On Thu, Aug 12, 2010 at 10:51:59PM +0200, LuX wrote:
I would really like to use the 'custom completion' feature documented
in the man page of wimenu-3.9.2, just as in the provided example:

Unfortunately the example script given there goes far beyond my
skills. I have copied and pasted it in a file ~/wim. Then 'sh wim'
does exactly what is expected (fortunately enough I didn't have any
important file called 'fifo' in my home directory ;)) except that once
I have selected something, for example 'foo 1', and pressed Return, I
would like to recover the string 'foo 1' in order to do something with
it. Instead of this wim quits silently and I haven't found a way to
recover the selected string.

Any hint?

Yes... Unfortunately awk dies on SIGPIPE in that example, so it never runs the END block. This slight variation should work:

    wimenu -c <fifo | awk '
        BEGIN {
             # Define the completion results
             cmds = "foo\nbar\nbaz\n"
             cmd["foo"] = "1\n2\n3\n"
             cmd["bar"] = "4\n5\n6\n"
             cmd["baz"] = "7\n8\n9\n"
# Print the first set of completions to wimenu’s fifo
             fifo = "fifo"
             print cmds >fifo; fflush(fifo)
        }
// # Push out a new set of completions
        function update(str, opts) {
             print length(str) >fifo # Print the length of the preceding string
             print opts >fifo        # and the options themself
             fflush(fifo)
        }
# Ensure correct argument count with trailing spaces
        / $/ { $0 = $0 "#"; }
{ # Process the input and provide the completions
             if (NF == 1)
                  update("", cmds)        # The first arg, command choices
             else
                  update($1 " ", cmd[$1]) # The second arg, command arguments
             # Skip the trailing part of the command
             getline rest
        }
    ' | tail -1

As for modifying it, you'd probably be happier with python, perl, or ruby, but I make a point of only using POSIX utilities in examples. But if you're looking for a start, this is the above modified to (crudely) complete a command and then files in the current directory:

    script=$(cat <<'!'
        # Push out a new set of completions
        function update(offset, cmd) {
# Only push out the completion if the offset has # changed. The behavior will be the same regardless,
            # but this is a minor optimization
            if (offset != loffset) {
                loffset = offset

                print offset >fifo
                print read(cmd) >fifo
                fflush(fifo)
            }
        }

        # Quote a string. This requires that your shell supports
        # rc quoting syntax (zsh with 'setopt rcquotes' or rc, namely)
        function quote(str) {
            if (!match(str, /[\[\](){}$'"^#~!&;*?|<>]/))
                return str
            gsub(/'/, "''", str)
            return "'" str "'"
        }

        # Read the output of a command and return it
        function read(cmd) {
            # We need to use a cache since every use of an
            # input command refers to the same process, which
            # means it can only be run once.
            if (cmd in cache)
                return cache[cmd]

            res = ""
            while (cmd | getline)
                res = res quote($0) "\n"
            return cache[cmd] = res
        }

        BEGIN {
            fifo  = "fifo"
            progs = ". wmii.sh; wi_proglist $PATH"

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

        // # Print the line

        # Process the input and provide the completions
        {
            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 # Set by match() above
                str    = substr($0, offset + 1)

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

                cmd = "ls " quote(str)
                if (str && !match(cmd, "/$"))
# Use dirname to chop off the end of the # last directory component
                    cmd = "ls \"$(dirname " quote(str) ")\""

                update(offset, cmd)
            }

            # Skip the trailing part of the command
            getline rest
        }
    !
    )
    wimenu -c <fifo | awk "$script" | tail -1

Although that may confuse you more than help. I've been told that awk is a fairly arcane language.

Ideally, it should be fairly simple to use a simple bash script to provide completions based on its programmable completion system, but I've never gotten far enough into its internals to make it work. If you'd like to try, though, I'm sure that someone in #bash could help.

By the way, is it intended to add to dmenu a similar feature (and also
the -S option of wimenu)? I'm asking this because at my office wmii is
not available (there is no wmii package in fedora) thus I'm using i3
with dmenu instead. On the other hand the vertical mode of dmenu is
much more comfortable than the horizontal mode of wimenu on the small
screen of my laptop…

I can't speak for the dmenu developers, but new features are usually added only after someone's written a patch and it becomes popular. You might be able to find an rpm somewhere. I would provide one, but I hate rpm with a passion. Personally, I just install wmii to my home directory.

    make PREFIX=$HOME/wmiinst install

and adding ~/wmiinst/bin to $PATH does the trick. If you don't have a compiler available, you should be able to copy the tree from any system, if you 'export LDFLAGS=-static' when you build.

I might add a horizontal mode to wimenu. I'm personally not very fond of it, but others seem to find it useful and it's a simple enough change.

--
Kris Maglione

The object-oriented model makes it easy to build up programs by
accretion.  What this often means, in practice, is that it provides a
structured way to write spaghetti code.
        --Paul Graham


Reply via email to