views:

429

answers:

2

Problem

I'm writing a Twitter client for the command line (in C). I'm currently working on doing TAB-completion for Twitter screen names, like so:

tweet "@s<TAB>
@sourcebits @spolsky

However, I can't get it to work mid-string, e.g.:

tweet "Foo bar @s<TAB>

since Bash treats the string as one word. I couldn't find anything in the Bash man page suggesting a simple fix, so I decided to hack around it. Below is my half-done solution to the problem. I simply split the incoming string by spaces, take the last word (simplification, for now) and send it to compgen (the variable $last_word below). However, I need to append $prefix to the beginning of the string that the TAB-completion produces, as it replaces the whole input string (remember: it's treated as one word). That's where I'm stuck.

Question

How can this be done?

Code etc.

__cltwitter_complete () {
  local cache="$HOME/.cltwitter_users.cache"
  local string=${COMP_WORDS[COMP_CWORD]}
  local last_word=${string##* }
  local prefix=${string% *}

  COMPREPLY=()

  #if [ ! -f ${cache} ] || [ "`find ${cache} -mmin +60`" != "" ]; then
  #  cltwitter-update-cache
  #fi

  if [[ "$last_word" == \"@* ]]; then  # if word is beginning of a quotation
    last_word=${last_word:2}
  elif [[ "$last_word" == @* ]]; then  # if user put '@' in front
    last_word=${last_word:1}
  fi

  COMPREPLY=( $( compgen -W "`cat $cache`" -P @ -- $last_word ) )
}

complete -F __cltwitter_complete tweet

Relevant section from the Bash man page:

COMP_WORDS

An array variable (see Arrays below) consisting of the individual words in the current command line. The words are split on shell metacharacters as the shell parser would separate them. This variable is available only in shell functions invoked by the programmable completion facilities.

+1  A: 

Look at programmable completion: http://www.faqs.org/docs/bashman/bashref_103.html

Don Werve
A: 

Do you really need the quotes?

For example, your command could be:

tweet Foo bar @s<TAB>

Then @s would simply be the word you are trying to complete, and you can search for that. Inside the script, you can still get at the entire string with $*.

You'll have to be more careful invoking now, though, as you can't use special characters such as ! and ( ) anymore without having to escape them with a backslash.

rix0rrr
I've thought about this. Basically, it was not the thing I was hoping to do, since it just doesn't feel right (in a UNIX-y sense). Also, since there are optionally more arguments that can be passed to the executable, the message now has to be the last argument since it's of variable length, which becomes awkward since it is the only mandatory argument.
mtah
Typically other command-line utilities get around this by having options prefixed with dashes, then having a delimiter (--) that means "everything after this is an argument, even if it looks like an option" (i.e., starts with a dash). Then you could still do things like:tweet -x My tweet rocks!tweet -x -- -This message starts with a dash!tweet -- -This one too
rix0rrr
Argh, spacing was butchered by the comment system, but you get the idea.
rix0rrr