-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Prompt.AppendFile
-- Description :  A prompt for appending a single line of text to a file.
-- Copyright   :  (c) 2007 Brent Yorgey
-- License     :  BSD-style (see LICENSE)
--
-- Maintainer  :  <byorgey@gmail.com>
-- Stability   :  stable
-- Portability :  unportable
--
-- A prompt for appending a single line of text to a file.  Useful for
-- keeping a file of notes, things to remember for later, and so on---
-- using a keybinding, you can write things down just about as quickly
-- as you think of them, so it doesn't have to interrupt whatever else
-- you're doing.
--
-- Who knows, it might be useful for other purposes as well!
--
-----------------------------------------------------------------------------

module XMonad.Prompt.AppendFile (
                                 -- * Usage
                                 -- $usage

                                 appendFilePrompt,
                                 appendFilePrompt',
                                 AppendFile,
                                ) where

import XMonad.Core
import XMonad.Prompt
import XMonad.Prelude (mkAbsolutePath)

import System.IO

-- $usage
--
-- You can use this module by importing it, along with
-- "XMonad.Prompt", into your @xmonad.hs@ file:
--
-- > import XMonad.Prompt
-- > import XMonad.Prompt.AppendFile
--
-- and adding an appropriate keybinding, for example:
--
-- >  , ((modm .|. controlMask, xK_n), appendFilePrompt def "/home/me/NOTES")
--
-- Additional notes can be added via regular Haskell or XMonad functions; for
-- example, to preface notes with the time they were made, one could write a
-- binding like
--
-- > ,  ((modm .|. controlMask, xK_n), do
-- >            spawn ("date>>"++"/home/me/NOTES")
-- >            appendFilePrompt def "/home/me/NOTES"
-- >        )
--
-- (Put the spawn on the line after the prompt to append the time instead.)
--
-- 'appendFilePrompt'' can be used to transform the string input in the prompt
-- before saving into the file. Previous example with date can be rewritten as:
--
-- > ,  ((modm .|. controlMask, xK_n), do
-- >            date <- io $ fmap (formatTime defaultTimeLocale "[%Y-%m-%d %H:%M] ") getZonedTime
-- >            appendFilePrompt' def (date ++) $ "/home/me/NOTES"
-- >        )
--
-- A benefit is that if the prompt is cancelled the date is not output to
-- the file too.
--
-- For detailed instructions on editing your key bindings, see
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.

newtype AppendFile = AppendFile FilePath

instance XPrompt AppendFile where
    showXPrompt :: AppendFile -> String
showXPrompt (AppendFile String
fn) = String
"Add to " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fn String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": "

-- | Given an XPrompt configuration and a file path, prompt the user
--   for a line of text, and append it to the given file.
appendFilePrompt :: XPConfig -> FilePath -> X ()
appendFilePrompt :: XPConfig -> String -> X ()
appendFilePrompt XPConfig
c = XPConfig -> (String -> String) -> String -> X ()
appendFilePrompt' XPConfig
c String -> String
forall a. a -> a
id

-- | Given an XPrompt configuration, string transformation function
--   and a file path, prompt the user for a line of text, transform it
--   and append the result to the given file.
appendFilePrompt' :: XPConfig -> (String -> String) -> FilePath -> X ()
appendFilePrompt' :: XPConfig -> (String -> String) -> String -> X ()
appendFilePrompt' XPConfig
c String -> String
trans String
fn = AppendFile -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
forall p.
XPrompt p =>
p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
mkXPrompt (String -> AppendFile
AppendFile String
fn)
                                  XPConfig
c
                                  (IO [String] -> ComplFunction
forall a b. a -> b -> a
const ([String] -> IO [String]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return []))
                                  ((String -> String) -> String -> String -> X ()
doAppend String -> String
trans String
fn)

-- | Append a string to a file.
doAppend :: (String -> String) -> FilePath -> String -> X ()
doAppend :: (String -> String) -> String -> String -> X ()
doAppend String -> String
trans String
fn String
s = String -> X String
forall (m :: * -> *). MonadIO m => String -> m String
mkAbsolutePath String
fn X String -> (String -> X ()) -> X ()
forall a b. X a -> (a -> X b) -> X b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \String
f -> (IO () -> X ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO () -> X ()) -> (String -> IO ()) -> String -> X ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IOMode -> (Handle -> IO ()) -> IO ()
forall r. String -> IOMode -> (Handle -> IO r) -> IO r
withFile String
f IOMode
AppendMode ((Handle -> IO ()) -> IO ())
-> (String -> Handle -> IO ()) -> String -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Handle -> String -> IO ()) -> String -> Handle -> IO ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip Handle -> String -> IO ()
hPutStrLn (String -> Handle -> IO ())
-> (String -> String) -> String -> Handle -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
trans) String
s