reline 0.2.1 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -0
- data/lib/reline.rb +26 -13
- data/lib/reline/ansi.rb +100 -46
- data/lib/reline/config.rb +22 -13
- data/lib/reline/general_io.rb +11 -3
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/line_editor.rb +161 -35
- data/lib/reline/terminfo.rb +84 -0
- data/lib/reline/unicode.rb +1 -0
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +138 -78
- metadata +4 -59
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'fiddle'
|
2
|
+
require 'fiddle/import'
|
3
|
+
|
4
|
+
module Reline::Terminfo
|
5
|
+
extend Fiddle::Importer
|
6
|
+
|
7
|
+
class TerminfoError < StandardError; end
|
8
|
+
|
9
|
+
@curses_dl = nil
|
10
|
+
def self.curses_dl
|
11
|
+
return @curses_dl if @curses_dl
|
12
|
+
if Fiddle.const_defined?(:VERSION) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1')
|
13
|
+
# Fiddle::TYPE_VARIADIC is supported from Fiddle 1.0.1.
|
14
|
+
%w[libncursesw.so libcursesw.so libncurses.so libcurses.so].each do |curses_name|
|
15
|
+
result = Fiddle::Handle.new(curses_name)
|
16
|
+
rescue Fiddle::DLError
|
17
|
+
next
|
18
|
+
else
|
19
|
+
@curses_dl = result
|
20
|
+
break
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@curses_dl
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Reline::Terminfo
|
28
|
+
dlload curses_dl
|
29
|
+
#extern 'int setupterm(char *term, int fildes, int *errret)'
|
30
|
+
@setupterm = Fiddle::Function.new(curses_dl['setupterm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
31
|
+
#extern 'char *tigetstr(char *capname)'
|
32
|
+
@tigetstr = Fiddle::Function.new(curses_dl['tigetstr'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP)
|
33
|
+
#extern 'char *tiparm(const char *str, ...)'
|
34
|
+
@tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
|
35
|
+
|
36
|
+
def self.setupterm(term, fildes)
|
37
|
+
errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT')
|
38
|
+
ret = @setupterm.(term, fildes, errret_int)
|
39
|
+
errret = errret_int.unpack('i')[0]
|
40
|
+
case ret
|
41
|
+
when 0 # OK
|
42
|
+
0
|
43
|
+
when -1 # ERR
|
44
|
+
case errret
|
45
|
+
when 1
|
46
|
+
raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.')
|
47
|
+
when 0
|
48
|
+
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.')
|
49
|
+
when -1
|
50
|
+
raise TerminfoError.new('The terminfo database could not be found.')
|
51
|
+
else # unknown
|
52
|
+
-1
|
53
|
+
end
|
54
|
+
else # unknown
|
55
|
+
-2
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.tigetstr(capname)
|
60
|
+
result = @tigetstr.(capname).to_s
|
61
|
+
def result.tiparm(*args) # for method chain
|
62
|
+
Reline::Terminfo.tiparm(self, *args)
|
63
|
+
end
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.tiparm(str, *args)
|
68
|
+
new_args = []
|
69
|
+
args.each do |a|
|
70
|
+
new_args << Fiddle::TYPE_INT << a
|
71
|
+
end
|
72
|
+
@tiparm.(str, *new_args).to_s
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.enabled?
|
76
|
+
true
|
77
|
+
end
|
78
|
+
end if Reline::Terminfo.curses_dl
|
79
|
+
|
80
|
+
module Reline::Terminfo
|
81
|
+
def self.enabled?
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end unless Reline::Terminfo.curses_dl
|
data/lib/reline/unicode.rb
CHANGED
@@ -108,6 +108,7 @@ class Reline::Unicode
|
|
108
108
|
end
|
109
109
|
m = mbchar.encode(Encoding::UTF_8).match(MBCharWidthRE)
|
110
110
|
case
|
111
|
+
when m.nil? then 1 # TODO should be U+FFFD � REPLACEMENT CHARACTER
|
111
112
|
when m[:width_2_1], m[:width_2_2] then 2
|
112
113
|
when m[:width_3] then 3
|
113
114
|
when m[:width_0] then 0
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -9,23 +9,40 @@ class Reline::Windows
|
|
9
9
|
true
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
12
|
+
def self.win_legacy_console?
|
13
|
+
@@legacy_console
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.set_default_key_bindings(config)
|
17
|
+
{
|
18
|
+
[224, 72] => :ed_prev_history, # ↑
|
19
|
+
[224, 80] => :ed_next_history, # ↓
|
20
|
+
[224, 77] => :ed_next_char, # →
|
21
|
+
[224, 75] => :ed_prev_char, # ←
|
22
|
+
[224, 83] => :key_delete, # Del
|
23
|
+
[224, 71] => :ed_move_to_beg, # Home
|
24
|
+
[224, 79] => :ed_move_to_end, # End
|
25
|
+
[ 0, 41] => :ed_unassigned, # input method on/off
|
26
|
+
[ 0, 72] => :ed_prev_history, # ↑
|
27
|
+
[ 0, 80] => :ed_next_history, # ↓
|
28
|
+
[ 0, 77] => :ed_next_char, # →
|
29
|
+
[ 0, 75] => :ed_prev_char, # ←
|
30
|
+
[ 0, 83] => :key_delete, # Del
|
31
|
+
[ 0, 71] => :ed_move_to_beg, # Home
|
32
|
+
[ 0, 79] => :ed_move_to_end # End
|
33
|
+
}.each_pair do |key, func|
|
34
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
35
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
36
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
37
|
+
end
|
38
|
+
|
39
|
+
{
|
40
|
+
[27, 32] => :em_set_mark, # M-<space>
|
41
|
+
[24, 24] => :em_exchange_mark, # C-x C-x
|
42
|
+
}.each_pair do |key, func|
|
43
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
44
|
+
end
|
45
|
+
end
|
29
46
|
|
30
47
|
if defined? JRUBY_VERSION
|
31
48
|
require 'win32api'
|
@@ -69,13 +86,35 @@ class Reline::Windows
|
|
69
86
|
end
|
70
87
|
end
|
71
88
|
|
89
|
+
VK_RETURN = 0x0D
|
72
90
|
VK_MENU = 0x12
|
73
91
|
VK_LMENU = 0xA4
|
74
92
|
VK_CONTROL = 0x11
|
75
93
|
VK_SHIFT = 0x10
|
94
|
+
|
95
|
+
KEY_EVENT = 0x01
|
96
|
+
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
97
|
+
|
98
|
+
CAPSLOCK_ON = 0x0080
|
99
|
+
ENHANCED_KEY = 0x0100
|
100
|
+
LEFT_ALT_PRESSED = 0x0002
|
101
|
+
LEFT_CTRL_PRESSED = 0x0008
|
102
|
+
NUMLOCK_ON = 0x0020
|
103
|
+
RIGHT_ALT_PRESSED = 0x0001
|
104
|
+
RIGHT_CTRL_PRESSED = 0x0004
|
105
|
+
SCROLLLOCK_ON = 0x0040
|
106
|
+
SHIFT_PRESSED = 0x0010
|
107
|
+
|
108
|
+
VK_END = 0x23
|
109
|
+
VK_HOME = 0x24
|
110
|
+
VK_LEFT = 0x25
|
111
|
+
VK_UP = 0x26
|
112
|
+
VK_RIGHT = 0x27
|
113
|
+
VK_DOWN = 0x28
|
114
|
+
VK_DELETE = 0x2E
|
115
|
+
|
76
116
|
STD_INPUT_HANDLE = -10
|
77
117
|
STD_OUTPUT_HANDLE = -11
|
78
|
-
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
79
118
|
FILE_TYPE_PIPE = 0x0003
|
80
119
|
FILE_NAME_INFO = 2
|
81
120
|
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
@@ -89,11 +128,31 @@ class Reline::Windows
|
|
89
128
|
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
90
129
|
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
91
130
|
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
92
|
-
@@
|
131
|
+
@@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L')
|
93
132
|
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
94
133
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
95
134
|
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
96
135
|
|
136
|
+
@@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
|
137
|
+
@@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
138
|
+
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
139
|
+
|
140
|
+
private_class_method def self.getconsolemode
|
141
|
+
mode = "\000\000\000\000"
|
142
|
+
@@GetConsoleMode.call(@@hConsoleHandle, mode)
|
143
|
+
mode.unpack1('L')
|
144
|
+
end
|
145
|
+
|
146
|
+
private_class_method def self.setconsolemode(mode)
|
147
|
+
@@SetConsoleMode.call(@@hConsoleHandle, mode)
|
148
|
+
end
|
149
|
+
|
150
|
+
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
151
|
+
#if @@legacy_console
|
152
|
+
# setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
153
|
+
# @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
154
|
+
#end
|
155
|
+
|
97
156
|
@@input_buf = []
|
98
157
|
@@output_buf = []
|
99
158
|
|
@@ -121,78 +180,78 @@ class Reline::Windows
|
|
121
180
|
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
122
181
|
end
|
123
182
|
|
124
|
-
def self.
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
183
|
+
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
184
|
+
char = char_code.chr(Encoding::UTF_8)
|
185
|
+
if char_code == 0x0D and control_key_state.anybits?(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | SHIFT_PRESSED)
|
186
|
+
# It's treated as Meta+Enter on Windows.
|
187
|
+
@@output_buf.push("\e".ord)
|
188
|
+
@@output_buf.push(char_code)
|
189
|
+
elsif char_code == 0x20 and control_key_state.anybits?(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
|
190
|
+
# It's treated as Meta+Space on Windows.
|
191
|
+
@@output_buf.push("\e".ord)
|
192
|
+
@@output_buf.push(char_code)
|
193
|
+
elsif control_key_state.anybits?(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
|
194
|
+
@@output_buf.push("\e".ord)
|
195
|
+
@@output_buf.concat(char.bytes)
|
196
|
+
elsif control_key_state.anybits?(ENHANCED_KEY)
|
197
|
+
case virtual_key_code # Emulate getwch() key sequences.
|
198
|
+
when VK_END
|
199
|
+
@@output_buf.push(0, 79)
|
200
|
+
when VK_HOME
|
201
|
+
@@output_buf.push(0, 71)
|
202
|
+
when VK_LEFT
|
203
|
+
@@output_buf.push(0, 75)
|
204
|
+
when VK_UP
|
205
|
+
@@output_buf.push(0, 72)
|
206
|
+
when VK_RIGHT
|
207
|
+
@@output_buf.push(0, 77)
|
208
|
+
when VK_DOWN
|
209
|
+
@@output_buf.push(0, 80)
|
210
|
+
when VK_DELETE
|
211
|
+
@@output_buf.push(0, 83)
|
138
212
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
@@
|
213
|
+
elsif char_code == 0 and control_key_state != 0
|
214
|
+
# unknown
|
215
|
+
else
|
216
|
+
case virtual_key_code
|
217
|
+
when VK_RETURN
|
218
|
+
@@output_buf.push("\n".ord)
|
219
|
+
else
|
220
|
+
@@output_buf.concat(char.bytes)
|
145
221
|
end
|
146
222
|
end
|
147
|
-
@@input_buf.shift
|
148
223
|
end
|
149
224
|
|
150
|
-
def self.
|
225
|
+
def self.check_input_event
|
151
226
|
num_of_events = 0.chr * 8
|
152
|
-
while @@
|
227
|
+
while @@output_buf.empty? #or true
|
228
|
+
next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
|
153
229
|
input_record = 0.chr * 18
|
154
230
|
read_event = 0.chr * 4
|
155
|
-
if @@
|
231
|
+
if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
|
156
232
|
event = input_record[0, 2].unpack('s*').first
|
157
|
-
|
233
|
+
case event
|
234
|
+
when WINDOW_BUFFER_SIZE_EVENT
|
158
235
|
@@winch_handler.()
|
236
|
+
when KEY_EVENT
|
237
|
+
key_down = input_record[4, 4].unpack('l*').first
|
238
|
+
repeat_count = input_record[8, 2].unpack('s*').first
|
239
|
+
virtual_key_code = input_record[10, 2].unpack('s*').first
|
240
|
+
virtual_scan_code = input_record[12, 2].unpack('s*').first
|
241
|
+
char_code = input_record[14, 2].unpack('S*').first
|
242
|
+
control_key_state = input_record[16, 2].unpack('S*').first
|
243
|
+
is_key_down = key_down.zero? ? false : true
|
244
|
+
if is_key_down
|
245
|
+
process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
246
|
+
end
|
159
247
|
end
|
160
248
|
end
|
161
249
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
control = (@@GetKeyState.call(VK_CONTROL) & 0x80) != 0
|
168
|
-
shift = (@@GetKeyState.call(VK_SHIFT) & 0x80) != 0
|
169
|
-
force_enter = !input.instance_of?(Array) && (control or shift) && input == 0x0D
|
170
|
-
if force_enter
|
171
|
-
# It's treated as Meta+Enter on Windows
|
172
|
-
@@output_buf.push("\e".ord)
|
173
|
-
@@output_buf.push(input)
|
174
|
-
else
|
175
|
-
case input
|
176
|
-
when 0x00
|
177
|
-
meta = false
|
178
|
-
@@output_buf.push(input)
|
179
|
-
input = getwch
|
180
|
-
@@output_buf.push(*input)
|
181
|
-
when 0xE0
|
182
|
-
@@output_buf.push(input)
|
183
|
-
input = getwch
|
184
|
-
@@output_buf.push(*input)
|
185
|
-
when 0x03
|
186
|
-
@@output_buf.push(input)
|
187
|
-
else
|
188
|
-
@@output_buf.push(input)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
if meta
|
192
|
-
"\e".ord
|
193
|
-
else
|
194
|
-
@@output_buf.shift
|
195
|
-
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.getc
|
253
|
+
check_input_event
|
254
|
+
@@output_buf.shift
|
196
255
|
end
|
197
256
|
|
198
257
|
def self.ungetc(c)
|
@@ -258,6 +317,7 @@ class Reline::Windows
|
|
258
317
|
cursor = csbi[4, 4].unpack('L').first
|
259
318
|
written = 0.chr * 4
|
260
319
|
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.last - cursor_pos.x, cursor, written)
|
320
|
+
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, 0, get_screen_size.last - cursor_pos.x, cursor, written)
|
261
321
|
end
|
262
322
|
|
263
323
|
def self.scroll_down(val)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|
@@ -24,62 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.5'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: bundler
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rake
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: test-unit
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: yamatanooroti
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 0.0.6
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 0.0.6
|
83
27
|
description: Alternative GNU Readline or Editline implementation by pure Ruby.
|
84
28
|
email:
|
85
29
|
- aycabta@gmail.com
|
@@ -104,6 +48,7 @@ files:
|
|
104
48
|
- lib/reline/kill_ring.rb
|
105
49
|
- lib/reline/line_editor.rb
|
106
50
|
- lib/reline/sibori.rb
|
51
|
+
- lib/reline/terminfo.rb
|
107
52
|
- lib/reline/unicode.rb
|
108
53
|
- lib/reline/unicode/east_asian_width.rb
|
109
54
|
- lib/reline/version.rb
|
@@ -128,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
73
|
- !ruby/object:Gem::Version
|
129
74
|
version: '0'
|
130
75
|
requirements: []
|
131
|
-
rubygems_version: 3.2.
|
76
|
+
rubygems_version: 3.2.17
|
132
77
|
signing_key:
|
133
78
|
specification_version: 4
|
134
79
|
summary: Alternative GNU Readline or Editline implementation by pure Ruby.
|