commands: add a Cmd argument type

This represents a command passed as an argument. Also split arguments from
command values themselves, making the command help for meta-commands much
clearer.
This commit is contained in:
Aldo Cortesi 2017-12-14 15:43:15 +13:00 committed by Aldo Cortesi
parent 04e19f9171
commit e64d5c6bb9
8 changed files with 35 additions and 20 deletions

View File

@ -37,7 +37,7 @@ class ClientPlayback:
ctx.master.addons.trigger("update", []) ctx.master.addons.trigger("update", [])
@command.command("replay.client.file") @command.command("replay.client.file")
def load_file(self, path: str) -> None: def load_file(self, path: command.Path) -> None:
try: try:
flows = io.read_flows_from_paths([path]) flows = io.read_flows_from_paths([path])
except exceptions.FlowReadException as e: except exceptions.FlowReadException as e:

View File

@ -31,7 +31,7 @@ class ServerPlayback:
ctx.master.addons.trigger("update", []) ctx.master.addons.trigger("update", [])
@command.command("replay.server.file") @command.command("replay.server.file")
def load_file(self, path: str) -> None: def load_file(self, path: command.Path) -> None:
try: try:
flows = io.read_flows_from_paths([path]) flows = io.read_flows_from_paths([path])
except exceptions.FlowReadException as e: except exceptions.FlowReadException as e:

View File

@ -23,6 +23,10 @@ class Path(str):
pass pass
class Cmd(str):
pass
def typename(t: type, ret: bool) -> str: def typename(t: type, ret: bool) -> str:
""" """
Translates a type to an explanatory string. If ret is True, we're Translates a type to an explanatory string. If ret is True, we're
@ -172,6 +176,8 @@ def parsearg(manager: CommandManager, spec: str, argtype: type) -> typing.Any:
"Invalid choice: see %s for options" % cmd "Invalid choice: see %s for options" % cmd
) )
return spec return spec
if argtype in (Path, Cmd):
return spec
elif issubclass(argtype, str): elif issubclass(argtype, str):
return spec return spec
elif argtype == bool: elif argtype == bool:

View File

@ -3,18 +3,18 @@ from urwid.text_layout import calc_coords
class CommandBuffer(): class CommandBuffer():
def __init__(self, start: str = ""): def __init__(self, start: str = "") -> None:
self.buf = start self.buf = start
# This is the logical cursor position - the display cursor is one # This is the logical cursor position - the display cursor is one
# character further on. Cursor is always within the range [0:len(buffer)]. # character further on. Cursor is always within the range [0:len(buffer)].
self._cursor = len(self.buf) self._cursor = len(self.buf)
@property @property
def cursor(self): def cursor(self) -> int:
return self._cursor return self._cursor
@cursor.setter @cursor.setter
def cursor(self, x): def cursor(self, x) -> None:
if x < 0: if x < 0:
self._cursor = 0 self._cursor = 0
elif x > len(self.buf): elif x > len(self.buf):
@ -25,19 +25,19 @@ class CommandBuffer():
def render(self): def render(self):
return self.buf return self.buf
def left(self): def left(self) -> None:
self.cursor = self.cursor - 1 self.cursor = self.cursor - 1
def right(self): def right(self) -> None:
self.cursor = self.cursor + 1 self.cursor = self.cursor + 1
def backspace(self): def backspace(self) -> None:
if self.cursor == 0: if self.cursor == 0:
return return
self.buf = self.buf[:self.cursor - 1] + self.buf[self.cursor:] self.buf = self.buf[:self.cursor - 1] + self.buf[self.cursor:]
self.cursor = self.cursor - 1 self.cursor = self.cursor - 1
def insert(self, k: str): def insert(self, k: str) -> None:
""" """
Inserts text at the cursor. Inserts text at the cursor.
""" """
@ -48,7 +48,7 @@ class CommandBuffer():
class CommandEdit(urwid.WidgetWrap): class CommandEdit(urwid.WidgetWrap):
leader = ": " leader = ": "
def __init__(self, text): def __init__(self, text) -> None:
self.cbuf = CommandBuffer(text) self.cbuf = CommandBuffer(text)
self._w = urwid.Text(self.leader) self._w = urwid.Text(self.leader)
self.update() self.update()
@ -82,4 +82,3 @@ class CommandEdit(urwid.WidgetWrap):
def get_value(self): def get_value(self):
return self.cbuf.buf return self.cbuf.buf

View File

@ -224,7 +224,11 @@ class ConsoleAddon:
@command.command("console.choose") @command.command("console.choose")
def console_choose( def console_choose(
self, prompt: str, choices: typing.Sequence[str], *cmd: str self,
prompt: str,
choices: typing.Sequence[str],
cmd: command.Cmd,
*args: str,
) -> None: ) -> None:
""" """
Prompt the user to choose from a specified list of strings, then Prompt the user to choose from a specified list of strings, then
@ -233,7 +237,7 @@ class ConsoleAddon:
""" """
def callback(opt): def callback(opt):
# We're now outside of the call context... # We're now outside of the call context...
repl = " ".join(cmd) repl = cmd + " " + " ".join(args)
repl = repl.replace("{choice}", opt) repl = repl.replace("{choice}", opt)
try: try:
self.master.commands.call(repl) self.master.commands.call(repl)
@ -246,7 +250,7 @@ class ConsoleAddon:
@command.command("console.choose.cmd") @command.command("console.choose.cmd")
def console_choose_cmd( def console_choose_cmd(
self, prompt: str, choicecmd: str, *cmd: str self, prompt: str, choicecmd: command.Cmd, *cmd: str
) -> None: ) -> None:
""" """
Prompt the user to choose from a list of strings returned by a Prompt the user to choose from a list of strings returned by a
@ -492,14 +496,20 @@ class ConsoleAddon:
return list(sorted(keymap.Contexts)) return list(sorted(keymap.Contexts))
@command.command("console.key.bind") @command.command("console.key.bind")
def key_bind(self, contexts: typing.Sequence[str], key: str, *command: str) -> None: def key_bind(
self,
contexts: typing.Sequence[str],
key: str,
cmd: command.Cmd,
*args: str,
) -> None:
""" """
Bind a shortcut key. Bind a shortcut key.
""" """
try: try:
self.master.keymap.add( self.master.keymap.add(
key, key,
" ".join(command), cmd + " " + " ".join(args),
contexts, contexts,
"" ""
) )

View File

@ -100,6 +100,7 @@ def test_typename():
assert command.typename(command.Choice("foo"), False) == "choice" assert command.typename(command.Choice("foo"), False) == "choice"
assert command.typename(command.Path, False) == "path" assert command.typename(command.Path, False) == "path"
assert command.typename(command.Cmd, False) == "cmd"
class DummyConsole: class DummyConsole:
@ -162,6 +163,9 @@ def test_parsearg():
assert command.parsearg( assert command.parsearg(
tctx.master.commands, "foo", command.Path tctx.master.commands, "foo", command.Path
) == "foo" ) == "foo"
assert command.parsearg(
tctx.master.commands, "foo", command.Cmd
) == "foo"
class TDec: class TDec:

View File

@ -32,6 +32,3 @@ class TestCommandBuffer:
cb.insert("x") cb.insert("x")
assert cb.buf == output[0] assert cb.buf == output[0]
assert cb.cursor == output[1] assert cb.cursor == output[1]