{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ExistentialQuantification #-}

-- | This module is a quick hack to enable representation of data with columns of text. We use the fact the since the candidates are supposed to fit in a line,
-- they can't have a newlines but text with newlines can otherwise be searched normally. We use this here to separate columns by newlines. Like in
-- "Talash.Brick" the candidates comes from vector of text. Each such text consists of a fixed number of lines each representing a column. We match against such
-- text and `partsColumns` then uses the newlines to reconstruct the columns and the parts of the match within each column. This trick of using newline saves us
-- from dealing with the partial state of the match when we cross a column but there is probably a better way . The function `runApp` , `selected` and
-- `selectedIndex` hide this and instead take as argument a `Vector` [`Text`] with each element of the list representing a column. Each list must have the same
-- length. Otherwise this module provides a reduced version of the functions in "Talash.Brick".

-- This module hasn't been tested on large data and will likely be slow.
module Talash.Brick.Columns (-- -- * Types
                      Searcher (..) , SearchEvent (..) , SearchEnv (..) ,  EventHooks (..) , AppTheme (..)
                    , AppSettings (..) , AppSettingsG (..) , SearchFunctions , CaseSensitivity (..)
                     -- * The Brick App and Helpers
                    , searchApp , defSettings , selected , selectedIndex , runApp
                     -- * Lenses
                     -- ** Searcher
                    , query , prevQuery , allMatches , matches , numMatches
                     -- ** SearchEvent
                    , matchedTop , totalMatches , term
                     -- ** SearchEnv
                    , searchFunctions , candidates , eventSource
                     -- ** AppTheme
                    , prompt , themeAttrs , borderStyle
                     -- ** SearchSettings
                    , theme , hooks
                     -- * Exposed Internals
                    , handleKeyEvent , handleSearch , searcherWidget , initialSearcher , partsColumns , runApp' , selected' , selectedIndex') where

import qualified Data.Text as T
import Data.Vector (elemIndex)
import GHC.Compact (Compact , compact , getCompact)
import Talash.Brick.Internal
import Talash.Intro hiding (on ,replicate , take)

data AppTheme = AppTheme { AppTheme -> Text
_prompt :: Text -- ^ The prompt to display next to the editor.
                         , AppTheme -> [AttrName]
_columnAttrs :: [AttrName] -- ^ The attrNames to use for each column. Must have the same length or greater length than the number of columns.
                         , AppTheme -> [Int]
_columnLimits :: [Int] -- ^ The area to limit each column to. This has a really naive and unituitive implementation. Each Int
                                                  -- must be between 0 and 100 and refers to the percentage of the width the widget for a column will occupy
                                                  -- from the space left over after all the columns before it have been rendered.
                         , AppTheme -> [(AttrName, Attr)]
_themeAttrs :: [(AttrName, Attr)]  -- ^ This is used to construct the `attrMap` for the app. By default the used attarNmaes are
                                                              --  `listSelectedAttr` , `borderAttr` , @"Prompt"@ , @"Highlight"@ and @"Stats"@
                         , AppTheme -> BorderStyle
_borderStyle :: BorderStyle -- ^ The border style to use. By default `unicodeRounded`
                         }
makeLenses ''AppTheme

type AppSettings n a = AppSettingsG n a Text AppTheme

data ColumnsIso a = ColumnsIso { forall a. ColumnsIso a -> a -> [Text]
_toColumns :: a -> [Text] , forall a. ColumnsIso a -> [Text] -> a
_fromColumns :: [Text] -> a}
makeLenses ''ColumnsIso

-- | The brick widget used to display the editor and the search result.
searcherWidget :: (KnownNat n , KnownNat m) => AppTheme -> SearchEnv n a Text -> SearcherSized m a -> Widget Bool
searcherWidget :: forall (n :: Nat) (m :: Nat) a.
(KnownNat n, KnownNat m) =>
AppTheme -> SearchEnv n a Text -> SearcherSized m a -> Widget Bool
searcherWidget AppTheme
t SearchEnv n a Text
env SearcherSized m a
s = forall n. Widget n -> Widget n
joinBorders forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall n. Widget n -> Widget n
border forall a b. (a -> b) -> a -> b
$    forall n.
(Ord n, Show n) =>
Bool -> Text -> Editor Text n -> Widget n -> Widget n
searchWidgetAux Bool
True (AppTheme
t forall s a. s -> Getting a s a -> a
^. Lens' AppTheme Text
prompt) (SearcherSized m a
s forall s a. s -> Getting a s a -> a
^. forall (n :: Nat) a. Lens' (SearcherSized n a) (Editor Text Bool)
queryEditor) (forall n. AttrName -> Widget n -> Widget n
withAttr (String -> AttrName
attrName String
"Stats") forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall n. Text -> Widget n
txt forall a b. (a -> b) -> a -> b
$ (String -> Text
T.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show forall a b. (a -> b) -> a -> b
$ SearcherSized m a
s forall s a. s -> Getting a s a -> a
^. forall (n :: Nat) a. Lens' (SearcherSized n a) Int
numMatches))
                                        forall n. Widget n -> Widget n -> Widget n
<=> forall n. Widget n
hBorder  forall n. Widget n -> Widget n -> Widget n
<=> forall n. Widget n -> Widget n
joinBorders (forall n (m :: Nat) (l :: Nat) a.
(Ord n, Show n, KnownNat m, KnownNat l) =>
SearchEnv l a Text
-> Text
-> [AttrName]
-> [Int]
-> MatcherSized m a
-> Bool
-> GenericList n MatchSetG (ScoredMatchSized m)
-> Widget n
makeColumns SearchEnv n a Text
env Text
"➜ " (AppTheme
t forall s a. s -> Getting a s a -> a
^. Lens' AppTheme [AttrName]
columnAttrs) (AppTheme
t forall s a. s -> Getting a s a -> a
^. Lens' AppTheme [Int]
columnLimits) (SearcherSized m a
s forall s a. s -> Getting a s a -> a
^. forall (n :: Nat) a. Lens' (SearcherSized n a) (MatcherSized n a)
matcher) Bool
False (SearcherSized m a
s forall s a. s -> Getting a s a -> a
^. forall (n :: Nat) a.
Lens'
  (SearcherSized n a)
  (GenericList Bool MatchSetG (ScoredMatchSized n))
matches))

defThemeAttrs :: [(AttrName, Attr)]
defThemeAttrs :: [(AttrName, Attr)]
defThemeAttrs = [ (AttrName
listSelectedAttr, Attr -> Style -> Attr
withStyle (Color -> Attr
bg Color
white) Style
bold) , (String -> AttrName
attrName String
"Prompt" , Attr -> Style -> Attr
withStyle (Color
white Color -> Color -> Attr
`on` Color
blue) Style
bold)
           , (String -> AttrName
attrName String
"Highlight" , Attr -> Style -> Attr
withStyle (Color -> Attr
fg Color
blue) Style
bold) ,  (String -> AttrName
attrName String
"Stats" , Color -> Attr
fg Color
blue) ,  (AttrName
borderAttr , Color -> Attr
fg Color
cyan)]

defTheme ::AppTheme
defTheme :: AppTheme
defTheme = AppTheme {_prompt :: Text
_prompt = Text
"Find: " , _columnAttrs :: [AttrName]
_columnAttrs = forall a. a -> [a]
repeat forall a. Monoid a => a
mempty , _columnLimits :: [Int]
_columnLimits = forall a. a -> [a]
repeat Int
50 , _themeAttrs :: [(AttrName, Attr)]
_themeAttrs = [(AttrName, Attr)]
defThemeAttrs
                    , _borderStyle :: BorderStyle
_borderStyle = BorderStyle
unicodeRounded}

-- | Default settings. Uses blue for various highlights and cyan for borders. All the hooks except keyHook which is `handleKeyEvent` are trivial.
{-# INLINE defSettings#-}
defSettings :: KnownNat n => AppSettings n a
defSettings :: forall (n :: Nat) a. KnownNat n => AppSettings n a
defSettings = forall (n :: Nat) a b t.
t
-> ReaderT (SearchEnv n a b) EventHooks (Searcher a)
-> Proxy n
-> Int
-> (SearchReport -> Bool)
-> AppSettingsG n a b t
AppSettings AppTheme
defTheme (forall r (m :: * -> *) a. (r -> m a) -> ReaderT r m a
ReaderT (\SearchEnv n a Text
e -> forall a. EventHooks a
defHooks {keyHook :: Key -> [Modifier] -> EventM Bool (Searcher a) ()
keyHook = forall (n :: Nat) a c b.
KnownNat n =>
SearchEnv n a c -> Key -> [Modifier] -> EventM Bool (Searcher b) ()
handleKeyEvent SearchEnv n a Text
e})) forall {k} (t :: k). Proxy t
Proxy Int
1024 (\SearchReport
r -> SearchReport
r forall s a. s -> Getting a s a -> a
^. Lens' SearchReport Ocassion
ocassion forall a. Eq a => a -> a -> Bool
== Ocassion
QueryDone)

-- | Tha app itself.  `selected` and the related functions are probably more convenient for embedding into a larger program.
searchApp :: KnownNat n => AppSettings n a -> SearchEnv n a Text -> App (Searcher a) (SearchEvent a) Bool
searchApp :: forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchEnv n a Text -> App (Searcher a) (SearchEvent a) Bool
searchApp (AppSettings AppTheme
th ReaderT (SearchEnv n a Text) EventHooks (Searcher a)
hks Proxy n
_ Int
_ SearchReport -> Bool
_) SearchEnv n a Text
env  = App {appDraw :: Searcher a -> [Widget Bool]
appDraw = Searcher a -> [Widget Bool]
ad , appChooseCursor :: Searcher a -> [CursorLocation Bool] -> Maybe (CursorLocation Bool)
appChooseCursor = forall s n. s -> [CursorLocation n] -> Maybe (CursorLocation n)
showFirstCursor , appHandleEvent :: BrickEvent Bool (SearchEvent a) -> EventM Bool (Searcher a) ()
appHandleEvent = forall {n}.
BrickEvent n (SearchEvent a) -> EventM Bool (Searcher a) ()
he , appStartEvent :: EventM Bool (Searcher a) ()
appStartEvent = EventM Bool (Searcher a) ()
as , appAttrMap :: Searcher a -> AttrMap
appAttrMap = forall {b}. b -> AttrMap
am}
  where
    ad :: Searcher a -> [Widget Bool]
ad (Searcher SearcherSized n a
s)                                  = (forall a. a -> [a] -> [a]
:[]) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall n. BorderStyle -> Widget n -> Widget n
withBorderStyle (AppTheme
th forall s a. s -> Getting a s a -> a
^. Lens' AppTheme BorderStyle
borderStyle) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat) (m :: Nat) a.
(KnownNat n, KnownNat m) =>
AppTheme -> SearchEnv n a Text -> SearcherSized m a -> Widget Bool
searcherWidget AppTheme
th SearchEnv n a Text
env forall a b. (a -> b) -> a -> b
$ SearcherSized n a
s
    as :: EventM Bool (Searcher a) ()
as                                               = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall (n :: Nat) a b.
KnownNat n =>
SearchEnv n a b -> Text -> IO ()
sendQuery SearchEnv n a Text
env Text
"")
    am :: b -> AttrMap
am                                               = forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ Attr -> [(AttrName, Attr)] -> AttrMap
attrMap Attr
defAttr (AppTheme
th forall s a. s -> Getting a s a -> a
^. Lens' AppTheme [(AttrName, Attr)]
themeAttrs)
    hk :: EventHooks (Searcher a)
hk                                               = forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT ReaderT (SearchEnv n a Text) EventHooks (Searcher a)
hks SearchEnv n a Text
env
    he :: BrickEvent n (SearchEvent a) -> EventM Bool (Searcher a) ()
he (VtyEvent (EvKey Key
k [Modifier]
m))                      = forall a. EventHooks a -> Key -> [Modifier] -> EventM Bool a ()
keyHook EventHooks (Searcher a)
hk Key
k [Modifier]
m
    he (VtyEvent (EvMouseDown Int
i Int
j Button
b [Modifier]
m))            = forall a.
EventHooks a
-> Int -> Int -> Button -> [Modifier] -> EventM Bool a ()
mouseDownHook   EventHooks (Searcher a)
hk Int
i Int
j Button
b [Modifier]
m
    he (VtyEvent (EvMouseUp   Int
i Int
j Maybe Button
b  ))            = forall a.
EventHooks a -> Int -> Int -> Maybe Button -> EventM Bool a ()
mouseUpHook     EventHooks (Searcher a)
hk Int
i Int
j Maybe Button
b
    he (VtyEvent (EvPaste     ByteString
b      ))            = forall a. EventHooks a -> ByteString -> EventM Bool a ()
pasteHook       EventHooks (Searcher a)
hk     ByteString
b
    he (VtyEvent  Event
EvGainedFocus       )            = forall a. EventHooks a -> EventM Bool a ()
focusGainedHook EventHooks (Searcher a)
hk
    he (VtyEvent  Event
EvLostFocus         )            = forall a. EventHooks a -> EventM Bool a ()
focusLostHook   EventHooks (Searcher a)
hk
    he (AppEvent SearchEvent a
e)                                = forall a. SearchEvent a -> EventM Bool (Searcher a) ()
handleSearch SearchEvent a
e
    he BrickEvent n (SearchEvent a)
_                                           = forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

-- | This function reconstructs the columns from the parts returned by the search by finding the newlines.
partsColumns :: [Text] -> [[Text]]
partsColumns :: [Text] -> [[Text]]
partsColumns = forall a. [a] -> [a] -> [a]
initDef [] forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b a. (b -> Maybe (a, b)) -> b -> [a]
unfoldr (\[Text]
l -> if forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Text]
l then forall a. Maybe a
Nothing else forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Text] -> ([Text], [Text])
go forall a b. (a -> b) -> a -> b
$ [Text]
l)
  where
    go :: [Text] -> ([Text], [Text])
