Skip to content

Commit 52af0db

Browse files
committed
API to binary search child by range
1 parent 78587c2 commit 52af0db

File tree

4 files changed

+54
-11
lines changed

4 files changed

+54
-11
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ edition = "2018"
1010
[dependencies]
1111
rustc-hash = "1.0.1"
1212
smol_str = "0.1.10"
13-
text-size = "1.0.0"
13+
text-size = "1.1.0"
1414
triomphe = "0.1.1"
1515

1616
serde = { version = "1.0.89", optional = true, default-features = false }

src/api.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,10 @@ impl<L: Language> SyntaxNode<L> {
274274
pub fn covering_element(&self, range: TextRange) -> SyntaxElement<L> {
275275
NodeOrToken::from(self.raw.covering_element(range))
276276
}
277+
278+
pub fn child_or_token_at_range(&self, range: TextRange) -> Option<SyntaxElement<L>> {
279+
self.raw.child_or_token_at_range(range).map(SyntaxElement::from)
280+
}
277281
}
278282

279283
impl<L: Language> SyntaxToken<L> {

src/cursor.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -470,18 +470,29 @@ impl SyntaxNode {
470470
);
471471
res = match &res {
472472
NodeOrToken::Token(_) => return res,
473-
NodeOrToken::Node(node) => {
474-
match node
475-
.children_with_tokens()
476-
.find(|child| child.text_range().contains_range(range))
477-
{
478-
Some(child) => child,
479-
None => return res,
480-
}
481-
}
473+
NodeOrToken::Node(node) => match node.child_or_token_at_range(range) {
474+
Some(it) => it,
475+
None => return res,
476+
},
482477
};
483478
}
484479
}
480+
481+
pub fn child_or_token_at_range(&self, range: TextRange) -> Option<SyntaxElement> {
482+
let start_offset = self.text_range().start();
483+
let (index, offset, child) = self.green().child_at_range(range - start_offset)?;
484+
let index = index as u32;
485+
let offset = offset + start_offset;
486+
let res: SyntaxElement = match child {
487+
NodeOrToken::Node(node) => {
488+
let data =
489+
NodeData::new(Kind::Child { parent: self.clone(), index, offset }, node.into());
490+
SyntaxNode::new(data).into()
491+
}
492+
NodeOrToken::Token(_token) => SyntaxToken::new(self.clone(), index, offset).into(),
493+
};
494+
Some(res)
495+
}
485496
}
486497

487498
impl SyntaxToken {

src/green/node.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use triomphe::{Arc, ThinArc};
44

55
use crate::{
66
green::{GreenElement, GreenElementRef, SyntaxKind},
7-
GreenToken, NodeOrToken, TextSize,
7+
GreenToken, NodeOrToken, TextRange, TextSize,
88
};
99

1010
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -43,6 +43,16 @@ impl GreenChild {
4343
GreenChild::Token { token, .. } => NodeOrToken::Token(token),
4444
}
4545
}
46+
fn offset_in_parent(&self) -> TextSize {
47+
match self {
48+
GreenChild::Node { offset_in_parent, .. }
49+
| GreenChild::Token { offset_in_parent, .. } => *offset_in_parent,
50+
}
51+
}
52+
fn range_in_parent(&self) -> TextRange {
53+
let len = self.as_ref().text_len();
54+
TextRange::at(self.offset_in_parent(), len)
55+
}
4656
}
4757

4858
#[cfg(target_pointer_width = "64")]
@@ -101,6 +111,24 @@ impl GreenNode {
101111
Children { inner: self.data.slice.iter() }
102112
}
103113

114+
pub(crate) fn child_at_range(
115+
&self,
116+
range: TextRange,
117+
) -> Option<(usize, TextSize, GreenElementRef<'_>)> {
118+
let idx = self
119+
.data
120+
.slice
121+
.binary_search_by(|it| {
122+
let child_range = it.range_in_parent();
123+
TextRange::ordering(child_range, range)
124+
})
125+
// XXX: this handles empty ranges
126+
.unwrap_or_else(|it| it.saturating_sub(1));
127+
let child =
128+
&self.data.slice.get(idx).filter(|it| it.range_in_parent().contains_range(range))?;
129+
Some((idx, child.offset_in_parent(), child.as_ref()))
130+
}
131+
104132
pub fn ptr(&self) -> *const c_void {
105133
self.data.heap_ptr()
106134
}

0 commit comments

Comments
 (0)