Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2eafb29
Fixed city rendering, multiple files, layout algorithm, improved sorting
liviacutra Mar 19, 2026
8ad2221
Add Python city support and example workspaces
arjun52 Mar 24, 2026
bf195bc
integrated webview manager to work with webviewview alongside panel. …
logann-tom Mar 25, 2026
599fe7a
Merge branch 'main' into fileWatcherFixes
logann-tom Mar 25, 2026
9984b05
removed unecessary code
logann-tom Mar 25, 2026
4f1fa6c
Merge branch 'fileWatcherFixes' of github.com:pdsl2005/codescape into…
logann-tom Mar 25, 2026
6f37f19
Merge branch 'pythonparser-master' into scrum-162-frontend-schema-bri…
arjun52 Mar 25, 2026
d10838e
Merge pull request #45 from pdsl2005/scrum-162-frontend-schema-bridging
arjun52 Mar 25, 2026
e01166d
Update src/extension.ts
logann-tom Mar 25, 2026
1567612
Update src/extension.ts
logann-tom Mar 25, 2026
0d287a1
Merge pull request #41 from pdsl2005/fileWatcherFixes
jan3tlim Mar 25, 2026
6217e1a
Merge branch 'main' into file-discovery-bugs
livia-cutra Mar 25, 2026
e431159
Merge pull request #36 from pdsl2005/file-discovery-bugs
livia-cutra Mar 25, 2026
7e8628e
organized files into extension, web, and media(shared rendering code)…
logann-tom Mar 26, 2026
e03ad76
implemented placer in javascript to allow for compatability with webv…
logann-tom Mar 31, 2026
171f45a
Merge origin/main into pythonparser-master
arjun52 Apr 1, 2026
d67233b
Fix WebviewPanel ready-state race condition and dead ternary in cityW…
Jackfu13 Apr 1, 2026
298195e
Merge pull request #49 from pdsl2005/pythonparser-master
arjun52 Apr 1, 2026
ad3f313
Moved gethtml to webview manager
logann-tom Apr 1, 2026
b27d091
merged main into branch:
logann-tom Apr 1, 2026
83c1fdd
merged main into renderer refactor for updated html
logann-tom Apr 2, 2026
eb0724b
refactored html so webviews html only handles event listeners
logann-tom Apr 3, 2026
23fbf4e
Update extension/src/cityWebviewHtml.ts
logann-tom Apr 3, 2026
4fe06cf
Update .github/workflows/ci.yml
logann-tom Apr 3, 2026
6d1ebc8
removed placer.js because its purpose is redundant
logann-tom Apr 3, 2026
e39f12b
t
logann-tom Apr 3, 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
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: CI

on:
push:
branches: [main]
paths:
- 'media/**'
- 'extension/**'
pull_request:
branches: [main]
paths:
- 'media/**'
- 'extension/**'

