aboutsummaryrefslogtreecommitdiffstats
path: root/ManPrompt.hs
blob: ad0b2e7f9a2c85dac4079963303fba33d19f3e74 (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
-----------------------------------------------------------------------------
-- |
-- Module      :  XMonadContrib.ManPrompt
-- Copyright   :  (c) 2007 Valery V. Vorotyntsev
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  valery.vv@gmail.com
-- Stability   :  unstable
-- Portability :  unportable
--
-- A manual page prompt for XMonad window manager.
--
-- TODO
--
--   * narrow completions by section number, if the one is specified
--     (like @\/etc\/bash_completion@ does)
--
--   * handle explicit paths (e.g., @~\/src\/xmonad\/man\/xmonad.1@)
--
--   * quickcheck properties
-----------------------------------------------------------------------------

module XMonadContrib.ManPrompt (
                                -- * Usage
                                -- $usage
                                manPrompt
                               , getCommandOutput
                               ) where

import XMonad
import XMonadContrib.XPrompt
import XMonadContrib.Run
import XMonadContrib.ShellPrompt (split)

import System.Directory
import System.Process
import System.IO

import qualified Control.Exception as E
import Control.Monad
import Data.List
import Data.Maybe

-- $usage
-- 1. In Config.hs add:
--
-- > import XMonadContrib.ManPrompt
--
-- 2. In your keybindings add something like:
--
-- >     , ((modMask, xK_F1), manPrompt defaultXPConfig)

-- %import XMonadContrib.XPrompt
-- %import XMonadContrib.ManPrompt
-- %keybind , ((modMask, xK_F1), manPrompt defaultXPConfig)

data Man = Man

instance XPrompt Man where
    showXPrompt Man = "Manual page: "

-- | Query for manual page to be displayed.
manPrompt :: XPConfig -> X ()
manPrompt c = mkXPrompt Man c manCompl $ runInTerm . (++) "man "

manCompl :: String -> IO [String]
manCompl s = getManpages >>= flip mkComplFunFromList s

-- | Obtain the list of manual pages.
--
-- /XXX Code duplication!/
-- Adopted from 'ShellPrompt.getCommands'.
getManpages :: IO [String]
getManpages = do
  p <- getCommandOutput "manpath -g 2>/dev/null" `E.catch` const (return [])
  let sections = ["man" ++ show n | n <- [1..9 :: Int]] -- XXX "cat1".."cat9"?
      ds       = [d ++ "/" ++ s | d <- split ':' p, s <- sections]
      stripSec = reverse . drop 1 . dropWhile (/= '.') . reverse
  ms <- forM ds $ \d -> do
              exists <- doesDirectoryExist d
              if exists
                then map (stripSec . stripSuffixes [".gz", ".bz2"]) `fmap`
                     getDirectoryContents d
                else return []
  return . uniqSort . concat $ ms

-- | Run a command using shell and return its output.
getCommandOutput :: String -> IO String
getCommandOutput s = do
  (pin, pout, perr, ph) <- runInteractiveCommand s
  hClose pin
  output <- hGetContents pout
  E.evaluate (null output)
  hClose perr
  waitForProcess ph
  return output

stripSuffixes :: Eq a => [[a]] -> [a] -> [a]
stripSuffixes sufs fn =
    head . catMaybes $ map (flip rstrip fn) sufs ++ [Just fn]

rstrip :: Eq a => [a] -> [a] -> Maybe [a]
rstrip suf lst
    | suf `isSuffixOf` lst = Just $ take (length lst - length suf) lst
    | otherwise            = Nothing