{-# LANGUAGE TemplateHaskell, CPP #-} -- | Widget like "Brick.Widgets.Edit", but with more emacs style keybindings. -- -- This is also a complete wrapper around the "Brick.Widgets.Edit" API to retain -- compatability with older brick versions. -- -- See 'handleEditorEvent' for a list of added keybindings. module Brick.Widgets.Edit.EmacsBindings ( Editor , editorText , getEditContents , applyEdit , editContentsL , handleEditorEvent , renderEditor ) where import Brick import Graphics.Vty import qualified Brick.Widgets.Edit as E import Data.Text.Zipper import Data.Text (Text) import Lens.Micro.TH import Lens.Micro import Data.Text.Zipper.Generic.Words -- | Wrapper around 'E.Editor', but specialized to 'Text' data Editor n = Editor { _origEditor :: E.Editor Text n, _drawingFunction :: [Text] -> Widget n } makeLenses ''Editor -- | Wrapper for 'E.editorText' specialized to 'Text' editorText :: n -> ([Text] -> Widget n)-> Maybe Int -> Text -> Editor n editorText name draw linum content = #if MIN_VERSION_brick(0,19,0) Editor (E.editorText name linum content) draw #else Editor (E.editorText name draw linum content) draw #endif -- | Wrapper for 'E.getEditContents' specialized to 'Text' getEditContents :: Editor n -> [Text] getEditContents edit = edit ^. origEditor . to E.getEditContents -- | Wrapper for 'E.applyEdit' specialized to 'Text' applyEdit :: (TextZipper Text -> TextZipper Text) -> Editor n -> Editor n applyEdit f = over origEditor (E.applyEdit f) -- | Wrapper for 'E.editContentsL' specialized to 'Text' editContentsL :: Lens (Editor n) (Editor n) (TextZipper Text) (TextZipper Text) editContentsL = origEditor . E.editContentsL -- | Same as 'E.handleEditorEvent', but with more emacs-style keybindings and -- specialized to 'Text' -- -- Specifically: -- -- - Ctrl-f: Move forward one character -- - Ctrl-b: Move backward one character -- - Alt-f: Move forward one word -- - Alt-b: Move backward one word -- - Alt-Backspace: Delete the previous word -- - Ctrl-w: Delete the previous word -- - Alt-d: Delete the next word handleEditorEvent :: Event -> Editor n -> EventM n (Editor n) handleEditorEvent event edit = case event of EvKey (KChar 'f') [MCtrl] -> return $ applyEdit moveRight edit EvKey (KChar 'b') [MCtrl] -> return $ applyEdit moveLeft edit EvKey (KChar 'f') [MMeta] -> return $ applyEdit moveWordRight edit EvKey (KChar 'b') [MMeta] -> return $ applyEdit moveWordLeft edit EvKey KBS [MMeta] -> return $ applyEdit deletePrevWord edit EvKey (KChar 'w') [MCtrl] -> return $ applyEdit deletePrevWord edit EvKey (KChar 'd') [MMeta] -> return $ applyEdit deleteWord edit EvKey KHome [] -> return $ applyEdit gotoBOL edit EvKey KEnd [] -> return $ applyEdit gotoEOL edit _ -> do newOrig <- E.handleEditorEvent event (edit^.origEditor) return $ edit & origEditor .~ newOrig -- | Wrapper for 'E.renderEditor' specialized to 'Text' renderEditor :: (Ord n, Show n) => Bool -> Editor n -> Widget n renderEditor focus edit = #if MIN_VERSION_brick(0,19,0) E.renderEditor (edit^.drawingFunction) focus (edit^.origEditor) #else E.renderEditor focus (edit^.origEditor) #endif