leksah-0.1: src/IDE/Core/State.hs
{-# OPTIONS_GHC -fglasgow-exts #-}
-----------------------------------------------------------------------------
--
-- Module : IDE.Core.State
-- Copyright : (c) Juergen Nicklisch-Franken (aka Jutaro)
-- License : GNU-GPL
--
-- Maintainer : Juergen Nicklisch-Franken <jnf at arcor.de>
-- Stability : experimental
-- Portability : portable
--
-- | The core state of ide. This module is imported from every other module,
-- | and all data structures of the state are declared here, to avoid circular
-- | module dependencies.
--
-------------------------------------------------------------------------------
module IDE.Core.State (
IDEObject(..)
, IDEEditor
, IDE(..)
, IDERef
, IDEM
, IDEAction
, IDEEvent(..)
, EventSelector(..)
-- * Convenience methods for accesing the IDE State
, readIDE
, modifyIDE
, modifyIDE_
, withIDE
, getIDE
, ideMessage
, logMessage
, forceJust
, module IDE.Core.Types
, module IDE.Core.Panes
, module IDE.Core.Exception
) where
import Graphics.UI.Gtk hiding (get)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.IORef
import Control.Monad.Reader
import GHC (Session)
import Data.Unique
import IDE.Core.Types
import IDE.Core.Panes
import IDE.Core.Exception
ideMessage :: MessageLevel -> String -> IDEAction
ideMessage level str = do
st <- ask
triggerEvent st (LogMessage (str ++ "\n") LogTag)
lift $ sysMessage level str
logMessage :: String -> LogTag -> IDEAction
logMessage str tag = do
st <- ask
triggerEvent st (LogMessage (str ++ "\n") tag)
return ()
data IDEEvent =
CurrentInfo
| ActivePack
| SelectInfo String
| SelectIdent IdentifierDescr
| LogMessage String LogTag
| GetToolbar [Widget]
data EventSelector =
CurrentInfoS
| ActivePackS
| SelectInfoS
| SelectIdentS
| LogMessageS
| GetToolbarS
deriving (Eq,Ord,Show)
eventAsSelector :: IDEEvent -> EventSelector
eventAsSelector CurrentInfo = CurrentInfoS
eventAsSelector ActivePack = ActivePackS
eventAsSelector (LogMessage _ _) = LogMessageS
eventAsSelector (GetToolbar _) = GetToolbarS
eventAsSelector (SelectInfo _) = SelectInfoS
eventAsSelector (SelectIdent _) = SelectIdentS
class IDEObject alpha where
canTriggerEvent :: alpha -> EventSelector -> Bool
canTriggerEvent _ _ = False
triggerEvent :: alpha -> IDEEvent -> IDEM IDEEvent
triggerEvent o e =
if canTriggerEvent o (eventAsSelector e)
then do
handlerMap <- readIDE handlers
let selector = eventAsSelector e
case selector `Map.lookup` handlerMap of
Nothing -> return e
Just l -> foldM (\e (_,ah) -> ah e) e l
else throwIDE $ "Object can't trigger event " ++ show (eventAsSelector e)
registerEvent :: alpha -> EventSelector -> Either (IDEEvent -> IDEM IDEEvent) Unique -> IDEM Unique
registerEvent o e (Left handler) = do
handlerMap <- readIDE handlers
unique <- lift $ newUnique
let newHandlers = case e `Map.lookup` handlerMap of
Nothing -> Map.insert e [(unique,handler)] handlerMap
Just l -> Map.insert e ((unique,handler):l) handlerMap
modifyIDE_ (\ide -> return (ide{handlers = newHandlers}))
return unique
registerEvent o e (Right unique) = do
handlerMap <- readIDE handlers
let newHandlers = case e `Map.lookup` handlerMap of
Nothing -> handlerMap
Just l -> let newList = filter (\ (mu,_) -> mu /= unique) l
in Map.insert e newList handlerMap
modifyIDE_ (\ide -> return (ide{handlers = newHandlers}))
return unique
class IDEObject o => IDEEditor o
-- ---------------------------------------------------------------------
-- IDE State
--
--
-- | The IDE state
--
data IDE = IDE {
window :: Window -- ^ the gtk window
, uiManager :: UIManager -- ^ the gtk uiManager
, panes :: Map PaneName IDEPane -- ^ a map with all panes (subwindows)
, activePane :: Maybe (PaneName,Connections)
, paneMap :: Map PaneName (PanePath, Connections)
-- ^ a map from the pane name to its gui path and signal connections
, layout :: PaneLayout -- ^ a description of the general gui layout
, specialKeys :: SpecialKeyTable IDERef -- ^ a structure for emacs like keystrokes
, specialKey :: SpecialKeyCons IDERef -- ^ the first of a double keystroke
, candy :: CandyTable -- ^ table for source candy
, prefs :: Prefs -- ^ configuration preferences
, activePack :: Maybe IDEPackage
, errors :: [ErrorSpec]
, currentErr :: Maybe Int
, accessibleInfo :: (Maybe (PackageScope)) -- ^ the world scope
, currentInfo :: (Maybe (PackageScope,PackageScope))
-- ^ the first is for the current package,
--the second is the scope in the current package
, session :: Session -- ^ a ghc session object, side effects
-- reusing with sessions?
, handlers :: Map EventSelector [(Unique, IDEEvent -> IDEM IDEEvent)]
-- ^ event handling table
} --deriving Show
instance IDEObject IDERef where
canTriggerEvent o LogMessageS = True
canTriggerEvent o GetToolbarS = True
canTriggerEvent o SelectInfoS = True
canTriggerEvent o SelectIdentS = True
canTriggerEvent o CurrentInfoS = True
canTriggerEvent o ActivePackS = True
-- canTriggerEvent o _ = False
--
-- | A mutable reference to the IDE state
--
type IDERef = IORef IDE
--
-- | A reader monad for a mutable reference to the IDE state
--
type IDEM = ReaderT (IDERef) IO
--
-- | A shorthand for a reader monad for a mutable reference to the IDE state
-- which does not return a value
--
type IDEAction = IDEM ()
-- ---------------------------------------------------------------------
-- Convenience methods for accesing the IDE State
--
-- | Read an attribute of the contents
readIDE :: (IDE -> beta) -> IDEM beta
readIDE f = do
e <- ask
lift $ liftM f (readIORef e)
-- | Modify the contents, using an IO action.
modifyIDE_ :: (IDE -> IO IDE) -> IDEM ()
modifyIDE_ f = do
e <- ask
e' <- lift $ (f =<< readIORef e)
lift $ writeIORef e e'
-- | Variation on modifyIDE_ that lets you return a value
modifyIDE :: (IDE -> IO (IDE,beta)) -> IDEM beta
modifyIDE f = do
e <- ask
(e',result) <- lift (f =<< readIORef e)
lift $ writeIORef e e'
return result
withIDE :: (IDE -> IO alpha) -> IDEM alpha
withIDE f = do
e <- ask
lift $ f =<< readIORef e
getIDE :: IDEM(IDE)
getIDE = do
e <- ask
st <- lift $ readIORef e
return st
-- ---------------------------------------------------------------------
-- Convenience methods for accesing the IDE State
--
forceJust :: Maybe alpha -> String -> alpha
forceJust mb str = case mb of
Nothing -> throwIDE str
Just it -> it