Merge branch 'hlint'

This commit is contained in:
Tomas Janousek 2022-06-25 18:48:11 +01:00
commit 45a89130d9
25 changed files with 143 additions and 102 deletions

View File

@ -160,6 +160,11 @@ jobs:
- name: update cabal index - name: update cabal index
run: | run: |
$CABAL v2-update -v $CABAL v2-update -v
- name: cache (tools)
uses: actions/cache@v2
with:
key: ${{ runner.os }}-${{ matrix.compiler }}-tools-c0dbbd39
path: ~/.haskell-ci-tools
- name: install cabal-plan - name: install cabal-plan
run: | run: |
mkdir -p $HOME/.cabal/bin mkdir -p $HOME/.cabal/bin
@ -169,6 +174,12 @@ jobs:
rm -f cabal-plan.xz rm -f cabal-plan.xz
chmod a+x $HOME/.cabal/bin/cabal-plan chmod a+x $HOME/.cabal/bin/cabal-plan
cabal-plan --version cabal-plan --version
- name: install hlint
run: |
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then HLINTVER=$(cd /tmp && (${CABAL} v2-install -v $ARG_COMPILER --dry-run hlint --constraint='hlint >=3.4 && <3.5' | perl -ne 'if (/\bhlint-(\d+(\.\d+)*)\b/) { print "$1"; last; }')); echo "HLint version $HLINTVER" ; fi
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then if [ ! -e $HOME/.haskell-ci-tools/hlint-$HLINTVER/hlint ]; then echo "Downloading HLint version $HLINTVER"; mkdir -p $HOME/.haskell-ci-tools; curl --write-out 'Status Code: %{http_code} Redirects: %{num_redirects} Total time: %{time_total} Total Dsize: %{size_download}\n' --silent --location --output $HOME/.haskell-ci-tools/hlint-$HLINTVER.tar.gz "https://github.com/ndmitchell/hlint/releases/download/v$HLINTVER/hlint-$HLINTVER-x86_64-linux.tar.gz"; tar -xzv -f $HOME/.haskell-ci-tools/hlint-$HLINTVER.tar.gz -C $HOME/.haskell-ci-tools; fi ; fi
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then mkdir -p $CABAL_DIR/bin && ln -sf "$HOME/.haskell-ci-tools/hlint-$HLINTVER/hlint" $CABAL_DIR/bin/hlint ; fi
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then hlint --version ; fi
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
@ -228,6 +239,10 @@ jobs:
- name: tests - name: tests
run: | run: |
$CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
- name: hlint
run: |
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then (cd ${PKGDIR_xmonad} && hlint -h ${GITHUB_WORKSPACE}/source/.hlint.yaml -XHaskell2010 src) ; fi
if [ $((HCNUMVER >= 90000 && HCNUMVER < 90200)) -ne 0 ] ; then (cd ${PKGDIR_xmonad} && hlint -h ${GITHUB_WORKSPACE}/source/.hlint.yaml -XHaskell2010 .) ; fi
- name: cabal check - name: cabal check
run: | run: |
cd ${PKGDIR_xmonad} || false cd ${PKGDIR_xmonad} || false

2
.hlint.yaml Normal file
View File

@ -0,0 +1,2 @@
# Ignore these warnings.
- ignore: {name: "Use camelCase"}

View File

@ -5,6 +5,11 @@ apt:
libxrandr-dev libxrandr-dev
libxss-dev libxss-dev
hlint: True
hlint-job: 9.0.2
hlint-yaml: .hlint.yaml
hlint-version: ==3.4.*
github-patches: github-patches:
.github/workflows/haskell-ci-hackage.patch .github/workflows/haskell-ci-hackage.patch

View File

