Changes on XPrompt:

* Adds mkPromptWithModes, creates a prompt given a list of modes (list of XPType).

    * Adds Setting `alwaysHighlight` to defaultXPConfig. When set to true, autocompletion always highlight the first result if it is not highlighted.
    
Adds module XMonad.Actions.Launcher. This module allows to combine and switch between instances of XPrompt. It includes a default set of modes which require the programs `hoogle`, `locate` and `calc` to be installed to work properly.
This commit is contained in:
c.lopez 2012-06-28 10:17:49 +00:00
parent 9d34e848d9
commit 3c74148a2f
3 changed files with 395 additions and 39 deletions

177
XMonad/Actions/Launcher.hs Normal file
View File

@ -0,0 +1,177 @@
{- |
Module : XMonad.Actions.Launcher
Copyright : (C) 2012 Carlos López-Camey
License : None; public domain
Maintainer : <c.lopez@kmels.net>
Stability : unstable
A set of prompts for XMonad
-}
module XMonad.Actions.Launcher(
-- * Description and use
-- $description
defaultLauncherModes
, ExtensionActions
, LauncherConfig(..)
, LocateFileMode
, LocateFileRegexMode
, launcherPrompt
-- * ToDo
-- $todo
) where
import Data.List (find, findIndex, isPrefixOf, tails)
import qualified Data.Map as M
import Data.Maybe (fromMaybe, isJust)
import System.Directory (doesDirectoryExist)
import XMonad hiding (config)
import XMonad.Prompt
import XMonad.Util.Run
{- $description
This module lets you combine and switch between different types of prompts (XMonad.Prompt). It includes a set of default modes:
* Hoogle mode: Search for functions using hoogle, choosing a function leads you to documentation in Haddock.
* Locate mode: Search for files using locate, choosing a file opens it with a program you specify depending on the file's extension.
* Locate regexp: Same as locate mode but autocomplete works with regular expressions.
* Calc: Uses the program calc to do calculations.
To use the default modes, modify your .xmonad:
> import XMonad.Prompt(defaultXPConfig)
> import XMonad.Actions.Launcher
> ((modm .|. controlMask, xK_l), launcherPrompt kmelsXPConfig $ defaultLauncherModes launcherConfig)
A LauncherConfig contains settings for the default modes, modify them accordingly.
> launcherConfig = LauncherConfig { pathToHoogle = "/home/YOU/.cabal/bin/hoogle" , actionsByExtension = extensionActions }
@extensionActions :: M.Map String (String -> X())
extensionActions = M.fromList $ [
(\".hs\", \p -> spawn $ \"emacs \" ++ p)
, (\".pdf\", \p -> spawn $ \"acroread \" ++ p)
, (\".*\", \p -> spawn $ \"emacs \" ++ p) --match with any files
, (\"/\", \p -> spawn $ \"nautilus \" ++ p) --match with directories
]@
To try it, restart xmonad. Press Ctrl + Your_Modkey + L and the first prompt should pop up.
You can change mode with xK_grave if you used defaultXP or change the value of changeModeKey in your XPConfig-}
data LocateFileMode = LMode ExtensionActions
data LocateFileRegexMode = LRMode ExtensionActions
data HoogleMode = HMode FilePath String --path to hoogle e.g. "/home/me/.cabal/bin/hoogle"
data CalculatorMode = CalcMode
data LauncherConfig = LauncherConfig {
browser :: String
, pathToHoogle :: String
, actionsByExtension :: ExtensionActions
}
type ExtensionActions = M.Map String (String -> X())
-- | Uses the program `locate` to list files
instance XPrompt LocateFileMode where
showXPrompt (LMode _) = "locate %s> "
completionFunction (LMode _) = \s -> if (s == "" || last s == ' ') then return [] else (completionFunctionWith "locate" ["--limit","5",s])
modeAction (LMode actions) _ fp = spawnWithActions actions fp
-- | Uses the program `locate --regex` to list files
instance XPrompt LocateFileRegexMode where
showXPrompt (LRMode _) = "locate --regexp %s> "
completionFunction (LRMode _) = \s -> if (s == "" || last s == ' ') then return [] else (completionFunctionWith "locate" ["--limit","5","--regexp",s])
modeAction (LRMode actions) _ fp = spawnWithActions actions fp
-- | Uses the command `calc` to compute arithmetic expressions
instance XPrompt CalculatorMode where
showXPrompt CalcMode = "calc %s> "
commandToComplete CalcMode = id --send the whole string to `calc`
completionFunction CalcMode = \s -> if (length s == 0) then return [] else do
fmap lines $ runProcessWithInput "calc" [s] ""
modeAction CalcMode _ _ = return () -- do nothing; this might copy the result to the clipboard
-- | Uses the program `hoogle` to search for functions
instance XPrompt HoogleMode where
showXPrompt _ = "hoogle %s> "
commandToComplete _ = id
completionFunction (HMode pathToHoogleBin' _) = \s -> completionFunctionWith pathToHoogleBin' ["--count","5",s]
-- This action calls hoogle again to find the URL corresponding to the autocompleted item
modeAction (HMode pathToHoogleBin'' browser) query result = do
completionsWithLink <- liftIO $ completionFunctionWith pathToHoogleBin'' ["--count","5","--link",query]
let link = do
s <- find (isJust . \c -> findSeqIndex c result) completionsWithLink
i <- findSeqIndex s "http://"
return $ drop i s
case link of
Just l -> spawn $ browser ++ " " ++ l
_ -> return ()
where
-- | Receives a sublist and a list. It returns the index where the sublist appears in the list.
findSeqIndex :: (Eq a) => [a] -> [a] -> Maybe Int
findSeqIndex xs xss = findIndex (isPrefixOf xss) $ tails xs
-- | Creates an autocompletion function for a programm given the program's name and a list of args to send to the command.
completionFunctionWith :: String -> [String] -> IO [String]
completionFunctionWith cmd args = do fmap lines $ runProcessWithInput cmd args ""
-- | Creates a prompt with the given modes
launcherPrompt :: XPConfig -> [XPMode] -> X()
launcherPrompt config modes = mkXPromptWithModes modes config
-- | Create a list of modes based on :
-- a list of extensions mapped to actions
-- the path to hoogle
defaultLauncherModes :: LauncherConfig -> [XPMode]
defaultLauncherModes cnf = let
ph = pathToHoogle cnf
actions = actionsByExtension cnf
in [ hoogleMode ph $ browser cnf
, locateMode actions
, locateRegexMode actions
, calcMode]
locateMode, locateRegexMode :: ExtensionActions -> XPMode
locateMode actions = XPT $ LMode actions
locateRegexMode actions = XPT $ LRMode actions
hoogleMode :: FilePath -> String -> XPMode
hoogleMode pathToHoogleBin browser = XPT $ HMode pathToHoogleBin browser
calcMode :: XPMode
calcMode = XPT CalcMode
-- | This function takes a map of extensions and a path file. It uses the map to find the pattern that matches the file path, then the corresponding program (listed in the map) is spawned.
spawnWithActions :: ExtensionActions -> FilePath -> X()
spawnWithActions actions fp = do
isDirectoryPath <- liftIO $ doesDirectoryExist fp
let
takeExtension = \p -> "." ++ (reverse . takeWhile (/= '.') $ reverse p) --it includes the dot
-- Patterns defined by the user
extAction = M.lookup (takeExtension fp) actions
dirAction = if (isDirectoryPath) then M.lookup "/" actions else Nothing -- / represents a directory
anyFileAction = M.lookup ".*" actions -- .* represents any file
action = fromMaybe (spawnNoPatternMessage (takeExtension fp)) $ extAction `orElse1` dirAction `orElse1` anyFileAction
action fp
where
-- | This function is defined in Data.Generics.Aliases (package syb "Scrap your boilerplate"), defined here to avoid dependency
orElse1 :: Maybe a -> Maybe a -> Maybe a
x `orElse1` y = case x of
Just _ -> x
Nothing -> y
spawnNoPatternMessage :: String -> String -> X ()
spawnNoPatternMessage fileExt _ = spawn $ "xmessage No action specified for file extension " ++ fileExt ++ ", add a default action by matching the extension \".*\" in the action map sent to launcherPrompt"
{- $todo
* Switch to mode by name of the prompt, 1. ':' at an empty(?) buffer, 2. autocomplete name in buffer should happen, 3. switch to mode with enter (cancel switch with C-g)
* Support for actions of type String -> X a
* Hoogle mode: add a setting in the action to either go to documentation or to the source code (needs hoogle change?)
* Hoogle mode: add setting to query hoogle at haskell.org instead (with &mode=json)
-}

View File

@ -18,9 +18,11 @@ module XMonad.Prompt
-- $usage -- $usage
mkXPrompt mkXPrompt
, mkXPromptWithReturn , mkXPromptWithReturn
, mkXPromptWithModes
, amberXPConfig , amberXPConfig
, defaultXPConfig , defaultXPConfig
, greenXPConfig , greenXPConfig
, XPMode
, XPType (..) , XPType (..)
, XPPosition (..) , XPPosition (..)
, XPConfig (..) , XPConfig (..)
@ -109,11 +111,11 @@ data XPState =
, screen :: !Rectangle , screen :: !Rectangle
, complWin :: Maybe Window , complWin :: Maybe Window
, complWinDim :: Maybe ComplWindowDim , complWinDim :: Maybe ComplWindowDim
, completionFunction :: String -> IO [String] , complIndex :: !(Int,Int)
, showComplWin :: Bool , showComplWin :: Bool
, operationMode :: XPOperationMode
, gcon :: !GC , gcon :: !GC
, fontS :: !XMonadFont , fontS :: !XMonadFont
, xptype :: !XPType
, commandHistory :: W.Stack String , commandHistory :: W.Stack String
, offset :: !Int , offset :: !Int
, config :: XPConfig , config :: XPConfig
@ -131,7 +133,8 @@ data XPConfig =
, borderColor :: String -- ^ Border color , borderColor :: String -- ^ Border color
, promptBorderWidth :: !Dimension -- ^ Border width , promptBorderWidth :: !Dimension -- ^ Border width
, position :: XPPosition -- ^ Position: 'Top' or 'Bottom' , position :: XPPosition -- ^ Position: 'Top' or 'Bottom'
, height :: !Dimension -- ^ Window height , alwaysHighlight :: !Bool -- ^ Always highlight an item, overriden to True with multiple modes. This implies having *one* column of autocompletions only.
, height :: !Dimension -- ^ Window height
, historySize :: !Int -- ^ The number of history entries to be saved , historySize :: !Int -- ^ The number of history entries to be saved
, historyFilter :: [String] -> [String] , historyFilter :: [String] -> [String]
-- ^ a filter to determine which -- ^ a filter to determine which
@ -139,6 +142,7 @@ data XPConfig =
, promptKeymap :: M.Map (KeyMask,KeySym) (XP ()) , promptKeymap :: M.Map (KeyMask,KeySym) (XP ())
-- ^ Mapping from key combinations to actions -- ^ Mapping from key combinations to actions
, completionKey :: KeySym -- ^ Key that should trigger completion , completionKey :: KeySym -- ^ Key that should trigger completion
, changeModeKey :: KeySym -- ^ Key to change mode (when the prompt has multiple modes)
, defaultText :: String -- ^ The text by default in the prompt line , defaultText :: String -- ^ The text by default in the prompt line
, autoComplete :: Maybe Int -- ^ Just x: if only one completion remains, auto-select it, , autoComplete :: Maybe Int -- ^ Just x: if only one completion remains, auto-select it,
, showCompletionOnTab :: Bool -- ^ Only show list of completions when Tab was pressed , showCompletionOnTab :: Bool -- ^ Only show list of completions when Tab was pressed
@ -149,6 +153,9 @@ data XPConfig =
} }
data XPType = forall p . XPrompt p => XPT p data XPType = forall p . XPrompt p => XPT p
type ComplFunction = String -> IO [String]
type XPMode = XPType
data XPOperationMode = XPSingleMode ComplFunction XPType | XPMultipleModes (W.Stack XPType)
instance Show XPType where instance Show XPType where
show (XPT p) = showXPrompt p show (XPT p) = showXPrompt p
@ -158,6 +165,8 @@ instance XPrompt XPType where
nextCompletion (XPT t) = nextCompletion t nextCompletion (XPT t) = nextCompletion t
commandToComplete (XPT t) = commandToComplete t commandToComplete (XPT t) = commandToComplete t
completionToCommand (XPT t) = completionToCommand t completionToCommand (XPT t) = completionToCommand t
completionFunction (XPT t) = completionFunction t
modeAction (XPT t) = modeAction t
-- | The class prompt types must be an instance of. In order to -- | The class prompt types must be an instance of. In order to
-- create a prompt you need to create a data type, without parameters, -- create a prompt you need to create a data type, without parameters,
@ -179,11 +188,13 @@ class XPrompt t where
-- printed in the command line when tab is pressed, given the -- printed in the command line when tab is pressed, given the
-- string presently in the command line and the list of -- string presently in the command line and the list of
-- completion. -- completion.
-- This function is not used when in multiple modes (because alwaysHighlight in XPConfig is True)
nextCompletion :: t -> String -> [String] -> String nextCompletion :: t -> String -> [String] -> String
nextCompletion = getNextOfLastWord nextCompletion = getNextOfLastWord
-- | This method is used to generate the string to be passed to -- | This method is used to generate the string to be passed to
-- the completion function. -- the completion function.
-- This function is not used when in multiple modes (because alwaysHighlight in XPConfig is True)
commandToComplete :: t -> String -> String commandToComplete :: t -> String -> String
commandToComplete _ = getLastWord commandToComplete _ = getLastWord
@ -197,6 +208,20 @@ class XPrompt t where
completionToCommand :: t -> String -> String completionToCommand :: t -> String -> String
completionToCommand _ c = c completionToCommand _ c = c
-- | When the prompt has multiple modes, this is the function
-- used to generate the autocompletion list.
-- The argument passed to this function is given by `commandToComplete`
-- The default implementation shows an error message.
completionFunction :: t -> ComplFunction
completionFunction t = \_ -> return ["Completions for " ++ (showXPrompt t) ++ " could not be loaded"]
-- | When the prompt has multiple modes, this function is called
-- when the user picked an item from the autocompletion list.
-- The first argument is the autocompleted item's text.
-- The second argument is the query made by the user (written in the prompt's buffer).
modeAction :: t -> String -> String -> X ()
modeAction _ _ _ = return ()
data XPPosition = Top data XPPosition = Top
| Bottom | Bottom
deriving (Show,Read) deriving (Show,Read)
@ -213,6 +238,7 @@ defaultXPConfig =
, promptBorderWidth = 1 , promptBorderWidth = 1
, promptKeymap = defaultXPKeymap , promptKeymap = defaultXPKeymap
, completionKey = xK_Tab , completionKey = xK_Tab
, changeModeKey = xK_asciitilde
, position = Bottom , position = Bottom
, height = 18 , height = 18
, historySize = 256 , historySize = 256
@ -221,29 +247,28 @@ defaultXPConfig =
, autoComplete = Nothing , autoComplete = Nothing
, showCompletionOnTab = False , showCompletionOnTab = False
, searchPredicate = isPrefixOf , searchPredicate = isPrefixOf
, alwaysHighlight = False
} }
greenXPConfig = defaultXPConfig { fgColor = "green", bgColor = "black", promptBorderWidth = 0 } greenXPConfig = defaultXPConfig { fgColor = "green", bgColor = "black", promptBorderWidth = 0 }
amberXPConfig = defaultXPConfig { fgColor = "#ca8f2d", bgColor = "black", fgHLight = "#eaaf4c" } amberXPConfig = defaultXPConfig { fgColor = "#ca8f2d", bgColor = "black", fgHLight = "#eaaf4c" }
type ComplFunction = String -> IO [String] initState :: Display -> Window -> Window -> Rectangle -> XPOperationMode
-> GC -> XMonadFont -> [String] -> XPConfig -> KeyMask -> XPState
initState :: XPrompt p => Display -> Window -> Window -> Rectangle -> ComplFunction initState d rw w s opMode gc fonts h c nm =
-> GC -> XMonadFont -> p -> [String] -> XPConfig -> KeyMask -> XPState
initState d rw w s compl gc fonts pt h c nm =
XPS { dpy = d XPS { dpy = d
, rootw = rw , rootw = rw
, win = w , win = w
, screen = s , screen = s
, complWin = Nothing , complWin = Nothing
, complWinDim = Nothing , complWinDim = Nothing
, completionFunction = compl
, showComplWin = not (showCompletionOnTab c) , showComplWin = not (showCompletionOnTab c)
, operationMode = opMode
, gcon = gc , gcon = gc
, fontS = fonts , fontS = fonts
, xptype = XPT pt
, commandHistory = W.Stack { W.focus = defaultText c , commandHistory = W.Stack { W.focus = defaultText c
, W.up = [] , W.up = []
, W.down = h } , W.down = h }
, complIndex = (0,0) --(column index, row index), used when `alwaysHighlight` in XPConfig is True
, offset = length (defaultText c) , offset = length (defaultText c)
, config = c , config = c
, successful = False , successful = False
@ -251,6 +276,36 @@ initState d rw w s compl gc fonts pt h c nm =
, numlockMask = nm , numlockMask = nm
} }
-- Returns the current XPType
currentXPMode :: XPState -> XPType
currentXPMode st = case operationMode st of
XPMultipleModes modes -> W.focus modes
XPSingleMode _ xptype -> xptype
-- When in multiple modes, this function sets the next mode
-- in the list of modes as active
setNextMode :: XPState -> XPState
setNextMode st = case operationMode st of
XPMultipleModes modes -> case W.down modes of
[] -> st -- there is no next mode, return same state
(m:ms) -> let
currentMode = W.focus modes
in st { operationMode = XPMultipleModes W.Stack { W.up = [], W.focus = m, W.down = ms ++ [currentMode]}} --set next and move previous current mode to the of the stack
_ -> st --nothing to do, the prompt's operation has only one mode
-- Returns the highlighted item
highlightedItem :: XPState -> [String] -> String
highlightedItem st' completions = case complWinDim st' of
Nothing -> "" -- when there isn't any compl win, we can't say how many cols,rows there are
Just winDim ->
let
(_,_,_,_,xx,yy) = winDim
complMatrix = splitInSubListsAt (length yy) (take (length xx * length yy) completions)
(col_index,row_index) = (complIndex st')
in case completions of
[] -> "" -- no completions
_ -> complMatrix !! col_index !! row_index
-- this would be much easier with functional references -- this would be much easier with functional references
command :: XPState -> String command :: XPState -> String
command = W.focus . commandHistory command = W.focus . commandHistory
@ -285,23 +340,32 @@ mkXPromptWithReturn t conf compl action = do
fs <- initXMF (font conf) fs <- initXMF (font conf)
numlock <- gets $ X.numberlockMask numlock <- gets $ X.numberlockMask
let hs = fromMaybe [] $ M.lookup (showXPrompt t) hist let hs = fromMaybe [] $ M.lookup (showXPrompt t) hist
st = initState d rw w s compl gc fs (XPT t) hs conf numlock om = (XPSingleMode compl (XPT t)) --operation mode
st = initState d rw w s om gc fs hs conf numlock
st' <- io $ execStateT runXP st st' <- io $ execStateT runXP st
releaseXMF fs releaseXMF fs
io $ freeGC d gc io $ freeGC d gc
if successful st' if successful st' then do
then do completions <- liftIO $ do getCompletionFunction st' (commandToComplete (currentXPMode st') (command st')) `catch` \(SomeException _) -> return []
let prune = take (historySize conf) let
io $ writeHistory $ M.insertWith prune = take (historySize conf)
(\xs ys -> prune . historyFilter conf $ xs ++ ys)
(showXPrompt t) io $ writeHistory $ M.insertWith
(prune $ historyFilter conf [command st']) (\xs ys -> prune . historyFilter conf $ xs ++ ys)
hist (showXPrompt t)
(prune $ historyFilter conf [command st'])
hist
-- we need to apply historyFilter before as well, since -- we need to apply historyFilter before as well, since
-- otherwise the filter would not be applied if -- otherwise the filter would not be applied if
-- there is no history -- there is no history
Just <$> action (command st') --When alwaysHighlight is True, autocompletion is handled with indexes.
--When it is false, it is handled depending on the prompt buffer's value
let selectedCompletion = case alwaysHighlight (config st') of
False -> command st'
True -> highlightedItem st' completions
--Just <$> action selectedCompletion
Just <$> action selectedCompletion
else return Nothing else return Nothing
-- | Creates a prompt given: -- | Creates a prompt given:
@ -318,6 +382,62 @@ mkXPromptWithReturn t conf compl action = do
mkXPrompt :: XPrompt p => p -> XPConfig -> ComplFunction -> (String -> X ()) -> X () mkXPrompt :: XPrompt p => p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
mkXPrompt t conf compl action = mkXPromptWithReturn t conf compl action >> return () mkXPrompt t conf compl action = mkXPromptWithReturn t conf compl action >> return ()
-- | Creates a prompt with multiple modes given:
--
-- * A non-empty list of modes
-- * A prompt configuration
--
-- The created prompt allows to switch between modes with `changeModeKey` in `conf`. The modes are
-- instances of XPrompt. See XMonad.Actions.Launcher for more details
--
-- The argument supplied to the action to execute is always the current highlighted item,
-- that means that this prompt overrides the value `alwaysHighlight` for its configuration to True.
mkXPromptWithModes :: [XPType] -> XPConfig -> X ()
mkXPromptWithModes modes conf = do
XConf { display = d, theRoot = rw } <- ask
s <- gets $ screenRect . W.screenDetail . W.current . windowset
hist <- io readHistory
w <- io $ createWin d rw conf s
io $ selectInput d w $ exposureMask .|. keyPressMask
gc <- io $ createGC d w
io $ setGraphicsExposures d gc False
fs <- initXMF (font conf)
numlock <- gets $ X.numberlockMask
let
defaultMode = head modes
hs = fromMaybe [] $ M.lookup (showXPrompt defaultMode) hist
modeStack = W.Stack{ W.focus = defaultMode --current mode
, W.up = []
, W.down = tail modes --other modes
}
st = initState d rw w s (XPMultipleModes modeStack) gc fs hs conf { alwaysHighlight = True} numlock
st' <- io $ execStateT runXP st
releaseXMF fs
io $ freeGC d gc
if successful st' then do
completions <- liftIO $ do getCompletionFunction st' (commandToComplete (currentXPMode st') (command st')) `catch` \(SomeException _) -> return []
let
prune = take (historySize conf)
-- insert into history the buffers value
io $ writeHistory $ M.insertWith
(\xs ys -> prune . historyFilter conf $ xs ++ ys)
(showXPrompt defaultMode)
(prune $ historyFilter conf [command st'])
hist
case operationMode st' of
XPMultipleModes ms -> let
action = modeAction $ W.focus ms
in action (command st') (highlightedItem st' completions)
_ -> return () --This should never happen, we are creating a prompt with multiple modes, so its operationMode should have been constructed with XPMultipleMode
else
return ()
runXP :: XP () runXP :: XP ()
runXP = do runXP = do
(d,w) <- gets (dpy &&& win) (d,w) <- gets (dpy &&& win)
@ -359,11 +479,16 @@ cleanMask msk = do
handle :: KeyStroke -> Event -> XP () handle :: KeyStroke -> Event -> XP ()
handle ks@(sym,_) e@(KeyEvent {ev_event_type = t, ev_state = m}) = do handle ks@(sym,_) e@(KeyEvent {ev_event_type = t, ev_state = m}) = do
complKey <- gets $ completionKey . config complKey <- gets $ completionKey . config
chgModeKey <- gets $ changeModeKey . config
c <- getCompletions c <- getCompletions
when (length c > 1) $ modify (\s -> s { showComplWin = True }) when (length c > 1) $ modify (\s -> s { showComplWin = True })
if complKey == sym if complKey == sym
then completionHandle c ks e then completionHandle c ks e
else when (t == keyPress) $ keyPressHandle m ks else if (sym == chgModeKey) then
do
modify setNextMode
updateWindows
else when (t == keyPress) $ keyPressHandle m ks
handle _ (ExposeEvent {ev_window = w}) = do handle _ (ExposeEvent {ev_window = w}) = do
st <- get st <- get
when (win st == w) updateWindows when (win st == w) updateWindows
@ -373,15 +498,18 @@ handle _ _ = return ()
completionHandle :: [String] -> KeyStroke -> Event -> XP () completionHandle :: [String] -> KeyStroke -> Event -> XP ()
completionHandle c ks@(sym,_) (KeyEvent { ev_event_type = t, ev_state = m }) = do completionHandle c ks@(sym,_) (KeyEvent { ev_event_type = t, ev_state = m }) = do
complKey <- gets $ completionKey . config complKey <- gets $ completionKey . config
alwaysHlight <- gets $ alwaysHighlight . config
case () of case () of
() | t == keyPress && sym == complKey -> () | t == keyPress && sym == complKey ->
do do
st <- get st <- get
let updateState l = let updateState l = case alwaysHlight of
let new_command = nextCompletion (xptype st) (command st) l --We will modify the next command (buffer's value), to be able to highlight the autocompletion (nextCompletion and commandToComplete implementation dependent)
in modify $ \s -> setCommand new_command $ s { offset = length new_command } False -> let new_command = nextCompletion (currentXPMode st) (command st) l
updateWins l = redrawWindows l >> in modify $ \s -> setCommand new_command $ s { offset = length new_command }
eventLoop (completionHandle l) --TODO: Scroll or paginate results
True -> modify $ \s -> s { complIndex = nextComplIndex st (length l)}
updateWins l = redrawWindows l >> eventLoop (completionHandle l)
case c of case c of
[] -> updateWindows >> eventLoop handle [] -> updateWindows >> eventLoop handle
[x] -> updateState [x] >> getCompletions >>= updateWins [x] -> updateState [x] >> getCompletions >>= updateWins
@ -391,6 +519,24 @@ completionHandle c ks@(sym,_) (KeyEvent { ev_event_type = t, ev_state = m }) = d
-- some other event: go back to main loop -- some other event: go back to main loop
completionHandle _ k e = handle k e completionHandle _ k e = handle k e
--Receives an state of the prompt, the size of the autocompletion list and returns the column,row
--which should be highlighted next
nextComplIndex :: XPState -> Int -> (Int,Int)
nextComplIndex st nitems = case complWinDim st of
Nothing -> (0,0) --no window dims (just destroyed or not created)
Just winDim -> let
(_,_,_,_,xx,yy) = winDim
(ncols,nrows) = (nitems `div` length yy + if (nitems `mod` length yy > 0) then 1 else 0, length yy)
(currentcol,currentrow) = complIndex st
in if (currentcol + 1 >= ncols) then --hlight is in the last column
if (currentrow + 1 < (nitems `mod` nrows) ) then --hlight is still not at the last row
(currentcol, currentrow + 1)
else
(0,0)
else if(currentrow + 1 < nrows) then --hlight not at the last row
(currentcol, currentrow + 1)
else
(currentcol + 1, 0)
tryAutoComplete :: XP Bool tryAutoComplete :: XP Bool
tryAutoComplete = do tryAutoComplete = do
@ -403,7 +549,7 @@ tryAutoComplete = do
Nothing -> return False Nothing -> return False
where runCompleted cmd delay = do where runCompleted cmd delay = do
st <- get st <- get
let new_command = nextCompletion (xptype st) (command st) [cmd] let new_command = nextCompletion (currentXPMode st) (command st) [cmd]
modify $ setCommand "autocompleting..." modify $ setCommand "autocompleting..."
updateWindows updateWindows
io $ threadDelay delay io $ threadDelay delay
@ -582,10 +728,18 @@ startOfLine =
flushString :: XP () flushString :: XP ()
flushString = modify $ \s -> setCommand "" $ s { offset = 0} flushString = modify $ \s -> setCommand "" $ s { offset = 0}
--reset index if config has `alwaysHighlight`. The inserted char could imply fewer autocompletions.
--If the current index was column 2, row 1 and now there are only 4 autocompletion rows with 1 column, what should we highlight? Set it to the first and start navigation again
resetComplIndex :: XPState -> XPState
resetComplIndex st = if (alwaysHighlight $ config st) then st { complIndex = (0,0) } else st
-- | Insert a character at the cursor position -- | Insert a character at the cursor position
insertString :: String -> XP () insertString :: String -> XP ()
insertString str = insertString str =
modify $ \s -> setCommand (c (command s) (offset s)) $ s { offset = o (offset s)} modify $ \s -> let
cmd = (c (command s) (offset s))
st = resetComplIndex $ s { offset = o (offset s)}
in setCommand cmd st
where o oo = oo + length str where o oo = oo + length str
c oc oo | oo >= length oc = oc ++ str c oc oo | oo >= length oc = oc ++ str
| otherwise = f ++ str ++ ss | otherwise = f ++ str ++ ss
@ -691,7 +845,7 @@ printPrompt :: Drawable -> XP ()
printPrompt drw = do printPrompt drw = do
st <- get st <- get
let (gc,(c,(d,fs))) = (gcon &&& config &&& dpy &&& fontS) st let (gc,(c,(d,fs))) = (gcon &&& config &&& dpy &&& fontS) st
(prt,(com,off)) = (show . xptype &&& command &&& offset) st (prt,(com,off)) = (show . currentXPMode &&& command &&& offset) st
str = prt ++ com str = prt ++ com
-- break the string in 3 parts: till the cursor, the cursor and the rest -- break the string in 3 parts: till the cursor, the cursor and the rest
(f,p,ss) = if off >= length com (f,p,ss) = if off >= length com
@ -713,12 +867,17 @@ printPrompt drw = do
-- reverse the colors and print the rest of the string -- reverse the colors and print the rest of the string
draw (fgColor c) (bgColor c) (x + fromIntegral (fsl + psl)) y ss draw (fgColor c) (bgColor c) (x + fromIntegral (fsl + psl)) y ss
-- get the current completion function depending on the active mode
getCompletionFunction :: XPState -> ComplFunction
getCompletionFunction st = case operationMode st of
XPSingleMode compl _ -> compl
XPMultipleModes modes -> completionFunction $ W.focus modes
-- Completions -- Completions
getCompletions :: XP [String] getCompletions :: XP [String]
getCompletions = do getCompletions = do
s <- get s <- get
io $ completionFunction s (commandToComplete (xptype s) (command s)) io $ getCompletionFunction s (commandToComplete (currentXPMode s) (command s))
`catch` \(SomeException _) -> return [] `catch` \(SomeException _) -> return []
setComplWin :: Window -> ComplWindowDim -> XP () setComplWin :: Window -> ComplWindowDim -> XP ()
@ -773,7 +932,7 @@ getComplWinDim compl = do
xp = (asc + desc) `div` 2 xp = (asc + desc) `div` 2
yy = map fi . take (fi actual_rows) $ [yp,(yp + ht)..] yy = map fi . take (fi actual_rows) $ [yp,(yp + ht)..]
xx = take (fi columns) [xp,(xp + max_compl_len)..] xx = take (fi columns) [xp,(xp + max_compl_len)..]
return (rect_x scr + x, rect_y scr + fi y, wh, actual_height, xx, yy) return (rect_x scr + x, rect_y scr + fi y, wh, actual_height, xx, yy)
drawComplWin :: Window -> [String] -> XP () drawComplWin :: Window -> [String] -> XP ()
@ -793,7 +952,9 @@ drawComplWin w compl = do
(defaultDepthOfScreen scr) (defaultDepthOfScreen scr)
io $ fillDrawable d p gc border bgcolor (fi bw) wh ht io $ fillDrawable d p gc border bgcolor (fi bw) wh ht
let ac = splitInSubListsAt (length yy) (take (length xx * length yy) compl) let ac = splitInSubListsAt (length yy) (take (length xx * length yy) compl)
printComplList d p gc (fgColor c) (bgColor c) xx yy ac printComplList d p gc (fgColor c) (bgColor c) xx yy ac
--lift $ spawn $ "xmessage " ++ " ac: " ++ show ac ++ " xx: " ++ show xx ++ " length xx: " ++ show (length xx) ++ " yy: " ++ show (length yy)
io $ copyArea d p w gc 0 0 wh ht 0 0 io $ copyArea d p w gc 0 0 wh ht 0 0
io $ freePixmap d p io $ freePixmap d p
@ -814,18 +975,35 @@ redrawComplWin compl = do
Nothing -> recreate Nothing -> recreate
else destroyComplWin else destroyComplWin
-- given a string and a matrix of strings, find the column and row indexes in which the string appears.
-- if the string is not in the matrix, the function returns (0,0)
findComplIndex :: String -> [[String]] -> (Int,Int)
findComplIndex x xss = let
colIndex = fromMaybe 0 $ findIndex (\cols -> x `elem` cols) xss
rowIndex = fromMaybe 0 $ elemIndex x $ (!!) xss colIndex
in (colIndex,rowIndex)
printComplList :: Display -> Drawable -> GC -> String -> String printComplList :: Display -> Drawable -> GC -> String -> String
-> [Position] -> [Position] -> [[String]] -> XP () -> [Position] -> [Position] -> [[String]] -> XP ()
printComplList d drw gc fc bc xs ys sss = printComplList d drw gc fc bc xs ys sss =
zipWithM_ (\x ss -> zipWithM_ (\x ss ->
zipWithM_ (\y s -> do zipWithM_ (\y item -> do
st <- get st <- get
let (f,b) = if completionToCommand (xptype st) s == commandToComplete (xptype st) (command st) alwaysHlight <- gets $ alwaysHighlight . config
then (fgHLight $ config st,bgHLight $ config st) let (f,b) = case alwaysHlight of
else (fc,bc) True -> --find the column, row in which this item is and decide if we should highlight
printStringXMF d drw (fontS st) gc f b x y s) let
colIndex = fromMaybe 0 $ findIndex (\cols -> item `elem` cols) sss
rowIndex = fromMaybe 0 $ elemIndex item $ (!!) sss colIndex
in
if ((complIndex st) == (colIndex,rowIndex)) then (fgHLight $ config st,bgHLight $ config st)
else (fc,bc)
False -> if completionToCommand (currentXPMode st) item == commandToComplete (currentXPMode st) (command st)
then (fgHLight $ config st,bgHLight $ config st)
else (fc,bc)
printStringXMF d drw (fontS st) gc f b x y item)
ys ss) xs sss ys ss) xs sss
-- History -- History
type History = M.Map String [String] type History = M.Map String [String]

View File

@ -102,6 +102,7 @@ library
XMonad.Actions.FocusNth XMonad.Actions.FocusNth
XMonad.Actions.GridSelect XMonad.Actions.GridSelect
XMonad.Actions.GroupNavigation XMonad.Actions.GroupNavigation
XMonad.Actions.Launcher
XMonad.Actions.MessageFeedback XMonad.Actions.MessageFeedback
XMonad.Actions.MouseGestures XMonad.Actions.MouseGestures
XMonad.Actions.MouseResize XMonad.Actions.MouseResize