<commit_before>{-# LANGUAGE FlexibleContexts, FlexibleInstances, RankNTypes, TupleSections #-}
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
module Vimus (
  Vimus
, ProgramState (..)
, Action (..)
, Command (..)
, View (..)
, Event (..)
, Tab
, tabFromList
, sendEvent
, Handler

, Widget (..)
, WidgetCommand
, WidgetAction
, widgetCommand
, SearchPredicate (..)
, SearchOrder (..)

-- * changing the current view
, nextView
, previousView
, getCurrentView
, setCurrentView

, withCurrentSong
, withCurrentItem
, modifyTabs
, modifyCurrentTab
, withTabs
, withCurrentTab
, withAllWidgets
, withCurrentWidget
, setCurrentWidget
, modifyCurrentWidget
, renderMainWindow
, renderToMainWindow
, addMacro
, setLibraryPath
) where

import Control.Monad.State (liftIO, gets, get, put, modify, StateT, MonadState)
import Control.Monad.Trans (MonadIO)
import Control.Monad

import Data.Default
import Data.Ord (comparing)
import Data.Function (on)

import Network.MPD.Core
import Network.MPD as MPD (LsResult)
import qualified Network.MPD as MPD hiding (withMPD)

import UI.Curses

import ListWidget (ListWidget, Searchable)
import qualified ListWidget

import qualified Macro
import           Macro (Macros)

import           Content
import           Type

-- | Widgets
data Widget = Widget {
    render      :: (MonadIO m) => Window -> m ()
  , title       :: String
  , commands    :: [WidgetCommand]
  , event       :: Event -> Vimus Widget
  , currentItem :: Maybe Content
  , searchItem  :: SearchOrder -> String -> Widget
  , filterItem  :: String -> Widget
}

data SearchOrder = Forward | Backward
data SearchPredicate = Search | Filter

-- | Events
data Event =
    EvCurrentSongChanged (Maybe MPD.Song)
  | EvPlaylistChanged [MPD.Song]
  | EvLibraryChanged [LsResult]
  | EvResize (Int, Int)

sendEvent :: Event -> Widget -> Vimus Widget
sendEvent = flip event

type Handler a = Event -> a -> Vimus (Maybe a)

-- | Define a command.

data Action =

  -- | An action that expects an arbitrary (possibly empty) strings as argument
  --
  -- This can be used to implement variadic actions.
    Action  (String -> Vimus ())

  -- | An action that expects no arguments
  | Action0 (Vimus ())

  -- | An action that expects one argument
  | Action1 (String -> Vimus ())

  -- | An action that expects two arguments
  | Action2 (String -> String -> Vimus ())

  -- | An action that expects three arguments
  | Action3 (String -> String -> String -> Vimus ())

data Command = Command {
  commandName   :: String
, commandAction :: Action
}

instance Searchable Command where
  searchTags item = [commandName item]

type WidgetCommand = (String, WidgetAction)
type WidgetAction  = Vimus (Maybe Widget)

widgetCommand :: String -> WidgetAction -> WidgetCommand
widgetCommand = (,)

instance Renderable Command where
  renderItem = commandName

instance Eq Command where
  (==) = (==) `on` commandName

instance Ord Command where
  compare = comparing commandName

-- | Define a macro.
addMacro :: String -- ^ macro
         -> String -- ^ expansion
         -> Vimus ()
addMacro m c = do
  st <- get
  put (st {programStateMacros = Macro.addMacro m c (programStateMacros st)})

data ProgramState = ProgramState {
  tabView            :: TabView
, mainWindow         :: Window
, statusLine         :: Window
, tabWindow          :: Window
, getLastSearchTerm  :: String
, programStateMacros :: Macros
, libraryPath        :: Maybe String
}

type Vimus a = StateT ProgramState MPD a

-- | Tab zipper
type Tab = (View, Widget)

tabName :: Tab -> View
tabName = fst

tabWidget :: Tab -> Widget
tabWidget = snd

data TabView = TabView ![Tab] ![Tab]

tabFromList :: [Tab] -> TabView
tabFromList = TabView []

tabNext :: TabView -> TabView
tabNext (TabView prev next) = case next of
  [this]    -> TabView [] (reverse $ this:prev)
  this:rest -> TabView (this:prev) rest
  _         -> error "No tabs!"

tabPrev :: TabView -> TabView
tabPrev (TabView prev next) = case prev of
  this:rest -> TabView rest (this:next)
  []        -> TabView (tail list) [head list]
                where list = reverse next

currentTab :: TabView -> Tab
currentTab (TabView _ next) = case next of
  this:_ -> this
  []     -> error "No tabs!"

-- Sanity check function, useful if we ever decide to c hange tabName to String instead of View
hasTab :: TabView -> View -> Bool
hasTab (TabView prev next) v = prev `has` v || next `has` v
  where
    has :: [Tab] -> View -> Bool
    has []     _ = False
    has (x:xs) y = (tabName x == y) || xs `has` y

getTabs :: TabView -> [Tab]
getTabs (TabView prev next) = reverse prev ++ next
<commit_msg>Fix comment<commit_after>{-# LANGUAGE FlexibleContexts, FlexibleInstances, RankNTypes, TupleSections #-}
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
module Vimus (
  Vimus
, ProgramState (..)
, Action (..)
, Command (..)
, View (..)
, Event (..)
, Tab
, tabFromList
, sendEvent
, Handler

, Widget (..)
, WidgetCommand
, WidgetAction
, widgetCommand
, SearchPredicate (..)
, SearchOrder (..)

-- * changing the current view
, nextView
, previousView
, getCurrentView
, setCurrentView

, withCurrentSong
, withCurrentItem
, modifyTabs
, modifyCurrentTab
, withTabs
, withCurrentTab
, withAllWidgets
, withCurrentWidget
, setCurrentWidget
, modifyCurrentWidget
, renderMainWindow
, renderToMainWindow
, addMacro
, setLibraryPath
) where

import Control.Monad.State (liftIO, gets, get, put, modify, StateT, MonadState)
import Control.Monad.Trans (MonadIO)
import Control.Monad

import Data.Default
import Data.Ord (comparing)
import Data.Function (on)

import Network.MPD.Core
import Network.MPD as MPD (LsResult)
import qualified Network.MPD as MPD hiding (withMPD)

import UI.Curses

import ListWidget (ListWidget, Searchable)
import qualified ListWidget

import qualified Macro
import           Macro (Macros)

import           Content
import           Type

-- | Widgets
data Widget = Widget {
    render      :: (MonadIO m) => Window -> m ()
  , title       :: String
  , commands    :: [WidgetCommand]
  , event       :: Event -> Vimus Widget
  , currentItem :: Maybe Content
  , searchItem  :: SearchOrder -> String -> Widget
  , filterItem  :: String -> Widget
}

data SearchOrder = Forward | Backward
data SearchPredicate = Search | Filter

-- | Events
data Event =
    EvCurrentSongChanged (Maybe MPD.Song)
  | EvPlaylistChanged [MPD.Song]
  | EvLibraryChanged [LsResult]
  | EvResize (Int, Int)

sendEvent :: Event -> Widget -> Vimus Widget
sendEvent = flip event

type Handler a = Event -> a -> Vimus (Maybe a)

-- | Define a command.

data Action =

  -- | An action that expects an arbitrary (possibly empty) strings as argument
  --
  -- This can be used to implement variadic actions.
    Action  (String -> Vimus ())

  -- | An action that expects no arguments
  | Action0 (Vimus ())

  -- | An action that expects one argument
  | Action1 (String -> Vimus ())

  -- | An action that expects two arguments
  | Action2 (String -> String -> Vimus ())

  -- | An action that expects three arguments
  | Action3 (String -> String -> String -> Vimus ())

data Command = Command {
  commandName   :: String
, commandAction :: Action
}

instance Searchable Command where
  searchTags item = [commandName item]

type WidgetCommand = (String, WidgetAction)
type WidgetAction  = Vimus (Maybe Widget)

widgetCommand :: String -> WidgetAction -> WidgetCommand
widgetCommand = (,)

instance Renderable Command where
  renderItem = commandName

instance Eq Command where
  (==) = (==) `on` commandName

instance Ord Command where
  compare = comparing commandName

-- | Define a macro.
addMacro :: String -- ^ macro
         -> String -- ^ expansion
         -> Vimus ()
addMacro m c = do
  st <- get
  put (st {programStateMacros = Macro.addMacro m c (programStateMacros st)})

data ProgramState = ProgramState {
  tabView            :: TabView
, mainWindow         :: Window
, statusLine         :: Window
, tabWindow          :: Window
, getLastSearchTerm  :: String
, programStateMacros :: Macros
, libraryPath        :: Maybe String
}

type Vimus a = StateT ProgramState MPD a

-- | Tab zipper
type Tab = (View, Widget)

tabName :: Tab -> View
tabName = fst

tabWidget :: Tab -> Widget
tabWidget = snd

data TabView = TabView ![Tab] ![Tab]

tabFromList :: [Tab] -> TabView
tabFromList = TabView []

tabNext :: TabView -> TabView
tabNext (TabView prev next) = case next of
  [this]    -> TabView [] (reverse $ this:prev)
  this:rest -> TabView (this:prev) rest
  _         -> error "No tabs!"

tabPrev :: TabView -> TabView
tabPrev (TabView prev next) = case prev of
  this:rest -> TabView rest (this:next)
  []        -> TabView (tail list) [head list]
                where list = reverse next

currentTab :: TabView -> Tab
currentTab (TabView _ next) = case next of
  this:_ -> this
  []     -> error "No tabs!"

-- Sanity check function, useful if we ever decide to change tabName to String
-- instead of View
hasTab :: TabView -> View -> Bool
hasTab (TabView prev next) v = prev `has` v || next `has` v
  where
    has :: [Tab] -> View -> Bool
    has []     _ = False
    has (x:xs) y = (tabName x == y) || xs `has` y

getTabs :: TabView -> [Tab]
getTabs (TabView prev next) = reverse prev ++ next

{'fraction_non_alphanumeric': 0.11303970492514645, 'fraction_numerical': 0.0008678672163159037, 'mean_word_length': 3.1377917414721725, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 81, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6906520', 'n_tokens_mistral': 2882, 'n_tokens_neox': 2843, 'n_words': 942}