@@ -34,6 +34,10 @@ pub struct Rope {
34
34
}
35
35
36
36
impl Rope {
37
+ pub ( super ) const fn arity ( ) -> usize {
38
+ ARITY
39
+ }
40
+
37
41
#[ doc( hidden) ]
38
42
pub fn assert_invariants ( & self ) {
39
43
self . tree . assert_invariants ( ) ;
@@ -66,6 +70,8 @@ impl Rope {
66
70
67
71
/// Returns the byte at `byte_index`.
68
72
///
73
+ /// For a checked version, see [`get_byte`](Self::get_byte).
74
+ ///
69
75
/// # Panics
70
76
///
71
77
/// Panics if the byte index is out of bounds (i.e. greater than or equal
@@ -85,14 +91,9 @@ impl Rope {
85
91
#[ track_caller]
86
92
#[ inline]
87
93
pub fn byte ( & self , byte_index : usize ) -> u8 {
88
- if byte_index >= self . byte_len ( ) {
89
- panic:: byte_index_out_of_bounds ( byte_index, self . byte_len ( ) ) ;
90
- }
91
-
92
- let ( chunk, ByteMetric ( chunk_byte_offset) ) =
93
- self . tree . leaf_at_measure ( ByteMetric ( byte_index + 1 ) ) ;
94
-
95
- chunk. byte ( byte_index - chunk_byte_offset)
94
+ self . get_byte ( byte_index) . unwrap_or_else ( || {
95
+ panic:: byte_index_out_of_bounds ( byte_index, self . line_len ( ) )
96
+ } )
96
97
}
97
98
98
99
/// Returns the length of the `Rope` in bytes.
@@ -299,8 +300,120 @@ impl Rope {
299
300
self . replace ( byte_range, "" ) ;
300
301
}
301
302
302
- pub ( super ) const fn arity ( ) -> usize {
303
- ARITY
303
+ /// Returns the byte at `byte_index`, if it exists.
304
+ ///
305
+ /// If `byte_index` is out of bounds, returns `None`.
306
+ ///
307
+ /// For a panic'ing version, see [`byte`](Self::byte).
308
+ ///
309
+ /// # Examples
310
+ ///
311
+ /// ```
312
+ /// # use crop::Rope;
313
+ /// #
314
+ /// let r = Rope::from("bar");
315
+ ///
316
+ /// assert_eq!(r.get_byte(0), Some(b'b'));
317
+ /// assert_eq!(r.get_byte(1), Some(b'a'));
318
+ /// assert_eq!(r.get_byte(2), Some(b'r'));
319
+ /// assert_eq!(r.get_byte(3), None);
320
+ /// ```
321
+ #[ track_caller]
322
+ #[ inline]
323
+ pub fn get_byte ( & self , byte_index : usize ) -> Option < u8 > {
324
+ if byte_index >= self . byte_len ( ) {
325
+ return None ;
326
+ }
327
+
328
+ let ( chunk, ByteMetric ( chunk_byte_offset) ) =
329
+ self . tree . leaf_at_measure ( ByteMetric ( byte_index + 1 ) ) ;
330
+
331
+ Some ( chunk. byte ( byte_index - chunk_byte_offset) )
332
+ }
333
+
334
+ /// Returns the line at `line_index`, without its line terminator, if it exists.
335
+ ///
336
+ /// If `line_index` is out of bounds, returns `None`.
337
+ ///
338
+ /// If you want to include the line break consider calling
339
+ /// [`get_line_slice()`](Self::get_line_slice()) in the
340
+ /// `line_index..line_index + 1` range.
341
+ ///
342
+ /// For a panic'ing version, see [`line_slice()`](Self::line_slice()).
343
+ ///
344
+ /// # Examples
345
+ ///
346
+ /// ```
347
+ /// # use crop::Rope;
348
+ /// #
349
+ /// let r = Rope::from("foo\nbar\r\nbaz");
350
+ ///
351
+ /// assert_eq!(r.get_line(0).unwrap(), "foo");
352
+ /// assert_eq!(r.get_line(1).unwrap(), "bar");
353
+ /// assert_eq!(r.get_line(2).unwrap(), "baz");
354
+ /// assert_eq!(r.get_line(3), None);
355
+ /// ```
356
+ #[ track_caller]
357
+ #[ inline]
358
+ pub fn get_line ( & self , line_index : usize ) -> Option < RopeSlice < ' _ > > {
359
+ if line_index >= self . line_len ( ) {
360
+ return None ;
361
+ }
362
+
363
+ let tree_slice = self
364
+ . tree
365
+ . slice ( RawLineMetric ( line_index) ..RawLineMetric ( line_index + 1 ) ) ;
366
+
367
+ let mut line = RopeSlice { tree_slice, has_trailing_newline : false } ;
368
+
369
+ if line. tree_slice . summary ( ) . line_breaks ( ) == 1 {
370
+ line. truncate_trailing_line_break ( ) ;
371
+ }
372
+
373
+ Some ( line)
374
+ }
375
+
376
+ /// Returns an immutable slice of the `Rope` in the specified line range,
377
+ /// if it exists, where the start and end of the range are interpreted as
378
+ /// offsets.
379
+ ///
380
+ /// If `line_range` is out of bounds, returns `None`.
381
+ ///
382
+ /// If you want a single line, see [`get_line()`](Self::get_line).
383
+ ///
384
+ /// For a panic'ing version, see [`line_slice`](Self::line_slice).
385
+ ///
386
+ /// # Examples
387
+ ///
388
+ /// ```
389
+ /// # use crop::Rope;
390
+ /// #
391
+ /// let r = Rope::from("foo\nbar\r\nbaz\nfoobar\n");
392
+ ///
393
+ /// assert_eq!(r.get_line_slice(..1).unwrap(), "foo\n");
394
+ /// assert_eq!(r.get_line_slice(1..3).unwrap(), "bar\r\nbaz\n");
395
+ /// assert_eq!(r.get_line_slice(3..).unwrap(), "foobar\n");
396
+ /// assert_eq!(r.get_line_slice(4..).unwrap(), "");
397
+ /// assert_eq!(r.get_line_slice(5..), None);
398
+ /// ```
399
+ #[ track_caller]
400
+ #[ inline]
401
+ pub fn get_line_slice < R > ( & self , line_range : R ) -> Option < RopeSlice < ' _ > >
402
+ where
403
+ R : RangeBounds < usize > ,
404
+ {
405
+ let ( start, end) =
406
+ range_bounds_to_start_end ( line_range, 0 , self . line_len ( ) ) ;
407
+
408
+ if start > end {
409
+ return None ;
410
+ }
411
+
412
+ if end > self . line_len ( ) {
413
+ return None ;
414
+ }
415
+
416
+ Some ( self . tree . slice ( RawLineMetric ( start) ..RawLineMetric ( end) ) . into ( ) )
304
417
}
305
418
306
419
/// Returns an iterator over the extended grapheme clusters of this
@@ -446,6 +559,9 @@ impl Rope {
446
559
/// [`line_slice()`](Self::line_slice()) in the
447
560
/// `line_index..line_index + 1` range.
448
561
///
562
+ /// For a checked version, see
563
+ /// [`get_line_slice()`](Self::get_line_slice()).
564
+ ///
449
565
/// # Panics
450
566
///
451
567
/// Panics if the line index is out of bounds (i.e. greater than or equal
@@ -465,21 +581,9 @@ impl Rope {
465
581
#[ track_caller]
466
582
#[ inline]
467
583
pub fn line ( & self , line_index : usize ) -> RopeSlice < ' _ > {
468
- if line_index >= self . line_len ( ) {
469
- panic:: line_index_out_of_bounds ( line_index, self . line_len ( ) ) ;
470
- }
471
-
472
- let tree_slice = self
473
- . tree
474
- . slice ( RawLineMetric ( line_index) ..RawLineMetric ( line_index + 1 ) ) ;
475
-
476
- let mut line = RopeSlice { tree_slice, has_trailing_newline : false } ;
477
-
478
- if line. tree_slice . summary ( ) . line_breaks ( ) == 1 {
479
- line. truncate_trailing_line_break ( ) ;
480
- }
481
-
482
- line
584
+ self . get_line ( line_index) . unwrap_or_else ( || {
585
+ panic:: line_index_out_of_bounds ( line_index, self . line_len ( ) )
586
+ } )
483
587
}
484
588
485
589
/// Returns the number of lines in the `Rope`.
@@ -551,6 +655,10 @@ impl Rope {
551
655
/// Returns an immutable slice of the `Rope` in the specified line range,
552
656
/// where the start and end of the range are interpreted as offsets.
553
657
///
658
+ /// If you want a single line, see [`line()`](Self::line).
659
+ ///
660
+ /// If you want a checked version, see [`get_line_slice()`](Self::get_line_slice).
661
+ ///
554
662
/// # Panics
555
663
///
556
664
/// Panics if the start is greater than the end or if the end is out of
@@ -566,6 +674,7 @@ impl Rope {
566
674
/// assert_eq!(r.line_slice(..1), "foo\n");
567
675
/// assert_eq!(r.line_slice(1..3), "bar\r\nbaz\n");
568
676
/// assert_eq!(r.line_slice(3..), "foobar\n");
677
+ /// assert_eq!(r.line_slice(4..), "");
569
678
/// ```
570
679
#[ track_caller]
571
680
#[ inline]
0 commit comments