on master help sign in
Avoid copying fragments (#3136)
0ee2061Matthias Deiml
helix-core/src/selection.rs+19−1
@@ -222,9 +222,23 @@ impl Range {
222
222
223 // groupAt
223 // groupAt
224
224
225+ /// Returns the text inside this range given the text of the whole buffer.
226+ ///
227+ /// The returned `Cow` is a reference if the range of text is inside a single
228+ /// chunk of the rope. Otherwise a copy of the text is returned. Consider
229+ /// using `slice` instead if you do not need a `Cow` or `String` to avoid copying.
225 #[inline]
230 #[inline]
226 pub fn fragment<'a, 'b: 'a>(&'a self, text: RopeSlice<'b>) -> Cow<'b, str> {
231 pub fn fragment<'a, 'b: 'a>(&'a self, text: RopeSlice<'b>) -> Cow<'b, str> {
232+ self.slice(text).into()
233+ }
234+
235+ /// Returns the text inside this range given the text of the whole buffer.
236+ ///
237+ /// The returned value is a reference to the passed slice. This method never
238+ /// copies any contents.
239+ #[inline]
240+ pub fn slice<'a, 'b: 'a>(&'a self, text: RopeSlice<'b>) -> RopeSlice<'b> {
227− text.slice(self.from()..self.to()).into()
241+ text.slice(self.from()..self.to())
228 }
242 }
229
243
230 //--------------------------------
244 //--------------------------------
@@ -548,6 +562,10 @@ impl Selection {
548 self.ranges.iter().map(move |range| range.fragment(text))
562 self.ranges.iter().map(move |range| range.fragment(text))
549 }
563 }
550
564
565+ pub fn slices<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = RopeSlice> + 'a {
566+ self.ranges.iter().map(move |range| range.slice(text))
567+ }
568+
551 #[inline(always)]
569 #[inline(always)]
552 pub fn iter(&self) -> std::slice::Iter<'_, Range> {
570 pub fn iter(&self) -> std::slice::Iter<'_, Range> {
553 self.ranges.iter()
571 self.ranges.iter()
helix-term/src/commands.rs+19−13
@@ -766,7 +766,7 @@ fn trim_selections(cx: &mut Context) {
766 .selection(view.id)
766 .selection(view.id)
767 .iter()
767 .iter()
768 .filter_map(|range| {
768 .filter_map(|range| {
769− if range.is_empty() || range.fragment(text).chars().all(|ch| ch.is_whitespace()) {
769+ if range.is_empty() || range.slice(text).chars().all(|ch| ch.is_whitespace()) {
770 return None;
770 return None;
771 }
771 }
772 let mut start = range.from();
772 let mut start = range.from();
@@ -1289,12 +1289,12 @@ fn replace(cx: &mut Context) {
1289
1289
1290fn switch_case_impl<F>(cx: &mut Context, change_fn: F)
1290fn switch_case_impl<F>(cx: &mut Context, change_fn: F)
1291where
1291where
1292− F: Fn(Cow<str>) -> Tendril,
1292+ F: Fn(RopeSlice) -> Tendril,
1293{
1293{
1294 let (view, doc) = current!(cx.editor);
1294 let (view, doc) = current!(cx.editor);
1295 let selection = doc.selection(view.id);
1295 let selection = doc.selection(view.id);
1296 let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
1296 let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
1297− let text: Tendril = change_fn(range.fragment(doc.text().slice(..)));
1297+ let text: Tendril = change_fn(range.slice(doc.text().slice(..)));
1298
1298
1299 (range.from(), range.to(), Some(text))
1299 (range.from(), range.to(), Some(text))
1300 });
1300 });
@@ -1320,11 +1320,15 @@ fn switch_case(cx: &mut Context) {
1320}
1320}
1321
1321
1322fn switch_to_uppercase(cx: &mut Context) {
1322fn switch_to_uppercase(cx: &mut Context) {
1323− switch_case_impl(cx, |string| string.to_uppercase().into());
1323+ switch_case_impl(cx, |string| {
1324+ string.chunks().map(|chunk| chunk.to_uppercase()).collect()
1325+ });
1324}
1326}
1325
1327
1326fn switch_to_lowercase(cx: &mut Context) {
1328fn switch_to_lowercase(cx: &mut Context) {
1327− switch_case_impl(cx, |string| string.to_lowercase().into());
1329+ switch_case_impl(cx, |string| {
1330+ string.chunks().map(|chunk| chunk.to_lowercase()).collect()
1331+ });
1328}
1332}
1329
1333
1330pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
1334pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
@@ -3903,8 +3907,8 @@ fn rotate_selection_contents(cx: &mut Context, direction: Direction) {
3903
3907
3904 let selection = doc.selection(view.id);
3908 let selection = doc.selection(view.id);
3905 let mut fragments: Vec<_> = selection
3909 let mut fragments: Vec<_> = selection
3906− .fragments(text)
3910+ .slices(text)
3907− .map(|fragment| Tendril::from(fragment.as_ref()))
3911+ .map(|fragment| fragment.chunks().collect())
3908 .collect();
3912 .collect();
3909
3913
3910 let group = count
3914 let group = count
@@ -4510,8 +4514,8 @@ fn shell_keep_pipe(cx: &mut Context) {
4510 let text = doc.text().slice(..);
4514 let text = doc.text().slice(..);
4511
4515
4512 for (i, range) in selection.ranges().iter().enumerate() {
4516 for (i, range) in selection.ranges().iter().enumerate() {
4513− let fragment = range.fragment(text);
4517+ let fragment = range.slice(text);
4514− let (_output, success) = match shell_impl(shell, input, Some(fragment.as_bytes())) {
4518+ let (_output, success) = match shell_impl(shell, input, Some(fragment)) {
4515 Ok(result) => result,
4519 Ok(result) => result,
4516 Err(err) => {
4520 Err(err) => {
4517 cx.editor.set_error(err.to_string());
4521 cx.editor.set_error(err.to_string());
@@ -4542,7 +4546,7 @@ fn shell_keep_pipe(cx: &mut Context) {
4542fn shell_impl(
4546fn shell_impl(
4543 shell: &[String],
4547 shell: &[String],
4544 cmd: &str,
4548 cmd: &str,
4545− input: Option<&[u8]>,
4549+ input: Option<RopeSlice>,
4546) -> anyhow::Result<(Tendril, bool)> {
4550) -> anyhow::Result<(Tendril, bool)> {
4547 use std::io::Write;
4551 use std::io::Write;
4548 use std::process::{Command, Stdio};
4552 use std::process::{Command, Stdio};
@@ -4564,7 +4568,9 @@ fn shell_impl(
4564 };
4568 };
4565 if let Some(input) = input {
4569 if let Some(input) = input {
4566 let mut stdin = process.stdin.take().unwrap();
4570 let mut stdin = process.stdin.take().unwrap();
4571+ for chunk in input.chunks() {
4567− stdin.write_all(input)?;
4572+ stdin.write_all(chunk.as_bytes())?;
4573+ }
4568 }
4574 }
4569 let output = process.wait_with_output()?;
4575 let output = process.wait_with_output()?;
4570
4576
@@ -4593,8 +4599,8 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
4593 let text = doc.text().slice(..);
4599 let text = doc.text().slice(..);
4594
4600
4595 for range in selection.ranges() {
4601 for range in selection.ranges() {
4596− let fragment = range.fragment(text);
4602+ let fragment = range.slice(text);
4597− let (output, success) = match shell_impl(shell, cmd, pipe.then(|| fragment.as_bytes())) {
4603+ let (output, success) = match shell_impl(shell, cmd, pipe.then(|| fragment)) {
4598 Ok(result) => result,
4604 Ok(result) => result,
4599 Err(err) => {
4605 Err(err) => {
4600 cx.editor.set_error(err.to_string());
4606 cx.editor.set_error(err.to_string());
helix-term/src/commands/typed.rs+2−2
@@ -1291,8 +1291,8 @@ fn sort_impl(
1291 let selection = doc.selection(view.id);
1291 let selection = doc.selection(view.id);
1292
1292
1293 let mut fragments: Vec<_> = selection
1293 let mut fragments: Vec<_> = selection
1294− .fragments(text)
1294+ .slices(text)
1295− .map(|fragment| Tendril::from(fragment.as_ref()))
1295+ .map(|fragment| fragment.chunks().collect())
1296 .collect();
1296 .collect();
1297
1297
1298 fragments.sort_by(match reverse {
1298 fragments.sort_by(match reverse {
helix-term/src/ui/editor.rs+2−2
@@ -1029,8 +1029,8 @@ impl EditorView {
1029 if doc
1029 if doc
1030 .selection(view.id)
1030 .selection(view.id)
1031 .primary()
1031 .primary()
1032− .fragment(doc.text().slice(..))
1032+ .slice(doc.text().slice(..))
1033− .width()
1033+ .len_chars()
1034 <= 1
1034 <= 1
1035 {
1035 {
1036 return EventResult::Ignored(None);
1036 return EventResult::Ignored(None);