use crate::config::{Comma, LanguageOptions, SingleLine};
use apollo_parser::{cst::*, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, S};
use rowan::Direction;
use tiny_pretty::Doc;

pub(super) struct Ctx<'a> {
    pub indent_width: usize,
    pub options: &'a LanguageOptions,
}

pub(super) trait DocGen {
    fn doc(&self, ctx: &Ctx) -> Doc<'static>;
}

impl DocGen for Alias {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(3);
        let mut trivias = vec![];
        if let Some(name) = self.name() {
            docs.push(name.doc(ctx));
            trivias.append(&mut format_trivias_after_node(&name, ctx));
        }

        docs.push(Doc::text(":"));
        docs.append(&mut trivias);
        Doc::list(docs)
    }
}

impl DocGen for Argument {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(4);
        let mut trivias = vec![];
        if let Some(name) = self.name() {
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(colon) = self.colon_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text(":"));
            trivias = format_trivias_after_token(&colon, ctx);
        }
        if let Some(value) = self.value() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(value.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for Arguments {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if is_empty_delimiter(self) {
            Doc::text("()")
        } else {
            DelimitersFormatter::paren(
                self.l_paren_token(),
                self.r_paren_token(),
                ctx.options.arguments_paren_spacing,
                ctx,
            )
            .with_single_line(&ctx.options.arguments_single_line)
            .format(format_optional_comma_separated_list(
                self,
                self.arguments(),
                &ctx.options.arguments_single_line,
                &ctx.options.arguments_comma,
                ctx,
            ))
        }
    }
}

impl DocGen for ArgumentsDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if is_empty_delimiter(self) {
            Doc::text("()")
        } else {
            DelimitersFormatter::paren(
                self.l_paren_token(),
                self.r_paren_token(),
                ctx.options.arguments_definition_paren_spacing,
                ctx,
            )
            .with_single_line(&ctx.options.arguments_definition_single_line)
            .format(format_optional_comma_separated_list(
                self,
                self.input_value_definitions(),
                &ctx.options.arguments_definition_single_line,
                &ctx.options.arguments_definition_comma,
                ctx,
            ))
            .group()
        }
    }
}

impl DocGen for BooleanValue {
    fn doc(&self, _: &Ctx) -> Doc<'static> {
        Doc::text(self.source_string())
    }
}

impl DocGen for DefaultValue {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(3);
        docs.push(Doc::text("="));
        let mut trivias = if let Some(eq) = self.eq_token() {
            format_trivias_after_token(&eq, ctx)
        } else {
            vec![]
        };

        if let Some(default_value) = self.value() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(default_value.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for Definition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        match self {
            Definition::OperationDefinition(node) => node.doc(ctx),
            Definition::FragmentDefinition(node) => node.doc(ctx),
            Definition::DirectiveDefinition(node) => node.doc(ctx),
            Definition::SchemaDefinition(node) => node.doc(ctx),
            Definition::ScalarTypeDefinition(node) => node.doc(ctx),
            Definition::ObjectTypeDefinition(node) => node.doc(ctx),
            Definition::InterfaceTypeDefinition(node) => node.doc(ctx),
            Definition::UnionTypeDefinition(node) => node.doc(ctx),
            Definition::EnumTypeDefinition(node) => node.doc(ctx),
            Definition::InputObjectTypeDefinition(node) => node.doc(ctx),
            Definition::SchemaExtension(node) => node.doc(ctx),
            Definition::ScalarTypeExtension(node) => node.doc(ctx),
            Definition::ObjectTypeExtension(node) => node.doc(ctx),
            Definition::InterfaceTypeExtension(node) => node.doc(ctx),
            Definition::UnionTypeExtension(node) => node.doc(ctx),
            Definition::EnumTypeExtension(node) => node.doc(ctx),
            Definition::InputObjectTypeExtension(node) => node.doc(ctx),
        }
    }
}

impl DocGen for Description {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if let Some(string) = self.string_value() {
            string.doc(ctx)
        } else {
            Doc::nil()
        }
    }
}

impl DocGen for Directive {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(4);
        docs.push(Doc::text("@"));
        if let Some(at) = self.at_token() {
            docs.append(&mut format_trivias_after_token(&at, ctx));
        }
        if let Some(name) = self.name() {
            docs.push(name.doc(ctx));
            docs.append(&mut format_trivias_after_node(&name, ctx));
        }
        if let Some(arguments) = self.arguments() {
            docs.push(arguments.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for DirectiveDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(directive) = self.directive_token() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("directive"));
            trivias = format_trivias_after_token(&directive, ctx);
        }
        if self.at_token().is_some() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(Doc::text("@"));
        }
        if let Some(name) = self.name() {
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(arguments_def) = self.arguments_definition() {
            docs.append(&mut trivias);
            docs.push(arguments_def.doc(ctx));
            trivias = format_trivias_after_node(&arguments_def, ctx);
        }
        if let Some(repeatable) = self.repeatable_token() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(Doc::text("repeatable"));
            trivias = format_trivias_after_token(&repeatable, ctx);
        }
        if let Some(on) = self.on_token() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(Doc::text("on"));
            trivias = format_trivias_after_token(&on, ctx);
        }
        if let Some(directive_locations) = self.directive_locations() {
            if trivias.is_empty() {
                docs.push(
                    Doc::line_or_space()
                        .append(directive_locations.doc(ctx))
                        .group()
                        .nest(ctx.indent_width),
                );
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directive_locations.doc(ctx).nest(ctx.indent_width));
            }
        }

        Doc::list(docs)
    }
}

impl DocGen for DirectiveLocation {
    fn doc(&self, _: &Ctx) -> Doc<'static> {
        Doc::text(self.source_string())
    }
}

impl DocGen for DirectiveLocations {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        format_union_like(
            self,
            self.directive_locations(),
            S![|],
            "|",
            &ctx.options.directive_locations_single_line,
            ctx,
        )
        .group()
    }
}

impl DocGen for Directives {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        format_optional_comma_separated_list(
            self,
            self.directives(),
            &ctx.options.directives_single_line,
            &ctx.options.directives_comma,
            ctx,
        )
    }
}

impl DocGen for Document {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(2);

