Skip to content
This repository was archived by the owner on Nov 7, 2026. It is now read-only.
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is loosely inspired by Keep a Changelog.

### Changed

- **Web 0.14.17, hint on a missing line break:** when a Java exercise's output is right except that lines run together (e.g. `System.out.print` twice instead of `println`), the check now says „Fast! Es fehlt nur ein Zeilenumbruch …“ and explains print vs. println, instead of only showing a mismatch. `\n` inside an output now starts a new line, so `System.out.print("…\n")` counts like `println` (de/en/pt; all exercises).
- **Web 0.14.16, „Im Editor öffnen“ under the Java examples in lessons 4–19:** every runnable Java example in the lesson text of lessons 4–19 now has the button „Im Editor öffnen“ (like the Mia example in lesson 4), so students can run and change it in Programmieren. Not in lesson 18, whose examples are a method and its call on their own, and not in cloze texts or games (de/en/pt).
- **Web 0.14.15, three kinds of errors, red line numbers and a light bulb with suggestions:** lesson 7 gets the new step „Drei Arten von Fehlern“ (syntax error, runtime error, logic error, with the app's own messages), a Kompetenz item and two quiz questions; lesson 20 explains the red line number and the light bulb and calls the debugger the tool for logic errors (plus Kompetenz and quiz question); lesson 22 calls an exception a runtime error; glossary entries Syntaxfehler, Laufzeitfehler, Logikfehler (de/en/pt). In the Code area the line number of an error turns red (syntax problems and the line where the run stopped), the console names the line of a runtime error (e.g. „Zeile 3: Division durch null.“), and a 💡 at the end of the line offers a fix when VisuStruct knows it (missing `import`, missing `Scanner … = new Scanner(System.in);`, missing `;` or `)`, misspelled data type such as `Strng` → `String`); hover shows it, a click applies it. The round red dot stays the breakpoint.
- **Web 0.13.37, no coffee cup and a better camel in lesson 4:** the coffee cup drawing in step „Jetzt in Java“ is removed again (Holger's decision). The camel for camelCase is redrawn to look more like a camel: long legs with knees and broad feet, U-shaped neck, longer snout, tail with tuft.
Expand Down
4 changes: 2 additions & 2 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "visustruct-web",
"private": true,
"version": "0.14.16",
"version": "0.14.17",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
6 changes: 6 additions & 0 deletions web/src/core/exerciseCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ export function sameOutput(actual: string[], expected: string[]): boolean {
return actual.every((line, i) => normalize(line) === normalize(expected[i]))
}

/** Stimmt die Ausgabe bis auf fehlende Zeilenumbrüche, z. B. zweimal `print` statt `println`? */
export function onlyLineBreaksMissing(actual: string[], expected: string[]): boolean {
const joined = (lines: string[]) => lines.join('').replace(/\s+/g, '')
return actual.length > 0 && actual.length < expected.length && joined(actual) === joined(expected)
}

function normalize(line: string): string {
const t = line.trim().replace(/\s+/g, ' ')
return /^-?\d+\.0+$/.test(t) ? t.replace(/\.0+$/, '') : t
Expand Down
11 changes: 8 additions & 3 deletions web/src/core/simulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1406,9 +1406,14 @@ export class SimulationEngine {
const rendered = this.peek(() => this.renderOutputExpression(expression))
// `println()` ohne Text: nur eine neue Zeile.
const output = expression.trim() === '' ? '' : this.evaluateOutputExpression(expression)
if (state.outputLineOpen && state.outputLines.length) state.outputLines[state.outputLines.length - 1] += output
else state.outputLines.push(output)
state.outputLineOpen = javaOut?.[1] === 'print'
// `\n` im Text beginnt eine neue Zeile; `print("…\n")` endet wie `println("…")`.
const print = javaOut?.[1] === 'print'
const endsLine = print && output.endsWith('\n')
const [first, ...rest] = (endsLine ? output.slice(0, -1) : output).split('\n')
if (state.outputLineOpen && state.outputLines.length) state.outputLines[state.outputLines.length - 1] += first
else state.outputLines.push(first)
state.outputLines.push(...rest)
state.outputLineOpen = print && !endsLine
this.afterOutput = true
state.lastTrace = `${rendered} -> ${output}`
return
Expand Down
1 change: 1 addition & 0 deletions web/src/i18n/de.strings
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"web_learn_actual" = "Deine Ausgabe";
"web_learn_missing_input" = "Dein Struktogramm fragt nach mehr Eingaben als vorgesehen.";
"web_learn_unused_input" = "Dein Struktogramm fragt nicht alle Eingaben ab.";
"web_learn_missing_line_break" = "Fast! Es fehlt nur ein Zeilenumbruch. System.out.print schreibt weiter in derselben Zeile, System.out.println beginnt danach eine neue.";
"web_learn_too_many_steps" = "Abbruch nach 100 000 Schritten. Läuft eine Schleife endlos?";
"web_learn_score_fmt" = "%d von %d richtig";
"web_learn_cloze_hint" = "Tippe die fehlenden Wörter in die Lücken und drücke „Prüfen“.";
Expand Down
1 change: 1 addition & 0 deletions web/src/i18n/en.strings
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"web_learn_actual" = "Your output";
"web_learn_missing_input" = "Your structure chart asks for more input than planned.";
"web_learn_unused_input" = "Your structure chart does not ask for all inputs.";
"web_learn_missing_line_break" = "Almost! Only a line break is missing. System.out.print keeps writing on the same line, System.out.println starts a new line afterwards.";
"web_learn_too_many_steps" = "Stopped after 100,000 steps. Is a loop running forever?";
"web_learn_score_fmt" = "%d of %d correct";
"web_learn_cloze_hint" = "Type the missing words into the gaps and press “Check”.";
Expand Down
1 change: 1 addition & 0 deletions web/src/i18n/pt-PT.strings
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"web_learn_actual" = "A tua saída";
"web_learn_missing_input" = "O teu estrutograma pede mais entradas do que o previsto.";
"web_learn_unused_input" = "O teu estrutograma não pede todas as entradas.";
"web_learn_missing_line_break" = "Quase! Só falta uma quebra de linha. System.out.print continua a escrever na mesma linha, System.out.println começa depois uma linha nova.";
"web_learn_too_many_steps" = "Parado após 100 000 passos. Há um ciclo que nunca termina?";
"web_learn_score_fmt" = "%d de %d certas";
"web_learn_cloze_hint" = "Escreve as palavras que faltam nos espaços e carrega em “Verificar”.";
Expand Down
4 changes: 3 additions & 1 deletion web/src/ui/learnPanel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { checkExercise, fileLines, loopDepth, usedElementTypes, type TestOutcome } from '../core/exerciseCheck'
import { checkExercise, fileLines, loopDepth, onlyLineBreaksMissing, usedElementTypes, type TestOutcome } from '../core/exerciseCheck'
import { compressDrive } from '../core/mbot'
import { isPlaceholderElement } from '../core/editing'
import { ElementType, makeElement, type VSDocument } from '../core/model'
Expand Down Expand Up @@ -778,6 +778,8 @@ export class LearnPanel {
h('span', {}, `📄 ${f.name}, ${t('web_learn_actual')}: `, h('code', {}, fileLines(o.result.files?.get(f.name)).join(' | ') || '–')),
)
}
if (!o.passed && !o.result.error && onlyLineBreaksMissing(o.result.outputs, o.test.expected))
detail.append(h('span', { class: 'learn-error' }, t('web_learn_missing_line_break')))
if (o.result.error) detail.append(h('span', { class: 'learn-error' }, noticeText(o.result.error)))
if (o.result.missingInput) detail.append(h('span', { class: 'learn-error' }, t('web_learn_missing_input')))
if (o.result.tooManySteps) detail.append(h('span', { class: 'learn-error' }, t('web_learn_too_many_steps')))
Expand Down
18 changes: 18 additions & 0 deletions web/test/javaParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { documentFromJava, mergeLiveJava, plainLiveJava, type JavaForm } from '.
import { ElementType, makeElement, type VSDocument } from '../src/core/model'
import { decodeDocument } from '../src/core/xmlCodec'
import { SimulationEngine } from '../src/core/simulation'
import { onlyLineBreaksMissing, sameOutput } from '../src/core/exerciseCheck'
import { generateCode } from '../src/core/codeGenerator'
import { indentJava } from '../src/core/javaIndent'

Expand Down Expand Up @@ -154,6 +155,23 @@ describe('Java tippen → Struktogramm', () => {
expect(keys('System.out.println("Scanner");')).toEqual([])
})

it('\\n in einer Ausgabe beginnt eine neue Zeile; fehlende Zeilenumbrüche werden erkannt', () => {
const run = (src: string) => {
const engine = new SimulationEngine(doc(src))
let steps = 0
while (engine.canStep && steps++ < 1000) engine.step()
return engine.state.outputLines
}
const expected = ['Ludwig ist 7 Jahre alt.', 'Größe: 1.2 m']
expect(run('System.out.print("Ludwig ist 7 Jahre alt.\\n");\nSystem.out.print("Größe: 1.2 m");')).toEqual(expected)
expect(run('System.out.println("A\\nB");\nSystem.out.print("C");')).toEqual(['A', 'B', 'C'])
const joined = run('System.out.print("Ludwig ist 7 Jahre alt.");\nSystem.out.print("Größe: 1.2 m");')
expect(sameOutput(joined, expected)).toBe(false)
expect(onlyLineBreaksMissing(joined, expected)).toBe(true)
expect(onlyLineBreaksMissing(['Ludwig ist 7 Jahre alt. Größe: 1.3 m'], expected)).toBe(false)
expect(onlyLineBreaksMissing(expected, expected)).toBe(false)
})

it('System.out.print schreibt ohne neue Zeile, auch mit Array-Fach', () => {
const d = doc(
'int[] intAllgemeinFach = {3, 5};\n' +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ const e=`/* Texte, die es nur in der Web-Version gibt. Alle anderen kommen aus d
"web_learn_actual" = "Deine Ausgabe";
"web_learn_missing_input" = "Dein Struktogramm fragt nach mehr Eingaben als vorgesehen.";
"web_learn_unused_input" = "Dein Struktogramm fragt nicht alle Eingaben ab.";
"web_learn_missing_line_break" = "Fast! Es fehlt nur ein Zeilenumbruch. System.out.print schreibt weiter in derselben Zeile, System.out.println beginnt danach eine neue.";
"web_learn_too_many_steps" = "Abbruch nach 100 000 Schritten. Läuft eine Schleife endlos?";
"web_learn_score_fmt" = "%d von %d richtig";
"web_learn_cloze_hint" = "Tippe die fehlenden Wörter in die Lücken und drücke „Prüfen“.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ const e=`/* Texts that exist only in the web version. Everything else comes from
"web_learn_actual" = "Your output";
"web_learn_missing_input" = "Your structure chart asks for more input than planned.";
"web_learn_unused_input" = "Your structure chart does not ask for all inputs.";
"web_learn_missing_line_break" = "Almost! Only a line break is missing. System.out.print keeps writing on the same line, System.out.println starts a new line afterwards.";
"web_learn_too_many_steps" = "Stopped after 100,000 steps. Is a loop running forever?";
"web_learn_score_fmt" = "%d of %d correct";
"web_learn_cloze_hint" = "Type the missing words into the gaps and press “Check”.";
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2,450 changes: 1,226 additions & 1,224 deletions website/app/assets/index-BzOyQMjP.js → website/app/assets/index-BTRPiUbu.js

Large diffs are not rendered by default.

Loading
Loading