perf(rome_rowan): `SyntaxNodeText` improvements
MODIFY
crates/rome_diagnostics/src/file.rs
crates/rome_diagnostics/src/file.rs
@@ -39,23 +39,19 @@ pub trait Span {
}
fn sub_start(&self, amount: TextSize) -> TextRange {
- let range = self.as_range();
- TextRange::new(range.start() - amount, range.end())
+ self.as_range().sub_start(amount)
}
fn add_start(&self, amount: TextSize) -> TextRange {
- let range = self.as_range();
- TextRange::new(range.start() + amount, range.end())
+ self.as_range().add_start(amount)
}
fn sub_end(&self, amount: TextSize) -> TextRange {
- let range = self.as_range();
- TextRange::new(range.start(), range.end() - amount)
+ self.as_range().sub_end(amount)
}
fn add_end(&self, amount: TextSize) -> TextRange {
- let range = self.as_range();
- TextRange::new(range.start(), range.end() + amount)
+ self.as_range().add_end(amount)
}
}

MODIFY
crates/rome_js_analyze/src/semantic_analyzers/style/no_shouty_constants.rs
crates/rome_js_analyze/src/semantic_analyzers/style/no_shouty_constants.rs
@@ -57,13 +57,14 @@ fn is_id_and_string_literal_inner_text_equal(
) -> Option<(JsIdentifierBinding, JsStringLiteralExpression)> {
let id = declarator.id().ok()?;
let id = id.as_js_any_binding()?.as_js_identifier_binding()?;
- let id_text = id.syntax().text_trimmed();
+ let name = id.name_token().ok()?;
+ let id_text = name.text_trimmed();
let expression = declarator.initializer()?.expression().ok()?;
let literal = expression
.as_js_any_literal_expression()?
.as_js_string_literal_expression()?;
- let literal_text = literal.inner_string_text();
+ let literal_text = literal.inner_string_text().ok()?;
for (from_id, from_literal) in id_text.chars().zip(literal_text.chars()) {
if from_id != from_literal {

MODIFY
crates/rome_js_analyze/tests/spec_tests.rs
crates/rome_js_analyze/tests/spec_tests.rs
@@ -31,7 +31,7 @@ fn run_test(input: &'static str, _: &str, _: &str, _: &str) {
let parsed = parse(&input_code, 0, source_type);
let root = parsed.tree();
- let (group, rule) = dbg!(parse_test_path(input_file));
+ let (group, rule) = parse_test_path(input_file);
let rule_filter = RuleFilter::Rule(group, rule);
let filter = AnalysisFilter {

MODIFY
crates/rome_js_syntax/src/expr_ext.rs
crates/rome_js_syntax/src/expr_ext.rs
@@ -11,7 +11,7 @@ use crate::{
use crate::{JsPreUpdateExpression, JsSyntaxKind::*};
use core::iter;
use rome_rowan::{
- AstNode, AstSeparatedList, NodeOrToken, SyntaxNodeText, SyntaxResult, TextRange, TextSize,
+ AstNode, AstSeparatedList, NodeOrToken, SyntaxResult, SyntaxTokenText, TextRange, TextSize,
};
impl JsReferenceIdentifier {
@@ -449,24 +449,23 @@ impl JsNumberLiteralExpression {
impl JsStringLiteralExpression {
/// Get the inner text of a string not including the quotes
- pub fn inner_string_text(&self) -> SyntaxNodeText {
- let start = self.syntax().text_range().start() + TextSize::from(1);
- let end_char = self
- .syntax()
- .text()
- .char_at(self.syntax().text().len() - TextSize::from(1))
- .unwrap();
- let end = if end_char == '"' || end_char == '\'' {
- self.syntax().text_range().end() - TextSize::from(1)
- } else {
- self.syntax().text_range().end()
- };
+ pub fn inner_string_text(&self) -> SyntaxResult<SyntaxTokenText> {
+ let value = self.value_token()?;
+ let mut text = value.token_text();
- let offset = self.syntax().text_range().start();
+ static QUOTES: [char; 2] = ['"', '\''];
- self.syntax()
- .text()
- .slice(TextRange::new(start - offset, end - offset))
+ if text.starts_with(QUOTES) {
+ let range = text.range().add_start(TextSize::from(1));
+ text = text.slice(range);
+ }
+
+ if text.ends_with(QUOTES) {
+ let range = text.range().sub_end(TextSize::from(1));
+ text = text.slice(range);
+ }
+
+ Ok(text)
}
}

MODIFY
crates/rome_rowan/src/cursor/node.rs
crates/rome_rowan/src/cursor/node.rs
@@ -335,8 +335,7 @@ impl SyntaxNode {
let mut children = self.children_with_tokens().filter(|child| {
let child_range = child.text_range();
- !child_range.is_empty()
- && (child_range.start() <= offset && offset <= child_range.end())
+ !child_range.is_empty() && child_range.contains_inclusive(offset)
});
let left = children.next().unwrap();
@@ -603,16 +602,6 @@ impl PreorderWithTokens {
WalkEvent::Leave(parent) => WalkEvent::Leave(parent),
})
}
-
- pub fn until_next_token(&mut self) -> Option<SyntaxToken> {
- loop {
- match self.next() {
- Some(crate::WalkEvent::Enter(NodeOrToken::Token(t))) => break Some(t),
- None => break None,
- _ => continue,
- }
- }
- }
}
impl Iterator for PreorderWithTokens {

MODIFY
crates/rome_rowan/src/syntax_node_text.rs
crates/rome_rowan/src/syntax_node_text.rs
use crate::{
cursor::{SyntaxNode, SyntaxToken},
- Direction, TextRange, TextSize,
+ TextRange, TextSize, TokenAtOffset,
};
+use rome_text_size::TextLen;
use std::fmt;
+use std::iter::FusedIterator;
#[derive(Clone)]
pub struct SyntaxNodeText {
@@ -114,54 +116,79 @@ impl SyntaxNodeText {
}
}
- fn tokens_with_ranges(&self) -> impl Iterator<Item = (SyntaxToken, TextRange)> {
- let text_range = self.range;
- self.node
- .descendants_with_tokens(Direction::Next)
- .filter_map(|element| element.into_token())
- .filter_map(move |token| {
+ fn tokens_with_ranges(&self) -> impl Iterator<Item = (SyntaxToken, TextRange)> + FusedIterator {
+ SyntaxNodeTokenWithRanges::new(self)
+ }
+
+ pub fn chars(&self) -> impl Iterator<Item = char> + FusedIterator {
+ SyntaxNodeTextChars::new(self)
+ }
+}
+
+#[derive(Clone)]
+struct SyntaxNodeTokenWithRanges {
+ text_range: TextRange,
+ next_token: Option<(SyntaxToken, TextRange)>,
+}
+
+impl SyntaxNodeTokenWithRanges {
+ fn new(text: &SyntaxNodeText) -> Self {
+ let text_range = text.range;
+
+ let token = match text.node.token_at_offset(text_range.start()) {
+ TokenAtOffset::None => None,
+ TokenAtOffset::Single(token) => Some(token),
+ TokenAtOffset::Between(_, next) => Some(next),
+ };
+
+ Self {
+ next_token: token.and_then(|token| Self::with_intersecting_range(token, text_range)),
+ text_range,
+ }
+ }
+
+ fn with_intersecting_range(
+ token: SyntaxToken,
+ text_range: TextRange,
+ ) -> Option<(SyntaxToken, TextRange)> {
let token_range = token.text_range();
+
let range = text_range.intersect(token_range)?;
Some((token, range - token_range.start()))
- })
+ }
}
- pub fn chars(&self) -> impl Iterator<Item = char> {
- let mut iter = SyntaxNodeTextChars {
- range: self.range,
- iter: self.node.preorder_with_tokens(Direction::Next),
- token: None,
- index: self.range.start().into(),
- };
- iter.advance_token();
- iter
+impl Iterator for SyntaxNodeTokenWithRanges {
+ type Item = (SyntaxToken, TextRange);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let (token, range) = self.next_token.take()?;
+
+ self.next_token = token
+ .next_token()
+ .and_then(|token| Self::with_intersecting_range(token, self.text_range));
+
+ Some((token, range))
}
}
+impl FusedIterator for SyntaxNodeTokenWithRanges {}
+
+#[derive(Clone)]
struct SyntaxNodeTextChars {
- range: TextRange,
- iter: crate::cursor::PreorderWithTokens,
- token: Option<(SyntaxToken, TextRange)>,
- index: usize,
+ head: Option<(SyntaxToken, TextRange)>,
+ tail: SyntaxNodeTokenWithRanges,
+ index: TextSize,
}
impl SyntaxNodeTextChars {
- fn advance_token(&mut self) {
- loop {
- self.token = self.iter.until_next_token().map(|x| {
- let range = x.text_range();
- (x, range)
- });
+ fn new(text: &SyntaxNodeText) -> Self {
+ let mut chunks = SyntaxNodeTokenWithRanges::new(text);
- let intersection = self
- .token
- .as_ref()
- .and_then(|(_, range)| range.intersect(self.range));
- if intersection.is_none() {
- continue;
- } else {
- break;
- }
+ Self {
+ head: chunks.next(),
+ tail: chunks,
+ index: TextSize::default(),
}
}
}
@@ -171,30 +198,30 @@ impl Iterator for SyntaxNodeTextChars {
fn next(&mut self) -> Option<Self::Item> {
loop {
- let end: usize = self.range.end().into();
- if self.index >= end {
- return None;
+ let (token, range) = self.head.as_ref()?;
+
+ if self.index >= range.end() {
+ self.head = self.tail.next();
+ self.index = TextSize::default();
+ continue;
}
- let (token, range) = self.token.as_ref()?;
let text = token.text();
- let start: usize = range.start().into();
- let next_char = text[self.index - start..].chars().next();
- match next_char {
- Some(chr) => {
- self.index += chr.len_utf8();
- break Some(chr);
- }
- None => {
- self.advance_token();
- continue;
- }
- }
+ // SAFETY: Index check above guarantees that there's at least some text left
+ let next_char = text[TextRange::new(self.index, range.end())]
+ .chars()
+ .next()
+ .unwrap();
+
+ self.index += next_char.text_len();
+ break Some(next_char);
}
}
}
+impl FusedIterator for SyntaxNodeTextChars {}
+
fn found<T>(res: Result<(), T>) -> Option<T> {
match res {
Ok(()) => None,
@@ -391,4 +418,27 @@ mod tests {
check(&["{", "abc", "}"], &["{", "123", "}", "{"]);
check(&["{", "abc", "}ab"], &["{", "abc", "}", "ab"]);
}
+
+ #[test]
+ fn test_chars() {
+ fn check(t1: &[&str], expected: &str) {
+ let t1 = build_tree(t1).text();
+ let actual = t1.chars().collect::<String>();
+
+ assert_eq!(
+ expected, &actual,
+ "`{}` (SyntaxText) `{}` (SyntaxText)",
+ actual, expected
+ );
+ }
+
+ check(&[""], "");
+ check(&["a"], "a");
+ check(&["hello", "world"], "helloworld");
+ check(&["hellowo", "rld"], "helloworld");
+ check(&["hel", "lowo", "rld"], "helloworld");
+ check(&["{", "abc", "}"], "{abc}");
+ check(&["{", "abc", "}", "{"], "{abc}{");
+ check(&["{", "abc", "}ab"], "{abc}ab");
+ }
}

MODIFY
crates/rome_rowan/src/syntax_token_text.rs
crates/rome_rowan/src/syntax_token_text.rs
@@ -51,6 +51,10 @@ impl SyntaxTokenText {
self
}
+ pub fn range(&self) -> TextRange {
+ self.range
+ }
+
pub fn text(&self) -> &str {
&self.token.text()[self.range]
}

MODIFY
crates/rome_service/src/configuration/mod.rs
crates/rome_service/src/configuration/mod.rs
@@ -143,8 +143,6 @@ pub fn load_config(
.map_err(|_| RomeError::CantReadFile(configuration_path))?;
let configuration: Configuration = serde_json::from_str(&buffer).map_err(|err| {
- dbg!(&err);
-
RomeError::Configuration(ConfigurationError::DeserializationError(err.to_string()))
})?;

MODIFY
crates/rome_text_size/src/range.rs
crates/rome_text_size/src/range.rs
@@ -340,6 +340,81 @@ impl TextRange {
Ordering::Equal
}
}
+
+ /// Subtracts an offset from the start position.
+ ///
+ ///
+ /// ## Panics
+ /// If `start - amount` is less than zero.
+ ///
+ /// ## Examples
+ ///
+ /// ```
+ /// use rome_text_size::{TextRange, TextSize};
+ ///
+ /// let range = TextRange::new(TextSize::from(5), TextSize::from(10));
+ /// assert_eq!(range.sub_start(TextSize::from(2)), TextRange::new(TextSize::from(3), TextSize::from(10)));
+ /// ```
+ #[inline]
+ pub fn sub_start(&self, amount: TextSize) -> TextRange {
+ TextRange::new(self.start() - amount, self.end())
+ }
+
+ /// Adds an offset to the start position.
+ ///
+ /// ## Panics
+ /// If `start + amount > end`
+ ///
+ /// ## Examples
+ ///
+ /// ```
+ /// use rome_text_size::{TextRange, TextSize};
+ ///
+ /// let range = TextRange::new(TextSize::from(5), TextSize::from(10));
+ /// assert_eq!(range.add_start(TextSize::from(3)), TextRange::new(TextSize::from(8), TextSize::from(10)));
+ /// ```
+ #[inline]
+ pub fn add_start(&self, amount: TextSize) -> TextRange {
+ TextRange::new(self.start() + amount, self.end())
+ }
+
+ /// Subtracts an offset from the end position.
+ ///
+ ///
+ /// ## Panics
+ /// If `end - amount < 0` or `end - amount < start`
+ ///
+ /// ## Examples
+ ///
+ /// ```
+ /// use rome_text_size::{TextRange, TextSize};
+ ///
+ /// let range = TextRange::new(TextSize::from(5), TextSize::from(10));
+ /// assert_eq!(range.sub_end(TextSize::from(2)), TextRange::new(TextSize::from(5), TextSize::from(8)));
+ /// ```
+ #[inline]
+ pub fn sub_end(&self, amount: TextSize) -> TextRange {
+ TextRange::new(self.start(), self.end() - amount)
+ }
+
+ /// Adds an offset to the end position.
+ ///
+ ///
+ /// ## Panics
+ /// If `end + amount > u32::MAX`
+ ///
+ /// ## Examples
+ ///
+ /// ```
+ /// use rome_text_size::{TextRange, TextSize};
+ ///
+ /// let range = TextRange::new(TextSize::from(5), TextSize::from(10));
+ /// assert_eq!(range.add_end(TextSize::from(2)), TextRange::new(TextSize::from(5), TextSize::from(12)));
+ /// ```
+ #[inline]
+ pub fn add_end(&self, amount: TextSize) -> TextRange {
+ TextRange::new(self.start(), self.end() + amount)
+ }
}
impl Index<TextRange> for str {

perf(rome_rowan): `SyntaxNodeText` improvements (#3217)