        let mut children = self.syntax().children_with_tokens().peekable();
        let mut prev_kind = SyntaxKind::WHITESPACE;
        while let Some(element) = children.next() {
            let kind = element.kind();
            match element {
                SyntaxElement::Node(node) => {
                    if should_ignore(&node, ctx) {
                        reflow(&node.to_string(), &mut docs);
                    } else if let Some(item) = Definition::cast(node) {
                        docs.push(item.doc(ctx));
                    }
                }
                SyntaxElement::Token(token) => match token.kind() {
                    SyntaxKind::COMMENT => {
                        docs.push(format_comment(token.to_string(), ctx));
                    }
                    SyntaxKind::WHITESPACE => {
                        if token.index() > 0 && children.peek().is_some() {
                            match token.text().chars().filter(|c| *c == '\n').count() {
                                0 => {
                                    if prev_kind == SyntaxKind::COMMENT {
                                        docs.push(Doc::hard_line());
                                    }
                                }
                                1 => {
                                    docs.push(Doc::hard_line());
                                }
                                _ => {
                                    docs.push(Doc::empty_line());
                                    docs.push(Doc::hard_line());
                                }
                            }
                        }
                    }
                    _ => {}
                },
            }
            prev_kind = kind;
        }

        docs.push(Doc::hard_line());
        Doc::list(docs)
    }
}

impl DocGen for EnumValue {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if let Some(name) = self.name() {
            name.doc(ctx)
        } else {
            Doc::nil()
        }
    }
}

impl DocGen for EnumTypeDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(r#enum) = self.enum_token() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("enum"));
            trivias = format_trivias_after_token(&r#enum, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(enum_values_def) = self.enum_values_definition() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(enum_values_def.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for EnumTypeExtension {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(extend) = self.extend_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text("extend"));
            trivias = format_trivias_after_token(&extend, ctx);
        }
        if let Some(r#enum) = self.enum_token() {
            if !docs.is_empty() {
                docs.push(Doc::space());
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("enum"));
            trivias = format_trivias_after_token(&r#enum, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(enum_values_def) = self.enum_values_definition() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(enum_values_def.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for EnumValueDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(enum_value) = self.enum_value() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(enum_value.doc(ctx));
            trivias = format_trivias_after_node(&enum_value, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
        }

        Doc::list(docs)
    }
}

impl DocGen for EnumValuesDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if is_empty_delimiter(self) {
            Doc::text("{}")
        } else {
            DelimitersFormatter::brace(
                self.l_curly_token(),
                self.r_curly_token(),
                ctx.options.enum_values_definition_brace_spacing,
                ctx,
            )
            .with_single_line(&ctx.options.enum_values_definition_single_line)
            .format(format_optional_comma_separated_list(
                self,
                self.enum_value_definitions(),
                &ctx.options.enum_values_definition_single_line,
                &ctx.options.enum_values_definition_comma,
                ctx,
            ))
        }
    }
}

impl DocGen for Field {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(alias) = self.alias() {
            docs.push(alias.doc(ctx));
            trivias = format_trivias_after_node(&alias, ctx);
        }
        if let Some(name) = self.name() {
            if !docs.is_empty() {
                docs.push(Doc::space());
            }
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(arguments) = self.arguments() {
            docs.append(&mut trivias);
            docs.push(arguments.doc(ctx));
            trivias = format_trivias_after_node(&arguments, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(
                    Doc::line_or_space()
                        .append(directives.doc(ctx))
                        .group()
                        .nest(ctx.indent_width),
                );
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group().nest(ctx.indent_width));
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(selection_set) = self.selection_set() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(selection_set.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for FieldDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(name) = self.name() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(arguments_def) = self.arguments_definition() {
            docs.append(&mut trivias);
            docs.push(arguments_def.doc(ctx));
            trivias = format_trivias_after_node(&arguments_def, ctx);
        }
        if let Some(colon) = self.colon_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text(":"));
            trivias = format_trivias_after_token(&colon, ctx);
        }
        if let Some(ty) = self.ty() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(ty.doc(ctx));
            trivias = format_trivias_after_node(&ty, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
        }

        Doc::list(docs)
    }
}

impl DocGen for FieldsDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if is_empty_delimiter(self) {
            Doc::text("{}")
        } else {
            DelimitersFormatter::brace(
                self.l_curly_token(),
                self.r_curly_token(),
                ctx.options.fields_definition_brace_spacing,
                ctx,
            )
            .with_single_line(&ctx.options.fields_definition_single_line)
            .format(format_optional_comma_separated_list(
                self,
                self.field_definitions(),
                &ctx.options.fields_definition_single_line,
                &ctx.options.fields_definition_comma,
                ctx,
            ))
        }
    }
}

impl DocGen for FloatValue {
    fn doc(&self, _: &Ctx) -> Doc<'static> {
        Doc::text(self.source_string())
    }
}

impl DocGen for FragmentDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(fragment) = self.fragment_token() {
            docs.push(Doc::text("fragment"));
            trivias = format_trivias_after_token(&fragment, ctx);
        }
        if let Some(fragment_name) = self.fragment_name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(fragment_name.doc(ctx));
            trivias = format_trivias_after_node(&fragment_name, ctx);
        }
        if let Some(type_condition) = self.type_condition() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(type_condition.doc(ctx));
            trivias = format_trivias_after_node(&type_condition, ctx);
        }
        if let Some(directives) = self.directives() {
            if !trivias.is_empty() {
                docs.append(&mut trivias);
            }
            docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(selection_set) = self.selection_set() {
            if trivias.is_empty() && !docs.is_empty() {
                docs.push(Doc::space());
            } else {
                docs.append(&mut trivias);
            }
            docs.push(selection_set.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for FragmentName {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if let Some(name) = self.name() {
            name.doc(ctx)
        } else {
            Doc::nil()
        }
    }
}

impl DocGen for FragmentSpread {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(dotdotdot) = self.dotdotdot_token() {
            docs.push(Doc::text("..."));
            trivias = format_trivias_after_token(&dotdotdot, ctx);
        }
        if let Some(fragment_name) = self.fragment_name() {
            docs.append(&mut trivias);
            docs.push(fragment_name.doc(ctx));
            trivias = format_trivias_after_node(&fragment_name, ctx);
        }
        if let Some(directives) = self.directives() {
            if !trivias.is_empty() {
                docs.append(&mut trivias);
            }
            docs.push(
                Doc::line_or_space()
                    .append(directives.doc(ctx))
                    .group()
                    .nest(ctx.indent_width),
            );
        }

        Doc::list(docs)
    }
}

impl DocGen for ImplementsInterfaces {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(3);
        let mut trivias = vec![];
        if let Some(implements) = self.implements_token() {
            docs.push(Doc::text("implements"));
            trivias = format_trivias_after_token(&implements, ctx);
        }
        if self.named_types().count() > 0 {
            let types_doc = format_union_like(
                self,
                self.named_types(),
                S![&],
                "&",
                &ctx.options.implements_interfaces_single_line,
                ctx,
            );
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(types_doc).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(types_doc.group());
            }
        }

        Doc::list(docs)
    }
}