go [Text]
x = forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap ([Text]
f forall a. Semigroup a => a -> a -> a
<>) (forall b a. b -> (a -> b) -> Maybe a -> b
maybe [Text]
s' (forall a. a -> [a] -> [a]
: [Text]
s')) ([Text], Maybe Text)
hs
      where
        ([Text]
f , [Text]
s) = forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Text -> Text -> Bool
T.isInfixOf Text
"\n") [Text]
x
        s' :: [Text]
s'      = forall a. [a] -> [a] -> [a]
tailDef [] [Text]
s
        hs :: ([Text], Maybe Text)
hs      = forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([] , forall a. Maybe a
Nothing) (forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap (forall a. a -> [a] -> [a]
:[]) (Text -> Text -> Maybe Text
T.stripPrefix Text
"\n") forall b c a. (b -> c) -> (a -> b) -> a -> c
. HasCallStack => Text -> Text -> (Text, Text)
T.breakOn Text
"\n") forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> Maybe a
headMay forall a b. (a -> b) -> a -> b
$ [Text]
s

makeColumns :: (Ord n , Show n , KnownNat m , KnownNat l) => SearchEnv l a Text -> Text -> [AttrName] -> [Int]
                                                         -> MatcherSized m a -> Bool -> GenericList n MatchSetG (ScoredMatchSized m) -> Widget n