@ -123,7 +123,7 @@ myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
-- , ((modm , xK_b ), sendMessage ToggleStruts) -- , ((modm , xK_b ), sendMessage ToggleStruts)
-- Quit xmonad -- Quit xmonad
, ((modm .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) , ((modm .|. shiftMask, xK_q ), io exitSuccess)
-- Restart xmonad -- Restart xmonad
, ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart") , ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
@ -154,18 +154,18 @@ myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Mouse bindings: default actions bound to mouse events -- Mouse bindings: default actions bound to mouse events
-- --
myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList $ myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList
-- mod-button1, Set the window to floating mode and move by dragging -- mod-button1, Set the window to floating mode and move by dragging
[ ((modm, button1), (\w -> focus w >> mouseMoveWindow w [ ((modm, button1), \w -> focus w >> mouseMoveWindow w
>> windows W.shiftMaster)) >> windows W.shiftMaster)
-- mod-button2, Raise the window to the top of the stack -- mod-button2, Raise the window to the top of the stack
, ((modm, button2), (\w -> focus w >> windows W.shiftMaster)) , ((modm, button2), \w -> focus w >> windows W.shiftMaster)
-- mod-button3, Set the window to floating mode and resize by dragging -- mod-button3, Set the window to floating mode and resize by dragging
, ((modm, button3), (\w -> focus w >> mouseResizeWindow w , ((modm, button3), \w -> focus w >> mouseResizeWindow w
>> windows W.shiftMaster)) >> windows W.shiftMaster)
-- you may also bind events to the mouse scroll wheel (button4 and button5) -- you may also bind events to the mouse scroll wheel (button4 and button5)
] ]

View File

@ -218,7 +218,7 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
, ((modMask , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area , ((modMask , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area
-- quit, or restart -- quit, or restart
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad , ((modMask .|. shiftMask, xK_q ), io exitSuccess) -- %! Quit xmonad
, ((modMask , xK_q ), spawn "if type xmonad; then xmonad --recompile && xmonad --restart; else xmessage xmonad not in \\$PATH: \"$PATH\"; fi") -- %! Restart xmonad , ((modMask , xK_q ), spawn "if type xmonad; then xmonad --recompile && xmonad --restart; else xmessage xmonad not in \\$PATH: \"$PATH\"; fi") -- %! Restart xmonad
, ((modMask .|. shiftMask, xK_slash ), helpCommand) -- %! Run xmessage with a summary of the default keybindings (useful for beginners) , ((modMask .|. shiftMask, xK_slash ), helpCommand) -- %! Run xmessage with a summary of the default keybindings (useful for beginners)

View File

@ -1,6 +1,11 @@
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving, {-# LANGUAGE DeriveTraversable #-}
MultiParamTypeClasses, TypeSynonymInstances, DeriveDataTypeable, {-# LANGUAGE ExistentialQuantification #-}
LambdaCase, NamedFieldPuns, DeriveTraversable #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- | -- |
@ -35,17 +40,17 @@ module XMonad.Core (
import XMonad.StackSet hiding (modify) import XMonad.StackSet hiding (modify)
import Prelude import Prelude
import Control.Exception (fromException, try, bracket, bracket_, throw, finally, SomeException(..)) import Control.Exception (fromException, try, bracket_, throw, finally, SomeException(..))
import qualified Control.Exception as E import qualified Control.Exception as E
import Control.Applicative ((<|>), empty) import Control.Applicative ((<|>), empty)
import Control.Monad.Fail import Control.Monad.Fail
import Control.Monad.State import Control.Monad.State
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad (void)
import Data.Semigroup import Data.Semigroup
import Data.Traversable (for) import Data.Traversable (for)
import Data.Time.Clock (UTCTime) import Data.Time.Clock (UTCTime)
import Data.Default.Class import Data.Default.Class
import Data.List (isInfixOf)
import System.FilePath import System.FilePath
import System.IO import System.IO
import System.Info import System.Info
@ -60,7 +65,7 @@ import System.Exit
import Graphics.X11.Xlib import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event) import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event)
import Data.Typeable import Data.Typeable
import Data.List ((\\)) import Data.List (isInfixOf, (\\))
import Data.Maybe (isJust,fromMaybe) import Data.Maybe (isJust,fromMaybe)
import qualified Data.Map as M import qualified Data.Map as M
@ -172,7 +177,7 @@ newtype Query a = Query (ReaderT Window X a)
deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO) deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO)
runQuery :: Query a -> Window -> X a runQuery :: Query a -> Window -> X a
runQuery (Query m) w = runReaderT m w runQuery (Query m) = runReaderT m
instance Semigroup a => Semigroup (Query a) where instance Semigroup a => Semigroup (Query a) where
(<>) = liftM2 (<>) (<>) = liftM2 (<>)
@ -195,7 +200,7 @@ catchX job errcase = do
st <- get st <- get
c <- ask c <- ask
(a, s') <- io $ runX c st job `E.catch` \e -> case fromException e of (a, s') <- io $ runX c st job `E.catch` \e -> case fromException e of
Just x -> throw e `const` (x `asTypeOf` ExitSuccess) Just (_ :: ExitCode) -> throw e
_ -> do hPrint stderr e; runX c st errcase _ -> do hPrint stderr e; runX c st errcase
put s' put s'
return a return a
@ -203,12 +208,12 @@ catchX job errcase = do
-- | Execute the argument, catching all exceptions. Either this function or -- | Execute the argument, catching all exceptions. Either this function or
-- 'catchX' should be used at all callsites of user customized code. -- 'catchX' should be used at all callsites of user customized code.
userCode :: X a -> X (Maybe a) userCode :: X a -> X (Maybe a)
userCode a = catchX (Just `liftM` a) (return Nothing) userCode a = catchX (Just <$> a) (return Nothing)
-- | Same as userCode but with a default argument to return instead of using -- | Same as userCode but with a default argument to return instead of using
-- Maybe, provided for convenience. -- Maybe, provided for convenience.
userCodeDef :: a -> X a -> X a userCodeDef :: a -> X a -> X a
userCodeDef defValue a = fromMaybe defValue `liftM` userCode a userCodeDef defValue a = fromMaybe defValue <$> userCode a
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- Convenient wrappers to state -- Convenient wrappers to state
@ -229,7 +234,7 @@ withWindowAttributes dpy win f = do
-- | True if the given window is the root window -- | True if the given window is the root window
isRoot :: Window -> X Bool isRoot :: Window -> X Bool
isRoot w = (w==) <$> asks theRoot isRoot w = asks $ (w ==) . theRoot
-- | Wrapper for the common case of atom internment -- | Wrapper for the common case of atom internment
getAtom :: String -> X Atom getAtom :: String -> X Atom
@ -431,7 +436,7 @@ catchIO f = io (f `E.catch` \(SomeException e) -> hPrint stderr e >> hFlush stde
-- --
-- Note this function assumes your locale uses utf8. -- Note this function assumes your locale uses utf8.
spawn :: MonadIO m => String -> m () spawn :: MonadIO m => String -> m ()
spawn x = spawnPID x >> return () spawn x = void $ spawnPID x
-- | Like 'spawn', but returns the 'ProcessID' of the launched application -- | Like 'spawn', but returns the 'ProcessID' of the launched application
spawnPID :: MonadIO m => String -> m ProcessID spawnPID :: MonadIO m => String -> m ProcessID
@ -645,7 +650,7 @@ getModTime f = E.catch (Just <$> getModificationTime f) (\(SomeException _) -> r
compile :: Directories -> Compile -> IO ExitCode compile :: Directories -> Compile -> IO ExitCode
compile dirs method = compile dirs method =
bracket_ uninstallSignalHandlers installSignalHandlers $ bracket_ uninstallSignalHandlers installSignalHandlers $
bracket (openFile (errFileName dirs) WriteMode) hClose $ \err -> do withFile (errFileName dirs) WriteMode $ \err -> do
let run = runProc (cfgDir dirs) err let run = runProc (cfgDir dirs) err
case method of case method of
CompileGhc -> CompileGhc ->

View File

@ -1,4 +1,6 @@
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances, DeriveDataTypeable, LambdaCase, MultiWayIf #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternGuards #-}
-- -------------------------------------------------------------------------- -- --------------------------------------------------------------------------
-- | -- |
@ -39,7 +41,7 @@ import Data.Maybe (fromMaybe)
data Resize = Shrink | Expand data Resize = Shrink | Expand
-- | Increase the number of clients in the master pane. -- | Increase the number of clients in the master pane.
data IncMasterN = IncMasterN !Int newtype IncMasterN = IncMasterN Int
instance Message Resize instance Message Resize
instance Message IncMasterN instance Message IncMasterN
@ -199,8 +201,8 @@ choose (Choose d l r) d' ml mr = f lr
(CL, CR) -> (hide l' , return r') (CL, CR) -> (hide l' , return r')
(CR, CL) -> (return l', hide r' ) (CR, CL) -> (return l', hide r' )
(_ , _ ) -> (return l', return r') (_ , _ ) -> (return l', return r')
f (x,y) = fmap Just $ liftM2 (Choose d') x y f (x,y) = Just <$> liftM2 (Choose d') x y
hide x = fmap (fromMaybe x) $ handle x Hide hide x = fromMaybe x <$> handle x Hide
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
runLayout (W.Workspace i (Choose CL l r) ms) = runLayout (W.Workspace i (Choose CL l r) ms) =

View File

@ -1,4 +1,6 @@
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, NamedFieldPuns #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
-- | -- |
-- Module : XMonad.Main -- Module : XMonad.Main
@ -87,14 +89,14 @@ usage :: IO ()
usage = do usage = do
self <- getProgName self <- getProgName
putStr . unlines $ putStr . unlines $
concat ["Usage: ", self, " [OPTION]"] : [ "Usage: " <> self <> " [OPTION]"
"Options:" : , "Options:"
" --help Print this message" : , " --help Print this message"
" --version Print the version number" : , " --version Print the version number"
" --recompile Recompile your xmonad.hs" : , " --recompile Recompile your xmonad.hs"
" --replace Replace the running window manager with xmonad" : , " --replace Replace the running window manager with xmonad"
" --restart Request a running xmonad process to restart" : , " --restart Request a running xmonad process to restart"
[] ]
-- | Build the xmonad configuration file with ghc, then execute it. -- | Build the xmonad configuration file with ghc, then execute it.
-- If there are no errors, this function does not return. An -- If there are no errors, this function does not return. An
@ -330,7 +332,7 @@ handle e@(DestroyWindowEvent {ev_window = w}) = do
-- it is synthetic or we are not expecting an unmap notification from a window. -- it is synthetic or we are not expecting an unmap notification from a window.
handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient w) $ do handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient w) $ do
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap) e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
if (synthetic || e == 0) if synthetic || e == 0
then unmanage w then unmanage w
else modify (\s -> s { waitingUnmap = M.update mpred w (waitingUnmap s) }) else modify (\s -> s { waitingUnmap = M.update mpred w (waitingUnmap s) })
where mpred 1 = Nothing where mpred 1 = Nothing
@ -428,7 +430,7 @@ handle event@(PropertyEvent { ev_event_type = t, ev_atom = a })
handle e@ClientMessageEvent { ev_message_type = mt } = do handle e@ClientMessageEvent { ev_message_type = mt } = do
a <- getAtom "XMONAD_RESTART" a <- getAtom "XMONAD_RESTART"
if (mt == a) if mt == a
then restart "xmonad" True then restart "xmonad" True
else broadcastMessage e else broadcastMessage e
@ -501,7 +503,7 @@ grabButtons = do
io $ ungrabButton dpy anyButton anyModifier rootw io $ ungrabButton dpy anyButton anyModifier rootw
ems <- extraModifiers ems <- extraModifiers
ba <- asks buttonActions ba <- asks buttonActions
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys $ ba) mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys ba)
-- | @replace@ to signals compliant window managers to exit. -- | @replace@ to signals compliant window managers to exit.
replace :: Display -> ScreenNumber -> Window -> IO () replace :: Display -> ScreenNumber -> Window -> IO ()

View File

@ -1,5 +1,3 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- | -- |
-- Module : XMonad.ManageHook -- Module : XMonad.ManageHook
@ -8,7 +6,6 @@
-- --
-- Maintainer : spencerjanssen@gmail.com -- Maintainer : spencerjanssen@gmail.com
-- Stability : unstable -- Stability : unstable
-- Portability : not portable, uses cunning newtype deriving
-- --
-- An EDSL for ManageHooks -- An EDSL for ManageHooks
-- --
@ -65,7 +62,7 @@ infixr 3 <&&>, <||>
-- | '||' lifted to a 'Monad'. -- | '||' lifted to a 'Monad'.
(<||>) :: Monad m => m Bool -> m Bool -> m Bool (<||>) :: Monad m => m Bool -> m Bool -> m Bool
(<||>) x y = ifM x (pure True) y (<||>) x = ifM x (pure True)
-- | If-then-else lifted to a 'Monad'. -- | If-then-else lifted to a 'Monad'.
ifM :: Monad m => m Bool -> m a -> m a -> m a ifM :: Monad m => m Bool -> m a -> m a -> m a
@ -98,7 +95,7 @@ className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getC
-- | A query that can return an arbitrary X property of type 'String', -- | A query that can return an arbitrary X property of type 'String',
-- identified by name. -- identified by name.
stringProperty :: String -> Query String stringProperty :: String -> Query String
stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap (fromMaybe "") $ getStringProperty d w p) stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fromMaybe "" <$> getStringProperty d w p)
getStringProperty :: Display -> Window -> String -> X (Maybe String) getStringProperty :: Display -> Window -> String -> X (Maybe String)
getStringProperty d w p = do getStringProperty d w p = do

