@@ -325,20 +325,41 @@ def do(self) -> None:
325325 b = r .buffer
326326 for _ in range (r .get_arg ()):
327327 p = r .pos + 1
328- if p <= len (b ):
329- r .pos = p
328+ # In vi normal mode, don't move past the last character
329+ if r .editor_mode .is_normal ():
330+ eol_pos = r .eol ()
331+ max_pos = max (r .bol (), eol_pos - 1 ) if eol_pos > r .bol () else r .bol ()
332+ if p <= max_pos :
333+ r .pos = p
334+ else :
335+ self .reader .error ("end of line" )
330336 else :
331- self .reader .error ("end of buffer" )
337+ if p <= len (b ):
338+ r .pos = p
339+ else :
340+ self .reader .error ("end of buffer" )
332341
333342
334343class beginning_of_line (MotionCommand ):
335344 def do (self ) -> None :
336345 self .reader .pos = self .reader .bol ()
337346
338347
348+ class first_non_whitespace_character (MotionCommand ):
349+ def do (self ) -> None :
350+ self .reader .pos = self .reader .first_non_whitespace ()
351+
352+
339353class end_of_line (MotionCommand ):
340354 def do (self ) -> None :
341- self .reader .pos = self .reader .eol ()
355+ r = self .reader
356+ eol_pos = r .eol ()
357+ if r .editor_mode .is_normal ():
358+ bol_pos = r .bol ()
359+ # Don't go past the last character (but stay at bol if line is empty)
360+ r .pos = max (bol_pos , eol_pos - 1 ) if eol_pos > bol_pos else bol_pos
361+ else :
362+ r .pos = eol_pos
342363
343364
344365class home (MotionCommand ):
@@ -365,6 +386,20 @@ def do(self) -> None:
365386 r .pos = r .bow ()
366387
367388
389+ class end_of_word (MotionCommand ):
390+ def do (self ) -> None :
391+ r = self .reader
392+ for _ in range (r .get_arg ()):
393+ r .pos = r .vi_eow ()
394+
395+
396+ class vi_forward_word (MotionCommand ):
397+ def do (self ) -> None :
398+ r = self .reader
399+ for _ in range (r .get_arg ()):
400+ r .pos = r .vi_forward_word ()
401+
402+
368403class self_insert (EditCommand ):
369404 def do (self ) -> None :
370405 r = self .reader
@@ -503,3 +538,55 @@ def do(self) -> None:
503538 )
504539 self .reader .insert (data .replace (done , "" ))
505540 self .reader .last_refresh_cache .invalidated = True
541+
542+
543+ class vi_normal_mode (Command ):
544+ def do (self ) -> None :
545+ self .reader .enter_normal_mode ()
546+
547+
548+ class vi_insert_mode (Command ):
549+ def do (self ) -> None :
550+ self .reader .enter_insert_mode ()
551+
552+
553+ class vi_append_mode (Command ):
554+ def do (self ) -> None :
555+ if self .reader .pos < len (self .reader .buffer ):
556+ self .reader .pos += 1
557+ self .reader .enter_insert_mode ()
558+
559+
560+ class vi_append_eol (Command ):
561+ def do (self ) -> None :
562+ while self .reader .pos < len (self .reader .buffer ):
563+ if self .reader .buffer [self .reader .pos ] == '\n ' :
564+ break
565+ self .reader .pos += 1
566+ self .reader .enter_insert_mode ()
567+
568+
569+ class vi_insert_bol (Command ):
570+ def do (self ) -> None :
571+ self .reader .pos = self .reader .first_non_whitespace ()
572+ self .reader .enter_insert_mode ()
573+
574+
575+ class vi_open_below (Command ):
576+ def do (self ) -> None :
577+ while self .reader .pos < len (self .reader .buffer ):
578+ if self .reader .buffer [self .reader .pos ] == '\n ' :
579+ break
580+ self .reader .pos += 1
581+
582+ self .reader .insert ('\n ' )
583+ self .reader .enter_insert_mode ()
584+
585+ class vi_open_above (Command ):
586+ def do (self ) -> None :
587+ while self .reader .pos > 0 and self .reader .buffer [self .reader .pos - 1 ] != '\n ' :
588+ self .reader .pos -= 1
589+
590+ self .reader .insert ('\n ' )
591+ self .reader .pos -= 1
592+ self .reader .enter_insert_mode ()
0 commit comments