Editor
AXIS Pine editor: CodeMirror 6, diagnostics, 80-col ruler, git sync, stats, docked vs popout, editor bridge.
This page
The editor is the source axis of intent: Pine text the operator authorizes the engine to run. Completion and hover can use local builtins metadata and optional remote LSP; evaluation is always an engine plugin.
Abstract
| Module | Role |
|---|---|
EditorPane.tsx | Docked/standalone chrome, header tools, detach |
tabbed-editor.tsx | Multi-tab docs, stats strip, Problems, Colors, Symbols, git bar |
PyneEditor.tsx | CM6 mount (extensions + view APIs) |
pyne-language.ts | StreamLanguage / highlighting (namespaces, types, multiline strings) |
pine-symbols.ts + SymbolEmojiManager.tsx | TV-editor-safe glyph catalog + insert panel |
pyne-lsp.ts + data/pyne-builtins.json | Offline completion/hover corpus |
column-ruler.ts | 80-character guide line |
minimap.ts | Optional right-rail scroll overview |
function-outline.ts + FunctionOutline.tsx | Optional right-rail function / type / enum tree |
diagnostics.ts | Underlines, gutter, jump-to from last run |
editor-intel.ts | Settings bag for lint / hover / complete / marks / timings |
inline-debug.ts | EOL chips, pin gutter, line flash |
git-sync.ts + ui/EditorGitBar.tsx | Pull / push / storage status |
ui/EditorProblems.tsx | Collapsible problems list |
editor-bridge.ts | Cross-window doc + run messages |
cm-void.ts | Void-theme CM styling |
Conceptual model
Rendering…
Why run on main? Chart, bars, and pane manager live in the main window. Popout is a pure editing surface.
Interface surface
Docked mode
- Right dock by default (
panelChrome.editor); may sit side-by-side with other right panels (e.g. Indicators left of Editor) - FloatableShell: dock left/right/bottom, float, new window
- Detach → popup or tab (
openEditorWindow)
Popout mode
- Main shows “Editor detached” chip + Reattach
- Shared doc key + bridge messages:
hello,doc,run,run-status,reattach,popout-opened,popout-closed
Run entry points
- Editor / topbar Run →
runAndApply(getDoc()) - Popout Run → bridge → main executes
- Command palette: save library, git push/pull, jump to line, toggles
Header tools (axis-editor-tools)
Icon-only (labels in tooltips / aria-label). Open in new tab is in the panel-icon menu with dock options.
| Control | Effect |
|---|---|
| Run | Run script against loaded bars |
| Scriptlogs | Open last-run log.* panel |
| Profiler | profilerEnabled + optional line % gutter; re-run when enabling |
| Debug | Inline end-of-line log/error chips (inlineDebugEnabled) |
| Pins | Chart markers + 📍 gutter (debugPinsEnabled); Alt+P |
| Ruler | 80-column guide (editorRulerEnabled) |
| Overflow → Minimap | Right-rail canvas overview (editorRightRail: minimap); click/drag scrolls |
| Overflow → Function tree | Right-rail outline of functions, methods, types, enums (editorRightRail: outline); click jumps to the declaration. Mutually exclusive with Minimap |
| Panel icon → Open in new tab | Editor in a full browser tab (vs New window popup) |
Details: Debugging.
Stats strip
Bottom of tabbed editor (data-testid="axis-editor-stats"):
| Field | Meaning |
|---|---|
| Pos L:C | Cursor line:column (1-based) |
| Ln | Total lines in document |
| Words / Chars | Document stats |
| Diagnostics badge | Error/warning count from last run — click jumps to first |
| Problems | Expand/collapse problems list |
| Colors | Color chips / picker / converter for hex and color.* |
| Symbols | Insert TV-editor-safe arrows, box drawing, marks, spaces, and chart emoji (raw, quoted, or plotchar) |
| wrap | Soft wrap toggle (editorWrapEnabled, default on) |
Symbols & emoji
Status-bar Symbols (data-testid="axis-editor-symbols") opens a searchable catalog of glyphs used in Pine Script™ plotchar / label.new / table text.
- Mono-safe — ~1 cell in IBM Plex Mono / the TV editor (box drawing,
▲▼●◆✓★, arrows). - Wide / chart-only — official
🠅🠇and emoji (🔥🚀📈). Filter with Mono-safe. - Click inserts at the cursor (replaces the selection). Right-click copies.
- Insert mode: raw glyph, quoted string, or a
plotchar(cond, "mark", "…", location.belowbar)snippet.
Sources: TradingView® Exploring Unicode, Text and shapes, box-drawing U+2500, PineCoders conventions.
Highlighting
pyne-language.ts is a stateful stream tokenizer: //@ annotations, // and /* */ (including across lines), quoted and """ / ''' strings with \\n escapes, #RRGGBB(AA) colors, namespaces (ta., label.new), types (int / float / string, series / simple in qualifier position, and declared UDTs / enums as typeName, bold in the void theme), control vs definition keywords, and function names before (. Format leaves continuation lines of an open string or block comment untouched.
Four information surfaces
| Surface | When | Answers |
|---|---|---|
| Hover | Rest on a symbol | Keyword, type, series, namespace, builtin, user input/function, or hex color |
| Param checklist | Inside a call | Used / current / unused args with type + default |
| Pre-eval | While typing / Save | What is wrong before Run |
| Debug chips | After Run, Debug on | Last-run log.* / errors on that source line |
Hover + param checklist are writing-time docs — not a TradingView® debug API. Pre-eval marks the buffer; chips still read last run. Details: Debugging.
Diagnostics & Problems
Pre-eval (as you type) — schedulePreeval waits the idle delay after the last keystroke (default PREEVAL_IDLE_MS = 1000 ms; tab switch uses a shorter beat). Problems and underlines stay until the next idle check replaces them (optional Clear marks while typing hides them mid-keystroke). Local parse/lint (localPreevaluate) plus POST /lsp/diagnostics when engine=server and remote lint is on. Severity error disables Run when Block Run on errors is on (topbar, editor, Mod-Enter, command palette). Warnings and typos do not block.
Every lint / hover / complete / underline / chip option lives in Settings → Editor (command palette: Open Editor Settings). Defaults match the previous hardcoded behavior.
Local checks: unclosed ( / [ / { and strings, missing entry point, study() → indicator() / strategy(), bare security() → request.security, duplicate indicator() / strategy() / library(), unknown builtin members (strategy.etry, plt → “did you mean …?”). User functions and import aliases are not flagged.
PYNE lint parity offline: a pre-v5 //@version warns as deprecated (W002), and kwargs Pine v6 removed are flagged (W104) — when= on strategy.* order calls and transp= on drawing / plot calls (plot*, fill, bgcolor, barcolor, hline, line.*, box.*, label.*, table.*, polyline.*, linefill.*). color.new / color() keep a valid transp and are never flagged. Both stay on when the Pro API is unreachable, so Pyodide / offline mode reports the same warnings as POST /lsp/diagnostics. Toggles: Warn if //@version is missing (also covers W002) and Warn on v6-removed kwargs.
Merge: remote style-noise C001–C004 is dropped; remote syntax errors win the same span; local unknown-member marks still show when remote is down. Hover tooltips use the diagnostic message (including the suggestion).
After a run, diagnosticsFromLastRun(lastRun, doc) still builds ranges from engine error, meta.errors / diagnostics, and error logs with line refs (line 12:, L12:, …). Live pre-eval for the current buffer is listed first; last-run engine messages are kept when they add detail (dropped only when the buffer no longer matches the stamped run source).
- CodeMirror: wavy underlines, severity gutter, hover tooltips
- Problems panel: clickable rows →
scrollToLine/jumpToDiagnostic; originpre-evalvsrunwhen the diagnostic has a source - Empty / pending pre-eval stays No problems (no flicker)
- Unit tests:
tests/editor-diagnostics.test.ts,tests/editor-problems.test.ts,tests/preevaluate.test.ts
80-column ruler
columnRulerExtension paints a dashed guide at character column 80 (measured via CM coords). Toggle Ruler or command palette “Toggle Editor Ruler”.
Git sync bar
Tab toolbar Pull / Push / status (EditorGitBar):
- Uses active storage plugin (
local|cloud|git) - Push →
writeScript(git commits when storage is git) - Pull → optional plugin
sync('pull')+ list/reload bound library id - Config stays in Settings / Script Library (no second credentials UI)
Completion corpus
src/editor/data/pyne-builtins.json is synced from PYNE (scripts/sync-pyne-builtins.sh). Offline completion/hover fall back to this catalog; optional remote LSP when the Pro API is available.
Remote LSP bridge
PYNE exposes four HTTP endpoints, and AXIS wires all four: POST /lsp/completion, POST /lsp/hover, POST /lsp/diagnostics, POST /lsp/convert (src/editor/pyne-lsp-client.ts). Everything else the PYNE language server advertises over stdio (signature help, document symbols, formatting, folding, document color, convert-to-v6) has a local CodeMirror implementation instead — pine-call-params.ts, pine-symbols.ts, pine-format.ts, code-folding.ts, pine-colors.ts, pine-convert.ts. Library annotation hover (//@function, //@param, //@returns, //@type, //@field, //@variable, //@description) is parsed locally in pyne-doc-annotations.ts and rendered as verbatim Markdown, matching PYNE's card shape.
The Pyodide runtime vendors pynescript-0.6.29-py3-none-any.whl; the builtins catalog is byte-identical to PYNE's builtin_metadata.json (941 entries).
Navigation
Pine-aware symbol navigation runs locally from one symbol index (src/editor/symbol-index.ts) — no round trip to the Pro API, so it works in Pyodide and offline mode.
| Action | Key | Also |
|---|---|---|
| Go to definition | F12 | Alt-click the symbol |
| Find references | Shift-F12 | — |
| Rename symbol | F2 | — |
| Occurrence highlight | automatic | cursor move / click |
Definition resolves a parameter or local inside the enclosing function before a script-level name, so f(n) and g(n) stay distinct. Every occurrence is resolved to the declaration it actually names, so references, highlight and rename skip shadowed uses instead of merging scopes. obj.field resolves through the receiver's declared (or Type.new()-inferred) type; when the receiver has no type information and several types share the field name, the member is left unresolved rather than jumping to the wrong type. Call named arguments (qty = qty) are not treated as variable writes. Rename refuses builtins and builtin namespaces (ta, strategy), Pine keywords, invalid identifiers, type / enum members (a field name is shared by every value of its type), and a new name that is declared or referenced in any scope the rename touches.
The same actions are in the command palette (Go to Definition, Find References, Rename Symbol). Browsers may reserve F12 for devtools — use the palette or Alt-click there.
Library load
loadLibraryDoc(doc, name) on editorRef injects storage reads into the active tab.
Internals
editorRef pattern
App holds { getDoc, setDoc?, loadLibraryDoc?, scrollToLine?, jumpToDiagnostic?, getCursor?, insertAtCursor? } populated by CM mount—avoids full-doc store writes every keystroke (doc also mirrored to EDITOR_DOC_KEY).
Persistence
| Key / field | Role |
|---|---|
store.editor + panelChrome.editor | Dock geometry, open, mode |
pynescript.axis.editor.doc | Draft text |
profilerEnabled / inlineDebugEnabled / debugPinsEnabled / editorRulerEnabled / editorWrapEnabled | Editor tools |
Invariants
- Empty doc Run is a no-op at topbar/editor handlers.
- Popout does not own PaneManager.
- Reattach restores shared doc into docked CM.
- AXIS ≠ engine: editor never fully evaluates Pine itself; pre-eval is parse/lint only.
- Debug/Pins still read last run; static diagnostics also come from pre-eval.
Failure modes
| Symptom | Mitigation |
|---|---|
| Two editors diverge | Prefer bridge doc events; reattach |
| Detached but no window | Popup blocked; allow popups; reattach |
| Lost text on crash | editor.doc localStorage; library Save / git push |
| Blank CM (no lines) | Dock column height chain; editor must fill strip |
| No chips/pins | Enable Debug/Pins; logs need line and/or bar_index |
See also
- Debugging — Debug vs Pins, Scriptlogs, Profiler
- UI shell — Topbar, docks, command palette
- Indicators
- Script library