makeColumns :: forall n (m :: Nat) (l :: Nat) a.
(Ord n, Show n, KnownNat m, KnownNat l) =>
SearchEnv l a Text
-> Text
-> [AttrName]
-> [Int]
-> MatcherSized m a
-> Bool
-> GenericList n MatchSetG (ScoredMatchSized m)
-> Widget n
makeColumns SearchEnv l a Text
env Text
c [AttrName]
as [Int]
ls MatcherSized m a
m = forall (t :: * -> *) n e.
(Foldable t, Splittable t, Ord n, Show n) =>
(Bool -> e -> Widget n) -> Bool -> GenericList n t e -> Widget n
renderList (forall a n.
Text
-> (a -> [[Text]]) -> [AttrName] -> [Int] -> Bool -> a -> Widget n
columnsWithHighlights Text
c ScoredMatchSized m -> [[Text]]
mp [AttrName]
as [Int]
ls)
  where
    mp :: ScoredMatchSized m -> [[Text]]
mp ScoredMatchSized m
s = [Text] -> [[Text]]
partsColumns forall a b. (a -> b) -> a -> b
$ (SearchEnv l a Text
env forall s a. s -> Getting a s a -> a
^. forall (n :: Nat) a b1 b2.
Lens
  (SearchEnv n a b1)
  (SearchEnv n a b2)
  (SearchFunctions a b1)
  (SearchFunctions a b2)
searchFunctions forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b (n :: Nat).
KnownNat n =>
SimpleGetter
  (SearchFunctions a b)
  ((Bool -> Text -> b)
   -> MatcherSized n a -> Text -> Vector n Int -> [b])
display) (forall a b. a -> b -> a
const forall a. a -> a
id) MatcherSized m a
m ((SearchEnv l a Text
env forall s a. s -> Getting a s a -> a
^. forall (n :: Nat) a b. Lens' (SearchEnv n a b) (Chunks n)
candidates) forall (n :: Nat). KnownNat n => Chunks n -> ChunkIndex -> Text
! forall (n :: Nat). ScoredMatchSized n -> ChunkIndex
chunkIndex ScoredMatchSized m
s) (forall (n :: Nat). ScoredMatchSized n -> Vector n Int
matchData ScoredMatchSized m
s)

