<commit_before>use itertools::Itertools;
use pretty::{Arena, DocAllocator, DocBuilder};

use ast::{Expr, is_operator_char, Literal, Pattern, SpannedExpr, SpannedPattern};
use types::{Type, pretty_print as pretty_type};

const INDENT: usize = 4;

pub fn ident<'b>(arena: &'b Arena<'b>, name: &'b str) -> DocBuilder<'b, Arena<'b>> {
    if name.starts_with(is_operator_char) {
        chain![arena; "(", name, ")"]
    } else {
        arena.text(name)
    }
}

fn forced_new_line<Id>(expr: &Expr<Id>) -> bool {
    match *expr {
        Expr::Block(..) |
        Expr::LetBindings(..) |
        Expr::Match(..) |
        Expr::TypeBindings(..) => true,
        _ => false,
    }
}

fn hang<'a, Id>(arena: &'a Arena<'a>, expr: &'a SpannedExpr<Id>) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    let line = if forced_new_line(&expr.value) {
        arena.newline()
    } else {
        arena.space()
    };
    line.append(pretty_expr(arena, &expr.value))
}

pub fn pretty_pattern<'a, Id>(
    arena: &'a Arena<'a>,
    pattern: &'a SpannedPattern<Id>,
) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    match pattern.value {
        Pattern::Constructor(ref ctor, ref args) => {
            chain![arena;
                ctor.as_ref(),
                arena.concat(args.iter().map(|arg| {
                    arena.text(" ").append(pretty_pattern(arena, arg))
                }))
            ]
        }
        Pattern::Ident(ref id) => ident(arena, id.as_ref()),
        Pattern::Record {
            ref fields,
            ref types,
            ..
        } => {
            chain![arena;
                "{",
                arena.concat(types.iter().map(|field| {
                    chain![arena;
                        arena.space(),
                        field.0.as_ref()
                    ]
                }).chain(fields.iter().map(|field| {
                    match field.1 {
                        Some(ref new_name) => {
                            chain![arena;
                                arena.space(),
                                field.0.as_ref(),
                                " = ",
                                pretty_pattern(arena, new_name)
                            ]
                        }
                        None => {
                            chain![arena;
                                arena.space(),
                                field.0.as_ref()
                            ]
                        }
                    }
                })).intersperse(arena.text(","))),
                if types.is_empty() && fields.is_empty() {
                    arena.nil()
                } else {
                    arena.space()
                },
                "}"
            ]
        }
        Pattern::Tuple { ref elems, .. } => {
            chain![arena;
                "(",
                arena.concat(elems
                    .iter()
                    .map(|elem| pretty_pattern(arena, elem))
                    .intersperse(arena.text(",").append(arena.space()))
                ),
                ")"
            ]
        }
        Pattern::Error => arena.text("<error>")
    }
}

