Virgil Developer Guide
March 17, 2026 · View on GitHub
type: developer-onboarding remote: git@github.com:ealt/virgil.git commit: f5eb6c7a2d4ba3289b9003ed8c53e00ce7659684
A comprehensive guide to understanding and contributing to the Virgil extension
Welcome to Virgil Development
This walkthrough will help you understand how the Virgil extension works and how to contribute to it.
What you'll learn:
- How the extension activates and detects walkthroughs
- The architecture and key components
- How navigation and highlighting work
- How to extend the extension
Prerequisites:
- Basic TypeScript knowledge
- Familiarity with VS Code extensions
- Understanding of VS Code API basics
Let's start by understanding how the extension activates, or skip ahead to UI Components if you're already familiar with the basics.
Extension Architecture
This section covers the core architecture of the Virgil extension, including how it activates, its entry point, and the data model. For navigation details, see Commands and Navigation.
Extension Activation
Virgil activates on startup. The activationEvents array is intentionally empty, so VS Code loads the extension without waiting for a file match.
Key points:
- Activation is eager (startup), not gated on
workspaceContains virgil.convertMarkdownis registered immediately (it still requires a workspace to run)- Once a workspace folder is available, walkthrough discovery looks in:
.walkthrough.jsonat workspace root- Any
.jsonfiles inwalkthroughs/directory
- The extension watches those JSON files for changes
Extension Entry Point
View code (44-52,55-98,100-155,157-180,207-228)
The activate() function in extension.ts is called when the extension loads.
What happens on activation:
- Creates a
HighlightManagerand reads default view modes from configuration - Registers
virgil.convertMarkdownearly so it's available even before a workspace is detected - Returns early if no workspace folder is open
- Initializes diff support (
DiffContentProvider,DiffResolver) and the markdown highlight provider - Initializes the
WalkthroughProviderand the tree view - Registers commands and a config watcher for step-numbering changes
- Sets up file watching for
.walkthrough.jsonandwalkthroughs/*.json - Auto-shows the first step if
virgil.view.autoShowFirstStepis enabled and a walkthrough exists (and then checks commit mismatch + git user name)
TypeScript Interfaces - Data Model
View code (1-21,68-112,114-130,161-214,216-262,264-276)
The types.ts file defines the shape of walkthrough JSON files and the navigation helpers.
Key interfaces:
Walkthrough- Root object with title, description, repository, metadata, stepsWalkthroughStep- Individual step with id, title, body,location, optionalbase_location, comments,parentIdStepTreeNode- Tree node for hierarchical step displayStepNavigationContext- Precomputed parent/sibling indices for fast navigationComment- User comments with id, author, bodyRepository- Git info (remote URL, head commit SHA, optionalbaseCommit/baseBranch/pr)
Utility functions and types:
parseLocation()- Parses location strings likepath:10-45,100-120buildStepTree()/flattenStepTree()- Build and flatten the hierarchical step treebuildNavigationMap()- Builds O(1) parent/sibling lookup for navigationViewMode,MarkdownViewMode,StepType+getStepType()- Diff/view mode typingisMarkdownFile()/getFileTypeIcon()- File-type helpers for UInormalizeRemoteUrl()- URL normalization helper (currently unused in selection filtering)
These types keep the extension's data model and navigation behavior consistent.
Commands and Navigation
This section covers how commands are registered and how step navigation works.
Command Registration
View code (231-308,310-317,319-341,347-378,379-398,404-413,418-442,444-454,508-549,553-569,571-607)
The extension registers several commands for navigation and control. (virgil.convertMarkdown is registered earlier during activation.)
Navigation commands:
virgil.start- Jump to first stepvirgil.next- Go to next stepvirgil.prev- Go to previous stepvirgil.goToStep- Jump to specific step indexvirgil.goToParent- Navigate to parent step in hierarchyvirgil.nextSibling- Navigate to next sibling step (same level)virgil.prevSibling- Navigate to previous sibling step (same level)
View mode commands (used by the webview):
virgil.setViewMode- Switch diff view mode (diff/head/base)virgil.setMarkdownViewMode- Switch markdown view mode (rendered/raw)
Control commands:
virgil.refresh- Reload walkthrough from filevirgil.selectWalkthrough- Switch walkthroughs or browse for JSON/Markdown and convert if neededvirgil.convertMarkdown- Convert a Markdown walkthrough to JSON (saves towalkthroughs/directory)virgil.submitComment- Add a comment to current stepvirgil.editComment- Update an existing comment on the current stepvirgil.deleteComment- Remove a comment from the current stepvirgil.openLocation- Open file at a specific locationvirgil.checkoutCommit- Checkout the commit specified in the walkthrough
Most commands delegate to WalkthroughProvider for state management, then call showCurrentStep() to update the UI.
File Watching - Auto-refresh
The extension uses VS Code FileSystemWatchers to monitor walkthrough JSON files in two locations:
.walkthrough.jsonat workspace rootwalkthroughs/*.jsonfiles
Event handling:
- onDidChange: Refreshes provider, updates context, and optionally auto-shows the first step
- onDidCreate: Refreshes provider, updates context, optionally auto-shows the first step, then checks commit mismatch and git user name
- onDidDelete: Refreshes provider, clears highlights, and updates context based on remaining walkthroughs
Benefits:
- No manual refresh needed when editing walkthrough JSON
- Automatic detection of new walkthroughs in both locations
- Clean state management when files are deleted
The watcher uses RelativePattern for both the workspace root and the walkthroughs/ directory.
Show Current Step - Core Navigation
View code (695-727,729-789,792-809)
The showCurrentStep() function orchestrates what happens when navigating to a step.
Flow:
- Gets walkthrough, current index, and step label from the provider
- Clears all previous highlights
- Resolves the base commit (if any) and determines the step type
- Builds step anchor links and hierarchical navigation options for the webview
- Shows the appropriate view based on step type:
- diff: opens diff/head/base view based on
currentViewMode - point-in-time: opens the head file with standard highlights
- base-only: opens the base file with base highlights
- informational: no file opened
- diff: opens diff/head/base view based on
- Renders the step detail panel (including errors if a base ref is missing)
Key coordination:
WalkthroughProvidermanages state and step labelsDiffResolverdetermines the base commitHighlightManagerhandles decorationsStepDetailPanelrenders the webview (diff toggle + markdown toggle)
This function is called by all navigation commands to keep the UI in sync.
UI Components
This section covers the main UI components: the sidebar tree view, detail panel, and code highlighting.
WalkthroughProvider - State Management
View code (46-51,53-83,158-186,199-242,266-302,319-329,568-591)
The WalkthroughProvider class implements VS Code's TreeDataProvider interface.
Responsibilities:
- Discovers walkthrough JSON files (
.walkthrough.jsonandwalkthroughs/*.json) - Loads and parses walkthrough files, then builds:
- The hierarchical tree (
buildStepTree) - The flat navigation list (
flattenStepTree) - The parent/sibling lookup map (
buildNavigationMap)
- The hierarchical tree (
- Tracks current step index and step labels (hierarchical numbering if enabled)
- Builds tree items with file-type icons and diff/base warnings
- Manages Git state (commit mismatch checks, checkout, stash, git user name)
- Manages comments (add, edit, delete, persist to JSON)
- Provides step anchor mapping for in-body step links
Key methods:
getAvailableWalkthroughs()- Finds walkthrough JSON filesloadWalkthrough()- Loads JSON and builds tree/flat/nav mapssetWalkthroughFile()- Sets the current walkthrough filegoToStep(),nextStep(),prevStep()- Step navigationgetStepAnchorMap()- Maps step-title anchors to indicesaddComment()/editComment()/deleteComment()- Update comments and save the walkthrough filehasCommitMismatch()/getCommitMismatchInfo()- Commit mismatch checks
Tree View - Building the Sidebar
The getRootItems() and createStepTreeItem() methods build the sidebar tree structure.
Tree structure:
- File selector (always shown) - Shows current file with a count of available walkthroughs
- Title header - Walkthrough title with description or commit mismatch warning
- Steps - Hierarchical tree of steps with parent/child relationships
Hierarchical display:
- Top-level steps (no
parentId) appear at root level - Sub-steps appear indented under their parent
- Steps with children are expandable (default: Expanded)
- Navigation traverses all steps in depth-first order
Step indicators:
- Current step: Green arrow icon + "(current)" label
- Point-in-time steps: File-type icon based on the location path
- Diff steps: File-type icon when recognized, otherwise
git-compare - Base-only steps: File-type icon when recognized, otherwise
history(red) - Informational steps: Note icon
- Diff/Base steps show "⚠️ no base ref" if the repository lacks a base reference
Commit mismatch: If the walkthrough commit doesn't match current HEAD, the title shows a warning icon and tooltip.
The tree view automatically updates when steps change via _onDidChangeTreeData.
StepDetailPanel - Webview UI
The StepDetailPanel creates a webview that displays rich step information.
What it shows:
- Step title and counter (e.g., "Step 2 of 5")
- Metadata (on first step only)
- Diff view toggle (Diff/Head/Base) for diff steps
- Markdown view toggle (Rendered/Raw) for markdown steps
- Clickable location information (head/base or base-only)
- Step body with Markdown rendering
- Comments section with add, edit, and delete controls
- Previous/Next navigation buttons (plus optional parent/sibling controls)
Webview features:
- Opens in
ViewColumn.Two(side panel) retainContextWhenHiddenpreserves state- Uses VS Code CSS variables for theming
- Communicates via
postMessageAPI
Markdown rendering: Uses marked with highlight.js for syntax highlighting in code blocks.
Webview HTML Generation
View code (163-183,194-223,225-336,338-352,353-371,403-439,742-770,772-799,802-848,864-887)
The getHtml() method generates the HTML for the detail panel.
Key features:
- Theming: Uses VS Code CSS variables (
var(--vscode-foreground), etc.) - Markdown rendering: Converts step body and comments from Markdown to HTML (with step-link anchors)
- Security: CSP restricts scripts; raw HTML is stripped to prevent XSS
- Interactivity: JavaScript handles navigation, view toggles, step links, and comment actions
- Diff/error UX: Shows base-ref errors inline and adapts location display per view
Styling:
- Matches VS Code's native UI appearance
- Syntax highlighting uses VS Code-compatible colors
- Supports dark/light themes automatically
Communication: JavaScript uses acquireVsCodeApi() to send messages back to the extension.
HighlightManager - Code Decorations
View code (3-9,16-39,45-79,87-145,147-179,182-207,209-229)
The HighlightManager creates and manages text decorations that highlight code ranges.
Color variants (configurable in virgil.highlights.*):
| Context | Setting key (default) | Notes |
|---|---|---|
| Point-in-time (default) | standard.backgroundColor (#569CDE1A) | Blue |
| Head file (diff mode) | diffHead.backgroundColor (#48B46126) | Green |
| Base file (diff mode) | diffBase.backgroundColor (#DC505026) | Red |
Decoration style:
- Colors are read from settings and converted from hex to RGBA
- Left border accent per color
isWholeLine: truehighlights entire lines- Appears in the overview ruler (minimap) for navigation
State management:
- Tracks decorations per file with color in a
Map<string, { color, ranges }> highlightRange(editor, start, end, color)adds or replaces ranges per colorclearFile()removes decorations for a specific fileclearAll()removes all decorationsrefreshEditor()reapplies decorations when editor is reopened- Configuration changes recreate decoration types and reapply active ranges
Usage: Called by showCurrentStep() to highlight step locations with appropriate colors.
Diff Mode Support
This section covers the components that enable diff-based walkthroughs.
DiffContentProvider - Git File Content
View code (22-26,31-46,51-76,82-93,99-109)
The DiffContentProvider implements VS Code's TextDocumentContentProvider to serve file content from specific Git commits.
URI scheme: virgil-git:///<commit>/<file-path>
Key methods:
createUri(commit, filePath)- Creates a URI for a file at a commitparseUri(uri)- Extracts commit and path from a URIprovideTextDocumentContent(uri)- Returns file content viagit showfileExistsAtCommit(commit, filePath)- Checks if file exists at commit
Usage:
// Create URI for file at specific commit
const uri = DiffContentProvider.createUri('abc123', 'src/auth.ts');
// Open the file
const doc = await vscode.workspace.openTextDocument(uri);
Error handling:
- Invalid commit references
- Files that don't exist at the specified commit
- Git command failures
DiffResolver - Base Reference Resolution
View code (25-69,76-96,101-118,124-160,164-201,204-219)
The DiffResolver resolves and validates base references for diff mode walkthroughs.
Resolution priority:
baseCommit- Direct commit SHAbaseBranch- Resolved to branch's current commitpr- Resolved via GitHub CLI or merge-base fallback
Key methods:
resolveBase(repository)- Returns resolved commit SHA and sourcevalidateSingleBaseRef(repository)- Warns if multiple refs specifiedvalidateStepBaseRef(hasBaseLocation, repository)- Checks step requirementsgetHeadCommit()- Gets current HEAD commit
Usage:
const diffResolver = new DiffResolver(workspaceRoot);
const result = diffResolver.resolveBase(walkthrough.repository);
if (result.commit) {
// Use result.commit as the base reference
// result.source tells you which field it came from
}
Data Persistence
This section covers how data is stored and managed.
Comments System
Users can add, edit, and delete comments on steps, and those changes are persisted to the walkthrough JSON file.
How it works:
- User adds, edits, or deletes a comment in the webview
- Webview sends the matching comment command via
postMessage - Extension command handler calls
WalkthroughProvider.addComment(),editComment(), ordeleteComment() - New comments get:
- Auto-generated ID (timestamp + random)
- Author from
git config user.name(or "Anonymous") - Body text (supports Markdown)
- Walkthrough JSON is saved to disk after each change
- Panel is refreshed to show the latest comment state
Comment features:
- Markdown rendering in comments
- Author attribution
- Persisted to JSON file
- Editable and removable from the webview UI
Git user prompt: On walkthrough load/selection, the extension checks for git config user.name and offers to set it if missing.
Commit Mismatch Detection
The extension can warn users when viewing walkthroughs created for different codebase states.
How it works:
- Walkthrough specifies
repository.commit - Extension compares current HEAD to the walkthrough commit (full SHA)
- If mismatch detected:
- Shows warning dialog with short SHAs
- Offers to checkout the commit (with stash option)
- User can ignore and continue
Git operations:
hasCommitMismatch()/getCommitMismatchInfo()- Compares commits and returns detailsrefreshGitState()- Refreshes current HEAD before comparisonscheckoutCommit()- Checks out specified commitstashChanges()- Stashes uncommitted changesisWorkingTreeDirty()- Checks for uncommitted changes
Benefits:
- Ensures walkthroughs are viewed with correct code
- Prevents confusion from code changes
- Optional (can be ignored if needed)
Repository Metadata
Walkthroughs can include repository metadata to capture the intended code state.
Current fields:
remote- Optional Git remote URL for referencecommit- Head commit SHA for the walkthrough's target statebaseCommit/baseBranch/pr- Optional base reference for diff mode
URL normalization helper: normalizeRemoteUrl() is available to compare SSH/HTTPS URLs, but the current selection logic does not filter walkthroughs by remote.
Utilities and Helpers
This section covers utility functions and parsing logic.
Location Parsing
The parseLocation() function parses location strings into structured data.
Location format: path:startLine-endLine or path:startLine-endLine,startLine-endLine (used by both location and base_location)
Examples:
src/auth.ts:10-45- Single rangesrc/auth.ts:10- Single line (treated as range 10-10)src/auth.ts:10-45,100-120- Multiple ranges (comma-separated)
Parsing logic:
- Finds last
:to separate path from ranges - Splits ranges by comma
- For each range:
- If contains
-, splits into start/end - Otherwise, treats as single line
- If contains
- Returns
ParsedLocationwith path and array of ranges
Error handling: Returns null if format is invalid.
Usage: Used by showCurrentStep() and virgil.openLocation command.
Markdown Parser
View code (144-163,164-223,234-241,243-373,379-388)
The parseMarkdownWalkthrough() function converts Markdown files to walkthrough JSON.
Header hierarchy:
#- Walkthrough title##- Top-level steps###- Sub-steps (children of##)####- Sub-sub-steps (children of###)- And so on up to
######
Parsing features:
- YAML frontmatter for repository fields (
remote,commit,baseBranch,baseCommit,pr) and arbitrary metadata - Location links immediately after headings in the format
[Text (10-20)](file.ts) - Base location links with
[Base (10-20)](file.ts)prefix - Automatic
parentIdassignment based on header nesting - Sequential ID generation (1, 2, 3, 4...)
- Warnings when location links appear in the step body (ignored for parsing)
Example input: A markdown file with ## Login Flow (level 2) followed by ### Token Generation (level 3) produces steps where "Token Generation" has parentId pointing to "Login Flow".
Project Structure Overview
Understanding the project structure helps when contributing.
Key directories:
src/- TypeScript source filesout/- Compiled JavaScript (generated)docs/- Documentation (schema.md, development.md)media/- Assets (currently just panel.css, though styles are inline)
Key files:
package.json- Extension manifest, commands, views, keybindingstsconfig.json- TypeScript configurationextension.ts- Entry point and coordinationtypes.ts- Data model and utilitiesWalkthroughProvider.ts- State and tree viewStepDetailPanel.ts- Webview UIHighlightManager.ts- Code decorationsmarkdownParser.ts- Markdown to JSON conversion
Build output: TypeScript compiles to out/ directory, which is what VS Code loads.
Project Documentation - README
The project's main documentation lives in the README.md file. This section showcases the core features of Virgil.
Tip: When viewing this step, try toggling between Rendered and Raw modes using the Markdown toggle in the detail panel. Rendered mode (the default) shows the markdown preview with highlighted sections, while Raw mode shows the source with line highlighting.
Documentation structure:
README.md- User-facing documentation, installation, usagedocs/schema.md- Complete walkthrough JSON schemadocs/development.md- Development setup and guidelinesdocs/developer-guide.md- This walkthrough (meta!)
When contributing, keep documentation in sync with code changes.
Extending the Extension
This section covers how to add new features and contribute to Virgil.
Adding New Commands
Here's how to add new commands to Virgil:
Steps:
- Add the command to
package.jsonundercontributes.commands(around lines 22-78, plus keybindings if needed) - Register the command in
extension.tswithvscode.commands.registerCommand() - Push the disposable into
context.subscriptionsfor cleanup
Example:
context.subscriptions.push(
vscode.commands.registerCommand('virgil.myCommand', () => {
// Command implementation
})
);
Enhancing the Webview
Modify StepDetailPanel.getHtml() to add new UI elements:
- Add new message handlers in
onDidReceiveMessage - Update CSS for styling
- Use VS Code CSS variables for theming
Feature Ideas
Some ideas for extending Virgil:
- Walkthrough validation: Add schema validation on load
- Export/import: Add commands to export walkthroughs
- Templates: Create walkthrough templates
- Search: Add search functionality for steps
- Multiple walkthroughs: Show multiple walkthroughs simultaneously
Testing
- Use Extension Development Host (
F5) - Test with various walkthrough files
- Test edge cases (missing files, invalid JSON, etc.)
Development Workflow
Here's the recommended workflow for contributing:
Setup:
- Clone repository and run
npm install - Run
npm run watchfor auto-compilation - Press
F5to launch Extension Development Host
Making changes:
- Edit TypeScript files in
src/ - Watch mode automatically recompiles
- Reload Extension Development Host window (
Cmd+R/Ctrl+R) - Test changes with a walkthrough file
Packaging:
- Run
npx vsce package --allow-missing-repository - Install
.vsixfile to test installation
Documentation:
- Update
docs/schema.mdfor schema changes - Update
docs/development.mdfor architecture changes - Update
README.mdfor user-facing changes
See docs/development.md for more details.
Summary and Next Steps
Quick links to key sections:
- Extension Activation - How the extension starts
- TypeScript Interfaces - Data Model - Core data structures
- WalkthroughProvider - State Management - State management
- StepDetailPanel - Webview UI - UI implementation
Key takeaways:
- Extension activates on startup; walkthroughs are discovered from
.walkthrough.jsonandwalkthroughs/*.json WalkthroughProvidermanages state and builds hierarchical sidebarStepDetailPanelshows rich step info in themed webviewHighlightManagerapplies line decorations to editors- All components coordinate through
extension.ts - Steps support hierarchy via
parentIdfield - Navigation traverses all steps in depth-first order
- Comments are persisted to JSON files
- Repository metadata tracks intended commit/base refs (no remote filtering yet)
- Commit mismatch warnings help ensure correct code state
Next steps for contributors:
- Read
docs/development.mdfor detailed setup instructions - Explore the codebase using this walkthrough
- Try making a small change (e.g., add a new command)
- Test thoroughly in Extension Development Host
- Submit a pull request
Resources:
- VS Code Extension API
- VS Code Extension Guidelines
docs/schema.md- Walkthrough JSON formatdocs/development.md- Development guide
Happy contributing!