From add5e1fee216bfebdcb6669c8108547fbfbae4af Mon Sep 17 00:00:00 2001 From: tompng Date: Sat, 19 Oct 2024 02:36:26 +0900 Subject: [PATCH] Drop loading terminfo, remove fiddle dependency in non-windows environment. Reline works perfectly in most major terminal emulators without terminfo. In minor/old terminal emulator, we used to get key bindings from terminfo, but I think it is not used so much. --- lib/reline.rb | 1 - lib/reline/io/ansi.rb | 49 +----- lib/reline/terminfo.rb | 158 ------------------ ..._ansi_without_terminfo.rb => test_ansi.rb} | 9 +- test/reline/test_ansi_with_terminfo.rb | 112 ------------- test/reline/test_terminfo.rb | 61 ------- 6 files changed, 8 insertions(+), 382 deletions(-) delete mode 100644 lib/reline/terminfo.rb rename test/reline/{test_ansi_without_terminfo.rb => test_ansi.rb} (84%) delete mode 100644 test/reline/test_ansi_with_terminfo.rb delete mode 100644 test/reline/test_terminfo.rb diff --git a/lib/reline.rb b/lib/reline.rb index 4ba74d2cb2..e2629d57f7 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -6,7 +6,6 @@ require 'reline/key_stroke' require 'reline/line_editor' require 'reline/history' -require 'reline/terminfo' require 'reline/io' require 'reline/face' require 'rbconfig' diff --git a/lib/reline/io/ansi.rb b/lib/reline/io/ansi.rb index cf0c1c1b31..d3f27f09bb 100644 --- a/lib/reline/io/ansi.rb +++ b/lib/reline/io/ansi.rb @@ -29,10 +29,6 @@ class Reline::ANSI < Reline::IO 'H' => [:ed_move_to_beg, {}], } - if Reline::Terminfo.enabled? - Reline::Terminfo.setupterm(0, 2) - end - def initialize @input = STDIN @output = STDOUT @@ -44,14 +40,10 @@ def encoding Encoding.default_external end - def set_default_key_bindings(config, allow_terminfo: true) + def set_default_key_bindings(config) set_bracketed_paste_key_bindings(config) set_default_key_bindings_ansi_cursor(config) - if allow_terminfo && Reline::Terminfo.enabled? - set_default_key_bindings_terminfo(config) - else - set_default_key_bindings_comprehensive_list(config) - end + set_default_key_bindings_comprehensive_list(config) { [27, 91, 90] => :completion_journey_up, # S-Tab }.each_pair do |key, func| @@ -98,23 +90,6 @@ def set_default_key_bindings_ansi_cursor(config) end end - def set_default_key_bindings_terminfo(config) - key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding| - begin - key_code = Reline::Terminfo.tigetstr(capname) - [ key_code.bytes, key_binding ] - rescue Reline::Terminfo::TerminfoError - # capname is undefined - end - end.compact.to_h - - key_bindings.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - config.add_default_key_binding_by_keymap(:vi_insert, key, func) - config.add_default_key_binding_by_keymap(:vi_command, key, func) - end - end - def set_default_key_bindings_comprehensive_list(config) { # xterm @@ -281,27 +256,11 @@ def move_cursor_down(x) end def hide_cursor - seq = "\e[?25l" - if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported? - begin - seq = Reline::Terminfo.tigetstr('civis') - rescue Reline::Terminfo::TerminfoError - # civis is undefined - end - end - @output.write seq + @output.write "\e[?25l" end def show_cursor - seq = "\e[?25h" - if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported? - begin - seq = Reline::Terminfo.tigetstr('cnorm') - rescue Reline::Terminfo::TerminfoError - # cnorm is undefined - end - end - @output.write seq + @output.write "\e[?25h" end def erase_after_cursor diff --git a/lib/reline/terminfo.rb b/lib/reline/terminfo.rb deleted file mode 100644 index c2b1f681b4..0000000000 --- a/lib/reline/terminfo.rb +++ /dev/null @@ -1,158 +0,0 @@ -begin - # Ignore warning `Add fiddle to your Gemfile or gemspec` in Ruby 3.4. - # terminfo.rb and ansi.rb supports fiddle unavailable environment. - verbose, $VERBOSE = $VERBOSE, nil - require 'fiddle' - require 'fiddle/import' -rescue LoadError - module Reline::Terminfo - def self.curses_dl - false - end - end -ensure - $VERBOSE = verbose -end - -module Reline::Terminfo - extend Fiddle::Importer - - class TerminfoError < StandardError; end - - def self.curses_dl_files - case RUBY_PLATFORM - when /mingw/, /mswin/ - # aren't supported - [] - when /cygwin/ - %w[cygncursesw-10.dll cygncurses-10.dll] - when /darwin/ - %w[libncursesw.dylib libcursesw.dylib libncurses.dylib libcurses.dylib] - else - %w[libncursesw.so libcursesw.so libncurses.so libcurses.so] - end - end - - @curses_dl = false - def self.curses_dl - return @curses_dl unless @curses_dl == false - if Fiddle.const_defined?(:TYPE_VARIADIC) - curses_dl_files.each do |curses_name| - result = Fiddle::Handle.new(curses_name) - rescue Fiddle::DLError - next - else - @curses_dl = result - break - end - end - @curses_dl = nil if @curses_dl == false - @curses_dl - end -end if not Reline.const_defined?(:Terminfo) or not Reline::Terminfo.respond_to?(:curses_dl) - -module Reline::Terminfo - dlload curses_dl - #extern 'int setupterm(char *term, int fildes, int *errret)' - @setupterm = Fiddle::Function.new(curses_dl['setupterm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - #extern 'char *tigetstr(char *capname)' - @tigetstr = Fiddle::Function.new(curses_dl['tigetstr'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP) - begin - #extern 'char *tiparm(const char *str, ...)' - @tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP) - rescue Fiddle::DLError - # OpenBSD lacks tiparm - #extern 'char *tparm(const char *str, ...)' - @tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP) - end - begin - #extern 'int tigetflag(char *str)' - @tigetflag = Fiddle::Function.new(curses_dl['tigetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - rescue Fiddle::DLError - # OpenBSD lacks tigetflag - #extern 'int tgetflag(char *str)' - @tigetflag = Fiddle::Function.new(curses_dl['tgetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - end - begin - #extern 'int tigetnum(char *str)' - @tigetnum = Fiddle::Function.new(curses_dl['tigetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - rescue Fiddle::DLError - # OpenBSD lacks tigetnum - #extern 'int tgetnum(char *str)' - @tigetnum = Fiddle::Function.new(curses_dl['tgetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - end - - def self.setupterm(term, fildes) - errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE) - ret = @setupterm.(term, fildes, errret_int) - case ret - when 0 # OK - @term_supported = true - when -1 # ERR - @term_supported = false - end - end - - class StringWithTiparm < String - def tiparm(*args) # for method chain - Reline::Terminfo.tiparm(self, *args) - end - end - - def self.tigetstr(capname) - raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String) - capability = @tigetstr.(capname) - case capability.to_i - when 0, -1 - raise TerminfoError, "can't find capability: #{capname}" - end - StringWithTiparm.new(capability.to_s) - end - - def self.tiparm(str, *args) - new_args = [] - args.each do |a| - new_args << Fiddle::TYPE_INT << a - end - @tiparm.(str, *new_args).to_s - end - - def self.tigetflag(capname) - raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String) - flag = @tigetflag.(capname).to_i - case flag - when -1 - raise TerminfoError, "not boolean capability: #{capname}" - when 0 - raise TerminfoError, "can't find capability: #{capname}" - end - flag - end - - def self.tigetnum(capname) - raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String) - num = @tigetnum.(capname).to_i - case num - when -2 - raise TerminfoError, "not numeric capability: #{capname}" - when -1 - raise TerminfoError, "can't find capability: #{capname}" - end - num - end - - # NOTE: This means Fiddle and curses are enabled. - def self.enabled? - true - end - - def self.term_supported? - @term_supported - end -end if Reline::Terminfo.curses_dl - -module Reline::Terminfo - def self.enabled? - false - end -end unless Reline::Terminfo.curses_dl diff --git a/test/reline/test_ansi_without_terminfo.rb b/test/reline/test_ansi.rb similarity index 84% rename from test/reline/test_ansi_without_terminfo.rb rename to test/reline/test_ansi.rb index 62b1e7dec0..5e28e72b06 100644 --- a/test/reline/test_ansi_without_terminfo.rb +++ b/test/reline/test_ansi.rb @@ -1,11 +1,11 @@ require_relative 'helper' require 'reline' -class Reline::ANSI::WithoutTerminfoTest < Reline::TestCase +class Reline::ANSITest < Reline::TestCase def setup Reline.send(:test_mode, ansi: true) @config = Reline::Config.new - Reline.core.io_gate.set_default_key_bindings(@config, allow_terminfo: false) + Reline.core.io_gate.set_default_key_bindings(@config) end def teardown @@ -50,7 +50,7 @@ def test_left_arrow assert_key_binding("\eOD", :ed_prev_char) end - # Ctrl+arrow and Meta+arrow; always mapped regardless of terminfo enabled or not + # Ctrl+arrow and Meta+arrow def test_extended assert_key_binding("\e[1;5C", :em_next_word) # Ctrl+→ assert_key_binding("\e[1;5D", :ed_prev_word) # Ctrl+← @@ -60,12 +60,11 @@ def test_extended assert_key_binding("\e\e[D", :ed_prev_word) # Meta+← end - # Shift-Tab; always mapped regardless of terminfo enabled or not def test_shift_tab assert_key_binding("\e[Z", :completion_journey_up, [:emacs, :vi_insert]) end - # A few emacs bindings that are always mapped regardless of terminfo enabled or not + # A few emacs bindings that are always mapped def test_more_emacs assert_key_binding("\e ", :em_set_mark, [:emacs]) assert_key_binding("\C-x\C-x", :em_exchange_mark, [:emacs]) diff --git a/test/reline/test_ansi_with_terminfo.rb b/test/reline/test_ansi_with_terminfo.rb deleted file mode 100644 index 3adda10716..0000000000 --- a/test/reline/test_ansi_with_terminfo.rb +++ /dev/null @@ -1,112 +0,0 @@ -require_relative 'helper' -require 'reline' - -class Reline::ANSI::WithTerminfoTest < Reline::TestCase - def setup - Reline.send(:test_mode, ansi: true) - @config = Reline::Config.new - Reline.core.io_gate.set_default_key_bindings(@config, allow_terminfo: true) - end - - def teardown - Reline.test_reset - end - - # Home key - def test_khome - assert_key_binding(Reline::Terminfo.tigetstr('khome'), :ed_move_to_beg) - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - # End key - def test_kend - assert_key_binding(Reline::Terminfo.tigetstr('kend'), :ed_move_to_end) - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - # Delete key - def test_kdch1 - assert_key_binding(Reline::Terminfo.tigetstr('kdch1'), :key_delete) - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - # PgUp key - def test_kpp - assert_key_binding(Reline::Terminfo.tigetstr('kpp'), :ed_search_prev_history) - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - # PgDn key - def test_knp - assert_key_binding(Reline::Terminfo.tigetstr('knp'), :ed_search_next_history) - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - # Up arrow key - def test_kcuu1 - assert_key_binding(Reline::Terminfo.tigetstr('kcuu1'), :ed_prev_history) - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - # Down arrow key - def test_kcud1 - assert_key_binding(Reline::Terminfo.tigetstr('kcud1'), :ed_next_history) - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - # Right arrow key - def test_kcuf1 - assert_key_binding(Reline::Terminfo.tigetstr('kcuf1'), :ed_next_char) - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - # Left arrow key - def test_kcub1 - assert_key_binding(Reline::Terminfo.tigetstr('kcub1'), :ed_prev_char) - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - # Home and End; always mapped regardless of terminfo enabled or not - def test_home_end - assert_key_binding("\e[H", :ed_move_to_beg) - assert_key_binding("\e[F", :ed_move_to_end) - end - - # Arrow; always mapped regardless of terminfo enabled or not - def test_arrow - assert_key_binding("\e[A", :ed_prev_history) - assert_key_binding("\e[B", :ed_next_history) - assert_key_binding("\e[C", :ed_next_char) - assert_key_binding("\e[D", :ed_prev_char) - end - - # Ctrl+arrow and Meta+arrow; always mapped regardless of terminfo enabled or not - def test_extended - assert_key_binding("\e[1;5C", :em_next_word) # Ctrl+→ - assert_key_binding("\e[1;5D", :ed_prev_word) # Ctrl+← - assert_key_binding("\e[1;3C", :em_next_word) # Meta+→ - assert_key_binding("\e[1;3D", :ed_prev_word) # Meta+← - assert_key_binding("\e\e[C", :em_next_word) # Meta+→ - assert_key_binding("\e\e[D", :ed_prev_word) # Meta+← - end - - # Shift-Tab; always mapped regardless of terminfo enabled or not - def test_shift_tab - assert_key_binding("\e[Z", :completion_journey_up, [:emacs, :vi_insert]) - end - - # A few emacs bindings that are always mapped regardless of terminfo enabled or not - def test_more_emacs - assert_key_binding("\e ", :em_set_mark, [:emacs]) - assert_key_binding("\C-x\C-x", :em_exchange_mark, [:emacs]) - end -end if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported? diff --git a/test/reline/test_terminfo.rb b/test/reline/test_terminfo.rb deleted file mode 100644 index 4e59c54838..0000000000 --- a/test/reline/test_terminfo.rb +++ /dev/null @@ -1,61 +0,0 @@ -require_relative 'helper' -require "reline" - -class Reline::Terminfo::Test < Reline::TestCase - def setup - Reline::Terminfo.setupterm(0, 2) - end - - def test_tigetstr - assert Reline::Terminfo.tigetstr('khome') - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - def test_tigetstr_with_error - assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetstr('unknown') } - assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetstr(nil) } - end - - def test_tiparm - assert Reline::Terminfo.tigetstr('khome').tiparm - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - def test_tigetstr_with_param - assert Reline::Terminfo.tigetstr('cuu').include?('%p1%d') - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - def test_tiparm_with_param - assert Reline::Terminfo.tigetstr('cuu').tiparm(4649).include?('4649') - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - def test_tigetflag - assert_instance_of Integer, Reline::Terminfo.tigetflag('xenl') - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - def test_tigetflag_with_error - assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetflag('cuu') } - assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetflag('unknown') } - assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetflag(nil) } - end - - def test_tigetnum - assert_instance_of Integer, Reline::Terminfo.tigetnum('colors') - rescue Reline::Terminfo::TerminfoError => e - omit e.message - end - - def test_tigetnum_with_error - assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetnum('cuu') } - assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetnum('unknown') } - assert_raise(Reline::Terminfo::TerminfoError) { Reline::Terminfo.tigetnum(nil) } - end -end if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?