Switch to prettyprinter

This commit is contained in:
John Ericson 2018-11-16 23:51:18 -05:00
parent a34dddb669
commit 04abd357bd
11 changed files with 224 additions and 190 deletions

View File

@ -2,7 +2,7 @@
--
-- see: https://github.com/sol/hpack
--
-- hash: 7e613ce82a3337411d625301abd33a6d7f1c400edadbd602287027f2af1e4fdf
-- hash: 0e0438f43eaffbbd952e85bc24499b40e17f276ec7488f3a483e7d848ea1075d
cabal-version: >= 1.10
name: hnix
@ -486,7 +486,6 @@ library
ghc-options: -Wall
build-depends:
aeson
, ansi-wl-pprint
, array >=0.4 && <0.6
, base >=4.9 && <5
, binary
@ -510,6 +509,7 @@ library
, monadlist
, mtl
, optparse-applicative
, prettyprinter
, process
, regex-tdfa
, regex-tdfa-text
@ -574,7 +574,6 @@ executable hnix
ghc-options: -Wall
build-depends:
aeson
, ansi-wl-pprint
, base >=4.9 && <5
, bytestring
, containers
@ -588,6 +587,7 @@ executable hnix
, mtl
, optparse-applicative
, pretty-show
, prettyprinter
, repline
, template-haskell
, text
@ -633,7 +633,6 @@ test-suite hnix-tests
build-depends:
Diff
, Glob
, ansi-wl-pprint
, base >=4.9 && <5
, bytestring
, containers
@ -651,6 +650,7 @@ test-suite hnix-tests
, mtl
, optparse-applicative
, pretty-show
, prettyprinter
, process
, split
, tasty
@ -696,8 +696,7 @@ benchmark hnix-benchmarks
benchmarks
ghc-options: -Wall
build-depends:
ansi-wl-pprint
, base >=4.9 && <5
base >=4.9 && <5
, bytestring
, containers
, criterion

View File

@ -24,6 +24,8 @@ import qualified Data.Text as Text
import qualified Data.Text.IO as Text
import qualified Data.Text.Lazy.Encoding as TL
import qualified Data.Text.Lazy.IO as TL
import Data.Text.Prettyprint.Doc
import Data.Text.Prettyprint.Doc.Render.Text
import Nix
import Nix.Convert
import qualified Nix.Eval as Eval
@ -36,7 +38,6 @@ import Options.Applicative hiding (ParserResult(..))
import qualified Repl
import System.FilePath
import System.IO
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
import qualified Text.Show.Pretty as PS
main :: IO ()
@ -127,8 +128,8 @@ main = do
void $ liftIO $ Exc.evaluate $ Deep.force expr
| otherwise =
liftIO $ displayIO stdout
. renderPretty 0.4 80
liftIO $ renderIO stdout
. layoutPretty (LayoutOptions $ AvailablePerLine 80 0.4)
. prettyNix
. stripAnnotation $ expr
where

View File

@ -55,7 +55,6 @@ ghc-options:
dependencies:
- base >= 4.9 && < 5
- ansi-wl-pprint
- bytestring
- containers
- data-fix
@ -98,7 +97,6 @@ library:
source-dirs: src
dependencies:
- aeson
- ansi-wl-pprint
- array >= 0.4 && < 0.6
- binary
- deriving-compat >= 0.3 && < 0.6
@ -112,6 +110,7 @@ library:
- logict
- megaparsec >= 6.5 && < 7.0
- monadlist
- prettyprinter
- process
- regex-tdfa
- regex-tdfa-text
@ -158,6 +157,7 @@ executables:
- hnix
- aeson
- pretty-show
- prettyprinter
- repline
- haskeline
when:
@ -193,6 +193,7 @@ tests:
- megaparsec
- tasty-quickcheck
- pretty-show
- prettyprinter
when:
- condition: "impl(ghcjs)"
then:

View File

