开发者

Change how Python Cmd Module handles autocompletion

I hav开发者_StackOverflow社区e a Cmd console set up to auto-complete card names for a Magic: the Gathering collection management system.

It uses the text parameter to query the database for cards, and uses the results to auto-complete/suggest cards.

However, these cards names have multiple words, and Cmd runs auto-completion from the last space to the end of the line.

For example:

mtgdb> add Mage<tab><tab>
Mage Slayer (Alara Reborn)     Magefire Wings (Alara Reborn)
mtgdb> add Mage S<tab><tab>
Sages of the Anima (Alara Reborn)
Sanctum Plowbeast (Alara Reborn)
Sangrite Backlash (Alara Reborn)
Sanity Gnawers (Alara Reborn)
Sen Triplets (Alara Reborn)
[...]
mtgdb> add Mage Sl<tab>
mtgdb> add Mage Slave of Bolas (Alara Reborn)

I tried manually grabbing what I wanted from the line parameter, which gets the results I want from the database, but this fails to overwrite the first word:

mtgdb> add Mage Sl<tab>
mtgdb> add Mage Mage Slayer (Alara Reborn)

In the end, I need the auto-completer to work like this:

mtgdb> add Mage Sl<tab>
mtgdb> add Mage Slayer (Alara Reborn)

Aside from the manual parsing attempt above, I also tried replacing spaces with plus signs, and discovered that Cmd is perfectly happy splitting on those as well. Replacing spaces with underscores works, but there is one card in Unhinged which is named _____, so I have to go through acrobatics to demunge the strings since I can't just line.replace("_", " ").

Here's some runnable test code:

import cmd

commands = [
    "foo",
    "foo bar blah",
    "bar",
    "bar baz blah",
    "baz",
    "baz foo blah"]

class Console(cmd.Cmd):
    intro = "Test console for" + \
            "http://stackoverflow.com/questions/4001708/\n" + \
            "Type \"cmd<space><tab><tab>\" to test " + \
            "auto-completion with spaces in commands\nwith " + \
            "similar beginings."

    def do_cmd(self, line):
        print(line)

    def complete_cmd(self, text, line, start_index, end_index):
        if text:
            return [command for command in commands
                    if command.startswith(text)]
        else:
            return commands

if __name__ == "__main__":
    command = Console()
    command.cmdloop()


It shouldn't need to be overly complicated. Something like the following:

import cmd

completions = [
    'Mage Slayer (Alara Reborn)',
    'Magefire Wings (Alara Reborn)',
    'Sages of the Anima (Alara Reborn)',
    'Sanctum Plowbeast (Alara Reborn)',
    'Sangrite Backlash (Alara Reborn)',
    'Sanity Gnawers (Alara Reborn)',
    'Sen Triplets (Alara Reborn)'
]

class mycmd(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)

    def do_quit(self, s):
        return True

    def do_add(self, s):
        pass

    def complete_add(self, text, line, begidx, endidx):
        mline = line.partition(' ')[2]
        offs = len(mline) - len(text)
        return [s[offs:] for s in completions if s.startswith(mline)]

if __name__ == '__main__':
    mycmd().cmdloop()


You could do readline.set_completer_delims('').

However, your complete_* functions won't be called anymore; you will have to override Cmd.complete or Cmd.completenames. Look at the sourcecode of the cmd module for details.


I did override of the cmdloop function, and it was pretty straightforward. I didn't have to change anything else. Just copy the cmdloop function from the module (find code by doing import cmd, cmd.__file__), and add the two lines for changing delimiters:

    try:
       import readline
       self.old_completer = readline.get_completer()
       readline.set_completer(self.complete)
       readline.parse_and_bind(self.completekey+": complete")
       # do not use - as delimiter
       old_delims = readline.get_completer_delims() # <-
       readline.set_completer_delims(old_delims.replace('-', '')) # <-
    except ImportError:
        pass

That did it for me. In your case you may want to remove whichever delimiter is causing the issues.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