impl DocGen for InlineFragment {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];

        docs.push(Doc::text("..."));
        if let Some(dotdotdot) = self.dotdotdot_token() {
            trivias = format_trivias_after_token(&dotdotdot, ctx);
        }
        if let Some(type_condition) = self.type_condition() {
            if trivias.is_empty() {
                docs.push(Doc::space());
            } else {
                docs.append(&mut trivias);
            }
            docs.push(type_condition.doc(ctx));
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(
                    Doc::line_or_space()
                        .append(directives.doc(ctx))
                        .group()
                        .nest(ctx.indent_width),
                );
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group().nest(ctx.indent_width));
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(selection_set) = self.selection_set() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(selection_set.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for InputFieldsDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if is_empty_delimiter(self) {
            Doc::text("{}")
        } else {
            DelimitersFormatter::brace(
                self.l_curly_token(),
                self.r_curly_token(),
                ctx.options.input_fields_definition_brace_spacing,
                ctx,
            )
            .with_single_line(&ctx.options.input_fields_definition_single_line)
            .format(format_optional_comma_separated_list(
                self,
                self.input_value_definitions(),
                &ctx.options.input_fields_definition_single_line,
                &ctx.options.input_fields_definition_comma,
                ctx,
            ))
        }
    }
}

impl DocGen for InputObjectTypeDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(input) = self.input_token() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("input"));
            trivias = format_trivias_after_token(&input, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(input_fields_def) = self.input_fields_definition() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(input_fields_def.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for InputObjectTypeExtension {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(extend) = self.extend_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text("extend"));
            trivias = format_trivias_after_token(&extend, ctx);
        }
        if let Some(input) = self.input_token() {
            if !docs.is_empty() {
                docs.push(Doc::space());
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("input"));
            trivias = format_trivias_after_token(&input, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(input_fields_def) = self.input_fields_definition() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(input_fields_def.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for InputValueDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(name) = self.name() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(colon) = self.colon_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text(":"));
            trivias = format_trivias_after_token(&colon, ctx);
        }
        if let Some(ty) = self.ty() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(ty.doc(ctx));
            trivias = format_trivias_after_node(&ty, ctx);
        }
        if let Some(default_value) = self.default_value() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(default_value.doc(ctx));
            trivias = format_trivias_after_node(&default_value, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
        }

        Doc::list(docs)
    }
}

impl DocGen for InterfaceTypeDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(interface) = self.interface_token() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("interface"));
            trivias = format_trivias_after_token(&interface, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(interfaces) = self.implements_interfaces() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(interfaces.doc(ctx));
            trivias = format_trivias_after_node(&interfaces, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(fields_def) = self.fields_definition() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(fields_def.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for InterfaceTypeExtension {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(extend) = self.extend_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text("extend"));
            trivias = format_trivias_after_token(&extend, ctx);
        }
        if let Some(interface) = self.interface_token() {
            if !docs.is_empty() {
                docs.push(Doc::space());
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("interface"));
            trivias = format_trivias_after_token(&interface, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(interfaces) = self.implements_interfaces() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(interfaces.doc(ctx));
            trivias = format_trivias_after_node(&interfaces, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(fields_def) = self.fields_definition() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(fields_def.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for IntValue {
    fn doc(&self, _: &Ctx) -> Doc<'static> {
        Doc::text(self.source_string())
    }
}

impl DocGen for ListType {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        DelimitersFormatter::bracket(self.l_brack_token(), self.r_brack_token(), Some(true), ctx)
            .with_single_line(&SingleLine::Prefer)
            .with_space(Doc::nil())
            .format(self.ty().map(|ty| ty.doc(ctx)).unwrap_or_else(Doc::nil))
    }
}

impl DocGen for ListValue {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if is_empty_delimiter(self) {
            Doc::text("[]")
        } else {
            DelimitersFormatter::bracket(
                self.l_brack_token(),
                self.r_brack_token(),
                Some(ctx.options.bracket_spacing),
                ctx,
            )
            .with_single_line(&ctx.options.list_value_single_line)
            .format(format_optional_comma_separated_list(
                self,
                self.values(),
                &ctx.options.list_value_single_line,
                &ctx.options.list_value_comma,
                ctx,
            ))
        }
    }
}

impl DocGen for Name {
    fn doc(&self, _: &Ctx) -> Doc<'static> {
        Doc::text(self.text().to_string())
    }
}

impl DocGen for NamedType {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if let Some(name) = self.name() {
            name.doc(ctx)
        } else {
            Doc::nil()
        }
    }
}

impl DocGen for NonNullType {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(3);
        let mut trivias = vec![];
        if let Some(named_type) = self.named_type() {
            docs.push(named_type.doc(ctx));
            trivias.append(&mut format_trivias_after_node(&named_type, ctx));
        } else if let Some(list_type) = self.list_type() {
            docs.push(list_type.doc(ctx));
        }

        docs.push(Doc::text("!"));
        docs.append(&mut trivias);
        Doc::list(docs)
    }
}

impl DocGen for NullValue {
    fn doc(&self, _: &Ctx) -> Doc<'static> {
        Doc::text("null")
    }
}

