packages feed

nix-lang-0.1.0.0: src/Nix/Lang/Outputable.hs

-- | Basic pretty-printing for Nix AST values.
--
-- This is the simple renderer shared across the AST. It does not try to preserve
-- parsed layout, and it is less opinionated than 'Nix.Lang.RFCPrint'.
module Nix.Lang.Outputable where

import Data.Data (Proxy (..))
import Data.Text (Text)
import Nix.Lang.Lexer.Text
import Nix.Lang.Types
import Nix.Lang.Types.Ps
import Nix.Lang.Types.Syn (Syn)
import Nix.Lang.Utils
import Prettyprinter
import Prettyprinter.Render.Text (renderStrict)

class Outputable a where
  output :: a -> Doc ann

class OutputableNames p where
  outputVarName :: Proxy p -> NixVarName p -> Doc ann
  outputBinderName :: Proxy p -> NixBinderName p -> Doc ann
  outputAttrName :: Proxy p -> NixAttrName p -> Doc ann

instance OutputableNames Ps where
  outputVarName _ = pretty
  outputBinderName _ = pretty
  outputAttrName _ = pretty

instance OutputableNames Syn where
  outputVarName _ = pretty
  outputBinderName _ = pretty
  outputAttrName _ = pretty

renderToText :: (Outputable a) => a -> Text
renderToText = renderStrict . layoutPretty defaultLayoutOptions . output

extErr :: a
extErr = error "unimplemented for extension"

unwrapText :: UnXRec p => Proxy p -> XRec p Text -> Text
unwrapText = unXRec

unwrapVarName :: UnXRec p => Proxy p -> LNixVarName p -> NixVarName p
unwrapVarName = unXRec

unwrapBinderName :: UnXRec p => Proxy p -> LNixBinderName p -> NixBinderName p
unwrapBinderName = unXRec

unwrapAttrName :: UnXRec p => Proxy p -> LNixAttrName p -> NixAttrName p
unwrapAttrName = unXRec

unwrapLit :: UnXRec p => Proxy p -> LNixLit p -> NixLit p
unwrapLit = unXRec

unwrapExpr :: UnXRec p => Proxy p -> LNixExpr p -> NixExpr p
unwrapExpr = unXRec

unwrapString :: UnXRec p => Proxy p -> LNixString p -> NixString p
unwrapString = unXRec

unwrapPath :: UnXRec p => Proxy p -> LNixPath p -> NixPath p
unwrapPath = unXRec

unwrapAttrKey :: UnXRec p => Proxy p -> LNixAttrKey p -> NixAttrKey p
unwrapAttrKey = unXRec

unwrapAttrPath :: UnXRec p => Proxy p -> LNixAttrPath p -> NixAttrPath p
unwrapAttrPath = unXRec

unwrapBinding :: UnXRec p => Proxy p -> LNixBinding p -> NixBinding p
unwrapBinding = unXRec

unwrapFuncPat :: UnXRec p => Proxy p -> LNixFuncPat p -> NixFuncPat p
unwrapFuncPat = unXRec

unwrapBindings :: UnXRec p => Proxy p -> LNixBindings p -> [LNixBinding p]
unwrapBindings = unXRec

unwrapStringPart :: UnXRec p => Proxy p -> LNixStringPart p -> NixStringPart p
unwrapStringPart = unXRec

unwrapSetPatAs :: UnXRec p => Proxy p -> LNixSetPatAs p -> NixSetPatAs p
unwrapSetPatAs = unXRec

unwrapSetPatBinding :: UnXRec p => Proxy p -> LNixSetPatBinding p -> NixSetPatBinding p
unwrapSetPatBinding = unXRec

outputLitX :: UnXRec p => Proxy p -> LNixLit p -> Doc ann
outputLitX proxy lit = output (unwrapLit proxy lit)

outputExprX :: (OutputableNames p, UnXRec p) => Proxy p -> LNixExpr p -> Doc ann
outputExprX proxy expr = output (unwrapExpr proxy expr)

outputStringX :: (OutputableNames p, UnXRec p) => Proxy p -> LNixString p -> Doc ann
outputStringX proxy str = output (unwrapString proxy str)

outputPathX :: (OutputableNames p, UnXRec p) => Proxy p -> LNixPath p -> Doc ann
outputPathX proxy path = output (unwrapPath proxy path)

