/*
* 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 {JsonVisitor} from "./visitor";
import {Json} from "./tree";
import {Cursor} from "../tree";
import {Markers} from "../markers";
class JsonPrinter extends JsonVisitor {
protected async visitArray(array: Json.Array, p: PrintOutputCapture): Promise {
await this.beforeSyntax(array, p);
p.append('[');
await this.visitRightPaddedWithSuffix(array.values, ",", p);
p.append(']');
this.afterSyntax(array, p);
return array;
}
protected async visitDocument(document: Json.Document, p: PrintOutputCapture): Promise {
await this.beforeSyntax(document, p);
await this.visit(document.value, p);
await this.visitSpace(document.eof, p);
this.afterSyntax(document, p);
return document;
}
protected async visitEmpty(empty: Json.Empty, p: PrintOutputCapture): Promise {
await this.beforeSyntax(empty, p);
this.afterSyntax(empty, p);
return empty;
}
protected async visitIdentifier(identifier: Json.Identifier, p: PrintOutputCapture): Promise {
await this.beforeSyntax(identifier, p);
p.append(identifier.name);
this.afterSyntax(identifier, p);
return identifier;
}
protected async visitLiteral(literal: Json.Literal, p: PrintOutputCapture): Promise {
await this.beforeSyntax(literal, p);
p.append(literal.source);
this.afterSyntax(literal, p);
return literal;
}
protected async visitMember(member: Json.Member, p: PrintOutputCapture): Promise {
await this.beforeSyntax(member, p);
await this.visitRightPadded(member.key, p);
p.append(':');
await this.visit(member.value, p);
this.afterSyntax(member, p);
return member;
}
protected async visitObject(jsonObject: Json.Object, p: PrintOutputCapture): Promise {
await this.beforeSyntax(jsonObject, p);
p.append('{');
await this.visitRightPaddedWithSuffix(jsonObject.members, ",", p);
p.append('}');
this.afterSyntax(jsonObject, p);
return jsonObject;
}
public async visitSpace(space: Json.Space, p: PrintOutputCapture): Promise {
p.append(space.whitespace);
for (const comment of space.comments) {
await this.visitMarkers(comment.markers, p);
if (comment.multiline) {
p.append(`/*${comment.text}*/`);
} else {
p.append(`//${comment.text}`);
}
p.append(comment.suffix);
}
return space;
}
private async visitRightPaddedWithSuffix(nodes: Json.RightPadded[], suffixBetween: string, p: PrintOutputCapture): Promise {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
await this.visit(node.element, p);
await this.visitSpace(node.after, p);
if (i < nodes.length - 1) {
p.append(suffixBetween);
}
}
}
private async beforeSyntax(json: Json, p: PrintOutputCapture): Promise {
await this.beforeSyntaxWithMarkers(json.prefix, json.markers, p);
}
private async beforeSyntaxWithMarkers(prefix: Json.Space, markers: Markers, p: PrintOutputCapture): Promise {
for (const marker of markers.markers) {
p.append(p.markerPrinter.beforePrefix(marker, new Cursor(marker, this.cursor), this.jsonMarkerWrapper));
}
await this.visitSpace(prefix, p);
await this.visitMarkers(markers, p);
for (const marker of markers.markers) {
p.append(p.markerPrinter.beforeSyntax(marker, new Cursor(marker, this.cursor), this.jsonMarkerWrapper));
}
}
private afterSyntax(json: Json, p: PrintOutputCapture): void {
this.afterSyntaxWithMarkers(json.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.jsonMarkerWrapper));
}
}
private jsonMarkerWrapper = (out: string): string => `/*~~${out}${out ? "~~" : ""}>*/`;
}
TreePrinters.register(Json.Kind.Document, () => new JsonPrinter());