Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
perf(rome_rowan): SyntaxNodeText improvements (#3217)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser authored Sep 14, 2022
1 parent 9bca1e0 commit 35d293f
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 97 deletions.
12 changes: 4 additions & 8 deletions crates/rome_diagnostics/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion crates/rome_js_analyze/tests/spec_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
33 changes: 16 additions & 17 deletions crates/rome_js_syntax/src/expr_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
}

Expand Down
13 changes: 1 addition & 12 deletions crates/rome_rowan/src/cursor/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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 {
Expand Down
160 changes: 105 additions & 55 deletions crates/rome_rowan/src/syntax_node_text.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
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 {
Expand Down Expand Up @@ -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| {
let token_range = token.text_range();
let range = text_range.intersect(token_range)?;
Some((token, range - token_range.start()))
})
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)
}
}

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(),
#[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),
};
iter.advance_token();
iter

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()))
}
}

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)
});

let intersection = self
.token
.as_ref()
.and_then(|(_, range)| range.intersect(self.range));
if intersection.is_none() {
continue;
} else {
break;
}
fn new(text: &SyntaxNodeText) -> Self {
let mut chunks = SyntaxNodeTokenWithRanges::new(text);

Self {
head: chunks.next(),
tail: chunks,
index: TextSize::default(),
}
}
}
Expand All @@ -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,
Expand Down Expand Up @@ -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");
}
}
4 changes: 4 additions & 0 deletions crates/rome_rowan/src/syntax_token_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ impl SyntaxTokenText {
self
}

pub fn range(&self) -> TextRange {
self.range
}

pub fn text(&self) -> &str {
&self.token.text()[self.range]
}
Expand Down
2 changes: 0 additions & 2 deletions crates/rome_service/src/configuration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
})?;

Expand Down
Loading

0 comments on commit 35d293f

Please sign in to comment.