Fix a small space leak in DynamicProjects

Add a couple of strictness annotations to keep ExtensibleState from
building up thunks.
This commit is contained in:
Peter Jones
2015-11-18 13:37:55 -07:00
parent b1360f08d0
commit 08c88abfb2

View File

@@ -26,7 +26,7 @@ module XMonad.Actions.DynamicProjects
-- * Hooks -- * Hooks
, dynamicProjects , dynamicProjects
-- * Bindings -- * Bindings
, switchProjectPrompt , switchProjectPrompt
, shiftToProjectPrompt , shiftToProjectPrompt
@@ -43,10 +43,10 @@ module XMonad.Actions.DynamicProjects
import Control.Applicative ((<|>)) import Control.Applicative ((<|>))
import Control.Monad (when, unless) import Control.Monad (when, unless)
import Data.List (sort, union, stripPrefix) import Data.List (sort, union, stripPrefix)
import Data.Map (Map) import Data.Map.Strict (Map)
import qualified Data.Map as Map import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe, isNothing) import Data.Maybe (fromMaybe, isNothing)
import Data.Monoid ((<>)) import Data.Monoid ((<>))
import System.Directory (setCurrentDirectory, getHomeDirectory) import System.Directory (setCurrentDirectory, getHomeDirectory)
import XMonad import XMonad
import XMonad.Actions.DynamicWorkspaces import XMonad.Actions.DynamicWorkspaces
@@ -62,7 +62,7 @@ import qualified XMonad.Util.ExtensibleState as XS
-- @DynamicProjects@ treats workspaces as projects while maintaining -- @DynamicProjects@ treats workspaces as projects while maintaining
-- compatibility with all existing workspace-related functionality in -- compatibility with all existing workspace-related functionality in
-- XMonad. -- XMonad.
-- --
-- Instead of using generic workspace names such as @3@ or @work@, -- Instead of using generic workspace names such as @3@ or @work@,
-- @DynamicProjects@ allows you to dedicate workspaces to specific -- @DynamicProjects@ allows you to dedicate workspaces to specific
-- projects and then switch between projects easily. -- projects and then switch between projects easily.
@@ -89,7 +89,7 @@ import qualified XMonad.Util.ExtensibleState as XS
-- configuration and then configure some optional key bindings. -- configuration and then configure some optional key bindings.
-- --
-- > import XMonad.Actions.DynamicProjects -- > import XMonad.Actions.DynamicProjects
-- --
-- Start by defining some projects: -- Start by defining some projects:
-- --
-- > projects :: [Project] -- > projects :: [Project]
@@ -98,16 +98,16 @@ import qualified XMonad.Util.ExtensibleState as XS
-- > , projectDirectory = "~/" -- > , projectDirectory = "~/"
-- > , projectStartHook = Nothing -- > , projectStartHook = Nothing
-- > } -- > }
-- > -- >
-- > , Project { projectName = "browser" -- > , Project { projectName = "browser"
-- > , projectDirectory = "~/download" -- > , projectDirectory = "~/download"
-- > , projectStartHook = Just $ do spawn "conkeror" -- > , projectStartHook = Just $ do spawn "conkeror"
-- > spawn "chromium" -- > spawn "chromium"
-- > } -- > }
-- > ] -- > ]
-- --
-- Then inject @DynamicProjects@ into your XMonad configuration: -- Then inject @DynamicProjects@ into your XMonad configuration:
-- --
-- > main = xmonad $ dynamicProjects projects def -- > main = xmonad $ dynamicProjects projects def
-- --
-- And finally, configure some optional key bindings: -- And finally, configure some optional key bindings:
@@ -117,24 +117,24 @@ import qualified XMonad.Util.ExtensibleState as XS
-- --
-- For detailed instructions on editing your key bindings, see -- For detailed instructions on editing your key bindings, see
-- "XMonad.Doc.Extending#Editing_key_bindings". -- "XMonad.Doc.Extending#Editing_key_bindings".
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
type ProjectName = String type ProjectName = String
type ProjectTable = Map ProjectName Project type ProjectTable = Map ProjectName Project
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Details about a workspace that represents a project. -- | Details about a workspace that represents a project.
data Project = Project data Project = Project
{ projectName :: ProjectName -- ^ Workspace name. { projectName :: !ProjectName -- ^ Workspace name.
, projectDirectory :: FilePath -- ^ Working directory. , projectDirectory :: !FilePath -- ^ Working directory.
, projectStartHook :: Maybe (X ()) -- ^ Optional start-up hook. , projectStartHook :: !(Maybe (X ())) -- ^ Optional start-up hook.
} deriving Typeable } deriving Typeable
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Internal project state. -- | Internal project state.
data ProjectState = ProjectState data ProjectState = ProjectState
{ projects :: ProjectTable { projects :: !ProjectTable
, previousProject :: Maybe WorkspaceId , previousProject :: !(Maybe WorkspaceId)
} deriving Typeable } deriving Typeable
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@@ -155,14 +155,14 @@ dynamicProjectsLogHook :: X ()
dynamicProjectsLogHook = do dynamicProjectsLogHook = do
name <- gets (W.tag . W.workspace . W.current . windowset) name <- gets (W.tag . W.workspace . W.current . windowset)
state <- XS.get state <- XS.get
unless (Just name == previousProject state) $ do unless (Just name == previousProject state) $ do
XS.modify $ \s -> s {previousProject = Just name} XS.modify $ \s -> s {previousProject = Just name}
activateProject . fromMaybe (defProject name) $ activateProject . fromMaybe (defProject name) $
Map.lookup name (projects state) Map.lookup name (projects state)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Start-up hook for recording configured projects. -- | Start-up hook for recording configured projects.
dynamicProjectsStartupHook :: [Project] -> X () dynamicProjectsStartupHook :: [Project] -> X ()
dynamicProjectsStartupHook ps = XS.modify go dynamicProjectsStartupHook ps = XS.modify go
where where
@@ -171,10 +171,10 @@ dynamicProjectsStartupHook ps = XS.modify go
update :: ProjectTable -> ProjectTable update :: ProjectTable -> ProjectTable
update = Map.union (Map.fromList $ map entry ps) update = Map.union (Map.fromList $ map entry ps)
entry :: Project -> (ProjectName, Project) entry :: Project -> (ProjectName, Project)
entry p = (projectName p, addDefaultHook p) entry p = (projectName p, addDefaultHook p)
-- Force the hook to be a @Just@ so that it doesn't automatically -- Force the hook to be a @Just@ so that it doesn't automatically
-- get deleted when switching away from a workspace with no -- get deleted when switching away from a workspace with no
-- windows. -- windows.
@@ -182,7 +182,7 @@ dynamicProjectsStartupHook ps = XS.modify go
addDefaultHook p = p { projectStartHook = projectStartHook p <|> addDefaultHook p = p { projectStartHook = projectStartHook p <|>
Just (return ()) Just (return ())
} }
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Find a project based on its name. -- | Find a project based on its name.
lookupProject :: ProjectName -> X (Maybe Project) lookupProject :: ProjectName -> X (Maybe Project)
@@ -211,7 +211,7 @@ switchProject p = do
-- it's a dynamic project, remove it from the configuration. -- it's a dynamic project, remove it from the configuration.
when (null ws && isNothing (projectStartHook oldp)) $ when (null ws && isNothing (projectStartHook oldp)) $
XS.modify (\s -> s {projects = Map.delete name $ projects s}) XS.modify (\s -> s {projects = Map.delete name $ projects s})
-- Remove the old workspace (if empty) and activate the new -- Remove the old workspace (if empty) and activate the new
-- workspace. The project will be activated by the log hook. -- workspace. The project will be activated by the log hook.
removeEmptyWorkspace removeEmptyWorkspace
@@ -233,8 +233,8 @@ switchProjectPrompt c = projectPrompt c switch
mkProject name dir = do mkProject name dir = do
let p = Project name dir Nothing let p = Project name dir Nothing
XS.modify $ \s -> s {projects = Map.insert name p $ projects s} XS.modify $ \s -> s {projects = Map.insert name p $ projects s}
switchProject p switchProject p
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Shift the currently focused window to the given project. -- | Shift the currently focused window to the given project.
shiftToProject :: Project -> X () shiftToProject :: Project -> X ()
@@ -258,7 +258,7 @@ projectPrompt :: XPConfig -> (ProjectTable -> ProjectName -> X ()) -> X ()
projectPrompt c f = do projectPrompt c f = do
ws <- map W.tag `fmap` gets (W.workspaces . windowset) ws <- map W.tag `fmap` gets (W.workspaces . windowset)
ps <- XS.gets projects ps <- XS.gets projects
let names = sort (Map.keys ps `union` ws) let names = sort (Map.keys ps `union` ws)
mkXPrompt (Wor "Project: ") c (mkComplFunFromList' names) (f ps) mkXPrompt (Wor "Project: ") c (mkComplFunFromList' names) (f ps)
@@ -270,10 +270,10 @@ activateProject :: Project -> X ()
activateProject p = do activateProject p = do
ws <- gets (W.integrate' . W.stack . W.workspace . W.current . windowset) ws <- gets (W.integrate' . W.stack . W.workspace . W.current . windowset)
home <- io getHomeDirectory home <- io getHomeDirectory
-- Change to the project's directory. -- Change to the project's directory.
catchIO (setCurrentDirectory $ expandHome home $ projectDirectory p) catchIO (setCurrentDirectory $ expandHome home $ projectDirectory p)
-- Possibly run the project's startup hook. -- Possibly run the project's startup hook.
when (null ws) $ fromMaybe (return ()) (projectStartHook p) when (null ws) $ fromMaybe (return ()) (projectStartHook p)
@@ -287,5 +287,5 @@ activateProject p = do
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Default project. -- | Default project.
defProject :: ProjectName -> Project defProject :: ProjectName -> Project
defProject name = Project name "~/" Nothing defProject name = Project name "~/" Nothing