X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=hooke%2Fui%2Fcommandline.py;h=005e039724eac468ba2b4f3c019df8fd29913f70;hb=86f7d4277dbae62efc73b59576fa10ad00bc2987;hp=3ab755d61b6180c5175acebdf71b26ab7448491a;hpb=14dc5541bca5edec1127ada8cf7a10ab110f2e82;p=hooke.git diff --git a/hooke/ui/commandline.py b/hooke/ui/commandline.py index 3ab755d..005e039 100644 --- a/hooke/ui/commandline.py +++ b/hooke/ui/commandline.py @@ -1,15 +1,35 @@ +# Copyright (C) 2010 W. Trevor King +# +# This file is part of Hooke. +# +# Hooke is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation, either +# version 3 of the License, or (at your option) any later version. +# +# Hooke is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with Hooke. If not, see +# . + """Defines :class:`CommandLine` for driving Hooke from the command line. """ +import codecs import cmd import optparse import readline # including readline makes cmd.Cmd.cmdloop() smarter import shlex from ..command import CommandExit, Exit, Command, Argument, StoreValue -from ..interaction import Request, BooleanRequest +from ..interaction import Request, BooleanRequest, ReloadUserInterfaceConfig from ..ui import UserInterface, CommandMessage +from ..util.encoding import get_input_encoding, get_output_encoding # Define a few helper classes. @@ -91,14 +111,16 @@ class DoCommand (CommandMethod): self.cmd.stdout.write(msg.__class__.__name__+'\n') self.cmd.stdout.write(str(msg).rstrip()+'\n') break + elif isinstance(msg, ReloadUserInterfaceConfig): + self.cmd.ui.reload_config(msg.config) + continue elif isinstance(msg, Request): self._handle_request(msg) continue self.cmd.stdout.write(str(msg).rstrip()+'\n') def _parse_args(self, args): - argv = shlex.split(args, comments=True, posix=True) - options,args = self.parser.parse_args(argv) + options,args = self.parser.parse_args(args) self._check_argument_length_bounds(args) params = {} for argument in self.parser.command_opts: @@ -124,7 +146,7 @@ class DoCommand (CommandMethod): If not, raise optparse.OptParseError(). """ min_args = 0 - max_args = -1 + max_args = 0 for argument in self.parser.command_args: if argument.optional == False and argument.count > 0: min_args += argument.count @@ -268,7 +290,7 @@ class LocalHelpCommand (Command): help='The name of the command you want help with.') ] - def _run(self, hooke, inqueue, outqueue, params): + def _run(self, commands, inqueue, outqueue, params): raise NotImplementedError # cmd.Cmd already implements .do_help() class LocalExitCommand (Command): @@ -285,19 +307,20 @@ typing mistakes ;). """.strip()), ]) - def _run(self, hooke, inqueue, outqueue, params): + def _run(self, commands, inqueue, outqueue, params): """The guts of the `do_exit/_quit/_EOF` commands. A `True` return stops :meth:`.cmdloop` execution. """ _exit = True if params['force'] == False: - # TODO: get results of hooke.playlists.current().is_saved() - is_saved = True + not_saved = [p.name for p in hooke.playlists + if p.is_saved() == False] msg = 'Exit?' default = True - if is_saved == False: - msg = 'You did not save your playlist. ' + msg + if len(not_saved) > 0: + msg = 'Unsaved playlists (%s). %s' \ + % (', '.join([str(p) for p in not_saved]), msg) default = False outqueue.put(BooleanRequest(msg, default)) result = inqueue.get() @@ -310,8 +333,9 @@ typing mistakes ;). # Now onto the main attraction. class HookeCmd (cmd.Cmd): - def __init__(self, commands, inqueue, outqueue): + def __init__(self, ui, commands, inqueue, outqueue): cmd.Cmd.__init__(self) + self.ui = ui self.commands = commands self.local_commands = [LocalExitCommand(), LocalHelpCommand()] self.prompt = 'hooke> ' @@ -334,6 +358,42 @@ class HookeCmd (cmd.Cmd): setattr(self.__class__, 'complete_%s' % name, CompleteCommand(self, command, self._name_fn)) + def parseline(self, line): + """Override Cmd.parseline to use shlex.split. + + Notes + ----- + This allows us to handle comments cleanly. With the default + Cmd implementation, a pure comment line will call the .default + error message. + + Since we use shlex to strip comments, we return a list of + split arguments rather than the raw argument string. + """ + line = line.strip() + argv = shlex.split(line, comments=True, posix=True) + if len(argv) == 0: + return None, None, '' # return an empty line + elif argv[0] == '?': + argv[0] = 'help' + elif argv[0] == '!': + argv[0] = 'system' + return argv[0], argv[1:], line + + def do_help(self, arg): + """Wrap Cmd.do_help to handle our .parseline argument list. + """ + if len(arg) == 0: + return cmd.Cmd.do_help(self, '') + return cmd.Cmd.do_help(self, arg[0]) + + def empytline(self): + """Override Cmd.emptyline to not do anything. + + Repeating the last non-empty command seems unwise. Explicit + is better than implicit. + """ + pass class CommandLine (UserInterface): """Command line interface. Simple and powerful. @@ -341,8 +401,20 @@ class CommandLine (UserInterface): def __init__(self): super(CommandLine, self).__init__(name='command line') - def run(self, commands, ui_to_command_queue, command_to_ui_queue): - cmd = HookeCmd(commands, + def _cmd(self, commands, ui_to_command_queue, command_to_ui_queue): + cmd = HookeCmd(self, commands, inqueue=ui_to_command_queue, outqueue=command_to_ui_queue) + #cmd.stdin = codecs.getreader(get_input_encoding())(cmd.stdin) + cmd.stdout = codecs.getwriter(get_output_encoding())(cmd.stdout) + return cmd + + def run(self, commands, ui_to_command_queue, command_to_ui_queue): + cmd = self._cmd(commands, ui_to_command_queue, command_to_ui_queue) cmd.cmdloop(self._splash_text()) + + def run_lines(self, commands, ui_to_command_queue, command_to_ui_queue, + lines): + cmd = self._cmd(commands, ui_to_command_queue, command_to_ui_queue) + for line in lines: + cmd.onecmd(line)