impl DocGen for ObjectField {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(4);
        let mut trivias = vec![];
        if let Some(name) = self.name() {
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(colon) = self.colon_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text(":"));
            trivias = format_trivias_after_token(&colon, ctx);
        }
        if let Some(value) = self.value() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(value.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for ObjectTypeDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        let mut has_last_comments_in_implements = false;
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(r#type) = self.type_token() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("type"));
            trivias = format_trivias_after_token(&r#type, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(interfaces) = self.implements_interfaces() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(interfaces.doc(ctx));
            has_last_comments_in_implements =
                check_last_comments_in_implements_interfaces(&interfaces);
            trivias = format_trivias_after_node(&interfaces, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                if !has_last_comments_in_implements || !trivias.is_empty() {
                    docs.push(Doc::space());
                }
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(fields_def) = self.fields_definition() {
            if !has_last_comments_in_implements || !trivias.is_empty() {
                docs.push(Doc::space());
            }
            docs.append(&mut trivias);
            docs.push(fields_def.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for ObjectTypeExtension {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        let mut has_last_comments_in_implements = false;
        if let Some(extend) = self.extend_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text("extend"));
            trivias = format_trivias_after_token(&extend, ctx);
        }
        if let Some(r#type) = self.type_token() {
            if !docs.is_empty() {
                docs.push(Doc::space());
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("type"));
            trivias = format_trivias_after_token(&r#type, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(interfaces) = self.implements_interfaces() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(interfaces.doc(ctx));
            has_last_comments_in_implements =
                check_last_comments_in_implements_interfaces(&interfaces);
            trivias = format_trivias_after_node(&interfaces, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                if !has_last_comments_in_implements || !trivias.is_empty() {
                    docs.push(Doc::space());
                }
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(fields_def) = self.fields_definition() {
            if !has_last_comments_in_implements || !trivias.is_empty() {
                docs.push(Doc::space());
            }
            docs.append(&mut trivias);
            docs.push(fields_def.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for ObjectValue {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if is_empty_delimiter(self) {
            Doc::text("{}")
        } else {
            DelimitersFormatter::brace(
                self.l_curly_token(),
                self.r_curly_token(),
                ctx.options.object_value_brace_spacing,
                ctx,
            )
            .with_single_line(&ctx.options.object_value_single_line)
            .format(format_optional_comma_separated_list(
                self,
                self.object_fields(),
                &ctx.options.object_value_single_line,
                &ctx.options.object_value_comma,
                ctx,
            ))
        }
    }
}

impl DocGen for OperationDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(operation_type) = self.operation_type() {
            docs.push(operation_type.doc(ctx));
            trivias = format_trivias_after_node(&operation_type, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(variable_defs) = self.variable_definitions() {
            docs.append(&mut trivias);
            docs.push(variable_defs.doc(ctx));
            trivias = format_trivias_after_node(&variable_defs, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(selection_set) = self.selection_set() {
            if !docs.is_empty() {
                docs.push(Doc::space());
            }
            docs.append(&mut trivias);
            docs.push(selection_set.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for OperationType {
    fn doc(&self, _: &Ctx) -> Doc<'static> {
        if self.query_token().is_some() {
            Doc::text("query")
        } else if self.mutation_token().is_some() {
            Doc::text("mutation")
        } else if self.subscription_token().is_some() {
            Doc::text("subscription")
        } else {
            Doc::nil()
        }
    }
}

impl DocGen for RootOperationTypeDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(6);
        let mut trivias = vec![];
        if let Some(operation_type) = self.operation_type() {
            docs.push(operation_type.doc(ctx));
            trivias = format_trivias_after_node(&operation_type, ctx);
        }
        if let Some(colon) = self.colon_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text(":"));
            trivias = format_trivias_after_token(&colon, ctx);
        }
        if let Some(named_type) = self.named_type() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(named_type.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for ScalarTypeDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(scalar) = self.scalar_token() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("scalar"));
            trivias = format_trivias_after_token(&scalar, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
        }

        Doc::list(docs)
    }
}

impl DocGen for ScalarTypeExtension {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(extend) = self.extend_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text("extend"));
            trivias = format_trivias_after_token(&extend, ctx);
        }
        if let Some(scalar) = self.scalar_token() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(Doc::text("scalar"));
            trivias = format_trivias_after_token(&scalar, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
        }

        Doc::list(docs)
    }
}

impl DocGen for SchemaDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(schema) = self.schema_token() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("schema"));
            trivias = format_trivias_after_token(&schema, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(l_curly) = self.l_curly_token() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            let is_empty = l_curly
                .siblings_with_tokens(Direction::Next)
                .all(|element| {
                    element.kind() != SyntaxKind::WHITESPACE
                        && matches!(element, SyntaxElement::Token(..))
                });
            docs.push(if is_empty {
                Doc::text("{}")
            } else {
                DelimitersFormatter::brace(
                    self.l_curly_token(),
                    self.r_curly_token(),
                    ctx.options.schema_definition_brace_spacing,
                    ctx,
                )
                .with_single_line(&ctx.options.schema_definition_single_line)
                .format(format_optional_comma_separated_list(
                    self,
                    self.root_operation_type_definitions(),
                    &ctx.options.schema_definition_single_line,
                    &ctx.options.schema_definition_comma,
                    ctx,
                ))
            });
        }

        Doc::list(docs)
    }
}

impl DocGen for SchemaExtension {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(extend) = self.extend_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text("extend"));
            trivias = format_trivias_after_token(&extend, ctx);
        }
        if let Some(schema) = self.schema_token() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(Doc::text("schema"));
            trivias = format_trivias_after_token(&schema, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(l_curly) = self.l_curly_token() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            let is_empty = l_curly
                .siblings_with_tokens(Direction::Next)
                .all(|element| {
                    element.kind() != SyntaxKind::WHITESPACE
                        && matches!(element, SyntaxElement::Token(..))
                });
            docs.push(if is_empty {
                Doc::text("{}")
            } else {
                DelimitersFormatter::brace(
                    self.l_curly_token(),
                    self.r_curly_token(),
                    ctx.options.schema_extension_brace_spacing,
                    ctx,
                )
                .with_single_line(&ctx.options.schema_extension_single_line)
                .format(format_optional_comma_separated_list(
                    self,
                    self.root_operation_type_definitions(),
                    &ctx.options.schema_extension_single_line,
                    &ctx.options.schema_extension_comma,
                    ctx,
                ))
            });
        }

        Doc::list(docs)
    }
}

impl DocGen for Selection {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        match self {
            Selection::Field(node) => node.doc(ctx),
            Selection::FragmentSpread(node) => node.doc(ctx),
            Selection::InlineFragment(node) => node.doc(ctx),
        }
    }
}

impl DocGen for SelectionSet {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        DelimitersFormatter::brace(
            self.l_curly_token(),
            self.r_curly_token(),
            ctx.options.selection_set_brace_spacing,
            ctx,
        )
        .with_single_line(&ctx.options.selection_set_single_line)
        .format(format_optional_comma_separated_list(
            self,
            self.selections(),
            &ctx.options.selection_set_single_line,
            &ctx.options.selection_set_comma,
            ctx,
        ))
    }
}