View File

@ -1,4 +1,10 @@
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, PatternGuards, LambdaCase #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- -------------------------------------------------------------------------- -- --------------------------------------------------------------------------
-- | -- |
-- Module : XMonad.Operations -- Module : XMonad.Operations
@ -66,6 +72,7 @@ import qualified Data.Set as S
import Control.Arrow (second) import Control.Arrow (second)
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import Control.Monad (void)
import qualified Control.Exception as C import qualified Control.Exception as C
import System.IO import System.IO
@ -136,7 +143,7 @@ killWindow w = withDisplay $ \d -> do
setEventType ev clientMessage setEventType ev clientMessage
setClientMessageEvent ev w wmprot 32 wmdelt currentTime setClientMessageEvent ev w wmprot 32 wmdelt currentTime
sendEvent d w False noEventMask ev sendEvent d w False noEventMask ev
else killClient d w >> return () else void (killClient d w)
-- | Kill the currently focused client. -- | Kill the currently focused client.
kill :: X () kill :: X ()
@ -187,7 +194,7 @@ windows f = do
let m = W.floating ws let m = W.floating ws
flt = [(fw, scaleRationalRect viewrect r) flt = [(fw, scaleRationalRect viewrect r)
| fw <- filter (flip M.member m) (W.index this) | fw <- filter (`M.member` m) (W.index this)
, Just r <- [M.lookup fw m]] , Just r <- [M.lookup fw m]]
vs = flt ++ rs vs = flt ++ rs
@ -213,7 +220,7 @@ windows f = do
-- all windows that are no longer in the windowset are marked as -- all windows that are no longer in the windowset are marked as
-- withdrawn, it is important to do this after the above, otherwise 'hide' -- withdrawn, it is important to do this after the above, otherwise 'hide'
-- will overwrite withdrawnState with iconicState -- will overwrite withdrawnState with iconicState
mapM_ (flip setWMState withdrawnState) (W.allWindows old \\ W.allWindows ws) mapM_ (`setWMState` withdrawnState) (W.allWindows old \\ W.allWindows ws)
isMouseFocused <- asks mouseFocused isMouseFocused <- asks mouseFocused
unless isMouseFocused $ clearEvents enterWindowMask unless isMouseFocused $ clearEvents enterWindowMask
@ -229,8 +236,8 @@ windowBracket :: (a -> Bool) -> X a -> X a
windowBracket p action = withWindowSet $ \old -> do windowBracket p action = withWindowSet $ \old -> do
a <- action a <- action
when (p a) . withWindowSet $ \new -> do when (p a) . withWindowSet $ \new -> do
modifyWindowSet $ \_ -> old modifyWindowSet $ const old
windows $ \_ -> new windows $ const new
return a return a
-- | Perform an @X@ action. If it returns @Any True@, unwind the -- | Perform an @X@ action. If it returns @Any True@, unwind the
@ -417,7 +424,7 @@ setFocusX w = withWindowSet $ \ws -> do
currevt <- asks currentEvent currevt <- asks currentEvent
let inputHintSet = wmh_flags hints `testBit` inputHintBit let inputHintSet = wmh_flags hints `testBit` inputHintBit
when ((inputHintSet && wmh_input hints) || (not inputHintSet)) $ when (inputHintSet && wmh_input hints || not inputHintSet) $
io $ do setInputFocus dpy w revertToPointerRoot 0 io $ do setInputFocus dpy w revertToPointerRoot 0
when (wmtf `elem` protocols) $ when (wmtf `elem` protocols) $
io $ allocaXEvent $ \ev -> do io $ allocaXEvent $ \ev -> do
@ -425,7 +432,7 @@ setFocusX w = withWindowSet $ \ws -> do
setClientMessageEvent ev w wmprot 32 wmtf $ maybe currentTime event_time currevt setClientMessageEvent ev w wmprot 32 wmtf $ maybe currentTime event_time currevt
sendEvent dpy w False noEventMask ev sendEvent dpy w False noEventMask ev
where event_time ev = where event_time ev =
if (ev_event_type ev) `elem` timedEvents then if ev_event_type ev `elem` timedEvents then
ev_time ev ev_time ev
else else
currentTime currentTime
@ -438,7 +445,7 @@ setFocusX w = withWindowSet $ \ws -> do
-- layout the windows, in which case changes are handled through a refresh. -- layout the windows, in which case changes are handled through a refresh.
sendMessage :: Message a => a -> X () sendMessage :: Message a => a -> X ()
sendMessage a = windowBracket_ $ do sendMessage a = windowBracket_ $ do
w <- W.workspace . W.current <$> gets windowset w <- gets $ W.workspace . W.current . windowset
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
whenJust ml' $ \l' -> whenJust ml' $ \l' ->
modifyWindowSet $ \ws -> ws { W.current = (W.current ws) modifyWindowSet $ \ws -> ws { W.current = (W.current ws)
@ -473,9 +480,9 @@ updateLayout i ml = whenJust ml $ \l ->
-- | Set the layout of the currently viewed workspace. -- | Set the layout of the currently viewed workspace.
setLayout :: Layout Window -> X () setLayout :: Layout Window -> X ()
setLayout l = do setLayout l = do
ss@(W.StackSet { W.current = c@(W.Screen { W.workspace = ws })}) <- gets windowset ss@W.StackSet{ W.current = c@W.Screen{ W.workspace = ws }} <- gets windowset
handleMessage (W.layout ws) (SomeMessage ReleaseResources) handleMessage (W.layout ws) (SomeMessage ReleaseResources)
windows $ const $ ss {W.current = c { W.workspace = ws { W.layout = l } } } windows $ const $ ss{ W.current = c{ W.workspace = ws{ W.layout = l } } }
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Utilities -- Utilities
@ -515,7 +522,7 @@ cleanMask km = do
-- | Get the 'Pixel' value for a named color. -- | Get the 'Pixel' value for a named color.
initColor :: Display -> String -> IO (Maybe Pixel) initColor :: Display -> String -> IO (Maybe Pixel)
initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $ initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
(Just . color_pixel . fst) <$> allocNamedColor dpy colormap c Just . color_pixel . fst <$> allocNamedColor dpy colormap c
where colormap = defaultColormap dpy (defaultScreen dpy) where colormap = defaultColormap dpy (defaultScreen dpy)
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -535,7 +542,7 @@ writeStateToFile = do
maybeShow _ = Nothing maybeShow _ = Nothing
wsData = W.mapLayout show . windowset wsData = W.mapLayout show . windowset
extState = catMaybes . map maybeShow . M.toList . extensibleState extState = mapMaybe maybeShow . M.toList . extensibleState
path <- asks $ stateFileName . directories path <- asks $ stateFileName . directories
stateData <- gets (\s -> StateFile (wsData s) (extState s)) stateData <- gets (\s -> StateFile (wsData s) (extState s))
@ -598,11 +605,10 @@ floatLocation w =
catchX go $ do catchX go $ do
-- Fallback solution if `go' fails. Which it might, since it -- Fallback solution if `go' fails. Which it might, since it
-- calls `getWindowAttributes'. -- calls `getWindowAttributes'.
sc <- W.current <$> gets windowset sc <- gets $ W.current . windowset
return (W.screen sc, W.RationalRect 0 0 1 1) return (W.screen sc, W.RationalRect 0 0 1 1)
where fi x = fromIntegral x where go = withDisplay $ \d -> do
go = withDisplay $ \d -> do
ws <- gets windowset ws <- gets windowset
wa <- io $ getWindowAttributes d w wa <- io $ getWindowAttributes d w
let bw = (fromIntegral . wa_border_width) wa let bw = (fromIntegral . wa_border_width) wa
@ -628,6 +634,9 @@ floatLocation w =
return (W.screen sc, rr) return (W.screen sc, rr)
fi :: (Integral a, Num b) => a -> b
fi = fromIntegral
-- | Given a point, determine the screen (if any) that contains it. -- | Given a point, determine the screen (if any) that contains it.
pointScreen :: Position -> Position pointScreen :: Position -> Position
-> X (Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail)) -> X (Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail))
@ -726,7 +735,7 @@ mkAdjust w = withDisplay $ \d -> liftIO $ do
sh <- getWMNormalHints d w sh <- getWMNormalHints d w
wa <- C.try $ getWindowAttributes d w wa <- C.try $ getWindowAttributes d w
case wa of case wa of
Left err -> const (return id) (err :: C.SomeException) Left (_ :: C.SomeException) -> return id
Right wa' -> Right wa' ->
let bw = fromIntegral $ wa_border_width wa' let bw = fromIntegral $ wa_border_width wa'
in return $ applySizeHints bw sh in return $ applySizeHints bw sh