-- | The \'raw\' version of `runApp` taking a vector of text with columns separated by newlines.
runApp' :: KnownNat n => AppSettings n a -> SearchFunctions a Text -> Chunks n -> IO (Searcher a)
runApp' :: forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Chunks n -> IO (Searcher a)
runApp' AppSettings n a
s SearchFunctions a Text
f Chunks n
c =     (\BChan (SearchEvent a)
b -> (\SearchEnv n a Text
env -> forall (n :: Nat) a b. KnownNat n => SearchEnv n a b -> IO ()
startSearcher SearchEnv n a Text
env forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> forall a b. IO a -> IO b -> IO a
finally (forall n b e. Ord n => App b e n -> BChan e -> b -> IO b
theMain (forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchEnv n a Text -> App (Searcher a) (SearchEvent a) Bool
searchApp AppSettings n a
s SearchEnv n a Text
env) BChan (SearchEvent a)
b forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a (n :: Nat). KnownNat n => SearcherSized n a -> Searcher a
Searcher forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat) a c.
SearchEnv n a c -> BChan (SearchEvent a) -> SearcherSized 0 a
initialSearcher SearchEnv n a Text
env forall a b. (a -> b) -> a -> b
$ BChan (SearchEvent a)
b) (forall (n :: Nat) a b. KnownNat n => SearchEnv n a b -> IO ()
stopSearcher SearchEnv n a Text
env))
                 forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (n :: Nat) a b.
