diff options
author | Yann Rouillard <yann@pleiades.fr.eu.org> | 2013-06-28 20:51:48 +0200 |
---|---|---|
committer | Patrick Totzke <patricktotzke@gmail.com> | 2013-10-30 20:54:13 +0000 |
commit | b1ccf65f4cf1ee14322b775fcbeacfd2035fe26a (patch) | |
tree | 75f68d6891294c0b9feacfe5b2a97506494ab4bf /alot | |
parent | 10ad2f1feead866348b5bc0166d019950f334433 (diff) |
canceling a command in a sequence will now stop the subsequent commands from being run
Conflicts:
alot/commands/globals.py
alot/ui.py
Diffstat (limited to 'alot')
-rw-r--r-- | alot/commands/__init__.py | 5 | ||||
-rw-r--r-- | alot/commands/globals.py | 43 | ||||
-rw-r--r-- | alot/commands/thread.py | 9 | ||||
-rw-r--r-- | alot/ui.py | 29 |
4 files changed, 58 insertions, 28 deletions
diff --git a/alot/commands/__init__.py b/alot/commands/__init__.py index cadb6426..3e8235e3 100644 --- a/alot/commands/__init__.py +++ b/alot/commands/__init__.py @@ -28,6 +28,11 @@ class Command(object): pass +class CommandCanceled(Exception): + """ Exception triggered when an interactive command has been canceled + """ + pass + COMMANDS = { 'search': {}, 'envelope': {}, diff --git a/alot/commands/globals.py b/alot/commands/globals.py index 21b14afc..40eebf2f 100644 --- a/alot/commands/globals.py +++ b/alot/commands/globals.py @@ -4,6 +4,7 @@ import os import code from twisted.internet import threads +from twisted.internet import defer import subprocess import email import urwid @@ -17,6 +18,7 @@ from alot.commands import Command, registerCommand from alot.completion import CommandLineCompleter from alot.commands import CommandParseError from alot.commands import commandfactory +from alot.commands import CommandCanceled from alot import buffers from alot.widgets.utils import DialogBox from alot import helper @@ -123,6 +125,8 @@ class PromptCommand(Command): # save into prompt history ui.commandprompthistory.append(cmdline) ui.apply_commandline(cmdline) + else: + raise CommandCanceled() @registerCommand(MODE, 'refresh') @@ -746,8 +750,8 @@ class ComposeCommand(Command): fromaddress = yield ui.prompt('From', completer=cmpl, tab=1) if fromaddress is None: - ui.notify('canceled') - return + raise CommandCanceled() + self.envelope.add('From', fromaddress) # add signature @@ -802,8 +806,8 @@ class ComposeCommand(Command): to = yield ui.prompt('To', completer=completer) if to is None: - ui.notify('canceled') - return + raise CommandCanceled() + self.envelope.add('To', to.strip(' \t\n,')) if settings.get('ask_subject') and \ @@ -811,8 +815,8 @@ class ComposeCommand(Command): subject = yield ui.prompt('Subject') logging.debug('SUBJECT: "%s"' % subject) if subject is None: - ui.notify('canceled') - return + raise CommandCanceled() + self.envelope.add('Subject', subject) if settings.get('compose_ask_tags'): @@ -820,8 +824,8 @@ class ComposeCommand(Command): tagsstring = yield ui.prompt('Tags', completer=comp) tags = filter(lambda x: x, tagsstring.split(',')) if tags is None: - ui.notify('canceled') - return + raise CommandCanceled() + self.envelope.tags = tags if self.attach: @@ -878,18 +882,27 @@ class CommandSequenceCommand(Command): Command.__init__(self, **kwargs) self.cmdline = cmdline.strip() - @inlineCallbacks def apply(self, ui): + + def apply_command(ignored, cmdstring, cmd): + logging.debug('CMDSEQ: apply %s' % str(cmdstring)) + # store cmdline for use with 'repeat' command + if cmd.repeatable: + ui.last_commandline = self.cmdline.lstrip() + return ui.apply_command(cmd, handle_error=False) + + # we initialize a deferred which is already triggered + # so that our callbacks will start to be called + # immediately as possible + d = defer.succeed(None) + # split commandline if necessary for cmdstring in split_commandline(self.cmdline): - logging.debug('CMDSEQ: apply %s' % str(cmdstring)) # translate cmdstring into :class:`Command` try: cmd = commandfactory(cmdstring, ui.mode) - # store cmdline for use with 'repeat' command - if cmd.repeatable: - ui.last_commandline = self.cmdline.lstrip() - except CommandParseError as e: + except CommandParseError, e: ui.notify(e.message, priority='error') return - yield ui.apply_command(cmd) + d.addCallback(apply_command, cmdstring, cmd) + return d diff --git a/alot/commands/thread.py b/alot/commands/thread.py index d09008d2..3c03644e 100644 --- a/alot/commands/thread.py +++ b/alot/commands/thread.py @@ -17,6 +17,7 @@ from alot.commands.globals import ExternalCommand from alot.commands.globals import FlushCommand from alot.commands.globals import ComposeCommand from alot.commands.globals import MoveCommand +from alot.commands.globals import CommandCanceled from alot.commands.envelope import SendCommand from alot import completion from alot.db.utils import decode_header @@ -385,8 +386,8 @@ class BounceMailCommand(Command): completer = None to = yield ui.prompt('To', completer=completer) if to is None: - ui.notify('canceled') - return + raise CommandCanceled() + mail['Resent-To'] = to.strip(' \t\n,') logging.debug("bouncing mail") @@ -823,7 +824,7 @@ class SaveAttachmentCommand(Command): ui.notify('not a directory: %s' % self.path, priority='error') else: - ui.notify('canceled') + raise CommandCanceled() else: # save focussed attachment focus = ui.get_deep_focus() if isinstance(focus, AttachmentWidget): @@ -842,7 +843,7 @@ class SaveAttachmentCommand(Command): except (IOError, OSError) as e: ui.notify(str(e), priority='error') else: - ui.notify('canceled') + raise CommandCanceled() class OpenAttachmentCommand(Command): @@ -7,6 +7,8 @@ from twisted.internet import reactor, defer from settings import settings from buffers import BufferlistBuffer +from commands import commandfactory +from commands import CommandCanceled from alot.commands import CommandParseError from alot.commands.globals import CommandSequenceCommand from alot.helper import string_decode @@ -563,7 +565,7 @@ class UI(object): footer_att = settings.get_theming_attribute('global', 'footer') return urwid.AttrMap(columns, footer_att) - def apply_command(self, cmd): + def apply_command(self, cmd, handle_error=True): """ applies a command @@ -572,6 +574,11 @@ class UI(object): :param cmd: an applicable command :type cmd: :class:`~alot.commands.Command` + :param handle_error: if True, the caller wants to rely on the default + error handling mechanism to process the eventual + errors raised while the command is applied. + This is the default. + :type handle_error: bool """ if cmd: # define (callback) function that invokes post-hook @@ -581,15 +588,18 @@ class UI(object): return defer.maybeDeferred(cmd.posthook, ui=self, dbm=self.dbman) - # define error handler for Failures/Exceptions + # define a generic error handler for Failures/Exceptions # raised in cmd.apply() def errorHandler(failure): - logging.error(failure.getTraceback()) - errmsg = failure.getErrorMessage() - if errmsg: - msg = "%s\n(check the log for details)" - self.notify( - msg % failure.getErrorMessage(), priority='error') + if failure.check(CommandCanceled): + self.notify('canceled') + else: + logging.error(failure.getTraceback()) + errmsg = failure.getErrorMessage() + if errmsg: + msg = "%s\n(check the log for details)" + self.notify( + msg % failure.getErrorMessage(), priority='error') # call cmd.apply def call_apply(ignored): @@ -599,5 +609,6 @@ class UI(object): d = defer.maybeDeferred(prehook, ui=self, dbm=self.dbman) d.addCallback(call_apply) d.addCallback(call_posthook) - d.addErrback(errorHandler) + if handle_error: + d.addErrback(errorHandler) return d |