Skip to content

Conversation

@MichaReiser
Copy link
Member

@MichaReiser MichaReiser commented Nov 3, 2025

Summary

This PR changes the signature of the TextSize <-> Position and TextRange <-> Range conversion methods in preparation for notebook support.

According to the LSP specification, each Notebook consists of many TextDocuments. However, ty internally concatenates all cells to a single text document. That means, offsets coming from ty need to be mapped back to their corresponding cell, and then to the offset within the cell. The same applies for LSP offsets: The cell-relative offset needs to be converted to an absolute offset into the concatenated notebook document.

This PR doesn't implement the logic itself for mapping between the different offsets but it refactors the call-sites and the methods used for those conversions to add the required logic in a follow-up PR.

The most important change is that we now pass db and File to the conversion methods. This will allow us to query the notebook to resolve the cell metadata (offset per cell, etc).

Test Plan

Existing tests. I tested different LSP features and they seem to work fine

@MichaReiser MichaReiser added internal An internal refactor or improvement ty Multi-file analysis & type inference labels Nov 3, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 3, 2025

Diagnostic diff on typing conformance tests

No changes detected when running ty on typing conformance tests ✅

@github-actions
Copy link
Contributor

github-actions bot commented Nov 3, 2025

mypy_primer results

No ecosystem changes detected ✅
No memory usage changes detected ✅

&line_index,
db,
file,
snapshot.url(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit unfortunate that we need the url here but the file isn't unique enough (the file maps to the entire notebook). We need the url to lookup the corresponding cell within the notebook.

Comment on lines +74 to +77
let range = edit
.range()
.as_lsp_range(db, file, snapshot.encoding())
.to_local_range();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using to_local_range here is technically incorrect. In fact, it's very likely that the importer adds the import to the very first cell within the notebook, in which case the edit is not local to a single cell.

However, fixing this requires changes to the importer and they're substantial enough to justify a separate PR

@MichaReiser MichaReiser force-pushed the micha/lsp-range branch 3 times, most recently from 4b69dd8 to 1114c07 Compare November 3, 2025 02:21
.range()
.to_lsp_range(&source, &line_index, snapshot.encoding());
.as_lsp_range(db, file, snapshot.encoding())
.to_local_range();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to limit the document_highlights to the current cell. This seems very unfortunate as the highlights are very likely to be cross cell but the LSP protocol doesn't allow us to specify a URI. Again, I think this is best done in a separate PR

Copy link
Member

@dhruvmanila dhruvmanila Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't something that requires to be investigated right now but I tested Pylance and it seems to be able to highlight a symbol across multiple cells in a notebook.

Screen.Recording.2025-11-04.at.15.48.55.mov

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is something Pylance does (although it could because it isn't an LSP).

But I don't see a highlight request for cells at all and the highlighting is also incorrect, suggesting it's just a simple name matching:

Screen.Recording.2025-11-04.at.15.58.25.mov

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could mean that we should return Option from to_location but I've to revisit this when being further along with the notebook integration

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense.

range: symbol
.full_range
.as_lsp_range(db, file, encoding)
.to_local_range(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: We need to limit the document symbol request to the current cell. But I'd prefer doing that in a separate PR


for token in &*semantic_token_data {
let lsp_range = token.range().to_lsp_range(&source, &line_index, encoding);
let lsp_range = token
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to limit the semantic tokens to a single range but I'll do this in a separate PR

@MichaReiser MichaReiser force-pushed the micha/lsp-range branch 2 times, most recently from 61afb54 to a62875f Compare November 3, 2025 02:42
@MichaReiser MichaReiser marked this pull request as ready for review November 3, 2025 02:50
@MichaReiser MichaReiser added the server Related to the LSP server label Nov 3, 2025
@sharkdp
Copy link
Contributor

sharkdp commented Nov 3, 2025

Seems like something that maybe @dhruvmanila would be a good candidate to review?

Copy link
Member

@dhruvmanila dhruvmanila left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Introduce new infrastructure to support converting positions and ranges
for documents that may map to multiple URIs (like notebook cells):

- Add Db trait with document() and notebook_document() queries
  - Provides access to document metadata needed for conversion
  - notebook_document() returns notebook info for a file if it exists

- Add LspRange and LspPosition types for deferred LSP conversion
  - Stores file, offset/range, db reference, and encoding
  - Allows callers to choose conversion strategy:
    * to_local_range()/to_local_position(): For use within same document/cell
    * to_location(): For cross-document references with URI
  - Critical for notebooks where one file maps to multiple cell URIs

- Update conversion traits (RangeExt, PositionExt, etc.)
  - Add methods to create LspRange/LspPosition from internal types
  - Support both notebook cells and regular files

This infrastructure prepares the codebase for notebook support where
position/range conversions must account for cell boundaries and URIs.
@MichaReiser MichaReiser changed the title [ty] Refactor Range<->TextRange conversion as prep for notebook support [ty] Refactor Range to/from TextRange conversion as prep for notebook support Nov 5, 2025
@MichaReiser MichaReiser merged commit 7009d60 into main Nov 5, 2025
40 checks passed
@MichaReiser MichaReiser deleted the micha/lsp-range branch November 5, 2025 13:24
carljm added a commit to MatthewMckee4/ruff that referenced this pull request Nov 6, 2025
* main: (188 commits)
  [ty] Discover site-packages from the environment that ty is installed in (astral-sh#21286)
  [ty] Make special cases for `UnionType` slightly narrower (astral-sh#21276)
  Require ignore 0.4.24 in `Cargo.toml` (astral-sh#21292)
  [ty] Favour imported symbols over builtin symbols (astral-sh#21285)
  docs: revise Ruff setup instructions for Zed editor (astral-sh#20935)
  [ty] Update salsa (astral-sh#21281)
  [syntax-error]: no binding for nonlocal  PLE0117 as a semantic syntax error (astral-sh#21032)
  [ty] Constraining a typevar with itself (possibly via union or intersection) (astral-sh#21273)
  [`ruff`] Fix false positives on starred arguments (`RUF057`) (astral-sh#21256)
  [ty] Simplify unions containing multiple type variables during inference (astral-sh#21275)
  [ty] Add `ty_server::Db` trait (astral-sh#21241)
  [ty] Refactor `Range` to/from `TextRange` conversion as prep for notebook support (astral-sh#21230)
  [ty] Fix playground crash when file name includes path separator (astral-sh#21151)
  [`refurb`] Fix false negative for underscores before sign in `Decimal` constructor (`FURB157`) (astral-sh#21190)
  [ty] Allow values of type `None` in type expressions (astral-sh#21263)
  Run codspeed benchmarks with `profiling` profile (astral-sh#21261)
  [ty] Update expected diagnostic count in benchmarks (astral-sh#21269)
  Avoid extra parentheses for long `match` patterns with `as` captures (astral-sh#21176)
  [ty] Update salsa (astral-sh#21265)
  [ty] `dict` is not assignable to `TypedDict` (astral-sh#21238)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal An internal refactor or improvement server Related to the LSP server ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants