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
Rendering…
Interface surface
- Method:
textDocument/hover - Capability:
hover_provider=True - Handler:
features/hover.py→handle_hover(receives version-matchedtree=doc.valid_astand optional per-versionfeature_cachefor the user-decl walk)
Lookup order
- Identifier segment at cursor via
get_identifier_segment_at_position(identifiers may include.). - Namespace: cursor on a module name (
tainta.sma, or bareta/math). Types that share a name (line,array,color) and callables (strategy(,input() fall through. - Type or qualifier (
int,float,series,var, …) when the segment is not followed by(. librarywith a script-level// @description→ library card.- User enum type or
Enum.memberfrom the cached AST. - User declaration lookup (
FunctionDef/TypeDef/EnumDef/Assign, including tuple targets). First declaration of a name wins. - Builtin lookup: full word, then leaf after
., thenmodule.wordif the preceding token ends with.. A user function / method / type with the same name wins over the builtin. - Remaining user declarations (variables) after builtins.
- Else keyword brief from
PINE_KEYWORDS. - Build hover with range covering
[start, end)of the word on the line.
Builtin card structure
`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:
// @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:
| Tag | Card section |
|---|---|
// @function / // @type / // @variable / // @enum | Description (Markdown body) |
// @param <name> <desc> | Parameters bullet |
// @field <name> <desc> | Fields bullet |
// @returns <desc> | Returns line |
// @description | Library 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,  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
| Path | Role |
|---|---|
features/hover.py | Word resolution + Markdown assembly |
providers/builtin_metadata.py | get_builtin |
protocol/utils.py | Identifier 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
- No source → null. Missing buffer aborts early.
- Line OOB → null. Cursor past last line is ignored.
- Empty word → null. Whitespace / punctuation yields no hover.
- User functions, assigns, types, and enums are hovered — locals are not “null”. Use definition when you need a jump, not a card.
- Docs links are dotted builtins only. Keywords, types, namespaces, and user decls stay link-free.
- Documentation truncation is first-paragraph / 500-char only — full docs live in metadata and completion resolve.
- 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// @tagare ignored (fallback card). - User symbols shadow builtins. A user variable/function/method/type with a builtin name wins, so a local
coloror librarybbis described as the user declaration. Unknown identifiers fall through to builtin metadata/keywords. - 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.smaif the dotted identifier is one match, orsmawith module recovery fromta.. - Response
contents.kind = markdownwith 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:
//@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:
// @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
| Symptom | Cause |
|---|---|
| Hover never appears | Metadata empty or word not in catalog / AST |
| Wrong symbol | Identifier regex swallowed neighboring text — rare with _. pattern |
| Stale docs after adding builtin | Regenerate metadata — builtin metadata |
| User local shows no card | Parse failed (tree=None) so the decl walk is skipped |