View File

@ -240,7 +240,7 @@ view i s
| otherwise = s -- not a member of the stackset | otherwise = s -- not a member of the stackset
where equating f = \x y -> f x == f y where equating f x y = f x == f y
-- 'Catch'ing this might be hard. Relies on monotonically increasing -- 'Catch'ing this might be hard. Relies on monotonically increasing
-- workspace tags defined in 'new' -- workspace tags defined in 'new'

View File

@ -36,7 +36,7 @@ instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary l, Arbitrary sd)
-- Pick a random window "number" in each workspace, to give focus. -- Pick a random window "number" in each workspace, to give focus.
focus <- sequence [ if null windows focus <- sequence [ if null windows
then return Nothing then return Nothing
else liftM Just $ choose (0, length windows - 1) else Just <$> choose (0, length windows - 1)
| windows <- wsWindows ] | windows <- wsWindows ]
let tags = [1 .. fromIntegral numWs] let tags = [1 .. fromIntegral numWs]
@ -80,7 +80,7 @@ newtype NonEmptyWindowsStackSet = NonEmptyWindowsStackSet T
instance Arbitrary NonEmptyWindowsStackSet where instance Arbitrary NonEmptyWindowsStackSet where
arbitrary = arbitrary =
NonEmptyWindowsStackSet `fmap` (arbitrary `suchThat` (not . null . allWindows)) NonEmptyWindowsStackSet <$> (arbitrary `suchThat` (not . null . allWindows))
instance Arbitrary Rectangle where instance Arbitrary Rectangle where
arbitrary = Rectangle <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary arbitrary = Rectangle <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
@ -99,7 +99,7 @@ newtype NonEmptyNubList a = NonEmptyNubList [a]
deriving ( Eq, Ord, Show, Read ) deriving ( Eq, Ord, Show, Read )
instance (Eq a, Arbitrary a) => Arbitrary (NonEmptyNubList a) where instance (Eq a, Arbitrary a) => Arbitrary (NonEmptyNubList a) where
arbitrary = NonEmptyNubList `fmap` ((liftM nub arbitrary) `suchThat` (not . null)) arbitrary = NonEmptyNubList <$> ((nub <$> arbitrary) `suchThat` (not . null))
@ -116,7 +116,7 @@ arbitraryTag :: T -> Gen Tag
arbitraryTag x = do arbitraryTag x = do
let ts = tags x let ts = tags x
-- There must be at least 1 workspace, thus at least 1 tag. -- There must be at least 1 workspace, thus at least 1 tag.
idx <- choose (0, (length ts) - 1) idx <- choose (0, length ts - 1)
return $ ts!!idx return $ ts!!idx
-- | Pull out an arbitrary window from a StackSet that is guaranteed to have a -- | Pull out an arbitrary window from a StackSet that is guaranteed to have a
@ -136,5 +136,5 @@ arbitraryWindow :: NonEmptyWindowsStackSet -> Gen Window
arbitraryWindow (NonEmptyWindowsStackSet x) = do arbitraryWindow (NonEmptyWindowsStackSet x) = do
let ws = allWindows x let ws = allWindows x
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet. -- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
idx <- choose(0, (length ws) - 1) idx <- choose (0, length ws - 1)
return $ ws!!idx return $ ws!!idx

