Skip to content

Commit

Permalink
Allow to automatically open attachments
Browse files Browse the repository at this point in the history
This adds a proof of concept mailcap declaration.

If the lookup for a content type is successful, we use the mapped command,
otherwise show an input editor. Keep the possibility to open the attachment with
a different command by binding the previous open action to a different
keystroke.

A sample mailcap is used and adds a mapping from text/html, application/pdf to
xdg-open. This patch would also fix #174 since it is now possible to
automatically invoke a program to view an HTML attachment.

Fixes #182 and
      #174
  • Loading branch information
romanofski committed Mar 30, 2019
1 parent ce23bc5 commit 534adc2
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 9 deletions.
4 changes: 4 additions & 0 deletions src/Config/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ defaultConfig =
, _mvMailListOfAttachmentsKeybindings = mailAttachmentsKeybindings
, _mvOpenWithKeybindings = openWithKeybindings
, _mvPipeToKeybindings = pipeToKeybindings
, _mvMailcap = [
(("text", Just "html"), "xdg-open")
, (("application", Just "pdf"), "xdg-open")
]
}
, _confIndexView = IndexViewSettings
{ _ivBrowseThreadsKeybindings = browseThreadsKeybindings
Expand Down
4 changes: 4 additions & 0 deletions src/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ data MailViewSettings = MailViewSettings
, _mvMailListOfAttachmentsKeybindings :: [Keybinding 'ViewMail 'MailListOfAttachments]
, _mvOpenWithKeybindings :: [Keybinding 'ViewMail 'MailAttachmentOpenWithEditor]
, _mvPipeToKeybindings :: [Keybinding 'ViewMail 'MailAttachmentPipeToEditor]
, _mvMailcap :: [((CI.CI B.ByteString, Maybe (CI.CI B.ByteString)), String)]
}
deriving (Generic, NFData)

Expand Down Expand Up @@ -374,6 +375,9 @@ mvOpenWithKeybindings = lens _mvOpenWithKeybindings (\s x -> s { _mvOpenWithKeyb
mvPipeToKeybindings :: Lens' MailViewSettings [Keybinding 'ViewMail 'MailAttachmentPipeToEditor]
mvPipeToKeybindings = lens _mvPipeToKeybindings (\s x -> s { _mvPipeToKeybindings = x })

mvMailcap :: Lens' MailViewSettings [((CI.CI B.ByteString, Maybe (CI.CI B.ByteString)), String)]
mvMailcap = lens _mvMailcap (\s x -> s { _mvMailcap = x })

data ViewName
= Threads
| Mails
Expand Down
38 changes: 30 additions & 8 deletions src/UI/Actions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ module UI.Actions (
, delete
, applySearch
, openWithCommand
, openAttachment
, pipeToCommand
) where

Expand Down Expand Up @@ -92,11 +93,11 @@ import qualified Data.Vector as Vector
import Prelude hiding (readFile, unlines)
import Data.Functor (($>))
import Control.Lens
(_Just, to, at, ix, _1, _2, toListOf, traversed, has, snoc,
(_Just, to, at, ix, _1, _2, toListOf, traverse, traversed, has, snoc,
filtered, set, over, preview, view, views, (&), nullOf, firstOf,
traversed, traverse, Getting, Lens')
Getting, Lens')
import Control.Concurrent (forkIO)
import Control.Monad ((>=>))
import Control.Monad ((>=>), join)
import Control.Monad.Except (runExceptT)
import Control.Exception (catch, IOException)
import Control.Monad.IO.Class (liftIO, MonadIO)
Expand All @@ -109,10 +110,10 @@ import Data.MIME
(createMultipartMixedMessage, contentTypeApplicationOctetStream,
createTextPlainMessage, createAttachmentFromFile, renderMessage,
contentDisposition, dispositionType, headers, filename,
parseContentType, attachments, isAttachment, entities,
matchContentType, contentType, mailboxList, renderMailboxes,
addressList, renderAddresses, renderRFC5422Date, MIMEMessage,
WireEntity, DispositionType(..), ContentType(..), Mailbox(..))
parseContentType, attachments, isAttachment, entities, matchContentType,
contentType, mailboxList, renderMailboxes, addressList, renderAddresses,
renderRFC5422Date, MIMEMessage, WireEntity, DispositionType(..),
ContentType(..), Mailbox(..))
import qualified Storage.Notmuch as Notmuch
import Storage.ParsedMail
(parseMail, getTo, getFrom, getSubject, toQuotedMail, entityToBytes)
Expand Down Expand Up @@ -559,10 +560,31 @@ invokeEditor = Action ["invoke external editor"] (Brick.suspendAndResume . liftI
edit :: Action 'ComposeView 'ComposeListOfAttachments (T.Next AppState)
edit = Action ["edit file"] (Brick.suspendAndResume . liftIO . editAttachment)

openAttachment :: Action 'ViewMail ctx (T.Next AppState)
openAttachment =
Action
{ _aDescription = ["open attachment with external command"]
, _aAction = \s ->
let
match ct = firstOf (asConfig . confMailView . mvMailcap . traversed
. filtered (flip (uncurry matchContentType) ct . fst)
. _2) s
maybeCommand =
join
$ match
<$> preview (asMailView . mvAttachments . to L.listSelectedElement . _Just . _2 . headers . contentType) s
in case maybeCommand of
(Just cmd) -> Brick.suspendAndResume $ liftIO $ openCommand' s cmd
Nothing ->
Brick.continue
$ s & set (asViews . vsViews . at ViewMail . _Just . vFocus) MailAttachmentOpenWithEditor
. set (asViews . vsViews . at ViewMail . _Just . vWidgets . ix MailAttachmentOpenWithEditor . veState) Visible
}

openWithCommand :: Action 'ViewMail 'MailAttachmentOpenWithEditor (T.Next AppState)
openWithCommand =
Action
{ _aDescription = ["pass to external command"]
{ _aDescription = ["ask for command to open attachment"]
, _aAction = \s ->
let cmd = view (asMailView . mvOpenCommand . E.editContentsL . to (T.unpack . currentLine)) s
in Brick.suspendAndResume $ liftIO $ openCommand' s cmd
Expand Down
3 changes: 2 additions & 1 deletion src/UI/Mail/Keybindings.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ mailAttachmentsKeybindings =
[ Keybinding (V.EvKey (V.KChar 'j') []) (listDown `chain` continue)
, Keybinding (V.EvKey (V.KChar 'k') []) (listUp `chain` continue)
, Keybinding (V.EvKey (V.KChar 'q') []) (abort `chain'` (focus :: Action 'ViewMail 'ScrollingMailView AppState) `chain` continue)
, Keybinding (V.EvKey V.KEnter []) (noop `chain'` (focus :: Action 'ViewMail 'MailAttachmentOpenWithEditor AppState) `chain` continue)
, Keybinding (V.EvKey V.KEnter []) openAttachment
, Keybinding (V.EvKey (V.KChar 'o') []) (noop `chain'` (focus :: Action 'ViewMail 'MailAttachmentOpenWithEditor AppState) `chain` continue)
, Keybinding (V.EvKey (V.KChar '|') []) (noop `chain'` (focus :: Action 'ViewMail 'MailAttachmentPipeToEditor AppState) `chain` continue)
]

Expand Down

0 comments on commit 534adc2

Please sign in to comment.