{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE InstanceSigs #-}
{-# OPTIONS_HADDOCK show-extensions #-}

-- |
-- Module      :  Yi.Fuzzy
-- License     :  GPL-2
-- Maintainer  :  yi-devel@googlegroups.com
-- Stability   :  experimental
-- Portability :  portable

--   TODO if need arises: factor out generic part that captures a pattern of
--   having an interactive minibuffer and a window that just renders some state.

module Yi.Fuzzy (fuzzyOpen, fuzzyOpenWithDepth, defaultDepth) where

import Control.Monad (void)
import Control.Monad.Base (liftBase)
import Control.Monad.State (gets)
import Data.Binary (Binary(..), Word8)
import Data.Default (Default(..))
import Data.Foldable (Foldable(..))
import Data.List (isSuffixOf)
import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
import Data.List.PointedList (PointedList(..))
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
import Data.Text (Text)
import Data.Typeable (Typeable)
import GHC.Generics (Generic)
import GHC.Natural (Natural)
import System.Directory (doesDirectoryExist, getDirectoryContents)
import System.FilePath ((</>))
import System.IO.Error (tryIOError)

import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Map.Strict as M
import qualified Data.List.PointedList as PL

import Data.List.PointedList.Extras as PL

import Yi
import Yi.Completion
import Yi.MiniBuffer
import Yi.Types
import Yi.Utils ()
import qualified Yi.Rope as R


-- FuzzyState is stored in minibuffer's dynamic state
data FuzzyState = FuzzyState
  { FuzzyState -> Maybe (PointedList FuzzyItem)
items :: !(Maybe (PointedList FuzzyItem))
  , FuzzyState -> Text
search :: !Text
  } deriving (Int -> FuzzyState -> ShowS
[FuzzyState] -> ShowS
FuzzyState -> String
(Int -> FuzzyState -> ShowS)
-> (FuzzyState -> String)
-> ([FuzzyState] -> ShowS)
-> Show FuzzyState
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FuzzyState] -> ShowS
$cshowList :: [FuzzyState] -> ShowS
show :: FuzzyState -> String
$cshow :: FuzzyState -> String
showsPrec :: Int -> FuzzyState -> ShowS
$cshowsPrec :: Int -> FuzzyState -> ShowS
Show, (forall x. FuzzyState -> Rep FuzzyState x)
-> (forall x. Rep FuzzyState x -> FuzzyState) -> Generic FuzzyState
forall x. Rep FuzzyState x -> FuzzyState
forall x. FuzzyState -> Rep FuzzyState x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep FuzzyState x -> FuzzyState
$cfrom :: forall x. FuzzyState -> Rep FuzzyState x
Generic, Typeable)

data FuzzyItem
  = FileItem !Text
  | BufferItem !BufferId
  deriving (Typeable)

instance Show FuzzyItem where
  show :: FuzzyItem -> String
  show :: FuzzyItem -> String
show i :: FuzzyItem
i@(FileItem   Text
_) = String
"File  "   String -> ShowS
forall a. Semigroup a => a -> a -> a
<> FuzzyItem -> String
itemAsStr FuzzyItem
i
  show i :: FuzzyItem
i@(BufferItem BufferId
_) = String
"Buffer  " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> FuzzyItem -> String
itemAsStr FuzzyItem
i

itemAsTxt :: FuzzyItem -> Text
itemAsTxt :: FuzzyItem -> Text
itemAsTxt (FileItem Text
x) = Text
x
itemAsTxt (BufferItem (MemBuffer  Text
x)) = Text
x
itemAsTxt (BufferItem (FileBuffer String
x)) = String -> Text
T.pack String
x

itemAsStr :: FuzzyItem -> String
itemAsStr :: FuzzyItem -> String
itemAsStr = Text -> String
T.unpack (Text -> String) -> (FuzzyItem -> Text) -> FuzzyItem -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FuzzyItem -> Text
itemAsTxt

-- | The depth 'fuzzyOpen' should traverse by default. Currently __5__.
defaultDepth :: Natural
defaultDepth :: Natural
defaultDepth = Natural
5

-- | Fuzzy open the current directory. The depth searched is
-- 'defaultDepth', use fuzzyOpenWithDepth if you want to customise
-- this.
fuzzyOpen :: YiM ()
fuzzyOpen :: YiM ()
fuzzyOpen = Natural -> YiM ()
fuzzyOpenWithDepth Natural
defaultDepth

-- | Fuzzy-opens the directory to the specified depth. The depth needs
-- to be at least @1@ for it to do anything meaningful.
fuzzyOpenWithDepth :: Natural -> YiM ()
fuzzyOpenWithDepth :: Natural -> YiM ()
fuzzyOpenWithDepth Natural
d = do
  [FuzzyItem]
fileList  <- (([String] -> [FuzzyItem]) -> YiM [String] -> YiM [FuzzyItem]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (([String] -> [FuzzyItem]) -> YiM [String] -> YiM [FuzzyItem])
-> ((String -> FuzzyItem) -> [String] -> [FuzzyItem])
-> (String -> FuzzyItem)
-> YiM [String]
-> YiM [FuzzyItem]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> FuzzyItem) -> [String] -> [FuzzyItem]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap) (Text -> FuzzyItem
FileItem (Text -> FuzzyItem) -> (String -> Text) -> String -> FuzzyItem
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack) (IO [String] -> YiM [String]
forall (b :: * -> *) (m :: * -> *) α. MonadBase b m => b α -> m α
liftBase (IO [String] -> YiM [String]) -> IO [String] -> YiM [String]
forall a b. (a -> b) -> a -> b
$ Natural -> String -> IO [String]
getRecursiveContents Natural
d String
".")
  [FuzzyItem]
