/* * 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 {JsonVisitor} from "./visitor"; import {asRef, RpcCodecs, RpcReceiveQueue, RpcSendQueue} from "../rpc"; import {Json} from "./tree"; import {updateIfChanged} from "../util"; class JsonSender extends JsonVisitor { protected async preVisit(j: Json, q: RpcSendQueue): Promise { await q.getAndSend(j, j2 => j2.id); await q.getAndSend(j, j2 => asRef(j2.prefix), async space => await this.visitSpace(space, q)); await q.getAndSend(j, j2 => j2.markers); return j; } protected async visitDocument(document: Json.Document, q: RpcSendQueue): Promise { await q.getAndSend(document, d => d.sourcePath); await q.getAndSend(document, d => d.charsetName); await q.getAndSend(document, d => d.charsetBomMarked); await q.getAndSend(document, d => d.checksum); await q.getAndSend(document, d => d.fileAttributes); await q.getAndSend(document, d => d.value, async j => await this.visit(j, q)); await q.getAndSend(document, d => asRef(d.eof), async space => await this.visitSpace(space, q)); return document; } protected async visitArray(array: Json.Array, q: RpcSendQueue): Promise { await q.getAndSendList(array, a => a.values, j => j.element.id, async j => await this.visitRightPadded(j, q)); return array; } protected async visitEmpty(empty: Json.Empty): Promise { return empty; } protected async visitIdentifier(identifier: Json.Identifier, q: RpcSendQueue): Promise { await q.getAndSend(identifier, id => id.name); return identifier; } protected async visitLiteral(literal: Json.Literal, q: RpcSendQueue): Promise { await q.getAndSend(literal, lit => lit.source); await q.getAndSend(literal, lit => lit.value); return literal; } protected async visitMember(member: Json.Member, q: RpcSendQueue): Promise { await q.getAndSend(member, m => m.key, async j => await this.visitRightPadded(j, q)); await q.getAndSend(member, m => m.value, async j => await this.visit(j, q)); return member; } protected async visitObject(obj: Json.Object, q: RpcSendQueue): Promise { await q.getAndSendList(obj, o => o.members, j => j.element.id, async j => await this.visitRightPadded(j, q)); return obj; } public async visitSpace(space: Json.Space, q: RpcSendQueue): Promise { await q.getAndSendList(space, s => s.comments, c => c.text + c.suffix, async c => { await q.getAndSend(c, c2 => c2.multiline); await q.getAndSend(c, c2 => c2.text); await q.getAndSend(c, c2 => c2.suffix); await q.getAndSend(c, c2 => c2.markers); }); await q.getAndSend(space, s => s.whitespace); return space; } public async visitRightPadded(right: Json.RightPadded, q: RpcSendQueue): Promise | undefined> { await q.getAndSend(right, r => r.element, j => this.visit(j, q)); await q.getAndSend(right, r => asRef(r.after), async space => await this.visitSpace(space, q)); await q.getAndSend(right, r => r.markers); return right; } } // noinspection ES6MissingAwait class JsonReceiver extends JsonVisitor { protected async preVisit(j: Json, q: RpcReceiveQueue): Promise { return updateIfChanged(j, { id: await q.receive(j.id), prefix: await q.receive(j.prefix, async space => await this.visitSpace(space, q)), markers: await q.receive(j.markers), }); } protected async visitDocument(document: Json.Document, q: RpcReceiveQueue): Promise { return updateIfChanged(document, { sourcePath: await q.receive(document.sourcePath), charsetName: await q.receive(document.charsetName), charsetBomMarked: await q.receive(document.charsetBomMarked), checksum: await q.receive(document.checksum), fileAttributes: await q.receive(document.fileAttributes), value: await q.receive(document.value, async j => await this.visit(j, q)!), eof: await q.receive(document.eof, async space => await this.visitSpace(space, q)), }); } protected async visitArray(array: Json.Array, q: RpcReceiveQueue): Promise { return updateIfChanged(array, { values: await q.receiveListDefined(array.values, async j => await this.visitRightPadded(j, q)!)!, }); } protected async visitEmpty(empty: Json.Empty): Promise { return empty; } protected async visitIdentifier(identifier: Json.Identifier, q: RpcReceiveQueue): Promise { return updateIfChanged(identifier, { name: await q.receive(identifier.name), }); } protected async visitLiteral(literal: Json.Literal, q: RpcReceiveQueue): Promise { return updateIfChanged(literal, { source: await q.receive(literal.source), value: await q.receive(literal.value), }); } protected async visitMember(member: Json.Member, q: RpcReceiveQueue): Promise { return updateIfChanged(member, { key: await q.receive(member.key, async j => await this.visitRightPadded(j, q)!)!, value: await q.receive(member.value, async j => await this.visit(j, q)!), }); } protected async visitObject(obj: Json.Object, q: RpcReceiveQueue): Promise { return updateIfChanged(obj, { members: await q.receiveListDefined(obj.members, async j => await this.visitRightPadded(j, q)), }); } public async visitSpace(space: Json.Space, q: RpcReceiveQueue): Promise { return updateIfChanged(space, { comments: await q.receiveListDefined(space.comments, async c => { return updateIfChanged(c, { multiline: await q.receive(c.multiline), text: await q.receive(c.text), suffix: await q.receive(c.suffix), markers: await q.receive(c.markers), }); }), whitespace: await q.receive(space.whitespace), }); } public async visitRightPadded(right: Json.RightPadded, p: RpcReceiveQueue): Promise | undefined> { if (!right) { throw new Error("TreeDataReceiveQueue should have instantiated an empty padding") } return updateIfChanged(right, { element: await p.receive(right.element, async j => await this.visit(j, p)!) as T, after: await p.receive(right.after, async space => await this.visitSpace(space, p)), markers: await p.receiveMarkers(right.markers), }); } } const receiver = new JsonReceiver(); const sender = new JsonSender(); // Register codec for all Java AST node types for (const kind of Object.values(Json.Kind)) { if (kind === Json.Kind.Space) { RpcCodecs.registerCodec(kind, { async rpcReceive(before: Json.Space, q: RpcReceiveQueue): Promise { return (await receiver.visitSpace(before, q))!; }, async rpcSend(after: Json.Space, q: RpcSendQueue): Promise { await sender.visitSpace(after, q); } }, Json.Kind.Document); } else if (kind === Json.Kind.RightPadded) { RpcCodecs.registerCodec(kind, { async rpcReceive(before: Json.RightPadded, q: RpcReceiveQueue): Promise> { return (await receiver.visitRightPadded(before, q))!; }, async rpcSend(after: Json.RightPadded, q: RpcSendQueue): Promise { await sender.visitRightPadded(after, q); } }, Json.Kind.Document); } else { RpcCodecs.registerCodec(kind as string, { async rpcReceive(before: Json, q: RpcReceiveQueue): Promise { return (await receiver.visit(before, q))!; }, async rpcSend(after: Json, q: RpcSendQueue): Promise { await sender.visit(after, q); } }, Json.Kind.Document); } }