Caspian VS Code Extension
Caspian is a VS Code extension for Caspian Python components, PulsePoint client code, and file-based routing.
Caspian projects are Python-only. Pages, layouts, and components are .py files, and all markup lives inside html(r"""...""") calls. This extension makes that embedded markup feel like a first-class HTML document: syntax highlighting, completions, hover, and navigation, without leaving the Python file.
Highlights
- HTML syntax highlighting inside
html(r"""...""") — tags, attributes, <script> JavaScript, <style> CSS, Jinja {{ ... }} / {% ... %}, and PulsePoint { ... } expressions
- Single-brace PulsePoint expressions with IntelliSense and hover
- PulsePoint script tooling for
pp. and this. inside <script> blocks
- PulsePoint and Caspian directive completions inside tags and
<template> tags
- Component tooling powered by
settings/component-map.json:
x-* component tag and prop completions
- auto-import edits that insert the matching
from ... import ... line
- hover and go-to-definition for component tags and Python import lines
- Route and asset tooling powered by
settings/files-list.json:
- completions, hover, definitions, and document links
- support for markup links,
pp.redirect("..."), and Python redirect("...")
- Global helper IntelliSense from
.casp/global-functions.d.ts
- PulsePoint state rename support inside a markup block
caspcomp Python snippet for authoring Caspian components
There are no diagnostics. The extension does not validate markup, props, imports, or routes — it only helps you read and write them.
Activation Model
The extension has two modes:
1) Full workspace mode
Full Caspian services boot only when the workspace root contains:
{}
saved as caspian.config.json.
When that file exists, the extension enables:
- component map loading and watchers
- markup completions, hover, definitions, document links, and rename
- route and asset tooling
- global helper loading from
.casp/global-functions.d.ts
2) Snippet-only mode
Without caspian.config.json, the full Caspian providers stay dormant.
The caspcomp snippet still works in:
- Python files
- plain text / untitled files
Syntax highlighting inside html(r"""...""") is a TextMate grammar, so it always works — no workspace config required.
Markup Blocks
A markup block is any html(...) call whose first argument is a triple-quoted string. All of these are recognized:
def page():
return html(r"""
<div>Hello</div>
""")
@component
def Card(**props):
return html(r"""
<div {{ attributes }}>{{ children }}</div>
""",
**{"attributes": attributes, "children": children},
)
def layout():
return html(r"""
<slot />
"""), {"title": "Dashboard"}
script = html(r"""
<script>pp.effect(() => {});</script>
""")
The @component decorator is not required — page(), layout(), module-level assignments, and nested calls such as Markup(html(r"""...""")) all work.
Current Feature Set
1) Highlighting
Inside a markup block:
| Construct |
Highlighted as |
<div class="..."> |
HTML |
<script> contents |
JavaScript |
<style> contents |
CSS |
onclick="doThing()" |
JavaScript |
{count + 1} |
JavaScript expression |
{{ children }}, {{ value \| e }} |
Jinja expression |
{% if x %}, {# note #} |
Jinja statement / comment |
<div {{ attributes }}> |
Jinja expression (not attribute names) |
pp-for, pp-ref, … |
PulsePoint attribute |
Everything outside the triple-quoted string keeps normal Python highlighting, including trailing tuple returns and keyword arguments after the closing """.
2) Template expressions
Caspian treats single-brace expressions as JavaScript-like template expressions:
return html(r"""
<script>
const [count, setCount] = pp.state(0);
function increment() {
setCount(count + 1);
}
</script>
<div>{count + 1}</div>
""")
What the extension provides:
- completions for state variables discovered from the block's
<script>
- completions for functions discovered from the block's
<script>
- completions for common JS globals such as
Date, Math, JSON, and console
- object-property completions for state like
{user.}
- loop-alias completions inside
pp-for scopes
- hover for state variables and functions inside mustache expressions
- go-to-definition from a mustache expression to its
pp.state(...) declaration
Typing pp. or this. offers the current PulsePoint API surface:
state, effect, layoutEffect, ref, memo, callback, reducer, context, provideContext, portal, props, createContext, mount, redirect, rpc, enablePerf, disablePerf, getPerfStats, resetPerfStats
Hover documentation is available for PulsePoint methods.
4) PulsePoint attribute completions
For ordinary tags:
pp-component, pp-spread, pp-ref, pp-style, pp-loading-content, pp-loading-url, pp-loading-transition, pp-reset-scroll, pp-spa
For <template> tags:
pp-for, pp-owner
When the cursor is inside a pp-for loop scope, it also suggests a key="..." attribute.
5) Components
Component tooling is driven by settings/component-map.json.
Components are imported with ordinary Python imports and used as x-* tags:
from src.components.Button import Button
from src.components.Card import Card as ProfileCard
def page():
return html(r"""
<x-button size="sm"></x-button>
<x-profile-card title="Hello" />
""")
The tag mapper is strict:
Button becomes x-button
ProfileCard becomes x-profile-card
- PascalCase tag usage is not treated as a component tag
What the extension does:
- component tag completion for
x-* tags
- auto-import: selecting an un-imported component inserts
from <importRoute> import <Name> after the existing imports
- prop-name completion for imported components
- literal and boolean-ish value suggestions when prop metadata provides options or
Literal[...] / bool
- hover for component tags, component props, and component symbols on Python import lines
- go-to-definition from a component tag or from a symbol on a Python import line
6) Global helper functions
If the workspace contains .casp/global-functions.d.ts, the extension loads helper signatures and exposes them inside markup expressions and <script> blocks, with completion, hover, and go-to-definition.
The loader expects definitions in this shape:
// @source: ./helpers/format
const formatPrice: (value: number) => string;
The @source comment is used to resolve definition targets.
7) File-based routing and asset intelligence
Routing and asset tooling is driven by settings/files-list.json.
Recognized route entries are derived from:
src/app/index.py -> /
src/app/(auth)/signin/index.py -> /signin
src/app/users/[id]/index.py -> /users/{id}
src/app/docs/[...slug]/index.py -> /docs/{...slug}
Route groups such as (auth) are ignored in the public URL.
Current route and asset support includes:
- completions in double-quoted
href="..." inside markup
- completions in double-quoted
src="..." inside markup
- completions in
pp.redirect("...") inside markup
- completions in Python
redirect("...") outside markup
- hover for route targets and static assets
- go-to-definition for routes and assets
- document links for
href routes and src assets
Dynamic routes are inserted as snippets so you can tab through parameters.
The provider also supports query-parameter fallback matching, so links such as /users?id=123 can still resolve to /users/{id} when the route metadata matches.
8) Rename support for PulsePoint state
Inside a markup block, the rename provider supports PulsePoint-oriented symbol updates:
- renaming a program-scope
pp.state(...) variable updates both the state variable and its derived setter
- matching references in the block's
<script> are updated
- matching references in the markup are updated
- local script-scope renames stay local instead of rewriting markup state names
Rename is scoped to the markup block under the cursor, not the whole workspace.
9) Python component authoring
In Python or plain text files, typing caspcomp inserts a Caspian component scaffold built around **props, children, class merging, attribute rendering, and an html(r"""...""") body.
Supported File Types
- Python: the full Caspian feature set
- Plain text / untitled:
caspcomp snippet only
HTML files are not supported. Caspian projects no longer contain them.
Caspian intentionally depends on explicit generated metadata instead of scanning the whole workspace on every keystroke.
1) caspian.config.json
This file is the switch that enables full Caspian workspace services.
2) settings/component-map.json
This file should contain an array of component records similar to:
[
{
"componentName": "Button",
"filePath": "C:/project/src/components/Button.py",
"relativePath": "src/components/Button.py",
"importRoute": "src.components.Button",
"acceptsArbitraryProps": true,
"props": [
{
"name": "size",
"type": "Literal['sm', 'lg']",
"hasDefault": false,
"options": ["sm", "lg"]
}
]
}
]
It powers component tag and prop completion, auto-imports, and hover/definition resolution.
3) settings/files-list.json
This file should contain a JSON array of known project files. Both Windows and POSIX-style paths are normalized.
It powers route completion, static asset completion, and hover/definitions/document links for routes and assets.
4) .casp/global-functions.d.ts (optional)
This file is optional. If present, it powers global helper completion, hover, and definitions.
All of these metadata files are watched and reloaded automatically when they change.
Recommended VS Code Settings
Markup lives inside Python strings, so quick suggestions in strings must be enabled for completions to trigger:
"editor.quickSuggestions": {
"strings": "on",
"other": "on"
}
Known Limitations
- Full Caspian services do not boot until
caspian.config.json exists at the workspace root.
- Route and asset tooling targets double-quoted
href and src attributes.
pp.redirect("...") and Python redirect("...") support targets string literals, not arbitrary computed expressions.
- Route detection is intentionally opinionated around
src/app/**/index.py entry files.
- Markup blocks are found by scanning for
html( followed by a triple-quoted string, so an html( mentioned inside a comment or another string can be picked up.
- If
settings/component-map.json, settings/files-list.json, or .casp/global-functions.d.ts are missing or stale, related suggestions and navigation will be incomplete.
Roadmap
- richer component documentation in hover text
- smarter route refactors and rename/move workflows
- better multi-root workspace behavior
- workspace bootstrap tooling for generating Caspian metadata files
Contributing
Contributions are welcome.
- Open an issue with a minimal repro.
- Submit a focused PR with tests when behavior changes.