Skip to content

Commit 05d48f7

Browse files
committed
Allow to automatically open attachments
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 to elinks and a catch all 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 erasf
1 parent ce23bc5 commit 05d48f7

File tree

4 files changed

+41
-10
lines changed

4 files changed

+41
-10
lines changed

src/Config/Main.hs

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import System.Directory (getHomeDirectory)
1717
import Data.Maybe (fromMaybe)
1818
import System.Exit (ExitCode(..))
1919

20-
import Data.MIME (contentTypeTextPlain)
20+
import Data.MIME (contentTypeTextPlain, matchContentType)
2121

2222
import UI.FileBrowser.Keybindings
2323
(fileBrowserKeybindings, manageSearchPathKeybindings)
@@ -178,6 +178,10 @@ defaultConfig =
178178
, _mvMailListOfAttachmentsKeybindings = mailAttachmentsKeybindings
179179
, _mvOpenWithKeybindings = openWithKeybindings
180180
, _mvPipeToKeybindings = pipeToKeybindings
181+
, _mvMailcap = [
182+
((matchContentType "text" (Just "html")), "elinks -force-html")
183+
, (const True, "xdg-open")
184+
]
181185
}
182186
, _confIndexView = IndexViewSettings
183187
{ _ivBrowseThreadsKeybindings = browseThreadsKeybindings

src/Types.hs

+4
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ data MailViewSettings = MailViewSettings
347347
, _mvMailListOfAttachmentsKeybindings :: [Keybinding 'ViewMail 'MailListOfAttachments]
348348
, _mvOpenWithKeybindings :: [Keybinding 'ViewMail 'MailAttachmentOpenWithEditor]
349349
, _mvPipeToKeybindings :: [Keybinding 'ViewMail 'MailAttachmentPipeToEditor]
350+
, _mvMailcap :: [(ContentType -> Bool, String)]
350351
}
351352
deriving (Generic, NFData)
352353

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

378+
mvMailcap :: Lens' MailViewSettings [(ContentType -> Bool, String)]
379+
mvMailcap = lens _mvMailcap (\s x -> s { _mvMailcap = x })
380+
377381
data ViewName
378382
= Threads
379383
| Mails

src/UI/Actions.hs

+30-8
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ module UI.Actions (
6161
, delete
6262
, applySearch
6363
, openWithCommand
64+
, openAttachment
6465
, pipeToCommand
6566
) where
6667

@@ -92,11 +93,11 @@ import qualified Data.Vector as Vector
9293
import Prelude hiding (readFile, unlines)
9394
import Data.Functor (($>))
9495
import Control.Lens
95-
(_Just, to, at, ix, _1, _2, toListOf, traversed, has, snoc,
96+
(_Just, to, at, ix, _1, _2, toListOf, traverse, traversed, has, snoc,
9697
filtered, set, over, preview, view, views, (&), nullOf, firstOf,
97-
traversed, traverse, Getting, Lens')
98+
Getting, Lens')
9899
import Control.Concurrent (forkIO)
99-
import Control.Monad ((>=>))
100+
import Control.Monad ((>=>), join)
100101
import Control.Monad.Except (runExceptT)
101102
import Control.Exception (catch, IOException)
102103
import Control.Monad.IO.Class (liftIO, MonadIO)
@@ -109,10 +110,10 @@ import Data.MIME
109110
(createMultipartMixedMessage, contentTypeApplicationOctetStream,
110111
createTextPlainMessage, createAttachmentFromFile, renderMessage,
111112
contentDisposition, dispositionType, headers, filename,
112-
parseContentType, attachments, isAttachment, entities,
113-
matchContentType, contentType, mailboxList, renderMailboxes,
114-
addressList, renderAddresses, renderRFC5422Date, MIMEMessage,
115-
WireEntity, DispositionType(..), ContentType(..), Mailbox(..))
113+
parseContentType, attachments, isAttachment, entities, matchContentType,
114+
contentType, mailboxList, renderMailboxes, addressList, renderAddresses,
115+
renderRFC5422Date, MIMEMessage, WireEntity, DispositionType(..),
116+
ContentType(..), Mailbox(..))
116117
import qualified Storage.Notmuch as Notmuch
117118
import Storage.ParsedMail
118119
(parseMail, getTo, getFrom, getSubject, toQuotedMail, entityToBytes)
@@ -559,10 +560,31 @@ invokeEditor = Action ["invoke external editor"] (Brick.suspendAndResume . liftI
559560
edit :: Action 'ComposeView 'ComposeListOfAttachments (T.Next AppState)
560561
edit = Action ["edit file"] (Brick.suspendAndResume . liftIO . editAttachment)
561562

563+
openAttachment :: Action 'ViewMail ctx (T.Next AppState)
564+
openAttachment =
565+
Action
566+
{ _aDescription = ["open attachment with external command"]
567+
, _aAction = \s ->
568+
let
569+
match ct = firstOf (asConfig . confMailView . mvMailcap . traversed
570+
. filtered (flip fst ct)
571+
. _2) s
572+
maybeCommand =
573+
join
574+
$ match
575+
<$> preview (asMailView . mvAttachments . to L.listSelectedElement . _Just . _2 . headers . contentType) s
576+
in case maybeCommand of
577+
(Just cmd) -> Brick.suspendAndResume $ liftIO $ openCommand' s cmd
578+
Nothing ->
579+
Brick.continue
580+
$ s & set (asViews . vsViews . at ViewMail . _Just . vFocus) MailAttachmentOpenWithEditor
581+
. set (asViews . vsViews . at ViewMail . _Just . vWidgets . ix MailAttachmentOpenWithEditor . veState) Visible
582+
}
583+
562584
openWithCommand :: Action 'ViewMail 'MailAttachmentOpenWithEditor (T.Next AppState)
563585
openWithCommand =
564586
Action
565-
{ _aDescription = ["pass to external command"]
587+
{ _aDescription = ["ask for command to open attachment"]
566588
, _aAction = \s ->
567589
let cmd = view (asMailView . mvOpenCommand . E.editContentsL . to (T.unpack . currentLine)) s
568590
in Brick.suspendAndResume $ liftIO $ openCommand' s cmd

src/UI/Mail/Keybindings.hs

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ mailAttachmentsKeybindings =
4141
[ Keybinding (V.EvKey (V.KChar 'j') []) (listDown `chain` continue)
4242
, Keybinding (V.EvKey (V.KChar 'k') []) (listUp `chain` continue)
4343
, Keybinding (V.EvKey (V.KChar 'q') []) (abort `chain'` (focus :: Action 'ViewMail 'ScrollingMailView AppState) `chain` continue)
44-
, Keybinding (V.EvKey V.KEnter []) (noop `chain'` (focus :: Action 'ViewMail 'MailAttachmentOpenWithEditor AppState) `chain` continue)
44+
, Keybinding (V.EvKey V.KEnter []) openAttachment
45+
, Keybinding (V.EvKey (V.KChar 'o') []) (noop `chain'` (focus :: Action 'ViewMail 'MailAttachmentOpenWithEditor AppState) `chain` continue)
4546
, Keybinding (V.EvKey (V.KChar '|') []) (noop `chain'` (focus :: Action 'ViewMail 'MailAttachmentPipeToEditor AppState) `chain` continue)
4647
]
4748

0 commit comments

Comments
 (0)