KnownNat n =>
SearchFunctions a b
-> Int
-> (forall (n1 :: Nat) (m :: Nat).
    (KnownNat n1, KnownNat m) =>
    Chunks n1
    -> SearchReport -> MatcherSized m a -> MatchSetSized m -> IO ())
-> Chunks n
-> IO (SearchEnv n a b)
searchEnv SearchFunctions a Text
f (AppSettings n a
s forall s a. s -> Getting a s a -> a
^. forall (n :: Nat) a b t. Lens' (AppSettingsG n a b t) Int
maximumMatches) (forall (m :: Nat) a b c.
KnownNat m =>
(SearchReport -> Bool)
-> BChan (SearchEvent a)
-> c
-> SearchReport
-> MatcherSized m a
-> MatchSetSized m
-> IO ()
generateSearchEvent (AppSettings n a
s forall s a. s -> Getting a s a -> a
^. forall (n :: Nat) a b t.
Lens' (AppSettingsG n a b t) (SearchReport -> Bool)
eventStrategy) BChan (SearchEvent a)
b) Chunks n
c) forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall a. Int -> IO (BChan a)
newBChan Int
8

-- -- | Run app with given settings and return the final Searcher state.
runApp :: KnownNat n => AppSettings n a  -> SearchFunctions a  Text -> Vector [Text] -> IO (Searcher a)
runApp :: forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Vector [Text] -> IO (Searcher a)
runApp AppSettings n a
s SearchFunctions a Text
f = forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Chunks n -> IO (Searcher a)
runApp' AppSettings n a
s SearchFunctions a Text
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat). KnownNat n => Vector Text -> Chunks n
makeChunks forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map [Text] -> Text
T.unlines