bufList   <- (([FBuffer] -> [FuzzyItem]) -> YiM [FBuffer] -> YiM [FuzzyItem]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (([FBuffer] -> [FuzzyItem]) -> YiM [FBuffer] -> YiM [FuzzyItem])
-> ((FBuffer -> FuzzyItem) -> [FBuffer] -> [FuzzyItem])
-> (FBuffer -> FuzzyItem)
-> YiM [FBuffer]
-> YiM [FuzzyItem]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FBuffer -> FuzzyItem) -> [FBuffer] -> [FuzzyItem]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap) (BufferId -> FuzzyItem
BufferItem (BufferId -> FuzzyItem)
-> (FBuffer -> BufferId) -> FBuffer -> FuzzyItem
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Attributes -> BufferId
ident (Attributes -> BufferId)
-> (FBuffer -> Attributes) -> FBuffer -> BufferId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FBuffer -> Attributes
attributes) (EditorM [FBuffer] -> YiM [FBuffer]
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor ((Editor -> [FBuffer]) -> EditorM [FBuffer]
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets (Map BufferRef FBuffer -> [FBuffer]
forall k a. Map k a -> [a]
M.elems (Map BufferRef FBuffer -> [FBuffer])
-> (Editor -> Map BufferRef FBuffer) -> Editor -> [FBuffer]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Editor -> Map BufferRef FBuffer
buffers)))
  BufferRef
promptRef <- EditorM BufferRef -> YiM BufferRef
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor (Text -> KeymapEndo -> EditorM BufferRef
spawnMinibufferE Text
"" (Keymap -> KeymapEndo
forall a b. a -> b -> a
const Keymap
localKeymap))

  let initialState :: FuzzyState
initialState = Maybe (PointedList FuzzyItem) -> Text -> FuzzyState
FuzzyState ([FuzzyItem] -> Maybe (PointedList FuzzyItem)
forall a. [a] -> Maybe (PointedList a)
PL.fromList ([FuzzyItem] -> [FuzzyItem]
filterNotCommon [FuzzyItem]
bufList [FuzzyItem] -> [FuzzyItem] -> [FuzzyItem]
forall a. Semigroup a => a -> a -> a
<> [FuzzyItem]
fileList)) Text
""
  BufferRef -> BufferM () -> YiM ()
