nixfmt 0.4.0 → 0.5.0
raw patch · 12 files changed
+203/−109 lines, 12 filesdep ~basedep ~megaparsecdep ~parser-combinatorsPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, megaparsec, parser-combinators
API changes (from Hackage documentation)
- Nixfmt.Parser: dropEmptyParts :: [[StringPart]] -> [[StringPart]]
- Nixfmt.Parser: stripFirstLine :: [[StringPart]] -> [[StringPart]]
- Nixfmt.Parser: textHeads :: [StringPart] -> [Text]
- Nixfmt.Pretty: isEmptyLine :: [StringPart] -> Bool
+ Nixfmt: formatVerify :: Width -> FilePath -> Text -> Either String Text
+ Nixfmt.Parser: fixFirstLine :: [[StringPart]] -> [[StringPart]]
+ Nixfmt.Parser: fixLastLine :: [[StringPart]] -> [[StringPart]]
+ Nixfmt.Parser: lineHead :: [StringPart] -> Maybe Text
+ Nixfmt.Parser: normalizeLine :: [StringPart] -> [StringPart]
+ Nixfmt.Parser: pathText :: Parser StringPart
+ Nixfmt.Parser: pathTraversal :: Parser [StringPart]
+ Nixfmt.Pretty: isInvisibleLine :: [StringPart] -> Bool
+ Nixfmt.Pretty: lastLineIsSpaces :: [[StringPart]] -> Bool
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.Binder
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.Expression
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.File
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.Fixity
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.Operator
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.ParamAttr
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.Parameter
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.Selector
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.SimpleSelector
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.StringPart
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.Term
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.TrailingComment
+ Nixfmt.Types: instance GHC.Classes.Eq Nixfmt.Types.Trivium
+ Nixfmt.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Nixfmt.Types.Ann a)
+ Nixfmt.Types: type Path = Ann [StringPart]
+ Nixfmt.Util: isSpaces :: Text -> Bool
+ Nixfmt.Util: replaceMultiple :: [(Text, Text)] -> Text -> Text
- Nixfmt: errorBundlePretty :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> String
+ Nixfmt: errorBundlePretty :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => ParseErrorBundle s e -> String
- Nixfmt: format :: Int -> FilePath -> Text -> Either ParseErrorBundle Text
+ Nixfmt: format :: Width -> FilePath -> Text -> Either String Text
- Nixfmt.Parser: path :: Parser (Ann Token)
+ Nixfmt.Parser: path :: Parser Path
- Nixfmt.Types: Path :: Text -> Token
+ Nixfmt.Types: Path :: Path -> Term
Files
- CHANGELOG.md +7/−0
- README.md +4/−0
- js/JSInterface.hs +1/−1
- js/js-interface-wrapper.js +0/−10
- main/Main.hs +21/−15
- main/System/IO/Utf8.hs +1/−1
- nixfmt.cabal +19/−11
- src/Nixfmt.hs +24/−3
- src/Nixfmt/Parser.hs +49/−35
- src/Nixfmt/Pretty.hs +28/−17
- src/Nixfmt/Types.hs +21/−15
- src/Nixfmt/Util.hs +28/−1
CHANGELOG.md view
@@ -7,6 +7,13 @@ # Revision history for nixfmt +## 0.5.0 -- 2022-03-15+* Add a nix flake to the nixfmt project.+* Add a --verify flag to check idempotency.+* Support nix path (`./${foo}.nix`) interpolations.+* Fix escaping of interpolations after single quotes.+* Fix handling of multiline strings with spaces in the last line.+ ## 0.4.0 -- 2020-02-10 * Report non-conforming files on the same line to aid line-oriented processing * Fix help, summary, and version flag contents.
README.md view
@@ -25,6 +25,10 @@ cachix use nixfmt nix-env -f https://github.com/serokell/nixfmt/archive/master.tar.gz -i +- Nix with flakes++ nix profile install github:serokell/nixfmt+ ## Development ### With Nix
js/JSInterface.hs view
@@ -29,7 +29,7 @@ out <- case format width filename text of Left err -> do setProp "err" (toJSBool True) obj- toJSVal $ S.pack $ errorBundlePretty err+ toJSVal $ S.pack err Right out_ -> do setProp "err" (toJSBool False) obj toJSVal out_
− js/js-interface-wrapper.js
@@ -1,10 +0,0 @@-/* © 2019 Serokell <hi@serokell.io>- *- * SPDX-License-Identifier: MPL-2.0- */--function nixfmt(text, width=80, filename="<stdin>") {- const param = {width, filename}- nixfmt_(text, param)- return {text: param.ret, err: param.err}-}
main/Main.hs view
@@ -9,7 +9,6 @@ module Main where import Control.Concurrent (Chan, forkIO, newChan, readChan, writeChan)-import Data.Bifunctor (first) import Data.Either (lefts) import Data.Text (Text) import Data.Version (showVersion)@@ -24,7 +23,7 @@ import qualified Data.Text.IO as TextIO (getContents, hPutStr, putStr) -import Nixfmt+import qualified Nixfmt import System.IO.Atomic (withOutputFile) import System.IO.Utf8 (readFileUtf8, withUtf8StdHandles) @@ -36,6 +35,7 @@ , width :: Width , check :: Bool , quiet :: Bool+ , verify :: Bool } deriving (Show, Data, Typeable) options :: Nixfmt@@ -44,30 +44,28 @@ , width = 80 &= help "Maximum width in characters" , check = False &= help "Check whether files are formatted" , quiet = False &= help "Do not report errors"+ , verify = False &= help "Check that the output parses and formats the same as the input" } &= summary ("nixfmt v" ++ showVersion version) &= help "Format Nix source code" -format' :: Width -> FilePath -> Text -> Either String Text-format' w path = first errorBundlePretty . format w path- data Target = Target { tDoRead :: IO Text , tPath :: FilePath , tDoWrite :: Text -> IO () } -formatTarget :: Width -> Target -> IO Result-formatTarget w Target{tDoRead, tPath, tDoWrite} = do+formatTarget :: Formatter -> Target -> IO Result+formatTarget format Target{tDoRead, tPath, tDoWrite} = do contents <- tDoRead- let formatted = format' w tPath contents+ let formatted = format tPath contents mapM tDoWrite formatted -- | Return an error if target could not be parsed or was not formatted -- correctly.-checkTarget :: Width -> Target -> IO Result-checkTarget w Target{tDoRead, tPath} = do+checkTarget :: Formatter -> Target -> IO Result+checkTarget format Target{tDoRead, tPath} = do contents <- tDoRead- return $ case format' w tPath contents of+ return $ case format tPath contents of Left err -> Left err Right formatted | formatted == contents -> Right ()@@ -87,16 +85,24 @@ toTargets Nixfmt{ files = [] } = [stdioTarget] toTargets Nixfmt{ files = paths } = map fileTarget paths -toOperation :: Nixfmt -> Target -> IO Result-toOperation Nixfmt{ width = w, check = True } = checkTarget w-toOperation Nixfmt{ width = w } = formatTarget w+type Formatter = FilePath -> Text -> Either String Text +toFormatter :: Nixfmt -> Formatter+toFormatter Nixfmt{ width, verify = True } = Nixfmt.formatVerify width+toFormatter Nixfmt{ width, verify = False } = Nixfmt.format width++type Operation = Formatter -> Target -> IO Result++toOperation :: Nixfmt -> Operation+toOperation Nixfmt{ check = True } = checkTarget+toOperation Nixfmt{ } = formatTarget+ toWriteError :: Nixfmt -> String -> IO () toWriteError Nixfmt{ quiet = False } = hPutStrLn stderr toWriteError Nixfmt{ quiet = True } = const $ return () toJobs :: Nixfmt -> [IO Result]-toJobs opts = map (toOperation opts) $ toTargets opts+toJobs opts = map (toOperation opts $ toFormatter opts) $ toTargets opts -- TODO: Efficient parallel implementation. This is just a sequential stub. -- This was originally implemented using parallel-io, but it gave a factor two
main/System/IO/Utf8.hs view
@@ -10,7 +10,7 @@ -- -- Standard IO functions assume that the character encoding of the data -- they read or write is the same as the one used by current locale. In many--- situtations this assumption is wrong, as tools work with files, and+-- situations this assumption is wrong, as tools work with files, and -- the files nowadays are mostly UTF-8 encoded, regardless of the locale. -- Therefore, it is almost always a good idea to switch the encoding of -- file handles to UTF-8.
nixfmt.cabal view
@@ -1,12 +1,12 @@ cabal-version: 2.0 --- © 2019 Serokell <hi@serokell.io>--- © 2019 Lars Jellema <lars.jellema@gmail.com>+-- © 2022 Serokell <hi@serokell.io>+-- © 2022 Lars Jellema <lars.jellema@gmail.com> -- -- SPDX-License-Identifier: MPL-2.0 name: nixfmt-version: 0.4.0+version: 0.5.0 synopsis: An opinionated formatter for Nix description: A formatter for Nix that ensures consistent and clear formatting by forgetting@@ -17,7 +17,7 @@ license-file: LICENSE author: Lars Jellema maintainer: lars.jellema@gmail.com-copyright: 2019 Serokell, 2019 Lars Jellema+copyright: 2022 Serokell, 2022 Lars Jellema category: Development build-type: Simple extra-source-files: README.md, CHANGELOG.md@@ -41,7 +41,7 @@ else buildable: True build-depends:- base >= 4.12.0 && < 4.13+ base >= 4.12.0 && < 4.17 , cmdargs >= 0.10.20 && < 0.11 , nixfmt , unix >= 2.7.2 && < 2.8@@ -69,12 +69,21 @@ Nixfmt.Pretty Nixfmt.Types Nixfmt.Util- other-extensions: OverloadedStrings, LambdaCase, FlexibleInstances, DeriveFoldable, DeriveFunctor, StandaloneDeriving++ other-extensions:+ DeriveFoldable+ DeriveFunctor+ FlexibleInstances+ LambdaCase+ OverloadedStrings+ StandaloneDeriving+ TupleSections+ hs-source-dirs: src build-depends:- base >= 4.12.0 && < 4.13- , megaparsec >= 7.0.5 && < 7.1- , parser-combinators >= 1.0.3 && < 1.3+ base >= 4.12.0 && < 4.17+ , megaparsec >= 9.0.1 && < 9.3+ , parser-combinators >= 1.0.3 && < 1.4 , text >= 1.2.3 && < 1.3 default-language: Haskell2010 ghc-options:@@ -98,10 +107,9 @@ -Wredundant-constraints -Wno-orphans build-depends:- base >= 4.12.0 && < 4.13+ base >= 4.12.0 && < 4.17 , ghcjs-base >= 0.2.0 && < 0.3 , nixfmt- js-sources: js/js-interface-wrapper.js hs-source-dirs: js/ else buildable: False
src/Nixfmt.hs view
@@ -8,10 +8,12 @@ ( errorBundlePretty , ParseErrorBundle , format+ , formatVerify ) where +import Data.Bifunctor (bimap, first) import Data.Text (Text)-import Text.Megaparsec (parse)+import qualified Text.Megaparsec as Megaparsec (parse) import Text.Megaparsec.Error (errorBundlePretty) import Nixfmt.Parser (file)@@ -19,8 +21,27 @@ import Nixfmt.Pretty () import Nixfmt.Types (ParseErrorBundle) +type Width = Int+ -- | @format w filename source@ returns either a parsing error specifying a -- failure in @filename@ or a formatted version of @source@ with a maximum width -- of @w@ columns where possible.-format :: Int -> FilePath -> Text -> Either ParseErrorBundle Text-format width filename = fmap (layout width) . parse file filename+format :: Width -> FilePath -> Text -> Either String Text+format width filename+ = bimap errorBundlePretty (layout width)+ . Megaparsec.parse file filename++formatVerify :: Width -> FilePath -> Text -> Either String Text+formatVerify width path unformatted = do+ unformattedParsed <- parse unformatted+ let formattedOnce = layout width unformattedParsed+ formattedOnceParsed <- parse formattedOnce+ let formattedTwice = layout width formattedOnceParsed+ if formattedOnceParsed /= unformattedParsed+ then pleaseReport "Parses differently after formatting."+ else if formattedOnce /= formattedTwice+ then pleaseReport "Nixfmt is not idempotent."+ else Right formattedOnce+ where+ parse = first errorBundlePretty . Megaparsec.parse file path+ pleaseReport x = Left $ path <> ": " <> x <> " This is a bug in nixfmt. Please report it at https://github.com/serokell/nixfmt"
src/Nixfmt/Parser.hs view
@@ -16,23 +16,22 @@ (Operator(..), makeExprParser) import Data.Char (isAlpha) import Data.Foldable (toList)-import Data.Maybe (fromMaybe)-import Data.Text as Text- (Text, cons, empty, null, singleton, split, strip, stripPrefix)+import Data.Maybe (fromMaybe, mapMaybe, maybeToList)+import Data.Text as Text (Text, cons, empty, singleton, split, stripPrefix) import Text.Megaparsec (anySingle, chunk, eof, label, lookAhead, many, notFollowedBy, oneOf,- optional, satisfy, try, (<|>))+ optional, satisfy, some, try, (<|>)) import Text.Megaparsec.Char (char) import qualified Text.Megaparsec.Char.Lexer as L (decimal, float) import Nixfmt.Lexer (lexeme) import Nixfmt.Types (Ann, Binder(..), Expression(..), File(..), Fixity(..), Leaf, Operator(..),- ParamAttr(..), Parameter(..), Parser, Selector(..), SimpleSelector(..),+ ParamAttr(..), Parameter(..), Parser, Path, Selector(..), SimpleSelector(..), String, StringPart(..), Term(..), Token(..), operators, tokenText) import Nixfmt.Util- (commonIndentation, identChar, manyP, manyText, pathChar, schemeChar, someP,- someText, uriChar)+ (commonIndentation, identChar, isSpaces, manyP, manyText, pathChar,+ schemeChar, someP, someText, uriChar) -- HELPER FUNCTIONS @@ -83,9 +82,16 @@ someP pathChar <> manyText (slash <> someP pathChar) <* char '>' -path :: Parser (Ann Token)-path = ann Path $ manyP pathChar <> someText (slash <> someP pathChar)+pathText :: Parser StringPart+pathText = TextPart <$> someP pathChar +pathTraversal :: Parser [StringPart]+pathTraversal = liftM2 (:) (TextPart <$> slash) (some (pathText <|> interpolation))++path :: Parser Path+path = try $ lexeme $ fmap normalizeLine $+ (maybeToList <$> optional pathText) <> (concat <$> some pathTraversal)+ uri :: Parser [[StringPart]] uri = fmap (pure . pure . TextPart) $ try $ someP schemeChar <> chunk ":" <> someP uriChar@@ -124,23 +130,28 @@ isEmptyLine :: [StringPart] -> Bool isEmptyLine [] = True-isEmptyLine [TextPart t] = Text.null (Text.strip t)+isEmptyLine [TextPart t] = isSpaces t isEmptyLine _ = False --- | Strip the first line of a string if it is empty.-stripFirstLine :: [[StringPart]] -> [[StringPart]]-stripFirstLine [] = []-stripFirstLine (x : xs)- | isEmptyLine x = xs- | otherwise = x : xs+-- | Drop the first line of a string if it is empty.+fixFirstLine :: [[StringPart]] -> [[StringPart]]+fixFirstLine [] = []+fixFirstLine (x : xs) = if isEmptyLine x' then xs else x' : xs+ where x' = normalizeLine x -textHeads :: [StringPart] -> [Text]-textHeads line@(TextPart t : _)- | isEmptyLine line = []- | otherwise = [t]-textHeads (Interpolation _ _ _ : _) = [""]-textHeads [] = []+-- | Empty the last line if it contains only spaces.+fixLastLine :: [[StringPart]] -> [[StringPart]]+fixLastLine [] = []+fixLastLine [line] = if isEmptyLine line' then [[]] else [line']+ where line' = normalizeLine line+fixLastLine (x : xs) = x : fixLastLine xs +lineHead :: [StringPart] -> Maybe Text+lineHead [] = Nothing+lineHead line | isEmptyLine line = Nothing+lineHead (TextPart t : _) = Just t+lineHead (Interpolation _ _ _ : _) = Just ""+ stripParts :: Text -> [StringPart] -> [StringPart] stripParts indentation (TextPart t : xs) = TextPart (fromMaybe Text.empty $ Text.stripPrefix indentation t) : xs@@ -162,28 +173,31 @@ _ -> error "unreachable" stripIndentation :: [[StringPart]] -> [[StringPart]]-stripIndentation parts = case commonIndentation (concatMap textHeads parts) of+stripIndentation parts = case commonIndentation $ mapMaybe lineHead parts of Nothing -> map (const []) parts Just indentation -> map (stripParts indentation) parts -dropEmptyParts :: [[StringPart]] -> [[StringPart]]-dropEmptyParts = map $ filter (\case- TextPart t | Text.null t -> False- _ -> True)+normalizeLine :: [StringPart] -> [StringPart]+normalizeLine [] = []+normalizeLine (TextPart "" : xs) = normalizeLine xs+normalizeLine (TextPart x : TextPart y : xs) = normalizeLine (TextPart (x <> y) : xs)+normalizeLine (x : xs) = x : normalizeLine xs fixSimpleString :: [StringPart] -> [[StringPart]]-fixSimpleString parts = case splitLines parts of- [] -> []- [line] -> [line]- parts' -> dropEmptyParts (stripIndentation parts')+fixSimpleString = map normalizeLine . splitLines simpleString :: Parser [[StringPart]] simpleString = rawSymbol TDoubleQuote *>- fmap splitLines (many (simpleStringPart <|> interpolation)) <*+ fmap fixSimpleString (many (simpleStringPart <|> interpolation)) <* rawSymbol TDoubleQuote fixIndentedString :: [[StringPart]] -> [[StringPart]]-fixIndentedString = dropEmptyParts . concatMap splitLines . stripIndentation . stripFirstLine+fixIndentedString+ = map normalizeLine+ . concatMap splitLines+ . stripIndentation+ . fixLastLine+ . fixFirstLine indentedString :: Parser [[StringPart]] indentedString = rawSymbol TDoubleSingleQuote *>@@ -212,8 +226,8 @@ many (selector $ Just $ symbol TDot) simpleTerm :: Parser Term-simpleTerm = (String <$> string) <|>- (Token <$> (path <|> envPath <|> float <|> integer <|> identifier)) <|>+simpleTerm = (String <$> string) <|> (Path <$> path) <|>+ (Token <$> (envPath <|> float <|> integer <|> identifier)) <|> parens <|> set <|> list term :: Parser Term
src/Nixfmt/Pretty.hs view
@@ -23,7 +23,7 @@ (Ann(..), Binder(..), Expression(..), File(..), Leaf, ParamAttr(..), Parameter(..), Selector(..), SimpleSelector(..), StringPart(..), Term(..), Token(..), TrailingComment(..), Trivia, Trivium(..), tokenText)-import Nixfmt.Util (commonIndentation)+import Nixfmt.Util (commonIndentation, isSpaces, replaceMultiple) prettyCommentLine :: Text -> Doc prettyCommentLine l@@ -88,6 +88,7 @@ prettyTerm :: Term -> Doc prettyTerm (Token t) = pretty t prettyTerm (String s) = pretty s+prettyTerm (Path p) = pretty p prettyTerm (Selection term selectors) = pretty term <> hcat selectors prettyTerm (List (Ann paropen Nothing []) [] parclose)@@ -301,23 +302,32 @@ nonEmpty _ = True inits = map textInit $ filter nonEmpty parts -isEmptyLine :: [StringPart] -> Bool-isEmptyLine [] = True-isEmptyLine [TextPart t] = Text.null $ Text.strip t-isEmptyLine _ = False+-- | If the last line has at least one space but nothing else, it cannot be+-- cleanly represented in an indented string.+lastLineIsSpaces :: [[StringPart]] -> Bool+lastLineIsSpaces [] = False+lastLineIsSpaces xs = case last xs of+ [TextPart t] -> isSpaces t+ _ -> False +isInvisibleLine :: [StringPart] -> Bool+isInvisibleLine [] = True+isInvisibleLine [TextPart t] = Text.null $ Text.strip t+isInvisibleLine _ = False+ isSimpleString :: [[StringPart]] -> Bool isSimpleString [parts]- | hasDualQuotes parts = True- | endsInSingleQuote parts = True- | isIndented [parts] = True- | hasQuotes parts = False- | otherwise = True+ | hasDualQuotes parts = True+ | endsInSingleQuote parts = True+ | isIndented [parts] = True+ | hasQuotes parts = False+ | otherwise = True isSimpleString parts- | all isEmptyLine parts = True- | isIndented parts = True- | otherwise = False+ | all isInvisibleLine parts = True+ | isIndented parts = True+ | lastLineIsSpaces parts = True+ | otherwise = False instance Pretty StringPart where pretty (TextPart t) = text t@@ -385,10 +395,11 @@ text "''" <> line' <> nest 2 (sepBy newline (map (prettyLine escape unescapeInterpol) parts)) <> text "''"- where escape- = Text.replace "$''${" "$${"- . Text.replace "${" "''${"- . Text.replace "''" "'''"+ where escape = replaceMultiple+ [ ("'${", "''\\'''${")+ , ("${", "''${")+ , ("''", "'''")+ ] unescapeInterpol t | Text.null t = t
src/Nixfmt/Types.hs view
@@ -24,59 +24,67 @@ = EmptyLine | LineComment Text | BlockComment [Text]- deriving (Show)+ deriving (Eq, Show) type Trivia = [Trivium] -newtype TrailingComment = TrailingComment Text deriving (Show)+newtype TrailingComment = TrailingComment Text deriving (Eq, Show) data Ann a = Ann a (Maybe TrailingComment) Trivia deriving (Show) +-- | Equality of annotated syntax is defines as equality of their corresponding+-- semantics, thus ignoring the annotations.+instance Eq a => Eq (Ann a) where+ Ann x _ _ == Ann y _ _ = x == y+ type Leaf = Ann Token data StringPart = TextPart Text | Interpolation Leaf Expression Token- deriving (Show)+ deriving (Eq, Show) +type Path = Ann [StringPart]+ type String = Ann [[StringPart]] data SimpleSelector = IDSelector Leaf | InterpolSelector (Ann StringPart) | StringSelector String- deriving (Show)+ deriving (Eq, Show) data Selector = Selector (Maybe Leaf) SimpleSelector (Maybe (Leaf, Term))- deriving (Show)+ deriving (Eq, Show) data Binder = Inherit Leaf (Maybe Term) [Leaf] Leaf | Assignment [Selector] Leaf Expression Leaf- deriving (Show)+ deriving (Eq, Show) data Term = Token Leaf | String String+ | Path Path | List Leaf [Term] Leaf | Set (Maybe Leaf) Leaf [Binder] Leaf | Selection Term [Selector] | Parenthesized Leaf Expression Leaf- deriving (Show)+ deriving (Eq, Show) data ParamAttr = ParamAttr Leaf (Maybe (Leaf, Expression)) (Maybe Leaf) | ParamEllipsis Leaf- deriving (Show)+ deriving (Eq, Show) data Parameter = IDParameter Leaf | SetParameter Leaf [ParamAttr] Leaf | ContextParameter Parameter Leaf Parameter- deriving (Show)+ deriving (Eq, Show) data Expression = Term Term@@ -91,17 +99,16 @@ | MemberCheck Expression Leaf [Selector] | Negation Leaf Expression | Inversion Leaf Expression- deriving (Show)+ deriving (Eq, Show) data File = File Leaf Expression- deriving (Show)+ deriving (Eq, Show) data Token = Integer Int | Float Double | Identifier Text- | Path Text | EnvPath Text | KAssert@@ -165,12 +172,12 @@ | InfixN | InfixR | Postfix- deriving (Show)+ deriving (Eq, Show) data Operator = Op Fixity Token | Apply- deriving (Show)+ deriving (Eq, Show) -- | A list of lists of operators where lists that come first contain operators -- that bind more strongly.@@ -201,7 +208,6 @@ tokenText (Identifier i) = i tokenText (Integer i) = pack (show i) tokenText (Float f) = pack (show f)-tokenText (Path p) = p tokenText (EnvPath p) = "<" <> p <> ">" tokenText KAssert = "assert"
src/Nixfmt/Util.hs view
@@ -4,6 +4,8 @@ - SPDX-License-Identifier: MPL-2.0 -} +{-# LANGUAGE TupleSections #-}+ module Nixfmt.Util ( manyP , someP@@ -13,15 +15,20 @@ , commonIndentation , dropCommonIndentation , identChar+ , isSpaces , pathChar+ , replaceMultiple , schemeChar , uriChar ) where +import Control.Applicative ((<|>)) import Data.Char (isAlpha, isDigit, isSpace)+import Data.Foldable (asum)+import Data.List (unfoldr) import Data.Maybe (fromMaybe) import Data.Text as Text- (Text, commonPrefixes, concat, empty, stripEnd, stripPrefix, takeWhile)+ (Text, all, commonPrefixes, concat, empty, null, splitAt, stripEnd, stripPrefix, takeWhile) import Text.Megaparsec (ParsecT, Stream, Token, Tokens, many, some, takeWhile1P, takeWhileP) @@ -78,3 +85,23 @@ in case commonIndentation (filter (/=empty) strippedLines) of Nothing -> map (const empty) strippedLines Just indentation -> map (fromMaybe empty . stripPrefix indentation) strippedLines++isSpaces :: Text -> Bool+isSpaces = Text.all (==' ')++-- | Apply multiple independent replacements. This function passes over the text+-- once and applies the first replacement it can find at each position. After a+-- replacement is matched, the function continues after the replacement, not+-- inside it.+replaceMultiple :: [(Text, Text)] -> Text -> Text+replaceMultiple replacements = mconcat . unfoldr replaceAny+ where+ -- | replaceAny assumes input is nonempty+ replaceAny :: Text -> Maybe (Text, Text)+ replaceAny t+ | Text.null t = Nothing+ | otherwise = asum (map (replaceStart t) replacements)+ <|> Just (Text.splitAt 1 t)++ replaceStart :: Text -> (Text, Text) -> Maybe (Text, Text)+ replaceStart t (pat, rep) = (rep,) <$> Text.stripPrefix pat t