-- | The \'raw\' version of `selected` taking a vector of text with columns separated by newlines.
selected' :: KnownNat n => AppSettings n a -> SearchFunctions a Text -> Chunks n -> IO (Maybe [Text])
selected' :: forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Chunks n -> IO (Maybe [Text])
selected' AppSettings n a
s SearchFunctions a Text
f = (\Chunks n
c -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map Text -> [Text]
T.lines forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat) a.
KnownNat n =>
Chunks n -> Searcher a -> Maybe Text
selectedElement Chunks n
c) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Chunks n -> IO (Searcher a)
runApp' AppSettings n a
s SearchFunctions a Text
f forall a b. (a -> b) -> a -> b
$ Chunks n
c) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Compact a -> a
getCompact forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< forall a. a -> IO (Compact a)
compact forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat). KnownNat n => Chunks n -> Chunks n
forceChunks

-- | Run app and return the the selection if there is one else Nothing.
selected :: KnownNat n => AppSettings n a -> SearchFunctions a Text -> Vector [Text] -> IO (Maybe [Text])
selected :: forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Vector [Text] -> IO (Maybe [Text])
selected AppSettings n a
s SearchFunctions a Text
f  = forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Chunks n -> IO (Maybe [Text])
selected' AppSettings n a
s SearchFunctions a Text
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat). KnownNat n => Vector Text -> Chunks n
makeChunks forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map [Text] -> Text
T.unlines

selectedIso :: KnownNat n => ColumnsIso a -> AppSettings n b -> SearchFunctions b Text -> Vector a -> IO (Maybe a)
selectedIso :: forall (n :: Nat) a b.
KnownNat n =>
ColumnsIso a
-> AppSettings n b
-> SearchFunctions b Text
-> Vector a
-> IO (Maybe a)
selectedIso (ColumnsIso a -> [Text]
from [Text] -> a
to) AppSettings n b
s SearchFunctions b Text
f = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map [Text] -> a
to) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Chunks n -> IO (Maybe [Text])
selected' AppSettings n b
s SearchFunctions b Text
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat). KnownNat n => Vector Text -> Chunks n
makeChunks forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map ([Text] -> Text
T.unlines forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> [Text]
from)

-- | The \'raw\' version of `selectedIndex` taking a vector of text with columns separated by newlines.
selectedIndex' :: KnownNat n => AppSettings n a -> SearchFunctions a Text -> Vector Text -> IO (Maybe Int)
selectedIndex' :: forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Vector Text -> IO (Maybe Int)
selectedIndex' AppSettings n a
s SearchFunctions a Text
f Vector Text
v = ((forall a. Eq a => a -> Vector a -> Maybe Int
`elemIndex` Vector Text
v) forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Text] -> Text
T.unlines forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<<) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Chunks n -> IO (Maybe [Text])
selected' AppSettings n a
s SearchFunctions a Text
f (forall (n :: Nat). KnownNat n => Vector Text -> Chunks n
makeChunks Vector Text
v)

-- | Returns the index of selected candidate in the vector of candidates. Note: it uses `elemIndex` which is O\(N\).
selectedIndex :: KnownNat n => AppSettings n a -> SearchFunctions a Text -> Vector [Text] -> IO (Maybe Int)
selectedIndex :: forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Vector [Text] -> IO (Maybe Int)
selectedIndex AppSettings n a
s SearchFunctions a Text
f = forall (n :: Nat) a.
KnownNat n =>
AppSettings n a
-> SearchFunctions a Text -> Vector Text -> IO (Maybe Int)
selectedIndex' AppSettings n a
s SearchFunctions a Text
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map [Text] -> Text
T.unlines