Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit 190c81f

Browse files
committed
Merge pull request #169 from cocreature/extensible-state
Refactor ExtensibleState
2 parents 3e09229 + 222600f commit 190c81f

File tree

2 files changed

+26
-68
lines changed

2 files changed

+26
-68
lines changed
Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{-# LANGUAGE PatternGuards #-}
1+
{-# LANGUAGE ScopedTypeVariables #-}
22
-- Based on the one in xmonad-contrib, original header below
33
-----------------------------------------------------------------------------
44
-- |
@@ -24,24 +24,21 @@ module Haskell.Ide.Engine.ExtensibleState (
2424
, gets
2525
) where
2626

27-
import Control.Applicative
28-
import Data.Typeable (typeOf,Typeable,cast)
29-
import qualified Data.Map as M
30-
import Haskell.Ide.Engine.PluginDescriptor
3127
import qualified Control.Monad.State.Strict as State
32-
import Data.Maybe (fromMaybe)
33-
import Control.Monad
34-
import Control.Monad.Trans.Class
28+
import Control.Monad.Trans.Class
29+
import Data.Dynamic
30+
import qualified Data.Map as M
31+
import Haskell.Ide.Engine.PluginDescriptor
3532

3633
-- ---------------------------------------------------------------------
3734
-- $usage
3835
--
39-
-- To utilize this feature in a contrib module, create a data type
36+
-- To utilize this feature in a plugin, create a data type
4037
-- and make it an instance of ExtensionClass. You can then use
4138
-- the functions from this module for storing and retrieving your data:
4239
--
4340
-- > {-# LANGUAGE DeriveDataTypeable #-}
44-
-- > import qualified XMonad.Util.ExtensibleState as XS
41+
-- > import qualified Haskell.Ide.Engine.ExtensibleState as XS
4542
-- >
4643
-- > data ListStorage = ListStorage [Integer] deriving Typeable
4744
-- > instance ExtensionClass ListStorage where
@@ -58,28 +55,19 @@ import Control.Monad.Trans.Class
5855
--
5956
-- > .. XS.get :: X ListStorage
6057
--
61-
-- To make your data persistent between restarts, the data type needs to be
62-
-- an instance of Read and Show and the instance declaration has to be changed:
63-
--
6458
-- > data ListStorage = ListStorage [Integer] deriving (Typeable,Read,Show)
6559
-- >
6660
-- > instance ExtensionClass ListStorage where
6761
-- > initialValue = ListStorage []
68-
-- > extensionType = PersistentExtension
6962
--
70-
-- One should take care that the string representation of the chosen type
71-
-- is unique among the stored values, otherwise it will be overwritten.
72-
-- Normally these string representations contain fully qualified module names
73-
-- when automatically deriving Typeable, so
74-
-- name collisions should not be a problem in most cases.
7563
-- A module should not try to store common datatypes(e.g. a list of Integers)
7664
-- without a custom data type as a wrapper to avoid collisions with other modules
7765
-- trying to store the same data type without a wrapper.
7866
--
7967

8068
-- | Modify the map of state extensions by applying the given function.
81-
modifyStateExts :: (M.Map String (Either String StateExtension)
82-
-> M.Map String (Either String StateExtension))
69+
modifyStateExts :: (M.Map TypeRep Dynamic
70+
-> M.Map TypeRep Dynamic)
8371
-> IdeM ()
8472
modifyStateExts f = lift $ lift $ State.modify $ \st -> st { extensibleState = f (extensibleState st) }
8573

@@ -92,30 +80,22 @@ modify f = put . f =<< get
9280
-- type will be overwritten. (More precisely: A value whose string representation of its type
9381
-- is equal to the new one's)
9482
put :: ExtensionClass a => a -> IdeM ()
95-
put v = modifyStateExts . M.insert (show . typeOf $ v) . Right . extensionType $ v
83+
put v = modifyStateExts . M.insert (typeOf $ v) . toDyn $ v
9684

9785
-- | Try to retrieve a value of the requested type, return an initial value if there is no such value.
98-
get :: ExtensionClass a => IdeM a
99-
get = getState' undefined -- `trick' to avoid needing -XScopedTypeVariables
100-
where toValue val = maybe initialValue id $ cast val
101-
getState' :: ExtensionClass a => a -> IdeM a
102-
getState' k = do
103-
v <- lift $ lift $ State.gets $ M.lookup (show . typeOf $ k) . extensibleState
104-
case v of
105-
Just (Right (StateExtension val)) -> return $ toValue val
106-
Just (Right (PersistentExtension val)) -> return $ toValue val
107-
Just (Left str) | PersistentExtension x <- extensionType k -> do
108-
let val = fromMaybe initialValue $ cast =<< safeRead str `asTypeOf` Just x
109-
put (val `asTypeOf` k)
110-
return val
111-
_ -> return $ initialValue
112-
safeRead str = case reads str of
113-
[(x,"")] -> Just x
114-
_ -> Nothing
86+
get :: forall a. ExtensionClass a => IdeM a
87+
get =
88+
do v <-
89+
lift $
90+
lift $
91+
State.gets $ M.lookup (typeRep $ (Proxy :: Proxy a)) . extensibleState
92+
case v of
93+
Just dyn -> return $ fromDyn dyn initialValue
94+
_ -> return initialValue
11595

11696
gets :: ExtensionClass a => (a -> b) -> IdeM b
11797
gets = flip fmap get
11898

11999
-- | Remove the value from the extensible state field that has the same type as the supplied argument
120-
remove :: ExtensionClass a => a -> IdeM ()
121-
remove wit = modifyStateExts $ M.delete (show . typeOf $ wit)
100+
remove :: ExtensionClass a => proxy a -> IdeM ()
101+
remove wit = modifyStateExts $ M.delete (typeRep $ wit)

hie-plugin-api/Haskell/Ide/Engine/PluginDescriptor.hs

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ module Haskell.Ide.Engine.PluginDescriptor
4444
-- * The IDE monad
4545
, IdeM
4646
, IdeState(..)
47-
, StateExtension(..)
4847
, ExtensionClass(..)
4948
, getPlugins
5049
, untagPluginDescriptor
@@ -59,19 +58,18 @@ module Haskell.Ide.Engine.PluginDescriptor
5958
, module Haskell.Ide.Engine.PluginTypes
6059
) where
6160

62-
import GHC.TypeLits
63-
import Haskell.Ide.Engine.PluginTypes
64-
65-
import Data.Singletons
6661
import Control.Applicative
6762
import Control.Monad.State.Strict
6863
import Data.Aeson
64+
import Data.Dynamic
6965
import qualified Data.Map as Map
66+
import Data.Singletons
7067
import qualified Data.Text as T
71-
import Data.Typeable
7268
import Data.Vinyl
7369
import qualified Data.Vinyl.Functor as Vinyl
7470
import GHC.Generics
71+
import GHC.TypeLits
72+
import Haskell.Ide.Engine.PluginTypes
7573
import qualified Language.Haskell.GhcMod.Monad as GM
7674

7775
-- ---------------------------------------------------------------------
@@ -211,7 +209,7 @@ type IdeT m = GM.GhcModT (StateT IdeState m)
211209
data IdeState = IdeState
212210
{
213211
idePlugins :: Plugins
214-
, extensibleState :: !(Map.Map String (Either String StateExtension))
212+
, extensibleState :: !(Map.Map TypeRep Dynamic)
215213
-- ^ stores custom state information.
216214
} deriving (Show)
217215

@@ -230,23 +228,3 @@ getPlugins = lift $ lift $ idePlugins <$> get
230228
class Typeable a => ExtensionClass a where
231229
-- | Defines an initial value for the state extension
232230
initialValue :: a
233-
-- | Specifies whether the state extension should be
234-
-- persistent. Setting this method to 'PersistentExtension'
235-
-- will make the stored data survive restarts, but
236-
-- requires a to be an instance of Read and Show.
237-
--
238-
-- It defaults to 'StateExtension', i.e. no persistence.
239-
extensionType :: a -> StateExtension
240-
extensionType = StateExtension
241-
242-
-- | Existential type to store a state extension.
243-
data StateExtension =
244-
forall a. ExtensionClass a => StateExtension a
245-
-- ^ Non-persistent state extension
246-
| forall a. (Read a, Show a, ExtensionClass a) => PersistentExtension a
247-
-- ^ Persistent extension
248-
249-
instance Show StateExtension where
250-
show _ = "StateExtension"
251-
252-
-- EOF

0 commit comments

Comments
 (0)