on master help sign in
Clean up `Selection` to not use so many allocations.
commit0ae522f3df433bb778fa2ff98fd3d7047021c6ef authorNathan Vegdahl <cessen@cessen.com> dateMon 28 Jun 2021, 11:40 (5 years ago) parent77a266e
4 files changed+169−196
helix-core/src/object.rs+1−1 helix-core/src/selection.rs+88−68 helix-term/src/commands.rs+62−125 helix-view/src/document.rs+18−2
helix-core/src/object.rs+1−1
@@ -6,7 +6,7 @@ use smallvec::smallvec;
66pub fn expand_selection(syntax: &Syntax, text: RopeSlice, selection: &Selection) -> Selection {
77 let tree = syntax.tree();
88
9− selection.transform(|range| {
9+ selection.clone().transformed(|range| {
1010 let from = text.char_to_byte(range.from());
1111 let to = text.char_to_byte(range.to());
1212
helix-core/src/selection.rs+88−68
@@ -137,6 +137,27 @@ impl Range {
137137 }
138138 }
139139
140+ /// Returns a range that encompasses both input ranges.
141+ ///
142+ /// This is like `extend()`, but tries to negotiate the
143+ /// anchor/head ordering between the two input ranges.
144+ #[must_use]
145+ pub fn merge(&self, other: Self) -> Self {
146+ if self.anchor > self.head && other.anchor > other.head {
147+ Range {
148+ anchor: self.anchor.max(other.anchor),
149+ head: self.head.min(other.head),
150+ horiz: None,
151+ }
152+ } else {
153+ Range {
154+ anchor: self.from().min(other.from()),
155+ head: self.to().max(other.to()),
156+ horiz: None,
157+ }
158+ }
159+ }
160+
140161 /// Compute a possibly new range from this range, attempting to ensure
141162 /// a minimum range width of 1 char by shifting the head in the forward
142163 /// direction as needed.
@@ -170,19 +191,20 @@ impl Range {
170191 /// ranges will never collapse to zero-width.
171192 #[must_use]
172193 pub fn grapheme_aligned(&self, slice: RopeSlice) -> Self {
173− let (new_anchor, new_head) = if self.anchor == self.head {
174− let pos = ensure_grapheme_boundary_prev(slice, self.anchor);
175− (pos, pos)
176− } else if self.anchor < self.head {
177− (
194+ use std::cmp::Ordering;
195+ let (new_anchor, new_head) = match self.anchor.cmp(&self.head) {
196+ Ordering::Equal => {
197+ let pos = ensure_grapheme_boundary_prev(slice, self.anchor);
198+ (pos, pos)
199+ }
200+ Ordering::Less => (
178201 ensure_grapheme_boundary_prev(slice, self.anchor),
179202 ensure_grapheme_boundary_next(slice, self.head),
180− )
181− } else {
182− (
203+ ),
204+ Ordering::Greater => (
183205 ensure_grapheme_boundary_next(slice, self.anchor),
184206 ensure_grapheme_boundary_prev(slice, self.head),
185− )
207+ ),
186208 };
187209 Range {
188210 anchor: new_anchor,
@@ -238,10 +260,8 @@ impl Selection {
238260 }
239261
240262 pub fn push(mut self, range: Range) -> Self {
241− let index = self.ranges.len();
242263 self.ranges.push(range);
243−
244− Self::normalize(self.ranges, index)
264+ self.normalized()
245265 }
246266 // replace_range
247267
@@ -287,80 +307,80 @@ impl Selection {
287307 Self::single(pos, pos)
288308 }
289309
290− fn normalize(mut ranges: SmallVec<[Range; 1]>, mut primary_index: usize) -> Self {
291− let primary = ranges[primary_index];
292− ranges.sort_unstable_by_key(Range::from);
293− primary_index = ranges.iter().position(|&range| range == primary).unwrap();
294−
295− let mut result = SmallVec::with_capacity(ranges.len()); // approx
296−
297− // TODO: we could do with one vec by removing elements as we mutate
298−
299− let mut i = 0;
300−
301− for range in ranges.into_iter() {
302− // if previous value exists
303− if let Some(prev) = result.last_mut() {
304− // and we overlap it
305−
306− // TODO: we used to simply check range.from() <(=) prev.to()
307− // avoiding two comparisons
308− if range.overlaps(prev) {
309− let from = prev.from();
310− let to = std::cmp::max(range.to(), prev.to());
311−
312− if i <= primary_index {
313− primary_index -= 1
314− }
315−
316− // merge into previous
317− if range.anchor > range.head {
318− prev.anchor = to;
319− prev.head = from;
320− } else {
321− prev.anchor = from;
322− prev.head = to;
323− }
324− continue;
310+ /// Normalizes a `Selection`.
311+ pub fn normalize(&mut self) -> &mut Self {
312+ let primary = self.ranges[self.primary_index];
313+ self.ranges.sort_unstable_by_key(Range::from);
314+ self.primary_index = self
315+ .ranges
316+ .iter()
317+ .position(|&range| range == primary)
318+ .unwrap();
319+
320+ let mut prev_i = 0;
321+ for i in 1..self.ranges.len() {
322+ if self.ranges[prev_i].overlaps(&self.ranges[i]) {
323+ if i == self.primary_index {
324+ self.primary_index = prev_i;
325325 }
326+ self.ranges[prev_i] = self.ranges[prev_i].merge(self.ranges[i]);
327+ } else {
328+ prev_i += 1;
329+ self.ranges[prev_i] = self.ranges[i];
326330 }
327−
328− result.push(range);
329− i += 1
330331 }
331332
332− Self {
333− ranges: result,
334− primary_index,
335− }
333+ self.ranges.truncate(prev_i + 1);
334+
335+ self
336+ }
337+
338+ /// Normalizes a `Selection`.
339+ #[must_use]
340+ pub fn normalized(mut self) -> Self {
341+ self.normalize();
342+ self
336343 }
337344
338345 // TODO: consume an iterator or a vec to reduce allocations?
339346 #[must_use]
340347 pub fn new(ranges: SmallVec<[Range; 1]>, primary_index: usize) -> Self {
341348 assert!(!ranges.is_empty());
349+ debug_assert!(primary_index < ranges.len());
342350
343− // fast path for a single selection (cursor)
344− if ranges.len() == 1 {
345− return Self {
346− ranges,
347− primary_index: 0,
348− };
351+ let mut selection = Self {
352+ ranges,
353+ primary_index,
354+ };
355+
356+ if selection.ranges.len() > 1 {
357+ // TODO: only normalize if needed (any ranges out of order)
358+ selection.normalize();
349359 }
350360
351− // TODO: only normalize if needed (any ranges out of order)
352− Self::normalize(ranges, primary_index)
361+ selection
353362 }
354363
355− /// Takes a closure and maps each selection over the closure.
356− pub fn transform<F>(&self, f: F) -> Self
364+ /// Takes a closure and maps each `Range` over the closure.
365+ pub fn transform<F>(&mut self, f: F) -> &mut Self
357366 where
358367 F: Fn(Range) -> Range,
359368 {
360− Self::new(
361− self.ranges.iter().copied().map(f).collect(),
362− self.primary_index,
363− )
369+ for range in self.ranges.iter_mut() {
370+ *range = f(*range)
371+ }
372+
373+ self
374+ }
375+
376+ /// Takes a closure and maps each `Range` over the closure.
377+ #[must_use]
378+ pub fn transformed<F>(mut self, f: F) -> Self
379+ where
380+ F: Fn(Range) -> Range,
381+ {
382+ self.transform(f);
383+ self
364384 }
365385
366386 pub fn fragments<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = Cow<str>> + 'a {
helix-term/src/commands.rs+62−125
@@ -310,48 +310,44 @@ impl PartialEq for Command {
310310fn move_char_left(cx: &mut Context) {
311311 let count = cx.count();
312312 let (view, doc) = current!(cx.editor);
313− let text = doc.text().slice(..);
314− let selection = doc.selection(view.id).transform(|range| {
313+ let (text, selection) = doc.text_and_mut_selection(view.id);
314+ selection.transform(|range| {
315315 movement::move_horizontally(text, range, Direction::Backward, count, Movement::Move)
316316 });
317− doc.set_selection(view.id, selection);
318317}
319318
320319fn move_char_right(cx: &mut Context) {
321320 let count = cx.count();
322321 let (view, doc) = current!(cx.editor);
323− let text = doc.text().slice(..);
324− let selection = doc.selection(view.id).transform(|range| {
322+ let (text, selection) = doc.text_and_mut_selection(view.id);
323+ selection.transform(|range| {
325324 movement::move_horizontally(text, range, Direction::Forward, count, Movement::Move)
326325 });
327− doc.set_selection(view.id, selection);
328326}
329327
330328fn move_line_up(cx: &mut Context) {
331329 let count = cx.count();
332330 let (view, doc) = current!(cx.editor);
333− let text = doc.text().slice(..);
334− let selection = doc.selection(view.id).transform(|range| {
331+ let (text, selection) = doc.text_and_mut_selection(view.id);
332+ selection.transform(|range| {
335333 movement::move_vertically(text, range, Direction::Backward, count, Movement::Move)
336334 });
337− doc.set_selection(view.id, selection);
338335}
339336
340337fn move_line_down(cx: &mut Context) {
341338 let count = cx.count();
342339 let (view, doc) = current!(cx.editor);
343− let text = doc.text().slice(..);
344− let selection = doc.selection(view.id).transform(|range| {
340+ let (text, selection) = doc.text_and_mut_selection(view.id);
341+ selection.transform(|range| {
345342 movement::move_vertically(text, range, Direction::Forward, count, Movement::Move)
346343 });
347− doc.set_selection(view.id, selection);
348344}
349345
350346fn move_line_end(cx: &mut Context) {
351347 let (view, doc) = current!(cx.editor);
352348
353− let selection = doc.selection(view.id).transform(|range| {
354− let text = doc.text();
349+ let (text, selection) = doc.text_and_mut_selection(view.id);
350+ selection.transform(|range| {
355351 let line = text.char_to_line(range.head);
356352
357353 let pos = line_end_char_index(&text.slice(..), line);
@@ -360,30 +356,26 @@ fn move_line_end(cx: &mut Context) {
360356
361357 Range::new(pos, pos)
362358 });
363−
364− doc.set_selection(view.id, selection);
365359}
366360
367361fn move_line_start(cx: &mut Context) {
368362 let (view, doc) = current!(cx.editor);
369363
370− let selection = doc.selection(view.id).transform(|range| {
371− let text = doc.text();
364+ let (text, selection) = doc.text_and_mut_selection(view.id);
365+ selection.transform(|range| {
372366 let line = text.char_to_line(range.head);
373367
374368 // adjust to start of the line
375369 let pos = text.line_to_char(line);
376370 Range::new(pos, pos)
377371 });
378−
379− doc.set_selection(view.id, selection);
380372}
381373
382374fn move_first_nonwhitespace(cx: &mut Context) {
383375 let (view, doc) = current!(cx.editor);
384376
385− let selection = doc.selection(view.id).transform(|range| {
386− let text = doc.text();
377+ let (text, selection) = doc.text_and_mut_selection(view.id);
378+ selection.transform(|range| {
387379 let line_idx = text.char_to_line(range.head);
388380
389381 if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
@@ -393,8 +385,6 @@ fn move_first_nonwhitespace(cx: &mut Context) {
393385 range
394386 }
395387 });
396−
397− doc.set_selection(view.id, selection);
398388}
399389
400390// TODO: move vs extend could take an extra type Extend/Move that would
@@ -404,73 +394,49 @@ fn move_first_nonwhitespace(cx: &mut Context) {
404394fn move_next_word_start(cx: &mut Context) {
405395 let count = cx.count();
406396 let (view, doc) = current!(cx.editor);
407− let text = doc.text().slice(..);
408397
409− let selection = doc
410− .selection(view.id)
411− .transform(|range| movement::move_next_word_start(text, range, count));
412−
413− doc.set_selection(view.id, selection);
398+ let (text, selection) = doc.text_and_mut_selection(view.id);
399+ selection.transform(|range| movement::move_next_word_start(text, range, count));
414400}
415401
416402fn move_prev_word_start(cx: &mut Context) {
417403 let count = cx.count();
418404 let (view, doc) = current!(cx.editor);
419− let text = doc.text().slice(..);
420405
421− let selection = doc
422− .selection(view.id)
423− .transform(|range| movement::move_prev_word_start(text, range, count));
424−
425− doc.set_selection(view.id, selection);
406+ let (text, selection) = doc.text_and_mut_selection(view.id);
407+ selection.transform(|range| movement::move_prev_word_start(text, range, count));
426408}
427409
428410fn move_next_word_end(cx: &mut Context) {
429411 let count = cx.count();
430412 let (view, doc) = current!(cx.editor);
431− let text = doc.text().slice(..);
432413
433− let selection = doc
434− .selection(view.id)
435− .transform(|range| movement::move_next_word_end(text, range, count));
436−
437− doc.set_selection(view.id, selection);
414+ let (text, selection) = doc.text_and_mut_selection(view.id);
415+ selection.transform(|range| movement::move_next_word_end(text, range, count));
438416}
439417
440418fn move_next_long_word_start(cx: &mut Context) {
441419 let count = cx.count();
442420 let (view, doc) = current!(cx.editor);
443− let text = doc.text().slice(..);
444−
445− let selection = doc
446− .selection(view.id)
447− .transform(|range| movement::move_next_long_word_start(text, range, count));
448421
449− doc.set_selection(view.id, selection);
422+ let (text, selection) = doc.text_and_mut_selection(view.id);
423+ selection.transform(|range| movement::move_next_long_word_start(text, range, count));
450424}
451425
452426fn move_prev_long_word_start(cx: &mut Context) {
453427 let count = cx.count();
454428 let (view, doc) = current!(cx.editor);
455− let text = doc.text().slice(..);
456429
457− let selection = doc
458− .selection(view.id)
459− .transform(|range| movement::move_prev_long_word_start(text, range, count));
460−
461− doc.set_selection(view.id, selection);
430+ let (text, selection) = doc.text_and_mut_selection(view.id);
431+ selection.transform(|range| movement::move_prev_long_word_start(text, range, count));
462432}
463433
464434fn move_next_long_word_end(cx: &mut Context) {
465435 let count = cx.count();
466436 let (view, doc) = current!(cx.editor);
467− let text = doc.text().slice(..);
468−
469− let selection = doc
470− .selection(view.id)
471− .transform(|range| movement::move_next_long_word_end(text, range, count));
472437
473− doc.set_selection(view.id, selection);
438+ let (text, selection) = doc.text_and_mut_selection(view.id);
439+ selection.transform(|range| movement::move_next_long_word_end(text, range, count));
474440}
475441
476442fn move_file_start(cx: &mut Context) {
@@ -490,42 +456,37 @@ fn move_file_end(cx: &mut Context) {
490456fn extend_next_word_start(cx: &mut Context) {
491457 let count = cx.count();
492458 let (view, doc) = current!(cx.editor);
493− let text = doc.text().slice(..);
494459
495− let selection = doc.selection(view.id).transform(|mut range| {
460+ let (text, selection) = doc.text_and_mut_selection(view.id);
461+ selection.transform(|mut range| {
496462 let word = movement::move_next_word_start(text, range, count);
497463 let pos = word.head;
498464 Range::new(range.anchor, pos)
499465 });
500−
501− doc.set_selection(view.id, selection);
502466}
503467
504468fn extend_prev_word_start(cx: &mut Context) {
505469 let count = cx.count();
506470 let (view, doc) = current!(cx.editor);
507− let text = doc.text().slice(..);
508471
509− let selection = doc.selection(view.id).transform(|mut range| {
472+ let (text, selection) = doc.text_and_mut_selection(view.id);
473+ selection.transform(|mut range| {
510474 let word = movement::move_prev_word_start(text, range, count);
511475 let pos = word.head;
512476 Range::new(range.anchor, pos)
513477 });
514− doc.set_selection(view.id, selection);
515478}
516479
517480fn extend_next_word_end(cx: &mut Context) {
518481 let count = cx.count();
519482 let (view, doc) = current!(cx.editor);
520− let text = doc.text().slice(..);
521483
522− let selection = doc.selection(view.id).transform(|mut range| {
484+ let (text, selection) = doc.text_and_mut_selection(view.id);
485+ selection.transform(|mut range| {
523486 let word = movement::move_next_word_end(text, range, count);
524487 let pos = word.head;
525488 Range::new(range.anchor, pos)
526489 });
527−
528− doc.set_selection(view.id, selection);
529490}
530491
531492#[inline]
@@ -568,9 +529,9 @@ where
568529 };
569530
570531 let (view, doc) = current!(cx.editor);
571− let text = doc.text().slice(..);
572532
573− let selection = doc.selection(view.id).transform(|mut range| {
533+ let (text, selection) = doc.text_and_mut_selection(view.id);
534+ selection.transform(|mut range| {
574535 search_fn(text, ch, range.head, count, inclusive).map_or(range, |pos| {
575536 if extend {
576537 Range::new(range.anchor, pos)
@@ -581,8 +542,6 @@ where
581542 // or (pos, pos) to move to found val
582543 })
583544 });
584−
585− doc.set_selection(view.id, selection);
586545 })
587546}
588547
@@ -661,8 +620,8 @@ fn extend_prev_char(cx: &mut Context) {
661620fn extend_first_nonwhitespace(cx: &mut Context) {
662621 let (view, doc) = current!(cx.editor);
663622
664− let selection = doc.selection(view.id).transform(|range| {
665− let text = doc.text();
623+ let (text, selection) = doc.text_and_mut_selection(view.id);
624+ selection.transform(|range| {
666625 let line_idx = text.char_to_line(range.head);
667626
668627 if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
@@ -672,8 +631,6 @@ fn extend_first_nonwhitespace(cx: &mut Context) {
672631 range
673632 }
674633 });
675−
676− doc.set_selection(view.id, selection);
677634}
678635
679636fn replace(cx: &mut Context) {
@@ -784,48 +741,44 @@ fn half_page_down(cx: &mut Context) {
784741fn extend_char_left(cx: &mut Context) {
785742 let count = cx.count();
786743 let (view, doc) = current!(cx.editor);
787− let text = doc.text().slice(..);
788− let selection = doc.selection(view.id).transform(|range| {
744+ let (text, selection) = doc.text_and_mut_selection(view.id);
745+ selection.transform(|range| {
789746 movement::move_horizontally(text, range, Direction::Backward, count, Movement::Extend)
790747 });
791− doc.set_selection(view.id, selection);
792748}
793749
794750fn extend_char_right(cx: &mut Context) {
795751 let count = cx.count();
796752 let (view, doc) = current!(cx.editor);
797− let text = doc.text().slice(..);
798− let selection = doc.selection(view.id).transform(|range| {
753+ let (text, selection) = doc.text_and_mut_selection(view.id);
754+ selection.transform(|range| {
799755 movement::move_horizontally(text, range, Direction::Forward, count, Movement::Extend)
800756 });
801− doc.set_selection(view.id, selection);
802757}
803758
804759fn extend_line_up(cx: &mut Context) {
805760 let count = cx.count();
806761 let (view, doc) = current!(cx.editor);
807− let text = doc.text().slice(..);
808− let selection = doc.selection(view.id).transform(|range| {
762+ let (text, selection) = doc.text_and_mut_selection(view.id);
763+ selection.transform(|range| {
809764 movement::move_vertically(text, range, Direction::Backward, count, Movement::Extend)
810765 });
811− doc.set_selection(view.id, selection);
812766}
813767
814768fn extend_line_down(cx: &mut Context) {
815769 let count = cx.count();
816770 let (view, doc) = current!(cx.editor);
817− let text = doc.text().slice(..);
818− let selection = doc.selection(view.id).transform(|range| {
771+ let (text, selection) = doc.text_and_mut_selection(view.id);
772+ selection.transform(|range| {
819773 movement::move_vertically(text, range, Direction::Forward, count, Movement::Extend)
820774 });
821− doc.set_selection(view.id, selection);
822775}
823776
824777fn extend_line_end(cx: &mut Context) {
825778 let (view, doc) = current!(cx.editor);
826779
827− let selection = doc.selection(view.id).transform(|range| {
828− let text = doc.text();
780+ let (text, selection) = doc.text_and_mut_selection(view.id);
781+ selection.transform(|range| {
829782 let line = text.char_to_line(range.head);
830783
831784 let pos = line_end_char_index(&text.slice(..), line);
@@ -834,23 +787,19 @@ fn extend_line_end(cx: &mut Context) {
834787
835788 Range::new(range.anchor, pos)
836789 });
837−
838− doc.set_selection(view.id, selection);
839790}
840791
841792fn extend_line_start(cx: &mut Context) {
842793 let (view, doc) = current!(cx.editor);
843794
844− let selection = doc.selection(view.id).transform(|range| {
845− let text = doc.text();
795+ let (text, selection) = doc.text_and_mut_selection(view.id);
796+ selection.transform(|range| {
846797 let line = text.char_to_line(range.head);
847798
848799 // adjust to start of the line
849800 let pos = text.line_to_char(line);
850801 Range::new(range.anchor, pos)
851802 });
852−
853− doc.set_selection(view.id, selection);
854803}
855804
856805fn select_all(cx: &mut Context) {
@@ -1043,20 +992,16 @@ fn change_selection(cx: &mut Context) {
1043992
1044993fn collapse_selection(cx: &mut Context) {
1045994 let (view, doc) = current!(cx.editor);
1046− let selection = doc
1047− .selection(view.id)
1048− .transform(|range| Range::new(range.head, range.head));
1049995
1050− doc.set_selection(view.id, selection);
996+ doc.selection_mut(view.id)
997+ .transform(|range| Range::new(range.head, range.head));
1051998}
1052999
10531000fn flip_selections(cx: &mut Context) {
10541001 let (view, doc) = current!(cx.editor);
1055− let selection = doc
1056− .selection(view.id)
1057− .transform(|range| Range::new(range.head, range.anchor));
10581002
1059− doc.set_selection(view.id, selection);
1003+ doc.selection_mut(view.id)
1004+ .transform(|range| Range::new(range.head, range.anchor));
10601005}
10611006
10621007fn enter_insert_mode(doc: &mut Document) {
@@ -1068,10 +1013,8 @@ fn insert_mode(cx: &mut Context) {
10681013 let (view, doc) = current!(cx.editor);
10691014 enter_insert_mode(doc);
10701015
1071− let selection = doc
1072− .selection(view.id)
1016+ doc.selection_mut(view.id)
10731017 .transform(|range| Range::new(range.to(), range.from()));
1074− doc.set_selection(view.id, selection);
10751018}
10761019
10771020// inserts at the end of each selection
@@ -1080,8 +1023,8 @@ fn append_mode(cx: &mut Context) {
10801023 enter_insert_mode(doc);
10811024 doc.restore_cursor = true;
10821025
1083− let text = doc.text().slice(..);
1084− let selection = doc.selection(view.id).transform(|range| {
1026+ let (text, selection) = doc.text_and_mut_selection(view.id);
1027+ selection.transform(|range| {
10851028 Range::new(
10861029 range.from(),
10871030 graphemes::next_grapheme_boundary(text, range.to()), // to() + next char
@@ -1097,8 +1040,6 @@ fn append_mode(cx: &mut Context) {
10971040 );
10981041 doc.apply(&transaction, view.id);
10991042 }
1100−
1101− doc.set_selection(view.id, selection);
11021043}
11031044
11041045mod cmd {
@@ -1904,13 +1845,12 @@ fn append_to_line(cx: &mut Context) {
19041845 let (view, doc) = current!(cx.editor);
19051846 enter_insert_mode(doc);
19061847
1907− let selection = doc.selection(view.id).transform(|range| {
1908− let text = doc.text();
1848+ let (text, selection) = doc.text_and_mut_selection(view.id);
1849+ selection.transform(|range| {
19091850 let line = text.char_to_line(range.head);
19101851 let pos = line_end_char_index(&text.slice(..), line);
19111852 Range::new(pos, pos)
19121853 });
1913− doc.set_selection(view.id, selection);
19141854}
19151855
19161856/// Sometimes when applying formatting changes we want to mark the buffer as unmodified, for
@@ -2040,14 +1980,13 @@ fn normal_mode(cx: &mut Context) {
20401980
20411981 // if leaving append mode, move cursor back by 1
20421982 if doc.restore_cursor {
2043− let text = doc.text().slice(..);
2044− let selection = doc.selection(view.id).transform(|range| {
1983+ let (text, selection) = doc.text_and_mut_selection(view.id);
1984+ selection.transform(|range| {
20451985 Range::new(
20461986 range.from(),
20471987 graphemes::prev_grapheme_boundary(text, range.to()),
20481988 )
20491989 });
2050− doc.set_selection(view.id, selection);
20511990
20521991 doc.restore_cursor = false;
20531992 }
@@ -2679,11 +2618,9 @@ pub mod insert {
26792618 pub fn delete_word_backward(cx: &mut Context) {
26802619 let count = cx.count();
26812620 let (view, doc) = current!(cx.editor);
2682− let text = doc.text().slice(..);
2683− let selection = doc
2684− .selection(view.id)
2685− .transform(|range| movement::move_prev_word_start(text, range, count));
2686− doc.set_selection(view.id, selection);
2621+
2622+ let (text, selection) = doc.text_and_mut_selection(view.id);
2623+ selection.transform(|range| movement::move_prev_word_start(text, range, count));
26872624 delete_selection(cx)
26882625 }
26892626}
helix-view/src/document.rs+18−2
@@ -13,8 +13,8 @@ use helix_core::{
1313 history::History,
1414 line_ending::auto_detect_line_ending,
1515 syntax::{self, LanguageConfiguration},
16− ChangeSet, Diagnostic, LineEnding, Rope, RopeBuilder, Selection, State, Syntax, Transaction,
17− DEFAULT_LINE_ENDING,
16+ ChangeSet, Diagnostic, LineEnding, Rope, RopeBuilder, RopeSlice, Selection, State, Syntax,
17+ Transaction, DEFAULT_LINE_ENDING,
1818};
1919use helix_lsp::util::LspFormatting;
2020
@@ -1000,6 +1000,22 @@ impl Document {
10001000 &self.selections[&view_id]
10011001 }
10021002
1003+ #[inline]
1004+ pub fn selection_mut(&mut self, view_id: ViewId) -> &mut Selection {
1005+ self.selections
1006+ .get_mut(&view_id)
1007+ .expect("No selection set with the given ViewId")
1008+ }
1009+
1010+ pub fn text_and_mut_selection(&mut self, view_id: ViewId) -> (RopeSlice, &mut Selection) {
1011+ (
1012+ self.text.slice(..),
1013+ self.selections
1014+ .get_mut(&view_id)
1015+ .expect("No selection set with the given ViewId"),
1016+ )
1017+ }
1018+
10031019 pub fn relative_path(&self) -> Option<PathBuf> {
10041020 let cwdir = std::env::current_dir().expect("couldn't determine current directory");
10051021