Avoid materializing strings

This commit is contained in:
Aleksey Kladov 2018-08-28 14:06:30 +03:00
parent 363f466627
commit 7e74af3226
7 changed files with 139 additions and 32 deletions

View file

@ -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)?;
Some(move || {
let mut edit = EditBuilder::new();
edit.replace(left.range(), right.text());
edit.replace(right.range(), left.text());
edit.replace(left.range(), right.text().to_string());
edit.replace(right.range(), left.text().to_string());
ActionResult {
edit: edit.finish(),
cursor_position: None,

View file

@ -15,16 +15,15 @@ use {ActionResult, EditBuilder, find_node_at_offset};
pub fn join_lines(file: &File, range: TextRange) -> ActionResult {
let range = if range.is_empty() {
let text = file.syntax().text();
let text = &text[TextRange::from_to(range.start(), TextUnit::of_str(&text))];
let pos = text.bytes().take_while(|&b| b != b'\n').count();
if pos == text.len() {
return ActionResult {
let syntax = file.syntax();
let text = syntax.text().slice(range.start()..);
let pos = match text.find('\n') {
None => return ActionResult {
edit: EditBuilder::new().finish(),
cursor_position: None
};
}
let pos: TextUnit = (pos as u32).into();
},
Some(pos) => pos
};
TextRange::offset_len(
range.start() + pos,
TextUnit::of_char('\n'),
@ -129,7 +128,7 @@ fn join_lambda_body(
let expr = single_expr(block)?;
edit.replace(
block_expr.syntax().range(),
expr.syntax().text(),
expr.syntax().text().to_string(),
);
Some(())
}

View file

@ -81,7 +81,7 @@ impl File {
pub fn incremental_reparse(&self, edit: &AtomEdit) -> Option<File> {
let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?;
let text = replace_range(
node.text(),
node.text().to_string(),
edit.delete - node.range().start(),
&edit.insert,
);
@ -97,7 +97,7 @@ impl File {
Some(File::new(green_root, errors))
}
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)
}
pub fn ast(&self) -> ast::Root {

View file

@ -43,21 +43,13 @@ impl GreenNode {
}
}
pub fn text(&self) -> String {
let mut buff = String::new();
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> {
self.leaf_text_ref().map(Clone::clone)
}
pub fn leaf_text(&self) -> Option<SmolStr> {
pub fn leaf_text_ref(&self) -> Option<&SmolStr> {
match self {
GreenNode::Leaf { text, .. } => Some(text.clone()),
GreenNode::Leaf { text, .. } => Some(text),
GreenNode::Branch(_) => None,
}
}

View file

@ -2,6 +2,7 @@ mod builder;
mod green;
mod red;
mod syntax;
mod syntax_text;
use std::{
sync::Arc,
@ -12,6 +13,7 @@ pub(crate) use self::{
builder::GreenBuilder,
green::GreenNode,
red::RedNode,
syntax_text::SyntaxText,
};
#[derive(Debug)]
@ -32,6 +34,12 @@ pub struct OwnedRoot(Arc<SyntaxRoot>);
#[derive(Clone, Copy, Debug)]
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 {
fn borrowed(&self) -> RefRoot {
RefRoot(&self)
@ -78,7 +86,7 @@ impl RedPtr {
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()
}
}

View file

@ -6,7 +6,7 @@ use std::{
use smol_str::SmolStr;
use {
yellow::{GreenNode, RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot},
yellow::{GreenNode, RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot, SyntaxText},
SyntaxKind::{self, *},
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> {
pub fn borrowed<'a>(&'a self) -> SyntaxNodeRef<'a> {
SyntaxNode {
@ -66,7 +73,7 @@ impl<R: TreeRoot> SyntaxNode<R> {
}
}
pub fn owned<'a>(&'a self) -> SyntaxNode {
pub fn owned(&self) -> SyntaxNode {
SyntaxNode {
root: self.root.owned(),
red: self.red,
@ -82,8 +89,8 @@ impl<R: TreeRoot> SyntaxNode<R> {
TextRange::offset_len(red.start_offset(), red.green().text_len())
}
pub fn text(&self) -> String {
self.red().green().text()
pub fn text(&self) -> SyntaxText {
SyntaxText::new(self.borrowed())
}
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 root = self.root.clone();
(0..n_children).map(move |i| {
let red = unsafe { red.get(&root) };
let red = unsafe { red.get(root.syntax_root()) };
SyntaxNode {
root: root.clone(),
red: red.get_child(i).unwrap(),
@ -171,7 +178,7 @@ impl<R: TreeRoot> SyntaxNode<R> {
}
fn red(&self) -> &RedNode {
unsafe { self.red.get(&self.root) }
unsafe { self.red.get(self.root.syntax_root()) }
}
}

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