[ty] Make range/position conversions fallible (#21297)

Co-authored-by: Aria Desires <aria.desires@gmail.com>
This commit is contained in:
Micha Reiser 2025-11-07 15:44:23 +01:00 committed by GitHub
parent c7ff9826d6
commit 6cc3393ccd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 239 additions and 225 deletions

View File

@ -20,7 +20,7 @@ pub(crate) trait ToLink {
impl ToLink for NavigationTarget { impl ToLink for NavigationTarget {
fn to_location(&self, db: &dyn Db, encoding: PositionEncoding) -> Option<Location> { fn to_location(&self, db: &dyn Db, encoding: PositionEncoding) -> Option<Location> {
FileRange::new(self.file(), self.focus_range()) FileRange::new(self.file(), self.focus_range())
.as_lsp_range(db, encoding) .to_lsp_range(db, encoding)?
.to_location() .to_location()
} }
@ -35,17 +35,17 @@ impl ToLink for NavigationTarget {
// Get target_range and URI together to ensure they're consistent (same cell for notebooks) // Get target_range and URI together to ensure they're consistent (same cell for notebooks)
let target_location = self let target_location = self
.full_range() .full_range()
.as_lsp_range(db, file, encoding) .to_lsp_range(db, file, encoding)?
.to_location()?; .into_location()?;
let target_range = target_location.range; let target_range = target_location.range;
// For selection_range, we can use as_local_range since we know it's in the same document/cell // For selection_range, we can use as_local_range since we know it's in the same document/cell
let selection_range = self let selection_range = self
.focus_range() .focus_range()
.as_lsp_range(db, file, encoding) .to_lsp_range(db, file, encoding)?
.to_local_range(); .local_range();
let src = src.map(|src| src.as_lsp_range(db, encoding).to_local_range()); let src = src.and_then(|src| Some(src.to_lsp_range(db, encoding)?.local_range()));
Some(lsp_types::LocationLink { Some(lsp_types::LocationLink {
target_uri: target_location.uri, target_uri: target_location.uri,
@ -58,7 +58,9 @@ impl ToLink for NavigationTarget {
impl ToLink for ReferenceTarget { impl ToLink for ReferenceTarget {
fn to_location(&self, db: &dyn Db, encoding: PositionEncoding) -> Option<Location> { fn to_location(&self, db: &dyn Db, encoding: PositionEncoding) -> Option<Location> {
self.file_range().as_lsp_range(db, encoding).to_location() self.file_range()
.to_lsp_range(db, encoding)?
.into_location()
} }
fn to_link( fn to_link(
@ -70,12 +72,12 @@ impl ToLink for ReferenceTarget {
// Get target_range and URI together to ensure they're consistent (same cell for notebooks) // Get target_range and URI together to ensure they're consistent (same cell for notebooks)
let target_location = self let target_location = self
.range() .range()
.as_lsp_range(db, self.file(), encoding) .to_lsp_range(db, self.file(), encoding)?
.to_location()?; .into_location()?;
let target_range = target_location.range; let target_range = target_location.range;
let selection_range = target_range; let selection_range = target_range;
let src = src.map(|src| src.as_lsp_range(db, encoding).to_local_range()); let src = src.and_then(|src| Some(src.to_lsp_range(db, encoding)?.local_range()));
Some(lsp_types::LocationLink { Some(lsp_types::LocationLink {
target_uri: target_location.uri, target_uri: target_location.uri,

View File

@ -2,133 +2,94 @@ use super::PositionEncoding;
use crate::Db; use crate::Db;
use crate::system::file_to_url; use crate::system::file_to_url;
use lsp_types as types;
use lsp_types::{Location, Position, Url};
use ruff_db::files::{File, FileRange}; use ruff_db::files::{File, FileRange};
use ruff_db::source::{line_index, source_text}; use ruff_db::source::{line_index, source_text};
use ruff_source_file::LineIndex; use ruff_source_file::LineIndex;
use ruff_source_file::{OneIndexed, SourceLocation}; use ruff_source_file::{OneIndexed, SourceLocation};
use ruff_text_size::{Ranged, TextRange, TextSize}; use ruff_text_size::{Ranged, TextRange, TextSize};
/// Represents a range that has been prepared for LSP conversion but requires /// A range in an LSP text document (cell or a regular document).
/// a decision about how to use it - either as a local range within the same #[derive(Clone, Debug, Default)]
/// document/cell, or as a location that can reference any document in the project. pub(crate) struct LspRange {
#[derive(Clone)] range: lsp_types::Range,
pub(crate) struct LspRange<'db> {
file: File, /// The URI of this range's text document
range: TextRange, uri: Option<lsp_types::Url>,
db: &'db dyn Db,
encoding: PositionEncoding,
} }
impl std::fmt::Debug for LspRange<'_> { impl LspRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// Returns the range within this document.
f.debug_struct("LspRange")
.field("range", &self.range)
.field("file", &self.file)
.field("encoding", &self.encoding)
.finish_non_exhaustive()
}
}
impl LspRange<'_> {
/// Convert to an LSP Range for use within the same document/cell.
/// Returns only the LSP Range without any URI information.
/// ///
/// Use this when you already have a URI context and this range is guaranteed /// Only use `range` when you already have a URI context and this range is guaranteed
/// to be within the same document/cell: /// to be within the same document/cell:
/// - Selection ranges within a `LocationLink` (where `target_uri` provides context) /// - Selection ranges within a `LocationLink` (where `target_uri` provides context)
/// - Additional ranges in the same cell (e.g., `selection_range` when you already have `target_range`) /// - Additional ranges in the same cell (e.g., `selection_range` when you already have `target_range`)
/// ///
/// Do NOT use this for standalone ranges - use `to_location()` instead to ensure /// Do NOT use this for standalone ranges - use [`Self::to_location`] instead to ensure
/// the URI and range are consistent. /// the URI and range are consistent.
pub(crate) fn to_local_range(&self) -> types::Range { pub(crate) fn local_range(&self) -> lsp_types::Range {
self.to_uri_and_range().1 self.range
} }
/// Convert to a Location that can reference any document. /// Converts this range into an LSP location.
/// Returns a Location with both URI and Range.
/// ///
/// Use this for: /// Returns `None` if the URI for this file couldn't be resolved.
/// - Go-to-definition targets pub(crate) fn to_location(&self) -> Option<lsp_types::Location> {
/// - References Some(lsp_types::Location {
/// - Diagnostics related information uri: self.uri.clone()?,
/// - Any cross-file navigation range: self.range,
pub(crate) fn to_location(&self) -> Option<Location> { })
let (uri, range) = self.to_uri_and_range();
Some(Location { uri: uri?, range })
} }
pub(crate) fn to_uri_and_range(&self) -> (Option<Url>, lsp_types::Range) { pub(crate) fn into_location(self) -> Option<lsp_types::Location> {
let source = source_text(self.db, self.file); Some(lsp_types::Location {
let index = line_index(self.db, self.file); uri: self.uri?,
range: self.range,
let uri = file_to_url(self.db, self.file); })
let range = text_range_to_lsp_range(self.range, &source, &index, self.encoding);
(uri, range)
} }
} }
/// Represents a position that has been prepared for LSP conversion but requires /// A position in an LSP text document (cell or a regular document).
/// a decision about how to use it - either as a local position within the same #[derive(Clone, Debug, Default)]
/// document/cell, or as a location with a single-point range that can reference pub(crate) struct LspPosition {
/// any document in the project. position: lsp_types::Position,
#[derive(Clone)]
pub(crate) struct LspPosition<'db> { /// The URI of this range's text document
file: File, uri: Option<lsp_types::Url>,
position: TextSize,
db: &'db dyn Db,
encoding: PositionEncoding,
} }
impl std::fmt::Debug for LspPosition<'_> { impl LspPosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// Returns the position within this document.
f.debug_struct("LspPosition")
.field("position", &self.position)
.field("file", &self.file)
.field("encoding", &self.encoding)
.finish_non_exhaustive()
}
}
impl LspPosition<'_> {
/// Convert to an LSP Position for use within the same document/cell.
/// Returns only the LSP Position without any URI information.
/// ///
/// Use this when you already have a URI context and this position is guaranteed /// Only use [`Self::local_position`] when you already have a URI context and this position is guaranteed
/// to be within the same document/cell: /// to be within the same document/cell
/// - Inlay hints (where the document URI is already known)
/// - Positions within the same cell as a parent range
/// ///
/// Do NOT use this for standalone positions that might need a URI - use /// Do NOT use this for standalone positions - use [`Self::to_location`] instead to ensure
/// `to_location()` instead to ensure the URI and position are consistent. /// the URI and position are consistent.
pub(crate) fn to_local_position(&self) -> types::Position { pub(crate) fn local_position(&self) -> lsp_types::Position {
self.to_location().1 self.position
} }
/// Convert to a Location with a single-point range that can reference any document. /// Returns the uri of the text document this position belongs to.
/// Returns a Location with both URI and a range where start == end. #[expect(unused)]
/// pub(crate) fn uri(&self) -> Option<&lsp_types::Url> {
/// Use this for any cross-file navigation where you need both URI and position. self.uri.as_ref()
pub(crate) fn to_location(&self) -> (Option<lsp_types::Url>, Position) {
let source = source_text(self.db, self.file);
let index = line_index(self.db, self.file);
let uri = file_to_url(self.db, self.file);
let position = text_size_to_lsp_position(self.position, &source, &index, self.encoding);
(uri, position)
} }
} }
pub(crate) trait RangeExt { pub(crate) trait RangeExt {
/// Convert an LSP Range to internal `TextRange`. /// Convert an LSP Range to internal [`TextRange`].
///
/// Returns `None` if `file` is a notebook and the
/// cell identified by `url` can't be looked up or if the notebook
/// isn't open in the editor.
fn to_text_range( fn to_text_range(
&self, &self,
db: &dyn Db, db: &dyn Db,
file: File, file: File,
url: &lsp_types::Url, url: &lsp_types::Url,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> TextRange; ) -> Option<TextRange>;
} }
impl RangeExt for lsp_types::Range { impl RangeExt for lsp_types::Range {
@ -138,23 +99,27 @@ impl RangeExt for lsp_types::Range {
file: File, file: File,
url: &lsp_types::Url, url: &lsp_types::Url,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> TextRange { ) -> Option<TextRange> {
let start = self.start.to_text_size(db, file, url, encoding); let start = self.start.to_text_size(db, file, url, encoding)?;
let end = self.end.to_text_size(db, file, url, encoding); let end = self.end.to_text_size(db, file, url, encoding)?;
TextRange::new(start, end) Some(TextRange::new(start, end))
} }
} }
pub(crate) trait PositionExt { pub(crate) trait PositionExt {
/// Convert an LSP Position to internal `TextSize`. /// Convert an LSP Position to internal `TextSize`.
///
/// Returns `None` if `file` is a notebook and the
/// cell identified by `url` can't be looked up or if the notebook
/// isn't open in the editor.
fn to_text_size( fn to_text_size(
&self, &self,
db: &dyn Db, db: &dyn Db,
file: File, file: File,
url: &lsp_types::Url, url: &lsp_types::Url,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> TextSize; ) -> Option<TextSize>;
} }
impl PositionExt for lsp_types::Position { impl PositionExt for lsp_types::Position {
@ -164,52 +129,59 @@ impl PositionExt for lsp_types::Position {
file: File, file: File,
_url: &lsp_types::Url, _url: &lsp_types::Url,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> TextSize { ) -> Option<TextSize> {
let source = source_text(db, file); let source = source_text(db, file);
let index = line_index(db, file); let index = line_index(db, file);
lsp_position_to_text_size(*self, &source, &index, encoding) Some(lsp_position_to_text_size(*self, &source, &index, encoding))
} }
} }
pub(crate) trait TextSizeExt { pub(crate) trait TextSizeExt {
/// Converts this position to an `LspPosition`, which then requires an explicit /// Converts self into a position into an LSP text document (can be a cell or regular document).
/// decision about how to use it (as a local position or as a location). ///
fn as_lsp_position<'db>( /// Returns `None` if the position can't be converted:
///
/// * If `file` is a notebook but the notebook isn't open in the editor,
/// preventing us from looking up the corresponding cell.
/// * If `position` is out of bounds.
fn to_lsp_position(
&self, &self,
db: &'db dyn Db, db: &dyn Db,
file: File, file: File,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> LspPosition<'db> ) -> Option<LspPosition>
where where
Self: Sized; Self: Sized;
} }
impl TextSizeExt for TextSize { impl TextSizeExt for TextSize {
fn as_lsp_position<'db>( fn to_lsp_position(
&self, &self,
db: &'db dyn Db, db: &dyn Db,
file: File, file: File,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> LspPosition<'db> { ) -> Option<LspPosition> {
LspPosition { let source = source_text(db, file);
file, let index = line_index(db, file);
position: *self,
db, let uri = file_to_url(db, file);
encoding, let position = text_size_to_lsp_position(*self, &source, &index, encoding);
}
Some(LspPosition { position, uri })
} }
} }
pub(crate) trait ToRangeExt { pub(crate) trait ToRangeExt {
/// Converts this range to an `LspRange`, which then requires an explicit /// Converts self into a range into an LSP text document (can be a cell or regular document).
/// decision about how to use it (as a local range or as a location). ///
fn as_lsp_range<'db>( /// Returns `None` if the range can't be converted:
&self, ///
db: &'db dyn Db, /// * If `file` is a notebook but the notebook isn't open in the editor,
file: File, /// preventing us from looking up the corresponding cell.
encoding: PositionEncoding, /// * If range is out of bounds.
) -> LspRange<'db>; fn to_lsp_range(&self, db: &dyn Db, file: File, encoding: PositionEncoding)
-> Option<LspRange>;
} }
fn u32_index_to_usize(index: u32) -> usize { fn u32_index_to_usize(index: u32) -> usize {
@ -221,7 +193,7 @@ fn text_size_to_lsp_position(
text: &str, text: &str,
index: &LineIndex, index: &LineIndex,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> types::Position { ) -> lsp_types::Position {
let source_location = index.source_location(offset, text, encoding.into()); let source_location = index.source_location(offset, text, encoding.into());
source_location_to_position(&source_location) source_location_to_position(&source_location)
} }
@ -231,8 +203,8 @@ fn text_range_to_lsp_range(
text: &str, text: &str,
index: &LineIndex, index: &LineIndex,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> types::Range { ) -> lsp_types::Range {
types::Range { lsp_types::Range {
start: text_size_to_lsp_position(range.start(), text, index, encoding), start: text_size_to_lsp_position(range.start(), text, index, encoding),
end: text_size_to_lsp_position(range.end(), text, index, encoding), end: text_size_to_lsp_position(range.end(), text, index, encoding),
} }
@ -272,23 +244,23 @@ pub(crate) fn lsp_range_to_text_range(
} }
impl ToRangeExt for TextRange { impl ToRangeExt for TextRange {
fn as_lsp_range<'db>( fn to_lsp_range(
&self, &self,
db: &'db dyn Db, db: &dyn Db,
file: File, file: File,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> LspRange<'db> { ) -> Option<LspRange> {
LspRange { let source = source_text(db, file);
file, let index = line_index(db, file);
range: *self, let range = text_range_to_lsp_range(*self, &source, &index, encoding);
db,
encoding, let uri = file_to_url(db, file);
} Some(LspRange { range, uri })
} }
} }
fn source_location_to_position(location: &SourceLocation) -> types::Position { fn source_location_to_position(location: &SourceLocation) -> lsp_types::Position {
types::Position { lsp_types::Position {
line: u32::try_from(location.line.to_zero_indexed()).expect("line usize fits in u32"), line: u32::try_from(location.line.to_zero_indexed()).expect("line usize fits in u32"),
character: u32::try_from(location.character_offset.to_zero_indexed()) character: u32::try_from(location.character_offset.to_zero_indexed())
.expect("character usize fits in u32"), .expect("character usize fits in u32"),
@ -298,16 +270,11 @@ fn source_location_to_position(location: &SourceLocation) -> types::Position {
pub(crate) trait FileRangeExt { pub(crate) trait FileRangeExt {
/// Converts this file range to an `LspRange`, which then requires an explicit /// Converts this file range to an `LspRange`, which then requires an explicit
/// decision about how to use it (as a local range or as a location). /// decision about how to use it (as a local range or as a location).
fn as_lsp_range<'db>(&self, db: &'db dyn Db, encoding: PositionEncoding) -> LspRange<'db>; fn to_lsp_range(&self, db: &dyn Db, encoding: PositionEncoding) -> Option<LspRange>;
} }
impl FileRangeExt for FileRange { impl FileRangeExt for FileRange {
fn as_lsp_range<'db>(&self, db: &'db dyn Db, encoding: PositionEncoding) -> LspRange<'db> { fn to_lsp_range(&self, db: &dyn Db, encoding: PositionEncoding) -> Option<LspRange> {
LspRange { self.range().to_lsp_range(db, self.file(), encoding)
file: self.file(),
range: self.range(),
db,
encoding,
}
} }
} }

View File

@ -281,8 +281,9 @@ pub(super) fn to_lsp_diagnostic(
let file = span.expect_ty_file(); let file = span.expect_ty_file();
span.range() span.range()
.map(|range| range.as_lsp_range(db, file, encoding).to_local_range()) .and_then(|range| range.to_lsp_range(db, file, encoding))
.unwrap_or_default() .unwrap_or_default()
.local_range()
} else { } else {
Range::default() Range::default()
}; };
@ -363,7 +364,7 @@ fn annotation_to_related_information(
let annotation_message = annotation.get_message()?; let annotation_message = annotation.get_message()?;
let range = FileRange::try_from(span).ok()?; let range = FileRange::try_from(span).ok()?;
let location = range.as_lsp_range(db, encoding).to_location()?; let location = range.to_lsp_range(db, encoding)?.into_location()?;
Some(DiagnosticRelatedInformation { Some(DiagnosticRelatedInformation {
location, location,
@ -381,7 +382,7 @@ fn sub_diagnostic_to_related_information(
let span = primary_annotation.get_span(); let span = primary_annotation.get_span();
let range = FileRange::try_from(span).ok()?; let range = FileRange::try_from(span).ok()?;
let location = range.as_lsp_range(db, encoding).to_location()?; let location = range.to_lsp_range(db, encoding)?.into_location()?;
Some(DiagnosticRelatedInformation { Some(DiagnosticRelatedInformation {
location, location,

View File

@ -48,12 +48,15 @@ impl BackgroundDocumentRequestHandler for CompletionRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params.text_document_position.position.to_text_size( let Some(offset) = params.text_document_position.position.to_text_size(
db, db,
file, file,
snapshot.url(), snapshot.url(),
snapshot.encoding(), snapshot.encoding(),
); ) else {
return Ok(None);
};
let settings = CompletionSettings { let settings = CompletionSettings {
auto_import: snapshot.global_settings().is_auto_import_enabled(), auto_import: snapshot.global_settings().is_auto_import_enabled(),
}; };
@ -70,15 +73,15 @@ impl BackgroundDocumentRequestHandler for CompletionRequestHandler {
.map(|(i, comp)| { .map(|(i, comp)| {
let kind = comp.kind(db).map(ty_kind_to_lsp_kind); let kind = comp.kind(db).map(ty_kind_to_lsp_kind);
let type_display = comp.ty.map(|ty| ty.display(db).to_string()); let type_display = comp.ty.map(|ty| ty.display(db).to_string());
let import_edit = comp.import.as_ref().map(|edit| { let import_edit = comp.import.as_ref().and_then(|edit| {
let range = edit let range = edit
.range() .range()
.as_lsp_range(db, file, snapshot.encoding()) .to_lsp_range(db, file, snapshot.encoding())?
.to_local_range(); .local_range();
TextEdit { Some(TextEdit {
range, range,
new_text: edit.content().map(ToString::to_string).unwrap_or_default(), new_text: edit.content().map(ToString::to_string).unwrap_or_default(),
} })
}); });
let name = comp.name.to_string(); let name = comp.name.to_string();

View File

@ -40,12 +40,14 @@ impl BackgroundDocumentRequestHandler for DocumentHighlightRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params.text_document_position_params.position.to_text_size( let Some(offset) = params.text_document_position_params.position.to_text_size(
db, db,
file, file,
snapshot.url(), snapshot.url(),
snapshot.encoding(), snapshot.encoding(),
); ) else {
return Ok(None);
};
let Some(highlights_result) = document_highlights(db, file, offset) else { let Some(highlights_result) = document_highlights(db, file, offset) else {
return Ok(None); return Ok(None);
@ -53,11 +55,11 @@ impl BackgroundDocumentRequestHandler for DocumentHighlightRequestHandler {
let highlights: Vec<_> = highlights_result let highlights: Vec<_> = highlights_result
.into_iter() .into_iter()
.map(|target| { .filter_map(|target| {
let range = target let range = target
.range() .range()
.as_lsp_range(db, file, snapshot.encoding()) .to_lsp_range(db, file, snapshot.encoding())?
.to_local_range(); .local_range();
let kind = match target.kind() { let kind = match target.kind() {
ReferenceKind::Read => Some(DocumentHighlightKind::READ), ReferenceKind::Read => Some(DocumentHighlightKind::READ),
@ -65,7 +67,7 @@ impl BackgroundDocumentRequestHandler for DocumentHighlightRequestHandler {
ReferenceKind::Other => Some(DocumentHighlightKind::TEXT), ReferenceKind::Other => Some(DocumentHighlightKind::TEXT),
}; };
DocumentHighlight { range, kind } Some(DocumentHighlight { range, kind })
}) })
.collect(); .collect();

View File

@ -57,7 +57,7 @@ impl BackgroundDocumentRequestHandler for DocumentSymbolRequestHandler {
let symbols = symbols.to_hierarchical(); let symbols = symbols.to_hierarchical();
let lsp_symbols: Vec<DocumentSymbol> = symbols let lsp_symbols: Vec<DocumentSymbol> = symbols
.iter() .iter()
.map(|(id, symbol)| { .filter_map(|(id, symbol)| {
convert_to_lsp_document_symbol( convert_to_lsp_document_symbol(
db, db,
file, file,
@ -93,10 +93,10 @@ fn convert_to_lsp_document_symbol(
id: SymbolId, id: SymbolId,
symbol: SymbolInfo<'_>, symbol: SymbolInfo<'_>,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> DocumentSymbol { ) -> Option<DocumentSymbol> {
let symbol_kind = convert_symbol_kind(symbol.kind); let symbol_kind = convert_symbol_kind(symbol.kind);
DocumentSymbol { Some(DocumentSymbol {
name: symbol.name.into_owned(), name: symbol.name.into_owned(),
detail: None, detail: None,
kind: symbol_kind, kind: symbol_kind,
@ -105,19 +105,19 @@ fn convert_to_lsp_document_symbol(
deprecated: None, deprecated: None,
range: symbol range: symbol
.full_range .full_range
.as_lsp_range(db, file, encoding) .to_lsp_range(db, file, encoding)?
.to_local_range(), .local_range(),
selection_range: symbol selection_range: symbol
.name_range .name_range
.as_lsp_range(db, file, encoding) .to_lsp_range(db, file, encoding)?
.to_local_range(), .local_range(),
children: Some( children: Some(
symbols symbols
.children(id) .children(id)
.map(|(child_id, child)| { .filter_map(|(child_id, child)| {
convert_to_lsp_document_symbol(db, file, symbols, child_id, child, encoding) convert_to_lsp_document_symbol(db, file, symbols, child_id, child, encoding)
}) })
.collect(), .collect(),
), ),
} })
} }

View File

@ -40,12 +40,14 @@ impl BackgroundDocumentRequestHandler for GotoDeclarationRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params.text_document_position_params.position.to_text_size( let Some(offset) = params.text_document_position_params.position.to_text_size(
db, db,
file, file,
snapshot.url(), snapshot.url(),
snapshot.encoding(), snapshot.encoding(),
); ) else {
return Ok(None);
};
let Some(ranged) = goto_declaration(db, file, offset) else { let Some(ranged) = goto_declaration(db, file, offset) else {
return Ok(None); return Ok(None);

View File

@ -40,12 +40,14 @@ impl BackgroundDocumentRequestHandler for GotoDefinitionRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params.text_document_position_params.position.to_text_size( let Some(offset) = params.text_document_position_params.position.to_text_size(
db, db,
file, file,
snapshot.url(), snapshot.url(),
snapshot.encoding(), snapshot.encoding(),
); ) else {
return Ok(None);
};
let Some(ranged) = goto_definition(db, file, offset) else { let Some(ranged) = goto_definition(db, file, offset) else {
return Ok(None); return Ok(None);

View File

@ -40,12 +40,14 @@ impl BackgroundDocumentRequestHandler for ReferencesRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params.text_document_position.position.to_text_size( let Some(offset) = params.text_document_position.position.to_text_size(
db, db,
file, file,
snapshot.url(), snapshot.url(),
snapshot.encoding(), snapshot.encoding(),
); ) else {
return Ok(None);
};
let include_declaration = params.context.include_declaration; let include_declaration = params.context.include_declaration;

View File

@ -40,12 +40,14 @@ impl BackgroundDocumentRequestHandler for GotoTypeDefinitionRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params.text_document_position_params.position.to_text_size( let Some(offset) = params.text_document_position_params.position.to_text_size(
db, db,
file, file,
snapshot.url(), snapshot.url(),
snapshot.encoding(), snapshot.encoding(),
); ) else {
return Ok(None);
};
let Some(ranged) = goto_type_definition(db, file, offset) else { let Some(ranged) = goto_type_definition(db, file, offset) else {
return Ok(None); return Ok(None);

View File

@ -39,12 +39,14 @@ impl BackgroundDocumentRequestHandler for HoverRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params.text_document_position_params.position.to_text_size( let Some(offset) = params.text_document_position_params.position.to_text_size(
db, db,
file, file,
snapshot.url(), snapshot.url(),
snapshot.encoding(), snapshot.encoding(),
); ) else {
return Ok(None);
};
let Some(range_info) = hover(db, file, offset) else { let Some(range_info) = hover(db, file, offset) else {
return Ok(None); return Ok(None);
@ -66,12 +68,10 @@ impl BackgroundDocumentRequestHandler for HoverRequestHandler {
kind: lsp_markup_kind, kind: lsp_markup_kind,
value: contents, value: contents,
}), }),
range: Some( range: range_info
range_info
.file_range() .file_range()
.as_lsp_range(db, snapshot.encoding()) .to_lsp_range(db, snapshot.encoding())
.to_local_range(), .map(|lsp_range| lsp_range.local_range()),
),
})) }))
} }
} }

View File

@ -39,19 +39,23 @@ impl BackgroundDocumentRequestHandler for InlayHintRequestHandler {
return Ok(None); return Ok(None);
}; };
let range = params let Some(range) = params
.range .range
.to_text_range(db, file, snapshot.url(), snapshot.encoding()); .to_text_range(db, file, snapshot.url(), snapshot.encoding())
else {
return Ok(None);
};
let inlay_hints = inlay_hints(db, file, range, workspace_settings.inlay_hints()); let inlay_hints = inlay_hints(db, file, range, workspace_settings.inlay_hints());
let inlay_hints = inlay_hints let inlay_hints = inlay_hints
.into_iter() .into_iter()
.map(|hint| lsp_types::InlayHint { .filter_map(|hint| {
Some(lsp_types::InlayHint {
position: hint position: hint
.position .position
.as_lsp_position(db, file, snapshot.encoding()) .to_lsp_position(db, file, snapshot.encoding())?
.to_local_position(), .local_position(),
label: inlay_hint_label(&hint.label), label: inlay_hint_label(&hint.label),
kind: Some(inlay_hint_kind(&hint.kind)), kind: Some(inlay_hint_kind(&hint.kind)),
tooltip: None, tooltip: None,
@ -60,6 +64,7 @@ impl BackgroundDocumentRequestHandler for InlayHintRequestHandler {
data: None, data: None,
text_edits: None, text_edits: None,
}) })
})
.collect(); .collect();
Ok(Some(inlay_hints)) Ok(Some(inlay_hints))

View File

@ -40,17 +40,24 @@ impl BackgroundDocumentRequestHandler for PrepareRenameRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params let Some(offset) =
params
.position .position
.to_text_size(db, file, snapshot.url(), snapshot.encoding()); .to_text_size(db, file, snapshot.url(), snapshot.encoding())
else {
return Ok(None);
};
let Some(range) = can_rename(db, file, offset) else { let Some(range) = can_rename(db, file, offset) else {
return Ok(None); return Ok(None);
}; };
let lsp_range = range let Some(lsp_range) = range
.as_lsp_range(db, file, snapshot.encoding()) .to_lsp_range(db, file, snapshot.encoding())
.to_local_range(); .map(|lsp_range| lsp_range.local_range())
else {
return Ok(None);
};
Ok(Some(PrepareRenameResponse::Range(lsp_range))) Ok(Some(PrepareRenameResponse::Range(lsp_range)))
} }

View File

@ -41,12 +41,14 @@ impl BackgroundDocumentRequestHandler for RenameRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params.text_document_position.position.to_text_size( let Some(offset) = params.text_document_position.position.to_text_size(
db, db,
file, file,
snapshot.url(), snapshot.url(),
snapshot.encoding(), snapshot.encoding(),
); ) else {
return Ok(None);
};
let Some(rename_results) = rename(db, file, offset, &params.new_name) else { let Some(rename_results) = rename(db, file, offset, &params.new_name) else {
return Ok(None); return Ok(None);

View File

@ -43,17 +43,25 @@ impl BackgroundDocumentRequestHandler for SelectionRangeRequestHandler {
let mut results = Vec::new(); let mut results = Vec::new();
for position in params.positions { for position in params.positions {
let offset = position.to_text_size(db, file, snapshot.url(), snapshot.encoding()); let Some(offset) = position.to_text_size(db, file, snapshot.url(), snapshot.encoding())
else {
continue;
};
let ranges = selection_range(db, file, offset); let ranges = selection_range(db, file, offset);
if !ranges.is_empty() { if !ranges.is_empty() {
// Convert ranges to nested LSP SelectionRange structure // Convert ranges to nested LSP SelectionRange structure
let mut lsp_range = None; let mut lsp_range = None;
for &range in &ranges { for &range in &ranges {
let Some(range) = range
.to_lsp_range(db, file, snapshot.encoding())
.map(|lsp_range| lsp_range.local_range())
else {
break;
};
lsp_range = Some(LspSelectionRange { lsp_range = Some(LspSelectionRange {
range: range range,
.as_lsp_range(db, file, snapshot.encoding())
.to_local_range(),
parent: lsp_range.map(Box::new), parent: lsp_range.map(Box::new),
}); });
} }

View File

@ -39,10 +39,13 @@ impl BackgroundDocumentRequestHandler for SemanticTokensRangeRequestHandler {
}; };
// Convert LSP range to text offsets // Convert LSP range to text offsets
let requested_range = let Some(requested_range) =
params params
.range .range
.to_text_range(db, file, snapshot.url(), snapshot.encoding()); .to_text_range(db, file, snapshot.url(), snapshot.encoding())
else {
return Ok(None);
};
let lsp_tokens = generate_semantic_tokens( let lsp_tokens = generate_semantic_tokens(
db, db,

View File

@ -42,12 +42,14 @@ impl BackgroundDocumentRequestHandler for SignatureHelpRequestHandler {
return Ok(None); return Ok(None);
}; };
let offset = params.text_document_position_params.position.to_text_size( let Some(offset) = params.text_document_position_params.position.to_text_size(
db, db,
file, file,
snapshot.url(), snapshot.url(),
snapshot.encoding(), snapshot.encoding(),
); ) else {
return Ok(None);
};
// Extract signature help capabilities from the client // Extract signature help capabilities from the client
let resolved_capabilities = snapshot.resolved_client_capabilities(); let resolved_capabilities = snapshot.resolved_client_capabilities();

View File

@ -24,10 +24,14 @@ pub(crate) fn generate_semantic_tokens(
let mut prev_start = 0u32; let mut prev_start = 0u32;
for token in &*semantic_token_data { for token in &*semantic_token_data {
let lsp_range = token let Some(lsp_range) = token
.range() .range()
.as_lsp_range(db, file, encoding) .to_lsp_range(db, file, encoding)
.to_local_range(); .map(|lsp_range| lsp_range.local_range())
else {
continue;
};
let line = lsp_range.start.line; let line = lsp_range.start.line;
let character = lsp_range.start.character; let character = lsp_range.start.character;

View File

@ -36,7 +36,7 @@ pub(crate) fn convert_to_lsp_symbol_information(
let location = symbol let location = symbol
.full_range .full_range
.as_lsp_range(db, file, encoding) .to_lsp_range(db, file, encoding)?
.to_location()?; .to_location()?;
Some(SymbolInformation { Some(SymbolInformation {