///|
/// Where a pattern is: this decides its parentheses (see
/// `pattern_parens`).
priv enum PatternAt {
AnyPattern // case branch, let, tuple and list item, inside parentheses
ConsLeft // left of `::`: `::` and `as` patterns need parentheses
ConsTail // the last pattern right of `::`
AsInner // the pattern of an `as`: `::` and `as` patterns need parentheses
PatternArg // argument or destructuring pattern: also a constructor with arguments
}
///|
/// Whether the printer writes `p` at `at` in parentheses. A `::` or `as`
/// pattern needs them left of `::`, in an `as` and as an argument; a
/// constructor with arguments needs them as an argument. elm-format also
/// writes a constructor with arguments in parentheses before and after
/// `::` and in an `as` (Box.hs `formatPattern`: those places are
/// `SpaceSeparated`). elm-syntax reads `a :: b as c` as `a :: (b as c)`,
/// so an `as` after `::` has none.
fn pattern_parens(p : @ast.Pattern, at : PatternAt) -> Bool {
match (p, at) {
(NamedPattern(_, args), PatternArg | ConsLeft | ConsTail | AsInner) =>
!args.is_empty()
(UnConsPattern(_, _) | AsPattern(_, _), PatternArg | ConsLeft | AsInner) =>
true
_ => false
}
}
///|
fn Ctx::pattern_doc(
self : Ctx,
path : @syntax.NodePath,
level : Int,
p : @ast.Node[@ast.Pattern],
at : PatternAt,
) -> @pretty.Doc raise PrintError {
check_level(path, level)
let lead = self.leading(p.range)
let d = self.pattern_body(path, level, p, at)
lead + d + self.trailing(p.range)
}
///|
/// The pattern without its own comments. Patterns are on one line, so a
/// line comment in a pattern is followed by a line break indented by 4
/// (`Ctx::after`).
fn Ctx::pattern_body(
self : Ctx,
path : @syntax.NodePath,
level : Int,
p : @ast.Node[@ast.Pattern],
at : PatternAt,
) -> @pretty.Doc raise PrintError {
match p.value {
AllPattern => @pretty.text("_")
UnitPattern =>
@pretty.text("(") + self.inner_alone(p.range) + @pretty.text(")")
CharPattern(c) => @pretty.text(char_literal(c))
StringPattern(s) => self.string_doc(p.range, s)
IntPattern(v) => {
guard v >= 0L else { raise PrintError(path~, problem=NegativePattern) }
@pretty.text(self.integer_text(p.range, v))
}
HexPattern(v) => {
guard v >= 0L else { raise PrintError(path~, problem=NegativePattern) }
@pretty.text(self.hex_literal_text(p.range, v))
}
FloatPattern(x) => {
if self.float_lexeme_text(p.range, x) is Some(text) {
return @pretty.text(text)
}
guard !x.is_nan() && !x.is_inf() else {
raise PrintError(path~, problem=NonFiniteFloat(x))
}
guard !is_negative(x) else {
raise PrintError(path~, problem=NegativePattern)
}
@pretty.text(float_text(x))
}
TuplePattern(items) => {
guard items.length() >= 2 else {
raise PrintError(path~, problem=ShortTuple)
}
self.flat_sequence(
"(",
")",
p.range,
self.pattern_items(path, level, items),
)
}
ListPattern(items) =>
self.flat_sequence(
"[",
"]",
p.range,
self.pattern_items(path, level, items),
)
RecordPattern(names) => {
let docs = []
for i, x in names {
let name = self.lower_name(path.child("value", i), x.value)
docs.push((self.with_comments(x.range, name), x.range))
}
self.flat_sequence("{", "}", p.range, docs)
}
VarPattern(name) => self.lower_name(path, name)
NamedPattern(name_ref, args) => {
let head = qualified_upper(path, name_ref.module_name, name_ref.name)
guard !args.is_empty() else { return head }
let mut d = head + @pretty.text(" ")
for i, a in args {
if i > 0 {
d = d + self.after(args[i - 1].range, " ")
}
d = d +
self.pattern_doc(path.child("patterns", i), level + 1, a, PatternArg)
}
if pattern_parens(p.value, at) {
flat_parens(d + self.after(args[args.length() - 1].range, ""))
} else {
d
}
}
UnConsPattern(_, _) => {
let mut node = p
let mut q = path
let mut d = @pretty.empty()
while node.value is UnConsPattern(left, right) {
if node.range != p.range {
d = d + self.leading(node.range)
}
let moved = self.after_token(left.range)
d = d +
self.pattern_doc(q.child("left", 0), level + 1, left, ConsLeft) +
self.separator(left.range, " ::", moved) +
@pretty.text(" ")
q = q.child("right", 0)
node = right
}
d = d + self.pattern_doc(q, level + 1, node, ConsTail)
if pattern_parens(p.value, at) {
flat_parens(d)
} else {
d
}
}
AsPattern(inner, name) => {
let moved = self.after_token(inner.range)
let d = self.pattern_doc(
path.child("pattern", 0),
level + 1,
inner,
AsInner,
) +
self.separator(inner.range, " as", moved) +
@pretty.text(" ") +
self.with_comments(
name.range,
self.lower_name(path.child("name", 0), name.value),
)
if pattern_parens(p.value, at) {
flat_parens(d)
} else {
d
}
}
ParenthesizedPattern(x) =>
flat_parens(
self.pattern_doc(path.child("value", 0), level + 1, x, AnyPattern) +
self.after(x.range, "") +
self.inline_inner(p.range),
)
}
}
///|
fn Ctx::pattern_items(
self : Ctx,
path : @syntax.NodePath,
level : Int,
items : ArrayView[@ast.Node[@ast.Pattern]],
) -> Array[(@pretty.Doc, @ast.Range)] raise PrintError {
let docs = []
for i, x in items {
docs.push(
(
self.pattern_doc(path.child("value", i), level + 1, x, AnyPattern),
x.range,
),
)
}
docs
}
///|
/// `[ a, b ]` always on one line (see `flat_sequence`), with the comments
/// of the pattern at `owner` before the closing token.
fn Ctx::flat_sequence(
self : Ctx,
open : String,
close : String,
owner : @ast.Range,
items : Array[(@pretty.Doc, @ast.Range)],
) -> @pretty.Doc {
guard !items.is_empty() else {
return @pretty.text(open) +
self.inner_alone(owner, brace=open == "{") +
@pretty.text(close)
}
let docs = []
for i, item in items {
let (d, r) = item
docs.push(if i + 1 < items.length() { d + self.after(r, "") } else { d })
}
let (_, last) = items[items.length() - 1]
// The break after a line comment comes before the inner comments.
let end = self.after(last, "") + self.inline_inner(owner)
flat_sequence(open, close, docs, inner=end)
}
///|
/// The inner comments of the node at `owner` on the line: ` {- a -}`. A
/// line comment is followed by a line break indented by 4.
fn Ctx::inline_inner(self : Ctx, owner : @ast.Range) -> @pretty.Doc {
let mut d = @pretty.empty()
for c in self.take(Inner, owner) {
d = d + @pretty.text(" ") + comment_doc(c)
if is_line_comment(c) {
d = d + @pretty.nest(4, @pretty.hardline())
}
}
d
}