impl DocGen for StringValue {
    fn doc(&self, _: &Ctx) -> Doc<'static> {
        let s = self.source_string();
        if let Some(s) = s
            .strip_prefix("\"\"\"")
            .and_then(|s| s.strip_suffix("\"\"\""))
            .filter(|s| s.contains(['\n', '\r']))
        {
            Doc::text("\"\"\"")
                .concat(reflow_with_indent(s).into_iter())
                .append(Doc::text("\"\"\""))
        } else {
            Doc::text(s)
        }
    }
}

impl DocGen for Type {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        match self {
            Type::NamedType(node) => node.doc(ctx),
            Type::ListType(node) => node.doc(ctx),
            Type::NonNullType(node) => node.doc(ctx),
        }
    }
}

impl DocGen for TypeCondition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(3);
        let mut trivias = vec![];
        docs.push(Doc::text("on"));
        if let Some(on) = self.on_token() {
            trivias = format_trivias_after_token(&on, ctx)
        }
        if let Some(named_type) = self.named_type() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(named_type.doc(ctx));
        }

        Doc::list(docs)
    }
}

impl DocGen for UnionMemberTypes {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(3);
        let mut trivias = vec![];
        if let Some(eq) = self.eq_token() {
            docs.push(Doc::text("="));
            trivias = format_trivias_after_token(&eq, ctx);
        }
        if self.named_types().count() > 0 {
            let types_doc = format_union_like(
                self,
                self.named_types(),
                S![|],
                "|",
                &ctx.options.union_member_types_single_line,
                ctx,
            );
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(types_doc).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(types_doc.group());
            }
        }

        Doc::list(docs)
    }
}

impl DocGen for UnionTypeDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(description) = self.description() {
            docs.push(description.doc(ctx));
            trivias = format_trivias_after_node(&description, ctx);
        }
        if let Some(union) = self.union_token() {
            if !docs.is_empty() {
                if self
                    .description()
                    .is_some_and(|description| description.source_string().ends_with("\"\"\""))
                {
                    docs.push(Doc::hard_line());
                } else {
                    docs.push(Doc::soft_line());
                }
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("union"));
            trivias = format_trivias_after_token(&union, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(union_member_types) = self.union_member_types() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(union_member_types.doc(ctx).nest(ctx.indent_width));
        }

        Doc::list(docs)
    }
}

impl DocGen for UnionTypeExtension {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(5);
        let mut trivias = vec![];
        if let Some(extend) = self.extend_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text("extend"));
            trivias = format_trivias_after_token(&extend, ctx);
        }
        if let Some(union) = self.union_token() {
            if !docs.is_empty() {
                docs.push(Doc::space());
            }
            docs.append(&mut trivias);
            docs.push(Doc::text("union"));
            trivias = format_trivias_after_token(&union, ctx);
        }
        if let Some(name) = self.name() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(name.doc(ctx));
            trivias = format_trivias_after_node(&name, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
            trivias = format_trivias_after_node(&directives, ctx);
        }
        if let Some(union_member_types) = self.union_member_types() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(union_member_types.doc(ctx).nest(ctx.indent_width));
        }

        Doc::list(docs)
    }
}

impl DocGen for Value {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        match self {
            Value::Variable(node) => node.doc(ctx),
            Value::StringValue(node) => node.doc(ctx),
            Value::FloatValue(node) => node.doc(ctx),
            Value::IntValue(node) => node.doc(ctx),
            Value::BooleanValue(node) => node.doc(ctx),
            Value::NullValue(node) => node.doc(ctx),
            Value::EnumValue(node) => node.doc(ctx),
            Value::ListValue(node) => node.doc(ctx),
            Value::ObjectValue(node) => node.doc(ctx),
        }
    }
}

impl DocGen for Variable {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        Doc::text("$").append(
            self.name()
                .map(|name| name.doc(ctx))
                .unwrap_or_else(Doc::nil),
        )
    }
}

impl DocGen for VariableDefinition {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        let mut docs = Vec::with_capacity(6);
        let mut trivias = vec![];
        if let Some(var) = self.variable() {
            docs.push(var.doc(ctx));
            trivias = format_trivias_after_node(&var, ctx);
        }
        if let Some(colon) = self.colon_token() {
            docs.append(&mut trivias);
            docs.push(Doc::text(":"));
            trivias = format_trivias_after_token(&colon, ctx);
        }
        if let Some(ty) = self.ty() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(ty.doc(ctx));
            trivias = format_trivias_after_node(&ty, ctx);
        }
        if let Some(default_value) = self.default_value() {
            docs.push(Doc::space());
            docs.append(&mut trivias);
            docs.push(default_value.doc(ctx));
            trivias = format_trivias_after_node(&default_value, ctx);
        }
        if let Some(directives) = self.directives() {
            if trivias.is_empty() {
                docs.push(Doc::line_or_space().append(directives.doc(ctx)).group());
            } else {
                docs.push(Doc::space());
                docs.append(&mut trivias);
                docs.push(directives.doc(ctx).group());
            }
        }

        Doc::list(docs)
    }
}

impl DocGen for VariableDefinitions {
    fn doc(&self, ctx: &Ctx) -> Doc<'static> {
        if is_empty_delimiter(self) {
            Doc::text("()")
        } else {
            DelimitersFormatter::paren(
                self.l_paren_token(),
                self.r_paren_token(),
                ctx.options.variable_definitions_paren_spacing,
                ctx,
            )
            .with_single_line(&ctx.options.variable_definitions_single_line)
            .format(format_optional_comma_separated_list(
                self,
                self.variable_definitions(),
                &ctx.options.variable_definitions_single_line,
                &ctx.options.variable_definitions_comma,
                ctx,
            ))
        }
    }
}

