/* * Copyright 2025 the original author or authors. *

* Licensed under the Moderne Source Available License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://docs.moderne.io/licensing/moderne-source-available-license *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import {PrintOutputCapture, TreePrinters} from "../print"; import {YamlVisitor} from "./visitor"; import {printTag, Yaml} from "./tree"; import {Cursor} from "../tree"; import {findMarker, Markers} from "../markers"; import "./markers"; // Ensures Yaml.Markers is defined class YamlPrinter extends YamlVisitor { protected async visitDocuments(documents: Yaml.Documents, p: PrintOutputCapture): Promise { await this.visitMarkers(documents.markers, p); for (const document of documents.documents) { await this.visit(document, p); } if (documents.suffix) { p.append(documents.suffix); } this.afterSyntax(documents, p); return documents; } protected async visitDocument(document: Yaml.Document, p: PrintOutputCapture): Promise { await this.beforeSyntax(document, p); for (const directive of document.directives) { await this.visit(directive, p); } if (document.explicit) { p.append("---"); } await this.visit(document.block, p); await this.visit(document.end, p); this.afterSyntax(document, p); return document; } protected async visitDirective(directive: Yaml.Directive, p: PrintOutputCapture): Promise { await this.beforeSyntax(directive, p); p.append('%'); p.append(directive.value); p.append(directive.suffix); this.afterSyntax(directive, p); return directive; } protected async visitDocumentEnd(end: Yaml.DocumentEnd, p: PrintOutputCapture): Promise { await this.beforeSyntax(end, p); if (end.explicit) { p.append("..."); } return end; } protected async visitMapping(mapping: Yaml.Mapping, p: PrintOutputCapture): Promise { await this.beforeSyntax(mapping, p); if (mapping.anchor) { await this.visit(mapping.anchor, p); } if (mapping.openingBracePrefix !== undefined) { p.append(mapping.openingBracePrefix); p.append('{'); } for (const entry of mapping.entries) { await this.visit(entry, p); } if (mapping.closingBracePrefix !== undefined) { p.append(mapping.closingBracePrefix); p.append('}'); } this.afterSyntax(mapping, p); return mapping; } protected async visitMappingEntry(entry: Yaml.MappingEntry, p: PrintOutputCapture): Promise { await this.beforeSyntax(entry, p); await this.visit(entry.key, p); p.append(entry.beforeMappingValueIndicator); if (!findMarker(entry, Yaml.Markers.OmitColon)) { p.append(':'); } await this.visit(entry.value, p); this.afterSyntax(entry, p); return entry; } protected async visitScalar(scalar: Yaml.Scalar, p: PrintOutputCapture): Promise { await this.beforeSyntax(scalar, p); if (scalar.tag) { await this.visit(scalar.tag, p); } if (scalar.anchor) { await this.visit(scalar.anchor, p); } switch (scalar.style) { case Yaml.ScalarStyle.DOUBLE_QUOTED: p.append('"'); p.append(scalar.value); p.append('"'); break; case Yaml.ScalarStyle.SINGLE_QUOTED: p.append("'"); p.append(scalar.value); p.append("'"); break; case Yaml.ScalarStyle.LITERAL: p.append('|'); p.append(scalar.value); break; case Yaml.ScalarStyle.FOLDED: p.append('>'); p.append(scalar.value); break; case Yaml.ScalarStyle.PLAIN: default: p.append(scalar.value); break; } this.afterSyntax(scalar, p); return scalar; } protected async visitSequence(sequence: Yaml.Sequence, p: PrintOutputCapture): Promise { await this.beforeSyntax(sequence, p); if (sequence.anchor) { await this.visit(sequence.anchor, p); } if (sequence.openingBracketPrefix !== undefined) { p.append(sequence.openingBracketPrefix); p.append('['); } for (const entry of sequence.entries) { await this.visit(entry, p); } if (sequence.closingBracketPrefix !== undefined) { p.append(sequence.closingBracketPrefix); p.append(']'); } this.afterSyntax(sequence, p); return sequence; } protected async visitSequenceEntry(entry: Yaml.SequenceEntry, p: PrintOutputCapture): Promise { p.append(entry.prefix); if (entry.dash) { p.append('-'); } await this.visit(entry.block, p); if (entry.trailingCommaPrefix !== undefined) { p.append(entry.trailingCommaPrefix); p.append(','); } this.afterSyntax(entry, p); return entry; } protected async visitAnchor(anchor: Yaml.Anchor, p: PrintOutputCapture): Promise { await this.visitMarkers(anchor.markers, p); p.append(anchor.prefix); p.append('&'); p.append(anchor.key); p.append(anchor.postfix); this.afterSyntax(anchor, p); return anchor; } protected async visitAlias(alias: Yaml.Alias, p: PrintOutputCapture): Promise { await this.beforeSyntax(alias, p); p.append('*'); if (alias.anchor) { p.append(alias.anchor.key); } this.afterSyntax(alias, p); return alias; } protected async visitTag(tag: Yaml.Tag, p: PrintOutputCapture): Promise { await this.visitMarkers(tag.markers, p); p.append(tag.prefix); p.append(printTag(tag)); p.append(tag.suffix); this.afterSyntax(tag, p); return tag; } private yamlMarkerWrapper = (out: string): string => `~~${out}${out ? "~~" : ""}>`; private async beforeSyntax(yaml: Yaml, p: PrintOutputCapture): Promise { await this.beforeSyntaxWithMarkers(yaml.prefix, yaml.markers, p); } private async beforeSyntaxWithMarkers(prefix: string, markers: Markers, p: PrintOutputCapture): Promise { for (const marker of markers.markers) { p.append(p.markerPrinter.beforePrefix(marker, new Cursor(marker, this.cursor), this.yamlMarkerWrapper)); } p.append(prefix); await this.visitMarkers(markers, p); for (const marker of markers.markers) { p.append(p.markerPrinter.beforeSyntax(marker, new Cursor(marker, this.cursor), this.yamlMarkerWrapper)); } } private afterSyntax(yaml: Yaml, p: PrintOutputCapture): void { this.afterSyntaxWithMarkers(yaml.markers, p); } private afterSyntaxWithMarkers(markers: Markers, p: PrintOutputCapture): void { for (const marker of markers.markers) { p.append(p.markerPrinter.afterSyntax(marker, new Cursor(marker, this.cursor), this.yamlMarkerWrapper)); } } } TreePrinters.register(Yaml.Kind.Documents, () => new YamlPrinter());