Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maintain expanded attrs/lists/attr params/inherits #224

Merged
merged 11 commits into from
Aug 8, 2024
7 changes: 5 additions & 2 deletions main/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import qualified Data.Text.IO as TextIO (getContents, hPutStr, putStr)
import Data.Version (showVersion)
import GHC.IO.Encoding (utf8)
import qualified Nixfmt
import Nixfmt.Predoc (layout)
import Paths_nixfmt (version)
import System.Console.CmdArgs (
Data,
Expand Down Expand Up @@ -41,6 +42,7 @@ data Nixfmt = Nixfmt
width :: Width,
check :: Bool,
quiet :: Bool,
strict :: Bool,
verify :: Bool,
ast :: Bool
}
Expand All @@ -58,6 +60,7 @@ options =
&= help (addDefaultHint defaultWidth "Maximum width in characters"),
check = False &= help "Check whether files are formatted without modifying them",
quiet = False &= help "Do not report errors",
strict = False &= help "Enable a stricter formatting mode that isn't influenced as much by how the input is formatted",
verify =
False
&= help
Expand Down Expand Up @@ -134,8 +137,8 @@ type Formatter = FilePath -> Text -> Either String Text

toFormatter :: Nixfmt -> Formatter
toFormatter Nixfmt{ast = True} = Nixfmt.printAst
toFormatter Nixfmt{width, verify = True} = Nixfmt.formatVerify width
toFormatter Nixfmt{width, verify = False} = Nixfmt.format width
toFormatter Nixfmt{width, verify = True, strict} = Nixfmt.formatVerify (layout width strict)
toFormatter Nixfmt{width, verify = False, strict} = Nixfmt.format (layout width strict)

type Operation = Formatter -> Target -> IO Result