pub fn pretty_expr<'a, Id>(arena: &'a Arena<'a>, expr: &'a Expr<Id>) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    let pretty = |expr: &'a SpannedExpr<_>| pretty_expr(arena, &expr.value);
    match *expr {
        Expr::App(ref func, ref args) => {
            pretty(func).append(
                arena.concat(args.iter().map(|arg| arena.text(" ").append(pretty(arg)))),
            )
        }
        Expr::Array(ref array) => {
            arena
                .text("[")
                .append(
                    arena.concat(
                        array
                            .exprs
                            .iter()
                            .map(|elem| pretty(elem))
                            .intersperse(arena.text(",").append(arena.space())),
                    ),
                )
                .append("]")
                .group()
        }
        Expr::Block(ref elems) => {
            arena.concat(
                elems
                    .iter()
                    .map(|elem| pretty(elem).group().append(arena.newline())),
            )
        }
        Expr::Ident(ref id) => ident(arena, id.name.as_ref()),
        Expr::IfElse(ref body, ref if_true, ref if_false) => {
            chain![arena;
                "if",
                pretty(body),
                "then",
                pretty(if_true),
                "else",
                pretty(if_false)
            ]
        }
        Expr::Infix(ref l, ref op, ref r) => {
            chain![arena;
                pretty(l),
                " ",
                op.value.name.as_ref(),
                " ",
                pretty(r)
            ]
        }
        Expr::Lambda(ref lambda) => {
            chain![arena;
                "\\",
                arena.concat(lambda.args.iter().map(|arg| arena.text(arg.name.as_ref()).append(" "))),
                "->",
                hang(arena, &lambda.body)
            ]
        }
        Expr::LetBindings(ref binds, ref body) => {
            chain![arena;
                "let ",
                arena.concat(binds.iter().map(|bind| {
                    chain![arena;
                        chain![arena;
                            pretty_pattern(arena, &bind.name),
                            " ",
                            arena.concat(bind.args.iter().map(|arg| {
                                arena.text(arg.name.as_ref()).append(" ")
                            }))
                        ].group(),
                        match *bind.typ {
                            Type::Hole => arena.nil(),
                            ref typ => arena.text(": ").append(pretty_type(arena, typ)),
                        },
                        "=",
                        hang(arena, &bind.expr).nest(INDENT)
                    ].group().nest(INDENT)
                }).intersperse(arena.newline().append("and "))),
                arena.newline(),
                pretty(body)
            ]
        }
        Expr::Literal(ref literal) => {
            match *literal {
                Literal::Byte(b) => arena.text(format!("b{}", b)),
                Literal::Char(c) => arena.text(format!("{:?}", c)),
                Literal::Float(f) => arena.text(format!("{}", f)),
                Literal::Int(i) => arena.text(format!("{}", i)),
                Literal::String(ref s) => arena.text(format!("{:?}", s)),
            }
        }
        Expr::Match(ref expr, ref alts) => {
            chain![arena;
                "match ",
                pretty(expr),<commit_msg>fix: Avoid double nesting in let expressions<commit_after>use itertools::Itertools;
use pretty::{Arena, DocAllocator, DocBuilder};

use ast::{Expr, is_operator_char, Literal, Pattern, SpannedExpr, SpannedPattern};
use types::{Type, pretty_print as pretty_type};

const INDENT: usize = 4;

pub fn ident<'b>(arena: &'b Arena<'b>, name: &'b str) -> DocBuilder<'b, Arena<'b>> {
    if name.starts_with(is_operator_char) {
        chain![arena; "(", name, ")"]
    } else {
        arena.text(name)
    }
}

fn forced_new_line<Id>(expr: &Expr<Id>) -> bool {
    match *expr {
        Expr::Block(..) |
        Expr::LetBindings(..) |
        Expr::Match(..) |
        Expr::TypeBindings(..) => true,
        _ => false,
    }
}

fn hang<'a, Id>(arena: &'a Arena<'a>, expr: &'a SpannedExpr<Id>) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    let line = if forced_new_line(&expr.value) {
        arena.newline()
    } else {
        arena.space()
    };
    line.append(pretty_expr(arena, &expr.value))
}

pub fn pretty_pattern<'a, Id>(
    arena: &'a Arena<'a>,
    pattern: &'a SpannedPattern<Id>,
) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    match pattern.value {
        Pattern::Constructor(ref ctor, ref args) => {
            chain![arena;
                ctor.as_ref(),
                arena.concat(args.iter().map(|arg| {
                    arena.text(" ").append(pretty_pattern(arena, arg))
                }))
            ]
        }
        Pattern::Ident(ref id) => ident(arena, id.as_ref()),
        Pattern::Record {
            ref fields,
            ref types,
            ..
        } => {
            chain![arena;
                "{",
                arena.concat(types.iter().map(|field| {
                    chain![arena;
                        arena.space(),
                        field.0.as_ref()
                    ]
                }).chain(fields.iter().map(|field| {
                    match field.1 {
                        Some(ref new_name) => {
                            chain![arena;
                                arena.space(),
                                field.0.as_ref(),
                                " = ",
                                pretty_pattern(arena, new_name)
                            ]
                        }
                        None => {
                            chain![arena;
                                arena.space(),
                                field.0.as_ref()
                            ]
                        }
                    }
                })).intersperse(arena.text(","))),
                if types.is_empty() && fields.is_empty() {
                    arena.nil()
                } else {
                    arena.space()
                },
                "}"
            ]
        }
        Pattern::Tuple { ref elems, .. } => {
            chain![arena;
                "(",
                arena.concat(elems
                    .iter()
                    .map(|elem| pretty_pattern(arena, elem))
                    .intersperse(arena.text(",").append(arena.space()))
                ),
                ")"
            ]
        }
        Pattern::Error => arena.text("<error>")
    }
}

