matterhorn-30802.1.0: src/Events/Main.hs
{-# LANGUAGE MultiWayIf #-}
module Events.Main where
import Prelude ()
import Prelude.Compat
import Brick
import Brick.Widgets.Edit
import Data.Maybe (catMaybes)
import Data.Monoid ((<>))
import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Data.Text.Zipper as Z
import qualified Data.Text.Zipper.Generic.Words as Z
import qualified Graphics.Vty as Vty
import Lens.Micro.Platform
import Types
import State
import State.Editing
import Command
import Completion
import InputHistory
import Network.Mattermost (Type(..))
onEventMain :: Vty.Event -> MH ()
onEventMain e | Just kb <- lookupKeybinding e mainKeybindings = kbAction kb
onEventMain (Vty.EvPaste bytes) = handlePaste bytes
onEventMain e = handleEditingInput e
mainKeybindings :: [Keybinding]
mainKeybindings =
[ KB "Show this help screen"
(Vty.EvKey (Vty.KFun 1) []) $
showHelpScreen MainHelp
, KB "Select a message to edit/reply/delete"
(Vty.EvKey (Vty.KChar 's') [Vty.MCtrl]) $
beginMessageSelect
, KB "Reply to the most recent message"
(Vty.EvKey (Vty.KChar 'r') [Vty.MCtrl]) $
replyToLatestMessage
, KB "Toggle message preview"
(Vty.EvKey (Vty.KChar 'p') [Vty.MMeta]) $
toggleMessagePreview
, KB "Invoke *$EDITOR* to edit the current message"
(Vty.EvKey (Vty.KChar 'k') [Vty.MMeta]) $
invokeExternalEditor
, KB "Enter fast channel selection mode"
(Vty.EvKey (Vty.KChar 'g') [Vty.MCtrl]) $
beginChannelSelect
, KB "Quit"
(Vty.EvKey (Vty.KChar 'q') [Vty.MCtrl]) $ requestQuit
, KB "Tab-complete forward"
(Vty.EvKey (Vty.KChar '\t') []) $
tabComplete Forwards
, KB "Tab-complete backward"
(Vty.EvKey (Vty.KBackTab) []) $
tabComplete Backwards
, KB "Scroll up in the channel input history"
(Vty.EvKey Vty.KUp []) $ do
-- Up in multiline mode does the usual thing; otherwise we
-- navigate the history.
isMultiline <- use (csEditState.cedMultiline)
case isMultiline of
True -> mhHandleEventLensed csCmdLine handleEditorEvent
(Vty.EvKey Vty.KUp [])
False -> channelHistoryBackward
, KB "Scroll down in the channel input history"
(Vty.EvKey Vty.KDown []) $ do
-- Down in multiline mode does the usual thing; otherwise
-- we navigate the history.
isMultiline <- use (csEditState.cedMultiline)
case isMultiline of
True -> mhHandleEventLensed csCmdLine handleEditorEvent
(Vty.EvKey Vty.KDown [])
False -> channelHistoryForward
, KB "Page up in the channel message list"
(Vty.EvKey Vty.KPageUp []) $ do
cId <- use csCurrentChannelId
let vp = ChannelMessages cId
mh $ invalidateCacheEntry vp
mh $ vScrollToEnd $ viewportScroll vp
mh $ vScrollBy (viewportScroll vp) (-1 * pageAmount)
csMode .= ChannelScroll
, KB "Change to the next channel in the channel list"
(Vty.EvKey (Vty.KChar 'n') [Vty.MCtrl]) $
nextChannel
, KB "Change to the previous channel in the channel list"
(Vty.EvKey (Vty.KChar 'p') [Vty.MCtrl]) $
prevChannel
, KB "Change to the next channel with unread messages"
(Vty.EvKey (Vty.KChar 'a') [Vty.MMeta]) $
nextUnreadChannel
, KB "Change to the most recently-focused channel"
(Vty.EvKey (Vty.KChar 's') [Vty.MMeta]) $
recentChannel
, KB "Send the current message"
(Vty.EvKey Vty.KEnter []) $ do
isMultiline <- use (csEditState.cedMultiline)
case isMultiline of
-- Enter in multiline mode does the usual thing; we
-- only send on Enter when we're outside of multiline
-- mode.
True -> mhHandleEventLensed csCmdLine handleEditorEvent
(Vty.EvKey Vty.KEnter [])
False -> do
csCurrentCompletion .= Nothing
handleInputSubmission
, KB "Select and open a URL posted to the current channel"
(Vty.EvKey (Vty.KChar 'o') [Vty.MCtrl]) $
startUrlSelect
, KB "Clear the current channel's unread message indicator"
(Vty.EvKey (Vty.KChar 'l') [Vty.MMeta]) $ do
cId <- use csCurrentChannelId
clearNewMessageCutoff cId
, KB "Toggle multi-line message compose mode"
(Vty.EvKey (Vty.KChar 'e') [Vty.MMeta]) $
toggleMultilineEditing
, KB "Cancel message reply or update"
(Vty.EvKey Vty.KEsc []) $
cancelReplyOrEdit
, KB "Cancel message reply or update"
(Vty.EvKey (Vty.KChar 'c') [Vty.MCtrl]) $
cancelReplyOrEdit
]
handleInputSubmission :: MH ()
handleInputSubmission = do
cmdLine <- use csCmdLine
cId <- use csCurrentChannelId
-- send the relevant message
mode <- use (csEditState.cedEditMode)
let (line:rest) = getEditContents cmdLine
allLines = T.intercalate "\n" $ line : rest
-- We clean up before dispatching the command or sending the message
-- since otherwise the command could change the state and then doing
-- cleanup afterwards could clean up the wrong things.
csCmdLine %= applyEdit Z.clearZipper
csInputHistory %= addHistoryEntry allLines cId
csInputHistoryPosition.at cId .= Nothing
csEditState.cedEditMode .= NewPost
case T.uncons line of
Just ('/',cmd) -> dispatchCommand cmd
_ -> sendMessage mode allLines
tabComplete :: Completion.Direction -> MH ()
tabComplete dir = do
st <- use id
let completableChannels = catMaybes (flip map (st^.csNames.cnChans) $ \cname -> do
-- Only permit completion of channel names for non-Group channels
cId <- st^.csNames.cnToChanId.at cname
let cInfo = st^.csChannel(cId).ccInfo
case cInfo^.cdType /= Group of
True -> Just cname
False -> Nothing
)
priorities = [] :: [T.Text]-- XXX: add recent completions to this
completions = Set.fromList (st^.csNames.cnUsers ++
completableChannels ++
map (T.singleton userSigil <>) (st^.csNames.cnUsers) ++
map (T.singleton normalChannelSigil <>) completableChannels ++
map ("/" <>) (commandName <$> commandList))
line = Z.currentLine $ st^.csCmdLine.editContentsL
curComp = st^.csCurrentCompletion
(nextComp, alts) = case curComp of
Nothing -> let cw = currentWord line
in (Just cw, filter (cw `T.isPrefixOf`) $ Set.toList completions)
Just cw -> (Just cw, filter (cw `T.isPrefixOf`) $ Set.toList completions)
mb_word = wordComplete dir priorities completions line curComp
csCurrentCompletion .= nextComp
csEditState.cedCompletionAlternatives .= alts
let (edit, curAlternative) = case mb_word of
Nothing -> (id, "")
Just w -> (Z.insertMany w . Z.deletePrevWord, w)
csCmdLine %= (applyEdit edit)
csEditState.cedCurrentAlternative .= curAlternative