View File

@ -64,7 +64,7 @@ prop_delete_focus_not_end = do
-- last one in the stack. -- last one in the stack.
`suchThat` \(x' :: T) -> `suchThat` \(x' :: T) ->
let currWins = index x' let currWins = index x'
in length (currWins) >= 2 && peek x' /= Just (last currWins) in length currWins >= 2 && peek x' /= Just (last currWins)
-- This is safe, as we know there are >= 2 windows -- This is safe, as we know there are >= 2 windows
let Just n = peek x let Just n = peek x
return $ peek (delete n x) == peek (focusDown x) return $ peek (delete n x) == peek (focusDown x)

View File

@ -32,8 +32,8 @@ prop_focusWindow_master (NonNegative n) (x :: T) =
in index (focusWindow (s !! i) x) == index x in index (focusWindow (s !! i) x) == index x
-- shifting focus is trivially reversible -- shifting focus is trivially reversible
prop_focus_left (x :: T) = (focusUp (focusDown x)) == x prop_focus_left (x :: T) = focusUp (focusDown x) == x
prop_focus_right (x :: T) = (focusDown (focusUp x)) == x prop_focus_right (x :: T) = focusDown (focusUp x) == x
-- focus master is idempotent -- focus master is idempotent
prop_focusMaster_idem (x :: T) = focusMaster x == focusMaster (focusMaster x) prop_focusMaster_idem (x :: T) = focusMaster x == focusMaster (focusMaster x)
@ -47,9 +47,9 @@ prop_focusWindow_works (NonNegative (n :: Int)) (x :: T) =
in (focus . fromJust . stack . workspace . current) (focusWindow (s !! i) x) == (s !! i) in (focus . fromJust . stack . workspace . current) (focusWindow (s !! i) x) == (s !! i)
-- rotation through the height of a stack gets us back to the start -- rotation through the height of a stack gets us back to the start
prop_focus_all_l (x :: T) = (foldr (const focusUp) x [1..n]) == x prop_focus_all_l (x :: T) = foldr (const focusUp) x [1..n] == x
where n = length (index x) where n = length (index x)
prop_focus_all_r (x :: T) = (foldr (const focusDown) x [1..n]) == x prop_focus_all_r (x :: T) = foldr (const focusDown) x [1..n] == x
where n = length (index x) where n = length (index x)
-- prop_rotate_all (x :: T) = f (f x) == f x -- prop_rotate_all (x :: T) = f (f x) == f x

