{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE CPP #-}
module Brick.Widgets.Edit
( Editor(editContents, editorName)
, editor
, editorText
, getEditContents
, getCursorPosition
, handleEditorEvent
, applyEdit
, editContentsL
, renderEditor
, editAttr
, editFocusedAttr
, DecodeUtf8(..)
)
where
#if !(MIN_VERSION_base(4,11,0))
import Data.Monoid
#endif
import Lens.Micro
import Graphics.Vty (Event(..), Key(..), Modifier(..))
import qualified Data.ByteString as BS
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.Zipper as Z hiding ( textZipper )
import qualified Data.Text.Zipper.Generic as Z
import qualified Data.Text.Zipper.Generic.Words as Z
import Data.Tuple (swap)
import Brick.Types
import Brick.Widgets.Core
import Brick.AttrMap
data Editor t n =
Editor { forall t n. Editor t n -> TextZipper t
editContents :: Z.TextZipper t
, forall t n. Editor t n -> n
editorName :: n
}
suffixLenses ''Editor
instance (Show t, Show n) => Show (Editor t n) where
show :: Editor t n -> String
show Editor t n
e =
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ String
"Editor { "
, String
"editContents = " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show (forall t n. Editor t n -> TextZipper t
editContents Editor t n
e)
, String
", editorName = " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show (forall t n. Editor t n -> n
editorName Editor t n
e)
, String
"}"
]
instance Named (Editor t n) n where
getName :: Editor t n -> n
getName = forall t n. Editor t n -> n
editorName
class DecodeUtf8 t where
decodeUtf8 :: BS.ByteString -> Either String t
instance DecodeUtf8 T.Text where
decodeUtf8 :: ByteString -> Either String Text
decodeUtf8 ByteString
bs = case ByteString -> Either UnicodeException Text
T.decodeUtf8' ByteString
bs of
Left UnicodeException
e -> forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show UnicodeException
e
Right Text
t -> forall a b. b -> Either a b
Right Text
t
instance DecodeUtf8 String where
decodeUtf8 :: ByteString -> Either String String
decodeUtf8 ByteString
bs = Text -> String
T.unpack forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall t. DecodeUtf8 t => ByteString -> Either String t
decodeUtf8 ByteString
bs
handleEditorEvent :: (Eq n, DecodeUtf8 t, Eq t, Z.GenericTextZipper t)
=> BrickEvent n e
-> EventM n (Editor t n) ()
handleEditorEvent :: forall n t e.
(Eq n, DecodeUtf8 t, Eq t, GenericTextZipper t) =>
BrickEvent n e -> EventM n (Editor t n) ()
handleEditorEvent BrickEvent n e
e = do
Editor t n
ed <- forall s (m :: * -> *). MonadState s m => m s
get
let f :: TextZipper t -> TextZipper t
f = case BrickEvent n e
e of
VtyEvent Event
ev ->
forall {a}.
(DecodeUtf8 a, GenericTextZipper a, Eq a) =>
Event -> TextZipper a -> TextZipper a
handleVtyEvent Event
ev
MouseDown n
n Button
_ [Modifier]
_ (Location (Int, Int)
pos) | n
n forall a. Eq a => a -> a -> Bool
== forall a n. Named a n => a -> n
getName Editor t n
ed ->
forall a. Monoid a => (Int, Int) -> TextZipper a -> TextZipper a
Z.moveCursorClosest (forall a b. (a, b) -> (b, a)
swap (Int, Int)
pos)
BrickEvent n e
_ -> forall a. a -> a
id
handleVtyEvent :: Event -> TextZipper a -> TextZipper a
handleVtyEvent Event
ev = case Event
ev of
EvPaste ByteString
bs -> case forall t. DecodeUtf8 t => ByteString -> Either String t
decodeUtf8 ByteString
bs of
Left String
_ -> forall a. a -> a
id
Right a
t -> forall a. Monoid a => a -> TextZipper a -> TextZipper a
Z.insertMany a
t
EvKey (KChar Char
'a') [Modifier
MCtrl] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.gotoBOL
EvKey (KChar Char
'e') [Modifier
MCtrl] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.gotoEOL
EvKey (KChar Char
'd') [Modifier
MCtrl] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.deleteChar
EvKey (KChar Char
'd') [Modifier
MMeta] -> forall a. GenericTextZipper a => TextZipper a -> TextZipper a
Z.deleteWord
EvKey (KChar Char
'k') [Modifier
MCtrl] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.killToEOL
EvKey (KChar Char
'u') [Modifier
MCtrl] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.killToBOL
EvKey Key
KEnter [] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.breakLine
EvKey Key
KDel [] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.deleteChar
EvKey (KChar Char
c) [] | Char
c forall a. Eq a => a -> a -> Bool
/= Char
'\t' -> forall a. Monoid a => Char -> TextZipper a -> TextZipper a
Z.insertChar Char
c
EvKey Key
KUp [] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.moveUp
EvKey Key
KDown [] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.moveDown
EvKey Key
KLeft [] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.moveLeft
EvKey Key
KRight [] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.moveRight
EvKey (KChar Char
'b') [Modifier
MCtrl] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.moveLeft
EvKey (KChar Char
'f') [Modifier
MCtrl] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.moveRight
EvKey (KChar Char
'b') [Modifier
MMeta] -> forall a. GenericTextZipper a => TextZipper a -> TextZipper a
Z.moveWordLeft
EvKey (KChar Char
'f') [Modifier
MMeta] -> forall a. GenericTextZipper a => TextZipper a -> TextZipper a
Z.moveWordRight
EvKey Key
KBS [] -> forall a. (Eq a, Monoid a) => TextZipper a -> TextZipper a
Z.deletePrevChar
EvKey (KChar Char
't') [Modifier
MCtrl] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.transposeChars
EvKey Key
KHome [] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.gotoBOL
EvKey Key
KEnd [] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.gotoEOL
EvKey (KChar Char
'<') [Modifier
MMeta] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.gotoBOF
EvKey (KChar Char
'>') [Modifier
MMeta] -> forall a. Monoid a => TextZipper a -> TextZipper a
Z.gotoEOF
Event
_ -> forall a. a -> a
id
forall s (m :: * -> *). MonadState s m => s -> m ()
put forall a b. (a -> b) -> a -> b
$ forall t n.
(TextZipper t -> TextZipper t) -> Editor t n -> Editor t n
applyEdit TextZipper t -> TextZipper t
f Editor t n
ed
editorText :: n
-> Maybe Int
-> T.Text
-> Editor T.Text n
editorText :: forall n. n -> Maybe Int -> Text -> Editor Text n
editorText = forall a n.
GenericTextZipper a =>
n -> Maybe Int -> a -> Editor a n
editor
editor :: Z.GenericTextZipper a
=> n
-> Maybe Int
-> a
-> Editor a n
editor :: forall a n.
GenericTextZipper a =>
n -> Maybe Int -> a -> Editor a n
editor n
name Maybe Int
limit a
s = forall t n. TextZipper t -> n -> Editor t n
Editor (forall a. GenericTextZipper a => [a] -> Maybe Int -> TextZipper a
Z.textZipper (forall a. GenericTextZipper a => a -> [a]
Z.lines a
s) Maybe Int
limit) n
name
applyEdit :: (Z.TextZipper t -> Z.TextZipper t)
-> Editor t n
-> Editor t n
applyEdit :: forall t n.
(TextZipper t -> TextZipper t) -> Editor t n -> Editor t n
applyEdit TextZipper t -> TextZipper t
f Editor t n
e = Editor t n
e forall a b. a -> (a -> b) -> b
& forall t n t.
Lens (Editor t n) (Editor t n) (TextZipper t) (TextZipper t)
editContentsL forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ TextZipper t -> TextZipper t
f
editAttr :: AttrName
editAttr :: AttrName
editAttr = String -> AttrName
attrName String
"edit"
editFocusedAttr :: AttrName
editFocusedAttr :: AttrName
editFocusedAttr = AttrName
editAttr forall a. Semigroup a => a -> a -> a
<> String -> AttrName
attrName String
"focused"
getEditContents :: Monoid t => Editor t n -> [t]
getEditContents :: forall t n. Monoid t => Editor t n -> [t]
getEditContents Editor t n
e = forall a. Monoid a => TextZipper a -> [a]
Z.getText forall a b. (a -> b) -> a -> b
$ Editor t n
eforall s a. s -> Getting a s a -> a
^.forall t n t.
Lens (Editor t n) (Editor t n) (TextZipper t) (TextZipper t)
editContentsL
getCursorPosition :: Editor t n -> (Int, Int)
getCursorPosition :: forall t n. Editor t n -> (Int, Int)
getCursorPosition Editor t n
e = forall a. TextZipper a -> (Int, Int)
Z.cursorPosition forall a b. (a -> b) -> a -> b
$ Editor t n
eforall s a. s -> Getting a s a -> a
^.forall t n t.
Lens (Editor t n) (Editor t n) (TextZipper t) (TextZipper t)
editContentsL
renderEditor :: (Ord n, Show n, Monoid t, TextWidth t, Z.GenericTextZipper t)
=> ([t] -> Widget n)
-> Bool
-> Editor t n
-> Widget n
renderEditor :: forall n t.
(Ord n, Show n, Monoid t, TextWidth t, GenericTextZipper t) =>
([t] -> Widget n) -> Bool -> Editor t n -> Widget n
renderEditor [t] -> Widget n
draw Bool
foc Editor t n
e =
let cp :: (Int, Int)
cp = forall a. TextZipper a -> (Int, Int)
Z.cursorPosition TextZipper t
z
z :: TextZipper t
z = Editor t n
eforall s a. s -> Getting a s a -> a
^.forall t n t.
Lens (Editor t n) (Editor t n) (TextZipper t) (TextZipper t)
editContentsL
toLeft :: t
toLeft = forall a. GenericTextZipper a => Int -> a -> a
Z.take ((Int, Int)
cpforall s a. s -> Getting a s a -> a
^.forall s t a b. Field2 s t a b => Lens s t a b
_2) (forall a. Monoid a => TextZipper a -> a
Z.currentLine TextZipper t
z)
cursorLoc :: Location
cursorLoc = (Int, Int) -> Location
Location (forall a. TextWidth a => a -> Int
textWidth t
toLeft, (Int, Int)
cpforall s a. s -> Getting a s a -> a
^.forall s t a b. Field1 s t a b => Lens s t a b
_1)
limit :: Widget n -> Widget n
limit = case Editor t n
eforall s a. s -> Getting a s a -> a
^.forall t n t.
Lens (Editor t n) (Editor t n) (TextZipper t) (TextZipper t)
editContentsLforall b c a. (b -> c) -> (a -> b) -> a -> c
.forall s a. (s -> a) -> SimpleGetter s a
to forall a. TextZipper a -> Maybe Int
Z.getLineLimit of
Maybe Int
Nothing -> forall a. a -> a
id
Just Int
lim -> forall n. Int -> Widget n -> Widget n
vLimit Int
lim
atChar :: Maybe t
atChar = forall t. GenericTextZipper t => TextZipper t -> Maybe t
charAtCursor forall a b. (a -> b) -> a -> b
$ Editor t n
eforall s a. s -> Getting a s a -> a
^.forall t n t.
Lens (Editor t n) (Editor t n) (TextZipper t) (TextZipper t)
editContentsL
atCharWidth :: Int
atCharWidth = forall b a. b -> (a -> b) -> Maybe a -> b
maybe Int
1 forall a. TextWidth a => a -> Int
textWidth Maybe t
atChar
in forall n. AttrName -> Widget n -> Widget n
withAttr (if Bool
foc then AttrName
editFocusedAttr else AttrName
editAttr) forall a b. (a -> b) -> a -> b
$
forall {n}. Widget n -> Widget n
limit forall a b. (a -> b) -> a -> b
$
forall n.
(Ord n, Show n) =>
n -> ViewportType -> Widget n -> Widget n
viewport (Editor t n
eforall s a. s -> Getting a s a -> a
^.forall t n n. Lens (Editor t n) (Editor t n) n n
editorNameL) ViewportType
Both forall a b. (a -> b) -> a -> b
$
(if Bool
foc then forall n. n -> Location -> Widget n -> Widget n
showCursor (Editor t n
eforall s a. s -> Getting a s a -> a
^.forall t n n. Lens (Editor t n) (Editor t n) n n
editorNameL) Location
cursorLoc else forall a. a -> a
id) forall a b. (a -> b) -> a -> b
$
forall n. Location -> (Int, Int) -> Widget n -> Widget n
visibleRegion Location
cursorLoc (Int
atCharWidth, Int
1) forall a b. (a -> b) -> a -> b
$
[t] -> Widget n
draw forall a b. (a -> b) -> a -> b
$
forall t n. Monoid t => Editor t n -> [t]
getEditContents Editor t n
e
charAtCursor :: (Z.GenericTextZipper t) => Z.TextZipper t -> Maybe t
charAtCursor :: forall t. GenericTextZipper t => TextZipper t -> Maybe t
charAtCursor TextZipper t
z =
let col :: Int
col = forall a b. (a, b) -> b
snd forall a b. (a -> b) -> a -> b
$ forall a. TextZipper a -> (Int, Int)
Z.cursorPosition TextZipper t
z
curLine :: t
curLine = forall a. Monoid a => TextZipper a -> a
Z.currentLine TextZipper t
z
toRight :: t
toRight = forall a. GenericTextZipper a => Int -> a -> a
Z.drop Int
col t
curLine
in if forall a. GenericTextZipper a => a -> Int
Z.length t
toRight forall a. Ord a => a -> a -> Bool
> Int
0
then forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a. GenericTextZipper a => Int -> a -> a
Z.take Int
1 t
toRight
else forall a. Maybe a
Nothing