Expand Down
31 changes: 17 additions & 14 deletions src/Nixfmt.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{-# LANGUAGE RankNTypes #-}

module Nixfmt (
errorBundlePretty,
ParseErrorBundle,
Expand All @@ -13,23 +15,24 @@ import Data.Either (fromRight)
import Data.Text (Text, unpack)
import Data.Text.Lazy (toStrict)
import qualified Nixfmt.Parser as Parser
import Nixfmt.Predoc (layout)
import Nixfmt.Predoc (Pretty)
import Nixfmt.Pretty ()
import Nixfmt.Types (Expression, ParseErrorBundle, Whole (..), walkSubprograms)
import Nixfmt.Types (Expression, LanguageElement, ParseErrorBundle, Whole (..), walkSubprograms)
import qualified Text.Megaparsec as Megaparsec (parse)
import Text.Megaparsec.Error (errorBundlePretty)
import Text.Pretty.Simple (pShow)

-- import Debug.Trace (traceShow, traceShowId)

type Width = Int
type Layouter = forall a. (Pretty a, LanguageElement a) => a -> Text

-- | @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 :: Width -> FilePath -> Text -> Either String Text
format width filename =
bimap errorBundlePretty (layout width)
format :: Layouter -> FilePath -> Text -> Either String Text
format layout filename =
bimap errorBundlePretty layout
. Megaparsec.parse Parser.file filename

-- | Pretty print the internal AST for debugging
Expand All @@ -44,34 +47,34 @@ printAst path unformatted = do
--
-- If any issues are found, the operation will fail and print an error message. It will contain a diff showcasing
-- the issue on an automatically minimized example based on the input.
formatVerify :: Width -> FilePath -> Text -> Either String Text
formatVerify width path unformatted = do
formatVerify :: Layouter -> FilePath -> Text -> Either String Text
formatVerify layout path unformatted = do
unformattedParsed@(Whole unformattedParsed' _) <- parse unformatted
let formattedOnce = layout width unformattedParsed
let formattedOnce = layout unformattedParsed
formattedOnceParsed <- first (\x -> pleaseReport "Fails to parse after formatting.\n" <> x <> "\n\nAfter Formatting:\n" <> unpack formattedOnce) (parse formattedOnce)
let formattedTwice = layout width formattedOnceParsed
let formattedTwice = layout formattedOnceParsed
if formattedOnceParsed /= unformattedParsed
then
Left $
let minimized = minimize unformattedParsed' (\e -> parse (layout width e) == Right (Whole e []))
let minimized = minimize unformattedParsed' (\e -> parse (layout e) == Right (Whole e []))
in pleaseReport "Parses differently after formatting."
<> "\n\nBefore formatting:\n"
<> show minimized
<> "\n\nAfter formatting:\n"
<> show (fromRight (error "TODO") $ parse (layout width minimized))
<> show (fromRight (error "TODO") $ parse (layout minimized))
else
if formattedOnce /= formattedTwice
then
Left $
let minimized =
minimize
unformattedParsed'
(\e -> layout width e == layout width (fromRight (error "TODO") $ parse $ layout width e))
(\e -> layout e == layout (fromRight (error "TODO") $ parse $ layout e))
in pleaseReport "Nixfmt is not idempotent."
<> "\n\nAfter one formatting:\n"
<> unpack (layout width minimized)
<> unpack (layout minimized)
<> "\n\nAfter two:\n"
<> unpack (layout width (fromRight (error "TODO") $ parse $ layout width minimized))
<> unpack (layout (fromRight (error "TODO") $ parse $ layout minimized))
else Right formattedOnce
where
parse = first errorBundlePretty . Megaparsec.parse Parser.file path
Expand Down
9 changes: 8 additions & 1 deletion src/Nixfmt/Lexer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,20 @@ pushTrivia t = modify (<> t)
lexeme :: Parser a -> Parser (Ann a)
lexeme p = do
lastLeading <- takeTrivia
SourcePos{Text.Megaparsec.sourceLine = line} <- getSourcePos
piegamesde marked this conversation as resolved.
Show resolved Hide resolved
token <- preLexeme p
parsedTrivia <- trivia
-- This is the position of the next lexeme after the currently parsed one
SourcePos{sourceColumn = col} <- getSourcePos
let (trailing, nextLeading) = convertTrivia parsedTrivia col
pushTrivia nextLeading
return $ Ann lastLeading token trailing
return $
Ann
{ preTrivia = lastLeading,
value = token,
Nixfmt.Types.sourceLine = line,
trailComment = trailing
}

-- | Tokens normally have only leading trivia and one trailing comment on the same
-- line. A whole x also parses and stores final trivia after the x. A whole also
Expand Down
16 changes: 14 additions & 2 deletions src/Nixfmt/Predoc.hs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ import qualified Data.List.NonEmpty as NonEmpty
import Data.Maybe (fromMaybe)
import Data.Text as Text (Text, concat, length, replicate, strip)
import GHC.Stack (HasCallStack)
import Nixfmt.Types (
LanguageElement,
mapAllTokens,
removeLineInfo,
)

-- | Sequential Spacings are reduced to a single Spacing by taking the maximum.
-- This means that e.g. a Space followed by an Emptyline results in just an
Expand Down Expand Up @@ -342,8 +347,15 @@ mergeSpacings Hardspace (Newlines x) = Newlines x
mergeSpacings _ (Newlines x) = Newlines (x + 1)
mergeSpacings _ y = y

layout :: (Pretty a) => Int -> a -> Text
layout w = (<> "\n") . Text.strip . layoutGreedy w . fixup . pretty
layout :: (Pretty a, LanguageElement a) => Int -> Bool -> a -> Text
layout width strict =
(<> "\n")
. Text.strip
. layoutGreedy width
. fixup
. pretty
-- In strict mode, set the line number of all tokens to zero
. (if strict then mapAllTokens removeLineInfo else id)

-- 1. Move and merge Spacings.
-- 2. Convert Softlines to Grouped Lines and Hardspaces to Texts.
Expand Down
Loading