1use std::cmp::Ordering;
18use std::ops::Range;
19
20use xi_rope::breaks::{BreakBuilder, Breaks, BreaksInfo, BreaksMetric};
21use xi_rope::spans::Spans;
22use xi_rope::{Cursor, Interval, LinesMetric, Rope, RopeDelta, RopeInfo};
23use xi_trace::trace_block;
24use xi_unicode::LineBreakLeafIter;
25
26use crate::client::Client;
27use crate::styles::{Style, N_RESERVED_STYLES};
28use crate::width_cache::{CodepointMono, Token, WidthCache, WidthMeasure};
29
30#[derive(Clone, Copy, Debug, PartialEq)]
32pub(crate) enum WrapWidth {
33 None,
35
36 Bytes(usize),
40
41 Width(f64),
43}
44
45impl Default for WrapWidth {
46 fn default() -> Self {
47 WrapWidth::None
48 }
49}
50
51impl WrapWidth {
52 fn differs_in_kind(self, other: WrapWidth) -> bool {
53 use self::WrapWidth::*;
54 match (self, other) {
55 (None, None) | (Bytes(_), Bytes(_)) | (Width(_), Width(_)) => false,
56 _else => true,
57 }
58 }
59}
60
61type Task = Interval;
63
64#[derive(Default)]
66pub(crate) struct Lines {
67 breaks: Breaks,
68 wrap: WrapWidth,
69 work: Vec<Task>,
71}
72
73pub(crate) struct VisualLine {
74 pub(crate) interval: Interval,
75 pub(crate) line_num: Option<usize>,
78}
79
80impl VisualLine {
81 fn new<I: Into<Interval>, L: Into<Option<usize>>>(iv: I, line: L) -> Self {
82 VisualLine { interval: iv.into(), line_num: line.into() }
83 }
84}
85
86pub(crate) struct InvalLines {
89 pub(crate) start_line: usize,
90 pub(crate) inval_count: usize,
91 pub(crate) new_count: usize,
92}
93
94struct WrapSummary {
100 start_line: usize,
101 inval_count: usize,
103 new_count: usize,
105 new_soft: usize,
107}
108
109impl Lines {
110 pub(crate) fn set_wrap_width(&mut self, text: &Rope, wrap: WrapWidth) {
111 self.work.clear();
112 self.add_task(0..text.len());
113 if self.breaks.is_empty() || self.wrap.differs_in_kind(wrap) {
114 self.breaks = Breaks::new_no_break(text.len());
116 }
117 self.wrap = wrap;
118 }
119
120 fn add_task<T: Into<Interval>>(&mut self, iv: T) {
121 let iv = iv.into();
122 if iv.is_empty() {
123 return;
124 }
125
126 let split_idx = match self.work.iter().position(|&t| !t.intersect(iv).is_empty()) {
128 Some(idx) => idx,
129 None => {
130 self.work.push(iv);
131 return;
132 }
133 };
134
135 let to_update = self.work.split_off(split_idx);
136 let mut new_task = Some(iv);
137
138 for t in &to_update {
139 match new_task.take() {
140 Some(new) if !t.intersect(new).is_empty() => new_task = Some(t.union(new)),
141 Some(new) => {
142 self.work.push(new);
143 self.work.push(*t);
144 }
145 None => self.work.push(*t),
146 }
147 }
148 if let Some(end) = new_task.take() {
149 self.work.push(end);
150 }
151 }
152
153 pub(crate) fn is_converged(&self) -> bool {
154 self.wrap == WrapWidth::None || self.work.is_empty()
155 }
156
157 pub(crate) fn interval_needs_wrap(&self, iv: Interval) -> bool {
159 self.work.iter().any(|t| !t.intersect(iv).is_empty())
160 }
161
162 pub(crate) fn visual_line_of_offset(&self, text: &Rope, offset: usize) -> usize {
163 let mut line = text.line_of_offset(offset);
164 if self.wrap != WrapWidth::None {
165 line += self.breaks.count::<BreaksMetric>(offset)
166 }
167 line
168 }
169
170 pub(crate) fn offset_of_visual_line(&self, text: &Rope, line: usize) -> usize {
172 match self.wrap {
173 WrapWidth::None => {
174 let line = line.min(text.measure::<LinesMetric>() + 1);
176 text.offset_of_line(line)
177 }
178 _ => {
179 let mut cursor = MergedBreaks::new(text, &self.breaks);
180 cursor.offset_of_line(line)
181 }
182 }
183 }
184
185 pub(crate) fn iter_lines<'a>(
188 &'a self,
189 text: &'a Rope,
190 start_line: usize,
191 ) -> impl Iterator<Item = VisualLine> + 'a {
192 let mut cursor = MergedBreaks::new(text, &self.breaks);
193 let offset = cursor.offset_of_line(start_line);
194 let logical_line = text.line_of_offset(offset) + 1;
195 cursor.set_offset(offset);
196 VisualLines { offset, cursor, len: text.len(), logical_line, eof: false }
197 }
198
199 fn get_next_task(&self, visible_offset: usize) -> Option<Task> {
202 self.work
205 .iter()
206 .find(|t| t.end > visible_offset)
207 .map(|t| Task::new(t.start.max(visible_offset), t.end))
208 .or(self.work.last().cloned())
209 }
210
211 fn update_tasks_after_wrap<T: Into<Interval>>(&mut self, wrapped_iv: T) {
212 if self.work.is_empty() {
213 return;
214 }
215 let wrapped_iv = wrapped_iv.into();
216
217 let mut work = Vec::new();
218 for task in &self.work {
219 if task.is_before(wrapped_iv.start) || task.is_after(wrapped_iv.end) {
220 work.push(*task);
221 continue;
222 }
223 if wrapped_iv.start > task.start {
224 work.push(task.prefix(wrapped_iv));
225 }
226 if wrapped_iv.end < task.end {
227 work.push(task.suffix(wrapped_iv));
228 }
229 }
230 self.work = work;
231 }
232
233 fn patchup_tasks<T: Into<Interval>>(&mut self, iv: T, new_len: usize) {
235 let iv = iv.into();
236 let mut new_work = Vec::new();
237
238 for task in &self.work {
239 if task.is_before(iv.start) {
240 new_work.push(*task);
241 } else if task.contains(iv.start) {
242 let head = task.prefix(iv);
243 let tail_end = iv.start.max((task.end + new_len).saturating_sub(iv.size()));
244 let tail = Interval::new(iv.start, tail_end);
245 new_work.push(head);
246 new_work.push(tail);
247 } else {
248 let tail = task.suffix(iv).translate(new_len).translate_neg(iv.size());
250 new_work.push(tail);
251 }
252 }
253 new_work.retain(|iv| !iv.is_empty());
254 self.work.clear();
255 for task in new_work {
256 if let Some(prev) = self.work.last_mut() {
257 if prev.end >= task.start {
258 *prev = prev.union(task);
259 continue;
260 }
261 }
262 self.work.push(task);
263 }
264 }
265
266 pub(crate) fn rewrap_chunk(
268 &mut self,
269 text: &Rope,
270 width_cache: &mut WidthCache,
271 client: &Client,
272 _spans: &Spans<Style>,
273 visible_lines: Range<usize>,
274 ) -> Option<InvalLines> {
275 if self.is_converged() {
276 None
277 } else {
278 let summary = self.do_wrap_task(text, width_cache, client, visible_lines, None);
279 let WrapSummary { start_line, inval_count, new_count, .. } = summary;
280 Some(InvalLines { start_line, inval_count, new_count })
281 }
282 }
283
284 pub(crate) fn after_edit(
287 &mut self,
288 text: &Rope,
289 old_text: &Rope,
290 delta: &RopeDelta,
291 width_cache: &mut WidthCache,
292 client: &Client,
293 visible_lines: Range<usize>,
294 ) -> Option<InvalLines> {
295 let (iv, newlen) = delta.summary();
296
297 let logical_start_line = text.line_of_offset(iv.start);
298 let old_logical_end_line = old_text.line_of_offset(iv.end) + 1;
299 let new_logical_end_line = text.line_of_offset(iv.start + newlen) + 1;
300 let old_logical_end_offset = old_text.offset_of_line(old_logical_end_line);
301 let old_hard_count = old_logical_end_line - logical_start_line;
302 let new_hard_count = new_logical_end_line - logical_start_line;
303
304 let prev_break = text.offset_of_line(logical_start_line);
307 let next_hard_break = text.offset_of_line(new_logical_end_line);
308
309 let inval_soft = self.breaks.count::<BreaksMetric>(old_logical_end_offset)
311 - self.breaks.count::<BreaksMetric>(prev_break);
312
313 let mut builder = BreakBuilder::new();
315 builder.add_no_break(newlen);
316 self.breaks.edit(iv, builder.build());
317 self.patchup_tasks(iv, newlen);
318
319 if self.wrap == WrapWidth::None {
320 return Some(InvalLines {
321 start_line: logical_start_line,
322 inval_count: old_hard_count,
323 new_count: new_hard_count,
324 });
325 }
326
327 let new_task = prev_break..next_hard_break;
328 self.add_task(new_task);
329
330 if !self.work.is_empty() {
332 let summary = self.do_wrap_task(text, width_cache, client, visible_lines, None);
333 let WrapSummary { start_line, new_soft, .. } = summary;
334 if self.is_converged() {
337 let inval_count = old_hard_count + inval_soft;
338 let new_count = new_hard_count + new_soft;
339 Some(InvalLines { start_line, new_count, inval_count })
340 } else {
341 None
342 }
343 } else {
344 None
345 }
346 }
347
348 fn do_wrap_task(
349 &mut self,
350 text: &Rope,
351 width_cache: &mut WidthCache,
352 client: &Client,
353 visible_lines: Range<usize>,
354 max_lines: Option<usize>,
355 ) -> WrapSummary {
356 use self::WrapWidth::*;
357 let _t = trace_block("Lines::do_wrap_task", &["core"]);
358 const MAX_LINES_PER_BATCH: usize = 500;
360
361 let mut cursor = MergedBreaks::new(text, &self.breaks);
362 let visible_off = cursor.offset_of_line(visible_lines.start);
363 let logical_off = text.offset_of_line(text.line_of_offset(visible_off));
364
365 let task = self.get_next_task(logical_off).unwrap();
367 cursor.set_offset(task.start);
368 debug_assert_eq!(cursor.offset, task.start, "task_start must be valid offset");
369
370 let mut ctx = match self.wrap {
371 Bytes(b) => RewrapCtx::new(text, &CodepointMono, b as f64, width_cache, task.start),
372 Width(w) => RewrapCtx::new(text, client, w, width_cache, task.start),
373 None => unreachable!(),
374 };
375
376 let start_line = cursor.cur_line;
377 let max_lines = max_lines.unwrap_or(MAX_LINES_PER_BATCH);
378 let batch_size = max_lines.max(visible_lines.end - visible_lines.start);
380
381 let mut builder = BreakBuilder::new();
382 let mut lines_wrapped = 0;
383 let mut pos = task.start;
384 let mut old_next_maybe = cursor.next();
385
386 loop {
387 if let Some(new_next) = ctx.wrap_one_line(pos) {
388 while let Some(old_next) = old_next_maybe {
389 if old_next >= new_next {
390 break; }
392 old_next_maybe = cursor.next();
393 }
394
395 let is_hard = cursor.offset == new_next && cursor.is_hard_break();
396 if is_hard {
397 builder.add_no_break(new_next - pos);
398 } else {
399 builder.add_break(new_next - pos);
400 }
401 lines_wrapped += 1;
402 pos = new_next;
403 if pos == task.end || (lines_wrapped > batch_size && is_hard) {
404 break;
405 }
406 } else {
407 builder.add_no_break(text.len() - pos);
409 break;
410 }
411 }
412
413 let breaks = builder.build();
414 let end = task.start + breaks.len();
415
416 let inval_soft =
418 self.breaks.count::<BreaksMetric>(end) - self.breaks.count::<BreaksMetric>(task.start);
419
420 let hard_count = 1 + text.line_of_offset(end) - text.line_of_offset(task.start);
421
422 let inval_count = inval_soft + hard_count;
423 let new_soft = breaks.measure::<BreaksMetric>();
424 let new_count = new_soft + hard_count;
425
426 let iv = Interval::new(task.start, end);
427 self.breaks.edit(iv, breaks);
428 self.update_tasks_after_wrap(iv);
429
430 WrapSummary { start_line, inval_count, new_count, new_soft }
431 }
432
433 pub fn logical_line_range(&self, text: &Rope, line: usize) -> (usize, usize) {
434 let mut cursor = MergedBreaks::new(text, &self.breaks);
435 let offset = cursor.offset_of_line(line);
436 let logical_line = text.line_of_offset(offset);
437 let start_logical_line_offset = text.offset_of_line(logical_line);
438 let end_logical_line_offset = text.offset_of_line(logical_line + 1);
439 (start_logical_line_offset, end_logical_line_offset)
440 }
441
442 #[cfg(test)]
443 fn for_testing(text: &Rope, wrap: WrapWidth) -> Lines {
444 let mut lines = Lines::default();
445 lines.set_wrap_width(text, wrap);
446 lines
447 }
448
449 #[cfg(test)]
450 fn rewrap_all(&mut self, text: &Rope, client: &Client, width_cache: &mut WidthCache) {
451 if !self.is_converged() {
452 self.do_wrap_task(text, width_cache, client, 0..10, Some(usize::max_value()));
453 }
454 }
455}
456
457struct PotentialBreak {
461 pos: usize,
463 tok: Token,
465 hard: bool,
467}
468
469struct RewrapCtx<'a> {
471 text: &'a Rope,
472 lb_cursor: LineBreakCursor<'a>,
473 lb_cursor_pos: usize,
474 width_cache: &'a mut WidthCache,
475 client: &'a dyn WidthMeasure,
476 pot_breaks: Vec<PotentialBreak>,
477 pot_break_ix: usize,
479 max_width: f64,
480}
481
482const MAX_POT_BREAKS: usize = 10_000;
485
486impl<'a> RewrapCtx<'a> {
487 fn new(
488 text: &'a Rope,
489 client: &'a dyn WidthMeasure,
491 max_width: f64,
492 width_cache: &'a mut WidthCache,
493 start: usize,
494 ) -> RewrapCtx<'a> {
495 let lb_cursor_pos = start;
496 let lb_cursor = LineBreakCursor::new(text, start);
497 RewrapCtx {
498 text,
499 lb_cursor,
500 lb_cursor_pos,
501 width_cache,
502 client,
503 pot_breaks: Vec::new(),
504 pot_break_ix: 0,
505 max_width,
506 }
507 }
508
509 fn refill_pot_breaks(&mut self) {
510 let mut req = self.width_cache.batch_req();
511
512 self.pot_breaks.clear();
513 self.pot_break_ix = 0;
514 let mut pos = self.lb_cursor_pos;
515 while pos < self.text.len() && self.pot_breaks.len() < MAX_POT_BREAKS {
516 let (next, hard) = self.lb_cursor.next();
517 let word = self.text.slice_to_cow(pos..next);
518 let tok = req.request(N_RESERVED_STYLES, &word);
519 pos = next;
520 self.pot_breaks.push(PotentialBreak { pos, tok, hard });
521 }
522 req.resolve_pending(self.client).unwrap();
523 self.lb_cursor_pos = pos;
524 }
525
526 fn wrap_one_line(&mut self, start: usize) -> Option<usize> {
530 let mut line_width = 0.0;
531 let mut pos = start;
532 while pos < self.text.len() {
533 if self.pot_break_ix >= self.pot_breaks.len() {
534 self.refill_pot_breaks();
535 }
536 let pot_break = &self.pot_breaks[self.pot_break_ix];
537 let width = self.width_cache.resolve(pot_break.tok);
538 if !pot_break.hard {
539 if line_width == 0.0 && width >= self.max_width {
540 if pot_break.pos == self.text.len() {
542 return None;
543 }
544 self.pot_break_ix += 1;
545 return Some(pot_break.pos);
546 }
547 line_width += width;
548 if line_width > self.max_width {
549 return Some(pos);
550 }
551 self.pot_break_ix += 1;
552 pos = pot_break.pos;
553 } else if line_width != 0. && width + line_width > self.max_width {
554 return Some(pos);
557 } else {
558 self.pot_break_ix += 1;
559 return Some(pot_break.pos);
560 }
561 }
562 None
563 }
564}
565
566struct LineBreakCursor<'a> {
567 inner: Cursor<'a, RopeInfo>,
568 lb_iter: LineBreakLeafIter,
569 last_byte: u8,
570}
571
572impl<'a> LineBreakCursor<'a> {
573 fn new(text: &'a Rope, pos: usize) -> LineBreakCursor<'a> {
574 let inner = Cursor::new(text, pos);
575 let lb_iter = match inner.get_leaf() {
576 Some((s, offset)) => LineBreakLeafIter::new(s.as_str(), offset),
577 _ => LineBreakLeafIter::default(),
578 };
579 LineBreakCursor { inner, lb_iter, last_byte: 0 }
580 }
581
582 fn next(&mut self) -> (usize, bool) {
584 let mut leaf = self.inner.get_leaf();
585 loop {
586 match leaf {
587 Some((s, offset)) => {
588 let (next, hard) = self.lb_iter.next(s.as_str());
589 if next < s.len() {
590 return (self.inner.pos() - offset + next, hard);
591 }
592 if !s.is_empty() {
593 self.last_byte = s.as_bytes()[s.len() - 1];
594 }
595 leaf = self.inner.next_leaf();
596 }
597 None => return (self.inner.pos(), self.last_byte == b'\n'),
599 }
600 }
601 }
602}
603
604struct VisualLines<'a> {
605 cursor: MergedBreaks<'a>,
606 offset: usize,
607 logical_line: usize,
609 len: usize,
610 eof: bool,
611}
612
613impl<'a> Iterator for VisualLines<'a> {
614 type Item = VisualLine;
615
616 fn next(&mut self) -> Option<VisualLine> {
617 let line_num = if self.cursor.is_hard_break() { Some(self.logical_line) } else { None };
618 let next_end_bound = match self.cursor.next() {
619 Some(b) => b,
620 None if self.eof => return None,
621 _else => {
622 self.eof = true;
623 self.len
624 }
625 };
626 let result = VisualLine::new(self.offset..next_end_bound, line_num);
627 if self.cursor.is_hard_break() {
628 self.logical_line += 1;
629 }
630 self.offset = next_end_bound;
631 Some(result)
632 }
633}
634
635struct MergedBreaks<'a> {
645 text: Cursor<'a, RopeInfo>,
646 soft: Cursor<'a, BreaksInfo>,
647 offset: usize,
648 cur_line: usize,
650 total_lines: usize,
651 len: usize,
653}
654
655impl<'a> Iterator for MergedBreaks<'a> {
656 type Item = usize;
657
658 fn next(&mut self) -> Option<usize> {
659 if self.text.pos() == self.offset && !self.at_eof() {
660 self.text.next::<LinesMetric>();
662 }
663 if self.soft.pos() == self.offset {
664 self.soft.next::<BreaksMetric>();
665 }
666 let prev_off = self.offset;
667 self.offset = self.text.pos().min(self.soft.pos());
668
669 let eof_without_newline = self.offset > 0 && self.at_eof() && self.eof_without_newline();
670 if self.offset == prev_off || eof_without_newline {
671 None
672 } else {
673 self.cur_line += 1;
674 Some(self.offset)
675 }
676 }
677}
678
679const MAX_LINEAR_DIST: usize = 20;
682
683impl<'a> MergedBreaks<'a> {
684 fn new(text: &'a Rope, breaks: &'a Breaks) -> Self {
685 debug_assert_eq!(text.len(), breaks.len());
686 let text = Cursor::new(text, 0);
687 let soft = Cursor::new(breaks, 0);
688 let total_lines =
689 text.root().measure::<LinesMetric>() + soft.root().measure::<BreaksMetric>() + 1;
690 let len = text.total_len();
691 MergedBreaks { text, soft, offset: 0, cur_line: 0, total_lines, len }
692 }
693
694 fn set_offset(&mut self, offset: usize) {
697 self.text.set(offset);
698 self.soft.set(offset);
699 if offset > 0 {
700 if self.text.at_or_prev::<LinesMetric>().is_none() {
701 self.text.set(0);
702 }
703 if self.soft.at_or_prev::<BreaksMetric>().is_none() {
704 self.soft.set(0);
705 }
706 }
707
708 match self.text.pos().cmp(&self.soft.pos()) {
711 Ordering::Less => {
712 self.text.next::<LinesMetric>();
713 }
714 Ordering::Greater => {
715 self.soft.next::<BreaksMetric>();
716 }
717 Ordering::Equal => assert!(self.text.pos() == 0),
718 }
719
720 self.offset = self.text.pos().min(self.soft.pos());
721 self.cur_line = merged_line_of_offset(self.text.root(), self.soft.root(), self.offset);
722 }
723
724 fn offset_of_line(&mut self, line: usize) -> usize {
725 match line {
726 0 => 0,
727 l if l >= self.total_lines => self.text.total_len(),
728 l if l == self.cur_line => self.offset,
729 l if l > self.cur_line && l - self.cur_line < MAX_LINEAR_DIST => {
730 self.offset_of_line_linear(l)
731 }
732 other => self.offset_of_line_bsearch(other),
733 }
734 }
735
736 fn offset_of_line_linear(&mut self, line: usize) -> usize {
737 assert!(line > self.cur_line);
738 let dist = line - self.cur_line;
739 self.nth(dist - 1).unwrap_or(self.len)
740 }
741
742 fn offset_of_line_bsearch(&mut self, line: usize) -> usize {
743 let mut range = 0..self.len;
744 loop {
745 let pivot = range.start + (range.end - range.start) / 2;
746 self.set_offset(pivot);
747
748 match self.cur_line {
749 l if l == line => break self.offset,
750 l if l > line => range = range.start..pivot,
751 l if line - l > MAX_LINEAR_DIST => range = pivot..range.end,
752 _else => break self.offset_of_line_linear(line),
753 }
754 }
755 }
756
757 fn is_hard_break(&self) -> bool {
758 self.offset == self.text.pos()
759 }
760
761 fn at_eof(&self) -> bool {
762 self.offset == self.len
763 }
764
765 fn eof_without_newline(&mut self) -> bool {
766 debug_assert!(self.at_eof());
767 self.text.set(self.len);
768 self.text.get_leaf().map(|(l, _)| l.as_bytes().last() != Some(&b'\n')).unwrap()
769 }
770}
771
772fn merged_line_of_offset(text: &Rope, soft: &Breaks, offset: usize) -> usize {
773 text.count::<LinesMetric>(offset) + soft.count::<BreaksMetric>(offset)
774}
775
776#[cfg(test)]
777mod tests {
778 use super::*;
779 use std::borrow::Cow;
780 use std::iter;
781 use xi_rpc::test_utils::DummyPeer;
782
783 fn make_lines(text: &Rope, width: f64) -> Lines {
784 let client = Client::new(Box::new(DummyPeer));
785 let mut width_cache = WidthCache::new();
786 let wrap = WrapWidth::Bytes(width as usize);
787 let mut lines = Lines::for_testing(text, wrap);
788 lines.rewrap_all(text, &client, &mut width_cache);
789 lines
790 }
791
792 fn render_breaks<'a>(text: &'a Rope, lines: &Lines) -> Vec<Cow<'a, str>> {
793 let result = lines.iter_lines(text, 0).map(|l| text.slice_to_cow(l.interval)).collect();
794 result
795 }
796
797 fn debug_breaks<'a>(text: &'a Rope, width: f64) -> Vec<Cow<'a, str>> {
798 let lines = make_lines(text, width);
799 render_breaks(text, &lines)
800 }
801
802 #[test]
803 fn column_breaks_basic() {
804 let text: Rope = "every wordthing should getits own".into();
805 let result = debug_breaks(&text, 8.0);
806 assert_eq!(result, vec!["every ", "wordthing ", "should ", "getits ", "own",]);
807 }
808
809 #[test]
810 fn column_breaks_trailing_newline() {
811 let text: Rope = "every wordthing should getits ow\n".into();
812 let result = debug_breaks(&text, 8.0);
813 assert_eq!(result, vec!["every ", "wordthing ", "should ", "getits ", "ow\n", "",]);
814 }
815
816 #[test]
817 fn soft_before_hard() {
818 let text: Rope = "create abreak between THESE TWO\nwords andbreakcorrectlyhere\nplz".into();
819 let result = debug_breaks(&text, 4.0);
820 assert_eq!(
821 result,
822 vec![
823 "create ",
824 "abreak ",
825 "between ",
826 "THESE ",
827 "TWO\n",
828 "words ",
829 "andbreakcorrectlyhere\n",
830 "plz",
831 ]
832 );
833 }
834
835 #[test]
836 fn column_breaks_hard_soft() {
837 let text: Rope = "so\nevery wordthing should getits own".into();
838 let result = debug_breaks(&text, 4.0);
839 assert_eq!(result, vec!["so\n", "every ", "wordthing ", "should ", "getits ", "own",]);
840 }
841
842 #[test]
843 fn empty_file() {
844 let text: Rope = "".into();
845 let result = debug_breaks(&text, 4.0);
846 assert_eq!(result, vec![""]);
847 }
848
849 #[test]
850 fn dont_break_til_i_tell_you() {
851 let text: Rope = "thisis_longerthan_our_break_width".into();
852 let result = debug_breaks(&text, 12.0);
853 assert_eq!(result, vec!["thisis_longerthan_our_break_width"]);
854 }
855
856 #[test]
857 fn break_now_though() {
858 let text: Rope = "thisis_longerthan_our_break_width hi".into();
859 let result = debug_breaks(&text, 12.0);
860 assert_eq!(result, vec!["thisis_longerthan_our_break_width ", "hi"]);
861 }
862
863 #[test]
864 fn newlines() {
865 let text: Rope = "\n\n".into();
866 let result = debug_breaks(&text, 4.0);
867 assert_eq!(result, vec!["\n", "\n", ""]);
868 }
869
870 #[test]
871 fn newline_eof() {
872 let text: Rope = "hello\n".into();
873 let result = debug_breaks(&text, 4.0);
874 assert_eq!(result, vec!["hello\n", ""]);
875 }
876
877 #[test]
878 fn no_newline_eof() {
879 let text: Rope = "hello".into();
880 let result = debug_breaks(&text, 4.0);
881 assert_eq!(result, vec!["hello"]);
882 }
883
884 #[test]
885 fn merged_offset() {
886 let text: Rope = "a quite\nshort text".into();
887 let mut builder = BreakBuilder::new();
888 builder.add_break(2);
889 builder.add_no_break(text.len() - 2);
890 let breaks = builder.build();
891 assert_eq!(merged_line_of_offset(&text, &breaks, 0), 0);
892 assert_eq!(merged_line_of_offset(&text, &breaks, 1), 0);
893 assert_eq!(merged_line_of_offset(&text, &breaks, 2), 1);
894 assert_eq!(merged_line_of_offset(&text, &breaks, 5), 1);
895 assert_eq!(merged_line_of_offset(&text, &breaks, 5), 1);
896 assert_eq!(merged_line_of_offset(&text, &breaks, 9), 2);
897 assert_eq!(merged_line_of_offset(&text, &breaks, text.len()), 2);
898
899 let text: Rope = "a quite\nshort tex\n".into();
900 assert_eq!(merged_line_of_offset(&text, &breaks, text.len()), 3);
902 }
903
904 #[test]
905 fn bsearch_equivalence() {
906 let text: Rope =
907 iter::repeat("this is a line with some text in it, which is not unusual\n")
908 .take(1000)
909 .collect::<String>()
910 .into();
911 let lines = make_lines(&text, 30.);
912
913 let mut linear = MergedBreaks::new(&text, &lines.breaks);
914 let mut binary = MergedBreaks::new(&text, &lines.breaks);
915
916 for i in 1..1000 {
918 linear.set_offset(0);
919 binary.set_offset(0);
920 assert_eq!(
921 linear.offset_of_line_linear(i),
922 binary.offset_of_line_bsearch(i),
923 "line {}",
924 i
925 );
926 }
927 }
928
929 #[test]
930 fn merge_cursor_no_breaks() {
931 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
932 let breaks = Breaks::new_no_break(text.len());
934 let mut cursor = MergedBreaks::new(&text, &breaks);
935 assert_eq!(cursor.offset, 0);
936 assert_eq!(cursor.cur_line, 0);
937 assert_eq!(cursor.len, text.len());
938 assert_eq!(cursor.total_lines, 4);
939 assert!(cursor.is_hard_break());
940
941 assert_eq!(cursor.next(), Some(5));
942 assert_eq!(cursor.cur_line, 1);
943 assert_eq!(cursor.offset, 5);
944 assert_eq!(cursor.text.pos(), 5);
945 assert_eq!(cursor.soft.pos(), text.len());
946 assert!(cursor.is_hard_break());
947
948 assert_eq!(cursor.next(), Some(14));
949 assert_eq!(cursor.cur_line, 2);
950 assert_eq!(cursor.offset, 14);
951 assert_eq!(cursor.text.pos(), 14);
952 assert_eq!(cursor.soft.pos(), text.len());
953 assert!(cursor.is_hard_break());
954
955 assert_eq!(cursor.next(), Some(30));
956 assert_eq!(cursor.next(), None);
957 }
958
959 #[test]
960 fn merge_cursor_breaks() {
961 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
962
963 let mut builder = BreakBuilder::new();
964 builder.add_break(8);
965 builder.add_break(3);
966 builder.add_no_break(text.len() - (8 + 3));
967 let breaks = builder.build();
968
969 let mut cursor = MergedBreaks::new(&text, &breaks);
970 assert_eq!(cursor.offset, 0);
971 assert_eq!(cursor.cur_line, 0);
972 assert_eq!(cursor.len, text.len());
973 assert_eq!(cursor.total_lines, 6);
974
975 assert_eq!(cursor.next(), Some(5));
976 assert_eq!(cursor.cur_line, 1);
977 assert_eq!(cursor.offset, 5);
978 assert_eq!(cursor.text.pos(), 5);
979 assert_eq!(cursor.soft.pos(), 8);
980 assert!(cursor.is_hard_break());
981
982 assert_eq!(cursor.next(), Some(8));
983 assert_eq!(cursor.cur_line, 2);
984 assert_eq!(cursor.offset, 8);
985 assert_eq!(cursor.text.pos(), 14);
986 assert_eq!(cursor.soft.pos(), 8);
987 assert!(!cursor.is_hard_break());
988
989 assert_eq!(cursor.next(), Some(11));
990 assert_eq!(cursor.cur_line, 3);
991 assert_eq!(cursor.offset, 11);
992 assert_eq!(cursor.text.pos(), 14);
993 assert_eq!(cursor.soft.pos(), 11);
994 assert!(!cursor.is_hard_break());
995
996 assert_eq!(cursor.next(), Some(14));
997 assert_eq!(cursor.cur_line, 4);
998 assert_eq!(cursor.offset, 14);
999 assert_eq!(cursor.text.pos(), 14);
1000 assert_eq!(cursor.soft.pos(), text.len());
1001 assert!(cursor.is_hard_break());
1002 }
1003
1004 #[test]
1005 fn set_offset() {
1006 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
1007 let lines = make_lines(&text, 2.);
1008 let mut merged = MergedBreaks::new(&text, &lines.breaks);
1009 assert_eq!(merged.total_lines, 10);
1010
1011 let check_props = |m: &MergedBreaks, line, off, softpos, hardpos| {
1012 assert_eq!(m.cur_line, line);
1013 assert_eq!(m.offset, off);
1014 assert_eq!(m.soft.pos(), softpos);
1015 assert_eq!(m.text.pos(), hardpos);
1016 };
1017 merged.next();
1018 check_props(&merged, 1, 5, 8, 5);
1019 merged.set_offset(0);
1020 check_props(&merged, 0, 0, 0, 0);
1021 merged.set_offset(5);
1022 check_props(&merged, 1, 5, 8, 5);
1023 merged.set_offset(0);
1024 merged.set_offset(6);
1025 check_props(&merged, 1, 5, 8, 5);
1026 merged.set_offset(9);
1027 check_props(&merged, 2, 8, 8, 14);
1028 merged.set_offset(text.len());
1029 check_props(&merged, 9, 33, 33, 37);
1030 merged.set_offset(text.len() - 1);
1031 check_props(&merged, 9, 33, 33, 37);
1032
1033 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff ggg\n".into();
1035 let lines = make_lines(&text, 2.);
1036 let mut merged = MergedBreaks::new(&text, &lines.breaks);
1037 assert_eq!(merged.total_lines, 11);
1038 merged.set_offset(text.len());
1039 check_props(&merged, 10, 37, 37, 37);
1040 merged.set_offset(text.len() - 1);
1041 check_props(&merged, 9, 33, 33, 37);
1042 }
1043
1044 #[test]
1045 fn test_break_at_linear_transition() {
1046 let text = "a b c d e f g h i j k l m n o p q r s t u v w x ".into();
1048 let lines = make_lines(&text, 1.);
1049
1050 for offset in 0..text.len() {
1051 let line = lines.visual_line_of_offset(&text, offset);
1052 let line_offset = lines.offset_of_visual_line(&text, line);
1053 assert!(line_offset <= offset, "{} <= {} L{} O{}", line_offset, offset, line, offset);
1054 }
1055 }
1056
1057 #[test]
1058 fn expected_soft_breaks() {
1059 let text = "a b c d ".into();
1060 let mut text_cursor = Cursor::new(&text, text.len());
1061 assert!(!text_cursor.is_boundary::<LinesMetric>());
1062
1063 let Lines { breaks, .. } = make_lines(&text, 1.);
1064 let mut cursor = Cursor::new(&breaks, 0);
1065
1066 cursor.set(2);
1067 assert!(cursor.is_boundary::<BreaksMetric>());
1068 cursor.set(4);
1069 assert!(cursor.is_boundary::<BreaksMetric>());
1070 cursor.set(6);
1071 assert!(cursor.is_boundary::<BreaksMetric>());
1072 cursor.set(8);
1073 assert!(!cursor.is_boundary::<BreaksMetric>());
1074
1075 cursor.set(0);
1076 let breaks = cursor.iter::<BreaksMetric>().collect::<Vec<_>>();
1077 assert_eq!(breaks, vec![2, 4, 6]);
1078 }
1079
1080 #[test]
1081 fn expected_soft_with_hard() {
1082 let text: Rope = "aa\nbb cc\ncc dd ee ff\ngggg".into();
1083 let Lines { breaks, .. } = make_lines(&text, 2.);
1084 let mut cursor = Cursor::new(&breaks, 0);
1085 let breaks = cursor.iter::<BreaksMetric>().collect::<Vec<_>>();
1086 assert_eq!(breaks, vec![6, 12, 15, 18]);
1087 }
1088
1089 #[test]
1090 fn offset_to_line() {
1091 let text = "a b c d ".into();
1092 let lines = make_lines(&text, 1.);
1093 let cursor = MergedBreaks::new(&text, &lines.breaks);
1094 assert_eq!(cursor.total_lines, 4);
1095
1096 assert_eq!(lines.visual_line_of_offset(&text, 0), 0);
1097 assert_eq!(lines.visual_line_of_offset(&text, 1), 0);
1098 assert_eq!(lines.visual_line_of_offset(&text, 2), 1);
1099 assert_eq!(lines.visual_line_of_offset(&text, 3), 1);
1100 assert_eq!(lines.visual_line_of_offset(&text, 4), 2);
1101 assert_eq!(lines.visual_line_of_offset(&text, 5), 2);
1102 assert_eq!(lines.visual_line_of_offset(&text, 6), 3);
1103 assert_eq!(lines.visual_line_of_offset(&text, 7), 3);
1104
1105 assert_eq!(lines.offset_of_visual_line(&text, 0), 0);
1106 assert_eq!(lines.offset_of_visual_line(&text, 1), 2);
1107 assert_eq!(lines.offset_of_visual_line(&text, 2), 4);
1108 assert_eq!(lines.offset_of_visual_line(&text, 3), 6);
1109 assert_eq!(lines.offset_of_visual_line(&text, 10), 8);
1110
1111 for offset in 0..text.len() {
1112 let line = lines.visual_line_of_offset(&text, offset);
1113 let line_offset = lines.offset_of_visual_line(&text, line);
1114 assert!(line_offset <= offset, "{} <= {} L{} O{}", line_offset, offset, line, offset);
1115 }
1116 }
1117
1118 #[test]
1119 fn iter_lines() {
1120 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
1121 let lines = make_lines(&text, 2.);
1122 let r: Vec<_> =
1123 lines.iter_lines(&text, 0).take(2).map(|l| text.slice_to_cow(l.interval)).collect();
1124 assert_eq!(r, vec!["aaaa\n", "bb "]);
1125
1126 let r: Vec<_> =
1127 lines.iter_lines(&text, 1).take(2).map(|l| text.slice_to_cow(l.interval)).collect();
1128 assert_eq!(r, vec!["bb ", "bb "]);
1129
1130 let r: Vec<_> =
1131 lines.iter_lines(&text, 3).take(3).map(|l| text.slice_to_cow(l.interval)).collect();
1132 assert_eq!(r, vec!["cc\n", "cc ", "dddd "]);
1133 }
1134
1135 #[test]
1136 fn line_numbers() {
1137 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
1138 let lines = make_lines(&text, 2.);
1139 let nums: Vec<_> = lines.iter_lines(&text, 0).map(|l| l.line_num).collect();
1140 assert_eq!(
1141 nums,
1142 vec![Some(1), Some(2), None, None, Some(3), None, None, None, Some(4), None]
1143 );
1144 }
1145
1146 fn make_ranges(ivs: &[Interval]) -> Vec<Range<usize>> {
1147 ivs.iter().map(|iv| iv.start..iv.end).collect()
1148 }
1149
1150 #[test]
1151 fn update_frontier() {
1152 let mut lines = Lines::default();
1153 lines.add_task(0..1000);
1154 lines.update_tasks_after_wrap(0..10);
1155 lines.update_tasks_after_wrap(50..100);
1156 assert_eq!(make_ranges(&lines.work), vec![10..50, 100..1000]);
1157 lines.update_tasks_after_wrap(200..1000);
1158 assert_eq!(make_ranges(&lines.work), vec![10..50, 100..200]);
1159 lines.add_task(300..400);
1160 assert_eq!(make_ranges(&lines.work), vec![10..50, 100..200, 300..400]);
1161 lines.add_task(250..350);
1162 assert_eq!(make_ranges(&lines.work), vec![10..50, 100..200, 250..400]);
1163 lines.add_task(60..450);
1164 assert_eq!(make_ranges(&lines.work), vec![10..50, 60..450]);
1165 }
1166
1167 #[test]
1168 fn patchup_frontier() {
1169 let mut lines = Lines::default();
1170 lines.add_task(0..100);
1171 assert_eq!(make_ranges(&lines.work), vec![0..100]);
1172 lines.patchup_tasks(20..30, 20);
1173 assert_eq!(make_ranges(&lines.work), vec![0..110]);
1174
1175 lines.patchup_tasks(0..200, 0);
1177 assert_eq!(make_ranges(&lines.work), vec![]);
1178
1179 lines.add_task(0..110);
1180 lines.patchup_tasks(0..30, 0);
1181 assert_eq!(make_ranges(&lines.work), vec![0..80]);
1182 lines.update_tasks_after_wrap(20..30);
1183 assert_eq!(make_ranges(&lines.work), vec![0..20, 30..80]);
1184 lines.patchup_tasks(10..40, 0);
1185 assert_eq!(make_ranges(&lines.work), vec![0..50]);
1186 lines.update_tasks_after_wrap(20..30);
1187 assert_eq!(make_ranges(&lines.work), vec![0..20, 30..50]);
1188 lines.patchup_tasks(10..10, 10);
1189 assert_eq!(make_ranges(&lines.work), vec![0..30, 40..60]);
1190 }
1191
1192 #[test]
1194 fn patchup_for_edit_before_task() {
1195 let mut lines = Lines::default();
1196 lines.add_task(0..100);
1197 lines.update_tasks_after_wrap(0..30);
1198 assert_eq!(make_ranges(&lines.work), vec![30..100]);
1199 lines.patchup_tasks(5..90, 80);
1200 assert_eq!(make_ranges(&lines.work), vec![85..95]);
1201 }
1202}