reline 0.2.1 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.2.1'
2
+ VERSION = '0.2.6'
3
3
  end
@@ -9,23 +9,40 @@ class Reline::Windows
9
9
  true
10
10
  end
11
11
 
12
- RAW_KEYSTROKE_CONFIG = {
13
- [224, 72] => :ed_prev_history, # ↑
14
- [224, 80] => :ed_next_history, # ↓
15
- [224, 77] => :ed_next_char, # →
16
- [224, 75] => :ed_prev_char, # ←
17
- [224, 83] => :key_delete, # Del
18
- [224, 71] => :ed_move_to_beg, # Home
19
- [224, 79] => :ed_move_to_end, # End
20
- [ 0, 41] => :ed_unassigned, # input method on/off
21
- [ 0, 72] => :ed_prev_history, #
22
- [ 0, 80] => :ed_next_history, #
23
- [ 0, 77] => :ed_next_char, #
24
- [ 0, 75] => :ed_prev_char, #
25
- [ 0, 83] => :key_delete, # Del
26
- [ 0, 71] => :ed_move_to_beg, # Home
27
- [ 0, 79] => :ed_move_to_end # End
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
- @@ReadConsoleInput = Win32API.new('kernel32', 'ReadConsoleInput', ['L', 'P', 'L', 'P'], 'L')
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.getwch
125
- unless @@input_buf.empty?
126
- return @@input_buf.shift
127
- end
128
- while @@kbhit.call == 0
129
- sleep(0.001)
130
- end
131
- until @@kbhit.call == 0
132
- ret = @@getwch.call
133
- if ret == 0 or ret == 0xE0
134
- @@input_buf << ret
135
- ret = @@getwch.call
136
- @@input_buf << ret
137
- return @@input_buf.shift
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
- begin
140
- bytes = ret.chr(Encoding::UTF_8).bytes
141
- @@input_buf.push(*bytes)
142
- rescue Encoding::UndefinedConversionError
143
- @@input_buf << ret
144
- @@input_buf << @@getwch.call if ret == 224
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.getc
225
+ def self.check_input_event
151
226
  num_of_events = 0.chr * 8
152
- while @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) != 0 and num_of_events.unpack('L').first > 0
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 @@ReadConsoleInput.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
231
+ if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
156
232
  event = input_record[0, 2].unpack('s*').first
157
- if event == WINDOW_BUFFER_SIZE_EVENT
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
- unless @@output_buf.empty?
163
- return @@output_buf.shift
164
- end
165
- input = getwch
166
- meta = (@@GetKeyState.call(VK_LMENU) & 0x80) != 0
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.1
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-01-11 00:00:00.000000000 Z
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.3
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.