outputAttrKeyX :: (OutputableNames p, UnXRec p) => Proxy p -> LNixAttrKey p -> Doc ann
outputAttrKeyX proxy key = output (unwrapAttrKey proxy key)

outputAttrPathX :: (OutputableNames p, UnXRec p) => Proxy p -> LNixAttrPath p -> Doc ann
outputAttrPathX proxy path = output (unwrapAttrPath proxy path)

outputBindingX :: (OutputableNames p, UnXRec p) => Proxy p -> LNixBinding p -> Doc ann
outputBindingX proxy binding = output (unwrapBinding proxy binding)

outputFuncPatX :: (OutputableNames p, UnXRec p) => Proxy p -> LNixFuncPat p -> Doc ann
outputFuncPatX proxy pat = output (unwrapFuncPat proxy pat)

outputSetPatBindingX :: (OutputableNames p, UnXRec p) => Proxy p -> LNixSetPatBinding p -> Doc ann
outputSetPatBindingX proxy binding = output (unwrapSetPatBinding proxy binding)

outputSetPatAsX :: (OutputableNames p, UnXRec p) => Proxy p -> LNixSetPatAs p -> Doc ann
outputSetPatAsX proxy asPat = output (unwrapSetPatAs proxy asPat)

instance Outputable (NixLit p) where
  output (NixUri _ uri) = pretty uri
  output (NixInteger _ int) = pretty int
  output (NixFloat _ float) = pretty float
  output (NixBoolean _ bool) = if bool then "true" else "false"
  output (NixNull _) = "null"
  output (XNixLit _) = extErr

instance (OutputableNames p, UnXRec p) => Outputable (NixExpr p) where
  output = go
    where
      proxy = Proxy @p

      go (NixLit _ lit) = outputLitX proxy lit
      go (NixString _ str) = outputStringX proxy str
      go (NixVar _ x) = outputVarName proxy (unwrapVarName proxy x)
      go (NixPar _ x) = parens $ outputExprX proxy x
      go (NixNegApp _ x) = "-" <> outputExprX proxy x
      go (NixNotApp _ x) = "!" <> outputExprX proxy x
      go (NixApp _ f x) = hsep [outputExprX proxy f, outputExprX proxy x]
      go (NixBinApp _ op x y) =
        hsep
          [ outputExprX proxy x,
            pretty $ showBinOP op,
            outputExprX proxy y
          ]
      go (NixHasAttr _ x p) = hsep [outputExprX proxy x, "?", outputAttrPathX proxy p]
      go (NixPath _ p) = outputPathX proxy p
      go (NixEnvPath _ p) = "<" <> pretty (unwrapText proxy p) <> ">"
      go (NixLam _ pat x) = nestedSep [outputFuncPatX proxy pat <> ":", outputExprX proxy x]
      go (NixList _ xs) = case xs of
        [] -> "[]"
        _ -> nestedSep $ "[" : (outputExprX proxy <$> xs) <> ["]"]
      go (NixSet _ NixSetRecursive bindings) = case unwrapBindings proxy bindings of
        [] -> "rec {}"
        xs -> nestedSep $ "rec {" : (outputBindingX proxy <$> xs) <> ["}"]
      go (NixSet _ NixSetNonRecursive bindings) = case unwrapBindings proxy bindings of
        [] -> "{}"
        xs -> nestedSep $ "{" : (outputBindingX proxy <$> xs) <> ["}"]
      go (NixSelect _ x p mx) = outputExprX proxy x <> "." <> outputAttrPathX proxy p <> alt
        where
          alt = maybe mempty ((" or " <>) . outputExprX proxy) mx
      go (NixLet _ bindings x) =
        group $
          vsep ["let", indent 2 (vsep $ outputBindingX proxy <$> unwrapBindings proxy bindings), "in " <> outputExprX proxy x]
      go (NixIf _ cond t f) = nestedSep ["if " <> outputExprX proxy cond, "then " <> outputExprX proxy t, "else " <> outputExprX proxy f]
      go (NixWith _ s x) = nestedSep ["with " <> outputExprX proxy s <> ";", outputExprX proxy x]
      go (NixAssert _ s x) = nestedSep ["assert " <> outputExprX proxy s <> ";", outputExprX proxy x]
      go (XNixExpr _) = extErr

nestedSep :: [Doc ann] -> Doc ann
nestedSep = group . nest 2 . vsep

