on master help sign in
Clean up `Selection` to not use so many allocations.
0ae522fNathan Vegdahl
helix-core/src/object.rs+1−1
@@ -6,7 +6,7 @@ use smallvec::smallvec;
6pub fn expand_selection(syntax: &Syntax, text: RopeSlice, selection: &Selection) -> Selection {
6pub fn expand_selection(syntax: &Syntax, text: RopeSlice, selection: &Selection) -> Selection {
7 let tree = syntax.tree();
7 let tree = syntax.tree();
8
8
9− selection.transform(|range| {
9+ selection.clone().transformed(|range| {
10 let from = text.char_to_byte(range.from());
10 let from = text.char_to_byte(range.from());
11 let to = text.char_to_byte(range.to());
11 let to = text.char_to_byte(range.to());
12
12
helix-core/src/selection.rs+88−68
@@ -137,6 +137,27 @@ impl Range {
137 }
137 }
138 }
138 }
139
139
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+
140 /// Compute a possibly new range from this range, attempting to ensure
161 /// Compute a possibly new range from this range, attempting to ensure
141 /// a minimum range width of 1 char by shifting the head in the forward
162 /// a minimum range width of 1 char by shifting the head in the forward
142 /// direction as needed.
163 /// direction as needed.
@@ -170,19 +191,20 @@ impl Range {
170 /// ranges will never collapse to zero-width.
191 /// ranges will never collapse to zero-width.
171 #[must_use]
192 #[must_use]
172 pub fn grapheme_aligned(&self, slice: RopeSlice) -> Self {
193 pub fn grapheme_aligned(&self, slice: RopeSlice) -> Self {
194+ use std::cmp::Ordering;
173− let (new_anchor, new_head) = if self.anchor == self.head {
195+ let (new_anchor, new_head) = match self.anchor.cmp(&self.head) {
196+ Ordering::Equal => {
174− let pos = ensure_grapheme_boundary_prev(slice, self.anchor);
197+ let pos = ensure_grapheme_boundary_prev(slice, self.anchor);
175− (pos, pos)
198+ (pos, pos)
176− } else if self.anchor < self.head {
199+ }
177− (
200+ Ordering::Less => (
178 ensure_grapheme_boundary_prev(slice, self.anchor),
201 ensure_grapheme_boundary_prev(slice, self.anchor),
179 ensure_grapheme_boundary_next(slice, self.head),
202 ensure_grapheme_boundary_next(slice, self.head),
180− )
203+ ),
181− } else {
204+ Ordering::Greater => (
182− (
183 ensure_grapheme_boundary_next(slice, self.anchor),
205 ensure_grapheme_boundary_next(slice, self.anchor),
184 ensure_grapheme_boundary_prev(slice, self.head),
206 ensure_grapheme_boundary_prev(slice, self.head),
185− )
207+ ),
186 };
208 };
187 Range {
209 Range {
188 anchor: new_anchor,
210 anchor: new_anchor,
@@ -238,10 +260,8 @@ impl Selection {
238 }
260 }
239
261
240 pub fn push(mut self, range: Range) -> Self {
262 pub fn push(mut self, range: Range) -> Self {
241− let index = self.ranges.len();
242 self.ranges.push(range);
263 self.ranges.push(range);
243−
264+ self.normalized()
244− Self::normalize(self.ranges, index)
245 }
265 }
246 // replace_range
266 // replace_range
247
267
@@ -287,80 +307,80 @@ impl Selection {
287 Self::single(pos, pos)
307 Self::single(pos, pos)
288 }
308 }
289
309
310+ /// Normalizes a `Selection`.
290− fn normalize(mut ranges: SmallVec<[Range; 1]>, mut primary_index: usize) -> Self {
311+ pub fn normalize(&mut self) -> &mut Self {
291− let primary = ranges[primary_index];
312+ let primary = self.ranges[self.primary_index];
292− ranges.sort_unstable_by_key(Range::from);
313+ self.ranges.sort_unstable_by_key(Range::from);
314+ self.primary_index = self
315+ .ranges
316+ .iter()
293− primary_index = ranges.iter().position(|&range| range == primary).unwrap();
317+ .position(|&range| range == primary)
294−
318+ .unwrap();
295− let mut result = SmallVec::with_capacity(ranges.len()); // approx
319+
296−
320+ let mut prev_i = 0;
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() {
321+ for i in 1..self.ranges.len() {
302− // if previous value exists
322+ if self.ranges[prev_i].overlaps(&self.ranges[i]) {
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 {
323+ if i == self.primary_index {
313− primary_index -= 1
324+ self.primary_index = prev_i;
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;
325 }
325 }
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];
326 }
330 }
327−
328− result.push(range);
329− i += 1
330 }
331 }
331
332
332− Self {
333+ self.ranges.truncate(prev_i + 1);
333− ranges: result,
334+
334− primary_index,
335+ self
335− }
336+ }
337+
338+ /// Normalizes a `Selection`.
339+ #[must_use]
340+ pub fn normalized(mut self) -> Self {
341+ self.normalize();
342+ self
336 }
343 }
337
344
338 // TODO: consume an iterator or a vec to reduce allocations?
345 // TODO: consume an iterator or a vec to reduce allocations?
339 #[must_use]
346 #[must_use]
340 pub fn new(ranges: SmallVec<[Range; 1]>, primary_index: usize) -> Self {
347 pub fn new(ranges: SmallVec<[Range; 1]>, primary_index: usize) -> Self {
341 assert!(!ranges.is_empty());
348 assert!(!ranges.is_empty());
349+ debug_assert!(primary_index < ranges.len());
342
350
343− // fast path for a single selection (cursor)
351+ let mut selection = Self {
352+ ranges,
353+ primary_index,
354+ };
355+
344− if ranges.len() == 1 {
356+ if selection.ranges.len() > 1 {
345− return Self {
357+ // TODO: only normalize if needed (any ranges out of order)
346− ranges,
358+ selection.normalize();
347− primary_index: 0,
348− };
349 }
359 }
350
360
351− // TODO: only normalize if needed (any ranges out of order)
361+ selection
352− Self::normalize(ranges, primary_index)
353 }
362 }
354
363
355− /// Takes a closure and maps each selection over the closure.
364+ /// Takes a closure and maps each `Range` over the closure.
356− pub fn transform<F>(&self, f: F) -> Self
365+ pub fn transform<F>(&mut self, f: F) -> &mut Self
357 where
366 where
358 F: Fn(Range) -> Range,
367 F: Fn(Range) -> Range,
359 {
368 {
360− Self::new(
369+ for range in self.ranges.iter_mut() {
361− self.ranges.iter().copied().map(f).collect(),
370+ *range = f(*range)
362− self.primary_index,
371+ }
363− )
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
364 }
384 }
365
385
366 pub fn fragments<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = Cow<str>> + 'a {
386 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 {
310fn move_char_left(cx: &mut Context) {
310fn move_char_left(cx: &mut Context) {
311 let count = cx.count();
311 let count = cx.count();
312 let (view, doc) = current!(cx.editor);
312 let (view, doc) = current!(cx.editor);
313− let text = doc.text().slice(..);
313+ let (text, selection) = doc.text_and_mut_selection(view.id);
314− let selection = doc.selection(view.id).transform(|range| {
314+ selection.transform(|range| {
315 movement::move_horizontally(text, range, Direction::Backward, count, Movement::Move)
315 movement::move_horizontally(text, range, Direction::Backward, count, Movement::Move)
316 });
316 });
317− doc.set_selection(view.id, selection);
318}
317}
319
318
320fn move_char_right(cx: &mut Context) {
319fn move_char_right(cx: &mut Context) {
321 let count = cx.count();
320 let count = cx.count();
322 let (view, doc) = current!(cx.editor);
321 let (view, doc) = current!(cx.editor);
323− let text = doc.text().slice(..);
322+ let (text, selection) = doc.text_and_mut_selection(view.id);
324− let selection = doc.selection(view.id).transform(|range| {
323+ selection.transform(|range| {
325 movement::move_horizontally(text, range, Direction::Forward, count, Movement::Move)
324 movement::move_horizontally(text, range, Direction::Forward, count, Movement::Move)
326 });
325 });
327− doc.set_selection(view.id, selection);
328}
326}
329
327
330fn move_line_up(cx: &mut Context) {
328fn move_line_up(cx: &mut Context) {
331 let count = cx.count();
329 let count = cx.count();
332 let (view, doc) = current!(cx.editor);
330 let (view, doc) = current!(cx.editor);
333− let text = doc.text().slice(..);
331+ let (text, selection) = doc.text_and_mut_selection(view.id);
334− let selection = doc.selection(view.id).transform(|range| {
332+ selection.transform(|range| {
335 movement::move_vertically(text, range, Direction::Backward, count, Movement::Move)
333 movement::move_vertically(text, range, Direction::Backward, count, Movement::Move)
336 });
334 });
337− doc.set_selection(view.id, selection);
338}
335}
339
336
340fn move_line_down(cx: &mut Context) {
337fn move_line_down(cx: &mut Context) {
341 let count = cx.count();
338 let count = cx.count();
342 let (view, doc) = current!(cx.editor);
339 let (view, doc) = current!(cx.editor);
343− let text = doc.text().slice(..);
340+ let (text, selection) = doc.text_and_mut_selection(view.id);
344− let selection = doc.selection(view.id).transform(|range| {
341+ selection.transform(|range| {
345 movement::move_vertically(text, range, Direction::Forward, count, Movement::Move)
342 movement::move_vertically(text, range, Direction::Forward, count, Movement::Move)
346 });
343 });
347− doc.set_selection(view.id, selection);
348}
344}
349
345
350fn move_line_end(cx: &mut Context) {
346fn move_line_end(cx: &mut Context) {
351 let (view, doc) = current!(cx.editor);
347 let (view, doc) = current!(cx.editor);
352
348
353− let selection = doc.selection(view.id).transform(|range| {
349+ let (text, selection) = doc.text_and_mut_selection(view.id);
354− let text = doc.text();
350+ selection.transform(|range| {
355 let line = text.char_to_line(range.head);
351 let line = text.char_to_line(range.head);
356
352
357 let pos = line_end_char_index(&text.slice(..), line);
353 let pos = line_end_char_index(&text.slice(..), line);
@@ -360,30 +356,26 @@ fn move_line_end(cx: &mut Context) {
360
356
361 Range::new(pos, pos)
357 Range::new(pos, pos)
362 });
358 });
363−
364− doc.set_selection(view.id, selection);
365}
359}
366
360
367fn move_line_start(cx: &mut Context) {
361fn move_line_start(cx: &mut Context) {
368 let (view, doc) = current!(cx.editor);
362 let (view, doc) = current!(cx.editor);
369
363
370− let selection = doc.selection(view.id).transform(|range| {
364+ let (text, selection) = doc.text_and_mut_selection(view.id);
371− let text = doc.text();
365+ selection.transform(|range| {
372 let line = text.char_to_line(range.head);
366 let line = text.char_to_line(range.head);
373
367
374 // adjust to start of the line
368 // adjust to start of the line
375 let pos = text.line_to_char(line);
369 let pos = text.line_to_char(line);
376 Range::new(pos, pos)
370 Range::new(pos, pos)
377 });
371 });
378−
379− doc.set_selection(view.id, selection);
380}
372}
381
373
382fn move_first_nonwhitespace(cx: &mut Context) {
374fn move_first_nonwhitespace(cx: &mut Context) {
383 let (view, doc) = current!(cx.editor);
375 let (view, doc) = current!(cx.editor);
384
376
385− let selection = doc.selection(view.id).transform(|range| {
377+ let (text, selection) = doc.text_and_mut_selection(view.id);
386− let text = doc.text();
378+ selection.transform(|range| {
387 let line_idx = text.char_to_line(range.head);
379 let line_idx = text.char_to_line(range.head);
388
380
389 if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
381 if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
@@ -393,8 +385,6 @@ fn move_first_nonwhitespace(cx: &mut Context) {
393 range
385 range
394 }
386 }
395 });
387 });
396−
397− doc.set_selection(view.id, selection);
398}
388}
399
389
400// TODO: move vs extend could take an extra type Extend/Move that would
390// TODO: move vs extend could take an extra type Extend/Move that would
@@ -404,73 +394,49 @@ fn move_first_nonwhitespace(cx: &mut Context) {
404fn move_next_word_start(cx: &mut Context) {
394fn move_next_word_start(cx: &mut Context) {
405 let count = cx.count();
395 let count = cx.count();
406 let (view, doc) = current!(cx.editor);
396 let (view, doc) = current!(cx.editor);
407− let text = doc.text().slice(..);
408
397
409− let selection = doc
398+ let (text, selection) = doc.text_and_mut_selection(view.id);
410− .selection(view.id)
411− .transform(|range| movement::move_next_word_start(text, range, count));
399+ selection.transform(|range| movement::move_next_word_start(text, range, count));
412−
413− doc.set_selection(view.id, selection);
414}
400}
415
401
416fn move_prev_word_start(cx: &mut Context) {
402fn move_prev_word_start(cx: &mut Context) {
417 let count = cx.count();
403 let count = cx.count();
418 let (view, doc) = current!(cx.editor);
404 let (view, doc) = current!(cx.editor);
419− let text = doc.text().slice(..);
420
405
421− let selection = doc
406+ let (text, selection) = doc.text_and_mut_selection(view.id);
422− .selection(view.id)
423− .transform(|range| movement::move_prev_word_start(text, range, count));
407+ selection.transform(|range| movement::move_prev_word_start(text, range, count));
424−
425− doc.set_selection(view.id, selection);
426}
408}
427
409
428fn move_next_word_end(cx: &mut Context) {
410fn move_next_word_end(cx: &mut Context) {
429 let count = cx.count();
411 let count = cx.count();
430 let (view, doc) = current!(cx.editor);
412 let (view, doc) = current!(cx.editor);
431− let text = doc.text().slice(..);
432
413
433− let selection = doc
414+ let (text, selection) = doc.text_and_mut_selection(view.id);
434− .selection(view.id)
435− .transform(|range| movement::move_next_word_end(text, range, count));
415+ selection.transform(|range| movement::move_next_word_end(text, range, count));
436−
437− doc.set_selection(view.id, selection);
438}
416}
439
417
440fn move_next_long_word_start(cx: &mut Context) {
418fn move_next_long_word_start(cx: &mut Context) {
441 let count = cx.count();
419 let count = cx.count();
442 let (view, doc) = current!(cx.editor);
420 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));
448
421
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));
450}
424}
451
425
452fn move_prev_long_word_start(cx: &mut Context) {
426fn move_prev_long_word_start(cx: &mut Context) {
453 let count = cx.count();
427 let count = cx.count();
454 let (view, doc) = current!(cx.editor);
428 let (view, doc) = current!(cx.editor);
455− let text = doc.text().slice(..);
456
429
457− let selection = doc
430+ let (text, selection) = doc.text_and_mut_selection(view.id);
458− .selection(view.id)
459− .transform(|range| movement::move_prev_long_word_start(text, range, count));
431+ selection.transform(|range| movement::move_prev_long_word_start(text, range, count));
460−
461− doc.set_selection(view.id, selection);
462}
432}
463
433
464fn move_next_long_word_end(cx: &mut Context) {
434fn move_next_long_word_end(cx: &mut Context) {
465 let count = cx.count();
435 let count = cx.count();
466 let (view, doc) = current!(cx.editor);
436 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));
472
437
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));
474}
440}
475
441
476fn move_file_start(cx: &mut Context) {
442fn move_file_start(cx: &mut Context) {
@@ -490,42 +456,37 @@ fn move_file_end(cx: &mut Context) {
490fn extend_next_word_start(cx: &mut Context) {
456fn extend_next_word_start(cx: &mut Context) {
491 let count = cx.count();
457 let count = cx.count();
492 let (view, doc) = current!(cx.editor);
458 let (view, doc) = current!(cx.editor);
493− let text = doc.text().slice(..);
494
459
460+ let (text, selection) = doc.text_and_mut_selection(view.id);
495− let selection = doc.selection(view.id).transform(|mut range| {
461+ selection.transform(|mut range| {
496 let word = movement::move_next_word_start(text, range, count);
462 let word = movement::move_next_word_start(text, range, count);
497 let pos = word.head;
463 let pos = word.head;
498 Range::new(range.anchor, pos)
464 Range::new(range.anchor, pos)
499 });
465 });
500−
501− doc.set_selection(view.id, selection);
502}
466}
503
467
504fn extend_prev_word_start(cx: &mut Context) {
468fn extend_prev_word_start(cx: &mut Context) {
505 let count = cx.count();
469 let count = cx.count();
506 let (view, doc) = current!(cx.editor);
470 let (view, doc) = current!(cx.editor);
507− let text = doc.text().slice(..);
508
471
472+ let (text, selection) = doc.text_and_mut_selection(view.id);
509− let selection = doc.selection(view.id).transform(|mut range| {
473+ selection.transform(|mut range| {
510 let word = movement::move_prev_word_start(text, range, count);
474 let word = movement::move_prev_word_start(text, range, count);
511 let pos = word.head;
475 let pos = word.head;
512 Range::new(range.anchor, pos)
476 Range::new(range.anchor, pos)
513 });
477 });
514− doc.set_selection(view.id, selection);
515}
478}
516
479
517fn extend_next_word_end(cx: &mut Context) {
480fn extend_next_word_end(cx: &mut Context) {
518 let count = cx.count();
481 let count = cx.count();
519 let (view, doc) = current!(cx.editor);
482 let (view, doc) = current!(cx.editor);
520− let text = doc.text().slice(..);
521
483
484+ let (text, selection) = doc.text_and_mut_selection(view.id);
522− let selection = doc.selection(view.id).transform(|mut range| {
485+ selection.transform(|mut range| {
523 let word = movement::move_next_word_end(text, range, count);
486 let word = movement::move_next_word_end(text, range, count);
524 let pos = word.head;
487 let pos = word.head;
525 Range::new(range.anchor, pos)
488 Range::new(range.anchor, pos)
526 });
489 });
527−
528− doc.set_selection(view.id, selection);
529}
490}
530
491
531#[inline]
492#[inline]
@@ -568,9 +529,9 @@ where
568 };
529 };
569
530
570 let (view, doc) = current!(cx.editor);
531 let (view, doc) = current!(cx.editor);
571− let text = doc.text().slice(..);
572
532
533+ let (text, selection) = doc.text_and_mut_selection(view.id);
573− let selection = doc.selection(view.id).transform(|mut range| {
534+ selection.transform(|mut range| {
574 search_fn(text, ch, range.head, count, inclusive).map_or(range, |pos| {
535 search_fn(text, ch, range.head, count, inclusive).map_or(range, |pos| {
575 if extend {
536 if extend {
576 Range::new(range.anchor, pos)
537 Range::new(range.anchor, pos)
@@ -581,8 +542,6 @@ where
581 // or (pos, pos) to move to found val
542 // or (pos, pos) to move to found val
582 })
543 })
583 });
544 });
584−
585− doc.set_selection(view.id, selection);
586 })
545 })
587}
546}
588
547
@@ -661,8 +620,8 @@ fn extend_prev_char(cx: &mut Context) {
661fn extend_first_nonwhitespace(cx: &mut Context) {
620fn extend_first_nonwhitespace(cx: &mut Context) {
662 let (view, doc) = current!(cx.editor);
621 let (view, doc) = current!(cx.editor);
663
622
664− let selection = doc.selection(view.id).transform(|range| {
623+ let (text, selection) = doc.text_and_mut_selection(view.id);
665− let text = doc.text();
624+ selection.transform(|range| {
666 let line_idx = text.char_to_line(range.head);
625 let line_idx = text.char_to_line(range.head);
667
626
668 if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
627 if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
@@ -672,8 +631,6 @@ fn extend_first_nonwhitespace(cx: &mut Context) {
672 range
631 range
673 }
632 }
674 });
633 });
675−
676− doc.set_selection(view.id, selection);
677}
634}
678
635
679fn replace(cx: &mut Context) {
636fn replace(cx: &mut Context) {
@@ -784,48 +741,44 @@ fn half_page_down(cx: &mut Context) {
784fn extend_char_left(cx: &mut Context) {
741fn extend_char_left(cx: &mut Context) {
785 let count = cx.count();
742 let count = cx.count();
786 let (view, doc) = current!(cx.editor);
743 let (view, doc) = current!(cx.editor);
787− let text = doc.text().slice(..);
744+ let (text, selection) = doc.text_and_mut_selection(view.id);
788− let selection = doc.selection(view.id).transform(|range| {
745+ selection.transform(|range| {
789 movement::move_horizontally(text, range, Direction::Backward, count, Movement::Extend)
746 movement::move_horizontally(text, range, Direction::Backward, count, Movement::Extend)
790 });
747 });
791− doc.set_selection(view.id, selection);
792}
748}
793
749
794fn extend_char_right(cx: &mut Context) {
750fn extend_char_right(cx: &mut Context) {
795 let count = cx.count();
751 let count = cx.count();
796 let (view, doc) = current!(cx.editor);
752 let (view, doc) = current!(cx.editor);
797− let text = doc.text().slice(..);
753+ let (text, selection) = doc.text_and_mut_selection(view.id);
798− let selection = doc.selection(view.id).transform(|range| {
754+ selection.transform(|range| {
799 movement::move_horizontally(text, range, Direction::Forward, count, Movement::Extend)
755 movement::move_horizontally(text, range, Direction::Forward, count, Movement::Extend)
800 });
756 });
801− doc.set_selection(view.id, selection);
802}
757}
803
758
804fn extend_line_up(cx: &mut Context) {
759fn extend_line_up(cx: &mut Context) {
805 let count = cx.count();
760 let count = cx.count();
806 let (view, doc) = current!(cx.editor);
761 let (view, doc) = current!(cx.editor);
807− let text = doc.text().slice(..);
762+ let (text, selection) = doc.text_and_mut_selection(view.id);
808− let selection = doc.selection(view.id).transform(|range| {
763+ selection.transform(|range| {
809 movement::move_vertically(text, range, Direction::Backward, count, Movement::Extend)
764 movement::move_vertically(text, range, Direction::Backward, count, Movement::Extend)
810 });
765 });
811− doc.set_selection(view.id, selection);
812}
766}
813
767
814fn extend_line_down(cx: &mut Context) {
768fn extend_line_down(cx: &mut Context) {
815 let count = cx.count();
769 let count = cx.count();
816 let (view, doc) = current!(cx.editor);
770 let (view, doc) = current!(cx.editor);
817− let text = doc.text().slice(..);
771+ let (text, selection) = doc.text_and_mut_selection(view.id);
818− let selection = doc.selection(view.id).transform(|range| {
772+ selection.transform(|range| {
819 movement::move_vertically(text, range, Direction::Forward, count, Movement::Extend)
773 movement::move_vertically(text, range, Direction::Forward, count, Movement::Extend)
820 });
774 });
821− doc.set_selection(view.id, selection);
822}
775}
823
776
824fn extend_line_end(cx: &mut Context) {
777fn extend_line_end(cx: &mut Context) {
825 let (view, doc) = current!(cx.editor);
778 let (view, doc) = current!(cx.editor);
826
779
827− let selection = doc.selection(view.id).transform(|range| {
780+ let (text, selection) = doc.text_and_mut_selection(view.id);
828− let text = doc.text();
781+ selection.transform(|range| {
829 let line = text.char_to_line(range.head);
782 let line = text.char_to_line(range.head);
830
783
831 let pos = line_end_char_index(&text.slice(..), line);
784 let pos = line_end_char_index(&text.slice(..), line);
@@ -834,23 +787,19 @@ fn extend_line_end(cx: &mut Context) {
834
787
835 Range::new(range.anchor, pos)
788 Range::new(range.anchor, pos)
836 });
789 });
837−
838− doc.set_selection(view.id, selection);
839}
790}
840
791
841fn extend_line_start(cx: &mut Context) {
792fn extend_line_start(cx: &mut Context) {
842 let (view, doc) = current!(cx.editor);
793 let (view, doc) = current!(cx.editor);
843
794
844− let selection = doc.selection(view.id).transform(|range| {
795+ let (text, selection) = doc.text_and_mut_selection(view.id);
845− let text = doc.text();
796+ selection.transform(|range| {
846 let line = text.char_to_line(range.head);
797 let line = text.char_to_line(range.head);
847
798
848 // adjust to start of the line
799 // adjust to start of the line
849 let pos = text.line_to_char(line);
800 let pos = text.line_to_char(line);
850 Range::new(range.anchor, pos)
801 Range::new(range.anchor, pos)
851 });
802 });
852−
853− doc.set_selection(view.id, selection);
854}
803}
855
804
856fn select_all(cx: &mut Context) {
805fn select_all(cx: &mut Context) {
@@ -1043,20 +992,16 @@ fn change_selection(cx: &mut Context) {
1043
992
1044fn collapse_selection(cx: &mut Context) {
993fn collapse_selection(cx: &mut Context) {
1045 let (view, doc) = current!(cx.editor);
994 let (view, doc) = current!(cx.editor);
1046− let selection = doc
1047− .selection(view.id)
1048− .transform(|range| Range::new(range.head, range.head));
1049
995
1050− doc.set_selection(view.id, selection);
996+ doc.selection_mut(view.id)
997+ .transform(|range| Range::new(range.head, range.head));
1051}
998}
1052
999
1053fn flip_selections(cx: &mut Context) {
1000fn flip_selections(cx: &mut Context) {
1054 let (view, doc) = current!(cx.editor);
1001 let (view, doc) = current!(cx.editor);
1055− let selection = doc
1056− .selection(view.id)
1057− .transform(|range| Range::new(range.head, range.anchor));
1058
1002
1059− doc.set_selection(view.id, selection);
1003+ doc.selection_mut(view.id)
1004+ .transform(|range| Range::new(range.head, range.anchor));
1060}
1005}
1061
1006
1062fn enter_insert_mode(doc: &mut Document) {
1007fn enter_insert_mode(doc: &mut Document) {
@@ -1068,10 +1013,8 @@ fn insert_mode(cx: &mut Context) {
1068 let (view, doc) = current!(cx.editor);
1013 let (view, doc) = current!(cx.editor);
1069 enter_insert_mode(doc);
1014 enter_insert_mode(doc);
1070
1015
1071− let selection = doc
1016+ doc.selection_mut(view.id)
1072− .selection(view.id)
1073 .transform(|range| Range::new(range.to(), range.from()));
1017 .transform(|range| Range::new(range.to(), range.from()));
1074− doc.set_selection(view.id, selection);
1075}
1018}
1076
1019
1077// inserts at the end of each selection
1020// inserts at the end of each selection
@@ -1080,8 +1023,8 @@ fn append_mode(cx: &mut Context) {
1080 enter_insert_mode(doc);
1023 enter_insert_mode(doc);
1081 doc.restore_cursor = true;
1024 doc.restore_cursor = true;
1082
1025
1083− let text = doc.text().slice(..);
1026+ let (text, selection) = doc.text_and_mut_selection(view.id);
1084− let selection = doc.selection(view.id).transform(|range| {
1027+ selection.transform(|range| {
1085 Range::new(
1028 Range::new(
1086 range.from(),
1029 range.from(),
1087 graphemes::next_grapheme_boundary(text, range.to()), // to() + next char
1030 graphemes::next_grapheme_boundary(text, range.to()), // to() + next char
@@ -1097,8 +1040,6 @@ fn append_mode(cx: &mut Context) {
1097 );
1040 );
1098 doc.apply(&transaction, view.id);
1041 doc.apply(&transaction, view.id);
1099 }
1042 }
1100−
1101− doc.set_selection(view.id, selection);
1102}
1043}
1103
1044
1104mod cmd {
1045mod cmd {
@@ -1904,13 +1845,12 @@ fn append_to_line(cx: &mut Context) {
1904 let (view, doc) = current!(cx.editor);
1845 let (view, doc) = current!(cx.editor);
1905 enter_insert_mode(doc);
1846 enter_insert_mode(doc);
1906
1847
1907− let selection = doc.selection(view.id).transform(|range| {
1848+ let (text, selection) = doc.text_and_mut_selection(view.id);
1908− let text = doc.text();
1849+ selection.transform(|range| {
1909 let line = text.char_to_line(range.head);
1850 let line = text.char_to_line(range.head);
1910 let pos = line_end_char_index(&text.slice(..), line);
1851 let pos = line_end_char_index(&text.slice(..), line);
1911 Range::new(pos, pos)
1852 Range::new(pos, pos)
1912 });
1853 });
1913− doc.set_selection(view.id, selection);
1914}
1854}
1915
1855
1916/// Sometimes when applying formatting changes we want to mark the buffer as unmodified, for
1856/// Sometimes when applying formatting changes we want to mark the buffer as unmodified, for
@@ -2040,14 +1980,13 @@ fn normal_mode(cx: &mut Context) {
2040
1980
2041 // if leaving append mode, move cursor back by 1
1981 // if leaving append mode, move cursor back by 1
2042 if doc.restore_cursor {
1982 if doc.restore_cursor {
2043− let text = doc.text().slice(..);
1983+ let (text, selection) = doc.text_and_mut_selection(view.id);
2044− let selection = doc.selection(view.id).transform(|range| {
1984+ selection.transform(|range| {
2045 Range::new(
1985 Range::new(
2046 range.from(),
1986 range.from(),
2047 graphemes::prev_grapheme_boundary(text, range.to()),
1987 graphemes::prev_grapheme_boundary(text, range.to()),
2048 )
1988 )
2049 });
1989 });
2050− doc.set_selection(view.id, selection);
2051
1990
2052 doc.restore_cursor = false;
1991 doc.restore_cursor = false;
2053 }
1992 }
@@ -2679,11 +2618,9 @@ pub mod insert {
2679 pub fn delete_word_backward(cx: &mut Context) {
2618 pub fn delete_word_backward(cx: &mut Context) {
2680 let count = cx.count();
2619 let count = cx.count();
2681 let (view, doc) = current!(cx.editor);
2620 let (view, doc) = current!(cx.editor);
2682− let text = doc.text().slice(..);
2621+
2683− let selection = doc
2622+ let (text, selection) = doc.text_and_mut_selection(view.id);
2684− .selection(view.id)
2685− .transform(|range| movement::move_prev_word_start(text, range, count));
2623+ selection.transform(|range| movement::move_prev_word_start(text, range, count));
2686− doc.set_selection(view.id, selection);
2687 delete_selection(cx)
2624 delete_selection(cx)
2688 }
2625 }
2689}
2626}
helix-view/src/document.rs+18−2
@@ -13,8 +13,8 @@ use helix_core::{
13 history::History,
13 history::History,
14 line_ending::auto_detect_line_ending,
14 line_ending::auto_detect_line_ending,
15 syntax::{self, LanguageConfiguration},
15 syntax::{self, LanguageConfiguration},
16− ChangeSet, Diagnostic, LineEnding, Rope, RopeBuilder, Selection, State, Syntax, Transaction,
16+ ChangeSet, Diagnostic, LineEnding, Rope, RopeBuilder, RopeSlice, Selection, State, Syntax,
17− DEFAULT_LINE_ENDING,
17+ Transaction, DEFAULT_LINE_ENDING,
18};
18};
19use helix_lsp::util::LspFormatting;
19use helix_lsp::util::LspFormatting;
20
20
@@ -1000,6 +1000,22 @@ impl Document {
1000 &self.selections[&view_id]
1000 &self.selections[&view_id]
1001 }
1001 }
1002
1002
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+
1003 pub fn relative_path(&self) -> Option<PathBuf> {
1019 pub fn relative_path(&self) -> Option<PathBuf> {
1004 let cwdir = std::env::current_dir().expect("couldn't determine current directory");
1020 let cwdir = std::env::current_dir().expect("couldn't determine current directory");
1005
1021