View File

@ -35,7 +35,7 @@ prop_greedyView_local (x :: T) = do
-- greedyView is idempotent -- greedyView is idempotent
prop_greedyView_idem (x :: T) = do prop_greedyView_idem (x :: T) = do
n <- arbitraryTag x n <- arbitraryTag x
return $ greedyView n (greedyView n x) == (greedyView n x) return $ greedyView n (greedyView n x) == greedyView n x
-- greedyView is reversible, though shuffles the order of hidden/visible -- greedyView is reversible, though shuffles the order of hidden/visible
prop_greedyView_reversible (x :: T) = do prop_greedyView_reversible (x :: T) = do

View File

@ -46,7 +46,7 @@ prop_insert_delete x = do
-- inserting n elements increases current stack size by n -- inserting n elements increases current stack size by n
prop_size_insert is (EmptyStackSet x) = prop_size_insert is (EmptyStackSet x) =
size (foldr insertUp x ws ) == (length ws) size (foldr insertUp x ws) == length ws
where where
ws = nub is ws = nub is
size = length . index size = length . index

View File

@ -29,6 +29,6 @@ prop_purelayout_full rect = do
-- what happens when we send an IncMaster message to Full --- Nothing -- what happens when we send an IncMaster message to Full --- Nothing
prop_sendmsg_full (NonNegative k) = prop_sendmsg_full (NonNegative k) =
isNothing (Full `pureMessage` (SomeMessage (IncMasterN k))) isNothing (Full `pureMessage` SomeMessage (IncMasterN k))
prop_desc_full = description Full == show Full prop_desc_full = description Full == show Full

View File

