Hover

textDocument/hover — Markdown cards for builtins, types, namespaces, keywords, and user declarations.

This page

Abstract

Hover answers “what is this identifier?” using the scope-aware static index first: local/global symbols (variables, params, functions, methods, types, enums, enum members, fields, imports) and type-directed members (p.field, Point.new), then namespace prefix, type / qualifier, builtin metadata, built-in variables (close, bar_index, …), and keywords. The handler extracts the word under the cursor (including dotted forms) and returns a Markdown card. User symbols show their inferred type and declaration signature; UDT members show their receiver. Builtin cards for dotted labels (ta.sma, strategy.entry, line.fill) append a Pine v6 function-reference markdown link. Keywords, types, namespaces, and user declarations stay link-free.

Library annotations become rich Markdown cards. The comment block directly above an exported declaration is parsed as Pine library annotations (// @function, // @param, // @returns, // @type, // @field, // @variable, // @description). Everything after the tag — including continuation // lines — is rendered verbatim as Markdown, so headings, bold/italic, strikethrough, blockquotes, horizontal rules, lists, code fences, tables, links, images, and two-space line breaks all appear in the hover. User-defined functions, methods, and types shadow builtin metadata, so a library function named like a builtin alias (e.g. bb) still shows its own docs.

Conceptual model

Diagram

Rendering…

Interface surface

  • Method: textDocument/hover
  • Capability: hover_provider=True
  • Handler: features/hover.py → handle_hover (receives version-matched tree=doc.valid_ast and optional per-version feature_cache for the user-decl walk)

Lookup order

  1. Identifier segment at cursor via get_identifier_segment_at_position (identifiers may include .).
  2. Namespace: cursor on a module name (ta in ta.sma, or bare ta / math). Types that share a name (line, array, color) and callables (strategy(, input() fall through.
  3. Type or qualifier (int, float, series, var, …) when the segment is not followed by (.
  4. library with a script-level // @description → library card.
  5. User enum type or Enum.member from the cached AST.
  6. User declaration lookup (FunctionDef / TypeDef / EnumDef / Assign, including tuple targets). First declaration of a name wins.
  7. Builtin lookup: full word, then leaf after ., then module.word if the preceding token ends with .. A user function / method / type with the same name wins over the builtin.
  8. Remaining user declarations (variables) after builtins.
  9. Else keyword brief from PINE_KEYWORDS.
  10. Build hover with range covering [start, end) of the word on the line.

Builtin card structure

markdown
`function`

```pinescript
{signature}
```

{brief}

**Parameters** (table of Name | Description when metadata includes params)
{first documentation paragraph, ≤500 chars}
**Example:** …
**See also:** `ta.ema`, …

---

[Pine v6: ta.sma](https://www.tradingview.com/pine-script-reference/v6/#fun_ta.sma)

Kind kickers (`function`, `namespace`, `type`, `qualifier`, `keyword`, …) plus the fenced signature are shared with completion docs so hover and the suggest extra panel read as the same AXIS card. The kicker is inline code so AXIS / HOOX / PYNE themes color it with textPreformat.

Examples and related maps are hardcoded for a small high-value set (ta.sma, ta.ema, ta.rsi, ta.macd, strategy entry/exit, etc.). Others show signature + brief (and the docs link when the label is dotted).

_build_docs_link emits that markdown link only for dotted builtin labels with a namespace (ta.sma, strategy.entry, line.fill). Empty, undotted (sma), or whitespace-only names return "". Keyword, type, namespace, and user-declaration cards never include the TradingView® URL.

Library annotation cards

Pine libraries document exported functions, methods, types, and variables with annotation comments directly above the declaration. The block is read from source (tag line plus every following // line until a non-comment line), so multi-line Markdown is preserved:

pinescript
// @function Creates bollinger bands out of any specified moving average
//
// #### Usage
// ___
// ```
// [bb_up, bb_mid, bb_dn] = bb(close, ta.sma(close, 50), 50, 4)
// ```
// ___
// @param src   The source of the standard deviation. `Optional`
// @param mult  The multiplier of the offsets. `Required`
// @returns `Bollinger bands` of the specified moving average.
export bb(series float src, series float movingAverage, series int maLength, series float mult) =>
    dev = mult * ta.stdev(src, maLength)
    [movingAverage, movingAverage + dev, movingAverage - dev]

Hovering bb renders the fenced signature, the Markdown description (headings, code fence, horizontal rules), a Parameters list from // @param, and a Returns line. Supported tags:

TagCard section
// @function / // @type / // @variable / // @enumDescription (Markdown body)
// @param <name> <desc>Parameters bullet
// @field <name> <desc>Fields bullet
// @returns <desc>Returns line
// @descriptionLibrary card (hovering library)

Markdown is passed through verbatim, so the TradingView® Markdown in the Pine Editor formatting works: # headings, **bold** / *italic* / ~~strike~~, > blockquotes, ___ / *** rules, 1. / - / + / * lists, ``` code fences, `code`, | tables, [text](url) links, ![alt](url) images, and two trailing spaces for a line break. Both //@function and // @function spellings are accepted. Cards without a known tag fall back to User-defined {'{'}kind{'}'}.

Internals

PathRole
features/hover.pyWord resolution + Markdown assembly
providers/builtin_metadata.pyget_builtin
protocol/utils.pyIdentifier segment extraction

The workspace feature_cache memos the user-declaration walk so each hover does not re-walk the whole AST.

Invariants and edge cases

  1. No source → null. Missing buffer aborts early.
  2. Line OOB → null. Cursor past last line is ignored.
  3. Empty word → null. Whitespace / punctuation yields no hover.
  4. User functions, assigns, types, and enums are hovered — locals are not “null”. Use definition when you need a jump, not a card.
  5. Docs links are dotted builtins only. Keywords, types, namespaces, and user decls stay link-free.
  6. Documentation truncation is first-paragraph / 500-char only — full docs live in metadata and completion resolve.
  7. Annotation blocks are contiguous. The block is the run of // lines immediately above the declaration. A blank line or code line ends it. Plain comments without a known // @tag are ignored (fallback card).
  8. User symbols shadow builtins. A user variable/function/method/type with a builtin name wins, so a local color or library bb is described as the user declaration. Unknown identifiers fall through to builtin metadata/keywords.
  9. Markdown is never escaped or sanitized — it is emitted as MarkupContent(kind=Markdown), so clients render the author's formatting.

Worked example

Cursor on sma in plot(ta.sma(close, 14)):

  • Word may be ta.sma if the dotted identifier is one match, or sma with module recovery from ta..
  • Response contents.kind = markdown with a fence of the metadata signature, plus [Pine v6: ta.sma](https://www.tradingview.com/pine-script-reference/v6/#fun_ta.sma).

Cursor on foo in:

pinescript
//@version=6
indicator("T")
foo(a) => a
plot(foo(1))
  • Declaration and use both hover as a user-defined function with fence foo(a) => a.
  • No Pine v6 URL.

Cursor on an annotated library function:

pinescript
// @function **Bold** heading
// #### Usage
// ```
// x = foo(1)
// ```
// @param a The first.
// @returns The result.
export foo(a) => a
  • Card = fenced signature + the Markdown body (**Bold** heading, #### Usage, code fence) + **Parameters:** - \a`: The first.+Returns: The result.`

Failure modes

SymptomCause
Hover never appearsMetadata empty or word not in catalog / AST
Wrong symbolIdentifier regex swallowed neighboring text — rare with _. pattern
Stale docs after adding builtinRegenerate metadata — builtin metadata
User local shows no cardParse failed (tree=None) so the decl walk is skipped

See also