@@ -238,13 +238,18 @@ def visitField(self, field, parent, vis, depth, constructor=None):
238238 if fieldtype and fieldtype .has_userdata :
239239 typ = f"{ typ } <U>"
240240 # don't box if we're doing Vec<T>, but do box if we're doing Vec<Option<Box<T>>>
241- if fieldtype and fieldtype .boxed and (not (parent .product or field .seq ) or field .opt ):
241+ if (
242+ fieldtype
243+ and fieldtype .boxed
244+ and (not (parent .product or field .seq ) or field .opt )
245+ ):
242246 typ = f"Box<{ typ } >"
243247 if field .opt or (
244248 # When a dictionary literal contains dictionary unpacking (e.g., `{**d}`),
245249 # the expression to be unpacked goes in `values` with a `None` at the corresponding
246250 # position in `keys`. To handle this, the type of `keys` needs to be `Option<Vec<T>>`.
247- constructor == "Dict" and field .name == "keys"
251+ constructor == "Dict"
252+ and field .name == "keys"
248253 ):
249254 typ = f"Option<{ typ } >"
250255 if field .seq :
@@ -311,7 +316,7 @@ def visitModule(self, mod, depth):
311316 depth ,
312317 )
313318 self .emit (
314- "Ok(Located { custom: folder.map_user(node.custom)?, location : node.location, end_location: node.end_location , node: f(folder, node.node)? })" ,
319+ "Ok(Located { custom: folder.map_user(node.custom)?, range : node.range , node: f(folder, node.node)? })" ,
315320 depth + 1 ,
316321 )
317322 self .emit ("}" , depth )
@@ -649,7 +654,7 @@ def write_ast_def(mod, typeinfo, f):
649654 #![allow(clippy::derive_partial_eq_without_eq)]
650655
651656 pub use crate::constant::*;
652- pub use crate::Location ;
657+ pub use ruff_text_size::{TextSize, TextRange} ;
653658
654659 type Ident = String;
655660 \n
@@ -661,26 +666,54 @@ def write_ast_def(mod, typeinfo, f):
661666 textwrap .dedent (
662667 """
663668 pub struct Located<T, U = ()> {
664- pub location: Location,
665- pub end_location: Option<Location>,
669+ pub range: TextRange,
666670 pub custom: U,
667671 pub node: T,
668672 }
669673
670674 impl<T> Located<T> {
671- pub fn new(location: Location, end_location: Location , node: T) -> Self {
672- Self { location, end_location: Some(end_location ), custom: (), node }
675+ pub fn new(start: TextSize, end: TextSize , node: T) -> Self {
676+ Self { range: TextRange::new(start, end ), custom: (), node }
673677 }
674678
675- pub const fn start(&self) -> Location {
676- self.location
679+ /// Creates a new node that spans the position specified by `range`.
680+ pub fn with_range(node: T, range: TextRange) -> Self {
681+ Self {
682+ range,
683+ custom: (),
684+ node,
685+ }
686+ }
687+
688+ /// Returns the absolute start position of the node from the beginning of the document.
689+ #[inline]
690+ pub const fn start(&self) -> TextSize {
691+ self.range.start()
692+ }
693+
694+ /// Returns the node
695+ #[inline]
696+ pub fn node(&self) -> &T {
697+ &self.node
698+ }
699+
700+ /// Consumes self and returns the node.
701+ #[inline]
702+ pub fn into_node(self) -> T {
703+ self.node
704+ }
705+
706+ /// Returns the `range` of the node. The range offsets are absolute to the start of the document.
707+ #[inline]
708+ pub const fn range(&self) -> TextRange {
709+ self.range
710+ }
711+
712+ /// Returns the absolute position at which the node ends in the source document.
713+ #[inline]
714+ pub const fn end(&self) -> TextSize {
715+ self.range.end()
677716 }
678-
679- /// Returns the node's [`end_location`](Located::end_location) or [`location`](Located::start) if
680- /// [`end_location`](Located::end_location) is `None`.
681- pub fn end(&self) -> Location {
682- self.end_location.unwrap_or(self.location)
683- }
684717 }
685718
686719 impl<T, U> std::ops::Deref for Located<T, U> {
0 commit comments