@ -29,12 +29,12 @@ prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster w
-- splitting horizontally yields sensible results -- splitting horizontally yields sensible results
prop_split_horizontal (NonNegative n) x = prop_split_horizontal (NonNegative n) x =
(noOverflows (+) (rect_x x) (rect_width x)) ==> noOverflows (+) (rect_x x) (rect_width x) ==>
sum (map rect_width xs) == rect_width x sum (map rect_width xs) == rect_width x
&& &&
all (== rect_height x) (map rect_height xs) all (\s -> rect_height s == rect_height x) xs
&& &&
(map rect_x xs) == (sort $ map rect_x xs) map rect_x xs == sort (map rect_x xs)
where where
xs = splitHorizontally n x xs = splitHorizontally n x
@ -72,7 +72,7 @@ prop_shrink_tall (NonNegative n) (Positive delta) (NonNegative frac) =
-- remaining fraction should shrink -- remaining fraction should shrink
where where
l1 = Tall n delta frac l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Shrink) Just l2@(Tall n' delta' frac') = l1 `pureMessage` SomeMessage Shrink
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a) -- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
@ -93,7 +93,7 @@ prop_expand_tall (NonNegative n)
where where
frac = min 1 (n1 % d1) frac = min 1 (n1 % d1)
l1 = Tall n delta frac l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Expand) Just l2@(Tall n' delta' frac') = l1 `pureMessage` SomeMessage Expand
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a) -- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- what happens when we send an IncMaster message to Tall -- what happens when we send an IncMaster message to Tall
@ -102,7 +102,7 @@ prop_incmaster_tall (NonNegative n) (Positive delta) (NonNegative frac)
delta == delta' && frac == frac' && n' == n + k delta == delta' && frac == frac' && n' == n + k
where where
l1 = Tall n delta frac l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage (IncMasterN k)) Just l2@(Tall n' delta' frac') = l1 `pureMessage` SomeMessage (IncMasterN k)
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a) -- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)

View File

@ -53,8 +53,8 @@ prop_aspect_hint_shrink hint (w,h) = case applyAspectHint hint (w,h) of
-- the desired range -- the desired range
prop_aspect_fits = prop_aspect_fits =
forAll ((,,,) <$> pos <*> pos <*> pos <*> pos) $ \ (x,y,a,b) -> forAll ((,,,) <$> pos <*> pos <*> pos <*> pos) $ \ (x,y,a,b) ->
let f v = applyAspectHint ((x, y+a), (x+b, y)) v let f = applyAspectHint ((x, y+a), (x+b, y))
in and [ noOverflows (*) x (y+a), noOverflows (*) (x+b) y ] in noOverflows (*) x (y+a) && noOverflows (*) (x+b) y
==> f (x,y) == (x,y) ==> f (x,y) == (x,y)
where pos = choose (0, 65535) where pos = choose (0, 65535)

View File

@ -27,7 +27,7 @@ prop_shift_reversible (x :: T) = do
-- shiftMaster -- shiftMaster
-- focus/local/idempotent same as swapMaster: -- focus/local/idempotent same as swapMaster:
prop_shift_master_focus (x :: T) = peek x == (peek $ shiftMaster x) prop_shift_master_focus (x :: T) = peek x == peek (shiftMaster x)
prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x) prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x)
prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x
-- ordering is constant modulo the focused window: -- ordering is constant modulo the focused window:
@ -57,14 +57,14 @@ prop_shift_win_fix_current = do
x <- arbitrary `suchThat` \(x' :: T) -> x <- arbitrary `suchThat` \(x' :: T) ->
-- Invariant, otherWindows are NOT in the current workspace. -- Invariant, otherWindows are NOT in the current workspace.
let otherWindows = allWindows x' L.\\ index x' let otherWindows = allWindows x' L.\\ index x'
in length(tags x') >= 2 && length(otherWindows) >= 1 in length (tags x') >= 2 && not (null otherWindows)
-- Sadly we have to construct `otherWindows` again, for the actual StackSet -- Sadly we have to construct `otherWindows` again, for the actual StackSet
-- that got chosen. -- that got chosen.
let otherWindows = allWindows x L.\\ index x let otherWindows = allWindows x L.\\ index x
-- We know such tag must exists, due to the precondition -- We know such tag must exists, due to the precondition
n <- arbitraryTag x `suchThat` (/= currentTag x) n <- arbitraryTag x `suchThat` (/= currentTag x)
-- we know length is >= 1, from above precondition -- we know length is >= 1, from above precondition
idx <- choose(0, length(otherWindows) - 1) idx <- choose (0, length otherWindows - 1)
let w = otherWindows !! idx let w = otherWindows !! idx
return $ (current $ x) == (current $ shiftWin n w x) return $ current x == current (shiftWin n w x)

View File

