-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Runtime-editable program settings. -- -- This library aims to be a tool for constructing a settings management -- UI on top of the relevant part of your program state. The settings -- tree structure definition is separate from the actual setting values, -- so your program logic code doesn't need to be changed. It can access -- the settings as regular Haskell values. -- -- The settings are presented to the user as a tree hierarchy. The idea -- has some similarity to git config and Weechat's -- settings system, but isn't identical. -- -- To get started, see the tutorial in the Data.Settings module. @package settings @version 0.2.2.0 module Data.Settings.Types -- | A settings option. The option value itself is held as usual in regular -- application state, not here. What is held here is functions -- applied to that state to get or set the value. data Option m Option :: m String -> (String -> m (Maybe SettingsError)) -> m () -> Option m [optGet] :: Option m -> m String [optSet] :: Option m -> String -> m (Maybe SettingsError) [optReset] :: Option m -> m () data Section m Section :: HashMap String (Option m) -> HashMap String (Section m) -> Section m [secOpts] :: Section m -> HashMap String (Option m) [secSubs] :: Section m -> HashMap String (Section m) type OptName = String type SecName = String type OptPath = String type OptRoute = [String] data SettingsError InvalidPath :: OptPath -> SettingsError NoSuchOption :: OptRoute -> SettingsError NoSuchSection :: OptRoute -> SettingsError NoSuchNode :: OptRoute -> SettingsError InvalidValueForType :: String -> SettingsError InvalidValue :: String -> SettingsError class OptionValue v readOption :: OptionValue v => String -> Maybe v showOption :: OptionValue v => v -> String typeName :: OptionValue v => v -> String class Monad m => MonadSettings m s | m -> s getSettings :: MonadSettings m s => m s putSettings :: MonadSettings m s => s -> m () modifySettings :: MonadSettings m s => (s -> s) -> m () getSTree :: MonadSettings m s => m (Section m) module Data.Settings.Option mkOptionV :: (Monad m, OptionValue v) => m v -> (v -> m Bool) -> m () -> Option m mkOptionS :: (MonadSettings m s, OptionValue v) => (s -> v) -> (v -> s -> Maybe s) -> (s -> (Maybe v, s)) -> (v -> m ()) -> Option m -- | This module provides functions work working with the Section -- type, i.e. option trees. The style is similar to the APIs for -- HashMaps and Maps. You can use these functions to -- construct a custom settings tree UI. Before you do that, try the -- Data.Settings.Interface module, which may already offer what -- you need. module Data.Settings.Section -- | Construct an empty section, no options and no subsections. empty :: Section m -- | Construct a section with a single option. singleton :: SecName -> Option m -> Section m -- | Return True if this section contains any options, False -- otherwise. hasOpts :: Section m -> Bool -- | Return True if this section contains any subsections, -- False otherwise. hasSubs :: Section m -> Bool -- | Return True if this section is empty (no options, no -- subsections), False otherwise. null :: Section m -> Bool -- | Return True if an option or a subsection is present at the -- specified path, False otherwise. member :: OptRoute -> Section m -> (Bool, Bool) -- | Return True if an option is present at the specified path, -- False otherwise. memberOpt :: OptRoute -> Section m -> Bool -- | Return True if a subsection is present at the specified path, -- False otherwise. memberSub :: OptRoute -> Section m -> Bool -- | Return Just the section or option at the specified path, or -- Nothing if this section contains no such path. lookup :: OptRoute -> Section m -> Maybe (Either (Section m) (Option m)) -- | Return Just the option at the specified path, or Nothing -- if this section doesn't contain an option at this path. lookupOpt :: [String] -> Section m -> Maybe (Option m) -- | Return Just the section at the specified path, or -- Nothing if this section doesn't contain a subsection at this -- path. lookupSub :: [String] -> Section m -> Maybe (Section m) -- | Alias for insertOpt. insert :: [String] -> Option m -> Section m -> Section m -- | Add the specified option at the specified path under this section. If -- the section previously contained an option for this path, the old -- value is replaced. insertOpt :: [String] -> Option m -> Section m -> Section m -- | Add the specified subsection at the specified path under this section. -- If the section previously contained a subsection for this path, the -- old value is replaced. insertSub :: [String] -> Section m -> Section m -> Section m -- | Remove the option at the specified path, if present. If there is a -- section under this path, it won't be removed. deleteOpt :: [String] -> Section m -> Section m -- | Remove the section at the specified path, if present. If there is an -- option under this path, it won't be removed. deleteSub :: [String] -> Section m -> Section m -- | Remove the option or section at the specified path, if present. delete :: [String] -> Section m -> Section m module Data.Settings.Route -- | Split a path string into its components, if it's a valid path -- syntactically. parseRoute :: OptPath -> Maybe OptRoute -- | Like parseRoute, but allows to choose the escape character -- (e.g. '\') and the path separator character (e.g. -- .). parseRoute' :: Char -> Char -> OptPath -> Maybe OptRoute -- | Create a string representation of a path, with the parts separated by -- periods, and literal periods escaped using backslashes. showRoute :: OptRoute -> OptPath -- | Like showRoute, but allows to choose the escape character (e.g. -- \\) and the path separator character (e.g. -- .). showRoute' :: Char -> Char -> OptRoute -> OptPath module Data.Settings.Interface -- | TODO queryR :: MonadSettings m s => OptRoute -> m (Either SettingsError (Either ([SecName], [OptName]) String)) -- | TODO querySectionR :: MonadSettings m s => OptRoute -> m (Either SettingsError ([SecName], [OptName])) -- | TODO queryOptionR :: MonadSettings m s => OptRoute -> m (Either SettingsError String) -- | TODO updateOptionR :: MonadSettings m s => OptRoute -> String -> m (Maybe SettingsError) -- | TODO resetOptionR :: MonadSettings m s => OptRoute -> m (Maybe SettingsError) -- | TODO query :: MonadSettings m s => OptPath -> m (Either SettingsError (Either ([SecName], [OptName]) String)) -- | TODO querySection :: MonadSettings m s => OptPath -> m (Either SettingsError ([SecName], [OptName])) -- | TODO queryOption :: MonadSettings m s => OptPath -> m (Either SettingsError String) -- | TODO updateOption :: MonadSettings m s => OptPath -> String -> m (Maybe SettingsError) -- | TODO resetOption :: MonadSettings m s => OptPath -> m (Maybe SettingsError) -- | This top-level module contains just a tutorial, which you can read -- below. It will help you figure out which of the sub-modules you need, -- and how to use them. -- --
-- data Settings = Settings
-- { setsTabWidth :: Int
-- , setsFont :: String
-- , setsTextSize :: Int
-- , setsColorScheme :: String
-- }
--
--
-- For simplicity, suppose the settings tree won't be changing, so all we
-- need in our application state is the settings. Let's use this:
--
--
-- data AppState = AppState
-- { appOpenFiles :: [String]
-- , appUI :: Widget
-- , appSettings :: Settings
-- }
--
--
-- If we wanted to allow the settings tree structure to change, we'd have
-- a field for it too in the app state record.
--
-- This will be our monad:
--
-- -- type App = StateT AppState IO ---- -- Now let's define a settings tree. A settings tree is the top-level -- section of it. Each such section consists of two things: A set -- of settings options, and a set of subsections. An empty tree looks -- like this: -- --
-- import Data.Settings.Section (empty) -- -- stree :: Section App -- stree = empty ---- -- Which is equivalent to: -- --
-- import qualified Data.HashMap.Lazy as M
--
-- stree :: Section App
-- stree = Section
-- { secOpts = M.empty
-- , secSubs = M.empty
-- }
--
--
-- The secOpts field is a map between option names and
-- Option values. The secSubs field is a map between
-- subsection names and Section values. We can then refer to a
-- specific tree node using period-separated syntax. For example, if we
-- have a tree with a single top-level option "a", we can refer
-- to it in the UI simply a "a". If we have a tree with a
-- subsection "s" and under it an option "a", we refer
-- to that section as "a" and to the option under it as
-- "s.a". And so on, we can have arbitrarily deep nesting of
-- sections and options, e.g. "s.t.u.v.w.x.a".
--
-- It is possible for a section or option name to contain a period. In
-- that case, the period must be escaped using a backslash before it. To
-- specify a literal backslash, escape it too, i.e. use two backslashes.
-- It is also possible different separator characters instead of a
-- period.
--
-- The low-level flexible way to define a settings tree is by using
-- Option value contructors directly. Let's define a simple flat
-- tree with 4 options and no subsections.
--
-- The Option fields are monadic actions in our application
-- monad, App.
--
--
-- import Control.Monad.Trans.State
-- import qualified Data.HashMap.Lazy as M
-- import Data.Settings.Types
-- import Text.Read (readMaybe)
--
-- -- Convenience wrappers to make the code shorter
-- -- Perhaps a good chance to use lens?
-- getS = gets appSettings
-- putS sets = modify $ \ app -> app { appSettings = sets }
-- modifyS f = modify $ \ app -> app { appSettings = f $ appSettings app }
--
-- stree :: Section App
-- stree = Section
-- { secOpts = M.fromList
-- [ ( "tab-width"
-- , Option
-- { optGet = liftM (show . setsTabWidth) getS
-- , optSet = \ val ->
-- case readMaybe val of
-- Just n -> do
-- modifyS $ \ s -> s { setsTabWidth = n }
-- return Nothing
-- Nothing -> return $ Just $ InvalidValueForType val
-- , optReset = modifyS $ \ s -> s { setsTabWidth = 4 }
-- }
-- )
-- , ( "font"
-- , Option {- ... similar fashion ... -}
-- )
-- , ( "text-size"
-- , Option {- ... similar fashion ... -}
-- )
-- , ( "color-scheme"
-- , Option {- ... similar fashion ... -}
-- )
-- ]
-- , secSubs = M.empty
-- }
--
--
-- -- instance MonadSettings App Settings where -- getSettings = getS -- putSettings = putS -- modifySettings = modifyS -- getSTree = return stree ---- -- Now, suppose the user enters the command get x.y.z in our -- text editor's command input line. This should return a friendly -- result. If x.y.z is a valid path in our settings tree leading -- to an option value, display that value. If it's a section, display a -- list of the options and subsections it contains. If it's neither, i.e. -- the path is invalid, report the error. -- -- Such a UI can easily be constructed using functions in -- Data.Settings.Interface, e.g. see the query function. -- Using the values it returns, you can construct UI strings to display -- on the screen. -- -- For example, in our case we'd want get to display the -- top-level tree contents, get tab-width to display a number (4 -- by default) and get foo to display an error no such option -- or section. -- --
-- instace OptionValue Int where -- readOption = readMaybe -- showOption = show -- typeName = const "Integer" ---- -- And here's an instance for Bool: -- --
-- instace OptionValue Bool where -- readOption s -- | sl `elem` ["true, "yes", "on", "1"] = Just True -- | sl `elem` ["false", "no", "off", "0"] = Just False -- | otherwise = Nothing -- where sl = map toLower s -- showOption = show -- typeName = const "Boolean" ---- -- Now, using mkOptionV, and this time also using the -- MonadSettings functions, we can redefine the tab width option -- like this: -- --
-- mkOptionV
-- (liftM setsTabWidth getSettings)
-- (\ n -> do
-- modifySettings $ \ s -> s { setsTabWidth = n }
-- return True
-- )
-- (modifySettings $ \ s -> s { setsTabWidth = 4 })
--
--
-- Now let's improve further. This will be the highest level of the API.
-- Given a MonadSettings instance, the repetitive parts of the
-- code can be cleaned further, by using the mkOptionS function.
--
--
-- mkOptionS
-- setsTabWidth
-- (\ n s -> Just s { setsTabWidth = n })
-- (\ s -> (Just 4, s { setsTabWidth = 4 }))
-- (const $ return ())
--
--
-- Perhaps a bit cleaner form removing duplication is this:
--
--
-- mkOptionS
-- setsTabWidth
-- (\ n s -> Just $ set n s)
-- (\ s -> (Just defval, set defval s))
-- (const $ return ())
-- where
-- set n s = s { setsTabWidth = n }
-- defval = 4
--
--
-- The last argument is a callback action to be run when a successful set
-- or reset of the value occurs.
--
--