pub fn pretty_expr<'a, Id>(arena: &'a Arena<'a>, expr: &'a Expr<Id>) -> DocBuilder<'a, Arena<'a>>
where
    Id: AsRef<str>,
{
    let pretty = |expr: &'a SpannedExpr<_>| pretty_expr(arena, &expr.value);
    match *expr {
        Expr::App(ref func, ref args) => {
            pretty(func).append(
                arena.concat(args.iter().map(|arg| arena.text(" ").append(pretty(arg)))),
            )
        }
        Expr::Array(ref array) => {
            arena
                .text("[")
                .append(
                    arena.concat(
                        array
                            .exprs
                            .iter()
                            .map(|elem| pretty(elem))
                            .intersperse(arena.text(",").append(arena.space())),
                    ),
                )
                .append("]")
                .group()
        }
        Expr::Block(ref elems) => {
            arena.concat(
                elems
                    .iter()
                    .map(|elem| pretty(elem).group().append(arena.newline())),
            )
        }
        Expr::Ident(ref id) => ident(arena, id.name.as_ref()),
        Expr::IfElse(ref body, ref if_true, ref if_false) => {
            chain![arena;
                "if",
                pretty(body),
                "then",
                pretty(if_true),
                "else",
                pretty(if_false)
            ]
        }
        Expr::Infix(ref l, ref op, ref r) => {
            chain![arena;
                pretty(l),
                " ",
                op.value.name.as_ref(),
                " ",
                pretty(r)
            ]
        }
        Expr::Lambda(ref lambda) => {
            chain![arena;
                "\\",
                arena.concat(lambda.args.iter().map(|arg| arena.text(arg.name.as_ref()).append(" "))),
                "->",
                hang(arena, &lambda.body)
            ]
        }
        Expr::LetBindings(ref binds, ref body) => {
            chain![arena;
                "let ",
                arena.concat(binds.iter().map(|bind| {
                    chain![arena;
                        chain![arena;
                            pretty_pattern(arena, &bind.name),
                            " ",
                            arena.concat(bind.args.iter().map(|arg| {
                                arena.text(arg.name.as_ref()).append(" ")
                            }))
                        ].group(),
                        match *bind.typ {
                            Type::Hole => arena.nil(),
                            ref typ => arena.text(": ").append(pretty_type(arena, typ)),
                        },
                        "=",
                        hang(arena, &bind.expr)
                    ].group().nest(INDENT)
                }).intersperse(arena.newline().append("and "))),
                arena.newline(),
                pretty(body)
            ]
        }
        Expr::Literal(ref literal) => {
            match *literal {
                Literal::Byte(b) => arena.text(format!("b{}", b)),
                Literal::Char(c) => arena.text(format!("{:?}", c)),
                Literal::Float(f) => arena.text(format!("{}", f)),
                Literal::Int(i) => arena.text(format!("{}", i)),
                Literal::String(ref s) => arena.text(format!("{:?}", s)),
            }
        }
        Expr::Match(ref expr, ref alts) => {
            chain![arena;
                "match ",
                pretty(expr),
{'fraction_non_alphanumeric': 0.15308386333382637, 'fraction_numerical': 0.0007395355716609969, 'mean_word_length': 1.065841735410938, 'pattern_counts': {'":': 2, '<': 55, '<?xml version=': 0, '>': 119, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2725149', 'n_tokens_mistral': 3877, 'n_tokens_neox': 3742, 'n_words': 581}