mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-05-19 03:20:21 -07:00
The links were broken due to: 1. Incorrect quotes (' instead of " for module links and occasionally vice-versa). 2. Changes in the name of the "target" module not reflected in the "source" docs. 3. Typos to begin with. 4. Use of `<foo>` in the docs is rendered as just `foo` with a link to `/foo`. 5. Similarly for `"Foo"` if it starts with a capital letter (and hence could be a module). 6. Markup inside `@` code blocks still being applied. e.g. `@M-<arrow-keys>@` is rendered as `M-arrow-keys` with a spurious hyperlink from arrow-keys to `/arrow-keys`, which is confusing. Three links from XMonad.Util.Run have been removed outright, since they're no longer examples of the usage of 'runProcessWithInput'. WmiiActions has been gone since 2008, while XMonad.Prompt.Directory and XMonad.Layout.WorkspaceDir haven't been using 'runProcessWithInput' since 2020 and 2012, respectively. In some cases the `<foo>` were surrounded with @, especially in the case of key definitions, for consistency. (This wasn't done everywhere, because it looks ugly in the source.) MoreManageHelpers has never been in xmonad-contrib. ManageHelpers seems to fill the expected role. In the case of the module description for X.H.ManageDebug the quotes were simply removed because none of the likely options to make the link work were successful.
511 lines
17 KiB
Haskell
511 lines
17 KiB
Haskell
{-# LANGUAGE CPP #-}
|
|
--------------------------------------------------------------------
|
|
-- |
|
|
-- Module : XMonad.Util.EZConfig
|
|
-- Description : Configure key bindings easily in Emacs style.
|
|
-- Copyright : Devin Mullins <me@twifkak.com>
|
|
-- Brent Yorgey <byorgey@gmail.com> (key parsing)
|
|
-- License : BSD3-style (see LICENSE)
|
|
--
|
|
-- Maintainer : Devin Mullins <me@twifkak.com>
|
|
--
|
|
-- Useful helper functions for amending the default configuration, and for
|
|
-- parsing keybindings specified in a special (emacs-like) format.
|
|
--
|
|
-- (See also "XMonad.Util.CustomKeys" in xmonad-contrib.)
|
|
--
|
|
--------------------------------------------------------------------
|
|
|
|
module XMonad.Util.EZConfig (
|
|
-- * Usage
|
|
-- $usage
|
|
|
|
-- * Adding or removing keybindings
|
|
|
|
additionalKeys, additionalKeysP,
|
|
removeKeys, removeKeysP,
|
|
additionalMouseBindings, removeMouseBindings,
|
|
|
|
-- * Emacs-style keybinding specifications
|
|
|
|
mkKeymap, checkKeymap,
|
|
mkNamedKeymap,
|
|
|
|
-- * Parsers
|
|
|
|
parseKey, -- used by XMonad.Util.Paste
|
|
parseKeyCombo,
|
|
parseKeySequence, readKeySequence,
|
|
#ifdef TESTING
|
|
parseModifier,
|
|
#endif
|
|
) where
|
|
|
|
import XMonad
|
|
import XMonad.Actions.Submap
|
|
import XMonad.Prelude
|
|
|
|
import XMonad.Util.NamedActions
|
|
import XMonad.Util.Parser
|
|
|
|
import Control.Arrow (first, (&&&))
|
|
import qualified Data.Map as M
|
|
import Data.Ord (comparing)
|
|
|
|
-- $usage
|
|
-- To use this module, first import it into your @~\/.xmonad\/xmonad.hs@:
|
|
--
|
|
-- > import XMonad.Util.EZConfig
|
|
--
|
|
-- Then, use one of the provided functions to modify your
|
|
-- configuration. You can use 'additionalKeys', 'removeKeys',
|
|
-- 'additionalMouseBindings', and 'removeMouseBindings' to easily add
|
|
-- and remove keybindings or mouse bindings. You can use 'mkKeymap'
|
|
-- to create a keymap using emacs-style keybinding specifications
|
|
-- like @\"M-x\"@ instead of @(modMask, xK_x)@, or 'additionalKeysP'
|
|
-- and 'removeKeysP' to easily add or remove emacs-style keybindings.
|
|
-- If you use emacs-style keybindings, the 'checkKeymap' function is
|
|
-- provided, suitable for adding to your 'startupHook', which can warn
|
|
-- you of any parse errors or duplicate bindings in your keymap.
|
|
--
|
|
-- For more information and usage examples, see the documentation
|
|
-- provided with each exported function, and check the xmonad config
|
|
-- archive (<http://haskell.org/haskellwiki/Xmonad/Config_archive>)
|
|
-- for some real examples of use.
|
|
|
|
-- |
|
|
-- Add or override keybindings from the existing set. Example use:
|
|
--
|
|
-- > main = xmonad $ def { terminal = "urxvt" }
|
|
-- > `additionalKeys`
|
|
-- > [ ((mod1Mask, xK_m ), spawn "echo 'Hi, mom!' | dzen2 -p 4")
|
|
-- > , ((mod1Mask, xK_BackSpace), withFocused hide) -- N.B. this is an absurd thing to do
|
|
-- > ]
|
|
--
|
|
-- This overrides the previous definition of mod-m.
|
|
--
|
|
-- Note that, unlike in xmonad 0.4 and previous, you can't use modMask to refer
|
|
-- to the modMask you configured earlier. You must specify mod1Mask (or
|
|
-- whichever), or add your own @myModMask = mod1Mask@ line.
|
|
additionalKeys :: XConfig a -> [((KeyMask, KeySym), X ())] -> XConfig a
|
|
additionalKeys conf keyList =
|
|
conf { keys = M.union (M.fromList keyList) . keys conf }
|
|
|
|
-- | Like 'additionalKeys', except using short @String@ key
|
|
-- descriptors like @\"M-m\"@ instead of @(modMask, xK_m)@, as
|
|
-- described in the documentation for 'mkKeymap'. For example:
|
|
--
|
|
-- > main = xmonad $ def { terminal = "urxvt" }
|
|
-- > `additionalKeysP`
|
|
-- > [ ("M-m", spawn "echo 'Hi, mom!' | dzen2 -p 4")
|
|
-- > , ("M-<Backspace>", withFocused hide) -- N.B. this is an absurd thing to do
|
|
-- > ]
|
|
|
|
additionalKeysP :: XConfig l -> [(String, X ())] -> XConfig l
|
|
additionalKeysP conf keyList =
|
|
conf { keys = \cnf -> M.union (mkKeymap cnf keyList) (keys conf cnf) }
|
|
|
|
-- |
|
|
-- Remove standard keybindings you're not using. Example use:
|
|
--
|
|
-- > main = xmonad $ def { terminal = "urxvt" }
|
|
-- > `removeKeys` [(mod1Mask .|. shiftMask, n) | n <- [xK_1 .. xK_9]]
|
|
removeKeys :: XConfig a -> [(KeyMask, KeySym)] -> XConfig a
|
|
removeKeys conf keyList =
|
|
conf { keys = \cnf -> foldr M.delete (keys conf cnf) keyList }
|
|
|
|
-- | Like 'removeKeys', except using short @String@ key descriptors
|
|
-- like @\"M-m\"@ instead of @(modMask, xK_m)@, as described in the
|
|
-- documentation for 'mkKeymap'. For example:
|
|
--
|
|
-- > main = xmonad $ def { terminal = "urxvt" }
|
|
-- > `removeKeysP` ["M-S-" ++ [n] | n <- ['1'..'9']]
|
|
|
|
removeKeysP :: XConfig l -> [String] -> XConfig l
|
|
removeKeysP conf keyList =
|
|
conf { keys = \cnf -> keys conf cnf `M.difference` mkKeymap cnf (zip keyList $ repeat (return ())) }
|
|
|
|
-- | Like 'additionalKeys', but for mouse bindings.
|
|
additionalMouseBindings :: XConfig a -> [((ButtonMask, Button), Window -> X ())] -> XConfig a
|
|
additionalMouseBindings conf mouseBindingsList =
|
|
conf { mouseBindings = M.union (M.fromList mouseBindingsList) . mouseBindings conf }
|
|
|
|
-- | Like 'removeKeys', but for mouse bindings.
|
|
removeMouseBindings :: XConfig a -> [(ButtonMask, Button)] -> XConfig a
|
|
removeMouseBindings conf mouseBindingList =
|
|
conf { mouseBindings = \cnf -> foldr M.delete (mouseBindings conf cnf) mouseBindingList }
|
|
|
|
|
|
--------------------------------------------------------------
|
|
-- Keybinding parsing ---------------------------------------
|
|
--------------------------------------------------------------
|
|
|
|
-- | Given a config (used to determine the proper modifier key to use)
|
|
-- and a list of @(String, X ())@ pairs, create a key map by parsing
|
|
-- the key sequence descriptions contained in the Strings. The key
|
|
-- sequence descriptions are \"emacs-style\": @M-@, @C-@, @S-@, and
|
|
-- @M\#-@ denote mod, control, shift, and mod1-mod5 (where @\#@ is
|
|
-- replaced by the appropriate number) respectively. Note that if
|
|
-- you want to make a keybinding using \'alt\' even though you use a
|
|
-- different key (like the \'windows\' key) for \'mod\', you can use
|
|
-- something like @\"M1-x\"@ for alt+x (check the output of @xmodmap@
|
|
-- to see which mod key \'alt\' is bound to). Some special keys can
|
|
-- also be specified by enclosing their name in angle brackets.
|
|
--
|
|
-- For example, @\"M-C-x\"@ denotes mod+ctrl+x; @\"S-\<Escape\>\"@
|
|
-- denotes shift-escape; @\"M1-C-\<Delete\>\"@ denotes alt+ctrl+delete
|
|
-- (assuming alt is bound to mod1, which is common).
|
|
--
|
|
-- Sequences of keys can also be specified by separating the key
|
|
-- descriptions with spaces. For example, @\"M-x y \<Down\>\"@ denotes the
|
|
-- sequence of keys mod+x, y, down. Submaps (see
|
|
-- "XMonad.Actions.Submap") will be automatically generated to
|
|
-- correctly handle these cases.
|
|
--
|
|
-- So, for example, a complete key map might be specified as
|
|
--
|
|
-- > keys = \c -> mkKeymap c $
|
|
-- > [ ("M-S-<Return>", spawn $ terminal c)
|
|
-- > , ("M-x w", spawn "xmessage 'woohoo!'") -- type mod+x then w to pop up 'woohoo!'
|
|
-- > , ("M-x y", spawn "xmessage 'yay!'") -- type mod+x then y to pop up 'yay!'
|
|
-- > , ("M-S-c", kill)
|
|
-- > ]
|
|
--
|
|
-- Alternatively, you can use 'additionalKeysP' to automatically
|
|
-- create a keymap and add it to your config.
|
|
--
|
|
-- Here is a complete list of supported special keys. Note that a few
|
|
-- keys, such as the arrow keys, have synonyms. If there are other
|
|
-- special keys you would like to see supported, feel free to submit a
|
|
-- patch, or ask on the xmonad mailing list; adding special keys is
|
|
-- quite simple.
|
|
--
|
|
-- > <Backspace>
|
|
-- > <Tab>
|
|
-- > <Return>
|
|
-- > <Pause>
|
|
-- > <Num_Lock>
|
|
-- > <Caps_Lock>
|
|
-- > <Scroll_lock>
|
|
-- > <Sys_Req>
|
|
-- > <Print>
|
|
-- > <Escape>, <Esc>
|
|
-- > <Delete>
|
|
-- > <Home>
|
|
-- > <Left>, <L>
|
|
-- > <Up>, <U>
|
|
-- > <Right>, <R>
|
|
-- > <Down>, <D>
|
|
-- > <Page_Up>
|
|
-- > <Page_Down>
|
|
-- > <End>
|
|
-- > <Insert>
|
|
-- > <Break>
|
|
-- > <Space>
|
|
-- > <Control_L>
|
|
-- > <Control_R>
|
|
-- > <Shift_L>
|
|
-- > <Shift_R>
|
|
-- > <Alt_L>
|
|
-- > <Alt_R>
|
|
-- > <Meta_L>
|
|
-- > <Meta_R>
|
|
-- > <Super_L>
|
|
-- > <Super_R>
|
|
-- > <Hyper_L>
|
|
-- > <Hyper_R>
|
|
-- > <F1>-<F24>
|
|
-- > <KP_Space>
|
|
-- > <KP_Tab>
|
|
-- > <KP_Enter>
|
|
-- > <KP_F1>
|
|
-- > <KP_F2>
|
|
-- > <KP_F3>
|
|
-- > <KP_F4>
|
|
-- > <KP_Home>
|
|
-- > <KP_Left>
|
|
-- > <KP_Up>
|
|
-- > <KP_Right>
|
|
-- > <KP_Down>
|
|
-- > <KP_Prior>
|
|
-- > <KP_Page_Up>
|
|
-- > <KP_Next>
|
|
-- > <KP_Page_Down>
|
|
-- > <KP_End>
|
|
-- > <KP_Begin>
|
|
-- > <KP_Insert>
|
|
-- > <KP_Delete>
|
|
-- > <KP_Equal>
|
|
-- > <KP_Multiply>
|
|
-- > <KP_Add>
|
|
-- > <KP_Separator>
|
|
-- > <KP_Subtract>
|
|
-- > <KP_Decimal>
|
|
-- > <KP_Divide>
|
|
-- > <KP_0>-<KP_9>
|
|
--
|
|
-- Long list of multimedia keys. Please note that not all keys may be
|
|
-- present in your particular setup although most likely they will do.
|
|
--
|
|
-- > <XF86ModeLock>
|
|
-- > <XF86MonBrightnessUp>
|
|
-- > <XF86MonBrightnessDown>
|
|
-- > <XF86KbdLightOnOff>
|
|
-- > <XF86KbdBrightnessUp>
|
|
-- > <XF86KbdBrightnessDown>
|
|
-- > <XF86Standby>
|
|
-- > <XF86AudioLowerVolume>
|
|
-- > <XF86AudioMute>
|
|
-- > <XF86AudioRaiseVolume>
|
|
-- > <XF86AudioPlay>
|
|
-- > <XF86AudioStop>
|
|
-- > <XF86AudioPrev>
|
|
-- > <XF86AudioNext>
|
|
-- > <XF86HomePage>
|
|
-- > <XF86Mail>
|
|
-- > <XF86Start>
|
|
-- > <XF86Search>
|
|
-- > <XF86AudioRecord>
|
|
-- > <XF86Calculator>
|
|
-- > <XF86Memo>
|
|
-- > <XF86ToDoList>
|
|
-- > <XF86Calendar>
|
|
-- > <XF86PowerDown>
|
|
-- > <XF86ContrastAdjust>
|
|
-- > <XF86RockerUp>
|
|
-- > <XF86RockerDown>
|
|
-- > <XF86RockerEnter>
|
|
-- > <XF86Back>
|
|
-- > <XF86Forward>
|
|
-- > <XF86Stop>
|
|
-- > <XF86Refresh>
|
|
-- > <XF86PowerOff>
|
|
-- > <XF86WakeUp>
|
|
-- > <XF86Eject>
|
|
-- > <XF86ScreenSaver>
|
|
-- > <XF86WWW>
|
|
-- > <XF86Sleep>
|
|
-- > <XF86Favorites>
|
|
-- > <XF86AudioPause>
|
|
-- > <XF86AudioMedia>
|
|
-- > <XF86MyComputer>
|
|
-- > <XF86VendorHome>
|
|
-- > <XF86LightBulb>
|
|
-- > <XF86Shop>
|
|
-- > <XF86History>
|
|
-- > <XF86OpenURL>
|
|
-- > <XF86AddFavorite>
|
|
-- > <XF86HotLinks>
|
|
-- > <XF86BrightnessAdjust>
|
|
-- > <XF86Finance>
|
|
-- > <XF86Community>
|
|
-- > <XF86AudioRewind>
|
|
-- > <XF86XF86BackForward>
|
|
-- > <XF86Launch0>-<XF86Launch9>, <XF86LaunchA>-<XF86LaunchF>
|
|
-- > <XF86ApplicationLeft>
|
|
-- > <XF86ApplicationRight>
|
|
-- > <XF86Book>
|
|
-- > <XF86CD>
|
|
-- > <XF86Calculater>
|
|
-- > <XF86Clear>
|
|
-- > <XF86Close>
|
|
-- > <XF86Copy>
|
|
-- > <XF86Cut>
|
|
-- > <XF86Display>
|
|
-- > <XF86DOS>
|
|
-- > <XF86Documents>
|
|
-- > <XF86Excel>
|
|
-- > <XF86Explorer>
|
|
-- > <XF86Game>
|
|
-- > <XF86Go>
|
|
-- > <XF86iTouch>
|
|
-- > <XF86LogOff>
|
|
-- > <XF86Market>
|
|
-- > <XF86Meeting>
|
|
-- > <XF86MenuKB>
|
|
-- > <XF86MenuPB>
|
|
-- > <XF86MySites>
|
|
-- > <XF86New>
|
|
-- > <XF86News>
|
|
-- > <XF86OfficeHome>
|
|
-- > <XF86Open>
|
|
-- > <XF86Option>
|
|
-- > <XF86Paste>
|
|
-- > <XF86Phone>
|
|
-- > <XF86Q>
|
|
-- > <XF86Reply>
|
|
-- > <XF86Reload>
|
|
-- > <XF86RotateWindows>
|
|
-- > <XF86RotationPB>
|
|
-- > <XF86RotationKB>
|
|
-- > <XF86Save>
|
|
-- > <XF86ScrollUp>
|
|
-- > <XF86ScrollDown>
|
|
-- > <XF86ScrollClick>
|
|
-- > <XF86Send>
|
|
-- > <XF86Spell>
|
|
-- > <XF86SplitScreen>
|
|
-- > <XF86Support>
|
|
-- > <XF86TaskPane>
|
|
-- > <XF86Terminal>
|
|
-- > <XF86Tools>
|
|
-- > <XF86Travel>
|
|
-- > <XF86UserPB>
|
|
-- > <XF86User1KB>
|
|
-- > <XF86User2KB>
|
|
-- > <XF86Video>
|
|
-- > <XF86WheelButton>
|
|
-- > <XF86Word>
|
|
-- > <XF86Xfer>
|
|
-- > <XF86ZoomIn>
|
|
-- > <XF86ZoomOut>
|
|
-- > <XF86Away>
|
|
-- > <XF86Messenger>
|
|
-- > <XF86WebCam>
|
|
-- > <XF86MailForward>
|
|
-- > <XF86Pictures>
|
|
-- > <XF86Music>
|
|
-- > <XF86TouchpadToggle>
|
|
-- > <XF86AudioMicMute>
|
|
-- > <XF86_Switch_VT_1>-<XF86_Switch_VT_12>
|
|
-- > <XF86_Ungrab>
|
|
-- > <XF86_ClearGrab>
|
|
-- > <XF86_Next_VMode>
|
|
-- > <XF86_Prev_VMode>
|
|
-- > <XF86Bluetooth>
|
|
|
|
mkKeymap :: XConfig l -> [(String, X ())] -> M.Map (KeyMask, KeySym) (X ())
|
|
mkKeymap c = M.fromList . mkSubmaps . readKeymap c
|
|
|
|
mkNamedKeymap :: XConfig l -> [(String, NamedAction)] -> [((KeyMask, KeySym), NamedAction)]
|
|
mkNamedKeymap c = mkNamedSubmaps . readKeymap c
|
|
|
|
-- | Given a list of pairs of parsed key sequences and actions,
|
|
-- group them into submaps in the appropriate way.
|
|
|
|
mkNamedSubmaps :: [([(KeyMask, KeySym)], NamedAction)] -> [((KeyMask, KeySym), NamedAction)]
|
|
mkNamedSubmaps = mkSubmaps' submapName
|
|
|
|
mkSubmaps :: [ ([(KeyMask,KeySym)], X ()) ] -> [((KeyMask, KeySym), X ())]
|
|
mkSubmaps = mkSubmaps' $ submap . M.fromList
|
|
|
|
mkSubmaps' :: (Ord a) => ([(a, c)] -> c) -> [([a], c)] -> [(a, c)]
|
|
mkSubmaps' subm binds = map combine gathered
|
|
where gathered = groupBy fstKey
|
|
. sortBy (comparing fst)
|
|
$ binds
|
|
combine [([k],act)] = (k,act)
|
|
combine ks = (head . fst . head $ ks,
|
|
subm . mkSubmaps' subm $ map (first tail) ks)
|
|
fstKey = (==) `on` (head . fst)
|
|
|
|
-- | Given a configuration record and a list of (key sequence
|
|
-- description, action) pairs, parse the key sequences into lists of
|
|
-- @(KeyMask,KeySym)@ pairs. Key sequences which fail to parse will
|
|
-- be ignored.
|
|
readKeymap :: XConfig l -> [(String, t)] -> [([(KeyMask, KeySym)], t)]
|
|
readKeymap c = mapMaybe (maybeKeys . first (readKeySequence c))
|
|
where maybeKeys (Nothing,_) = Nothing
|
|
maybeKeys (Just k, act) = Just (k, act)
|
|
|
|
-- | Parse a sequence of keys, returning Nothing if there is
|
|
-- a parse failure (no parse, or ambiguous parse).
|
|
readKeySequence :: XConfig l -> String -> Maybe [(KeyMask, KeySym)]
|
|
readKeySequence c = runParser (parseKeySequence c <* eof)
|
|
|
|
-- | Parse a sequence of key combinations separated by spaces, e.g.
|
|
-- @\"M-c x C-S-2\"@ (mod+c, x, ctrl+shift+2).
|
|
parseKeySequence :: XConfig l -> Parser [(KeyMask, KeySym)]
|
|
parseKeySequence c = parseKeyCombo c `sepBy1` many1 (char ' ')
|
|
|
|
-- | Parse a modifier-key combination such as "M-C-s" (mod+ctrl+s).
|
|
parseKeyCombo :: XConfig l -> Parser (KeyMask, KeySym)
|
|
parseKeyCombo c = do mods <- many (parseModifier c)
|
|
k <- parseKey
|
|
return (foldl' (.|.) 0 mods, k)
|
|
|
|
-- | Parse a modifier: either M- (user-defined mod-key),
|
|
-- C- (control), S- (shift), or M#- where # is an integer
|
|
-- from 1 to 5 (mod1Mask through mod5Mask).
|
|
parseModifier :: XConfig l -> Parser KeyMask
|
|
parseModifier c = (string "M-" $> modMask c)
|
|
<> (string "C-" $> controlMask)
|
|
<> (string "S-" $> shiftMask)
|
|
<> do _ <- char 'M'
|
|
n <- satisfy (`elem` ['1'..'5'])
|
|
_ <- char '-'
|
|
return $ indexMod (read [n] - 1)
|
|
where indexMod = (!!) [mod1Mask,mod2Mask,mod3Mask,mod4Mask,mod5Mask]
|
|
|
|
-- | Parse an unmodified basic key, like @\"x\"@, @\"\<F1\>\"@, etc.
|
|
parseKey :: Parser KeySym
|
|
parseKey = parseSpecial <> parseRegular
|
|
|
|
-- | Parse a regular key name (represented by itself).
|
|
parseRegular :: Parser KeySym
|
|
parseRegular = choice [ string s $> k | (s, k) <- regularKeys ]
|
|
|
|
-- | Parse a special key name (one enclosed in angle brackets).
|
|
parseSpecial :: Parser KeySym
|
|
parseSpecial = do _ <- char '<'
|
|
choice [ k <$ string name <* char '>'
|
|
| (name, k) <- allSpecialKeys
|
|
]
|
|
|
|
-- | Given a configuration record and a list of (key sequence
|
|
-- description, action) pairs, check the key sequence descriptions
|
|
-- for validity, and warn the user (via a popup xmessage window) of
|
|
-- any unparseable or duplicate key sequences. This function is
|
|
-- appropriate for adding to your @startupHook@, and you are highly
|
|
-- encouraged to do so; otherwise, duplicate or unparseable
|
|
-- keybindings will be silently ignored.
|
|
--
|
|
-- For example, you might do something like this:
|
|
--
|
|
-- > main = xmonad $ myConfig
|
|
-- >
|
|
-- > myKeymap = [("S-M-c", kill), ...]
|
|
-- > myConfig = def {
|
|
-- > ...
|
|
-- > keys = \c -> mkKeymap c myKeymap
|
|
-- > startupHook = return () >> checkKeymap myConfig myKeymap
|
|
-- > ...
|
|
-- > }
|
|
--
|
|
-- NOTE: the @return ()@ in the example above is very important!
|
|
-- Otherwise, you might run into problems with infinite mutual
|
|
-- recursion: the definition of myConfig depends on the definition of
|
|
-- startupHook, which depends on the definition of myConfig, ... and
|
|
-- so on. Actually, it's likely that the above example in particular
|
|
-- would be OK without the @return ()@, but making @myKeymap@ take
|
|
-- @myConfig@ as a parameter would definitely lead to
|
|
-- problems. Believe me. It, uh, happened to my friend. In... a
|
|
-- dream. Yeah. In any event, the @return () >>@ introduces enough
|
|
-- laziness to break the deadlock.
|
|
--
|
|
checkKeymap :: XConfig l -> [(String, a)] -> X ()
|
|
checkKeymap conf km = warn (doKeymapCheck conf km)
|
|
where warn ([],[]) = return ()
|
|
warn (bad,dup) = xmessage $ "Warning:\n"
|
|
++ msg "bad" bad ++ "\n"
|
|
++ msg "duplicate" dup
|
|
msg _ [] = ""
|
|
msg m xs = m ++ " keybindings detected: " ++ showBindings xs
|
|
showBindings = unwords . map (("\""++) . (++"\""))
|
|
|
|
-- | Given a config and a list of (key sequence description, action)
|
|
-- pairs, check the key sequence descriptions for validity,
|
|
-- returning a list of unparseable key sequences, and a list of
|
|
-- duplicate key sequences.
|
|
doKeymapCheck :: XConfig l -> [(String,a)] -> ([String], [String])
|
|
doKeymapCheck conf km = (bad,dups)
|
|
where ks = map ((readKeySequence conf &&& id) . fst) km
|
|
bad = nub . map snd . filter (isNothing . fst) $ ks
|
|
dups = map (snd . head)
|
|
. filter ((>1) . length)
|
|
. groupBy ((==) `on` fst)
|
|
. sortBy (comparing fst)
|
|
. map (first fromJust)
|
|
. filter (isJust . fst)
|
|
$ ks
|