Cleaning up hooke.ui.gui. Can initialize, but without most bindings.
[hooke.git] / hooke / ui / commandline.py
index 3ab755d61b6180c5175acebdf71b26ab7448491a..005e039724eac468ba2b4f3c019df8fd29913f70 100644 (file)
@@ -1,15 +1,35 @@
+# Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
+#
+# 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
+# <http://www.gnu.org/licenses/>.
+
 """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)