forall (m :: * -> *) a.
MonadEditor m =>
BufferRef -> BufferM a -> m a
withGivenBuffer BufferRef
promptRef (BufferM () -> YiM ()) -> BufferM () -> YiM ()
forall a b. (a -> b) -> a -> b
$ FuzzyState -> BufferM ()
forall a (m :: * -> *).
(YiVariable a, MonadState FBuffer m, Functor m) =>
a -> m ()
putBufferDyn FuzzyState
initialState
  EditorM () -> YiM ()
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor (FuzzyState -> EditorM ()
renderE FuzzyState
initialState)
 where
  filterNotCommon :: [FuzzyItem] -> [FuzzyItem]
  filterNotCommon :: [FuzzyItem] -> [FuzzyItem]
filterNotCommon = (FuzzyItem -> Bool) -> [FuzzyItem] -> [FuzzyItem]
forall a. (a -> Bool) -> [a] -> [a]
filter ((\Text
n -> Bool -> Bool
not (Text
n Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"console" Bool -> Bool -> Bool
|| Text
n Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"messages")) (Text -> Bool) -> (FuzzyItem -> Text) -> FuzzyItem -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FuzzyItem -> Text
itemAsTxt)


-- takes about 3 seconds to traverse linux kernel, which is not too outrageous
-- TODO: check if it works at all with cyclic links
-- TODO: perform in background, limit file count or directory depth
getRecursiveContents :: Natural -> FilePath -> IO [FilePath]
getRecursiveContents :: Natural -> String -> IO [String]
getRecursiveContents Natural
d String
t
  | Natural
