Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
184276c
Encapsualtion and WebDev inclusion in README
simonroeschhso Mar 5, 2026
baf7e40
Moved last html out of webview.ts
simonroeschhso Mar 6, 2026
5095b63
Merge branch 'master' into encapsulate-visualization
skogsbaer Mar 17, 2026
16fae7f
Added and enhanced documentation
simonroeschhso Mar 19, 2026
c467fb5
Improvemnets
simonroeschhso Mar 31, 2026
5bbf0f6
Further simplification and dead code removal
simonroeschhso Mar 31, 2026
635e335
README adjustment
simonroeschhso Mar 31, 2026
0cdf670
Merge branch 'skogsbaer:master' into encapsulate-visualization
simonroeschhso Mar 31, 2026
864e602
minor fixes after review
skogsbaer Apr 17, 2026
24e0a26
Merge branch 'master' into encapsulate-visualization
skogsbaer Apr 20, 2026
01b29bc
elk-restyling start and plan
simonroeschhso Sep 9, 2026
e295780
finished plan (including reachability.ts)
simonroeschhso Sep 11, 2026
68d45f5
spike done
simonroeschhso Sep 11, 2026
29db78c
implementation step
simonroeschhso Sep 11, 2026
9997071
switch to elk and collapsable
simonroeschhso Sep 11, 2026
d4e47de
added pan/zoom
simonroeschhso Sep 11, 2026
375a269
styling pass
simonroeschhso Sep 11, 2026
d57cfb0
unit-tests and cleanup
simonroeschhso Sep 11, 2026
b98750f
screenshot of new visualization view
simonroeschhso Sep 11, 2026
ad100b9
top-right tool-bar and +/- zoom buttons
simonroeschhso Sep 18, 2026
5e079cc
BugFixes: Step Buttons Z > stdout, Chevron resize dimming
simonroeschhso Sep 18, 2026
6f8ad74
frames background color Blue
simonroeschhso Sep 18, 2026
41b7ef4
frames stack on top of each other chronologically
simonroeschhso Sep 21, 2026
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
27 changes: 26 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,32 @@
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/naming-convention": "warn",
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "default",
"format": ["camelCase"],
"leadingUnderscore": "allow",
"trailingUnderscore": "allow"
},
{
"selector": "variable",
"format": ["camelCase", "UPPER_CASE"],
"leadingUnderscore": "allow",
"trailingUnderscore": "allow"
},
{
"selector": "typeLike",
"format": ["PascalCase"]
},
{
// Keys of a foreign API, quoted because they have to be - the dotted ELK
// layout options have no camelCase spelling to pick.
"selector": ["objectLiteralProperty", "typeProperty"],
"modifiers": ["requiresQuotes"],
"format": null
}
],
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,14 @@ You can debug the extension from Visual Studio Code:
* Open the main folder of the plugin with vscode.
* Open the file `extension.ts`.
* Choose "Run" from the menu, then "Start Debugging".

## WebDev

You can develop the design of the visualization using an example trace in your browser:

* `npm install`
* `npm run build`
* `npm run watch:web` this starts a process which will build the contents of `src/programflow-visualization/web` into `out/programflow-visualization/web` on any changes
* In console: `cd out/programflow-visualization/web` + `python3 -m http.server 5173`
* Then open http://localhost:5173/index.web.html in your browser
* Now you can edit files in `src/programflow-visualization/web`, watch:web will rebuild automatically and you can refresh your browser tab to see the changes instantly
Binary file added elk-task/current.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
733 changes: 733 additions & 0 deletions elk-task/elk-plan.md

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions elk-task/example-anonymous.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from wypp import *

# Definition-of-done case: objects that are reachable ONLY through another object.
#
# In example.py every Student is also bound to a module-level global, so it stays
# reachable from a stack root no matter what you collapse - collapsing a list there
# hides edges but removes no nodes. Here nothing inside the containers has a name of
# its own, so collapsing the container must make its contents disappear.

@record
class Student:
name: str
grade: float


# Nested lists, no names on the inner lists.
# Collapsing `data` must remove both inner lists.
data = [[1, 2], [3, 4]]

# Anonymous instances. Collapsing `group` must remove both Students.
group = [Student('Anna', 1.0), Student('Ben', 2.3)]

# One shared object for contrast: `shared` keeps its own name, so collapsing
# `holder` must NOT remove it.
shared = Student('Cleo', 1.7)
holder = [shared]