@ -1,6 +1,10 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
#ifdef VERSION_quickcheck_classes
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
#endif
module Properties.Stack where module Properties.Stack where
import Test.QuickCheck import Test.QuickCheck
@ -24,7 +28,7 @@ import Test.QuickCheck.Classes (
-- windows kept in the zipper -- windows kept in the zipper
prop_index_length (x :: T) = prop_index_length (x :: T) =
case stack . workspace . current $ x of case stack . workspace . current $ x of
Nothing -> length (index x) == 0 Nothing -> null (index x)
Just it -> length (index x) == length (focus it : up it ++ down it) Just it -> length (index x) == length (focus it : up it ++ down it)
@ -43,7 +47,7 @@ prop_allWindowsMember (NonEmptyWindowsStackSet x) = do
-- which is a key component in this test (together with member). -- which is a key component in this test (together with member).
let ws = allWindows x let ws = allWindows x
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet. -- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
idx <- choose(0, (length ws) - 1) idx <- choose (0, length ws - 1)
return $ member (ws!!idx) x return $ member (ws!!idx) x
@ -56,8 +60,8 @@ prop_filter_order (x :: T) =
-- differentiate should return Nothing if the list is empty or Just stack, with -- differentiate should return Nothing if the list is empty or Just stack, with
-- the first element of the list is current, and the rest of the list is down. -- the first element of the list is current, and the rest of the list is down.
prop_differentiate xs = prop_differentiate xs =
if null xs then differentiate xs == Nothing if null xs then isNothing (differentiate xs)
else (differentiate xs) == Just (Stack (head xs) [] (tail xs)) else differentiate xs == Just (Stack (head xs) [] (tail xs))
where _ = xs :: [Int] where _ = xs :: [Int]

View File

@ -58,7 +58,7 @@ invariant (s :: T) = and
-- inBounds = and [ w >=0 && w < size s | (w,sc) <- M.assocs (screens s) ] -- inBounds = and [ w >=0 && w < size s | (w,sc) <- M.assocs (screens s) ]
monotonic [] = True monotonic [] = True
monotonic (x:[]) = True monotonic [x] = True
monotonic (x:y:zs) | x == y-1 = monotonic (y:zs) monotonic (x:y:zs) | x == y-1 = monotonic (y:zs)
| otherwise = False | otherwise = False
@ -126,7 +126,7 @@ prop_empty (EmptyStackSet x) =
prop_empty_current (EmptyStackSet x) = currentTag x == head (tags x) prop_empty_current (EmptyStackSet x) = currentTag x == head (tags x)
-- no windows will be a member of an empty workspace -- no windows will be a member of an empty workspace
prop_member_empty i (EmptyStackSet x) = member i x == False prop_member_empty i (EmptyStackSet x) = not (member i x)
-- peek either yields nothing on the Empty workspace, or Just a valid window -- peek either yields nothing on the Empty workspace, or Just a valid window
prop_member_peek (x :: T) = prop_member_peek (x :: T) =

View File

@ -11,8 +11,8 @@ import XMonad.StackSet hiding (filter)
-- swapUp, swapDown, swapMaster: reordiring windows -- swapUp, swapDown, swapMaster: reordiring windows
-- swap is trivially reversible -- swap is trivially reversible
prop_swap_left (x :: T) = (swapUp (swapDown x)) == x prop_swap_left (x :: T) = swapUp (swapDown x) == x
prop_swap_right (x :: T) = (swapDown (swapUp x)) == x prop_swap_right (x :: T) = swapDown (swapUp x) == x
-- TODO swap is reversible -- TODO swap is reversible
-- swap is reversible, but involves moving focus back the window with -- swap is reversible, but involves moving focus back the window with
-- master on it. easy to do with a mouse... -- master on it. easy to do with a mouse...
@ -26,12 +26,12 @@ prop_promote_reversible x b = (not . null . fromMaybe [] . flip index x . curren
-} -}
-- swap doesn't change focus -- swap doesn't change focus
prop_swap_master_focus (x :: T) = peek x == (peek $ swapMaster x) prop_swap_master_focus (x :: T) = peek x == peek (swapMaster x)
-- = case peek x of -- = case peek x of
-- Nothing -> True -- Nothing -> True
-- Just f -> focus (stack (workspace $ current (swap x))) == f -- Just f -> focus (stack (workspace $ current (swap x))) == f
prop_swap_left_focus (x :: T) = peek x == (peek $ swapUp x) prop_swap_left_focus (x :: T) = peek x == peek (swapUp x)
prop_swap_right_focus (x :: T) = peek x == (peek $ swapDown x) prop_swap_right_focus (x :: T) = peek x == peek (swapDown x)
-- swap is local -- swap is local
prop_swap_master_local (x :: T) = hidden_spaces x == hidden_spaces (swapMaster x) prop_swap_master_local (x :: T) = hidden_spaces x == hidden_spaces (swapMaster x)
@ -39,9 +39,9 @@ prop_swap_left_local (x :: T) = hidden_spaces x == hidden_spaces (swapUp x)
prop_swap_right_local (x :: T) = hidden_spaces x == hidden_spaces (swapDown x) prop_swap_right_local (x :: T) = hidden_spaces x == hidden_spaces (swapDown x)
-- rotation through the height of a stack gets us back to the start -- rotation through the height of a stack gets us back to the start
prop_swap_all_l (x :: T) = (foldr (const swapUp) x [1..n]) == x prop_swap_all_l (x :: T) = foldr (const swapUp) x [1..n] == x
where n = length (index x) where n = length (index x)
prop_swap_all_r (x :: T) = (foldr (const swapDown) x [1..n]) == x prop_swap_all_r (x :: T) = foldr (const swapDown) x [1..n] == x
where n = length (index x) where n = length (index x)
prop_swap_master_idempotent (x :: T) = swapMaster (swapMaster x) == swapMaster x prop_swap_master_idempotent (x :: T) = swapMaster (swapMaster x) == swapMaster x

View File

@ -37,7 +37,7 @@ prop_view_local (x :: T) = do
-- view is idempotent -- view is idempotent
prop_view_idem (x :: T) = do prop_view_idem (x :: T) = do
n <- arbitraryTag x n <- arbitraryTag x
return $ view n (view n x) == (view n x) return $ view n (view n x) == view n x
-- view is reversible, though shuffles the order of hidden/visible -- view is reversible, though shuffles the order of hidden/visible
prop_view_reversible (x :: T) = do prop_view_reversible (x :: T) = do

View File

@ -12,8 +12,8 @@ hidden_spaces x = map workspace (visible x) ++ hidden x
-- normalise workspace list -- normalise workspace list
normal s = s { hidden = sortBy g (hidden s), visible = sortBy f (visible s) } normal s = s { hidden = sortBy g (hidden s), visible = sortBy f (visible s) }
where where
f = \a b -> tag (workspace a) `compare` tag (workspace b) f a b = tag (workspace a) `compare` tag (workspace b)
g = \a b -> tag a `compare` tag b g a b = tag a `compare` tag b
noOverlaps [] = True noOverlaps [] = True