diff --git a/css/elements.css b/css/elements.css index cda2b290..6384489b 100644 --- a/css/elements.css +++ b/css/elements.css @@ -75,6 +75,8 @@ --attributes-tag-foreground-color: #884400; + --ac-body-border-color: #ddd; + --figure-background: #fff; } @@ -157,6 +159,8 @@ --attributes-tag-foreground-color: #e6a96d; + --ac-body-border-color: #555; + --figure-background: #fff; } } @@ -496,6 +500,12 @@ emu-alg ol.nested-lots ol { list-style-type: lower-roman; } +emu-alg ol.ac-body { + border-left: 3px solid var(--ac-body-border-color); + border-radius: 0 0 0 20px; + padding-bottom: 5px; +} + emu-eqn { display: block; margin-left: 4em; diff --git a/src/Algorithm.ts b/src/Algorithm.ts index e6560d86..9a5df160 100644 --- a/src/Algorithm.ts +++ b/src/Algorithm.ts @@ -4,7 +4,7 @@ import type { PartialBiblioEntry, StepBiblioEntry } from './Biblio'; import Builder from './Builder'; import { SPECIAL_KINDS_MAP, SPECIAL_KINDS } from './Clause'; -import { warnEmdFailure, wrapEmdFailure } from './utils'; +import { isAbstractClosureHeader, ownTextContent, warnEmdFailure, wrapEmdFailure } from './utils'; import { collectNonterminalsFromEmd } from './lint/utils'; import * as emd from 'ecmarkdown'; @@ -119,6 +119,16 @@ export default class Algorithm extends Builder { } } + for (const step of node.querySelectorAll('li')) { + if (isAbstractClosureHeader(ownTextContent(step))) { + for (const ol of step.children) { + if (ol.tagName === 'OL') { + ol.classList.add('ac-body'); + } + } + } + } + for (const step of node.querySelectorAll(kindSelector)) { // prettier-ignore const attributes = SPECIAL_KINDS diff --git a/src/lint/rules/variable-use-def.ts b/src/lint/rules/variable-use-def.ts index a5fc0a9f..abb7b21b 100644 --- a/src/lint/rules/variable-use-def.ts +++ b/src/lint/rules/variable-use-def.ts @@ -8,7 +8,7 @@ import type { import type { Reporter } from '../algorithm-error-reporter-type'; import type { Seq } from '../../expr-parser'; import { walk as walkExpr } from '../../expr-parser'; -import { offsetToLineAndColumn } from '../../utils'; +import { isAbstractClosureHeader, offsetToLineAndColumn } from '../../utils'; /* Ecmaspeak scope rules are a bit weird. @@ -244,10 +244,7 @@ function walkAlgorithm( } // handle abstract closures - if ( - last?.name === 'text' && - / performs the following steps (atomically )?when called:$/.test(last.contents) - ) { + if (last?.name === 'text' && isAbstractClosureHeader(last.contents)) { if (first.name === 'text' && first.contents === 'Let ' && isVariable(expr.items[1])) { const closureName = expr.items[1]; scope.declare(closureName.contents, closureName); diff --git a/src/utils.ts b/src/utils.ts index bd0a4bb5..008c3d27 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -284,10 +284,7 @@ export function doesEffectPropagateToParent(node: Element, effect: string) { // This is super hacky. It's checking the output of ecmarkdown. if (parent.tagName !== 'LI') continue; - if ( - effect === 'user-code' && - /be a new (\w+ )*Abstract Closure/.test(parent.textContent ?? '') - ) { + if (effect === 'user-code' && isAbstractClosureHeader(ownTextContent(parent))) { return false; } @@ -343,3 +340,17 @@ export function withOrdinalSuffix(n: number): string { const suffixes = { one: 'st', two: 'nd', few: 'rd', other: 'th' }; return `${n}${suffixes[rule as keyof typeof suffixes]}`; } + +const acHeaderRe = / performs the following steps (atomically )?when called:$/; +export function isAbstractClosureHeader(text: string): boolean { + return acHeaderRe.test(text); +} + +export function ownTextContent(el: Element): string { + let text = ''; + for (const child of el.childNodes) { + if (child.nodeType === 1 && (child as Element).tagName === 'OL') continue; + text += child.textContent; + } + return text; +} diff --git a/test/baselines/generated-reference/assets-inline.html b/test/baselines/generated-reference/assets-inline.html index cbc629c9..42e33c39 100644 --- a/test/baselines/generated-reference/assets-inline.html +++ b/test/baselines/generated-reference/assets-inline.html @@ -1693,6 +1693,8 @@ --attributes-tag-foreground-color: #884400; + --ac-body-border-color: #ddd; + --figure-background: #fff; } @@ -1775,6 +1777,8 @@ --attributes-tag-foreground-color: #e6a96d; + --ac-body-border-color: #555; + --figure-background: #fff; } } @@ -2114,6 +2118,12 @@ list-style-type: lower-roman; } +emu-alg ol.ac-body { + border-left: 3px solid var(--ac-body-border-color); + border-radius: 0 0 0 20px; + padding-bottom: 5px; +} + emu-eqn { display: block; margin-left: 4em; diff --git a/test/baselines/generated-reference/effect-user-code.html b/test/baselines/generated-reference/effect-user-code.html index fa5442ab..10c992d1 100644 --- a/test/baselines/generated-reference/effect-user-code.html +++ b/test/baselines/generated-reference/effect-user-code.html @@ -111,13 +111,13 @@
The abstract operation MakeAbstractClosure takes no arguments. The user-code effect doesn't propagate through
The abstract operation MakeJobAbstractClosure takes no arguments. The user-code effect doesn't propagate through specialized