aboutsummaryrefslogtreecommitdiffstats
path: root/XMonad/Prompt/DirExec.hs
blob: 491904915e402751136371a0690c8942820e9819 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Prompt.DirExec
-- Copyright   :  (C) 2008 Juraj Hercek
-- License     :  BSD3
--
-- Maintainer  :  juhe_xmonad@hck.sk
-- Stability   :  unstable
-- Portability :  unportable
--
-- A directory file executables prompt for XMonad. This might be useful if you
-- don't want to have scripts in your PATH environment variable (same
-- executable names, different behavior) - otherwise you might want to use
-- "XMonad.Prompt.Shell" instead - but you want to have easy access to these
-- executables through the xmonad's prompt.
--
-----------------------------------------------------------------------------

module XMonad.Prompt.DirExec
    ( -- * Usage
      -- $usage
      dirExecPrompt
    , dirExecPromptNamed
    , DirExec
    ) where

import Control.Exception as E
import System.Directory
import Control.Monad
import Data.List
import XMonad
import XMonad.Prompt

econst :: Monad m => a -> IOException -> m a
econst = const . return

-- $usage
-- 1. In your @~\/.xmonad\/xmonad.hs@:
--
-- > import XMonad.Prompt.DirExec
--
-- 2. In your keybindings add something like:
--
-- >   , ("M-C-x", dirExecPrompt def spawn "/home/joe/.scipts")
--
-- or
--
-- >   , ("M-C-x", dirExecPromptNamed def spawn
-- >                                  "/home/joe/.scripts" "My Scripts: ")
--
-- or add this after your default bindings:
--
-- >   ++
-- >   [ ("M-x " ++ key, dirExecPrompt def fn "/home/joe/.scripts")
-- >     | (key, fn) <- [ ("x", spawn), ("M-x", runInTerm "-hold") ]
-- >   ]
-- >   ++
--
-- The first alternative uses the last element of the directory path for
-- a name of prompt. The second alternative uses the provided string
-- for the name of the prompt. The third alternative defines 2 key bindings,
-- first one spawns the program by shell, second one runs the program in
-- terminal
--
-- For detailed instruction on editing the key binding see
-- "XMonad.Doc.Extending#Editing_key_bindings".

data DirExec = DirExec String

instance XPrompt DirExec where
    showXPrompt (DirExec name) = name

-- | Function 'dirExecPrompt' starts the prompt with list of all executable
-- files in directory specified by 'FilePath'. The name of the prompt is taken
-- from the last element of the path. If you specify root directory - @\/@ - as
-- the path, name @Root:@ will be used as the name of the prompt instead. The
-- 'XPConfig' parameter can be used to customize visuals of the prompt.
-- The runner parameter specifies the function used to run the program - see
-- usage for more information
dirExecPrompt :: XPConfig -> (String -> X ()) -> FilePath -> X ()
dirExecPrompt cfg runner path = do
    let name = (++ ": ") . last
                         . (["Root"] ++) -- handling of "/" path parameter
                         . words
                         . map (\x -> if x == '/' then ' ' else x)
                         $ path
    dirExecPromptNamed cfg runner path name

-- | Function 'dirExecPromptNamed' does the same as 'dirExecPrompt' except
-- the name of the prompt is specified by 'String' parameter.
dirExecPromptNamed :: XPConfig -> (String -> X ()) -> FilePath -> String -> X ()
dirExecPromptNamed cfg runner path name = do
    let path' = path ++ "/"
    cmds <- io $ getDirectoryExecutables path'
    mkXPrompt (DirExec name) cfg (compList cmds) (runner . (path' ++))
    where
        compList cmds s = return . filter (isInfixOf s) $ cmds

getDirectoryExecutables :: FilePath -> IO [String]
getDirectoryExecutables path =
    (getDirectoryContents path >>=
        filterM (\x -> let x' = path ++ x in
            liftM2 (&&)
                (doesFileExist x')
                (liftM executable (getPermissions x'))))
    `E.catch` econst []