From 8672ce7b35e873dccb7e40566b19d84538ef952c Mon Sep 17 00:00:00 2001 From: Kaz Date: Mon, 11 Mar 2024 11:51:15 -0700 Subject: [PATCH 1/2] More type-checking for concrete children --- app/gui2/shared/ast/tree.ts | 55 +++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/app/gui2/shared/ast/tree.ts b/app/gui2/shared/ast/tree.ts index dbb1e0bd316d..aa43e3459310 100644 --- a/app/gui2/shared/ast/tree.ts +++ b/app/gui2/shared/ast/tree.ts @@ -608,7 +608,7 @@ export class App extends Ast { const spacedEquals = useParens && !!nameSpecification?.equals.whitespace if (useParens) yield ensureSpaced(parens.open, verbatim) if (nameSpecification) { - yield ensureSpacedIf(nameSpecification.name, !useParens, verbatim) + yield useParens ? preferUnspaced(nameSpecification.name) : ensureSpaced(nameSpecification.name, verbatim) yield ensureSpacedOnlyIf(nameSpecification.equals, spacedEquals, verbatim) } yield ensureSpacedOnlyIf(argument, !nameSpecification || spacedEquals, verbatim) @@ -626,36 +626,39 @@ export class App extends Ast { return super.printSubtree(info, offset, parentIndent, verbatim_) } } -function ensureSpacedIf( - child: NodeChild, - condition: boolean, - verbatim: boolean | undefined, -): NodeChild { - return condition ? ensureSpaced(child, verbatim) : child -} function ensureSpacedOnlyIf( child: NodeChild, condition: boolean, verbatim: boolean | undefined, -): NodeChild { +): ConcreteChild { return condition ? ensureSpaced(child, verbatim) : ensureUnspaced(child, verbatim) } -function ensureSpaced(child: NodeChild, verbatim: boolean | undefined): NodeChild { - if (verbatim && child.whitespace != null) return child - return child.whitespace ? child : { ...child, whitespace: ' ' } + +type ConcreteChild = { whitespace: string; node: T } +function isConcrete(child: NodeChild): child is ConcreteChild { + return child.whitespace !== undefined } -function ensureUnspaced(child: NodeChild, verbatim: boolean | undefined): NodeChild { - if (verbatim && child.whitespace != null) return child - return child.whitespace === '' ? child : { ...child, whitespace: '' } +function tryAsConcrete(child: NodeChild): ConcreteChild | undefined { + return isConcrete(child) ? child : undefined } -function preferSpacedIf(child: NodeChild, condition: boolean): NodeChild { +function ensureSpaced(child: NodeChild, verbatim: boolean | undefined): ConcreteChild { + const concreteInput = tryAsConcrete(child) + if (verbatim && concreteInput) return concreteInput + return concreteInput?.whitespace ? concreteInput : { ...child, whitespace: ' ' } +} +function ensureUnspaced(child: NodeChild, verbatim: boolean | undefined): ConcreteChild { + const concreteInput = tryAsConcrete(child) + if (verbatim && concreteInput) return concreteInput + return concreteInput?.whitespace === '' ? concreteInput : { ...child, whitespace: '' } +} +function preferSpacedIf(child: NodeChild, condition: boolean): ConcreteChild { return condition ? preferSpaced(child) : preferUnspaced(child) } -function preferUnspaced(child: NodeChild): NodeChild { - return child.whitespace === undefined ? { ...child, whitespace: '' } : child +function preferUnspaced(child: NodeChild): ConcreteChild { + return tryAsConcrete(child) ?? { ...child, whitespace: '' } } -function preferSpaced(child: NodeChild): NodeChild { - return child.whitespace === undefined ? { ...child, whitespace: ' ' } : child +function preferSpaced(child: NodeChild): ConcreteChild { + return tryAsConcrete(child) ?? { ...child, whitespace: ' ' } } export class MutableApp extends App implements MutableAst { declare readonly module: MutableModule @@ -1865,9 +1868,9 @@ export class Assignment extends Ast { *concreteChildren(verbatim?: boolean): IterableIterator { const { pattern, equals, expression } = getAll(this.fields) - yield pattern + yield ensureUnspaced(pattern, verbatim) yield ensureSpacedOnlyIf(equals, expression.whitespace !== '', verbatim) - yield expression + yield preferSpaced(expression) } } export class MutableAssignment extends Assignment implements MutableAst { @@ -1926,7 +1929,7 @@ export class BodyBlock extends Ast { *concreteChildren(_verbatim?: boolean): IterableIterator { for (const line of this.fields.get('lines')) { - yield line.newline ?? { node: Token.new('\n', RawAst.Token.Type.Newline) } + yield preferUnspaced(line.newline) if (line.expression) yield line.expression } } @@ -2223,15 +2226,15 @@ export class Vector extends Ast { return Vector.tryBuild(inputs, elementBuilder, edit) } - *concreteChildren(_verbatim?: boolean): IterableIterator { + *concreteChildren(verbatim?: boolean): IterableIterator { const { open, elements, close } = getAll(this.fields) - yield unspaced(open.node) + yield ensureUnspaced(open, verbatim) let isFirst = true for (const { delimiter, value } of elements) { if (isFirst && value) { yield preferUnspaced(value) } else { - yield delimiter + yield preferUnspaced(delimiter) if (value) yield preferSpaced(value) } isFirst = false From 6cc55c87ffbfdf93b2167ad2e2502e06bc7b79b0 Mon Sep 17 00:00:00 2001 From: Kaz Date: Tue, 12 Mar 2024 06:06:13 -0700 Subject: [PATCH 2/2] Lint --- app/gui2/shared/ast/tree.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/gui2/shared/ast/tree.ts b/app/gui2/shared/ast/tree.ts index aa43e3459310..d62c0188a9aa 100644 --- a/app/gui2/shared/ast/tree.ts +++ b/app/gui2/shared/ast/tree.ts @@ -608,7 +608,9 @@ export class App extends Ast { const spacedEquals = useParens && !!nameSpecification?.equals.whitespace if (useParens) yield ensureSpaced(parens.open, verbatim) if (nameSpecification) { - yield useParens ? preferUnspaced(nameSpecification.name) : ensureSpaced(nameSpecification.name, verbatim) + yield useParens ? + preferUnspaced(nameSpecification.name) + : ensureSpaced(nameSpecification.name, verbatim) yield ensureSpacedOnlyIf(nameSpecification.equals, spacedEquals, verbatim) } yield ensureSpacedOnlyIf(argument, !nameSpecification || spacedEquals, verbatim) @@ -1778,7 +1780,7 @@ export class Function extends Ast { } } - *concreteChildren(verbatim?: boolean): IterableIterator { + *concreteChildren(_verbatim?: boolean): IterableIterator { const { name, argumentDefinitions, equals, body } = getAll(this.fields) yield name for (const def of argumentDefinitions) yield* def