///|
/// The Elm source of a type annotation, laid out in `width` columns where
/// elm-format allows a choice. Parentheses are added where precedence needs
/// them. Raises `PrintError` when the type cannot print as valid Elm; the
/// error's path starts at `t`.
///
/// ```mbt check
/// test {
/// let r : @ast.Range = {
/// start: { row: 0, column: 0, },
/// end: { row: 0, column: 0, },
/// }
/// let a : @ast.Node[@ast.TypeAnnotation] = {
/// range: r,
/// value: GenericType("a"),
/// }
/// let maybe : @ast.Node[@ast.TypeAnnotation] = {
/// range: r,
/// value: Typed({ range: r, value: ([][:], "Maybe"), }, [a][:]),
/// }
/// let list : @ast.Node[@ast.TypeAnnotation] = {
/// range: r,
/// value: Typed({ range: r, value: ([][:], "List"), }, [maybe][:]),
/// }
/// inspect(@printer.print_type_annotation(list), content="List (Maybe a)")
/// }
/// ```
pub fn print_type_annotation(
t : @ast.Node[@ast.TypeAnnotation],
width? : Int = 120,
dialect? : @dialect.Dialect = @dialect.Dialect::elm_0_19_1(),
) -> String raise PrintError {
let ctx : Ctx = { dialect, }
@pretty.render(ctx.type_doc(@syntax.NodePath::root(), 0, t, AnyType), width~)
}
///|
/// The Elm source of a pattern. Raises `PrintError` for a pattern that Elm
/// cannot write (a negative number, a bad name, a tuple with one item); the
/// error's path starts at `p`.
///
/// `UnConsPattern(a, AsPattern(b, c))` prints as `a :: b as c`, because
/// krueger and elm-syntax read that text back as the same AST. `elm make`
/// 0.19.1 and elm-format read `a :: b as c` as `(a :: b) as c`. To bind
/// only the tail, use `ParenthesizedPattern`: `a :: (b as c)`.
///
/// ```mbt check
/// test {
/// let r : @ast.Range = {
/// start: { row: 0, column: 0, },
/// end: { row: 0, column: 0, },
/// }
/// fn pvar(name : String) -> @ast.Node[@ast.Pattern] {
/// { range: r, value: VarPattern(name), }
/// }
/// let inner : @ast.Node[@ast.Pattern] = {
/// range: r,
/// value: UnConsPattern(pvar("a"), pvar("b")),
/// }
/// let outer : @ast.Node[@ast.Pattern] = {
/// range: r,
/// value: UnConsPattern(inner, pvar("rest")),
/// }
/// // `::` is right-associative, so an `::` on its left gets parentheses.
/// inspect(@printer.print_pattern(outer), content="(a :: b) :: rest")
/// }
/// ```
pub fn print_pattern(
p : @ast.Node[@ast.Pattern],
width? : Int = 120,
dialect? : @dialect.Dialect = @dialect.Dialect::elm_0_19_1(),
) -> String raise PrintError {
let ctx : Ctx = { dialect, }
@pretty.render(
ctx.pattern_doc(@syntax.NodePath::root(), 0, p, AnyPattern),
width~,
)
}
///|
/// The Elm source of an expression. Parentheses are added where
/// precedence needs them; parentheses in the AST stay. `if`, `case` and
/// `let` are always multi-line (elm-format). Raises `PrintError` for an
/// expression that cannot print as valid Elm; the error's path starts at
/// `e`.
///
/// ```mbt check
/// test {
/// let r : @ast.Range = {
/// start: { row: 0, column: 0, },
/// end: { row: 0, column: 0, },
/// }
/// fn v(name : String) -> @ast.Node[@ast.Expression] {
/// { range: r, value: FunctionOrValue([][:], name), }
/// }
/// let sum : @ast.Node[@ast.Expression] = {
/// range: r,
/// value: OperatorApplication("+", Left, v("a"), v("b")),
/// }
/// let product : @ast.Node[@ast.Expression] = {
/// range: r,
/// value: OperatorApplication("*", Left, sum, v("c")),
/// }
/// inspect(@printer.print_expression(product), content="(a + b) * c")
/// }
/// ```
pub fn print_expression(
e : @ast.Node[@ast.Expression],
width? : Int = 120,
dialect? : @dialect.Dialect = @dialect.Dialect::elm_0_19_1(),
) -> String raise PrintError {
let ctx : Ctx = { dialect, }
@pretty.render(ctx.expr_doc(@syntax.NodePath::root(), 0, e, AnyExpr), width~)
}
///|
/// The Elm source of a declaration, without a trailing line feed. A port's
/// doc comment is not part of its declaration (elm-syntax keeps it in
/// `File.comments`), so only `print_file` prints it.
///
/// ```mbt check
/// test {
/// let r : @ast.Range = {
/// start: { row: 0, column: 0, },
/// end: { row: 0, column: 0, },
/// }
/// let int : @ast.Node[@ast.TypeAnnotation] = {
/// range: r,
/// value: Typed({ range: r, value: ([][:], "Int"), }, [][:]),
/// }
/// let decl : @ast.Node[@ast.Declaration] = {
/// range: r,
/// value: AliasDeclaration({
/// documentation: None,
/// name: { range: r, value: "Age", },
/// generics: [][:],
/// type_annotation: int,
/// }),
/// }
/// inspect(
/// @printer.print_declaration(decl),
/// content=(
/// #|type alias Age =
/// #| Int
/// ),
/// )
/// }
/// ```
pub fn print_declaration(
d : @ast.Node[@ast.Declaration],
width? : Int = 120,
dialect? : @dialect.Dialect = @dialect.Dialect::elm_0_19_1(),
) -> String raise PrintError {
let ctx : Ctx = { dialect, }
@pretty.render(ctx.declaration_doc(@syntax.NodePath::root(), 0, d), width~)
}
///|
/// The Elm source of a file in the elm-format layout, ending with one line
/// feed. Fixed shapes (declaration bodies, custom types, `if`, `case`,
/// `let`) are always multi-line; other constructs stay on one line when they
/// fit in `width` columns. Prints documentation and the doc comments of
/// `File.comments` (module and port documentation), not regular comments.
/// Text inside doc comments and GLSL is written as it is, line ends
/// included: a doc comment from a CRLF source keeps its `\r\n`.
/// Raises `PrintError` for an AST that cannot print as valid Elm; the
/// error's path starts at the file.
///
/// `UnConsPattern(a, AsPattern(b, c))` prints as `a :: b as c`, which
/// `elm make` 0.19.1 and elm-format read as `(a :: b) as c` (see
/// `print_pattern`).
///
/// ```mbt check
/// test {
/// let text = "module Main exposing (main)\n\n\nmain =\n 1 + 2\n"
/// let result = @parser.parse_module(
/// @scanner.SourceText::new(text),
/// @scanner.DefaultScanner::new(),
/// )
/// inspect(@printer.print_file(result.ast.unwrap()) == text, content="true")
/// }
/// ```
pub fn print_file(
file : @ast.File,
width? : Int = 120,
dialect? : @dialect.Dialect = @dialect.Dialect::elm_0_19_1(),
) -> String raise PrintError {
let ctx : Ctx = { dialect, }
@pretty.render(ctx.file_doc(file), width~)
}