@@ -157,6 +157,7 @@ def call(*args)
157157 STD_OUTPUT_HANDLE = -11
158158 FILE_TYPE_PIPE = 0x0003
159159 FILE_NAME_INFO = 2
160+ ENABLE_WRAP_AT_EOL_OUTPUT = 2
160161 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
161162
162163 # Calling Win32API with console handle is reported to fail after executing some external command.
@@ -170,7 +171,7 @@ def call(*args)
170171 end
171172
172173 private def getconsolemode
173- mode = " \000 \000 \000 \000 "
174+ mode = + " \0 \0 \0 \0 "
174175 call_with_console_handle ( @GetConsoleMode , mode )
175176 mode . unpack1 ( 'L' )
176177 end
@@ -344,94 +345,78 @@ def get_console_screen_buffer_info
344345 # [18,2] dwMaximumWindowSize.X
345346 # [20,2] dwMaximumWindowSize.Y
346347 csbi = 0 . chr * 22
347- return if call_with_console_handle ( @GetConsoleScreenBufferInfo , csbi ) == 0
348- csbi
348+ if call_with_console_handle ( @GetConsoleScreenBufferInfo , csbi ) != 0
349+ # returns [width, height, x, y, attributes, left, top, right, bottom]
350+ csbi . unpack ( "s9" )
351+ else
352+ return nil
353+ end
349354 end
350355
356+ ALTERNATIVE_CSBI = [ 80 , 24 , 0 , 0 , 7 , 0 , 0 , 79 , 23 ] . freeze
357+
351358 def get_screen_size
352- unless csbi = get_console_screen_buffer_info
353- return [ 1 , 1 ]
354- end
355- csbi [ 0 , 4 ] . unpack ( 'SS' ) . reverse
359+ width , _ , _ , _ , _ , _ , top , _ , bottom = get_console_screen_buffer_info || ALTERNATIVE_CSBI
360+ [ bottom - top + 1 , width ]
356361 end
357362
358363 def cursor_pos
359- unless csbi = get_console_screen_buffer_info
360- return Reline ::CursorPos . new ( 0 , 0 )
361- end
362- x = csbi [ 4 , 2 ] . unpack1 ( 's' )
363- y = csbi [ 6 , 2 ] . unpack1 ( 's' )
364- Reline ::CursorPos . new ( x , y )
364+ _ , _ , x , y , _ , _ , top , = get_console_screen_buffer_info || ALTERNATIVE_CSBI
365+ Reline ::CursorPos . new ( x , y - top )
365366 end
366367
367368 def move_cursor_column ( val )
368- call_with_console_handle ( @SetConsoleCursorPosition , cursor_pos . y * 65536 + val )
369+ _ , _ , _ , y , = get_console_screen_buffer_info
370+ call_with_console_handle ( @SetConsoleCursorPosition , y * 65536 + val ) if y
369371 end
370372
371373 def move_cursor_up ( val )
372374 if val > 0
373- y = cursor_pos . y - val
375+ _ , _ , x , y , _ , _ , top , = get_console_screen_buffer_info
376+ return unless y
377+ y = ( y - top ) - val
374378 y = 0 if y < 0
375- call_with_console_handle ( @SetConsoleCursorPosition , y * 65536 + cursor_pos . x )
379+ call_with_console_handle ( @SetConsoleCursorPosition , ( y + top ) * 65536 + x )
376380 elsif val < 0
377381 move_cursor_down ( -val )
378382 end
379383 end
380384
381385 def move_cursor_down ( val )
382386 if val > 0
383- return unless csbi = get_console_screen_buffer_info
384- screen_height = get_screen_size . first
385- y = cursor_pos . y + val
386- y = screen_height - 1 if y > ( screen_height - 1 )
387- call_with_console_handle ( @SetConsoleCursorPosition , ( cursor_pos . y + val ) * 65536 + cursor_pos . x )
387+ _ , _ , x , y , _ , _ , top , _ , bottom = get_console_screen_buffer_info
388+ return unless y
389+ screen_height = bottom - top
390+ y = ( y - top ) + val
391+ y = screen_height if y > screen_height
392+ call_with_console_handle ( @SetConsoleCursorPosition , ( y + top ) * 65536 + x )
388393 elsif val < 0
389394 move_cursor_up ( -val )
390395 end
391396 end
392397
393398 def erase_after_cursor
394- return unless csbi = get_console_screen_buffer_info
395- attributes = csbi [ 8 , 2 ] . unpack1 ( 'S' )
396- cursor = csbi [ 4 , 4 ] . unpack1 ( 'L' )
399+ width , _ , x , y , attributes , = get_console_screen_buffer_info
400+ return unless x
397401 written = 0 . chr * 4
398- call_with_console_handle ( @FillConsoleOutputCharacter , 0x20 , get_screen_size . last - cursor_pos . x , cursor , written )
399- call_with_console_handle ( @FillConsoleOutputAttribute , attributes , get_screen_size . last - cursor_pos . x , cursor , written )
400- end
401-
402- def scroll_down ( val )
403- return if val < 0
404- return unless csbi = get_console_screen_buffer_info
405- buffer_width , buffer_lines , x , y , attributes , window_left , window_top , window_bottom = csbi . unpack ( 'ssssSssx2s' )
406- screen_height = window_bottom - window_top + 1
407- val = screen_height if val > screen_height
408-
409- if @legacy_console || window_left != 0
410- # unless ENABLE_VIRTUAL_TERMINAL,
411- # if srWindow.Left != 0 then it's conhost.exe hosted console
412- # and puts "\n" causes horizontal scroll. its glitch.
413- # FYI irb write from culumn 1, so this gives no gain.
414- scroll_rectangle = [ 0 , val , buffer_width , buffer_lines - val ] . pack ( 's4' )
415- destination_origin = 0 # y * 65536 + x
416- fill = [ ' ' . ord , attributes ] . pack ( 'SS' )
417- call_with_console_handle ( @ScrollConsoleScreenBuffer , scroll_rectangle , nil , destination_origin , fill )
418- else
419- origin_x = x + 1
420- origin_y = y - window_top + 1
421- @output . write [
422- ( origin_y != screen_height ) ? "\e [#{ screen_height } ;H" : nil ,
423- "\n " * val ,
424- ( origin_y != screen_height or !x . zero? ) ? "\e [#{ origin_y } ;#{ origin_x } H" : nil
425- ] . join
426- end
402+ call_with_console_handle ( @FillConsoleOutputCharacter , 0x20 , width - x , y * 65536 + x , written )
403+ call_with_console_handle ( @FillConsoleOutputAttribute , attributes , width - x , y * 65536 + x , written )
404+ end
405+
406+ # This only works when the cursor is at the bottom of the scroll range
407+ # For more details, see https://github.com/ruby/reline/pull/577#issuecomment-1646679623
408+ def scroll_down ( x )
409+ return if x . zero?
410+ # We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576
411+ @output . write "\n " * x
427412 end
428413
429414 def clear_screen
430415 if @legacy_console
431- return unless csbi = get_console_screen_buffer_info
432- buffer_width , _buffer_lines , attributes , window_top , window_bottom = csbi . unpack ( 'ss@8S@12sx2s' )
433- fill_length = buffer_width * ( window_bottom - window_top + 1 )
434- screen_topleft = window_top * 65536
416+ width , _ , _ , _ , attributes , _ , top , _ , bottom = get_console_screen_buffer_info
417+ return unless width
418+ fill_length = width * ( bottom - top + 1 )
419+ screen_topleft = top * 65536
435420 written = 0 . chr * 4
436421 call_with_console_handle ( @FillConsoleOutputCharacter , 0x20 , fill_length , screen_topleft , written )
437422 call_with_console_handle ( @FillConsoleOutputAttribute , attributes , fill_length , screen_topleft , written )
@@ -472,6 +457,28 @@ def deprep(otio)
472457 # do nothing
473458 end
474459
460+ def disable_auto_linewrap ( setting = true , &block )
461+ mode = getconsolemode
462+ if 0 == ( mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING )
463+ if block
464+ begin
465+ setconsolemode ( mode & ~ENABLE_WRAP_AT_EOL_OUTPUT )
466+ block . call
467+ ensure
468+ setconsolemode ( mode | ENABLE_WRAP_AT_EOL_OUTPUT )
469+ end
470+ else
471+ if setting
472+ setconsolemode ( mode & ~ENABLE_WRAP_AT_EOL_OUTPUT )
473+ else
474+ setconsolemode ( mode | ENABLE_WRAP_AT_EOL_OUTPUT )
475+ end
476+ end
477+ else
478+ block . call if block
479+ end
480+ end
481+
475482 class KeyEventRecord
476483
477484 attr_reader :virtual_key_code , :char_code , :control_key_state , :control_keys
0 commit comments