dprint-plugin-pug 0.1.0

A super minimal Pug formatter plugin for dprint.
Documentation
use crate::ast::{Document, Node, RawTextNode, StatementNode};
use crate::config::Configuration;

pub fn format(document: &Document, config: &Configuration) -> String {
    let mut output = String::new();

    for (index, node) in document.children.iter().enumerate() {
        if index > 0 {
            output.push('\n');
        }
        write_node(
            &mut output,
            node,
            0,
            config.indent_width(),
            config.use_tabs(),
        );
    }

    if !output.ends_with('\n') {
        output.push('\n');
    }

    output
}

fn write_node(output: &mut String, node: &Node, depth: usize, indent_width: usize, use_tabs: bool) {
    match node {
        Node::Statement(statement) => {
            write_statement(output, statement, depth, indent_width, use_tabs)
        }
        Node::Comment(text) => {
            write_indent(output, depth, indent_width, use_tabs);
            output.push_str("// ");
            output.push_str(text.trim());
        }
        Node::Text(text) => {
            write_indent(output, depth, indent_width, use_tabs);
            output.push('|');
            output.push_str(text);
        }
        Node::RawText(text) => write_raw_text(output, text, depth, indent_width, use_tabs),
    }
}

fn write_statement(
    output: &mut String,
    element: &StatementNode,
    depth: usize,
    indent_width: usize,
    use_tabs: bool,
) {
    write_indent(output, depth, indent_width, use_tabs);
    output.push_str(&element.head.to_source());
    if element.is_text_block {
        output.push('.');
    }

    for child in &element.children {
        output.push('\n');
        write_node(output, child, depth + 1, indent_width, use_tabs);
    }
}

fn write_raw_text(
    output: &mut String,
    text: &RawTextNode,
    depth: usize,
    indent_width: usize,
    use_tabs: bool,
) {
    write_indent(output, depth, indent_width, use_tabs);
    for _ in 0..text.extra_indent {
        output.push(' ');
    }
    output.push_str(&text.content);
}

fn write_indent(output: &mut String, depth: usize, indent_width: usize, use_tabs: bool) {
    if depth == 0 {
        return;
    }

    if use_tabs {
        for _ in 0..depth {
            output.push('\t');
        }
        return;
    }

    if indent_width == 0 {
        return;
    }

    for _ in 0..depth {
        for _ in 0..indent_width {
            output.push(' ');
        }
    }
}