@@ -198,14 +198,19 @@ impl<'a> TextArea<'a> {
198198 CursorMove :: Forward => {
199199 self . cursor = (
200200 current_row,
201- ( current_column + 1 )
202- . min ( self . lines [ current_row] . len ( ) ) ,
201+ ( current_column + 1 ) . min (
202+ self . lines [ current_row]
203+ . char_indices ( )
204+ . count ( ) ,
205+ ) ,
203206 ) ;
204207 }
205208 CursorMove :: Home => self . cursor = ( current_row, 0 ) ,
206209 CursorMove :: End => {
207- self . cursor =
208- ( current_row, self . lines [ current_row] . len ( ) ) ;
210+ self . cursor = (
211+ current_row,
212+ self . lines [ current_row] . char_indices ( ) . count ( ) ,
213+ ) ;
209214 }
210215 }
211216 }
@@ -227,22 +232,43 @@ impl<'a> TextArea<'a> {
227232 }
228233
229234 fn delete_next_char ( & mut self ) {
230- todo ! ( ) ;
235+ let ( current_row, current_column) = self . cursor ;
236+ let current_line = & mut self . lines [ current_row] ;
237+
238+ if current_column < current_line. len ( ) {
239+ if let Some ( ( offset, _) ) =
240+ current_line. char_indices ( ) . nth ( current_column)
241+ {
242+ current_line. remove ( offset) ;
243+ }
244+ } else if current_row < self . lines . len ( ) . saturating_sub ( 1 ) {
245+ let next_line = self . lines [ current_row + 1 ] . clone ( ) ;
246+ self . lines [ current_row] . push_str ( & next_line) ;
247+ self . lines . remove ( current_row + 1 ) ;
248+ } else {
249+ // We're at the end of the input. Do nothing.
250+ }
231251 }
232252
233253 fn delete_char ( & mut self ) {
234254 let ( current_row, current_column) = self . cursor ;
255+ let current_line = & mut self . lines [ current_row] ;
235256
236257 if current_column > 0 {
237- self . lines [ current_row]
238- . remove ( current_column. saturating_sub ( 1 ) ) ;
239- self . cursor = ( current_row, current_column - 1 ) ;
258+ if let Some ( ( offset, _) ) =
259+ current_line. char_indices ( ) . nth ( current_column - 1 )
260+ {
261+ current_line. remove ( offset) ;
262+ self . cursor = ( current_row, current_column - 1 ) ;
263+ }
240264 } else if current_row > 0 {
241265 let current_line = self . lines [ current_row] . clone ( ) ;
242266 self . lines [ current_row - 1 ] . push_str ( & current_line) ;
243267 self . lines . remove ( current_row) ;
244- self . cursor =
245- ( current_row - 1 , self . lines [ current_row - 1 ] . len ( ) ) ;
268+ self . cursor = (
269+ current_row - 1 ,
270+ self . lines [ current_row - 1 ] . char_indices ( ) . count ( ) ,
271+ ) ;
246272 } else {
247273 // We're at (0, 0), there's no characters to be deleted. Do nothing.
248274 }
@@ -254,8 +280,14 @@ impl<'a> TextArea<'a> {
254280
255281 fn insert_char ( & mut self , char : char ) {
256282 let ( current_row, current_column) = self . cursor ;
283+ let current_line = & mut self . lines [ current_row] ;
284+
285+ let offset = current_line
286+ . char_indices ( )
287+ . nth ( current_column)
288+ . map_or_else ( || current_line. len ( ) , |( i, _) | i) ;
257289
258- self . lines [ current_row ] . insert ( current_column , char) ;
290+ current_line . insert ( offset , char) ;
259291 self . cursor = ( current_row, current_column + 1 ) ;
260292 }
261293
@@ -321,26 +353,43 @@ impl<'a> TextAreaComponent {
321353 . skip ( self . scroll . get_top ( ) )
322354 . map ( |( row, line) | {
323355 if row == current_row {
324- if current_column == line. len ( ) {
325- Line :: from ( vec ! [
356+ if current_column == line. char_indices ( ) . count ( ) {
357+ return Line :: from ( vec ! [
326358 Span :: from( line. clone( ) ) ,
327359 Span :: styled( " " , self . cursor_style) ,
328- ] )
329- } else {
360+ ] ) ;
361+ }
362+
363+ if let Some ( ( offset, _) ) =
364+ line. char_indices ( ) . nth ( current_column)
365+ {
330366 let ( before_cursor, cursor) =
331- line. split_at ( current_column) ;
332- let ( cursor, after_cursor) =
333- cursor. split_at ( 1 ) ;
367+ line. split_at ( offset) ;
368+
369+ if let Some ( ( next_offset, _) ) =
370+ cursor. char_indices ( ) . nth ( 1 )
371+ {
372+ let ( cursor, after_cursor) =
373+ cursor. split_at ( next_offset) ;
374+
375+ return Line :: from ( vec ! [
376+ Span :: from( before_cursor) ,
377+ Span :: styled(
378+ cursor,
379+ self . cursor_style,
380+ ) ,
381+ Span :: from( after_cursor) ,
382+ ] ) ;
383+ }
334384
335- Line :: from ( vec ! [
385+ return Line :: from ( vec ! [
336386 Span :: from( before_cursor) ,
337387 Span :: styled( cursor, self . cursor_style) ,
338- Span :: from( after_cursor) ,
339- ] )
388+ ] ) ;
340389 }
341- } else {
342- Line :: from ( line. clone ( ) )
343390 }
391+
392+ Line :: from ( line. clone ( ) )
344393 } )
345394 . collect ( ) ;
346395 let paragraph = Paragraph :: new ( Text :: from ( lines) ) ;
@@ -1134,4 +1183,89 @@ mod tests {
11341183 assert_eq ! ( ta. cursor( ) , ( 1 , 5 ) ) ;
11351184 }
11361185 }
1186+
1187+ #[ test]
1188+ fn test_delete_char_unicode ( ) {
1189+ let env = Environment :: test_env ( ) ;
1190+ let mut comp = TextInputComponent :: new ( & env, "" , "" , false ) ;
1191+ comp. show_inner_textarea ( ) ;
1192+ comp. set_text ( String :: from ( "äÜö" ) ) ;
1193+ assert ! ( comp. is_visible( ) ) ;
1194+
1195+ if let Some ( ta) = & mut comp. textarea {
1196+ ta. move_cursor ( CursorMove :: End ) ;
1197+ assert_eq ! ( ta. cursor( ) , ( 0 , 3 ) ) ;
1198+
1199+ ta. delete_char ( ) ;
1200+ assert_eq ! ( ta. lines( ) , & [ "äÜ" ] ) ;
1201+ assert_eq ! ( ta. cursor( ) , ( 0 , 2 ) ) ;
1202+ }
1203+ }
1204+
1205+ #[ test]
1206+ fn test_delete_next_char ( ) {
1207+ let env = Environment :: test_env ( ) ;
1208+ let mut comp = TextInputComponent :: new ( & env, "" , "" , false ) ;
1209+ comp. show_inner_textarea ( ) ;
1210+ comp. set_text ( String :: from ( "aa\n def sa\n gitui" ) ) ;
1211+ assert ! ( comp. is_visible( ) ) ;
1212+
1213+ if let Some ( ta) = & mut comp. textarea {
1214+ assert_eq ! ( ta. cursor( ) , ( 0 , 0 ) ) ;
1215+
1216+ ta. delete_next_char ( ) ;
1217+ assert_eq ! ( ta. lines( ) , & [ "a" , "def sa" , "gitui" ] ) ;
1218+ assert_eq ! ( ta. cursor( ) , ( 0 , 0 ) ) ;
1219+
1220+ ta. delete_next_char ( ) ;
1221+ assert_eq ! ( ta. lines( ) , & [ "" , "def sa" , "gitui" ] ) ;
1222+ assert_eq ! ( ta. cursor( ) , ( 0 , 0 ) ) ;
1223+
1224+ ta. delete_next_char ( ) ;
1225+ assert_eq ! ( ta. lines( ) , & [ "def sa" , "gitui" ] ) ;
1226+ assert_eq ! ( ta. cursor( ) , ( 0 , 0 ) ) ;
1227+
1228+ ta. move_cursor ( CursorMove :: Down ) ;
1229+ assert_eq ! ( ta. cursor( ) , ( 1 , 0 ) ) ;
1230+
1231+ ta. delete_next_char ( ) ;
1232+ assert_eq ! ( ta. lines( ) , & [ "def sa" , "itui" ] ) ;
1233+ assert_eq ! ( ta. cursor( ) , ( 1 , 0 ) ) ;
1234+ }
1235+ }
1236+
1237+ #[ test]
1238+ fn test_delete_next_char_empty ( ) {
1239+ let env = Environment :: test_env ( ) ;
1240+ let mut comp = TextInputComponent :: new ( & env, "" , "" , false ) ;
1241+ comp. show_inner_textarea ( ) ;
1242+ comp. set_text ( "" . into ( ) ) ;
1243+ assert ! ( comp. is_visible( ) ) ;
1244+
1245+ if let Some ( ta) = & mut comp. textarea {
1246+ assert_eq ! ( ta. cursor( ) , ( 0 , 0 ) ) ;
1247+
1248+ ta. delete_next_char ( ) ;
1249+ assert_eq ! ( ta. lines( ) , & [ "" ] ) ;
1250+ assert_eq ! ( ta. cursor( ) , ( 0 , 0 ) ) ;
1251+ }
1252+ }
1253+
1254+ #[ test]
1255+ fn test_delete_next_char_unicode ( ) {
1256+ let env = Environment :: test_env ( ) ;
1257+ let mut comp = TextInputComponent :: new ( & env, "" , "" , false ) ;
1258+ comp. show_inner_textarea ( ) ;
1259+ comp. set_text ( "üäu" . into ( ) ) ;
1260+ assert ! ( comp. is_visible( ) ) ;
1261+
1262+ if let Some ( ta) = & mut comp. textarea {
1263+ ta. move_cursor ( CursorMove :: Forward ) ;
1264+ assert_eq ! ( ta. cursor( ) , ( 0 , 1 ) ) ;
1265+
1266+ ta. delete_next_char ( ) ;
1267+ assert_eq ! ( ta. lines( ) , & [ "üu" ] ) ;
1268+ assert_eq ! ( ta. cursor( ) , ( 0 , 1 ) ) ;
1269+ }
1270+ }
11371271}
0 commit comments