summaryrefslogtreecommitdiff
path: root/alot
diff options
context:
space:
mode:
authorYann Rouillard <yann@pleiades.fr.eu.org>2013-06-28 20:51:48 +0200
committerPatrick Totzke <patricktotzke@gmail.com>2013-10-30 20:54:13 +0000
commitb1ccf65f4cf1ee14322b775fcbeacfd2035fe26a (patch)
tree75f68d6891294c0b9feacfe5b2a97506494ab4bf /alot
parent10ad2f1feead866348b5bc0166d019950f334433 (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__.py5
-rw-r--r--alot/commands/globals.py43
-rw-r--r--alot/commands/thread.py9
-rw-r--r--alot/ui.py29
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):
diff --git a/alot/ui.py b/alot/ui.py
index 971345ac..682c37e6 100644
--- a/alot/ui.py
+++ b/alot/ui.py
@@ -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