diff options
Diffstat (limited to 'accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs')
-rw-r--r-- | accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs new file mode 100644 index 00000000..c268f87d --- /dev/null +++ b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs | |||
@@ -0,0 +1,105 @@ | |||
1 | module XMonad.Prompt.MyShell | ||
2 | ( Shell (..) | ||
3 | , shellPrompt | ||
4 | , prompt | ||
5 | , safePrompt | ||
6 | , unsafePrompt | ||
7 | , getCommands | ||
8 | , getShellCompl | ||
9 | , split | ||
10 | ) where | ||
11 | |||
12 | import Codec.Binary.UTF8.String (encodeString) | ||
13 | import Control.Exception as E | ||
14 | import Control.Monad (forM) | ||
15 | import Data.List (isPrefixOf) | ||
16 | import System.Directory (doesDirectoryExist, getDirectoryContents) | ||
17 | import System.Environment (getEnv) | ||
18 | import System.Posix.Files (getFileStatus, isDirectory) | ||
19 | |||
20 | import XMonad hiding (config) | ||
21 | import XMonad.Prompt | ||
22 | import XMonad.Util.Run | ||
23 | |||
24 | econst :: Monad m => a -> IOException -> m a | ||
25 | econst = const . return | ||
26 | |||
27 | data Shell = Shell String | ||
28 | |||
29 | instance XPrompt Shell where | ||
30 | showXPrompt (Shell q) = q | ||
31 | completionToCommand _ = escape | ||
32 | |||
33 | shellPrompt :: String -> XPConfig -> X () | ||
34 | shellPrompt q c = do | ||
35 | cmds <- io getCommands | ||
36 | mkXPrompt (Shell q) c (getShellCompl cmds) spawn | ||
37 | |||
38 | {- $spawns | ||
39 | See safe and unsafeSpawn in "XMonad.Util.Run". | ||
40 | prompt is an alias for safePrompt; | ||
41 | safePrompt and unsafePrompt work on the same principles, but will use | ||
42 | XPrompt to interactively query the user for input; the appearance is | ||
43 | set by passing an XPConfig as the second argument. The first argument | ||
44 | is the program to be run with the interactive input. | ||
45 | You would use these like this: | ||
46 | |||
47 | > , ((modm, xK_b), safePrompt "firefox" greenXPConfig) | ||
48 | > , ((modm .|. shiftMask, xK_c), prompt ("xterm" ++ " -e") greenXPConfig) | ||
49 | |||
50 | Note that you want to use safePrompt for Firefox input, as Firefox | ||
51 | wants URLs, and unsafePrompt for the XTerm example because this allows | ||
52 | you to easily start a terminal executing an arbitrary command, like | ||
53 | 'top'. -} | ||
54 | |||
55 | prompt, unsafePrompt, safePrompt :: String -> FilePath -> XPConfig -> X () | ||
56 | prompt = unsafePrompt | ||
57 | safePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run | ||
58 | where run = safeSpawn c . return | ||
59 | unsafePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run | ||
60 | where run a = unsafeSpawn $ c ++ " " ++ a | ||
61 | |||
62 | getShellCompl :: [String] -> String -> IO [String] | ||
63 | getShellCompl cmds s | s == "" || last s == ' ' = return [] | ||
64 | | otherwise = do | ||
65 | f <- fmap lines $ runProcessWithInput "bash" [] ("compgen -A file -- " | ||
66 | ++ s ++ "\n") | ||
67 | files <- case f of | ||
68 | [x] -> do fs <- getFileStatus (encodeString x) | ||
69 | if isDirectory fs then return [x ++ "/"] | ||
70 | else return [x] | ||
71 | _ -> return f | ||
72 | return . uniqSort $ files ++ commandCompletionFunction cmds s | ||
73 | |||
74 | commandCompletionFunction :: [String] -> String -> [String] | ||
75 | commandCompletionFunction cmds str | '/' `elem` str = [] | ||
76 | | otherwise = filter (isPrefixOf str) cmds | ||
77 | |||
78 | getCommands :: IO [String] | ||
79 | getCommands = do | ||
80 | p <- getEnv "PATH" `E.catch` econst [] | ||
81 | let ds = filter (/= "") $ split ':' p | ||
82 | es <- forM ds $ \d -> do | ||
83 | exists <- doesDirectoryExist d | ||
84 | if exists | ||
85 | then getDirectoryContents d | ||
86 | else return [] | ||
87 | return . uniqSort . filter ((/= '.') . head) . concat $ es | ||
88 | |||
89 | split :: Eq a => a -> [a] -> [[a]] | ||
90 | split _ [] = [] | ||
91 | split e l = | ||
92 | f : split e (rest ls) | ||
93 | where | ||
94 | (f,ls) = span (/=e) l | ||
95 | rest s | s == [] = [] | ||
96 | | otherwise = tail s | ||
97 | |||
98 | escape :: String -> String | ||
99 | escape [] = "" | ||
100 | escape (x:xs) | ||
101 | | isSpecialChar x = '\\' : x : escape xs | ||
102 | | otherwise = x : escape xs | ||
103 | |||
104 | isSpecialChar :: Char -> Bool | ||
105 | isSpecialChar = flip elem " &\\@\"'#?$*()[]{};" | ||