Editor

AXIS Pine editor: CodeMirror 6, diagnostics, 80-col ruler, git sync, stats, docked vs popout, editor bridge.

This page

Editor

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.

CodeMirror 6 Pine editor showing a v6 RSI indicator

Abstract

ModuleRole
EditorPane.tsxDocked/standalone chrome, header tools, detach
tabbed-editor.tsxMulti-tab docs, stats strip, Problems, Colors, Symbols, git bar
PyneEditor.tsxCM6 mount (extensions + view APIs)
pyne-language.tsStreamLanguage / highlighting (namespaces, types, multiline strings)
pine-symbols.ts + SymbolEmojiManager.tsxTV-editor-safe glyph catalog + insert panel
pyne-lsp.ts + data/pyne-builtins.jsonOffline completion/hover corpus
column-ruler.ts80-character guide line
diagnostics.tsUnderlines, gutter, jump-to from last run
editor-intel.tsSettings bag for lint / hover / complete / marks / timings
inline-debug.tsEOL chips, pin gutter, line flash
git-sync.ts + ui/EditorGitBar.tsxPull / push / storage status
ui/EditorProblems.tsxCollapsible problems list
editor-bridge.tsCross-window doc + run messages
cm-void.tsVoid-theme CM styling

Conceptual model

Diagram

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 RunrunAndApply(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 left hamburger menu with dock options.

ControlEffect
RunRun script against loaded bars
ScriptlogsOpen last-run log.* panel
ProfilerprofilerEnabled + optional line % gutter; re-run when enabling
DebugInline end-of-line log/error chips (inlineDebugEnabled)
PinsChart markers + 📍 gutter (debugPinsEnabled); Alt+P
Ruler80-column guide (editorRulerEnabled)
Hamburger → Open in new tabEditor in a full browser tab (vs New window popup)

Details: Debugging.

Stats strip

Bottom of tabbed editor (data-testid="axis-editor-stats"):

FieldMeaning
Pos L:CCursor line:column (1-based)
LnTotal lines in document
Words / CharsDocument stats
Diagnostics badgeError/warning count from last run — click jumps to first
ProblemsExpand/collapse problems list
ColorsColor chips / picker / converter for hex and color.*
SymbolsInsert TV-editor-safe arrows, box drawing, marks, spaces, and chart emoji (raw, quoted, or plotchar)
wrapSoft 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

SurfaceWhenAnswers
HoverRest on a symbolKeyword, type, series, namespace, builtin, user input/function, or hex color
Param checklistInside a callUsed / current / unused args with type + default
Pre-evalWhile typing / SaveWhat is wrong before Run
Debug chipsAfter Run, Debug onLast-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). Marks clear while typing unless Clear marks while typing is off. 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.

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; origin pre-eval vs run when 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.

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 / fieldRole
store.editor + panelChrome.editorDock geometry, open, mode
pynescript.axis.editor.docDraft text
profilerEnabled / inlineDebugEnabled / debugPinsEnabled / editorRulerEnabled / editorWrapEnabledEditor tools

Invariants

  1. Empty doc Run is a no-op at topbar/editor handlers.
  2. Popout does not own PaneManager.
  3. Reattach restores shared doc into docked CM.
  4. AXIS ≠ engine: editor never fully evaluates Pine itself; pre-eval is parse/lint only.
  5. Debug/Pins still read last run; static diagnostics also come from pre-eval.

Failure modes

SymptomMitigation
Two editors divergePrefer bridge doc events; reattach
Detached but no windowPopup blocked; allow popups; reattach
Lost text on crasheditor.doc localStorage; library Save / git push
Blank CM (no lines)Dock column height chain; editor must fill strip
No chips/pinsEnable Debug/Pins; logs need line and/or bar_index

See also