{-# OPTIONS_HADDOCK show-extensions #-} {-# language RankNTypes #-} -- | -- Module : Yi.Keymap.Emacs.KillRing -- License : GPL-2 -- Maintainer : yi-devel@googlegroups.com -- Stability : experimental -- Portability : portable module Yi.Keymap.Emacs.KillRing where import Lens.Micro.Platform (use, (%=), (.=), Getting) import Control.Monad (replicateM_, when) import Control.Monad.State.Class (MonadState) import Data.List.NonEmpty (NonEmpty ((:|))) import Data.Maybe (fromMaybe) import Yi.Buffer import Yi.Editor (EditorM, killringA, withCurrentBuffer) import Yi.Keymap (YiM) import Yi.KillRing (Killring (_krContents), krKilled, krPut) import qualified Yi.Rope as R (YiString, fromString, toString) import Yi.Types (withEditor) import Yi.Utils (io) import System.Hclip (getClipboard, setClipboard) uses :: forall a b f s. MonadState s f => Getting a s a -> (a -> b) -> f b uses l f = f <$> use l -- * Killring actions -- | Adds system clipboard's contents on top of the killring if not already there clipboardToKillring :: YiM () clipboardToKillring = do text <- fmap R.fromString $ io getClipboard withEditor $ do text' <- killringGet when (text' /= text) $ killringPut Forward text -- | Adds the top of the killring to the system clipboard killringToClipboard :: YiM () killringToClipboard = do text <- withEditor killringGet io . setClipboard $ R.toString text -- This is like @kill-region-or-backward-word@. killRegionB :: BufferM () killRegionB = getSelectRegionB >>= \r -> if regionStart r == regionEnd r then bkillWordB else deleteRegionB r -- | C-w -- Like `killRegionB`, but with system clipboard synchronization killRegion :: YiM () killRegion = withCurrentBuffer killRegionB >> killringToClipboard -- | Kills current line killLineB :: Maybe Int -> BufferM () killLineB mbr = replicateM_ (fromMaybe 1 mbr) $ do eol <- atEol let tu = if eol then Character else Line deleteRegionB =<< regionOfPartNonEmptyB tu Forward -- | C-k -- | Like `killLineB`, but with system clipboard synchronization killLine :: Maybe Int -> YiM () killLine mbr = withCurrentBuffer (killLineB mbr) >> killringToClipboard killringGet :: EditorM R.YiString killringGet = do text :| _ <- uses killringA _krContents return text killringPut :: Direction -> R.YiString -> EditorM () killringPut dir s = killringA %= krPut dir s -- | Yanks top of killbuffer yankE :: EditorM () yankE = do text :| _ <- uses killringA _krContents withCurrentBuffer $ pointB >>= setSelectionMarkPointB >> insertN text -- | C-y -- Like `yankE`, but with system clipboard synchronization yank :: YiM () yank = clipboardToKillring >> withEditor yankE -- | Saves current selection to killring and then clears it killRingSaveE :: EditorM () killRingSaveE = do (r, text) <- withCurrentBuffer $ do r <- getSelectRegionB text <- readRegionB r highlightSelectionA .= False return (r, text) killringPut (regionDirection r) text -- | M-w -- Like `killRingSaveE`, but with system clipboard synchronization killRingSave :: YiM () killRingSave = withEditor killRingSaveE >> killringToClipboard -- | M-y -- TODO: Handle argument, verify last command was a yank yankPopE :: EditorM () yankPopE = do kr <- use killringA withCurrentBuffer (deleteRegionB =<< getRawestSelectRegionB) killringA .= let x :| xs = _krContents kr in kr { _krContents = case xs of [] -> x :| [] y:ys -> y :| ys ++ [x] } yankE -- | C-M-w appendNextKillE :: EditorM () appendNextKillE = killringA . krKilled .= True