Semantic Tokens

textDocument/semanticTokens/full and /range — AST visitor, custom legend, and LSP delta encoding.

This page

Abstract

Semantic tokens let the server paint identifiers with roles richer than TextMate scopes (library namespaces vs user variables, function defs vs properties). PYNE advertises a full and range semantic-tokens provider with a short custom legend (not the full LSP standard set). textDocument/semanticTokens/full walks the workspace AST; textDocument/semanticTokens/range clips the same legend to tokens whose start falls inside the requested range.

Conceptual model

Diagram

Rendering…

Interface surface

Capability (config.py → semantic_tokens_provider):

text
legend.token_types (index order is contract):
  namespace, type, class, function, method, variable,
  parameter, property, keyword, string, number, operator, comment

legend.token_modifiers (bit 0 = declaration):
  declaration, definition, readonly, defaultLibrary

range: true
full: true
MethodHandler
textDocument/semanticTokens/fullhandle_semantic_tokens
textDocument/semanticTokens/rangehandle_semantic_tokens_range

Both take (params, source, tree=…) and return lsp.SemanticTokens(data=[...]) — LSP-encoded five-tuples as a flat int array (relative line/character deltas). Range uses the same legend as full: collect, sort, then keep tokens whose start (line, col) is in params.range (inclusive start, exclusive end) before encoding.

What is emitted

Symbol occurrenceToken typeModifiers
Function / method declarationfunction / methoddefinition | declaration
Type declarationclassdefinition | declaration
Enum declarationtypedefinition | declaration
Enum member / UDT fieldpropertydeclaration on the member
Variable declaration and readsvariabledeclaration on the binding
Parameter declaration and readsparameterdeclaration on the binding
Import alias and usesnamespacedeclaration on the alias
Builtin namespace attribute (ta.sma)namespace + methoddefaultLibrary (+ readonly on ns)
Other attribute (unknown receiver)property—

Builtin namespace set (_BUILTIN_NS): ta, math, str, array, matrix, map, strategy, request, input, color, line, label, box, table, polyline, log, ticker, timeframe, chart, runtime, syminfo, barstate, session, time.

Internals

PathRole
features/semantic_tokens.pyCollector + _encode + range clip
config.pyLegend + capability — indices must match _TT
server.pyTEXT_DOCUMENT_SEMANTIC_TOKENS_FULL / _RANGE; passes version-matched doc.valid_ast

Attribute columns prefer end_col_offset - len(attr) when present; otherwise parent col_offset (approximate).

TextMate from the extension repo's syntaxes/pinescript.tmLanguage.json still colors keywords, strings, and comments — the visitor does not emit those yet even though the legend reserves the slots.

Invariants and edge cases

  1. Never throws — missing source, tree=None, or parse exception yields empty data.
  2. Range is a clip of full — same types/modifiers; tokens whose start is outside the range are dropped. A range token stream is a subsequence of the full stream.
  3. Legend is fixed at initialize; changing indices later would break cached clients.
  4. Empty data is valid protocol (empty file, parse failure, or no tokens in range), not an error.
  5. Tokens are sorted by (line, col) before delta encoding.

Worked example

pinescript
//@version=6
indicator("tok")
length = 14
plot(ta.sma(close, length))

Full document: tokens for length (variable/declaration), ta (namespace/defaultLibrary), sma (method/defaultLibrary). Keywords such as indicator remain TextMate-only.

Range covering only the plot(ta.sma…) line keeps ta / sma (and any other starts on that line) and drops the length assignment. A range covering only the //@version=6 pragma yields no namespace token.

Failure modes

SymptomCause
Semantic highlighting off despite tokensClient editor.semanticHighlighting.enabled false
Colors look “shifted” after a legend editClient cached old type indices — restart the language client
Range request returns empty dataParse failure, empty buffer, or no token starts in range — not a capability miss
Attribute token sits on the wrong columnMissing end_col_offset on the AST node — approximate path

See also