roguestar-gl-0.6.0.0: src/Keymaps/Keymaps.hs
-- | 'Keymap's are simple mappings from a sequence of keystrokes to the
-- names of various commands.
{-# LANGUAGE OverloadedStrings #-}
module Keymaps.Keymaps
(Keymap,
KeymapName,
fixKeymap,
filterKeySequence,
keysToActionNames,
actionNameToKeys)
where
import Actions
import Data.List
import Control.Monad
import Control.Concurrent.STM
import qualified Data.ByteString.Char8 as B
import qualified KeyStroke as K
import Data.Monoid
type Keymap = [(K.KeyString,B.ByteString)]
type KeymapName = String
-- 'fixKeymap' processes a keymap to make it usable, by:
-- * removing erroneous whitespace
-- * placing \\n and \\r at the end of multi-key keystrokes that should be
-- activated via the enter key.
fixKeymap :: Keymap -> Keymap
fixKeymap = concatMap $ \(keystrokes,action_name) ->
case () of
() | K.null keystrokes -> []
() | K.length keystrokes == 1 -> [(keystrokes,action_name)]
() -> [(keystrokes `mappend` "&KeyActivate;",action_name)]
-- | 'validKeyMap' reduces a 'Keymap' to one that contains only those actions
-- that are valid at the instant of the atomic transaction.
validKeyMap :: ActionInput -> Keymap -> STM Keymap
validKeyMap action_input raw_keymap =
do valid_actions <- getValidActions action_input Nothing
return $ filter (\x -> snd x `elem` valid_actions) raw_keymap
-- | 'filterKeySequence' transforms a key sequence, providing services such as:
-- * blanking the input buffer if there is no possible completion and
-- * performing tab complation
filterKeySequence :: ActionInput -> Keymap -> K.KeyString -> STM K.KeyString
filterKeySequence _ _ key_sequence |
(length $ words $ K.asString key_sequence) /= 1 = return ""
filterKeySequence action_input keymap key_sequence =
do valid_key_map <- validKeyMap action_input keymap
let is_tab_key_completion = "&KeyTab;" `K.isSuffixOf` key_sequence &&
key_sequence /= "&KeyTab;"
let stripped_key_sequence = if is_tab_key_completion
then K.init key_sequence
else key_sequence
let possible_completions =
filter (\x -> elem (stripped_key_sequence) $ K.inits x) $
map fst $ valid_key_map
return $ case length possible_completions of
-- if there are no possible completions, clear the buffer
0 -> ""
-- if the trailing input isn't a tab, chill
_ | not is_tab_key_completion -> key_sequence
-- if there's only one completion, get it, but strip
-- the trailing return in case they didn't want
-- to exec that completion.
1 -> if key_sequence /= head possible_completions
then K.init $ head possible_completions
else stripped_key_sequence
-- get the best completion we can find
_ -> maximumBy (\x y -> compare (K.length x) (K.length y)) $
foldr1 intersect $ map K.inits possible_completions
-- | 'keysToActionNames' gets a list of the names of all actions that could be
-- executed by the given key sequence at this instant. A null result
-- indicates that the key sequence isn't a valid command. A result with more
-- than one element indicates a collision in the key map, which is an error.
keysToActionNames :: ActionInput -> Keymap -> K.KeyString -> STM [B.ByteString]
keysToActionNames action_input keymap key_sequence =
liftM (map snd . filter (\x -> fst x == key_sequence)) $ validKeyMap action_input keymap
-- 'actionNameToKeys' provides reverse lookup versus keysToActionNames.
actionNameToKeys :: ActionInput -> Keymap -> B.ByteString -> STM [K.KeyString]
actionNameToKeys action_input keymap action_name =
liftM (map fst . filter (\x -> snd x == action_name)) $ validKeyMap action_input keymap