reline 0.2.1 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.