d Natural -> Natural -> Bool
forall a. Eq a => a -> a -> Bool
== Natural
0 = [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return [String]
forall a. Monoid a => a
mempty
  | Bool
otherwise = do
    Either IOError [String]
x <- IO [String] -> IO (Either IOError [String])
forall a. IO a -> IO (Either IOError a)
tryIOError (String -> IO [String]
getDirectoryContents String
t)
    case Either IOError [String]
x of
      Left  IOError
_     -> [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return [String]
forall a. Monoid a => a
mempty
      Right [String]
names -> do
        [[String]]
paths <- (String -> IO [String]) -> [String] -> IO [[String]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM String -> IO [String]
withName ((String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter String -> Bool
isProperName [String]
names)
        [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return ([String] -> IO [String]) -> [String] -> IO [String]
forall a b. (a -> b) -> a -> b
$ [[String]] -> [String]
forall a. Monoid a => [a] -> a
mconcat [[String]]
paths
 where
  isProperName :: FilePath -> Bool
  isProperName :: String -> Bool
isProperName String
fileName = [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
and
    [ String
fileName String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String
".", String
"..", String
".git", String
".svn"]
    , Bool -> Bool
not (String
".hi" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
fileName)
    , Bool -> Bool
not (String
"-boot" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
fileName)
    ]

  withName :: FilePath -> IO [FilePath]
  withName :: String -> IO [String]
withName String
name = do
    let path :: String
path = String
t String -> ShowS
</> String
name
    Bool
isDirectory <- String -> IO Bool
doesDirectoryExist String
path
    if Bool
isDirectory then Natural -> String -> IO [String]
getRecursiveContents (Natural
d Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
- Natural
1) String
path else [String] -> IO [String]
forall (f :: * -> *) a. Applicative f => a -> f a
pure [String
path]

localKeymap :: Keymap
localKeymap :: Keymap
localKeymap =
  [Keymap] -> Keymap
forall (m :: * -> *) w e a.
(MonadInteract m w e, MonadFail m) =>
[m a] -> m a
choice
      [ Key -> Event
spec Key
KEnter Event -> YiM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! YiM ()
openInThisWindow
      , Char -> Event
ctrlCh Char
't' Event -> YiM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! YiM ()
openInNewTab
      , Char -> Event
ctrlCh Char
's' Event -> YiM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! YiM ()
openInSplit
      , Key -> Event
spec Key
KEsc Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! EditorM ()
cleanupE
      , Char -> Event
ctrlCh Char
'g' Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! EditorM ()
cleanupE
      , Char -> Event
ctrlCh Char
'h' Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! BufferM () -> EditorM ()
updatingB (TextUnit -> Direction -> BufferM ()
deleteB TextUnit
Character Direction
Backward)
      , Key -> Event
spec Key
KBS Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! BufferM () -> EditorM ()
updatingB (TextUnit -> Direction -> BufferM ()
deleteB TextUnit
Character Direction
Backward)
      , Key -> Event
spec Key
KDel Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! BufferM () -> EditorM ()
updatingB (TextUnit -> Direction -> BufferM ()
deleteB TextUnit
Character Direction
Backward)
      , Char -> Event
ctrlCh Char
'a' Event -> BufferM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! BufferM ()
moveToSol
      , Char -> Event
ctrlCh Char
'e' Event -> BufferM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! BufferM ()
moveToEol
      , Key -> Event
spec Key
KLeft Event -> BufferM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! Int -> BufferM ()
moveXorSol Int
1
      , Key -> Event
spec Key
KRight Event -> BufferM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! Int -> BufferM ()
moveXorEol Int
1
      , Char -> Event
ctrlCh Char
'p' Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! (FuzzyState -> FuzzyState) -> EditorM ()
modifyE FuzzyState -> FuzzyState
goPrevious
      , Char -> Event
ctrlCh Char
'n' Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! (FuzzyState -> FuzzyState) -> EditorM ()
modifyE FuzzyState -> FuzzyState
goNext
      , Key -> Event
spec Key
KDown Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! (FuzzyState -> FuzzyState) -> EditorM ()
modifyE FuzzyState -> FuzzyState
goNext
      , Key -> [Modifier] -> Event
Event Key
KTab [Modifier
MShift] Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! (FuzzyState -> FuzzyState) -> EditorM ()
modifyE FuzzyState -> FuzzyState
goPrevious
      , Key -> [Modifier] -> Event
Event Key
KTab [] Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! (FuzzyState -> FuzzyState) -> EditorM ()
modifyE FuzzyState -> FuzzyState
goNext
      , Char -> Event
ctrlCh Char
'w' Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! BufferM () -> EditorM ()
updatingB (TextUnit -> Direction -> BufferM ()
deleteB TextUnit
unitWord Direction
Backward)
      , Char -> Event
ctrlCh Char
'u' Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! BufferM () -> EditorM ()
updatingB (BufferM ()
moveToSol BufferM () -> BufferM () -> BufferM ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> BufferM ()
deleteToEol)
      , Char -> Event
ctrlCh Char
'k' Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! BufferM () -> EditorM ()
updatingB BufferM ()
deleteToEol
      ]
    Keymap -> KeymapEndo
forall (f :: * -> *) w e a.
MonadInteract f w e =>
f a -> f a -> f a
<|| (Keymap
insertChar Keymap -> EditorM () -> Keymap
forall (m :: * -> *) a x b.
(MonadInteract m Action Event, YiAction a x, Show x) =>
m b -> a -> m ()
>>! (BufferM FuzzyState -> EditorM FuzzyState
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer BufferM FuzzyState
updateNeedleB EditorM FuzzyState -> (FuzzyState -> EditorM ()) -> EditorM ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= FuzzyState -> EditorM ()
renderE))
 where
  updatingB :: BufferM () -> EditorM ()
  updatingB :: BufferM () -> EditorM ()
updatingB BufferM ()
bufAction = BufferM FuzzyState -> EditorM FuzzyState
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer (BufferM ()
bufAction BufferM () -> BufferM FuzzyState -> BufferM FuzzyState
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> BufferM FuzzyState
updateNeedleB) EditorM FuzzyState -> (FuzzyState -> EditorM ()) -> EditorM ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= FuzzyState -> EditorM ()
renderE

updateNeedleB :: BufferM FuzzyState
updateNeedleB :: BufferM FuzzyState
updateNeedleB = do
  Text
s        <- YiString -> Text
R.toText (YiString -> Text) -> BufferM YiString -> BufferM Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BufferM YiString
readLnB
  FuzzyState
oldState <- BufferM FuzzyState
forall (m :: * -> *) a.
(Default a, YiVariable a, MonadState FBuffer m, Functor m) =>
m a
getBufferDyn
  let newState :: FuzzyState
newState = FuzzyState
oldState FuzzyState -> Text -> FuzzyState
`filterState` Text
s
  FuzzyState -> BufferM ()
forall a (m :: * -> *).
(YiVariable a, MonadState FBuffer m, Functor m) =>
a -> m ()
putBufferDyn FuzzyState
newState
  FuzzyState -> BufferM FuzzyState
forall (m :: * -> *) a. Monad m => a -> m a
return FuzzyState
newState
 where
  filterState :: FuzzyState -> Text -> FuzzyState
  filterState :: FuzzyState -> Text -> FuzzyState
filterState FuzzyState
old Text
s = FuzzyState
old { search :: Text
search = Text
s, items :: Maybe (PointedList FuzzyItem)
items = Maybe (PointedList FuzzyItem)
newItems }
   where
    newItems :: Maybe (PointedList FuzzyItem)
    newItems :: Maybe (PointedList FuzzyItem)
newItems = do
      PointedList FuzzyItem
o <- FuzzyState -> Maybe (PointedList FuzzyItem)
items FuzzyState
old
      PointedList FuzzyItem
f <- Text -> PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
filterItems Text
s PointedList FuzzyItem
o
      Int -> PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
forall a. Int -> PointedList a -> Maybe (PointedList a)
PL.moveTo Int
0 PointedList FuzzyItem
f


filterItems :: Text -> PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
filterItems :: Text -> PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
filterItems Text
s PointedList FuzzyItem
zipper = (FuzzyItem -> Bool)
-> PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
forall a. (a -> Bool) -> PointedList a -> Maybe (PointedList a)
PL.filterr (Text -> Text -> Bool
subsequenceTextMatch Text
s (Text -> Bool) -> (FuzzyItem -> Text) -> FuzzyItem -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FuzzyItem -> Text
itemAsTxt) PointedList FuzzyItem
zipper

modifyE :: (FuzzyState -> FuzzyState) -> EditorM ()
modifyE :: (FuzzyState -> FuzzyState) -> EditorM ()
modifyE FuzzyState -> FuzzyState
f = do
  FuzzyState
prevState <- BufferM FuzzyState -> EditorM FuzzyState
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer BufferM FuzzyState
forall (m :: * -> *) a.
(Default a, YiVariable a, MonadState FBuffer m, Functor m) =>
m a
getBufferDyn
  let newState :: FuzzyState
newState = FuzzyState -> FuzzyState
f FuzzyState
prevState
  BufferM () -> EditorM ()
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer (FuzzyState -> BufferM ()
forall a (m :: * -> *).
(YiVariable a, MonadState FBuffer m, Functor m) =>
a -> m ()
putBufferDyn FuzzyState
newState)
  FuzzyState -> EditorM ()
renderE FuzzyState
newState

goNext :: FuzzyState -> FuzzyState
goNext :: FuzzyState -> FuzzyState
goNext = (PointedList FuzzyItem -> Maybe (PointedList FuzzyItem))
-> FuzzyState -> FuzzyState
changeIndex PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
forall a. PointedList a -> Maybe (PointedList a)
PL.next

goPrevious :: FuzzyState -> FuzzyState
goPrevious :: FuzzyState -> FuzzyState
goPrevious = (PointedList FuzzyItem -> Maybe (PointedList FuzzyItem))
-> FuzzyState -> FuzzyState
changeIndex PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
forall a. PointedList a -> Maybe (PointedList a)
PL.previous

changeIndex :: (PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)) -> FuzzyState -> FuzzyState
changeIndex :: (PointedList FuzzyItem -> Maybe (PointedList FuzzyItem))
-> FuzzyState -> FuzzyState
changeIndex PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
dir FuzzyState
fs = FuzzyState
fs { items :: Maybe (PointedList FuzzyItem)
items = FuzzyState -> Maybe (PointedList FuzzyItem)
items FuzzyState
fs Maybe (PointedList FuzzyItem)
-> (PointedList FuzzyItem -> Maybe (PointedList FuzzyItem))
-> Maybe (PointedList FuzzyItem)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
dir }

renderE :: FuzzyState -> EditorM ()
renderE :: FuzzyState -> EditorM ()
renderE (FuzzyState Maybe (PointedList FuzzyItem)
maybeZipper Text
s) =
  case Maybe (NonEmpty Text)
mcontent of
    Maybe (NonEmpty Text)
Nothing      -> Text -> EditorM ()
forall (m :: * -> *). MonadEditor m => Text -> m ()
printMsg Text
"No match found"
    Just NonEmpty Text
content -> Status -> EditorM ()
forall (m :: * -> *). MonadEditor m => Status -> m ()
setStatus (NonEmpty Text -> [Text]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty Text
content, StyleName
defaultStyle)
 where
  tshow :: Show s => s -> Text
  tshow :: s -> Text
tshow = String -> Text
T.pack (String -> Text) -> (s -> String) -> s -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. s -> String
forall a. Show a => a -> String
show

  mcontent :: Maybe (NonEmpty Text)
  mcontent :: Maybe (NonEmpty Text)
mcontent = do
    PointedList FuzzyItem
zipper  <- Maybe (PointedList FuzzyItem)
maybeZipper
    PointedList (FuzzyItem, Bool)
zipper' <- PointedList FuzzyItem -> PointedList (FuzzyItem, Bool)
forall a. PointedList a -> PointedList (a, Bool)
PL.withFocus (PointedList FuzzyItem -> PointedList (FuzzyItem, Bool))
-> Maybe (PointedList FuzzyItem)
-> Maybe (PointedList (FuzzyItem, Bool))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> PointedList FuzzyItem -> Maybe (PointedList FuzzyItem)
filterItems Text
s PointedList FuzzyItem
zipper
    [Text] -> Maybe (NonEmpty Text)
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty ([Text] -> Maybe (NonEmpty Text))
-> (PointedList Text -> [Text])
-> PointedList Text
-> Maybe (NonEmpty Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PointedList Text -> [Text]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (PointedList Text -> Maybe (NonEmpty Text))
-> PointedList Text -> Maybe (NonEmpty Text)
forall a b. (a -> b) -> a -> b
$ ((FuzzyItem, Bool) -> Text)
-> PointedList (FuzzyItem, Bool) -> PointedList Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((FuzzyItem -> Bool -> Text) -> (FuzzyItem, Bool) -> Text
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ((FuzzyItem -> Bool -> Text) -> (FuzzyItem, Bool) -> Text)
-> (FuzzyItem -> Bool -> Text) -> (FuzzyItem, Bool) -> Text
forall a b. (a -> b) -> a -> b
$ (Bool -> FuzzyItem -> Text) -> FuzzyItem -> Bool -> Text
forall a b c. (a -> b -> c) -> b -> a -> c
flip Bool -> FuzzyItem -> Text
renderItem) PointedList (FuzzyItem, Bool)
zipper'

  -- TODO justify to actual screen width
  renderItem :: Bool -> FuzzyItem -> Text
  renderItem :: Bool -> FuzzyItem -> Text
renderItem Bool
isFocus FuzzyItem
fi = Bool -> Text -> Text
renderStar Bool
isFocus (Int -> Char -> Text -> Text
T.justifyLeft Int
79 Char
' ' (Text -> Text) -> (FuzzyItem -> Text) -> FuzzyItem -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> (FuzzyItem -> String) -> FuzzyItem -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FuzzyItem -> String
forall a. Show a => a -> String
show (FuzzyItem -> Text) -> FuzzyItem -> Text
forall a b. (a -> b) -> a -> b
$ FuzzyItem
fi)

  renderStar :: Bool -> (Text -> Text)
  renderStar :: Bool -> Text -> Text
renderStar Bool
y = if Bool
y then (Text
"* "Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>) else (Text
"  "Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>)

openInThisWindow :: YiM ()
openInThisWindow :: YiM ()
openInThisWindow = EditorM () -> YiM ()
openRoutine (() -> EditorM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ())

openInSplit :: YiM ()
openInSplit :: YiM ()
openInSplit = EditorM () -> YiM ()
openRoutine EditorM ()
splitE

openInNewTab :: YiM ()
openInNewTab :: YiM ()
openInNewTab = EditorM () -> YiM ()
openRoutine EditorM ()
newTabE

openRoutine :: EditorM () -> YiM ()
openRoutine :: EditorM () -> YiM ()
openRoutine EditorM ()
preOpenAction = do
  Maybe (PointedList FuzzyItem)
mzipper <- FuzzyState -> Maybe (PointedList FuzzyItem)
items (FuzzyState -> Maybe (PointedList FuzzyItem))
-> YiM FuzzyState -> YiM (Maybe (PointedList FuzzyItem))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BufferM FuzzyState -> YiM FuzzyState
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer BufferM FuzzyState
forall (m :: * -> *) a.
(Default a, YiVariable a, MonadState FBuffer m, Functor m) =>
m a
getBufferDyn
  case Maybe (PointedList FuzzyItem)
mzipper of
    Maybe (PointedList FuzzyItem)
Nothing                  -> Text -> YiM ()
forall (m :: * -> *). MonadEditor m => Text -> m ()
printMsg Text
"Nothing selected"
    Just (PointedList [FuzzyItem]
_ FuzzyItem
f [FuzzyItem]
_) -> do
      EditorM () -> YiM ()
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor (EditorM () -> YiM ()) -> EditorM () -> YiM ()
forall a b. (a -> b) -> a -> b
$ do
        EditorM ()
cleanupE
        EditorM ()
preOpenAction
      FuzzyItem -> YiM ()
action FuzzyItem
f
 where
  action :: FuzzyItem -> YiM ()
  action :: FuzzyItem -> YiM ()
action (FileItem   Text
x) = YiM (Either Text BufferRef) -> YiM ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (String -> YiM (Either Text BufferRef)
editFile (Text -> String
T.unpack Text
x))
  action (BufferItem BufferId
x) = EditorM () -> YiM ()
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor (EditorM () -> YiM ()) -> EditorM () -> YiM ()
forall a b. (a -> b) -> a -> b
$ do
    [(BufferRef, FBuffer)]
bufs <- (Editor -> [(BufferRef, FBuffer)])
-> EditorM [(BufferRef, FBuffer)]
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets (Map BufferRef FBuffer -> [(BufferRef, FBuffer)]
forall k a. Map k a -> [(k, a)]
M.assocs (Map BufferRef FBuffer -> [(BufferRef, FBuffer)])
-> (Editor -> Map BufferRef FBuffer)
-> Editor
-> [(BufferRef, FBuffer)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Editor -> Map BufferRef FBuffer
buffers)
    case ((BufferRef, FBuffer) -> Bool)
-> [(BufferRef, FBuffer)] -> [(BufferRef, FBuffer)]
forall a. (a -> Bool) -> [a] -> [a]
filter ((BufferId -> BufferId -> Bool
forall a. Eq a => a -> a -> Bool
==BufferId
x) (BufferId -> Bool)
-> ((BufferRef, FBuffer) -> BufferId)
-> (BufferRef, FBuffer)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Attributes -> BufferId
ident (Attributes -> BufferId)
-> ((BufferRef, FBuffer) -> Attributes)
-> (BufferRef, FBuffer)
-> BufferId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FBuffer -> Attributes
attributes (FBuffer -> Attributes)
-> ((BufferRef, FBuffer) -> FBuffer)
-> (BufferRef, FBuffer)
-> Attributes
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (BufferRef, FBuffer) -> FBuffer
forall a b. (a, b) -> b
snd) [(BufferRef, FBuffer)]
bufs of
      []            -> String -> EditorM ()
forall a. HasCallStack => String -> a
error (String
"Couldn't find " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> BufferId -> String
forall a. Show a => a -> String
show BufferId
x)
      (BufferRef
bufRef, FBuffer
_):[(BufferRef, FBuffer)]
_ -> BufferRef -> EditorM ()
switchToBufferE BufferRef
bufRef


insertChar :: Keymap
insertChar :: Keymap
insertChar = KeymapM Char
textChar KeymapM Char -> (Char -> Keymap) -> Keymap
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= BufferM () -> Keymap
forall (m :: * -> *) ev a x.
(MonadInteract m Action ev, YiAction a x, Show x) =>
a -> m ()
write (BufferM () -> Keymap) -> (Char -> BufferM ()) -> Char -> Keymap
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> BufferM ()
insertB

cleanupE :: EditorM ()
cleanupE :: EditorM ()
cleanupE = EditorM ()
clrStatus EditorM () -> EditorM () -> EditorM ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> EditorM ()
closeBufferAndWindowE

instance Binary FuzzyItem where
  put :: FuzzyItem -> Put
put (FileItem Text
x) = Word8 -> Put
forall t. Binary t => t -> Put
put (Word8
0 :: Word8) Put -> Put -> Put
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Text -> Put
forall t. Binary t => t -> Put
put Text
x
  put (BufferItem BufferId
x) = Word8 -> Put
forall t. Binary t => t -> Put
put (Word8
1 :: Word8) Put -> Put -> Put
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> BufferId -> Put
forall t. Binary t => t -> Put
put BufferId
x
  get :: Get FuzzyItem
get = do
    Word8
tag :: Word8 <- Get Word8
forall t. Binary t => Get t
get
    case Word8
tag of
      Word8
0 -> Text -> FuzzyItem
FileItem (Text -> FuzzyItem) -> Get Text -> Get FuzzyItem
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Text
forall t. Binary t => Get t
get
      Word8
1 -> BufferId -> FuzzyItem
BufferItem (BufferId -> FuzzyItem) -> Get BufferId -> Get FuzzyItem
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get BufferId
forall t. Binary t => Get t
get
      Word8
_ -> String -> Get FuzzyItem
forall a. HasCallStack => String -> a
error String
"Unexpected FuzzyItem Binary."

instance Binary FuzzyState where
  put :: FuzzyState -> Put
put (FuzzyState Maybe (PointedList FuzzyItem)
mzipper Text
s) = do
    Maybe (PointedList FuzzyItem) -> Put
forall t. Binary t => t -> Put
put Maybe (PointedList FuzzyItem)
mzipper
    ByteString -> Put
forall t. Binary t => t -> Put
put (Text -> ByteString
T.encodeUtf8 Text
s)

  get :: Get FuzzyState
get = Maybe (PointedList FuzzyItem) -> Text -> FuzzyState
FuzzyState (Maybe (PointedList FuzzyItem) -> Text -> FuzzyState)
-> Get (Maybe (PointedList FuzzyItem)) -> Get (Text -> FuzzyState)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get (Maybe (PointedList FuzzyItem))
forall t. Binary t => Get t
get Get (Text -> FuzzyState) -> Get Text -> Get FuzzyState
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (ByteString -> Text) -> Get ByteString -> Get Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
T.decodeUtf8 Get ByteString
forall t. Binary t => Get t
get

instance Default FuzzyState where
  def :: FuzzyState
def = Maybe (PointedList FuzzyItem) -> Text -> FuzzyState
FuzzyState Maybe (PointedList FuzzyItem)
forall a. Maybe a
Nothing Text
forall a. Monoid a => a
mempty

instance YiVariable FuzzyState