# A container that is itself anonymous: only reachable through `outer`.
outer = [[shared, Student('Dan', 3.0)]]


def summarize(students: list[Student]) -> list[float]:
grades = []
for student in students:
grades.append(student.grade)
return grades


groupGrades = summarize(group)
28 changes: 28 additions & 0 deletions elk-task/example-cycles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from wypp import *

# Definition-of-done case: heap shapes that example.py never produces.
# Covers reference cycles (the traversal must terminate), dicts with reference
# keys and reference values, tuples, and sets.

# Self-referencing list: selfRef[0] is selfRef.
selfRef = []
selfRef.append(selfRef)

# Two-object cycle: left -> right -> left.
left = []
right = [left]
left.append(right)

# Dict with reference values, both anonymous -> collapsing `lookup` removes them.
lookup = {'evens': [2, 4], 'odds': [1, 3]}

# Dict with reference keys (tuples are hashable, and appear as heap objects).
byPair = {(1, 2): ['a'], (3, 4): ['b']}

# Set of plain values, and a tuple mixing references with plain values.
letters = {'a', 'b', 'c'}
mixed = (selfRef, 42, 'text')

# A dict pointing at something that also has its own name.
named = [1, 2, 3]
container = {'named': named, 'anonymous': [4, 5, 6]}
21 changes: 21 additions & 0 deletions elk-task/example-error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from wypp import *

# Definition-of-done case: the trace ends in an exception.
# The visualization must still render every step up to the failure, show the
# traceback in the output pane, and not break on the partial trace.

@record
class Student:
name: str
grade: float


students = [Student('Anna', 1.0), Student('Ben', 2.3)]
grades = []

for student in students:
grades.append(student.grade)

# Fails: only two grades were collected.
third = grades[2]
print(third)
104 changes: 104 additions & 0 deletions elk-task/example-showcase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from wypp import *

# A tour of everything the visualization can draw.
#
# Unlike the other three examples, this one is not a definition-of-done case - it exists
# to be looked at. Each block puts one particular shape on screen; the comment says what
# to look for. Step to the end first, then walk backwards.


@record
class Book:
title: str
pages: int
rating: float


# --- One node of every kind, so the colour accents can be compared side by side ------
# instance (blue), list (green), tuple (purple), dict (orange), set (yellow).

dune = Book('Dune', 412, 4.5)
sizes = (13, 21)
tags = {'scifi', 'classic', 'reread'}
byTitle = {'Dune': dune}


# --- Plain values, one of each type the renderer formats ------------------------------
# Note that None prints as None rather than as an empty cell.

count = 3
average = 4.25
shelfName = 'Reading list'
finished = True
nextUp = None


# --- Collapsing: nothing in here has a name of its own --------------------------------
# Collapse `shelf` (click its header) and both Books disappear with it, because no other
# arrow reaches them.

shelf = [Book('Emma', 474, 4.0), Book('Ulysses', 730, 3.2)]


# --- ... but a shared object survives -------------------------------------------------
# `dune` has a name of its own and `byTitle` also points at it, so collapsing
# `favourites` removes only the arrow, not the node.

favourites = [dune, Book('Solaris', 204, 4.1)]


# --- Dicts whose keys are references ---------------------------------------------------
# A reference key has no text, so the cell reads [key] and grows its own arrow.

ratingOf = {(4, 5): 'great', (1, 2): 'poor'}

# Here key *and* value are references, so the row sends out two arrows from
# different heights.

pairs = {sizes: shelf}


# --- Cycles: the edges come back on themselves -----------------------------------------

chain = []
chain.append(chain)

alpha = []
beta = [alpha]
alpha.append(beta)


# --- A tall node, to show the row striping ---------------------------------------------

pageCounts = [412, 474, 730, 204, 96, 288, 350, 512]


# --- Several frames at once -------------------------------------------------------------
# While `describe` runs there are three frames on screen: Global, report and describe.
# Only the innermost one gets the current-frame accent. Watch the `return` row appear in
# a frame just before it disappears - it is the one row drawn in the accent colour.

def describe(book: Book) -> str:
return book.title + ' (' + str(book.pages) + ' pages)'


def report(books: list[Book]) -> list[str]:
lines = []
for book in books:
lines.append(describe(book))
return lines


def longest(books: list[Book]) -> Book:
best = books[0]
for book in books:
if book.pages > best.pages:
best = book
return best


summary = report(favourites)
biggest = longest(favourites)

print(summary)
print(biggest.title)
Loading