mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
1927 lines
63 KiB
Rust
1927 lines
63 KiB
Rust
use bumpalo::collections::String;
|
|
use bumpalo::Bump;
|
|
use roc_module::called_via::{Associativity, BinOp, UnaryOp};
|
|
use roc_parse::{
|
|
ast::{
|
|
AbilityImpls, AssignedField, Base, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
|
|
FunctionArrow, Header, ImplementsAbilities, ImplementsAbility, ImplementsClause,
|
|
ImportAlias, ImportAsKeyword, ImportExposingKeyword, ImportedModuleName,
|
|
IngestedFileAnnotation, IngestedFileImport, ModuleImport, ModuleImportParams, Pattern,
|
|
Spaced, Spaces, SpacesBefore, Tag, TypeAnnotation, TypeDef, TypeHeader, TypeVar, ValueDef,
|
|
},
|
|
header::{
|
|
AppHeader, ExposedName, HostedHeader, Keyword, KeywordItem, ModuleHeader, ModuleName,
|
|
PackageHeader, PlatformHeader,
|
|
},
|
|
ident::Accessor,
|
|
};
|
|
use roc_region::all::Loc;
|
|
|
|
use crate::{
|
|
annotation::{is_collection_multiline, Parens},
|
|
collection::Braces,
|
|
expr::{expr_lift_spaces, fmt_str_literal, format_sq_literal, push_op},
|
|
header::{
|
|
fmt_app_header, fmt_hosted_header, fmt_package_header, fmt_platform_header,
|
|
fmt_spaces_with_outdent,
|
|
},
|
|
pattern::snakify_camel_ident,
|
|
spaces::fmt_spaces,
|
|
Buf, MigrationFlags,
|
|
};
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub enum Suffix {
|
|
None,
|
|
Comma,
|
|
OpenRound,
|
|
Question,
|
|
}
|
|
|
|
trait Fmt {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError>;
|
|
}
|
|
|
|
impl Fmt for Expr<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
fmt_expr(buf, indent, self, suffix)
|
|
}
|
|
}
|
|
|
|
impl Fmt for Pattern<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
fmt_pattern(buf, indent, self, suffix)
|
|
}
|
|
}
|
|
|
|
impl<F: Fmt> Fmt for &F {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
(*self).fmt(buf, indent, suffix)
|
|
}
|
|
}
|
|
|
|
impl<F: Fmt> Fmt for Loc<F> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
self.value.fmt(buf, indent, suffix)
|
|
}
|
|
}
|
|
|
|
impl<F: Fmt> Fmt for AssignedField<'_, F> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
AssignedField::RequiredValue(name, comment_or_newlines, loc1) => {
|
|
buf.indent(indent);
|
|
buf.push_str(name.value);
|
|
if !comment_or_newlines.is_empty() {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
buf.indent(indent);
|
|
buf.push_str(":");
|
|
buf.spaces(1);
|
|
loc1.fmt(buf, indent, suffix)?;
|
|
}
|
|
AssignedField::OptionalValue(name, comment_or_newlines, loc1) => {
|
|
buf.indent(indent);
|
|
buf.push_str(name.value);
|
|
if !comment_or_newlines.is_empty() {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
buf.indent(indent);
|
|
buf.push_str(":");
|
|
buf.spaces(1);
|
|
loc1.fmt(buf, indent, suffix)?;
|
|
}
|
|
AssignedField::IgnoredValue(name, comment_or_newlines, loc1) => {
|
|
buf.indent(indent);
|
|
buf.push('_');
|
|
buf.push_str(name.value);
|
|
if !comment_or_newlines.is_empty() {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
buf.indent(indent);
|
|
buf.push_str(":");
|
|
buf.spaces(1);
|
|
loc1.fmt(buf, indent, suffix)?;
|
|
}
|
|
AssignedField::LabelOnly(name) => {
|
|
buf.indent(indent);
|
|
buf.push_str(name.value);
|
|
}
|
|
AssignedField::SpaceBefore(assigned_field, comment_or_newlines) => {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
assigned_field.fmt(buf, indent, suffix)?;
|
|
}
|
|
AssignedField::SpaceAfter(assigned_field, comment_or_newlines) => {
|
|
assigned_field.fmt(buf, indent, suffix)?;
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub fn fmt_pattern(
|
|
buf: &mut Buf,
|
|
indent: u16,
|
|
pat: &Pattern<'_>,
|
|
suffix: Suffix,
|
|
) -> Result<(), MigrateError> {
|
|
match pat {
|
|
Pattern::Identifier { ident } => {
|
|
buf.indent(indent);
|
|
snakify_camel_ident(buf, ident);
|
|
}
|
|
Pattern::QualifiedIdentifier { module_name, ident } => {
|
|
buf.indent(indent);
|
|
buf.push_str(module_name);
|
|
buf.push('.');
|
|
snakify_camel_ident(buf, ident);
|
|
}
|
|
Pattern::Tag(tag) => {
|
|
buf.indent(indent);
|
|
buf.push_str(tag);
|
|
}
|
|
Pattern::OpaqueRef(opaque_ref) => {
|
|
buf.indent(indent);
|
|
buf.push_str(opaque_ref);
|
|
}
|
|
Pattern::Apply(loc, locs) => {
|
|
fmt_pattern(buf, indent, &loc.value, Suffix::OpenRound)?;
|
|
for (i, loc) in locs.iter().enumerate() {
|
|
let is_last = i == locs.len() - 1;
|
|
fmt_pattern(
|
|
buf,
|
|
indent,
|
|
&loc.value,
|
|
if is_last { Suffix::None } else { Suffix::Comma },
|
|
)?;
|
|
}
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
}
|
|
Pattern::PncApply(loc, collection) => {
|
|
fmt_pattern(buf, indent, &loc.value, Suffix::OpenRound)?;
|
|
for (i, loc) in collection.iter().enumerate() {
|
|
let is_last = i == collection.len() - 1;
|
|
fmt_pattern(
|
|
buf,
|
|
indent,
|
|
&loc.value,
|
|
if is_last { Suffix::None } else { Suffix::Comma },
|
|
)?;
|
|
if !is_last {
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
if !collection.final_comments().is_empty() {
|
|
fmt_spaces(buf, collection.final_comments().iter(), indent);
|
|
}
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
}
|
|
Pattern::RecordDestructure(collection) => {
|
|
fmt_collection(buf, indent, Braces::Curly, None, *collection, None)?;
|
|
}
|
|
Pattern::RequiredField(name, loc) => {
|
|
buf.indent(indent);
|
|
buf.push_str(name);
|
|
buf.push_str(":");
|
|
buf.spaces(1);
|
|
fmt_pattern(buf, indent, &loc.value, Suffix::None)?;
|
|
}
|
|
Pattern::OptionalField(name, loc) => {
|
|
buf.indent(indent);
|
|
buf.push_str(name);
|
|
buf.push_str("?");
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent, &loc.value, Suffix::Question)?;
|
|
}
|
|
Pattern::NumLiteral(num_literal) => {
|
|
buf.indent(indent);
|
|
buf.push_str(num_literal);
|
|
}
|
|
Pattern::NonBase10Literal {
|
|
string,
|
|
base,
|
|
is_negative,
|
|
} => {
|
|
buf.indent(indent);
|
|
if *is_negative {
|
|
buf.push('-');
|
|
}
|
|
match base {
|
|
Base::Hex => buf.push_str("0x"),
|
|
Base::Octal => buf.push_str("0o"),
|
|
Base::Binary => buf.push_str("0b"),
|
|
Base::Decimal => { /* nothing */ }
|
|
}
|
|
buf.push_str(string);
|
|
}
|
|
Pattern::FloatLiteral(float_literal) => {
|
|
buf.indent(indent);
|
|
buf.push_str(float_literal);
|
|
}
|
|
Pattern::StrLiteral(str_literal) => {
|
|
buf.indent(indent);
|
|
fmt_str_literal(buf, *str_literal, indent);
|
|
}
|
|
Pattern::Underscore(name) => {
|
|
buf.indent(indent);
|
|
buf.push('_');
|
|
buf.push_str(name);
|
|
}
|
|
Pattern::SingleQuote(single_quote) => {
|
|
buf.indent(indent);
|
|
buf.push('\'');
|
|
buf.push_str(single_quote);
|
|
buf.push('\'');
|
|
}
|
|
Pattern::Tuple(collection) => {
|
|
fmt_collection(buf, indent, Braces::Round, None, *collection, None)?;
|
|
}
|
|
Pattern::List(collection) => {
|
|
fmt_collection(buf, indent, Braces::Square, None, *collection, None)?;
|
|
}
|
|
Pattern::ListRest(rest) => {
|
|
buf.indent(indent);
|
|
buf.push_str("..");
|
|
if let Some(rest) = rest {
|
|
fmt_spaces(buf, rest.0.iter(), indent);
|
|
buf.indent(indent);
|
|
buf.ensure_ends_with_whitespace();
|
|
buf.push_str("as");
|
|
fmt_spaces(buf, rest.1.spaces_before.iter(), indent);
|
|
buf.ensure_ends_with_whitespace();
|
|
buf.indent(indent);
|
|
buf.push_str(rest.1.identifier.value);
|
|
}
|
|
}
|
|
Pattern::As(loc, pattern_as) => {
|
|
fmt_pattern(buf, indent, &loc.value, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push_str(" as");
|
|
fmt_spaces(buf, pattern_as.spaces_before.iter(), indent);
|
|
buf.ensure_ends_with_whitespace();
|
|
buf.indent(indent);
|
|
buf.push_str(pattern_as.identifier.value);
|
|
}
|
|
Pattern::SpaceBefore(pattern, comment_or_newlines) => {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
fmt_pattern(buf, indent, pattern, suffix)?;
|
|
}
|
|
Pattern::SpaceAfter(pattern, comment_or_newlines) => {
|
|
fmt_pattern(buf, indent, pattern, suffix)?;
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
Pattern::Malformed(_malformed) => {
|
|
return Err(MigrateError::MalformedPatternNotSupported);
|
|
}
|
|
Pattern::MalformedIdent(_, _bad_ident) => {
|
|
return Err(MigrateError::MalformedPatternIdentNotSupported);
|
|
}
|
|
Pattern::MalformedExpr(_expr) => {
|
|
return Err(MigrateError::MalformedPatternAsExprNotSupported);
|
|
}
|
|
}
|
|
|
|
if !matches!(pat, Pattern::SpaceAfter(..) | Pattern::SpaceBefore(..)) {
|
|
fmt_suffix(buf, indent, suffix);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn fmt_expr(
|
|
buf: &mut Buf,
|
|
indent: u16,
|
|
expr: &Expr<'_>,
|
|
suffix: Suffix,
|
|
) -> Result<(), MigrateError> {
|
|
match expr {
|
|
Expr::Float(string) => {
|
|
buf.indent(indent);
|
|
buf.push_str(string);
|
|
}
|
|
Expr::Num(string) => {
|
|
buf.indent(indent);
|
|
buf.push_str(string);
|
|
}
|
|
Expr::Tag(string) | Expr::OpaqueRef(string) => {
|
|
buf.indent(indent);
|
|
buf.push_str(string)
|
|
}
|
|
Expr::SingleQuote(string) => {
|
|
buf.indent(indent);
|
|
format_sq_literal(buf, string);
|
|
}
|
|
Expr::NonBase10Int {
|
|
base,
|
|
string,
|
|
is_negative,
|
|
} => {
|
|
buf.indent(indent);
|
|
if *is_negative {
|
|
buf.push('-');
|
|
}
|
|
|
|
match base {
|
|
Base::Hex => buf.push_str("0x"),
|
|
Base::Octal => buf.push_str("0o"),
|
|
Base::Binary => buf.push_str("0b"),
|
|
Base::Decimal => { /* nothing */ }
|
|
}
|
|
|
|
buf.push_str(string);
|
|
}
|
|
Expr::Str(literal) => {
|
|
fmt_str_literal(buf, *literal, indent);
|
|
}
|
|
Expr::Var { module_name, ident } => {
|
|
buf.indent(indent);
|
|
if !module_name.is_empty() {
|
|
buf.push_str(module_name);
|
|
buf.push('.');
|
|
}
|
|
snakify_camel_ident(buf, ident);
|
|
}
|
|
Expr::Underscore(name) => {
|
|
buf.indent(indent);
|
|
buf.push('_');
|
|
buf.push_str(name);
|
|
}
|
|
Expr::Crash => {
|
|
buf.indent(indent);
|
|
buf.push_str("crash");
|
|
}
|
|
Expr::Dbg => {
|
|
buf.indent(indent);
|
|
buf.push_str("dbg");
|
|
}
|
|
Expr::Try => {
|
|
buf.indent(indent);
|
|
buf.push_str("try");
|
|
}
|
|
Expr::RecordAccess(expr, name) => {
|
|
buf.indent(indent);
|
|
fmt_expr(buf, indent, expr, Suffix::None)?;
|
|
buf.push_str(".");
|
|
buf.push_str(name);
|
|
}
|
|
Expr::TupleAccess(expr, index) => {
|
|
buf.indent(indent);
|
|
fmt_expr(buf, indent, expr, Suffix::None)?;
|
|
buf.push_str(".");
|
|
buf.push_str(index);
|
|
}
|
|
Expr::AccessorFunction(Accessor::TupleIndex(string))
|
|
| Expr::AccessorFunction(Accessor::RecordField(string)) => {
|
|
buf.indent(indent);
|
|
buf.push_str(".");
|
|
buf.push_str(string);
|
|
}
|
|
Expr::RecordUpdater(name) => {
|
|
buf.indent(indent);
|
|
buf.push_str("&");
|
|
buf.push_str(name);
|
|
}
|
|
Expr::TrySuffix(expr) => {
|
|
fmt_expr(buf, indent, expr, Suffix::Question)?;
|
|
}
|
|
Expr::List(collection) => {
|
|
fmt_collection(buf, indent, Braces::Square, None, *collection, None)?;
|
|
}
|
|
Expr::RecordUpdate { update, fields } => {
|
|
fmt_collection(
|
|
buf,
|
|
indent,
|
|
Braces::Curly,
|
|
Some(CollectionHead::Update(update)),
|
|
*fields,
|
|
None,
|
|
)?;
|
|
}
|
|
Expr::Record(collection) => {
|
|
fmt_collection(buf, indent, Braces::Curly, None, *collection, None)?;
|
|
}
|
|
Expr::Tuple(collection) => {
|
|
fmt_collection(buf, indent, Braces::Round, None, *collection, None)?;
|
|
}
|
|
Expr::RecordBuilder { mapper, fields } => {
|
|
fmt_collection(
|
|
buf,
|
|
indent,
|
|
Braces::Curly,
|
|
Some(CollectionHead::Mapper(mapper)),
|
|
*fields,
|
|
None,
|
|
)?;
|
|
}
|
|
Expr::Closure(args, body) => {
|
|
buf.indent(indent);
|
|
buf.push('|');
|
|
for arg in *args {
|
|
fmt_pattern(buf, indent, &arg.value, Suffix::None)?;
|
|
}
|
|
buf.indent(indent);
|
|
buf.push_str("|");
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent, &body.value, Suffix::None)?;
|
|
}
|
|
Expr::Defs(defs, final_expr) => {
|
|
buf.indent(indent);
|
|
buf.push('{');
|
|
buf.ensure_ends_with_newline();
|
|
|
|
for (index, def) in defs.defs().enumerate() {
|
|
let spaces_before = &defs.spaces[defs.space_before[index].indices()];
|
|
let spaces_after = &defs.spaces[defs.space_after[index].indices()];
|
|
|
|
if !spaces_before.is_empty() {
|
|
fmt_spaces(buf, spaces_before.iter(), indent + 4);
|
|
}
|
|
|
|
match def {
|
|
Ok(type_def) => type_def.fmt(buf, indent + 4, Suffix::None)?,
|
|
Err(value_def) => value_def.fmt(buf, indent + 4, Suffix::None)?,
|
|
}
|
|
|
|
if !spaces_after.is_empty() {
|
|
fmt_spaces(buf, spaces_after.iter(), indent + 4);
|
|
}
|
|
}
|
|
|
|
fmt_expr(buf, indent + 4, &final_expr.value, Suffix::None)?;
|
|
|
|
buf.ensure_ends_with_newline();
|
|
buf.indent(indent);
|
|
buf.push('}');
|
|
}
|
|
Expr::DbgStmt {
|
|
first,
|
|
extra_args,
|
|
continuation,
|
|
pnc_style: _,
|
|
} => {
|
|
buf.indent(indent);
|
|
buf.push_str("dbg");
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent, &first.value, Suffix::None)?;
|
|
for arg in *extra_args {
|
|
buf.push_str(",");
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent, &arg.value, Suffix::None)?;
|
|
}
|
|
buf.ensure_ends_with_newline();
|
|
fmt_expr(buf, indent, &continuation.value, Suffix::None)?;
|
|
}
|
|
Expr::LowLevelTry(..) => todo!(),
|
|
Expr::LowLevelDbg(..) => todo!(),
|
|
Expr::Apply(func, args, _) => {
|
|
fmt_expr(buf, indent, &func.value, Suffix::OpenRound)?;
|
|
for (i, arg) in args.iter().enumerate() {
|
|
let is_last = i == args.len() - 1;
|
|
fmt_expr(
|
|
buf,
|
|
indent,
|
|
&arg.value,
|
|
if is_last { Suffix::None } else { Suffix::Comma },
|
|
)?;
|
|
if !is_last {
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
}
|
|
Expr::PncApply(func, collection) => {
|
|
fmt_expr(buf, indent, &func.value, Suffix::OpenRound)?;
|
|
for (i, arg) in collection.iter().enumerate() {
|
|
let is_last = i == collection.len() - 1;
|
|
fmt_expr(
|
|
buf,
|
|
indent,
|
|
&arg.value,
|
|
if is_last { Suffix::None } else { Suffix::Comma },
|
|
)?;
|
|
if !is_last {
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
if !collection.final_comments().is_empty() {
|
|
fmt_spaces(buf, collection.final_comments().iter(), indent);
|
|
}
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
}
|
|
Expr::BinOps(expr_op_pairs, last_expr) => {
|
|
let arena = buf.text.bump();
|
|
let converted = migrate_pizza(buf, arena, expr_op_pairs, **last_expr)?;
|
|
fmt_converted_ops(buf, indent, &converted, Suffix::None)?;
|
|
}
|
|
Expr::UnaryOp(expr, op) => {
|
|
buf.indent(indent);
|
|
let ch = match op.value {
|
|
UnaryOp::Negate => "-",
|
|
UnaryOp::Not => "!",
|
|
};
|
|
buf.push_str(ch);
|
|
fmt_expr(buf, indent, &expr.value, Suffix::None)?;
|
|
}
|
|
Expr::If {
|
|
if_thens,
|
|
final_else,
|
|
indented_else: _,
|
|
} => {
|
|
for (i, (cond, then)) in if_thens.iter().enumerate() {
|
|
buf.indent(indent);
|
|
if i == 0 {
|
|
buf.push_str("if");
|
|
} else {
|
|
buf.push_str("else if");
|
|
}
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent, &cond.value, Suffix::None)?;
|
|
buf.spaces(1);
|
|
fmt_expr_ensure_block(buf, indent, &then.value)?;
|
|
}
|
|
buf.indent(indent);
|
|
buf.spaces(1);
|
|
buf.push_str("else");
|
|
buf.spaces(1);
|
|
fmt_expr_ensure_block(buf, indent, &final_else.value)?;
|
|
}
|
|
Expr::When(cond, when_branchs) => {
|
|
buf.indent(indent);
|
|
buf.push_str("match");
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent, &cond.value, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.spaces(1);
|
|
buf.push('{');
|
|
buf.ensure_ends_with_newline();
|
|
for branch in when_branchs.iter() {
|
|
for (i, pat) in branch.patterns.iter().enumerate() {
|
|
if i != 0 {
|
|
buf.indent(indent);
|
|
buf.push_str(" |");
|
|
buf.spaces(1);
|
|
}
|
|
fmt_pattern(buf, indent + 4, &pat.value, Suffix::None)?;
|
|
}
|
|
if let Some(guard) = &branch.guard {
|
|
buf.indent(indent);
|
|
buf.push_str(" if");
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent + 4, &guard.value, Suffix::None)?;
|
|
}
|
|
buf.indent(indent);
|
|
buf.push_str("->");
|
|
fmt_expr(buf, indent + 4, &branch.value.value, Suffix::None)?;
|
|
}
|
|
buf.indent(indent);
|
|
buf.push('}');
|
|
}
|
|
Expr::Return(value, cont) => {
|
|
buf.indent(indent);
|
|
buf.push_str("return");
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent, &value.value, Suffix::None)?;
|
|
buf.ensure_ends_with_newline();
|
|
if let Some(cont) = cont {
|
|
fmt_expr(buf, indent, &cont.value, Suffix::None)?;
|
|
}
|
|
}
|
|
Expr::SpaceBefore(expr, comment_or_newlines) => {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
fmt_expr(buf, indent, expr, suffix)?;
|
|
}
|
|
Expr::SpaceAfter(expr, comment_or_newlines) => {
|
|
fmt_expr(buf, indent, expr, suffix)?;
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
Expr::ParensAround(expr) => {
|
|
buf.indent(indent);
|
|
buf.push('(');
|
|
fmt_expr(buf, indent, expr, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
}
|
|
Expr::MalformedIdent(_name, _bad_ident) => {
|
|
return Err(MigrateError::MalformedIdentNotSupported)
|
|
}
|
|
Expr::PrecedenceConflict(_precedence_conflict) => {
|
|
return Err(MigrateError::PrecedenceConflictNotSupported)
|
|
}
|
|
Expr::EmptyRecordBuilder(_inner) => todo!(),
|
|
Expr::SingleFieldRecordBuilder(_loc) => todo!(),
|
|
Expr::OptionalFieldInRecordBuilder(_loc, _loc1) => todo!(),
|
|
}
|
|
|
|
if !matches!(expr, Expr::SpaceAfter(..) | Expr::SpaceBefore(..)) {
|
|
fmt_suffix(buf, indent, suffix);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum MigratedBinOp<'a> {
|
|
BinOp {
|
|
lhs: &'a MigratedBinOp<'a>,
|
|
op: BinOp,
|
|
rhs: &'a MigratedBinOp<'a>,
|
|
},
|
|
Parens(&'a MigratedBinOp<'a>),
|
|
StaticDispatch {
|
|
lhs: &'a MigratedBinOp<'a>,
|
|
before: &'a [CommentOrNewline<'a>],
|
|
func: &'a str,
|
|
args: &'a [Expr<'a>],
|
|
after: &'a [CommentOrNewline<'a>],
|
|
},
|
|
FuncStaticDispatch {
|
|
lhs: &'a MigratedBinOp<'a>,
|
|
before: &'a [CommentOrNewline<'a>],
|
|
func: &'a Expr<'a>,
|
|
args: &'a [Expr<'a>],
|
|
after: &'a [CommentOrNewline<'a>],
|
|
},
|
|
Expr(Expr<'a>),
|
|
}
|
|
|
|
fn apply_ops<'a>(
|
|
buf: &Buf<'a>,
|
|
arena: &'a Bump,
|
|
stack: &mut Vec<MigratedBinOp<'a>>,
|
|
ops: &mut Vec<BinOp>,
|
|
min_precedence: u8,
|
|
) -> Result<(), MigrateError> {
|
|
while let Some(&op) = ops.last() {
|
|
if op.precedence() <= min_precedence
|
|
&& (op.associativity() == Associativity::RightAssociative
|
|
|| op.precedence() < min_precedence)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ops.pop();
|
|
|
|
let rhs = stack.pop().unwrap();
|
|
let lhs = stack.pop().unwrap();
|
|
|
|
let result = match op {
|
|
BinOp::Pizza => {
|
|
// Need to migrate to StaticDispatch or FuncStaticDispatch!
|
|
match rhs {
|
|
MigratedBinOp::Expr(expr) => {
|
|
let expr = expr.extract_spaces();
|
|
match expr.item {
|
|
Expr::Apply(func, args, _) => {
|
|
if let Some(name) = is_staticable(buf, &func.value) {
|
|
MigratedBinOp::StaticDispatch {
|
|
lhs: maybe_parens(arena, lhs),
|
|
before: expr.before,
|
|
func: arena.alloc_str(name),
|
|
args: arena.alloc_slice_fill_iter(
|
|
args.iter().map(|arg| arg.value),
|
|
),
|
|
after: expr.after,
|
|
}
|
|
} else {
|
|
MigratedBinOp::FuncStaticDispatch {
|
|
lhs: maybe_parens(arena, lhs),
|
|
before: expr.before,
|
|
func: arena.alloc(func.value),
|
|
args: arena.alloc_slice_fill_iter(
|
|
args.iter().map(|arg| arg.value),
|
|
),
|
|
after: expr.after,
|
|
}
|
|
}
|
|
}
|
|
Expr::Var { module_name, ident } => {
|
|
let ident = if buf.flags.snakify {
|
|
snakify_camel_ident_in_bump(buf.text.bump(), ident)
|
|
.into_bump_str()
|
|
} else {
|
|
ident
|
|
};
|
|
if is_static_method(module_name, ident) {
|
|
MigratedBinOp::StaticDispatch {
|
|
lhs: maybe_parens(arena, lhs),
|
|
before: expr.before,
|
|
func: arena.alloc_str(ident),
|
|
args: &[],
|
|
after: expr.after,
|
|
}
|
|
} else {
|
|
MigratedBinOp::FuncStaticDispatch {
|
|
lhs: maybe_parens(arena, lhs),
|
|
before: expr.before,
|
|
func: arena.alloc(Expr::Var { module_name, ident }),
|
|
args: &[],
|
|
after: expr.after,
|
|
}
|
|
}
|
|
}
|
|
_ => return Err(MigrateError::PizzaOpRhsNotSupported),
|
|
}
|
|
}
|
|
_ => return Err(MigrateError::PizzaOpRhsNotSupported),
|
|
}
|
|
}
|
|
_ => MigratedBinOp::BinOp {
|
|
lhs: arena.alloc(lhs),
|
|
op,
|
|
rhs: arena.alloc(rhs),
|
|
},
|
|
};
|
|
|
|
stack.push(result);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn maybe_parens<'a>(arena: &'a Bump, lhs: MigratedBinOp<'a>) -> &'a MigratedBinOp<'a> {
|
|
match lhs {
|
|
MigratedBinOp::Parens(_)
|
|
| MigratedBinOp::StaticDispatch { .. }
|
|
| MigratedBinOp::FuncStaticDispatch { .. }
|
|
| MigratedBinOp::Expr(_) => arena.alloc(lhs),
|
|
MigratedBinOp::BinOp { .. } => arena.alloc(MigratedBinOp::Parens(arena.alloc(lhs))),
|
|
}
|
|
}
|
|
|
|
fn is_staticable<'a>(buf: &Buf<'a>, value: &Expr<'a>) -> Option<&'a str> {
|
|
match value {
|
|
Expr::Var { module_name, ident } => {
|
|
let ident = if buf.flags.snakify {
|
|
snakify_camel_ident_in_bump(buf.text.bump(), ident).into_bump_str()
|
|
} else {
|
|
ident
|
|
};
|
|
if is_static_method(module_name, ident) {
|
|
Some(ident)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn is_static_method(module_name: &str, ident: &str) -> bool {
|
|
match module_name {
|
|
"Str" => matches!(
|
|
ident,
|
|
"concat"
|
|
| "is_empty"
|
|
| "join_with"
|
|
| "split_on"
|
|
| "repeat"
|
|
| "count_utf8_bytes"
|
|
| "to_utf8"
|
|
| "starts_with"
|
|
| "ends_with"
|
|
| "trim"
|
|
| "trim_start"
|
|
| "trim_end"
|
|
| "to_dec"
|
|
| "to_f64"
|
|
| "to_f32"
|
|
| "to_u128"
|
|
| "to_i128"
|
|
| "to_u64"
|
|
| "to_i64"
|
|
| "to_u32"
|
|
| "to_i32"
|
|
| "to_u16"
|
|
| "to_i16"
|
|
| "to_u8"
|
|
| "to_i8"
|
|
| "replace_each"
|
|
| "replace_first"
|
|
| "replace_last"
|
|
| "split_first"
|
|
| "split_last"
|
|
| "walk_utf8"
|
|
| "walk_utf8_with_index"
|
|
| "reserve"
|
|
| "release_excess_capacity"
|
|
| "with_prefix"
|
|
| "contains"
|
|
| "drop_prefix"
|
|
| "drop_suffix"
|
|
| "with_ascii_lowercased"
|
|
| "with_ascii_uppercased"
|
|
| "caseless_ascii_equals"
|
|
),
|
|
|
|
// TODO: other modules
|
|
"List" => matches!(
|
|
ident,
|
|
"is_empty"
|
|
| "get"
|
|
| "set"
|
|
| "replace"
|
|
| "update"
|
|
| "append"
|
|
| "append_if_ok"
|
|
| "prepend"
|
|
| "prepend_if_ok"
|
|
| "map"
|
|
| "len"
|
|
| "walk_backwards"
|
|
| "concat"
|
|
| "first"
|
|
| "single"
|
|
| "repeat"
|
|
| "reverse"
|
|
| "join"
|
|
| "keep_if"
|
|
| "contains"
|
|
| "sum"
|
|
| "walk"
|
|
| "last"
|
|
| "keep_oks"
|
|
| "keep_errs"
|
|
| "map_with_index"
|
|
| "map2"
|
|
| "map3"
|
|
| "product"
|
|
| "walk_with_index"
|
|
| "walk_until"
|
|
| "walk_with_index_until"
|
|
| "walk_from"
|
|
| "walk_from_until"
|
|
| "range"
|
|
| "sort_with"
|
|
| "swap"
|
|
| "drop_at"
|
|
| "min"
|
|
| "max"
|
|
| "map4"
|
|
| "map_try"
|
|
| "walk_try"
|
|
| "join_map"
|
|
| "any"
|
|
| "take_first"
|
|
| "take_last"
|
|
| "drop_first"
|
|
| "drop_last"
|
|
| "find_first"
|
|
| "find_last"
|
|
| "find_first_index"
|
|
| "find_last_index"
|
|
| "sublist"
|
|
| "intersperse"
|
|
| "split_at"
|
|
| "split_on"
|
|
| "split_on_list"
|
|
| "split_first"
|
|
| "split_last"
|
|
| "starts_with"
|
|
| "ends_with"
|
|
| "all"
|
|
| "drop_if"
|
|
| "sort_asc"
|
|
| "sort_desc"
|
|
| "reserve"
|
|
| "release_excess_capacity"
|
|
| "walk_backwards_until"
|
|
| "count_if"
|
|
| "chunks_of"
|
|
| "concat_utf8"
|
|
| "for_each!"
|
|
| "for_each_try!"
|
|
| "walk!"
|
|
| "walk_try!"
|
|
),
|
|
"Dict" => {
|
|
matches!(
|
|
ident,
|
|
"clear"
|
|
| "capacity"
|
|
| "reserve"
|
|
| "release_excess_capacity"
|
|
| "len"
|
|
| "is_empty"
|
|
| "get"
|
|
| "contains"
|
|
| "insert"
|
|
| "remove"
|
|
| "update"
|
|
| "walk"
|
|
| "walk_until"
|
|
| "keep_if"
|
|
| "drop_if"
|
|
| "to_list"
|
|
| "keys"
|
|
| "values"
|
|
| "insert_all"
|
|
| "keep_shared"
|
|
| "remove_all"
|
|
| "map"
|
|
| "join_map"
|
|
)
|
|
}
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
fn migrate_pizza<'a>(
|
|
buf: &Buf<'a>,
|
|
arena: &'a Bump,
|
|
expr_op_pairs: &[(Loc<Expr<'a>>, Loc<BinOp>)],
|
|
last_expr: Loc<Expr<'a>>,
|
|
) -> Result<MigratedBinOp<'a>, MigrateError> {
|
|
println!(
|
|
"Starting migrate_pizza with {} expr_op_pairs",
|
|
expr_op_pairs.len()
|
|
);
|
|
|
|
let mut stack: Vec<MigratedBinOp<'a>> = Vec::new();
|
|
let mut ops: Vec<BinOp> = Vec::new();
|
|
|
|
for (expr, op) in expr_op_pairs {
|
|
println!("Processing expression: {:?}", expr.value);
|
|
stack.push(MigratedBinOp::Expr(expr.value));
|
|
apply_ops(buf, arena, &mut stack, &mut ops, op.value.precedence())?;
|
|
|
|
println!("Pushing operator onto stack: {:?}", op.value);
|
|
ops.push(op.value);
|
|
}
|
|
|
|
// Push the last expression onto the stack
|
|
println!("Pushing last expression onto stack: {:?}", last_expr.value);
|
|
stack.push(MigratedBinOp::Expr(last_expr.value));
|
|
|
|
// Apply all remaining operators
|
|
println!("Applying all remaining operators (min_precedence: 0)");
|
|
apply_ops(buf, arena, &mut stack, &mut ops, 0)?;
|
|
println!("Final stack size: {}", stack.len());
|
|
|
|
// The final result should be at the top of the stack
|
|
let result = stack.pop().unwrap();
|
|
println!("Returning final result: {:?}", result);
|
|
Ok(result)
|
|
}
|
|
|
|
fn fmt_converted_ops(
|
|
buf: &mut Buf,
|
|
indent: u16,
|
|
converted: &MigratedBinOp,
|
|
suffix: Suffix,
|
|
) -> Result<(), MigrateError> {
|
|
match converted {
|
|
MigratedBinOp::BinOp { lhs, op, rhs } => {
|
|
// Format left-hand side
|
|
fmt_converted_ops(buf, indent, lhs, Suffix::None)?;
|
|
|
|
// Format operator
|
|
buf.indent(indent);
|
|
buf.spaces(1);
|
|
push_op(buf, *op);
|
|
buf.spaces(1);
|
|
|
|
// Format right-hand side
|
|
fmt_converted_ops(buf, indent, rhs, suffix)?;
|
|
}
|
|
MigratedBinOp::Expr(expr) => {
|
|
fmt_expr(buf, indent, expr, suffix)?;
|
|
}
|
|
MigratedBinOp::Parens(inner) => {
|
|
buf.push('(');
|
|
fmt_converted_ops(buf, indent, inner, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
fmt_suffix(buf, indent, suffix);
|
|
}
|
|
MigratedBinOp::StaticDispatch {
|
|
lhs,
|
|
before,
|
|
func,
|
|
args,
|
|
after,
|
|
} => {
|
|
// lhs.func(args)
|
|
fmt_converted_ops(buf, indent, lhs, Suffix::None)?;
|
|
if !before.is_empty() {
|
|
fmt_spaces(buf, before.iter(), indent);
|
|
}
|
|
buf.indent(indent);
|
|
buf.push('.');
|
|
buf.push_str(func);
|
|
buf.push('(');
|
|
for (i, arg) in args.iter().enumerate() {
|
|
fmt_expr(buf, indent, arg, Suffix::None)?;
|
|
if i != args.len() - 1 {
|
|
buf.indent(indent);
|
|
buf.push(',');
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
fmt_suffix(buf, indent, suffix);
|
|
if !after.is_empty() {
|
|
fmt_spaces(buf, after.iter(), indent);
|
|
}
|
|
}
|
|
MigratedBinOp::FuncStaticDispatch {
|
|
lhs,
|
|
before,
|
|
func,
|
|
args,
|
|
after,
|
|
} => {
|
|
// lhs.(func)(args)
|
|
fmt_converted_ops(buf, indent, lhs, Suffix::None)?;
|
|
if !before.is_empty() {
|
|
fmt_spaces(buf, before.iter(), indent);
|
|
}
|
|
buf.indent(indent);
|
|
buf.push('.');
|
|
buf.push('(');
|
|
fmt_expr(buf, indent, func, Suffix::None)?;
|
|
buf.push(')');
|
|
buf.push('(');
|
|
for (i, arg) in args.iter().enumerate() {
|
|
fmt_expr(buf, indent, arg, Suffix::None)?;
|
|
if i != args.len() - 1 {
|
|
buf.indent(indent);
|
|
buf.push(',');
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
fmt_suffix(buf, indent, suffix);
|
|
if !after.is_empty() {
|
|
fmt_spaces(buf, after.iter(), indent);
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn fmt_expr_top_level(
|
|
buf: &mut Buf<'_>,
|
|
indent: u16,
|
|
expr: &Expr<'_>,
|
|
) -> Result<(), MigrateError> {
|
|
let expr = expr_lift_spaces(Parens::NotNeeded, buf.text.bump(), expr);
|
|
if !expr.before.is_empty() {
|
|
fmt_spaces(buf, expr.before.iter(), indent);
|
|
}
|
|
match expr.item {
|
|
Expr::Defs(defs, final_expr) => {
|
|
fmt_defs(buf, defs)?;
|
|
fmt_expr(buf, indent, &final_expr.value, Suffix::None)?;
|
|
}
|
|
_ => {
|
|
fmt_expr(buf, indent, &expr.item, Suffix::None)?;
|
|
}
|
|
}
|
|
if !expr.after.is_empty() {
|
|
fmt_spaces(buf, expr.after.iter(), indent);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn fmt_expr_ensure_block(
|
|
buf: &mut Buf<'_>,
|
|
indent: u16,
|
|
expr: &Expr<'_>,
|
|
) -> Result<(), MigrateError> {
|
|
if matches!(expr, Expr::Defs(..)) {
|
|
fmt_expr(buf, indent, expr, Suffix::None)?;
|
|
} else {
|
|
buf.indent(indent);
|
|
buf.push('{');
|
|
buf.ensure_ends_with_newline();
|
|
fmt_expr(buf, indent + 4, expr, Suffix::None)?;
|
|
buf.ensure_ends_with_newline();
|
|
buf.indent(indent);
|
|
buf.push('}');
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn fmt_suffix(buf: &mut Buf, indent: u16, suffix: Suffix) {
|
|
buf.indent(indent);
|
|
match suffix {
|
|
Suffix::None => {}
|
|
Suffix::Comma => buf.push(','),
|
|
Suffix::OpenRound => buf.push('('),
|
|
Suffix::Question => buf.push('?'),
|
|
}
|
|
}
|
|
|
|
enum CollectionHead<'a> {
|
|
Mapper(&'a Loc<Expr<'a>>),
|
|
Update(&'a Loc<Expr<'a>>),
|
|
}
|
|
|
|
enum CollectionTail<'a> {
|
|
Ext(TypeAnnotation<'a>),
|
|
}
|
|
|
|
fn fmt_collection<F: Fmt>(
|
|
buf: &mut Buf<'_>,
|
|
indent: u16,
|
|
braces: Braces,
|
|
head: Option<CollectionHead<'_>>,
|
|
collection: Collection<'_, F>,
|
|
tail: Option<CollectionTail<'_>>,
|
|
) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
buf.push(braces.start());
|
|
if collection.is_empty() && collection.final_comments().is_empty() && head.is_none() {
|
|
buf.push(braces.end());
|
|
return Ok(());
|
|
}
|
|
|
|
if let Some(head) = head {
|
|
match head {
|
|
CollectionHead::Mapper(mapper) => {
|
|
fmt_expr(buf, indent, &mapper.value, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push_str("<-");
|
|
}
|
|
CollectionHead::Update(update) => {
|
|
fmt_expr(buf, indent, &update.value, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push_str("&");
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i, item) in collection.iter().enumerate() {
|
|
let is_last = i == collection.len() - 1 && tail.is_none();
|
|
item.fmt(
|
|
buf,
|
|
indent + 4,
|
|
if is_last { Suffix::None } else { Suffix::Comma },
|
|
)?;
|
|
if !is_last {
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
|
|
if let Some(tail) = tail {
|
|
match tail {
|
|
CollectionTail::Ext(ext) => {
|
|
buf.indent(indent);
|
|
buf.push_str("..");
|
|
ext.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
}
|
|
}
|
|
|
|
if !collection.final_comments().is_empty() {
|
|
fmt_spaces(buf, collection.final_comments().iter(), indent);
|
|
}
|
|
|
|
buf.indent(indent);
|
|
buf.push(braces.end());
|
|
|
|
Ok(())
|
|
}
|
|
impl Fmt for TypeVar<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
TypeVar::Identifier(name) => {
|
|
buf.indent(indent);
|
|
buf.push_str(name);
|
|
|
|
fmt_suffix(buf, indent, suffix);
|
|
}
|
|
TypeVar::SpaceBefore(type_var, comment_or_newlines) => {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
type_var.fmt(buf, indent, suffix)?;
|
|
}
|
|
TypeVar::SpaceAfter(type_var, comment_or_newlines) => {
|
|
type_var.fmt(buf, indent, suffix)?;
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
TypeVar::Malformed(expr) => {
|
|
fmt_expr(buf, indent, expr, suffix)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for TypeHeader<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
buf.push_str(self.name.value);
|
|
if !self.vars.is_empty() {
|
|
buf.push('(');
|
|
for (i, arg) in self.vars.iter().enumerate() {
|
|
let is_last = i == self.vars.len() - 1;
|
|
arg.fmt(
|
|
buf,
|
|
indent,
|
|
if is_last { Suffix::None } else { Suffix::Comma },
|
|
)?;
|
|
if !is_last {
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
}
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for Tag<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
Tag::Apply { name, args } => {
|
|
buf.indent(indent);
|
|
buf.push_str(name.value);
|
|
if !args.is_empty() {
|
|
buf.push('(');
|
|
for (i, arg) in args.iter().enumerate() {
|
|
let is_last = i == args.len() - 1;
|
|
arg.fmt(
|
|
buf,
|
|
indent,
|
|
if is_last { Suffix::None } else { Suffix::Comma },
|
|
)?;
|
|
if !is_last {
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
}
|
|
|
|
fmt_suffix(buf, indent, suffix);
|
|
}
|
|
Tag::SpaceBefore(tag, comment_or_newlines) => {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
tag.fmt(buf, indent, suffix)?;
|
|
}
|
|
Tag::SpaceAfter(tag, comment_or_newlines) => {
|
|
tag.fmt(buf, indent, suffix)?;
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for TypeAnnotation<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
TypeAnnotation::Function(args, function_arrow, res) => {
|
|
for (i, arg) in args.iter().enumerate() {
|
|
let is_last = i == args.len() - 1;
|
|
arg.fmt(
|
|
buf,
|
|
indent,
|
|
if is_last { Suffix::None } else { Suffix::Comma },
|
|
)?;
|
|
if !is_last {
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
match function_arrow {
|
|
FunctionArrow::Pure => {
|
|
buf.push_str(" ->");
|
|
}
|
|
FunctionArrow::Effectful => {
|
|
buf.push_str(" =>");
|
|
}
|
|
}
|
|
buf.spaces(1);
|
|
res.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
TypeAnnotation::Apply(module, func, locs) => {
|
|
buf.indent(indent);
|
|
if !module.is_empty() {
|
|
buf.push_str(module);
|
|
buf.push('.');
|
|
}
|
|
buf.push_str(func);
|
|
if !locs.is_empty() {
|
|
buf.push('(');
|
|
for (i, loc) in locs.iter().enumerate() {
|
|
let is_last = i == locs.len() - 1;
|
|
loc.fmt(
|
|
buf,
|
|
indent,
|
|
if is_last { Suffix::None } else { Suffix::Comma },
|
|
)?;
|
|
if !is_last {
|
|
buf.spaces(1);
|
|
}
|
|
}
|
|
buf.indent(indent);
|
|
buf.push(')');
|
|
}
|
|
}
|
|
TypeAnnotation::BoundVariable(name) => {
|
|
buf.indent(indent);
|
|
buf.push_str(name);
|
|
}
|
|
TypeAnnotation::As(lhs, comment_or_newlines, type_header) => {
|
|
lhs.fmt(buf, indent, Suffix::None)?;
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
buf.indent(indent);
|
|
buf.push_str("as");
|
|
buf.spaces(1);
|
|
type_header.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
TypeAnnotation::Record { fields, ext } => {
|
|
fmt_collection(
|
|
buf,
|
|
indent,
|
|
Braces::Curly,
|
|
None,
|
|
*fields,
|
|
ext.map(|ext| CollectionTail::Ext(ext.value)),
|
|
)?;
|
|
}
|
|
TypeAnnotation::Tuple { elems, ext } => {
|
|
fmt_collection(
|
|
buf,
|
|
indent,
|
|
Braces::Round,
|
|
None,
|
|
*elems,
|
|
ext.map(|ext| CollectionTail::Ext(ext.value)),
|
|
)?;
|
|
}
|
|
TypeAnnotation::TagUnion { ext, tags } => {
|
|
fmt_collection(
|
|
buf,
|
|
indent,
|
|
Braces::Square,
|
|
None,
|
|
*tags,
|
|
ext.map(|ext| CollectionTail::Ext(ext.value)),
|
|
)?;
|
|
}
|
|
TypeAnnotation::Inferred => {
|
|
buf.indent(indent);
|
|
buf.push_str("_");
|
|
}
|
|
TypeAnnotation::Wildcard => {
|
|
return Err(MigrateError::WildcardTypeNotSupported);
|
|
}
|
|
TypeAnnotation::Where(left, clauses) => {
|
|
left.fmt(buf, indent, Suffix::None)?;
|
|
for clause in clauses.iter() {
|
|
buf.indent(indent);
|
|
buf.push_str(" where");
|
|
buf.spaces(1);
|
|
clause.value.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
}
|
|
TypeAnnotation::SpaceBefore(type_annotation, comment_or_newlines) => {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
type_annotation.fmt(buf, indent, suffix)?;
|
|
}
|
|
TypeAnnotation::SpaceAfter(type_annotation, comment_or_newlines) => {
|
|
type_annotation.fmt(buf, indent, suffix)?;
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
TypeAnnotation::Malformed(_) => todo!(),
|
|
}
|
|
|
|
if !matches!(
|
|
self,
|
|
TypeAnnotation::SpaceAfter(..) | TypeAnnotation::SpaceBefore(..)
|
|
) {
|
|
fmt_suffix(buf, indent, suffix);
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<F: Fmt> Fmt for Spaced<'_, F> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
Spaced::Item(value) => value.fmt(buf, indent, suffix)?,
|
|
Spaced::SpaceBefore(spaced, comment_or_newlines) => {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
spaced.fmt(buf, indent, suffix)?;
|
|
}
|
|
Spaced::SpaceAfter(spaced, comment_or_newlines) => {
|
|
spaced.fmt(buf, indent, suffix)?;
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for &str {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
buf.push_str(self);
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for ImplementsClause<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
self.var.value.fmt(buf, indent, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push_str(" implements");
|
|
buf.spaces(1);
|
|
for (i, var) in self.abilities.iter().enumerate() {
|
|
if i > 0 {
|
|
buf.indent(indent);
|
|
buf.spaces(1);
|
|
buf.push_str("&");
|
|
buf.spaces(1);
|
|
}
|
|
var.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for ImplementsAbilities<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
if !self.before_implements_kw.is_empty() {
|
|
buf.newline();
|
|
buf.indent(indent);
|
|
fmt_spaces(buf, self.before_implements_kw.iter(), indent);
|
|
}
|
|
buf.ensure_ends_with_newline();
|
|
buf.indent(indent);
|
|
buf.push_str(roc_parse::keyword::IMPLEMENTS);
|
|
if !self.after_implements_kw.is_empty() {
|
|
fmt_spaces(buf, self.after_implements_kw.iter(), indent);
|
|
}
|
|
buf.ensure_ends_with_whitespace();
|
|
fmt_collection(buf, indent, Braces::Square, None, self.item.value, None)?;
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for AbilityImpls<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
AbilityImpls::AbilityImpls(impls) => {
|
|
fmt_collection(buf, indent, Braces::Curly, None, *impls, None)?;
|
|
fmt_suffix(buf, indent, suffix);
|
|
}
|
|
AbilityImpls::SpaceBefore(impls, spaces) => {
|
|
fmt_spaces(buf, spaces.iter(), indent);
|
|
impls.fmt(buf, indent, suffix)?;
|
|
}
|
|
AbilityImpls::SpaceAfter(impls, spaces) => {
|
|
impls.fmt(buf, indent, suffix)?;
|
|
fmt_spaces(buf, spaces.iter(), indent);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for ImplementsAbility<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
ImplementsAbility::ImplementsAbility { ability, impls } => {
|
|
ability.fmt(buf, indent, Suffix::None)?;
|
|
if let Some(impls) = impls {
|
|
buf.push_str(" implements");
|
|
buf.spaces(1);
|
|
impls.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
fmt_suffix(buf, indent, suffix);
|
|
}
|
|
ImplementsAbility::SpaceBefore(implements_ability, comment_or_newlines) => {
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
implements_ability.fmt(buf, indent, suffix)?;
|
|
}
|
|
ImplementsAbility::SpaceAfter(implements_ability, comment_or_newlines) => {
|
|
implements_ability.fmt(buf, indent, suffix)?;
|
|
fmt_spaces(buf, comment_or_newlines.iter(), indent);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
impl Fmt for TypeDef<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
TypeDef::Alias { header, ann } => {
|
|
header.fmt(buf, indent, Suffix::None)?;
|
|
buf.push_str(" :");
|
|
buf.spaces(1);
|
|
ann.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
TypeDef::Opaque {
|
|
header,
|
|
typ,
|
|
derived,
|
|
} => {
|
|
if true {
|
|
// current new compiler doesn't support opaque types
|
|
// TODO: fix this!
|
|
return Err(MigrateError::OpaqueNotSupported);
|
|
}
|
|
header.fmt(buf, indent, Suffix::None)?;
|
|
buf.push_str(" :=");
|
|
buf.spaces(1);
|
|
typ.fmt(buf, indent, Suffix::None)?;
|
|
if let Some(derived) = derived {
|
|
buf.push_str(" implements");
|
|
buf.spaces(1);
|
|
derived.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
}
|
|
TypeDef::Ability {
|
|
header: _,
|
|
loc_implements: _,
|
|
members: _,
|
|
} => return Err(MigrateError::AbilitiesNotSupported),
|
|
}
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum MigrateError {
|
|
AbilitiesNotSupported,
|
|
WildcardTypeNotSupported,
|
|
MalformedIdentNotSupported,
|
|
MalformedPatternNotSupported,
|
|
MalformedPatternIdentNotSupported,
|
|
MalformedPatternAsExprNotSupported,
|
|
PrecedenceConflictNotSupported,
|
|
OpaqueNotSupported,
|
|
PizzaOpRhsNotSupported,
|
|
}
|
|
|
|
impl Fmt for ValueDef<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
ValueDef::Annotation(pat, ty) => {
|
|
fmt_pattern(buf, indent, &pat.value, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push_str(":");
|
|
buf.spaces(1);
|
|
ty.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
ValueDef::Body(pat, body) => {
|
|
fmt_pattern(buf, indent, &pat.value, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push_str(" =");
|
|
buf.spaces(1);
|
|
body.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
ValueDef::AnnotatedBody {
|
|
ann_pattern,
|
|
ann_type,
|
|
lines_between,
|
|
body_pattern,
|
|
body_expr,
|
|
} => {
|
|
ann_pattern.fmt(buf, indent, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push_str(":");
|
|
buf.spaces(1);
|
|
ann_type.fmt(buf, indent, Suffix::None)?;
|
|
fmt_spaces(buf, lines_between.iter(), indent);
|
|
body_pattern.fmt(buf, indent, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push_str(" =");
|
|
buf.spaces(1);
|
|
body_expr.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
ValueDef::Dbg {
|
|
condition,
|
|
preceding_comment: _,
|
|
} => {
|
|
buf.indent(indent);
|
|
buf.push_str("dbg");
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent, &condition.value, Suffix::None)?;
|
|
}
|
|
ValueDef::Expect {
|
|
condition,
|
|
preceding_comment: _,
|
|
} => {
|
|
buf.indent(indent);
|
|
buf.push_str("expect");
|
|
buf.spaces(1);
|
|
fmt_expr(buf, indent, &condition.value, Suffix::None)?;
|
|
}
|
|
ValueDef::ModuleImport(module_import) => {
|
|
module_import.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
ValueDef::IngestedFileImport(ingested_file_import) => {
|
|
ingested_file_import.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
ValueDef::Stmt(loc) => {
|
|
fmt_expr(buf, indent, &loc.value, suffix)?;
|
|
}
|
|
ValueDef::StmtAfterExpr => todo!(),
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for IngestedFileImport<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
let Self {
|
|
before_path,
|
|
path,
|
|
name,
|
|
annotation,
|
|
} = self;
|
|
|
|
buf.indent(indent);
|
|
buf.push_str("import");
|
|
buf.spaces(1);
|
|
|
|
let indent = indent + 4;
|
|
|
|
if !before_path.is_empty() {
|
|
fmt_spaces(buf, before_path.iter(), indent);
|
|
}
|
|
fmt_str_literal(buf, path.value, indent);
|
|
|
|
name.keyword.fmt(buf, indent, Suffix::None)?;
|
|
buf.indent(indent);
|
|
buf.push_str(name.item.value);
|
|
|
|
if let Some(annotation) = annotation {
|
|
annotation.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for IngestedFileAnnotation<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
if !self.before_colon.is_empty() {
|
|
fmt_spaces(buf, self.before_colon.iter(), indent);
|
|
}
|
|
buf.indent(indent);
|
|
buf.push_str(":");
|
|
buf.spaces(1);
|
|
self.annotation.fmt(buf, indent, Suffix::None)?;
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for ImportedModuleName<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
|
|
if let Some(package_shorthand) = self.package {
|
|
buf.push_str(package_shorthand);
|
|
buf.push_str(".");
|
|
}
|
|
|
|
self.name.fmt(buf, indent, Suffix::None)?;
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<F: Fmt> Fmt for Spaces<'_, F> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
if !self.before.is_empty() {
|
|
fmt_spaces(buf, self.before.iter(), indent);
|
|
}
|
|
self.item.fmt(buf, indent, Suffix::None)?;
|
|
if !self.after.is_empty() {
|
|
fmt_spaces(buf, self.after.iter(), indent);
|
|
}
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<F: Fmt> Fmt for SpacesBefore<'_, F> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
if !self.before.is_empty() {
|
|
fmt_spaces(buf, self.before.iter(), indent);
|
|
}
|
|
self.item.fmt(buf, indent, suffix)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for ExposedName<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
buf.push_str(self.as_str());
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for ModuleName<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
buf.push_str(self.as_str());
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for ModuleImport<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
let Self {
|
|
before_name,
|
|
name,
|
|
params,
|
|
alias,
|
|
exposed,
|
|
} = self;
|
|
|
|
buf.indent(indent);
|
|
buf.push_str("import");
|
|
buf.spaces(1);
|
|
|
|
if !before_name.is_empty() {
|
|
fmt_spaces(buf, before_name.iter(), indent);
|
|
}
|
|
|
|
name.fmt(buf, indent, Suffix::None)?;
|
|
if let Some(params) = params {
|
|
params.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
if let Some(alias) = alias {
|
|
alias.fmt(buf, indent, Suffix::None)?;
|
|
}
|
|
|
|
if let Some(exposed) = exposed {
|
|
buf.spaces(1);
|
|
exposed.keyword.fmt(buf, indent, Suffix::None)?;
|
|
buf.spaces(1);
|
|
fmt_collection(buf, indent, Braces::Square, None, exposed.item, None)?;
|
|
}
|
|
fmt_suffix(buf, indent, suffix);
|
|
buf.ensure_ends_with_newline();
|
|
Ok(())
|
|
}
|
|
}
|
|
macro_rules! impl_fmt_for_keywords {
|
|
($($t:ty),*) => {
|
|
$(
|
|
impl Fmt for $t {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
buf.push_str(<$t as Keyword>::KEYWORD);
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
impl_fmt_for_keywords!(ImportExposingKeyword, ImportAsKeyword);
|
|
|
|
impl Fmt for ImportAlias<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
buf.push_str(self.as_str());
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for ModuleImportParams<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
if !self.before.is_empty() {
|
|
fmt_spaces(buf, self.before.iter(), indent);
|
|
}
|
|
fmt_collection(buf, indent, Braces::Curly, None, self.params.value, None)?;
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<K: Keyword, V: Fmt> Fmt for KeywordItem<'_, K, V> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
buf.push_str(K::KEYWORD);
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for ModuleHeader<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
buf.indent(indent);
|
|
buf.push_str("module");
|
|
|
|
let mut indent = fmt_spaces_with_outdent(buf, self.after_keyword, 0);
|
|
|
|
if let Some(params) = &self.params {
|
|
if is_collection_multiline(¶ms.pattern.value) {
|
|
indent = 4;
|
|
}
|
|
|
|
fmt_collection(buf, indent, Braces::Curly, None, params.pattern.value, None)?;
|
|
|
|
indent = fmt_spaces_with_outdent(buf, params.before_arrow, indent);
|
|
buf.push_str("->");
|
|
indent = fmt_spaces_with_outdent(buf, params.after_arrow, indent);
|
|
}
|
|
|
|
fmt_collection(buf, indent, Braces::Square, None, self.exposes, None)?;
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for AppHeader<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
fmt_app_header(buf, self);
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for PackageHeader<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
fmt_package_header(buf, self);
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for PlatformHeader<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
fmt_platform_header(buf, self);
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for HostedHeader<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
fmt_hosted_header(buf, self);
|
|
fmt_suffix(buf, indent, suffix);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Fmt for Header<'_> {
|
|
fn fmt(&self, buf: &mut Buf, indent: u16, suffix: Suffix) -> Result<(), MigrateError> {
|
|
match self {
|
|
Header::Module(module_header) => module_header.fmt(buf, indent, suffix),
|
|
Header::App(app_header) => app_header.fmt(buf, indent, suffix),
|
|
Header::Package(package_header) => package_header.fmt(buf, indent, suffix),
|
|
Header::Platform(platform_header) => platform_header.fmt(buf, indent, suffix),
|
|
Header::Hosted(hosted_header) => hosted_header.fmt(buf, indent, suffix),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn fmt_header(
|
|
buf: &mut Buf<'_>,
|
|
header: &SpacesBefore<'_, Header<'_>>,
|
|
) -> Result<(), MigrateError> {
|
|
header.fmt(buf, 0, Suffix::None)
|
|
}
|
|
|
|
pub fn fmt_defs(buf: &mut Buf<'_>, defs: &Defs<'_>) -> Result<(), MigrateError> {
|
|
for (index, def) in defs.defs().enumerate() {
|
|
let spaces_before = &defs.spaces[defs.space_before[index].indices()];
|
|
let spaces_after = &defs.spaces[defs.space_after[index].indices()];
|
|
|
|
if !spaces_before.is_empty() {
|
|
fmt_spaces(buf, spaces_before.iter(), 0);
|
|
}
|
|
|
|
match def {
|
|
Ok(type_def) => type_def.fmt(buf, 0, Suffix::None)?,
|
|
Err(value_def) => value_def.fmt(buf, 0, Suffix::None)?,
|
|
}
|
|
|
|
if !spaces_after.is_empty() {
|
|
fmt_spaces(buf, spaces_after.iter(), 0);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn snakify_camel_ident_in_bump<'a>(arena: &'a Bump, string: &str) -> String<'a> {
|
|
let mut buf = Buf::new_in(arena, MigrationFlags::default());
|
|
snakify_camel_ident(&mut buf, string);
|
|
buf.text
|
|
}
|