outputStringPart :: forall p ann. (OutputableNames p, UnXRec p) => Proxy p -> (Text -> Text) -> NixStringPart p -> Doc ann
outputStringPart _ escape (NixStringLiteral _ text) = pretty $ escape text
outputStringPart proxy _ (NixStringInterpol _ expr) = "${" <> outputExprX proxy expr <> "}"
outputStringPart _ _ (XNixStringPart _) = extErr

instance (OutputableNames p, UnXRec p) => Outputable (NixString p) where
  output = go
    where
      proxy = Proxy @p

      go (NixDoubleQuotesString _ parts) = dquotes $ outputParts $ fmap (unwrapStringPart proxy) parts
        where
          outputParts = mconcat . fmap (outputStringPart proxy escapeDoubleQuotedText)
      go (NixDoubleSingleQuotesString _ parts) = vcat ["''", nest 2 $ outputParts $ fmap (unwrapStringPart proxy) parts, "''"]
        where
          outputParts = mconcat . fmap (outputStringPart proxy escapeIndentedText)
      go (XNixString _) = extErr

instance (OutputableNames p, UnXRec p) => Outputable (NixAttrKey p) where
  output = go
    where
      proxy = Proxy @p

      go (NixStaticAttrKey _ x) = outputAttrName proxy (unwrapAttrName proxy x)
      go (NixDynamicStringAttrKey _ parts) = dquotes $ mconcat $ outputStringPart proxy escapeDoubleQuotedText . unwrapStringPart proxy <$> parts
      go (NixDynamicInterpolAttrKey _ expr) = "${" <> outputExprX proxy expr <> "}"
      go (XNixNixAttrKey _) = extErr

instance (OutputableNames p, UnXRec p) => Outputable (NixAttrPath p) where
  output (NixAttrPath _ keys) = hcat . punctuate dot $ outputAttrKeyX (Proxy @p) <$> keys

instance (OutputableNames p, UnXRec p) => Outputable (NixPath p) where
  output = go
    where
      proxy = Proxy @p

      go (NixLiteralPath _ p) = pretty p
      go (NixInterpolPath _ parts) = hcat $ outputStringPart proxy id . unwrapStringPart proxy <$> parts
      go (XNixPath _) = extErr

instance (OutputableNames p, UnXRec p) => Outputable (NixBinding p) where
  output = go
    where
      proxy = Proxy @p

      go (NixNormalBinding _ p x) = hsep [outputAttrPathX proxy p, "=", outputExprX proxy x] <> ";"
      go (NixInheritBinding _ mScope names) = "inherit" <> outputScope <> outputNames names <> ";"
        where
          outputScope = maybe mempty ((" " <>) . outputExprX proxy) mScope
          outputNames [] = mempty
          outputNames ns = " " <> (align . fillSep $ outputAttrKeyX proxy <$> ns)
      go (XNixBinding _) = extErr

instance (OutputableNames p, UnXRec p) => Outputable (NixSetPatBinding p) where
  output NixSetPatBinding {..} = outputBinderName proxy (unwrapBinderName proxy nspbVar) <> outputDefault
    where
      proxy = Proxy @p
      outputDefault = maybe mempty ((" ? " <>) . outputExprX proxy) nspbDefault

instance (OutputableNames p, UnXRec p) => Outputable (NixSetPatAs p) where
  output NixSetPatAs {..} = case nspaLocation of
    NixSetPatAsLeading -> outputNm <> "@"
    NixSetPatAsTrailing -> "@" <> outputNm
    where
      proxy = Proxy @p
      outputNm = outputBinderName proxy $ unwrapBinderName proxy nspaVar

instance (OutputableNames p, UnXRec p) => Outputable (NixFuncPat p) where
  output = go
    where
      proxy = Proxy @p

      go (NixVarPat _ x) = outputBinderName proxy (unwrapBinderName proxy x)
      go (NixSetPat _ ellipses mAs params) = case fmap (nspaLocation . unwrapSetPatAs proxy) mAs of
        Just NixSetPatAsLeading -> outputAs <> outputParams
        Just NixSetPatAsTrailing -> outputParams <> outputAs
        Nothing -> outputParams
        where
          outputAs = maybe mempty (outputSetPatAsX proxy) mAs
          outputParams = encloseSep "{ " " }" ", " ((outputSetPatBindingX proxy <$> params) <> ["..." | ellipses == NixSetPatIsEllipses])
      go (XNixFuncPat _) = extErr