fn format_optional_comma_separated_list<N, Entry>(
    node: &N,
    entries: CstChildren<Entry>,
    single_line: &SingleLine,
    comma: &Comma,
    ctx: &Ctx,
) -> Doc<'static>
where
    N: CstNode,
    Entry: CstNode + DocGen,
{
    let node = node.syntax();
    let mut docs = vec![];
    let mut entries = entries.peekable();
    let single_line = if let SingleLine::Inherit = single_line {
        &ctx.options.single_line
    } else {
        single_line
    };
    let separator_space = match single_line {
        SingleLine::Prefer => Doc::line_or_space(),
        SingleLine::Smart | SingleLine::Inherit => {
            if let Some(token) = node.first_token() {
                if token
                    .siblings_with_tokens(Direction::Next)
                    .skip(1)
                    .map_while(|element| element.into_token())
                    .any(|token| {
                        token.kind() == SyntaxKind::WHITESPACE
                            && token.text().contains(['\n', '\r'])
                    })
                {
                    Doc::hard_line()
                } else {
                    Doc::line_or_space()
                }
            } else if node
                .first_child()
                .into_iter()
                .flat_map(|node| node.siblings_with_tokens(Direction::Next))
                .skip(1)
                .map_while(|element| element.into_token())
                .any(|token| {
                    token.kind() == SyntaxKind::WHITESPACE && token.text().contains(['\n', '\r'])
                })
            {
                Doc::hard_line()
            } else {
                Doc::line_or_space()
            }
        }
        SingleLine::Never => Doc::hard_line(),
    };

    let mut commas = node
        .children_with_tokens()
        .filter_map(|element| match element {
            SyntaxElement::Token(token) if token.kind() == S![,] => Some(token),
            _ => None,
        });
    let comma = if let Comma::Inherit = comma {
        &ctx.options.comma
    } else {
        comma
    };
    while let Some(entry) = entries.next() {
        let entry_node = entry.syntax();
        if should_ignore(entry_node, ctx) {
            reflow(&entry_node.to_string(), &mut docs);
        } else {
            docs.push(entry.doc(ctx));
        }
        match comma {
            Comma::Always => {
                if entries.peek().is_some() {
                    docs.push(Doc::text(","));
                } else {
                    docs.push(Doc::flat_or_break(Doc::nil(), Doc::text(",")));
                }
            }
            Comma::Never => {}
            Comma::NoTrailing => {
                if entries.peek().is_some() {
                    docs.push(Doc::text(","));
                }
            }
            Comma::OnlySingleLine | Comma::Inherit => {
                if entries.peek().is_some() {
                    docs.push(Doc::flat_or_break(Doc::text(","), Doc::nil()));
                }
            }
        }

        let comma = commas.next();
        let mut has_comment_before_comma = false;
        let mut has_last_line_break = false;
        if let Some(next) = entries.peek() {
            let last_ws_index = comma
                .as_ref()
                .and_then(|comma| comma.prev_token())
                .or_else(|| {
                    next.syntax()
                        .prev_sibling_or_token()
                        .and_then(|element| element.into_token())
                })
                .filter(|token| {
                    if token.kind() == SyntaxKind::WHITESPACE {
                        has_last_line_break =
                            token.text().chars().filter(|c| *c == '\n').count() > 1;
                        !has_last_line_break
                    } else {
                        false
                    }
                })
                .map(|token| token.index());
            let mut trivia_docs = format_trivias(
                entry
                    .syntax()
                    .siblings_with_tokens(Direction::Next)
                    .filter(|token| {
                        last_ws_index
                            .map(|index| token.index() != index)
                            .unwrap_or(true)
                    }),
                &mut has_comment_before_comma,
                false,
                ctx,
            );
            docs.append(&mut trivia_docs);
            if has_comment_before_comma && !has_last_line_break {
                docs.push(Doc::hard_line());
            }
        }

        if let Some(comma) = &comma {
            if entries.peek().is_some() {
                let mut trivia_docs = format_trivias(
                    comma.siblings_with_tokens(Direction::Next),
                    &mut has_comment_before_comma,
                    false,
                    ctx,
                );
                if trivia_docs.is_empty() {
                    docs.push(separator_space.clone());
                } else {
                    docs.append(&mut trivia_docs);
                }
            } else {
                let last_ws_index = node
                    .last_token()
                    .into_iter()
                    .flat_map(|token| token.siblings_with_tokens(Direction::Prev))
                    .find_map(|element| match element {
                        SyntaxElement::Token(token) if token.kind() == SyntaxKind::WHITESPACE => {
                            Some(token.index())
                        }
                        _ => None,
                    });
                let mut trivia_docs = format_trivias(
                    entry
                        .syntax()
                        .siblings_with_tokens(Direction::Next)
                        .filter(|token| {
                            last_ws_index
                                .map(|index| token.index() != index)
                                .unwrap_or(true)
                        }),
                    &mut has_comment_before_comma,
                    false,
                    ctx,
                );
                if !trivia_docs.is_empty() {
                    docs.append(&mut trivia_docs);
                }
            }
        } else if entries.peek().is_some() && !has_comment_before_comma && !has_last_line_break {
            docs.push(separator_space.clone());
        }
    }
    Doc::list(docs)
}

