Linked editing

textDocument/linkedEditingRange — live multi-cursor ranges for user identifiers.

This page

Abstract

textDocument/linkedEditingRange returns same-document ranges for the resolved user symbol under the cursor so the client can drive live multi-cursor edits (rename-as-you-type). Resolution uses the scope-aware symbol index, so a shadowed local is not linked to a same-named global. Keywords, builtin namespaces, and unresolved identifiers stay unlinked — the same refusal set as rename. Empty buffers, unparsed documents, and non-renamable tokens return null.

Conceptual model

Diagram

Rendering…

Interface surface

  • Method: textDocument/linkedEditingRange
  • Capability: linked_editing_range_provider=True
  • Handler: features/linked_editing.py → handle_linked_editing_range (receives version-matched tree=doc.valid_ast)

Response:

FieldValue
rangesCharacter-precise Range list for every same-document occurrence of the ident
wordPattern[A-Za-z_][A-Za-z0-9_]*

Unlike textDocument/rename, linked editing is one document — it does not walk other open buffers.

What is unlinked

is_renamable_ident (shared with highlight / rename) returns false for:

  • Empty or invalid tokens (not a single Pine ident)
  • Keywords from PINE_KEYWORDS (if, plot, enum, …)
  • Builtin namespaces (ta, strategy, math, input, request, str, array, matrix, map, color, line, label, box, table, polyline, log, chart, runtime, session, time, barstate, syminfo, timeframe, ticker, alert, hline, …)
  • Dotted words whose head is a builtin namespace (ta.sma as a whole)

Hits are collected with include_builtin_attrs=False, so library attributes are not linked when the cursor is on a user ident.

Internals

PathRole
features/linked_editing.pyLSP handler + WORD_PATTERN
langserver/analysis.pyScope/symbol resolution and exact occurrence ranges
features/highlight.pycursor_ident, is_renamable_ident
config.pylinked_editing_range_provider=True
server.pyTEXT_DOCUMENT_LINKED_EDITING_RANGE

Tests: tests/test_lsp_features.py (TestLinkedEditingRangeHandler).

Invariants and edge cases

  1. No source → null. Missing or empty buffer aborts early.
  2. Keywords and builtin namespaces never link. Cursor on ta, plot, or if yields null.
  3. Parse failure → null. tree=None (workspace already failed) skips a redundant re-parse.
  4. Same-document only. Cross-file live edits are rename’s job (open documents), not linked editing.
  5. wordPattern is the Pine ident token — the client should not extend the edit into . or digits-leading junk.

Worked example

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

Cursor on either length: two ranges (the assignment target and the call argument), wordPattern [A-Za-z_][A-Za-z0-9_]*. Cursor on ta or plot: null.

Failure modes

SymptomCause
Linked editing never startsCursor on a keyword / builtin namespace, or parse failed
Only one occurrence linkedOther hits are a different ident, or builtin attrs excluded
Edit spills into neighboring textClient ignored wordPattern

See also