module Nixfmt.Util
( manyP
, someP
, manyText
, someText
, commonPrefix
, commonIndentation
, dropCommonIndentation
, identChar
, pathChar
, schemeChar
, uriChar
) where
import Data.Char (isAlpha, isDigit, isSpace)
import Data.Maybe (fromMaybe)
import Data.Text as Text
(Text, commonPrefixes, concat, empty, stripEnd, stripPrefix, takeWhile)
import Text.Megaparsec
(ParsecT, Stream, Token, Tokens, many, some, takeWhile1P, takeWhileP)
charClass :: [Char] -> Char -> Bool
charClass s c = isAlpha c || isDigit c || elem c s
identChar :: Char -> Bool
identChar = charClass "_'-"
pathChar :: Char -> Bool
pathChar = charClass "._-+~"
schemeChar :: Char -> Bool
schemeChar = charClass "-.+"
uriChar :: Char -> Bool
uriChar = charClass "~!@$%&*-=_+:',./?"
someP :: (Stream s, Ord e) => (Token s -> Bool) -> ParsecT e s m (Tokens s)
someP = takeWhile1P Nothing
manyP :: (Stream s, Ord e) => (Token s -> Bool) -> ParsecT e s m (Tokens s)
manyP = takeWhileP Nothing
someText :: (Stream s, Ord e) => ParsecT e s m Text -> ParsecT e s m Text
someText p = Text.concat <$> some p
manyText :: (Stream s, Ord e) => ParsecT e s m Text -> ParsecT e s m Text
manyText p = Text.concat <$> many p
commonPrefix :: Text -> Text -> Text
commonPrefix a b =
case commonPrefixes a b of
Nothing -> empty
Just (prefix, _, _) -> prefix
commonIndentation :: [Text] -> Maybe Text
commonIndentation [] = Nothing
commonIndentation [x] = Just $ Text.takeWhile isSpace x
commonIndentation (x:y:xs) = commonIndentation (commonPrefix x y : xs)
dropCommonIndentation :: [Text] -> [Text]
dropCommonIndentation unstrippedLines =
let strippedLines = map stripEnd unstrippedLines
in case commonIndentation (filter (/=empty) strippedLines) of
Nothing -> map (const empty) strippedLines
Just indentation -> map (fromMaybe empty . stripPrefix indentation) strippedLines