@@ -155,6 +155,8 @@ const kSavePreviousState = Symbol('_savePreviousState');
155155const kRestorePreviousState = Symbol ( '_restorePreviousState' ) ;
156156const kPreviousLine = Symbol ( '_previousLine' ) ;
157157const kPreviousCursor = Symbol ( '_previousCursor' ) ;
158+ const kPreviousCursorCols = Symbol ( '_previousCursorCols' ) ;
159+ const kResetPreviousCursorCols = Symbol ( '_resetPreviousCursorCols' ) ;
158160const kPreviousPrevRows = Symbol ( '_previousPrevRows' ) ;
159161const kAddNewLineOnTTY = Symbol ( '_addNewLineOnTTY' ) ;
160162
@@ -245,6 +247,7 @@ function InterfaceConstructor(input, output, completer, terminal) {
245247 this [ kRedoStack ] = [ ] ;
246248 this . history = history ;
247249 this . historySize = historySize ;
250+ this [ kResetPreviousCursorCols ] ( ) ;
248251
249252 // The kill ring is a global list of blocks of text that were previously
250253 // killed (deleted). If its size exceeds kMaxLengthOfKillRing, the oldest
@@ -1114,6 +1117,10 @@ class Interface extends InterfaceConstructor {
11141117 this [ kRefreshLine ] ( ) ;
11151118 }
11161119
1120+ [ kResetPreviousCursorCols ] ( ) {
1121+ this [ kPreviousCursorCols ] = - 1 ;
1122+ }
1123+
11171124 [ kMoveDownOrHistoryNext ] ( ) {
11181125 const { cols, rows } = this . getCursorPos ( ) ;
11191126 const splitLine = StringPrototypeSplit ( this . line , '\n' ) ;
@@ -1125,16 +1132,34 @@ class Interface extends InterfaceConstructor {
11251132 if ( this [ kIsMultiline ] && rows < splitLine . length - 1 ) {
11261133 const currentLine = splitLine [ rows ] ;
11271134 const nextLine = splitLine [ rows + 1 ] ;
1135+ let amountToMove = 0 ;
1136+ const amountToClamp = currentLine . length - cols + // Go to the end of the current line
1137+ kMultilinePrompt . description . length + // Add the prompt length
1138+ nextLine . length + 1 ; // Add the length of the next line + 1 to go to the end of it
11281139 // If I am moving down and the current line is longer than the next line
1129- const amountToMove = ( cols > nextLine . length + 1 ) ?
1130- currentLine . length - cols + nextLine . length +
1131- kMultilinePrompt . description . length + 1 : // Move to the end of the current line
1132- // + chars to account for the kMultilinePrompt prefix, + 1 to go to the first char
1133- currentLine . length + 1 ; // Otherwise just move to the next line, in the same position
1140+ const shouldClampColumn = ( cols > nextLine . length + 1 ) ;
1141+ if ( shouldClampColumn ) {
1142+ if ( this [ kPreviousCursorCols ] === - 1 ) {
1143+ this [ kPreviousCursorCols ] = cols ; // Save the cursor cols position to restore it later
1144+ }
1145+ amountToMove = amountToClamp ;
1146+ } else {
1147+ amountToMove = currentLine . length + 1 ;
1148+ if ( this [ kPreviousCursorCols ] !== - 1 ) {
1149+ if ( this [ kPreviousCursorCols ] <= nextLine . length ) {
1150+ // If, while moving down, the cursor passed through a line shorter than where the cursor was
1151+ // I need to move it back to the original position
1152+ amountToMove += this [ kPreviousCursorCols ] - cols ;
1153+ this [ kResetPreviousCursorCols ] ( ) ; // Reset it to avoid moving again
1154+ } else {
1155+ amountToMove = amountToClamp ; // Go to the end of next line
1156+ }
1157+ }
1158+ }
11341159 this [ kMoveCursor ] ( amountToMove ) ;
11351160 return ;
11361161 }
1137-
1162+ this [ kResetPreviousCursorCols ] ( ) ; // Reset it to avoid moving again
11381163 this [ kHistoryNext ] ( ) ;
11391164 }
11401165
@@ -1178,14 +1203,38 @@ class Interface extends InterfaceConstructor {
11781203 if ( this [ kIsMultiline ] && rows > 0 ) {
11791204 const splitLine = StringPrototypeSplit ( this . line , '\n' ) ;
11801205 const previousLine = splitLine [ rows - 1 ] ;
1206+ let amountToMove = 0 ;
1207+ // Move to the beginning of the current line + 1 char to go to the end of the previous line
1208+ const amountToClamp = - cols + 1 ;
1209+
11811210 // If I am moving up and the current line is longer than the previous line
1182- const amountToMove = ( cols > previousLine . length + 1 ) ?
1183- - cols + 1 : // Move to the beginning of the current line + 1 char to go to the end of the previous line
1184- - previousLine . length - 1 ; // Otherwise just move to the previous line, in the same position
1211+ const shouldClampColumn = ( cols > previousLine . length + 1 ) ;
1212+ if ( shouldClampColumn ) {
1213+ if ( this [ kPreviousCursorCols ] === - 1 ) {
1214+ this [ kPreviousCursorCols ] = cols ; // Save the cursor cols position to restore it later
1215+ }
1216+ amountToMove = amountToClamp ;
1217+ } else {
1218+ // When pressing up a couple of times passing through shorter lines.
1219+ // Move to the previous line, in the same position
1220+ amountToMove = - previousLine . length - 1 ;
1221+ if ( this [ kPreviousCursorCols ] !== - 1 ) {
1222+ if ( this [ kPreviousCursorCols ] <= previousLine . length ) {
1223+ // If, while moving up, the cursor passed through a line shorter than where the cursor was
1224+ // I need to move it back to the original position
1225+ amountToMove += this [ kPreviousCursorCols ] - cols ;
1226+ this [ kResetPreviousCursorCols ] ( ) ; // Reset it to avoid moving again
1227+ } else {
1228+ amountToMove = amountToClamp ; // Go to the end of the previous line
1229+ }
1230+ }
1231+ }
1232+
11851233 this [ kMoveCursor ] ( amountToMove ) ;
11861234 return ;
11871235 }
11881236
1237+ this [ kResetPreviousCursorCols ] ( ) ; // Reset it to avoid moving again
11891238 this [ kHistoryPrev ] ( ) ;
11901239 }
11911240
@@ -1296,6 +1345,7 @@ class Interface extends InterfaceConstructor {
12961345 const previousKey = this [ kPreviousKey ] ;
12971346 key ||= kEmptyObject ;
12981347 this [ kPreviousKey ] = key ;
1348+ let shouldResetPreviousCursorCols = true ;
12991349
13001350 if ( ! key . meta || key . name !== 'y' ) {
13011351 // Reset yanking state unless we are doing yank pop.
@@ -1543,10 +1593,12 @@ class Interface extends InterfaceConstructor {
15431593 break ;
15441594
15451595 case 'up' :
1596+ shouldResetPreviousCursorCols = false ;
15461597 this [ kMoveUpOrHistoryPrev ] ( ) ;
15471598 break ;
15481599
15491600 case 'down' :
1601+ shouldResetPreviousCursorCols = false ;
15501602 this [ kMoveDownOrHistoryNext ] ( ) ;
15511603 break ;
15521604
@@ -1582,6 +1634,9 @@ class Interface extends InterfaceConstructor {
15821634 }
15831635 }
15841636 }
1637+ if ( shouldResetPreviousCursorCols ) {
1638+ this [ kResetPreviousCursorCols ] ( ) ;
1639+ }
15851640 }
15861641
15871642 /**
0 commit comments