@@ -120,13 +120,57 @@ impl Editor {
120120 EditCommand :: CutSelection => self . cut_selection_to_cut_buffer ( ) ,
121121 EditCommand :: CopySelection => self . copy_selection_to_cut_buffer ( ) ,
122122 EditCommand :: Paste => self . paste_cut_buffer ( ) ,
123+ EditCommand :: CopyFromStart => self . copy_from_start ( ) ,
124+ EditCommand :: CopyFromLineStart => self . copy_from_line_start ( ) ,
125+ EditCommand :: CopyToEnd => self . copy_from_end ( ) ,
126+ EditCommand :: CopyToLineEnd => self . copy_to_line_end ( ) ,
127+ EditCommand :: CopyWordLeft => self . copy_word_left ( ) ,
128+ EditCommand :: CopyBigWordLeft => self . copy_big_word_left ( ) ,
129+ EditCommand :: CopyWordRight => self . copy_word_right ( ) ,
130+ EditCommand :: CopyBigWordRight => self . copy_big_word_right ( ) ,
131+ EditCommand :: CopyWordRightToNext => self . copy_word_right_to_next ( ) ,
132+ EditCommand :: CopyBigWordRightToNext => self . copy_big_word_right_to_next ( ) ,
133+ EditCommand :: CopyRightUntil ( c) => self . copy_right_until_char ( * c, false , true ) ,
134+ EditCommand :: CopyRightBefore ( c) => self . copy_right_until_char ( * c, true , true ) ,
135+ EditCommand :: CopyLeftUntil ( c) => self . copy_left_until_char ( * c, false , true ) ,
136+ EditCommand :: CopyLeftBefore ( c) => self . copy_left_until_char ( * c, true , true ) ,
137+ EditCommand :: CopyCurrentLine => {
138+ let range = self . line_buffer . current_line_range ( ) ;
139+ let copy_slice = & self . line_buffer . get_buffer ( ) [ range] ;
140+ if !copy_slice. is_empty ( ) {
141+ self . cut_buffer . set ( copy_slice, ClipboardMode :: Lines ) ;
142+ }
143+ }
144+ EditCommand :: CopyLeft => {
145+ let insertion_offset = self . line_buffer . insertion_point ( ) ;
146+ if insertion_offset > 0 {
147+ let left_index = self . line_buffer . grapheme_left_index ( ) ;
148+ let copy_range = left_index..insertion_offset;
149+ self . cut_buffer . set (
150+ & self . line_buffer . get_buffer ( ) [ copy_range] ,
151+ ClipboardMode :: Normal ,
152+ ) ;
153+ }
154+ }
155+ EditCommand :: CopyRight => {
156+ let insertion_offset = self . line_buffer . insertion_point ( ) ;
157+ let right_index = self . line_buffer . grapheme_right_index ( ) ;
158+ if right_index > insertion_offset {
159+ let copy_range = insertion_offset..right_index;
160+ self . cut_buffer . set (
161+ & self . line_buffer . get_buffer ( ) [ copy_range] ,
162+ ClipboardMode :: Normal ,
163+ ) ;
164+ }
165+ }
123166 #[ cfg( feature = "system_clipboard" ) ]
124167 EditCommand :: CutSelectionSystem => self . cut_selection_to_system ( ) ,
125168 #[ cfg( feature = "system_clipboard" ) ]
126169 EditCommand :: CopySelectionSystem => self . copy_selection_to_system ( ) ,
127170 #[ cfg( feature = "system_clipboard" ) ]
128171 EditCommand :: PasteSystem => self . paste_from_system ( ) ,
129172 EditCommand :: CutInside { left, right } => self . cut_inside ( * left, * right) ,
173+ EditCommand :: YankInside { left, right } => self . yank_inside ( * left, * right) ,
130174 }
131175 if !matches ! ( command. edit_type( ) , EditType :: MoveCursor { select: true } ) {
132176 self . selection_anchor = None ;
@@ -687,6 +731,165 @@ impl Editor {
687731 }
688732 }
689733 }
734+
735+ pub ( crate ) fn copy_from_start ( & mut self ) {
736+ let insertion_offset = self . line_buffer . insertion_point ( ) ;
737+ if insertion_offset > 0 {
738+ self . cut_buffer . set (
739+ & self . line_buffer . get_buffer ( ) [ ..insertion_offset] ,
740+ ClipboardMode :: Normal ,
741+ ) ;
742+ }
743+ }
744+
745+ pub ( crate ) fn copy_from_line_start ( & mut self ) {
746+ let previous_offset = self . line_buffer . insertion_point ( ) ;
747+ let start_offset = {
748+ let temp_pos = self . line_buffer . insertion_point ( ) ;
749+ self . line_buffer . move_to_line_start ( ) ;
750+ let start = self . line_buffer . insertion_point ( ) ;
751+ self . line_buffer . set_insertion_point ( temp_pos) ;
752+ start
753+ } ;
754+ let copy_range = start_offset..previous_offset;
755+ let copy_slice = & self . line_buffer . get_buffer ( ) [ copy_range] ;
756+ if !copy_slice. is_empty ( ) {
757+ self . cut_buffer . set ( copy_slice, ClipboardMode :: Normal ) ;
758+ }
759+ }
760+
761+ pub ( crate ) fn copy_from_end ( & mut self ) {
762+ let copy_slice = & self . line_buffer . get_buffer ( ) [ self . line_buffer . insertion_point ( ) ..] ;
763+ if !copy_slice. is_empty ( ) {
764+ self . cut_buffer . set ( copy_slice, ClipboardMode :: Normal ) ;
765+ }
766+ }
767+
768+ pub ( crate ) fn copy_to_line_end ( & mut self ) {
769+ let copy_slice = & self . line_buffer . get_buffer ( )
770+ [ self . line_buffer . insertion_point ( ) ..self . line_buffer . find_current_line_end ( ) ] ;
771+ if !copy_slice. is_empty ( ) {
772+ self . cut_buffer . set ( copy_slice, ClipboardMode :: Normal ) ;
773+ }
774+ }
775+
776+ pub ( crate ) fn copy_word_left ( & mut self ) {
777+ let insertion_offset = self . line_buffer . insertion_point ( ) ;
778+ let left_index = self . line_buffer . word_left_index ( ) ;
779+ if left_index < insertion_offset {
780+ let copy_range = left_index..insertion_offset;
781+ self . cut_buffer . set (
782+ & self . line_buffer . get_buffer ( ) [ copy_range] ,
783+ ClipboardMode :: Normal ,
784+ ) ;
785+ }
786+ }
787+
788+ pub ( crate ) fn copy_big_word_left ( & mut self ) {
789+ let insertion_offset = self . line_buffer . insertion_point ( ) ;
790+ let left_index = self . line_buffer . big_word_left_index ( ) ;
791+ if left_index < insertion_offset {
792+ let copy_range = left_index..insertion_offset;
793+ self . cut_buffer . set (
794+ & self . line_buffer . get_buffer ( ) [ copy_range] ,
795+ ClipboardMode :: Normal ,
796+ ) ;
797+ }
798+ }
799+
800+ pub ( crate ) fn copy_word_right ( & mut self ) {
801+ let insertion_offset = self . line_buffer . insertion_point ( ) ;
802+ let right_index = self . line_buffer . word_right_index ( ) ;
803+ if right_index > insertion_offset {
804+ let copy_range = insertion_offset..right_index;
805+ self . cut_buffer . set (
806+ & self . line_buffer . get_buffer ( ) [ copy_range] ,
807+ ClipboardMode :: Normal ,
808+ ) ;
809+ }
810+ }
811+
812+ pub ( crate ) fn copy_big_word_right ( & mut self ) {
813+ let insertion_offset = self . line_buffer . insertion_point ( ) ;
814+ let right_index = self . line_buffer . next_whitespace ( ) ;
815+ if right_index > insertion_offset {
816+ let copy_range = insertion_offset..right_index;
817+ self . cut_buffer . set (
818+ & self . line_buffer . get_buffer ( ) [ copy_range] ,
819+ ClipboardMode :: Normal ,
820+ ) ;
821+ }
822+ }
823+
824+ pub ( crate ) fn copy_word_right_to_next ( & mut self ) {
825+ let insertion_offset = self . line_buffer . insertion_point ( ) ;
826+ let right_index = self . line_buffer . word_right_start_index ( ) ;
827+ if right_index > insertion_offset {
828+ let copy_range = insertion_offset..right_index;
829+ self . cut_buffer . set (
830+ & self . line_buffer . get_buffer ( ) [ copy_range] ,
831+ ClipboardMode :: Normal ,
832+ ) ;
833+ }
834+ }
835+
836+ pub ( crate ) fn copy_big_word_right_to_next ( & mut self ) {
837+ let insertion_offset = self . line_buffer . insertion_point ( ) ;
838+ let right_index = self . line_buffer . big_word_right_start_index ( ) ;
839+ if right_index > insertion_offset {
840+ let copy_range = insertion_offset..right_index;
841+ self . cut_buffer . set (
842+ & self . line_buffer . get_buffer ( ) [ copy_range] ,
843+ ClipboardMode :: Normal ,
844+ ) ;
845+ }
846+ }
847+
848+ pub ( crate ) fn copy_right_until_char ( & mut self , c : char , before_char : bool , current_line : bool ) {
849+ if let Some ( index) = self . line_buffer . find_char_right ( c, current_line) {
850+ let extra = if before_char { 0 } else { c. len_utf8 ( ) } ;
851+ let copy_slice =
852+ & self . line_buffer . get_buffer ( ) [ self . line_buffer . insertion_point ( ) ..index + extra] ;
853+ if !copy_slice. is_empty ( ) {
854+ self . cut_buffer . set ( copy_slice, ClipboardMode :: Normal ) ;
855+ }
856+ }
857+ }
858+
859+ pub ( crate ) fn copy_left_until_char ( & mut self , c : char , before_char : bool , current_line : bool ) {
860+ if let Some ( index) = self . line_buffer . find_char_left ( c, current_line) {
861+ let extra = if before_char { c. len_utf8 ( ) } else { 0 } ;
862+ let copy_slice =
863+ & self . line_buffer . get_buffer ( ) [ index + extra..self . line_buffer . insertion_point ( ) ] ;
864+ if !copy_slice. is_empty ( ) {
865+ self . cut_buffer . set ( copy_slice, ClipboardMode :: Normal ) ;
866+ }
867+ }
868+ }
869+
870+ /// Yank text strictly between matching `left_char` and `right_char`.
871+ /// Copies it into the cut buffer without removing anything.
872+ /// Leaves the buffer unchanged and restores the original cursor.
873+ pub ( crate ) fn yank_inside ( & mut self , left_char : char , right_char : char ) {
874+ let old_pos = self . insertion_point ( ) ;
875+ let buffer_len = self . line_buffer . len ( ) ;
876+
877+ if let Some ( ( lp, rp) ) =
878+ self . line_buffer
879+ . find_matching_pair ( left_char, right_char, self . insertion_point ( ) )
880+ {
881+ let inside_start = lp + left_char. len_utf8 ( ) ;
882+ if inside_start < rp && rp <= buffer_len {
883+ let inside_slice = & self . line_buffer . get_buffer ( ) [ inside_start..rp] ;
884+ if !inside_slice. is_empty ( ) {
885+ self . cut_buffer . set ( inside_slice, ClipboardMode :: Normal ) ;
886+ }
887+ }
888+ }
889+
890+ // Always restore the cursor position
891+ self . line_buffer . set_insertion_point ( old_pos) ;
892+ }
690893}
691894
692895fn insert_clipboard_content_before ( line_buffer : & mut LineBuffer , clipboard : & mut dyn Clipboard ) {
@@ -1004,4 +1207,62 @@ mod test {
10041207 assert_eq ! ( editor. insertion_point( ) , 4 ) ;
10051208 assert_eq ! ( editor. cut_buffer. get( ) . 0 , "bar()qux" ) ;
10061209 }
1210+
1211+ #[ test]
1212+ fn test_yank_inside_brackets ( ) {
1213+ let mut editor = editor_with ( "foo(bar)baz" ) ;
1214+ editor. move_to_position ( 5 , false ) ; // Move inside brackets
1215+ editor. yank_inside ( '(' , ')' ) ;
1216+ assert_eq ! ( editor. get_buffer( ) , "foo(bar)baz" ) ; // Buffer shouldn't change
1217+ assert_eq ! ( editor. insertion_point( ) , 5 ) ; // Cursor should return to original position
1218+
1219+ // Test yanked content by pasting
1220+ editor. paste_cut_buffer ( ) ;
1221+ assert_eq ! ( editor. get_buffer( ) , "foo(bbarar)baz" ) ;
1222+
1223+ // Test with cursor outside brackets
1224+ let mut editor = editor_with ( "foo(bar)baz" ) ;
1225+ editor. move_to_position ( 0 , false ) ;
1226+ editor. yank_inside ( '(' , ')' ) ;
1227+ assert_eq ! ( editor. get_buffer( ) , "foo(bar)baz" ) ;
1228+ assert_eq ! ( editor. insertion_point( ) , 0 ) ;
1229+ }
1230+
1231+ #[ test]
1232+ fn test_yank_inside_quotes ( ) {
1233+ let mut editor = editor_with ( "foo\" bar\" baz" ) ;
1234+ editor. move_to_position ( 5 , false ) ; // Move inside quotes
1235+ editor. yank_inside ( '"' , '"' ) ;
1236+ assert_eq ! ( editor. get_buffer( ) , "foo\" bar\" baz" ) ; // Buffer shouldn't change
1237+ assert_eq ! ( editor. insertion_point( ) , 5 ) ; // Cursor should return to original position
1238+ assert_eq ! ( editor. cut_buffer. get( ) . 0 , "bar" ) ;
1239+
1240+ // Test with no matching quotes
1241+ let mut editor = editor_with ( "foo bar baz" ) ;
1242+ editor. move_to_position ( 4 , false ) ;
1243+ editor. yank_inside ( '"' , '"' ) ;
1244+ assert_eq ! ( editor. get_buffer( ) , "foo bar baz" ) ;
1245+ assert_eq ! ( editor. insertion_point( ) , 4 ) ;
1246+ assert_eq ! ( editor. cut_buffer. get( ) . 0 , "" ) ;
1247+ }
1248+
1249+ #[ test]
1250+ fn test_yank_inside_nested ( ) {
1251+ let mut editor = editor_with ( "foo(bar(baz)qux)quux" ) ;
1252+ editor. move_to_position ( 8 , false ) ; // Move inside inner brackets
1253+ editor. yank_inside ( '(' , ')' ) ;
1254+ assert_eq ! ( editor. get_buffer( ) , "foo(bar(baz)qux)quux" ) ; // Buffer shouldn't change
1255+ assert_eq ! ( editor. insertion_point( ) , 8 ) ;
1256+ assert_eq ! ( editor. cut_buffer. get( ) . 0 , "baz" ) ;
1257+
1258+ // Test yanked content by pasting
1259+ editor. paste_cut_buffer ( ) ;
1260+ assert_eq ! ( editor. get_buffer( ) , "foo(bar(bazbaz)qux)quux" ) ;
1261+
1262+ editor. move_to_position ( 4 , false ) ; // Move inside outer brackets
1263+ editor. yank_inside ( '(' , ')' ) ;
1264+ assert_eq ! ( editor. get_buffer( ) , "foo(bar(bazbaz)qux)quux" ) ;
1265+ assert_eq ! ( editor. insertion_point( ) , 4 ) ;
1266+ assert_eq ! ( editor. cut_buffer. get( ) . 0 , "bar(bazbaz)qux" ) ;
1267+ }
10071268}
0 commit comments