Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,10 @@ or `while`'s condition; not a bare name or literal):
becomes its value with its last child. A call with a single
`return <expression>` is replaced by that expression's tree. Any other
call, once its box opens, has the box in its node's place - the parent's
edge leads to the box's top, its siblings stay on their row - with the
note why it isn't replaced at its top; inside it, each line has its own
tree and calls of their own boxes.
edge leads to the box's top, its siblings stay on their row - without
the note of the substitution mode (the box in the call's place shows it
stays a box); inside it, each line has its own tree and calls of their
own boxes.
3. **Close:** one step from the trivial tree to the value in the line.

For a call of a function of the program's own, each edge to an argument is
Expand Down Expand Up @@ -298,8 +299,9 @@ Long labels aren't shortened, unlike the visualizer's trees.

### Copying the evaluation as a → chain

The **Copy → chain** item in the toolbar's **⋯ Options** menu copies the evaluation of the statement
the scrubber is on (for a visualized REPL entry: that input) in the course's
The **Copy → chain** item in the toolbar's **⋯ Options** menu copies the whole evaluation of the
statement the scrubber is on - from its first step to its value, at whichever of its steps the
scrubber is (for a visualized REPL entry: that input) - in the course's
paper notation: its expression, then one line per change, each starting with
`→`, taking the same steps as the visualizer. A call to a function with a single
`return <expression>` is replaced as in substitution mode. Any other call is
Expand Down
14 changes: 10 additions & 4 deletions scripts/check-console-browser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,8 @@ try {
failed += 1
console.error(`Baum: the box in the call node's place: ${error.message}`)
}
// The hint at a call that stays a box, in both modes.
// The hint at a call that stays a box: in «Ersetzung», not in «Baum»
// (the box in the call's place says it).
for (const name of ['Ersetzung', 'Baum']) {
try {
await mode(name)
Expand All @@ -718,10 +719,15 @@ try {
for (let index = 0; index < 6; index++) await next.click()
// Not a closing box's fading copy (.visualizer-closing-boxes).
const hint = frame.locator('.visualizer-view > .visualizer-canvas > .frame-box .frame-hint[data-box-reason="locals"]')
await hint.waitFor({ timeout: 5000 })
assert.equal(await hint.innerText(), 'notenschnitt hat lokale Variablen – der Aufruf wird als Kasten gezeigt.')
if (name === 'Ersetzung') {
await hint.waitFor({ timeout: 5000 })
assert.equal(await hint.innerText(), 'notenschnitt hat lokale Variablen – der Aufruf wird als Kasten gezeigt.')
} else {
await frame.locator('.visualizer-view > .visualizer-canvas [data-tree-box-key] .frame-box').first().waitFor({ timeout: 5000 })
assert.equal(await frame.locator('.visualizer-view > .visualizer-canvas .frame-hint').count(), 0, 'no hint in «Baum»')
}
assert.equal(await frame.locator('.visualizer-mode button', { hasText: name }).getAttribute('aria-pressed'), 'true')
console.log(`Baum: the hint at the box in «${name}»: ok`)
console.log(`Baum: the hint at the box in «${name}»${name === 'Baum' ? ': none' : ''}: ok`)
} catch (error) {
failed += 1
console.error(`Baum: the hint in «${name}»: ${error.message}`)
Expand Down
13 changes: 13 additions & 0 deletions scripts/check-tree-mode.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -163,5 +163,18 @@ check('notenschnitt([4, 5, 4.5]) + 1: the box in the call node\'s place, 1 besid
assert.ok(episode.expansions.includes(enter.step) && episode.expansions.includes(close.step), 'laid out again where the box opens and closes')
})

check('the → chain export: the whole statement, wherever the scrubber is in it', () => {
for (const name of ['shortForm', 'locals', 'boxInPlace']) {
const result = results[name]
const nodeIndexByFile = new Map(Object.entries(result.ast).map(([path, file]) => [path, indexAstNodes(file)]))
const chainAt = (step) => buildEvaluationChain({ trace: result.trace, ast: result.ast, nodeIndexByFile, sources: { 'main.py': programs[name] }, rootPath: 'main.py', step, errorMessage: null, comments: false, strings: { conditionComment: () => '', localsComment: '' } })
const last = result.trace.at(-1)
const full = chainAt(last)
const ofStatement = result.trace.filter((step) => chainAt(step) !== null && chainAt(step).split('\n')[0] === full.split('\n')[0])
assert.ok(ofStatement.length > 3, `${name}: steps of the statement`)
for (const step of [ofStatement[0], ofStatement[Math.floor(ofStatement.length / 2)], ofStatement.at(-1)]) assert.equal(chainAt(step), full, `${name}: at step ${step.step}`)
}
})

if (failed > 0) process.exit(1)
console.log('Tree mode checks passed.')
4 changes: 3 additions & 1 deletion src/GlPythonWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,7 @@
{parens && '('}
{lines.map((line, index) => {
const lineStart = cursor
cursor += line.length + 1

Check warning on line 1053 in src/GlPythonWorkspace.tsx

View workflow job for this annotation

GitHub Actions / check

react(immutability)

src/GlPythonWorkspace.tsx:1053:11: Cannot reassign variable after render completes
return <Fragment key={index}>{index > 0 && '\n'}{renderSegments(lineSegments(line, lineStart))}</Fragment>
})}
{parens && ')'}
Expand All @@ -1058,7 +1058,9 @@
)
}

const showBoxHint = substitutionMode && scope.kind === 'call' && !insideBox
// Not in the tree mode: there the box stands in the call's place, which
// shows by itself that the call stays a box.
const showBoxHint = substitutionMode && trees === null && scope.kind === 'call' && !insideBox
const boxReason = showBoxHint ? trace.find((step) => step.kind === 'call-enter' && step.frameId === frameId)?.boxReason ?? null : null
return (
<div className={`frame-box${isModule ? ' frame-box--root' : ''}`} data-frame-id={frameId}>
Expand Down
Loading