packages feed

vimus-0.1.0: src/Widget/HelpWidget.hs

{-# LANGUAGE RankNTypes, OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Widget.HelpWidget (
  makeHelpWidget

-- exported to silence warnings
, CommandList (..)
, HelpWidget (..)
) where

import           Data.List (intercalate)
import           Data.Monoid
import           Control.Applicative
import           Text.Printf (printf)
import           Data.String
import           Data.List (sortBy)
import           Data.Map (Map)
import qualified Data.Map as Map
import           Data.Ord (comparing)

import           Vimus
import           Command.Help hiding (help)
import           Command.Core (Command, commandName, commandSynopsis)
import           Command.Type (commandHelp_)
import           Widget.ListWidget (ListWidget)
import qualified Widget.ListWidget as ListWidget
import           Widget.TextWidget
import           Widget.Type
import           Content
import           WindowLayout

data HelpWidget = HelpWidget {
  helpWidgetCommandList  :: CommandList
, helpWidgetDetailedHelp :: Maybe AnyWidget
}

makeHelpWidget :: [Command] -> Map String [String] -> AnyWidget
makeHelpWidget commands macroGuesses = AnyWidget (HelpWidget commandList Nothing)
  where
    commandList = CommandList (ListWidget.new $ sortBy (comparing commandName) commands) macroGuesses

-- helper for searchItem and filterItem pass-through
passThrough :: (forall a . Widget a => (a -> a)) -> HelpWidget -> HelpWidget
passThrough f (HelpWidget commandList mDetails) = case mDetails of
  Just details -> HelpWidget commandList (Just $ f details)
  Nothing      -> HelpWidget (f commandList) Nothing

commandHelp :: Command -> [TextLine]
commandHelp c = TextLine [Colored SuggestionsColor $ commandSynopsis c] : (map (fromString . ("  " ++)) . commandHelpText) c

instance Widget HelpWidget where
  render (HelpWidget commandList mDetails) = maybe (render commandList) render mDetails
  currentItem _                            = Nothing
  searchItem widget o t                    = passThrough (\w -> searchItem w o t) widget
  filterItem widget t                      = passThrough (`filterItem` t) widget

  handleEvent widget@(HelpWidget commandList mDetails) ev  = case ev of

    -- switch between command list and details on :default-action
    EvDefaultAction -> maybe moveIn (const moveOut) mDetails

    -- show details on :move-in
    EvMoveIn        -> moveIn

    -- go back to command list on :move-out
    EvMoveOut       -> moveOut

    -- pass through all other events
    _               -> passThrough_
    where
      passThrough_ = case mDetails of
        Just details -> HelpWidget commandList . Just <$> handleEvent details ev
        Nothing      -> (`HelpWidget` Nothing) <$> handleEvent commandList ev

      moveOut = return $ HelpWidget commandList Nothing

      moveIn = return $ case (mDetails, selectCommand commandList) of

        -- command selected, show details
        (Nothing, Just c) -> HelpWidget commandList (Just . makeTextWidget $ commandHelp c)

        -- already showing details (or no command under cursor), do nothing
        _ -> widget

data CommandList = CommandList {
  commandListCommands     :: ListWidget () Command
, commandListMacroGuesses :: Map String [String]
}

instance Searchable Command where
  searchTags c = commandName c : concatMap words (unHelp $ commandHelp_ c)

selectCommand :: CommandList -> Maybe Command
selectCommand = ListWidget.select . commandListCommands

instance Widget CommandList where
  render (CommandList w ms) = do
    ListWidget.render (const False) (fmap help w)
    where
      help c = mconcat [TextLine . return . Colored SuggestionsColor . printf "%-30s" $ commandSynopsis c, macros, fromString $ commandShortHelp c]
        where
          -- macros defined for this command
          macros  = TextLine . return . Colored InputColor . printf "%-18s  " $ maybe "" (intercalate "  ") mMacros
          mMacros = Map.lookup (commandName c) ms

  currentItem _                      = Nothing
  searchItem  (CommandList w ms) o t = CommandList (ListWidget.searchItem w o t) ms
  filterItem  (CommandList w ms) t   = CommandList (ListWidget.filterItem w t) ms
  handleEvent (CommandList w ms) ev  = (`CommandList` ms) <$> ListWidget.handleEvent w ev