fn format_union_like<N, Entry>(
    node: &N,
    entries: CstChildren<Entry>,
    sep_token_kind: SyntaxKind,
    sep_text: &'static str,
    single_line: &SingleLine,
    ctx: &Ctx,
) -> Doc<'static>
where
    N: CstNode,
    Entry: CstNode + DocGen,
{
    let node = node.syntax();
    let mut entries = entries.peekable();
    let sep_tokens = node
        .children_with_tokens()
        .filter_map(|element| match element {
            SyntaxElement::Token(token) if token.kind() == sep_token_kind => Some(token),
            _ => None,
        });
    let mut docs = Vec::with_capacity(4);

    let mut has_leading_sep_token = true;
    let mut has_line_break_after_first = false;
    if node
        .first_token()
        .is_some_and(|token| token.kind() != sep_token_kind)
    {
        has_leading_sep_token = false;
        if let Some(first) = entries.next() {
            docs.push(Doc::flat_or_break(
                Doc::nil(),
                Doc::text(sep_text).append(Doc::space()),
            ));
            docs.push(first.doc(ctx));
            has_line_break_after_first = first
                .syntax()
                .siblings_with_tokens(Direction::Next)
                .skip(1)
                .map_while(|element| element.into_token())
                .any(|token| {
                    token.kind() == SyntaxKind::WHITESPACE && token.text().contains(['\n', '\r'])
                });
            if entries.peek().is_some() {
                let mut trivias = format_trivias_after_node(&first, ctx);
                if trivias.is_empty() {
                    docs.push(Doc::line_or_space());
                } else {
                    docs.push(Doc::space());
                    docs.append(&mut trivias);
                }
            }
        }
    } else {
        has_line_break_after_first = entries
            .peek()
            .into_iter()
            .flat_map(|entry| entry.syntax().siblings_with_tokens(Direction::Next))
            .skip(1)
            .map_while(|element| element.into_token())
            .any(|token| {
                token.kind() == SyntaxKind::WHITESPACE && token.text().contains(['\n', '\r'])
            });
    }

    let single_line = if let SingleLine::Inherit = single_line {
        &ctx.options.single_line
    } else {
        single_line
    };
    let space = match single_line {
        SingleLine::Prefer => Doc::line_or_space(),
        SingleLine::Smart | SingleLine::Inherit => {
            if has_line_break_after_first {
                Doc::hard_line()
            } else {
                Doc::line_or_space()
            }
        }
        SingleLine::Never => Doc::hard_line(),
    };

    let mut it = entries.zip(sep_tokens).enumerate().peekable();
    while let Some((i, (entry, sep_token))) = it.next() {
        if i == 0 && has_leading_sep_token {
            docs.push(Doc::flat_or_break(
                Doc::nil(),
                Doc::text(sep_text).append(Doc::space()),
            ));
        } else {
            docs.push(Doc::text(sep_text).append(Doc::space()));
        }
        docs.push(entry.doc(ctx));
        let mut trivias_after_sep_token = format_trivias_after_token(&sep_token, ctx);
        let mut trivias_after_node = format_trivias_after_node(&entry, ctx);
        if trivias_after_sep_token.is_empty() && trivias_after_node.is_empty() {
            if it.peek().is_some() {
                docs.push(space.clone());
            }
        } else {
            docs.push(Doc::space());
            docs.append(&mut trivias_after_sep_token);
            docs.append(&mut trivias_after_node);
        }
    }

    Doc::list(docs)
}

struct DelimitersFormatter<'a> {
    open_text: &'static str,
    close_text: &'static str,
    space: Doc<'static>,
    open_token: Option<SyntaxToken>,
    close_token: Option<SyntaxToken>,
    single_line: &'a SingleLine,
    ctx: &'a Ctx<'a>,
}
impl<'a> DelimitersFormatter<'a> {
    fn paren(
        open: Option<SyntaxToken>,
        close: Option<SyntaxToken>,
        spacing: Option<bool>,
        ctx: &'a Ctx,
    ) -> Self {
        Self {
            open_text: "(",
            close_text: ")",
            space: if spacing.unwrap_or(ctx.options.paren_spacing) {
                Doc::line_or_space()
            } else {
                Doc::line_or_nil()
            },
            open_token: open,
            close_token: close,
            single_line: &SingleLine::Inherit,
            ctx,
        }
    }
    fn bracket(
        open: Option<SyntaxToken>,
        close: Option<SyntaxToken>,
        spacing: Option<bool>,
        ctx: &'a Ctx,
    ) -> Self {
        Self {
            open_text: "[",
            close_text: "]",
            space: if spacing.unwrap_or(ctx.options.bracket_spacing) {
                Doc::line_or_space()
            } else {
                Doc::line_or_nil()
            },
            open_token: open,
            close_token: close,
            single_line: &SingleLine::Inherit,
            ctx,
        }
    }
    fn brace(
        open: Option<SyntaxToken>,
        close: Option<SyntaxToken>,
        spacing: Option<bool>,
        ctx: &'a Ctx,
    ) -> Self {
        Self {
            open_text: "{",
            close_text: "}",
            space: if spacing.unwrap_or(ctx.options.brace_spacing) {
                Doc::line_or_space()
            } else {
                Doc::line_or_nil()
            },
            open_token: open,
            close_token: close,
            single_line: &SingleLine::Inherit,
            ctx,
        }
    }
    fn with_space(mut self, space: Doc<'static>) -> Self {
        self.space = space;
        self
    }
    fn with_single_line(mut self, single_line: &'a SingleLine) -> Self {
        self.single_line = single_line;
        self
    }
    fn format(self, body: Doc<'static>) -> Doc<'static> {
        let ctx = self.ctx;
        let mut docs = Vec::with_capacity(5);

        docs.push(Doc::text(self.open_text));

        if let Some(open) = self.open_token {
            let single_line = if let SingleLine::Inherit = &self.single_line {
                &ctx.options.single_line
            } else {
                self.single_line
            };
            if let Some(token) = open
                .next_token()
                .filter(|token| token.kind() == SyntaxKind::WHITESPACE)
            {
                match single_line {
                    SingleLine::Prefer => docs.push(self.space.clone()),
                    SingleLine::Smart | SingleLine::Inherit => {
                        if token.text().contains(['\n', '\r']) {
                            docs.push(Doc::hard_line());
                        } else {
                            docs.push(self.space.clone());
                        }
                    }
                    SingleLine::Never => docs.push(Doc::hard_line()),
                }
                let mut trivia_docs = format_trivias_after_token(&token, ctx);
                docs.append(&mut trivia_docs);
            } else {
                match single_line {
                    SingleLine::Prefer | SingleLine::Smart | SingleLine::Inherit => {
                        docs.push(self.space.clone())
                    }
                    SingleLine::Never => docs.push(Doc::hard_line()),
                }
                let mut trivia_docs = format_trivias_after_token(&open, ctx);
                docs.append(&mut trivia_docs);
            }
        }

        docs.push(body);

        let mut has_comment = false;
        if let Some(close) = self.close_token {
            let last_ws_index = close
                .prev_token()
                .filter(|token| token.kind() == SyntaxKind::WHITESPACE)
                .map(|token| token.index());
            let last_non_trivia =
                close
                    .siblings_with_tokens(Direction::Prev)
                    .skip(1)
                    .find(|element| {
                        !matches!(element.kind(), SyntaxKind::WHITESPACE | SyntaxKind::COMMENT)
                    });
            let mut trivias = match last_non_trivia {
                Some(SyntaxElement::Node(node)) => format_trivias(
                    node.siblings_with_tokens(Direction::Next).filter(|token| {
                        last_ws_index
                            .map(|index| token.index() != index)
                            .unwrap_or(true)
                    }),
                    &mut has_comment,
                    false,
                    ctx,
                ),
                Some(SyntaxElement::Token(token)) => format_trivias(
                    token.siblings_with_tokens(Direction::Next).filter(|token| {
                        last_ws_index
                            .map(|index| token.index() != index)
                            .unwrap_or(true)
                    }),
                    &mut has_comment,
                    false,
                    ctx,
                ),
                None => vec![],
            };
            docs.append(&mut trivias);
        }

        Doc::list(docs)
            .nest(ctx.indent_width)
            .append(if has_comment {
                Doc::hard_line()
            } else {
                self.space
            })
            .append(Doc::text(self.close_text))
            .group()
    }
}

fn format_comment(text: String, ctx: &Ctx) -> Doc<'static> {
    if ctx.options.format_comments {
        let content = text.strip_prefix('#').expect("comment must start with '#'");
        if content.is_empty() || content.starts_with([' ', '\t']) {
            Doc::text(text.to_string())
        } else {
            Doc::text(format!("# {content}"))
        }
    } else {
        Doc::text(text)
    }
}

