directory-layout-0.6.0.0: src/System/Directory/Layout/QQ.hs
{-# LANGUAGE TemplateHaskell #-}
-- | Convenience quasiquoter to ease the pain working with multiline strings
module System.Directory.Layout.QQ (dedent) where
import Data.Char (isSpace)
import Data.List (intercalate)
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Language.Haskell.TH.Syntax (liftString)
import Language.Haskell.TH (Q, Exp)
-- | A handy quasiquoter to work with the multiline file contents
--
-- Strips the longest common leading spaces segment. All spacey characters are treated
-- equally. The first line is ignored if it's spaces only.
--
-- >>> :set -XQuasiQuotes
-- >>> :{
-- putStr [dedent|
-- hello
-- world
-- !
-- |]
-- :}
-- hello
-- world
-- !
dedent :: QuasiQuoter
dedent = quoter $
liftString . intercalate "\n" . stripCommonLeadingWhitespace . dropFirst (all isSpace) . lines
dropFirst :: (a -> Bool) -> [a] -> [a]
dropFirst _ [] = []
dropFirst p (x : xs)
| p x = xs
| otherwise = x : xs
stripCommonLeadingWhitespace :: [String] -> [String]
stripCommonLeadingWhitespace xs = map (drop (commonLeadingWhitespace xs)) xs
commonLeadingWhitespace :: [String] -> Int
commonLeadingWhitespace = minimumOr 0 . map (length . takeWhile isSpace)
minimumOr :: Ord a => a -> [a] -> a
minimumOr n [] = n
minimumOr _ xs = minimum xs
quoter :: (String -> Q Exp) -> QuasiQuoter
quoter quote = QuasiQuoter
{ quoteExp = quote
, quotePat = failure "patterns"
, quoteType = failure "types"
, quoteDec = failure "declarations"
}
where
failure kind =
fail $ "this quasiquoter does not support splicing " ++ kind