Skip to content

Commit be215ee

Browse files
committed
take into account empty tokesn
also implements token_at_offset TODO and adds a test
1 parent 6cb3d2e commit be215ee

File tree

1 file changed

+88
-20
lines changed

1 file changed

+88
-20
lines changed

Diff for: src/cursor.rs

+88-20
Original file line numberDiff line numberDiff line change
@@ -442,9 +442,6 @@ impl SyntaxNode {
442442
/// Find a token in the subtree corresponding to this node, which covers the offset.
443443
/// Precondition: offset must be withing node's range.
444444
pub fn token_at_offset(&self, offset: TextUnit) -> TokenAtOffset<SyntaxToken> {
445-
// TODO: this could be faster if we first drill-down to node, and only
446-
// then switch to token search. We should also replace explicit
447-
// recursion with a loop.
448445
let range = self.text_range();
449446
assert!(
450447
range.start() <= offset && offset <= range.end(),
@@ -456,25 +453,37 @@ impl SyntaxNode {
456453
return TokenAtOffset::None;
457454
}
458455

459-
let mut children = self.children_with_tokens().filter(|child| {
460-
let child_range = child.text_range();
461-
!child_range.is_empty()
462-
&& (child_range.start() <= offset && offset <= child_range.end())
463-
});
464-
465-
let left = children.next().unwrap();
466-
let right = children.next();
467-
assert!(children.next().is_none());
468-
469-
if let Some(right) = right {
470-
match (left.token_at_offset(offset), right.token_at_offset(offset)) {
471-
(TokenAtOffset::Single(left), TokenAtOffset::Single(right)) => {
472-
TokenAtOffset::Between(left, right)
456+
let mut container_element = self.clone();
457+
'outer: loop {
458+
for child in container_element.children_with_tokens() {
459+
match child {
460+
SyntaxElement::Token(token) => {
461+
let range: TextRange = token.text_range();
462+
if range.contains_inclusive(offset) {
463+
return if offset == range.start() {
464+
match token.prev_token() {
465+
Some(prev_token) => TokenAtOffset::Between(prev_token, token),
466+
None => TokenAtOffset::Single(token),
467+
}
468+
} else if offset == range.end() {
469+
match token.next_token() {
470+
Some(next_token) => TokenAtOffset::Between(token, next_token),
471+
None => TokenAtOffset::Single(token),
472+
}
473+
} else {
474+
TokenAtOffset::Single(token)
475+
};
476+
}
477+
}
478+
SyntaxElement::Node(node) => {
479+
if node.text_range().contains_inclusive(offset) {
480+
container_element = node;
481+
continue 'outer;
482+
}
483+
}
473484
}
474-
_ => unreachable!(),
475485
}
476-
} else {
477-
left.token_at_offset(offset)
486+
unreachable!()
478487
}
479488
}
480489

@@ -796,3 +805,62 @@ fn filter_nodes<'a, I: Iterator<Item = (GreenElementRef<'a>, T)>, T>(
796805
NodeOrToken::Token(_) => None,
797806
})
798807
}
808+
809+
#[cfg(test)]
810+
mod tests {
811+
use crate::green::GreenNodeBuilder;
812+
813+
use super::*;
814+
815+
fn node(
816+
builder: &mut GreenNodeBuilder,
817+
create_children: impl FnOnce(&mut GreenNodeBuilder) -> (),
818+
) {
819+
builder.start_node(SyntaxKind(4));
820+
create_children(builder);
821+
builder.finish_node();
822+
}
823+
824+
fn token(builder: &mut GreenNodeBuilder, text: &str) {
825+
builder.token(SyntaxKind(7), SmolStr::from(text))
826+
}
827+
828+
#[test]
829+
fn token_at_offset_with_empty_token() {
830+
let mut builder = GreenNodeBuilder::new();
831+
node(&mut builder, |b| {
832+
node(b, |b| token(b, "A"));
833+
token(b, "+");
834+
node(b, |b| {
835+
node(b, |b| token(b, ""));
836+
token(b, "*");
837+
node(b, |b| token(b, "C"));
838+
});
839+
});
840+
let root = SyntaxNode::new_root(builder.finish());
841+
842+
fn check(actual: TokenAtOffset<SyntaxToken>, expected: TokenAtOffset<&str>) {
843+
match (actual, expected) {
844+
(
845+
TokenAtOffset::Between(actual_left, actual_right),
846+
TokenAtOffset::Between(expected_left, expected_right),
847+
) => {
848+
assert_eq!(actual_left.text(), expected_left);
849+
assert_eq!(actual_right.text(), expected_right);
850+
}
851+
(TokenAtOffset::Single(actual), TokenAtOffset::Single(expected)) => {
852+
assert_eq!(actual.text(), expected);
853+
}
854+
(actual, _) => {
855+
assert!(false, "unexpected {:#?}", actual);
856+
}
857+
}
858+
}
859+
860+
check(root.token_at_offset(0.into()), TokenAtOffset::Single("A"));
861+
check(root.token_at_offset(1.into()), TokenAtOffset::Between("A", "+"));
862+
check(root.token_at_offset(2.into()), TokenAtOffset::Between("+", ""));
863+
check(root.token_at_offset(3.into()), TokenAtOffset::Between("*", "C"));
864+
check(root.token_at_offset(4.into()), TokenAtOffset::Single("C"));
865+
}
866+
}

0 commit comments

Comments
 (0)