summaryrefslogtreecommitdiff
path: root/lib/reline
diff options
context:
space:
mode:
authoraycabta <aycabta@gmail.com>2021-05-18 21:46:16 +0900
committeraycabta <aycabta@gmail.com>2021-06-21 17:58:48 +0900
commit46c813969be642e61763379e03c9698b91ab1a96 (patch)
treef538fd28c94ee848571e00ffefc196682ddc4a1b /lib/reline
parent1b543dc2261fee0317ba69b366b6a26fe3aeb43f (diff)
[ruby/reline] Add terminfo support
https://github.com/ruby/reline/commit/74a7ffaa2f
Diffstat (limited to 'lib/reline')
-rw-r--r--lib/reline/ansi.rb66
-rw-r--r--lib/reline/terminfo.rb80
2 files changed, 132 insertions, 14 deletions
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-<space>
+ [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-<space>
- [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