@ -13,7 +13,7 @@ use crate::{
} ;
} ;
use helix_stdx ::rope ::{ self , RopeSliceExt } ;
use helix_stdx ::rope ::{ self , RopeSliceExt } ;
use smallvec ::{ smallvec , SmallVec } ;
use smallvec ::{ smallvec , SmallVec } ;
use std ::borrow ::Cow ;
use std ::{ borrow ::Cow , iter , slice } ;
use tree_sitter ::Node ;
use tree_sitter ::Node ;
/// A single selection range.
/// A single selection range.
@ -503,6 +503,16 @@ impl Selection {
& self . ranges
& self . ranges
}
}
/// Returns an iterator over the line ranges of each range in the selection.
///
/// Adjacent and overlapping line ranges of the [Range]s in the selection are merged.
pub fn line_ranges < ' a > ( & ' a self , text : RopeSlice < ' a > ) -> LineRangeIter < ' a > {
LineRangeIter {
ranges : self . ranges . iter ( ) . peekable ( ) ,
text ,
}
}
pub fn primary_index ( & self ) -> usize {
pub fn primary_index ( & self ) -> usize {
self . primary_index
self . primary_index
}
}
@ -727,6 +737,33 @@ impl From<Range> for Selection {
}
}
}
}
pub struct LineRangeIter < ' a > {
ranges : iter ::Peekable < slice ::Iter < ' a , Range > > ,
text : RopeSlice < ' a > ,
}
impl < ' a > Iterator for LineRangeIter < ' a > {
type Item = ( usize , usize ) ;
fn next ( & mut self ) -> Option < Self ::Item > {
let ( start , mut end ) = self . ranges . next ( ) ? . line_range ( self . text ) ;
while let Some ( ( next_start , next_end ) ) =
self . ranges . peek ( ) . map ( | range | range . line_range ( self . text ) )
{
// Merge overlapping and adjacent ranges.
// This subtraction cannot underflow because the ranges are sorted.
if next_start - end < = 1 {
end = next_end ;
self . ranges . next ( ) ;
} else {
break ;
}
}
Some ( ( start , end ) )
}
}
// TODO: checkSelection -> check if valid for doc length && sorted
// TODO: checkSelection -> check if valid for doc length && sorted
pub fn keep_or_remove_matches (
pub fn keep_or_remove_matches (
@ -1165,6 +1202,32 @@ mod test {
assert_eq! ( Range ::new ( 12 , 0 ) . line_range ( s ) , ( 0 , 2 ) ) ;
assert_eq! ( Range ::new ( 12 , 0 ) . line_range ( s ) , ( 0 , 2 ) ) ;
}
}
#[ test ]
fn selection_line_ranges ( ) {
let ( text , selection ) = crate ::test ::print (
r #" L0
#[ |these ] # line #( | ranges ) # are #( | merged ) # L1
L2
single one - line #( | range ) # L3
L4
single #( | multiline L5
range ) # L6
L7
these #( | multiline L8
ranges ) # are #( | also L9
merged ) # L10
L11
adjacent #( | ranges ) # L12
are merged #( | the same way ) # L13
" #,
) ;
let rope = Rope ::from_str ( & text ) ;
assert_eq! (
vec! [ ( 1 , 1 ) , ( 3 , 3 ) , ( 5 , 6 ) , ( 8 , 10 ) , ( 12 , 13 ) ] ,
selection . line_ranges ( rope . slice ( .. ) ) . collect ::< Vec < _ > > ( ) ,
) ;
}
#[ test ]
#[ test ]
fn test_cursor ( ) {
fn test_cursor ( ) {
let r = Rope ::from_str ( "\r\nHi\r\nthere!" ) ;
let r = Rope ::from_str ( "\r\nHi\r\nthere!" ) ;