mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-27 04:15:08 +00:00
Avoid materializing strings
This commit is contained in:
parent
363f466627
commit
7e74af3226
7 changed files with 139 additions and 32 deletions
|
@ -29,8 +29,8 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce()
|
||||||
let right = non_trivia_sibling(comma, Direction::Forward)?;
|
let right = non_trivia_sibling(comma, Direction::Forward)?;
|
||||||
Some(move || {
|
Some(move || {
|
||||||
let mut edit = EditBuilder::new();
|
let mut edit = EditBuilder::new();
|
||||||
edit.replace(left.range(), right.text());
|
edit.replace(left.range(), right.text().to_string());
|
||||||
edit.replace(right.range(), left.text());
|
edit.replace(right.range(), left.text().to_string());
|
||||||
ActionResult {
|
ActionResult {
|
||||||
edit: edit.finish(),
|
edit: edit.finish(),
|
||||||
cursor_position: None,
|
cursor_position: None,
|
||||||
|
|
|
@ -15,16 +15,15 @@ use {ActionResult, EditBuilder, find_node_at_offset};
|
||||||
|
|
||||||
pub fn join_lines(file: &File, range: TextRange) -> ActionResult {
|
pub fn join_lines(file: &File, range: TextRange) -> ActionResult {
|
||||||
let range = if range.is_empty() {
|
let range = if range.is_empty() {
|
||||||
let text = file.syntax().text();
|
let syntax = file.syntax();
|
||||||
let text = &text[TextRange::from_to(range.start(), TextUnit::of_str(&text))];
|
let text = syntax.text().slice(range.start()..);
|
||||||
let pos = text.bytes().take_while(|&b| b != b'\n').count();
|
let pos = match text.find('\n') {
|
||||||
if pos == text.len() {
|
None => return ActionResult {
|
||||||
return ActionResult {
|
|
||||||
edit: EditBuilder::new().finish(),
|
edit: EditBuilder::new().finish(),
|
||||||
cursor_position: None
|
cursor_position: None
|
||||||
};
|
},
|
||||||
}
|
Some(pos) => pos
|
||||||
let pos: TextUnit = (pos as u32).into();
|
};
|
||||||
TextRange::offset_len(
|
TextRange::offset_len(
|
||||||
range.start() + pos,
|
range.start() + pos,
|
||||||
TextUnit::of_char('\n'),
|
TextUnit::of_char('\n'),
|
||||||
|
@ -129,7 +128,7 @@ fn join_lambda_body(
|
||||||
let expr = single_expr(block)?;
|
let expr = single_expr(block)?;
|
||||||
edit.replace(
|
edit.replace(
|
||||||
block_expr.syntax().range(),
|
block_expr.syntax().range(),
|
||||||
expr.syntax().text(),
|
expr.syntax().text().to_string(),
|
||||||
);
|
);
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl File {
|
||||||
pub fn incremental_reparse(&self, edit: &AtomEdit) -> Option<File> {
|
pub fn incremental_reparse(&self, edit: &AtomEdit) -> Option<File> {
|
||||||
let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?;
|
let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?;
|
||||||
let text = replace_range(
|
let text = replace_range(
|
||||||
node.text(),
|
node.text().to_string(),
|
||||||
edit.delete - node.range().start(),
|
edit.delete - node.range().start(),
|
||||||
&edit.insert,
|
&edit.insert,
|
||||||
);
|
);
|
||||||
|
@ -97,7 +97,7 @@ impl File {
|
||||||
Some(File::new(green_root, errors))
|
Some(File::new(green_root, errors))
|
||||||
}
|
}
|
||||||
fn full_reparse(&self, edit: &AtomEdit) -> File {
|
fn full_reparse(&self, edit: &AtomEdit) -> File {
|
||||||
let text = replace_range(self.syntax().text(), edit.delete, &edit.insert);
|
let text = replace_range(self.syntax().text().to_string(), edit.delete, &edit.insert);
|
||||||
File::parse(&text)
|
File::parse(&text)
|
||||||
}
|
}
|
||||||
pub fn ast(&self) -> ast::Root {
|
pub fn ast(&self) -> ast::Root {
|
||||||
|
|
|
@ -43,21 +43,13 @@ impl GreenNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text(&self) -> String {
|
pub fn leaf_text(&self) -> Option<SmolStr> {
|
||||||
let mut buff = String::new();
|
self.leaf_text_ref().map(Clone::clone)
|
||||||
go(self, &mut buff);
|
|
||||||
return buff;
|
|
||||||
fn go(node: &GreenNode, buff: &mut String) {
|
|
||||||
match node {
|
|
||||||
GreenNode::Leaf { text, .. } => buff.push_str(text.as_str()),
|
|
||||||
GreenNode::Branch(b) => b.children().iter().for_each(|child| go(child, buff)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn leaf_text(&self) -> Option<SmolStr> {
|
pub fn leaf_text_ref(&self) -> Option<&SmolStr> {
|
||||||
match self {
|
match self {
|
||||||
GreenNode::Leaf { text, .. } => Some(text.clone()),
|
GreenNode::Leaf { text, .. } => Some(text),
|
||||||
GreenNode::Branch(_) => None,
|
GreenNode::Branch(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod builder;
|
||||||
mod green;
|
mod green;
|
||||||
mod red;
|
mod red;
|
||||||
mod syntax;
|
mod syntax;
|
||||||
|
mod syntax_text;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -12,6 +13,7 @@ pub(crate) use self::{
|
||||||
builder::GreenBuilder,
|
builder::GreenBuilder,
|
||||||
green::GreenNode,
|
green::GreenNode,
|
||||||
red::RedNode,
|
red::RedNode,
|
||||||
|
syntax_text::SyntaxText,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -32,6 +34,12 @@ pub struct OwnedRoot(Arc<SyntaxRoot>);
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct RefRoot<'a>(&'a OwnedRoot); // TODO: shared_from_this instead of double indirection
|
pub struct RefRoot<'a>(&'a OwnedRoot); // TODO: shared_from_this instead of double indirection
|
||||||
|
|
||||||
|
impl<'a> RefRoot<'a> {
|
||||||
|
fn syntax_root(&self) -> &'a SyntaxRoot {
|
||||||
|
self.0.syntax_root()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TreeRoot for OwnedRoot {
|
impl TreeRoot for OwnedRoot {
|
||||||
fn borrowed(&self) -> RefRoot {
|
fn borrowed(&self) -> RefRoot {
|
||||||
RefRoot(&self)
|
RefRoot(&self)
|
||||||
|
@ -78,7 +86,7 @@ impl RedPtr {
|
||||||
RedPtr(red.into())
|
RedPtr(red.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get<'a>(self, _root: &'a impl TreeRoot) -> &'a RedNode {
|
unsafe fn get<'a>(self, _root: &'a SyntaxRoot) -> &'a RedNode {
|
||||||
&*self.0.as_ptr()
|
&*self.0.as_ptr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
yellow::{GreenNode, RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot},
|
yellow::{GreenNode, RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot, SyntaxText},
|
||||||
SyntaxKind::{self, *},
|
SyntaxKind::{self, *},
|
||||||
TextRange, TextUnit,
|
TextRange, TextUnit,
|
||||||
};
|
};
|
||||||
|
@ -58,6 +58,13 @@ impl SyntaxNode<OwnedRoot> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> SyntaxNode<RefRoot<'a>> {
|
||||||
|
pub(crate) fn leaf_text_ref(self) -> Option<&'a SmolStr> {
|
||||||
|
let red = unsafe { self.red.get(self.root.syntax_root()) };
|
||||||
|
red.green().leaf_text_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<R: TreeRoot> SyntaxNode<R> {
|
impl<R: TreeRoot> SyntaxNode<R> {
|
||||||
pub fn borrowed<'a>(&'a self) -> SyntaxNodeRef<'a> {
|
pub fn borrowed<'a>(&'a self) -> SyntaxNodeRef<'a> {
|
||||||
SyntaxNode {
|
SyntaxNode {
|
||||||
|
@ -66,7 +73,7 @@ impl<R: TreeRoot> SyntaxNode<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn owned<'a>(&'a self) -> SyntaxNode {
|
pub fn owned(&self) -> SyntaxNode {
|
||||||
SyntaxNode {
|
SyntaxNode {
|
||||||
root: self.root.owned(),
|
root: self.root.owned(),
|
||||||
red: self.red,
|
red: self.red,
|
||||||
|
@ -82,8 +89,8 @@ impl<R: TreeRoot> SyntaxNode<R> {
|
||||||
TextRange::offset_len(red.start_offset(), red.green().text_len())
|
TextRange::offset_len(red.start_offset(), red.green().text_len())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text(&self) -> String {
|
pub fn text(&self) -> SyntaxText {
|
||||||
self.red().green().text()
|
SyntaxText::new(self.borrowed())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn children(&self) -> impl Iterator<Item = SyntaxNode<R>> {
|
pub fn children(&self) -> impl Iterator<Item = SyntaxNode<R>> {
|
||||||
|
@ -91,7 +98,7 @@ impl<R: TreeRoot> SyntaxNode<R> {
|
||||||
let n_children = self.red().n_children();
|
let n_children = self.red().n_children();
|
||||||
let root = self.root.clone();
|
let root = self.root.clone();
|
||||||
(0..n_children).map(move |i| {
|
(0..n_children).map(move |i| {
|
||||||
let red = unsafe { red.get(&root) };
|
let red = unsafe { red.get(root.syntax_root()) };
|
||||||
SyntaxNode {
|
SyntaxNode {
|
||||||
root: root.clone(),
|
root: root.clone(),
|
||||||
red: red.get_child(i).unwrap(),
|
red: red.get_child(i).unwrap(),
|
||||||
|
@ -171,7 +178,7 @@ impl<R: TreeRoot> SyntaxNode<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn red(&self) -> &RedNode {
|
fn red(&self) -> &RedNode {
|
||||||
unsafe { self.red.get(&self.root) }
|
unsafe { self.red.get(self.root.syntax_root()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
101
crates/libsyntax2/src/yellow/syntax_text.rs
Normal file
101
crates/libsyntax2/src/yellow/syntax_text.rs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
use std::{
|
||||||
|
fmt, ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
SyntaxNodeRef, TextRange, TextUnit,
|
||||||
|
algo::walk::preorder,
|
||||||
|
text_utils::{intersect, contains_offset_nonstrict},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SyntaxText<'a> {
|
||||||
|
node: SyntaxNodeRef<'a>,
|
||||||
|
range: TextRange,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SyntaxText<'a> {
|
||||||
|
pub(crate) fn new(node: SyntaxNodeRef<'a>) -> SyntaxText<'a> {
|
||||||
|
SyntaxText {
|
||||||
|
node,
|
||||||
|
range: node.range()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn chunks(&self) -> impl Iterator<Item=&'a str> {
|
||||||
|
let range = self.range;
|
||||||
|
preorder(self.node)
|
||||||
|
.filter_map(move |node| {
|
||||||
|
let text = node.leaf_text_ref()?;
|
||||||
|
let range = intersect(range, node.range())?;
|
||||||
|
let range = range - node.range().start();
|
||||||
|
Some(&text[range])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
self.chunks().collect()
|
||||||
|
}
|
||||||
|
pub fn contains(&self, c: char) -> bool {
|
||||||
|
self.chunks().any(|it| it.contains(c))
|
||||||
|
}
|
||||||
|
pub fn find(&self, c: char) -> Option<TextUnit> {
|
||||||
|
let mut acc: TextUnit = 0.into();
|
||||||
|
for chunk in self.chunks() {
|
||||||
|
if let Some(pos) = chunk.find(c) {
|
||||||
|
let pos: TextUnit = (pos as u32).into();
|
||||||
|
return Some(acc + pos);
|
||||||
|
}
|
||||||
|
acc += TextUnit::of_str(chunk);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
pub fn len(&self) -> TextUnit {
|
||||||
|
self.range.len()
|
||||||
|
}
|
||||||
|
pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> {
|
||||||
|
let range = range.restrict(self.range)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range)
|
||||||
|
});
|
||||||
|
SyntaxText { node: self.node, range }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Debug for SyntaxText<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.to_string(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Display for SyntaxText<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.to_string(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SyntaxTextSlice: fmt::Debug {
|
||||||
|
fn restrict(&self, range: TextRange) -> Option<TextRange>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyntaxTextSlice for TextRange {
|
||||||
|
fn restrict(&self, range: TextRange) -> Option<TextRange> {
|
||||||
|
intersect(*self, range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyntaxTextSlice for ops::RangeTo<TextUnit> {
|
||||||
|
fn restrict(&self, range: TextRange) -> Option<TextRange> {
|
||||||
|
if !contains_offset_nonstrict(range, self.end) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(TextRange::from_to(range.start(), self.end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyntaxTextSlice for ops::RangeFrom<TextUnit> {
|
||||||
|
fn restrict(&self, range: TextRange) -> Option<TextRange> {
|
||||||
|
if !contains_offset_nonstrict(range, self.start) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(TextRange::from_to(self.start, range.end()))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue