@@ -120,13 +120,57 @@ impl Editor {
120
120
EditCommand :: CutSelection => self . cut_selection_to_cut_buffer ( ) ,
121
121
EditCommand :: CopySelection => self . copy_selection_to_cut_buffer ( ) ,
122
122
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
+ }
123
166
#[ cfg( feature = "system_clipboard" ) ]
124
167
EditCommand :: CutSelectionSystem => self . cut_selection_to_system ( ) ,
125
168
#[ cfg( feature = "system_clipboard" ) ]
126
169
EditCommand :: CopySelectionSystem => self . copy_selection_to_system ( ) ,
127
170
#[ cfg( feature = "system_clipboard" ) ]
128
171
EditCommand :: PasteSystem => self . paste_from_system ( ) ,
129
172
EditCommand :: CutInside { left, right } => self . cut_inside ( * left, * right) ,
173
+ EditCommand :: YankInside { left, right } => self . yank_inside ( * left, * right) ,
130
174
}
131
175
if !matches ! ( command. edit_type( ) , EditType :: MoveCursor { select: true } ) {
132
176
self . selection_anchor = None ;
@@ -687,6 +731,165 @@ impl Editor {
687
731
}
688
732
}
689
733
}
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
+ }
690
893
}
691
894
692
895
fn insert_clipboard_content_before ( line_buffer : & mut LineBuffer , clipboard : & mut dyn Clipboard ) {
@@ -1004,4 +1207,62 @@ mod test {
1004
1207
assert_eq ! ( editor. insertion_point( ) , 4 ) ;
1005
1208
assert_eq ! ( editor. cut_buffer. get( ) . 0 , "bar()qux" ) ;
1006
1209
}
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
+ }
1007
1268
}
0 commit comments