Comment on lines +4 to +14
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow only runs when media/** changes due to the paths filters, but it compiles the extension. Extension changes in PRs won’t be built/checked by CI. Remove the paths restriction or include extension/** so the compile step runs for relevant changes.

Copilot uses AI. Check for mistakes.
jobs:
extension:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install extension deps
run: cd extension && npm install
- name: Compile extension
run: cd extension && npm run compile
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
"--extensionDevelopmentPath=${workspaceFolder}/extension"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
"${workspaceFolder}/extension/out/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
}
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Codescape

Codescape is a VS Code extension that parses Java code and renders it as an isometric city.
Codescape is a VS Code extension that parses Java and Python code and renders it as an isometric city.

- Buildings represent classes/interfaces.
- Height is based on methods + fields.
Expand All @@ -15,6 +15,7 @@ Active prototype with working parser, watcher, relationship graph, and canvas re
| Area | Status | Notes |
|---|---|---|
| Java parsing (Tree-sitter) | Implemented | Classes/interfaces, methods, fields, constructors |
| Python parsing (Tree-sitter) | Implemented | Classes, module nodes, methods, fields, inheritance |
| Relationship graph | Implemented | Extends/implements/field/ctor dependencies |
| Incremental updates | Implemented | Watcher emits `PARTIAL_STATE` |
| Explorer webview | Implemented | `codescape.Cityview` |
Expand Down Expand Up @@ -43,6 +44,10 @@ npm run compile
- Open the explorer view `Codescape City`, or
- Run command `Create Panel` (`codescape.createPanel`).

Known-good example workspaces:
- `examples/java-city`
- `examples/python-city`

## Development

- Watch compile:
Expand Down
23 changes: 16 additions & 7 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
### Prerequisites
- VS Code `^1.74.0`
- Node.js + npm
- Java project/workspace with `.java` files
- Java and/or Python project/workspace with `.java` / `.py` files

### Build and run from source
1. Clone and enter repo.
Expand Down Expand Up @@ -37,7 +37,7 @@ Placement options in VS Code:

## 3) What the Visualization Means

- Building = Java class/interface (`ClassInfo`).
- Building = parsed Java or Python entity (`ClassInfo`).
- Building height = complexity proxy (`methods + fields`, min 1).
- Building color = relationship signal (intended UX) and currently implemented as stable per-class palette assignment.
- Related classes = computed in backend (`relations.ts`) for incremental updates.
Expand All @@ -56,7 +56,7 @@ Important current behavior:

## 5) Incremental Updates

Codescape watches `**/*.java` and sends partial updates:
Codescape watches `**/*.java` and `**/*.py` and sends partial updates:

- File changed:
- Re-parse file.
Expand Down Expand Up @@ -92,20 +92,29 @@ Runtime-registered internal commands (not contributed in `package.json`):
## 8) Practical Workflow

1. Start extension host (`F5`).
2. Open Java workspace in extension host window.
2. Open a Java or Python workspace in extension host window.
3. Open Codescape via sidebar or `Create Panel` command.
4. Edit/save Java files and observe updates.
4. Edit/save Java or Python files and observe updates.
5. Use zoom wheel to inspect layout.

## 9) Troubleshooting

- City view not updating:
- Confirm `.java` file is not excluded by `.exclude`.
- Confirm the `.java` or `.py` file is not excluded by `.exclude`.
- Check extension host logs for parser errors.

- Empty view:
- Verify workspace has Java files.
- Verify workspace has Java or Python files.
- Run `codescape.scan` (re-parses backend store; current implementation may still require reopening/reloading view to reflect a full-state refresh).

- Commands missing:
- Recompile (`npm run compile`) and relaunch extension host.

## 10) Example Workspaces

For a known-good visual smoke test, open one of these folders in the extension host:

- `examples/java-city`
- `examples/python-city`

Both are intentionally small so the generated city stays compact and in view with the current layout algorithm.
11 changes: 11 additions & 0 deletions examples/java-city/RouteMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
public class RouteMap {
private int districtCode;

public RouteMap(int districtCode) {
this.districtCode = districtCode;
}

public int getDistrictCode() {
return districtCode;
}
}
11 changes: 11 additions & 0 deletions examples/java-city/ShuttleService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
public class ShuttleService {
private TransitHub hub;

public ShuttleService(TransitHub hub) {
this.hub = hub;
}

public TransitHub getHub() {
return hub;
}
}
29 changes: 29 additions & 0 deletions examples/java-city/TransitHub.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
public class TransitHub {
private RouteMap routeMap;
private Gate primaryGate;

public TransitHub(RouteMap routeMap, Gate primaryGate) {
this.routeMap = routeMap;
this.primaryGate = primaryGate;
}

public RouteMap getRouteMap() {
return routeMap;
}

public Gate getPrimaryGate() {
return primaryGate;
}

static class Gate {
private int gateNumber;

Gate(int gateNumber) {
this.gateNumber = gateNumber;
}

public int getGateNumber() {
return gateNumber;
}
}
}
5 changes: 5 additions & 0 deletions examples/python-city/city_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DEFAULT_BLOCKS = 12


def estimate_blocks(distance):
return (distance + DEFAULT_BLOCKS - 1) // DEFAULT_BLOCKS
15 changes: 15 additions & 0 deletions examples/python-city/network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class BaseStation:
def __init__(self, district):
self.district = district

def label(self):
return f"Station<{self.district}>"


class DispatchCenter(BaseStation):
def __init__(self, district):
super().__init__(district)
self.active_routes = 3

def assign_route(self, route_name):
return f"{route_name}:{self.active_routes}"
6 changes: 6 additions & 0 deletions examples/python-city/route_map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class RouteMap:
def __init__(self):
self.routes = ["A", "B"]

def count(self):
return len(self.routes)
File renamed without changes.
File renamed without changes.
5 changes: 0 additions & 5 deletions package-lock.json → extension/package-lock.json

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

File renamed without changes.
43 changes: 22 additions & 21 deletions src/JavaFileWatcher.ts → extension/src/JavaFileWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,26 @@ import { parseAndStore } from './parser';
import { ClassInfo } from './parser/javaExtractor';
import { buildGraph, getRelated } from './relations';
import { WebviewManager } from './WebviewManager';

type IncrementalChangePayload = {
changed?: ClassInfo[];
related?: ClassInfo[];
removed?: string[];
};
import { computeCityLayout } from './cityLayout';
import type { PartialStatePayload } from './types/messages';
export class JavaFileWatcher {
private _watcher: vscode.FileSystemWatcher;
private _pythonWatcher: vscode.FileSystemWatcher;
private _javaWatcher: vscode.FileSystemWatcher;
private _pythonWatcher : vscode.FileSystemWatcher;

constructor(store: FileParseStore, private webviewManager: WebviewManager) {
this._watcher = vscode.workspace.createFileSystemWatcher('**/*.java');
this._javaWatcher = vscode.workspace.createFileSystemWatcher('**/*.java');

this._watcher.onDidChange(async (uri: vscode.Uri) => {
this._javaWatcher.onDidChange(async (uri: vscode.Uri) => {
console.log('Java file changed:', uri.fsPath);
this.handleIncrementalChange(uri, store);
});

this._watcher.onDidDelete((uri: vscode.Uri) => {
this._javaWatcher.onDidDelete((uri: vscode.Uri) => {
console.log('Java file deleted:', uri.fsPath);
const before = store.get(uri);
const removedNames = (before?.data ?? []).map((c: ClassInfo) => c.Classname);
store.remove(uri);
this.postIncrementalChange({ removed: removedNames });
this.postIncrementalChange(this.buildPartialStatePayload([], removedNames, store));
});

// Python file watcher — same incremental pipeline as Java
Expand All @@ -49,37 +45,42 @@ export class JavaFileWatcher {
const before = store.get(uri);
const removedNames = (before?.data ?? []).map((c: ClassInfo) => c.Classname);
store.remove(uri);
this.postIncrementalChange({ removed: removedNames });
this.postIncrementalChange(this.buildPartialStatePayload([], removedNames, store));
});
}
private buildPartialStatePayload(
changedClasses: ClassInfo[],
removedNames: string[],
store: FileParseStore
): { changed: ClassInfo[]; related: ClassInfo[]; removed: string[] } {
): PartialStatePayload {
const allClasses = store.snapshot().flatMap(e => e.entry.data ?? []);
const layout = computeCityLayout(allClasses);
const graph = buildGraph(allClasses);
const changedNames = changedClasses.map(c => c.Classname);
const relatedNames = getRelated([...changedNames, ...removedNames], graph);
const relatedClasses = allClasses.filter(c => relatedNames.includes(c.Classname));
return { changed: changedClasses, related: relatedClasses, removed: removedNames };
return {
changed: changedClasses,
related: relatedClasses,
removed: removedNames,
fullClasses: allClasses,
layout,
};
}

private async handleIncrementalChange(uri: vscode.Uri, store: FileParseStore) {
if (!await isExcluded(uri)) {
const { changed, removed } = await parseAndStore(uri, store);
//create payload from parsed data
const payload: IncrementalChangePayload = this.buildPartialStatePayload(changed, removed, store);
//send message to frontend
const payload = this.buildPartialStatePayload(changed, removed, store);
this.postIncrementalChange(payload);
}
}
private async postIncrementalChange(payload: IncrementalChangePayload) {
private postIncrementalChange(payload: PartialStatePayload): void {
this.webviewManager.broadcastPartialState(payload);
}

dispose() {
this._watcher.dispose();
this._javaWatcher.dispose();
this._pythonWatcher.dispose();
}
}
}
Loading