@ -45,9 +45,10 @@ import Data.List
import qualified Data.List.NonEmpty as NE
import Data.List.Split
import Data.Maybe (maybeToList)
import Data.Monoid
import Data.Semigroup
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Text.Prettyprint.Doc
import Data.Typeable
import GHC.IO.Exception (IOErrorType(..))
import Network.HTTP.Client
@ -81,8 +82,6 @@ import qualified System.Info
import System.IO.Error
import System.Posix.Files
import System.Process (readProcessWithExitCode)
import Text.PrettyPrint.ANSI.Leijen (text)
import qualified Text.PrettyPrint.ANSI.Leijen as P
#ifdef MIN_VERSION_pretty_show
import qualified Text.Show.Pretty as PS
#endif
@ -558,8 +557,10 @@ instance (MonadFix m, MonadCatch m, MonadIO m, Alternative m,
eres <- Lazy $ parseNixFileLoc path
case eres of
Failure err ->
throwError $ ErrorCall . show $
text "Parse during import failed:" P.</> err
throwError $ ErrorCall . show $ fillSep $
[ "Parse during import failed:"
, err
]
Success expr -> do
Lazy $ ReaderT $ const $
modify (M.insert path expr)
@ -781,16 +782,16 @@ addTracing k v = do
let rendered =
if verbose opts >= Chatty
#ifdef MIN_VERSION_pretty_show
then text $ PS.ppShow (void x)
then pretty $ PS.ppShow (void x)
#else
then text $ show (void x)
then pretty $ show (void x)
#endif
else prettyNix (Fix (Fix (NSym "?") <$ x))
msg x = text ("eval: " ++ replicate depth ' ') <> x
loc <- renderLocation span (msg rendered <> text " ...\n")
msg x = pretty ("eval: " ++ replicate depth ' ') <> x
loc <- renderLocation span (msg rendered <> " ...\n")
liftIO $ putStr $ show loc
res <- k v'
liftIO $ print $ msg rendered <> text " ...done"
liftIO $ print $ msg rendered <> " ...done"
return res
evalExprLoc :: forall e m. (MonadNix e m, Has e Options, MonadIO m)

View File

@ -5,10 +5,10 @@ import Data.Char (isDigit)
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Text.Prettyprint.Doc
import Data.Time
import Nix.Options
import Options.Applicative hiding (ParserResult(..))
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
decodeVerbosity :: Int -> Verbosity
decodeVerbosity 0 = ErrorsOnly

View File

@ -62,6 +62,7 @@ import qualified Data.List.NonEmpty as NE
import qualified Data.Map as Map
import Data.Text (Text)
import Data.Text hiding (map, foldr1, concat, concatMap, zipWith)
import Data.Text.Prettyprint.Doc (Doc, pretty)
import qualified Data.Text.IO as T
import Data.Typeable (Typeable)
import Data.Void
@ -72,7 +73,6 @@ import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
import Text.Megaparsec.Expr
import Text.PrettyPrint.ANSI.Leijen (Doc, text)
infixl 3 <+>
(<+>) :: MonadPlus m => m a -> m a -> m a
@ -437,17 +437,17 @@ reservedNames = HashSet.fromList
type Parser = ParsecT Void Text Identity
data Result a = Success a | Failure Doc deriving (Show, Functor)
data Result a = Success a | Failure (Doc Void) deriving (Show, Functor)
parseFromFileEx :: MonadIO m => Parser a -> FilePath -> m (Result a)
parseFromFileEx p path = do
txt <- liftIO (T.readFile path)
return $ either (Failure . text . parseErrorPretty' txt) Success
return $ either (Failure . pretty . parseErrorPretty' txt) Success
$ parse p path txt
parseFromText :: Parser a -> Text -> Result a
parseFromText p txt =
either (Failure . text . parseErrorPretty' txt) Success $
either (Failure . pretty . parseErrorPretty' txt) Success $
parse p "<string>" txt
{- Parser.Operators -}

View File

@ -24,6 +24,7 @@ import qualified Data.List.NonEmpty as NE
import Data.Maybe (isJust, fromMaybe)
import Data.Text (pack, unpack, replace, strip)
import qualified Data.Text as Text
import Data.Text.Prettyprint.Doc
import Nix.Atoms
import Nix.Expr
import Nix.Parser
@ -37,13 +38,12 @@ import Nix.Utils hiding ((<$>))
#endif
import Nix.Value
import Prelude hiding ((<$>))
import Text.PrettyPrint.ANSI.Leijen
-- | This type represents a pretty printed nix expression
-- together with some information about the expression.
data NixDoc = NixDoc
data NixDoc ann = NixDoc
{ -- | The rendered expression, without any parentheses.
withoutParens :: Doc
withoutParens :: Doc ann
-- | The root operator is the operator at the root of
-- the expression tree. For example, in '(a * b) + c', '+' would be the root
@ -54,16 +54,16 @@ data NixDoc = NixDoc
-- we can add brackets appropiately
}
mkNixDoc :: Doc -> OperatorInfo -> NixDoc
mkNixDoc :: Doc ann -> OperatorInfo -> NixDoc ann
mkNixDoc d o = NixDoc { withoutParens = d, rootOp = o, wasPath = False }
-- | A simple expression is never wrapped in parentheses. The expression
-- behaves as if its root operator had a precedence higher than all
-- other operators (including function application).
simpleExpr :: Doc -> NixDoc
simpleExpr :: Doc ann -> NixDoc ann
simpleExpr d = mkNixDoc d (OperatorInfo minBound NAssocNone "simple expr")
pathExpr :: Doc -> NixDoc
pathExpr :: Doc ann -> NixDoc ann
pathExpr d = (simpleExpr d) { wasPath = True }
-- | An expression that behaves as if its root operator had a precedence lower
@ -71,7 +71,7 @@ pathExpr d = (simpleExpr d) { wasPath = True }
-- parantheses in almost always, but it's still rendered without parentheses
-- in cases where parentheses are never required (such as in the LHS of a
-- binding).
leastPrecedence :: Doc -> NixDoc
leastPrecedence :: Doc ann -> NixDoc ann
leastPrecedence =
flip mkNixDoc $ OperatorInfo maxBound NAssocNone "least precedence"
@ -87,7 +87,7 @@ selectOp = getSpecialOperator NSelectOp
hasAttrOp :: OperatorInfo
hasAttrOp = getSpecialOperator NHasAttrOp
wrapParens :: OperatorInfo -> NixDoc -> Doc
wrapParens :: OperatorInfo -> NixDoc ann -> Doc ann
wrapParens op sub
| precedence (rootOp sub) < precedence op = withoutParens sub
| precedence (rootOp sub) == precedence op
@ -97,104 +97,117 @@ wrapParens op sub
-- Used in the selector case to print a path in a selector as
-- "${./abc}"
wrapPath :: OperatorInfo -> NixDoc -> Doc
wrapPath :: OperatorInfo -> NixDoc ann -> Doc ann
wrapPath op sub =
if wasPath sub then dquotes (text "$" <> braces (withoutParens sub))
else wrapParens op sub
if wasPath sub
then dquotes $ "$" <> braces (withoutParens sub)
else wrapParens op sub
prettyString :: NString NixDoc -> Doc
prettyString :: NString (NixDoc ann)-> Doc ann
prettyString (DoubleQuoted parts) = dquotes . hcat . map prettyPart $ parts
where prettyPart (Plain t) = text . concatMap escape . unpack $ t
prettyPart EscapedNewline = text "''\\n"
prettyPart (Antiquoted r) = text "$" <> braces (withoutParens r)
where prettyPart (Plain t) = pretty . concatMap escape . unpack $ t
prettyPart EscapedNewline = "''\\n"
prettyPart (Antiquoted r) = "$" <> braces (withoutParens r)
escape '"' = "\\\""
escape x = maybe [x] (('\\':) . (:[])) $ toEscapeCode x
prettyString (Indented _ parts)
= group $ nest 2 (squote <> squote <$$> content) <$$> squote <> squote
= group $ nest 2 $ vcat [dsquote, content, dsquote]
where
dsquote = squote <> squote
content = vsep . map prettyLine . stripLastIfEmpty . splitLines $ parts
stripLastIfEmpty = reverse . f . reverse where
f ([Plain t] : xs) | Text.null (strip t) = xs
f xs = xs
prettyLine = hcat . map prettyPart
prettyPart (Plain t) = text . unpack . replace "${" "''${" . replace "''" "'''" $ t
prettyPart EscapedNewline = text "\\n"
prettyPart (Antiquoted r) = text "$" <> braces (withoutParens r)
prettyPart (Plain t) = pretty . unpack . replace "${" "''${" . replace "''" "'''" $ t
prettyPart EscapedNewline = "\\n"
prettyPart (Antiquoted r) = "$" <> braces (withoutParens r)
prettyParams :: Params NixDoc -> Doc
prettyParams (Param n) = text $ unpack n
prettyParams :: Params (NixDoc ann) -> Doc ann
prettyParams (Param n) = pretty $ unpack n
prettyParams (ParamSet s v mname) = prettyParamSet s v <> case mname of
Nothing -> empty
Just name | Text.null name -> empty
| otherwise -> text "@" <> text (unpack name)
Nothing -> mempty
Just name | Text.null name -> mempty
| otherwise -> "@" <> pretty (unpack name)
prettyParamSet :: ParamSet NixDoc -> Bool -> Doc
prettyParamSet :: ParamSet (NixDoc ann) -> Bool -> Doc ann
prettyParamSet args var =
encloseSep (lbrace <> space) (align (space <> rbrace)) sep (map prettySetArg args ++ prettyVariadic)
where
prettySetArg (n, maybeDef) = case maybeDef of
Nothing -> text (unpack n)
Just v -> text (unpack n) <+> text "?" <+> withoutParens v
prettyVariadic = [text "..." | var]
Nothing -> pretty (unpack n)
Just v -> pretty (unpack n) <+> "?" <+> withoutParens v
prettyVariadic = ["..." | var]
sep = align (comma <> space)
prettyBind :: Binding NixDoc -> Doc
prettyBind :: Binding (NixDoc ann) -> Doc ann
prettyBind (NamedVar n v _p) =
prettySelector n <+> equals <+> withoutParens v <> semi
prettyBind (Inherit s ns _p)
= text "inherit" <+> scope <> align (fillSep (map prettyKeyName ns)) <> semi
where scope = maybe empty ((<> space) . parens . withoutParens) s
= "inherit" <+> scope <> align (fillSep (map prettyKeyName ns)) <> semi
where scope = maybe mempty ((<> space) . parens . withoutParens) s
prettyKeyName :: NKeyName NixDoc -> Doc
prettyKeyName (StaticKey "") = dquotes $ text ""
prettyKeyName :: NKeyName (NixDoc ann) -> Doc ann
prettyKeyName (StaticKey "") = dquotes ""
prettyKeyName (StaticKey key)
| HashSet.member key reservedNames = dquotes $ text $ unpack key
prettyKeyName (StaticKey key) = text . unpack $ key
| HashSet.member key reservedNames = dquotes $ pretty $ unpack key
prettyKeyName (StaticKey key) = pretty . unpack $ key
prettyKeyName (DynamicKey key) =
runAntiquoted (DoubleQuoted [Plain "\n"])
prettyString ((text "$" <>) . braces . withoutParens) key
prettyString (("$" <>) . braces . withoutParens) key
prettySelector :: NAttrPath NixDoc -> Doc
prettySelector :: NAttrPath (NixDoc ann) -> Doc ann
prettySelector = hcat . punctuate dot . map prettyKeyName . NE.toList
prettyAtom :: NAtom -> NixDoc
prettyAtom atom = simpleExpr $ text $ unpack $ atomText atom
prettyAtom :: NAtom -> NixDoc ann
prettyAtom atom = simpleExpr $ pretty $ unpack $ atomText atom
prettyNix :: NExpr -> Doc
prettyNix :: NExpr -> Doc ann
prettyNix = withoutParens . cata exprFNixDoc
prettyOriginExpr :: NExprLocF (Maybe (NValue m)) -> Doc
prettyOriginExpr :: NExprLocF (Maybe (NValue m)) -> Doc ann
prettyOriginExpr = withoutParens . go
where
go = exprFNixDoc . annotated . getCompose . fmap render
render Nothing = simpleExpr $ text "_"
render Nothing = simpleExpr $ "_"
render (Just (NValue (reverse -> p:_) _)) = go (_originExpr p)
render (Just (NValue _ _)) = simpleExpr $ text "?"
-- simpleExpr $ foldr ((<$>) . parens . indent 2 . withoutParens
render (Just (NValue _ _)) = simpleExpr "?"
-- simpleExpr $ foldr ((\x y -> vsep [x, y]) . parens . indent 2 . withoutParens
-- . go . originExpr)
-- mempty (reverse ps)
exprFNixDoc :: NExprF NixDoc -> NixDoc
exprFNixDoc :: NExprF (NixDoc ann) -> NixDoc ann
exprFNixDoc = \case
NConstant atom -> prettyAtom atom
NStr str -> simpleExpr $ prettyString str
NList [] -> simpleExpr $ lbracket <> rbracket
NList xs -> simpleExpr $ group $
nest 2 (vsep $ lbracket : map (wrapParens appOpNonAssoc) xs) <$> rbracket
NList xs -> simpleExpr $ group $ nest 2 $ vsep $ concat $
[ [lbracket]
, map (wrapParens appOpNonAssoc) xs
, [rbracket]
]
NSet [] -> simpleExpr $ lbrace <> rbrace
NSet xs -> simpleExpr $ group $
nest 2 (vsep $ lbrace : map prettyBind xs) <$> rbrace
NSet xs -> simpleExpr $ group $ nest 2 $ vsep $ concat $
[ [lbrace]
, map prettyBind xs
, [rbrace]
]
NRecSet [] -> simpleExpr $ recPrefix <> lbrace <> rbrace
NRecSet xs -> simpleExpr $ group $
nest 2 (vsep $ recPrefix <> lbrace : map prettyBind xs) <$> rbrace
NAbs args body -> leastPrecedence $
nest 2 ((prettyParams args <> colon) <$> withoutParens body)
NRecSet xs -> simpleExpr $ group $ nest 2 $ vsep $ concat $
[ [recPrefix <> lbrace]
, map prettyBind xs
, [rbrace]
]
NAbs args body -> leastPrecedence $ nest 2 $ vsep $
[ prettyParams args <> colon
, withoutParens body
]
NBinary NApp fun arg ->
mkNixDoc (wrapParens appOp fun <+> wrapParens appOpNonAssoc arg) appOp
NBinary op r1 r2 -> flip mkNixDoc opInfo $ hsep
[ wrapParens (f NAssocLeft) r1
, text $ unpack $ operatorName opInfo
, pretty $ unpack $ operatorName opInfo
, wrapParens (f NAssocRight) r2
]
where
@ -202,18 +215,18 @@ exprFNixDoc = \case
f x | associativity opInfo /= x = opInfo { associativity = NAssocNone }
| otherwise = opInfo
NUnary op r1 ->
mkNixDoc (text (unpack (operatorName opInfo)) <> wrapParens opInfo r1) opInfo
mkNixDoc (pretty (unpack (operatorName opInfo)) <> wrapParens opInfo r1) opInfo
where opInfo = getUnaryOperator op
NSelect r' attr o ->
(if isJust o then leastPrecedence else flip mkNixDoc selectOp) $
wrapPath selectOp r <> dot <> prettySelector attr <> ordoc
where
r = flip mkNixDoc selectOp $ wrapParens appOpNonAssoc r'
ordoc = maybe empty (((space <> text "or") <+>) . wrapParens appOpNonAssoc) o
ordoc = maybe mempty (((space <> "or") <+>) . wrapParens appOpNonAssoc) o
NHasAttr r attr ->
mkNixDoc (wrapParens hasAttrOp r <+> text "?" <+> prettySelector attr) hasAttrOp
NEnvPath p -> simpleExpr $ text ("<" ++ p ++ ">")
NLiteralPath p -> pathExpr $ text $ case p of
mkNixDoc (wrapParens hasAttrOp r <+> "?" <+> prettySelector attr) hasAttrOp
NEnvPath p -> simpleExpr $ pretty ("<" ++ p ++ ">")
NLiteralPath p -> pathExpr $ pretty $ case p of
"./" -> "./."
"../" -> "../."
".." -> "../."
@ -222,20 +235,28 @@ exprFNixDoc = \case
| "./" `isPrefixOf` txt -> txt
| "../" `isPrefixOf` txt -> txt
| otherwise -> "./" ++ txt
NSym name -> simpleExpr $ text (unpack name)
NLet binds body -> leastPrecedence $ group $ text "let" <$> indent 2 (
vsep (map prettyBind binds)) <$> text "in" <+> withoutParens body
NSym name -> simpleExpr $ pretty (unpack name)
NLet binds body -> leastPrecedence $ group $ vsep $
[ "let"
, indent 2 (vsep (map prettyBind binds))
, "in" <+> withoutParens body
]
NIf cond trueBody falseBody -> leastPrecedence $
group $ nest 2 $ (text "if" <+> withoutParens cond) <$>
( align (text "then" <+> withoutParens trueBody)
<$> align (text "else" <+> withoutParens falseBody)
)
NWith scope body -> leastPrecedence $
text "with" <+> withoutParens scope <> semi <$> align (withoutParens body)
NAssert cond body -> leastPrecedence $
text "assert" <+> withoutParens cond <> semi <$> align (withoutParens body)
group $ nest 2 $ vsep $
[ "if" <+> withoutParens cond
, align ("then" <+> withoutParens trueBody)
, align ("else" <+> withoutParens falseBody)
]
NWith scope body -> leastPrecedence $ vsep $
[ "with" <+> withoutParens scope <> semi
, align $ withoutParens body
]
NAssert cond body -> leastPrecedence $ vsep $
[ "assert" <+> withoutParens cond <> semi
, align $ withoutParens body
]
where
recPrefix = text "rec" <> space
recPrefix = "rec" <> space
fixate :: Functor f => (a -> f (Fix f)) -> Free f a -> Fix f
fixate g = Fix . go
@ -243,7 +264,7 @@ fixate g = Fix . go
go (Pure a) = g a
go (Free f) = fmap (Fix . go) f
prettyNValueNF :: Functor m => NValueNF m -> Doc
prettyNValueNF :: Functor m => NValueNF m -> Doc ann
prettyNValueNF = prettyNix . valueToExpr
where
check :: NValueNF m -> Fix (NValueF m)
@ -288,27 +309,33 @@ removeEffects = Free . fmap dethunk
removeEffectsM :: MonadVar m => NValueF m (NThunk m) -> m (NValueNF m)
removeEffectsM = fmap Free . traverse dethunk
prettyNValueF :: MonadVar m => NValueF m (NThunk m) -> m Doc
prettyNValueF :: MonadVar m => NValueF m (NThunk m) -> m (Doc ann)
prettyNValueF = fmap prettyNValueNF . removeEffectsM
prettyNValue :: MonadVar m => NValue m -> m Doc
prettyNValue :: MonadVar m => NValue m -> m (Doc ann)
prettyNValue (NValue _ v) = prettyNValueF v
prettyNValueProv :: MonadVar m => NValue m -> m Doc
prettyNValueProv :: MonadVar m => NValue m -> m (Doc ann)
prettyNValueProv = \case
NValue [] v -> prettyNValueF v
NValue ps v -> do
v' <- prettyNValueF v
pure $ v' </> indent 2 (parens (mconcat
(text "from: " : map (prettyOriginExpr . _originExpr) ps)))
prettyNThunk :: MonadVar m => NThunk m -> m Doc
pure $ fillSep $
[ v'
, indent 2 $ parens $ mconcat
$ "from: "
: map (prettyOriginExpr . _originExpr) ps
]
prettyNThunk :: MonadVar m => NThunk m -> m (Doc ann)
prettyNThunk = \case
t@(NThunk ps _) -> do
v' <- fmap prettyNValueNF (dethunk t)
pure $ v' </> indent 2 (parens (mconcat
(text "thunk from: " : map (prettyOriginExpr . _originExpr) ps)))
pure $ fillSep $
[ v'
, indent 2 $ parens $ mconcat
$ "thunk from: "
: map (prettyOriginExpr . _originExpr) ps
]
dethunk :: MonadVar m => NThunk m -> m (NValueNF m)
dethunk = \case
NThunk _ (Value v) -> removeEffectsM (_baseValue v)
@ -322,4 +349,4 @@ dethunk = \case
Computed v -> removeEffectsM (_baseValue v)
_ -> pure $ Free $ NVStrF (hackyMakeNixStringWithoutContext "<thunk>")
_ <- atomicModifyVar active (False,)
return res
return res

View File

@ -12,24 +12,24 @@ module Nix.Render where
import Data.ByteString (ByteString)
import Data.List.NonEmpty (NonEmpty((:|)))
import qualified Data.Set as Set
import Data.Text.Prettyprint.Doc
import Data.Void
import Nix.Expr.Types.Annotated
import Text.Megaparsec.Error
import Text.Megaparsec.Pos (SourcePos(..))
import Text.PrettyPrint.ANSI.Leijen
class Monad m => MonadFile m where
readFile :: FilePath -> m ByteString
posAndMsg :: SourcePos -> Doc -> ParseError t Void
posAndMsg :: SourcePos -> Doc a-> ParseError t Void
posAndMsg beg msg =
FancyError (beg :| [])
(Set.fromList [ErrorFail (show msg) :: ErrorFancy Void])
renderLocation :: MonadFile m => SrcSpan -> Doc -> m Doc
renderLocation :: MonadFile m => SrcSpan -> Doc a -> m (Doc a)
renderLocation (SrcSpan beg@(SourcePos "<string>" _ _) _) msg =
return $ text $ init $ parseErrorPretty @Char (posAndMsg beg msg)
return $ pretty $ init $ parseErrorPretty @Char (posAndMsg beg msg)
renderLocation (SrcSpan beg@(SourcePos path _ _) _) msg = do
contents <- Nix.Render.readFile path
return $ text $ init $ parseErrorPretty' contents (posAndMsg beg msg)
return $ pretty $ init $ parseErrorPretty' contents (posAndMsg beg msg)

View File

@ -16,6 +16,7 @@ module Nix.Render.Frame where
import Control.Monad.Reader
import Data.Fix
import Data.Typeable
import Data.Text.Prettyprint.Doc
import Nix.Eval
import Nix.Exec
import Nix.Expr
@ -28,16 +29,15 @@ import Nix.Thunk
import Nix.Utils
import Nix.Value
import Text.Megaparsec.Pos
import qualified Text.PrettyPrint.ANSI.Leijen as P
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
#ifdef MIN_VERSION_pretty_show
import qualified Text.Show.Pretty as PS
#endif
renderFrames :: forall v e m.
(MonadReader e m, Has e Options,
MonadVar m, MonadFile m, Typeable m, Typeable v)
=> Frames -> m Doc
renderFrames
:: forall v e m ann
. ( MonadReader e m, Has e Options
, MonadVar m, MonadFile m, Typeable m, Typeable v)
=> Frames -> m (Doc ann)
renderFrames [] = pure mempty
renderFrames (x:xs) = do
opts :: Options <- asks (view hasLens)
@ -51,13 +51,13 @@ renderFrames (x:xs) = do
concat <$> mapM (renderFrame @v) (reverse (x:xs))
pure $ case frames of
[] -> mempty
_ -> foldr1 (P.<$>) frames
_ -> vsep frames
where
go :: NixFrame -> [Doc]
go :: NixFrame -> [Doc ann]
go f = case framePos @v @m f of
Just pos ->
[text "While evaluating at "
<> text (sourcePosPretty pos)
["While evaluating at "
<> pretty (sourcePosPretty pos)
<> colon]
Nothing -> []
@ -70,29 +70,29 @@ framePos (NixFrame _ f)
_ -> Nothing
| otherwise = Nothing
renderFrame :: forall v e m.
renderFrame :: forall v e m ann.
(MonadReader e m, Has e Options, MonadVar m,
MonadFile m, Typeable m, Typeable v)
=> NixFrame -> m [Doc]
=> NixFrame -> m [Doc ann]
renderFrame (NixFrame level f)
| Just (e :: EvalFrame m v) <- fromException f = renderEvalFrame level e
| Just (e :: ThunkLoop) <- fromException f = renderThunkLoop level e
| Just (e :: ValueFrame m) <- fromException f = renderValueFrame level e
| Just (e :: NormalLoop m) <- fromException f = renderNormalLoop level e
| Just (e :: ExecFrame m) <- fromException f = renderExecFrame level e
| Just (e :: ErrorCall) <- fromException f = pure [text (show e)]
| Just (e :: ErrorCall) <- fromException f = pure [pretty (show e)]
| otherwise = error $ "Unrecognized frame: " ++ show f
wrapExpr :: NExprF r -> NExpr
wrapExpr x = Fix (Fix (NSym "<?>") <$ x)
renderEvalFrame :: (MonadReader e m, Has e Options, MonadFile m)
=> NixLevel -> EvalFrame m v -> m [Doc]
=> NixLevel -> EvalFrame m v -> m [Doc ann]
renderEvalFrame level f = do
opts :: Options <- asks (view hasLens)
case f of
EvaluatingExpr scope e@(Fix (Compose (Ann ann _))) -> do
let scopeInfo | scopes opts = [string (show scope)]
let scopeInfo | scopes opts = [pretty $ show scope]
| otherwise = []
fmap (\x -> scopeInfo ++ [x]) $ renderLocation ann
=<< renderExpr level "While evaluating" "Expression" e
@ -105,59 +105,64 @@ renderEvalFrame level f = do
Calling name ann ->
fmap (:[]) $ renderLocation ann $
text "While calling builtins." <> text name
"While calling builtins." <> pretty name
_ -> pure []
renderExpr :: (MonadReader e m, Has e Options, MonadFile m)
=> NixLevel -> String -> String -> NExprLoc -> m Doc
=> NixLevel -> String -> String -> NExprLoc -> m (Doc ann)
renderExpr _level longLabel shortLabel e@(Fix (Compose (Ann _ x))) = do
opts :: Options <- asks (view hasLens)
let rendered
| verbose opts >= DebugInfo =
#ifdef MIN_VERSION_pretty_show
text (PS.ppShow (stripAnnotation e))
pretty (PS.ppShow (stripAnnotation e))
#else
text (show (stripAnnotation e))
pretty (show (stripAnnotation e))
#endif
| verbose opts >= Chatty =
prettyNix (stripAnnotation e)
| otherwise =
prettyNix (Fix (Fix (NSym "<?>") <$ x))
pure $ if verbose opts >= Chatty
then text (longLabel ++ ":\n>>>>>>>>")
P.<$> indent 2 rendered
P.<$> text "<<<<<<<<"
else text shortLabel <> text ": " </> rendered
then vsep $
[ pretty (longLabel ++ ":\n>>>>>>>>")
, indent 2 rendered
, "<<<<<<<<"
]
else pretty shortLabel <> fillSep [": ", rendered]
renderValueFrame :: (MonadReader e m, Has e Options,
MonadFile m, MonadVar m)
=> NixLevel -> ValueFrame m -> m [Doc]
=> NixLevel -> ValueFrame m -> m [Doc ann]
renderValueFrame level = fmap (:[]) . \case
ForcingThunk -> pure $ text "ForcingThunk"
ConcerningValue _v -> pure $ text "ConcerningValue"
Comparison _ _ -> pure $ text "Comparing"
Addition _ _ -> pure $ text "Adding"
Division _ _ -> pure $ text "Dividing"
Multiplication _ _ -> pure $ text "Multiplying"
ForcingThunk -> pure "ForcingThunk"
ConcerningValue _v -> pure "ConcerningValue"
Comparison _ _ -> pure "Comparing"
Addition _ _ -> pure "Adding"
Division _ _ -> pure "Dividing"
Multiplication _ _ -> pure "Multiplying"
Coercion x y ->
pure $ text desc <> text (describeValue x)
<> text " to " <> text (describeValue y)
Coercion x y -> pure $ mconcat
[ desc
, pretty (describeValue x)
, " to "
, pretty (describeValue y)
]
where
desc | level <= Error = "Cannot coerce "
| otherwise = "While coercing "
CoercionToJsonNF _v -> pure $ text "CoercionToJsonNF"
CoercionFromJson _j -> pure $ text "CoercionFromJson"
ExpectationNF _t _v -> pure $ text "ExpectationNF"
CoercionToJsonNF _v -> pure "CoercionToJsonNF"
CoercionFromJson _j -> pure "CoercionFromJson"
ExpectationNF _t _v -> pure "ExpectationNF"
Expectation t v -> do
v' <- renderValue level "" "" v
pure $ text "Saw " <> v'
<> text " but expected " <> text (describeValue t)
pure $ "Saw " <> v'
<> " but expected " <> pretty (describeValue t)
renderValue :: (MonadReader e m, Has e Options, MonadFile m, MonadVar m)
=> NixLevel -> String -> String -> NValue m -> m Doc
=> NixLevel -> String -> String -> NValue m -> m (Doc ann)
renderValue _level _longLabel _shortLabel v = do
opts :: Options <- asks (view hasLens)
if values opts
@ -165,23 +170,23 @@ renderValue _level _longLabel _shortLabel v = do
else prettyNValue v
renderExecFrame :: (MonadReader e m, Has e Options, MonadVar m, MonadFile m)
=> NixLevel -> ExecFrame m -> m [Doc]
=> NixLevel -> ExecFrame m -> m [Doc ann]
renderExecFrame level = \case
Assertion ann v ->
fmap (:[]) $ renderLocation ann
=<< ((text "Assertion failed:" </>)
=<< ((\d -> fillSep ["Assertion failed:", d])
<$> renderValue level "" "" v)
renderThunkLoop :: (MonadReader e m, Has e Options, MonadFile m)
=> NixLevel -> ThunkLoop -> m [Doc]
=> NixLevel -> ThunkLoop -> m [Doc ann]
renderThunkLoop _level = pure . (:[]) . \case
ThunkLoop Nothing -> text "<<thunk loop>>"
ThunkLoop Nothing -> "<<thunk loop>>"
ThunkLoop (Just n) ->
text $ "<<loop forcing thunk #" ++ show n ++ ">>"
pretty $ "<<loop forcing thunk #" ++ show n ++ ">>"
renderNormalLoop :: (MonadReader e m, Has e Options, MonadFile m, MonadVar m)
=> NixLevel -> NormalLoop m -> m [Doc]
=> NixLevel -> NormalLoop m -> m [Doc ann]
renderNormalLoop level = fmap (:[]) . \case
NormalLoop v -> do
v' <- renderValue level "" "" v
pure $ text "<<loop during normalization forcing " <> v' <> text ">>"
pure $ "<<loop during normalization forcing " <> v' <> ">>"

View File

@ -12,7 +12,9 @@ import Data.Fix
import Data.List.NonEmpty (NonEmpty(..))
import Data.Semigroup
import Data.String.Interpolate.IsString
import Data.Text (Text, unpack, pack)
import Data.Text (Text, unpack)
import Data.Text.Prettyprint.Doc
import Data.Text.Prettyprint.Doc.Render.Text
import Nix.Atoms
import Nix.Expr
import Nix.Parser
@ -20,7 +22,6 @@ import Nix.Pretty
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.TH
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>))
case_constant_int = assertParseText "234" $ mkInt 234
@ -394,9 +395,9 @@ assertParseFail str = case parseNixText str of
assertParsePrint :: Text -> Text -> Assertion
assertParsePrint src expect =
let Success expr = parseNixTextLoc src
result = displayS
. renderPretty 0.4 80
result = renderStrict
. layoutPretty (LayoutOptions $ AvailablePerLine 80 0.4)
. prettyNix
. stripAnnotation
$ expr
in assertEqual "" expect (pack (result ""))
in assertEqual "" expect result

View File

@ -17,6 +17,7 @@ import Data.Char
import Data.Fix
import qualified Data.List.NonEmpty as NE
import Data.Text (Text, pack)
import Data.Text.Prettyprint.Doc
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
@ -27,8 +28,6 @@ import Nix.Pretty
import Test.Tasty
import Test.Tasty.Hedgehog
import Text.Megaparsec (Pos, SourcePos, mkPos)
import Text.PrettyPrint.ANSI.Leijen ((</>), text)
import qualified Text.PrettyPrint.ANSI.Leijen as P
import qualified Text.Show.Pretty as PS
asciiString :: MonadGen m => m String
@ -188,38 +187,38 @@ normalize = cata $ \case
-- | Test that parse . pretty == id up to attribute position information.
prop_prettyparse :: Monad m => NExpr -> PropertyT m ()
prop_prettyparse p = do
let prog = show (pretty p)
let prog = show (prettyNix p)
case parse (pack prog) of
Failure s -> do
footnote $ show $
text "Parse failed:" </> text (show s)
P.<$> P.indent 2 (pretty p)
footnote $ show $ vsep
[ fillSep ["Parse failed:", pretty (show s)]
, indent 2 (prettyNix p)
]
discard
Success v
| equivUpToNormalization p v -> success
| otherwise -> do
let pp = normalise prog
pv = normalise (show (pretty v))
footnote $ show $
text "----------------------------------------"
P.<$> text "Expr before:" P.<$> P.indent 2 (text (PS.ppShow p))
P.<$> text "----------------------------------------"
P.<$> text "Expr after:" P.<$> P.indent 2 (text (PS.ppShow v))
P.<$> text "----------------------------------------"
P.<$> text "Pretty before:" P.<$> P.indent 2 (text prog)
P.<$> text "----------------------------------------"
P.<$> text "Pretty after:" P.<$> P.indent 2 (pretty v)
P.<$> text "----------------------------------------"
P.<$> text "Normalised before:" P.<$> P.indent 2 (text pp)
P.<$> text "----------------------------------------"
P.<$> text "Normalised after:" P.<$> P.indent 2 (text pv)
P.<$> text "========================================"
P.<$> text "Normalised diff:"
P.<$> text (ppDiff (diff pp pv))
P.<$> text "========================================"
pv = normalise (show (prettyNix v))
footnote $ show $ vsep $
[ "----------------------------------------"
, vsep ["Expr before:", indent 2 (pretty (PS.ppShow p))]
, "----------------------------------------"
, vsep ["Expr after:", indent 2 (pretty (PS.ppShow v))]
, "----------------------------------------"
, vsep ["Pretty before:", indent 2 (pretty prog)]
, "----------------------------------------"
, vsep ["Pretty after:", indent 2 (prettyNix v)]
, "----------------------------------------"
, vsep ["Normalised before:", indent 2 (pretty pp)]
, "----------------------------------------"
, vsep ["Normalised after:", indent 2 (pretty pv)]
, "========================================"
, vsep ["Normalised diff:", pretty (ppDiff (diff pp pv))]
, "========================================"
]
assert (pp == pv)
where
pretty = prettyNix
parse = parseNixText
normalise = unlines . map (reverse . dropWhile isSpace . reverse) . lines