fn format_trivias_after_node<N: CstNode>(node: &N, ctx: &Ctx) -> Vec<Doc<'static>> {
    let mut _has_comment = false;
    format_trivias(
        node.syntax().siblings_with_tokens(Direction::Next),
        &mut _has_comment,
        true,
        ctx,
    )
}

fn format_trivias_after_token(token: &SyntaxToken, ctx: &Ctx) -> Vec<Doc<'static>> {
    let mut _has_comment = false;
    format_trivias(
        token.siblings_with_tokens(Direction::Next),
        &mut _has_comment,
        true,
        ctx,
    )
}

fn format_trivias(
    it: impl Iterator<Item = SyntaxElement>,
    has_comment: &mut bool,
    skip_first_ws: bool,
    ctx: &Ctx,
) -> Vec<Doc<'static>> {
    let mut docs = vec![];
    let mut trivias = it
        .skip(1)
        .skip_while(|element| skip_first_ws && element.kind() == SyntaxKind::WHITESPACE)
        .map_while(|element| match element {
            SyntaxElement::Token(token)
                if token.kind() == SyntaxKind::WHITESPACE
                    || token.kind() == SyntaxKind::COMMENT =>
            {
                Some(token)
            }
            _ => None,
        })
        .peekable();
    if !skip_first_ws
        && trivias
            .peek()
            .is_some_and(|token| token.kind() == SyntaxKind::COMMENT)
    {
        docs.push(Doc::space());
    }

    while let Some(token) = trivias.next() {
        match token.kind() {
            SyntaxKind::WHITESPACE => match token.text().chars().filter(|c| *c == '\n').count() {
                0 => {
                    if *has_comment {
                        docs.push(Doc::hard_line());
                    } else if trivias
                        .peek()
                        .is_some_and(|token| token.kind() == SyntaxKind::COMMENT)
                    {
                        docs.push(Doc::space());
                    } else {
                        docs.push(Doc::line_or_space());
                    }
                }
                1 => {
                    if *has_comment {
                        docs.push(Doc::hard_line());
                    } else {
                        docs.push(Doc::line_or_space());
                    }
                }
                _ => {
                    docs.push(Doc::empty_line());
                    docs.push(Doc::hard_line());
                }
            },
            SyntaxKind::COMMENT => {
                docs.push(format_comment(token.to_string(), ctx));
                *has_comment = true;
            }
            _ => {}
        }
    }
    docs
}

fn reflow(text: &str, docs: &mut Vec<Doc<'static>>) {
    let mut lines = text.lines();
    if let Some(line) = lines.next() {
        docs.push(Doc::text(line.to_owned()));
    }
    for line in lines {
        docs.push(Doc::empty_line());
        docs.push(Doc::text(line.to_owned()));
    }
}

fn reflow_with_indent(s: &str) -> Vec<Doc<'static>> {
    let indent = s
        .lines()
        .skip(if s.starts_with([' ', '\t']) { 0 } else { 1 })
        .filter(|line| !line.trim().is_empty())
        .map(|line| {
            line.as_bytes()
                .iter()
                .take_while(|byte| byte.is_ascii_whitespace())
                .count()
        })
        .min()
        .unwrap_or_default();
    let mut docs = Vec::with_capacity(2);
    let mut lines = s.split('\n').enumerate().peekable();
    while let Some((i, line)) = lines.next() {
        let s = line.strip_suffix('\r').unwrap_or(line);
        let s = if s.starts_with([' ', '\t']) && i > 0 {
            s.get(indent..).unwrap_or(s)
        } else {
            s
        };
        if i > 0 {
            if s.trim().is_empty() && lines.peek().is_some() {
                docs.push(Doc::empty_line());
            } else {
                docs.push(Doc::hard_line());
            }
        }
        docs.push(Doc::text(s.to_owned()));
    }
    docs
}

fn should_ignore(node: &SyntaxNode, ctx: &Ctx) -> bool {
    // for the case that comment comes in the middle of a list of nodes
    node.prev_sibling_or_token()
        .and_then(|element| element.prev_sibling_or_token())
        .or_else(|| {
            // for the case that comment comes at the start or the end of a list of nodes
            node.parent()
                .and_then(|parent| parent.prev_sibling_or_token())
                .and_then(|parent| parent.prev_sibling_or_token())
        })
        .as_ref()
        .and_then(|element| match element {
            SyntaxElement::Token(token) if token.kind() == SyntaxKind::COMMENT => {
                token.text().strip_prefix('#').and_then(|s| {
                    s.trim_start()
                        .strip_prefix(&ctx.options.ignore_comment_directive)
                })
            }
            _ => None,
        })
        .is_some_and(|rest| rest.is_empty() || rest.starts_with(|c: char| c.is_ascii_whitespace()))
}

fn is_empty_delimiter<N: CstNode>(node: &N) -> bool {
    node.syntax()
        .children_with_tokens()
        .all(|element| element.kind() != SyntaxKind::COMMENT && element.as_node().is_none())
}

fn check_last_comments_in_implements_interfaces(node: &ImplementsInterfaces) -> bool {
    node.syntax()
        .last_child()
        .into_iter()
        .flat_map(|node| node.siblings_with_tokens(Direction::Prev))
        .skip(1)
        .take_while(|element| {
            matches!(element.kind(), SyntaxKind::WHITESPACE | SyntaxKind::COMMENT)
        })
        .any(|element| element.kind() == SyntaxKind::COMMENT)
}
