From 46c813969be642e61763379e03c9698b91ab1a96 Mon Sep 17 00:00:00 2001 From: aycabta Date: Tue, 18 May 2021 21:46:16 +0900 Subject: [ruby/reline] Add terminfo support https://github.com/ruby/reline/commit/74a7ffaa2f --- lib/reline/ansi.rb | 66 ++++++++++++++++++++++++++++++++--------- lib/reline/terminfo.rb | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 14 deletions(-) create mode 100644 lib/reline/terminfo.rb (limited to 'lib/reline') diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb index d590d00ad1..5e8fd0fd28 100644 --- a/lib/reline/ansi.rb +++ b/lib/reline/ansi.rb @@ -1,7 +1,12 @@ require 'io/console' require 'timeout' +require_relative 'terminfo' class Reline::ANSI + if Reline::Terminfo.enabled? + Reline::Terminfo.setupterm(0, 2) + end + def self.encoding Encoding.default_external end @@ -11,6 +16,51 @@ class Reline::ANSI end def self.set_default_key_bindings(config) + if Reline::Terminfo.enabled? + set_default_key_bindings_terminfo(config) + else + set_default_key_bindings_comprehensive_list(config) + end + { + # extended entries of terminfo + [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→, extended entry + [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←, extended entry + }.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 + { + # default bindings + [27, 32] => :em_set_mark, # M- + [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows + }.each_pair do |key, func| + config.add_default_key_binding_by_keymap(:emacs, key, func) + end + end + + def self.set_default_key_bindings_terminfo(config) + { + Reline::Terminfo.tigetstr('khome').to_s.bytes => :ed_move_to_beg, + Reline::Terminfo.tigetstr('kend').to_s.bytes => :ed_move_to_end, + Reline::Terminfo.tigetstr('kcuu1').to_s.bytes => :ed_prev_history, + Reline::Terminfo.tigetstr('kcud1').to_s.bytes => :ed_next_history, + Reline::Terminfo.tigetstr('kcuf1').to_s.bytes => :ed_next_char, + Reline::Terminfo.tigetstr('kcub1').to_s.bytes => :ed_prev_char, + # Escape sequences that omit the move distance and are set to defaults + # value 1 may be sometimes sent by pressing the arrow-key. + Reline::Terminfo.tigetstr('cuu').to_s.sub(/%p1%d/, '').bytes => :ed_prev_history, + Reline::Terminfo.tigetstr('cud').to_s.sub(/%p1%d/, '').bytes => :ed_next_history, + Reline::Terminfo.tigetstr('cuf').to_s.sub(/%p1%d/, '').bytes => :ed_next_char, + Reline::Terminfo.tigetstr('cub').to_s.sub(/%p1%d/, '').bytes => :ed_prev_char, + }.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 self.set_default_key_bindings_comprehensive_list(config) { # Console (80x25) [27, 91, 49, 126] => :ed_move_to_beg, # Home @@ -41,15 +91,11 @@ class Reline::ANSI # Arrow keys are the same of KDE # iTerm2 - [27, 27, 91, 67] => :em_next_word, # Option+→ - [27, 27, 91, 68] => :ed_prev_word, # Option+← + [27, 27, 91, 67] => :em_next_word, # Option+→, extended entry + [27, 27, 91, 68] => :ed_prev_word, # Option+←, extended entry [195, 166] => :em_next_word, # Option+f [195, 162] => :ed_prev_word, # Option+b - # others - [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→ - [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+← - [27, 79, 65] => :ed_prev_history, # ↑ [27, 79, 66] => :ed_next_history, # ↓ [27, 79, 67] => :ed_next_char, # → @@ -59,14 +105,6 @@ class Reline::ANSI config.add_default_key_binding_by_keymap(:vi_insert, key, func) config.add_default_key_binding_by_keymap(:vi_command, key, func) end - - { - # others - [27, 32] => :em_set_mark, # M- - [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows - }.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - end end @@input = STDIN diff --git a/lib/reline/terminfo.rb b/lib/reline/terminfo.rb new file mode 100644 index 0000000000..9c1615cc2b --- /dev/null +++ b/lib/reline/terminfo.rb @@ -0,0 +1,80 @@ +require 'fiddle' +require 'fiddle/import' + +module Reline::Terminfo + extend Fiddle::Importer + + class TerminfoError < StandardError; end + + @curses_dl = nil + def self.curses_dl + return @curses_dl if @curses_dl + if Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1') + # Fiddle::TYPE_VARIADIC is supported from Fiddle 1.0.1. + %w[libncursesw.so libcursesw.so libncurses.so libcurses.so].each do |curses_name| + result = Fiddle::Handle.new(curses_name) + rescue Fiddle::DLError + next + else + @curses_dl = result + break + end + end + @curses_dl + end +end + +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) + #extern 'char *tiparm(const char *str, ...)' + @tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP) + + def self.setupterm(term, fildes) + errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT') + ret = @setupterm.(term, fildes, errret_int) + errret = errret_int.unpack('i')[0] + case ret + when 0 # OK + 0 + when -1 # ERR + case errret + when 1 + raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.') + when 0 + raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.') + when -1 + raise TerminfoError.new('The terminfo database could not be found.') + else # unknown + -1 + end + else # unknown + -2 + end + end + + def self.tigetstr(capname) + @tigetstr.(capname) + end + + def self.tiparm(str, *args) + new_args = [] + args.each do |a| + new_args << Fiddle::TYPE_INT << a + end + @tiparm.(str, *new_args) + end + + def self.enabled? + true + end +end if Reline::Terminfo.curses_dl + +module Reline::Terminfo + def self.enabled? + false + end +end unless Reline::Terminfo.curses_dl -- cgit v1.2.3