reline 0.1.1 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb3a343a202a2c043257a432f50eb05eeb58b5638a88c24eb095c96a40b03fea
4
- data.tar.gz: e9c8aec5f70fe3897fb8f835d0d5b228a93a5444b0a7ce804290c19ae44c6c55
3
+ metadata.gz: '0259fdcb2bf55d2b5532e0da9259247dbee06eefc4564e664006218a094d140f'
4
+ data.tar.gz: ea484914d377faed8c7b346fce10a94e996661950714bac68bf8c604a4bd492b
5
5
  SHA512:
6
- metadata.gz: a0e2a92e1e007d23542313783108250c66ad81b90389c306a9f413b8426ce14a39e14731f974aa8fcabcddaeceae529a70388e6e1a3db6ec7a511278b0c568d7
7
- data.tar.gz: 0f86f497938921154a475f4b63059a4cda560b44b5b6f9fb63b3b4afcae9de3ea351c1226640eb074546a765b2f51ff5a62bd1af20142a6211c8e74a06f16a57
6
+ metadata.gz: f108f900ffb86ad334c82f6a64777e78b32e010985f9add33f4ad6ae55f7f1890ed2b16d250d02388ba9b8bc8aed3e985241e3f95b4e43b0d6fe2b9c0ee28057
7
+ data.tar.gz: eb45c06febe94e7f953f24db1be2375e619c0fb3d270de7dddc3056c0a473438de2c528e78f6d6682b0a35dcd84d901845aba72c0a16346109b3f84de7276702
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  [![Build Status](https://travis-ci.com/ruby/reline.svg?branch=master)](https://travis-ci.com/ruby/reline)
2
2
 
3
+ This is a screen capture of *IRB improved by Reline*.
4
+
5
+ ![IRB improved by Reline](https://raw.githubusercontent.com/wiki/ruby/reline/images/irb_improved_by_reline.gif)
6
+
3
7
  # Reline
4
8
 
5
9
  Reline is compatible with the API of Ruby's stdlib 'readline', GNU Readline and Editline by pure Ruby implementation.
@@ -7,6 +7,7 @@ require 'reline/key_actor'
7
7
  require 'reline/key_stroke'
8
8
  require 'reline/line_editor'
9
9
  require 'reline/history'
10
+ require 'rbconfig'
10
11
 
11
12
  module Reline
12
13
  FILENAME_COMPLETION_PROC = nil
@@ -45,40 +46,44 @@ module Reline
45
46
  @completion_quote_character = nil
46
47
  end
47
48
 
49
+ def encoding
50
+ Reline::IOGate.encoding
51
+ end
52
+
48
53
  def completion_append_character=(val)
49
54
  if val.nil?
50
55
  @completion_append_character = nil
51
56
  elsif val.size == 1
52
- @completion_append_character = val.encode(Encoding::default_external)
57
+ @completion_append_character = val.encode(Reline::IOGate.encoding)
53
58
  elsif val.size > 1
54
- @completion_append_character = val[0].encode(Encoding::default_external)
59
+ @completion_append_character = val[0].encode(Reline::IOGate.encoding)
55
60
  else
56
61
  @completion_append_character = nil
57
62
  end
58
63
  end
59
64
 
60
65
  def basic_word_break_characters=(v)
61
- @basic_word_break_characters = v.encode(Encoding::default_external)
66
+ @basic_word_break_characters = v.encode(Reline::IOGate.encoding)
62
67
  end
63
68
 
64
69
  def completer_word_break_characters=(v)
65
- @completer_word_break_characters = v.encode(Encoding::default_external)
70
+ @completer_word_break_characters = v.encode(Reline::IOGate.encoding)
66
71
  end
67
72
 
68
73
  def basic_quote_characters=(v)
69
- @basic_quote_characters = v.encode(Encoding::default_external)
74
+ @basic_quote_characters = v.encode(Reline::IOGate.encoding)
70
75
  end
71
76
 
72
77
  def completer_quote_characters=(v)
73
- @completer_quote_characters = v.encode(Encoding::default_external)
78
+ @completer_quote_characters = v.encode(Reline::IOGate.encoding)
74
79
  end
75
80
 
76
81
  def filename_quote_characters=(v)
77
- @filename_quote_characters = v.encode(Encoding::default_external)
82
+ @filename_quote_characters = v.encode(Reline::IOGate.encoding)
78
83
  end
79
84
 
80
85
  def special_prefixes=(v)
81
- @special_prefixes = v.encode(Encoding::default_external)
86
+ @special_prefixes = v.encode(Reline::IOGate.encoding)
82
87
  end
83
88
 
84
89
  def completion_case_fold=(v)
@@ -94,22 +99,22 @@ module Reline
94
99
  end
95
100
 
96
101
  def completion_proc=(p)
97
- raise ArgumentError unless p.respond_to?(:call)
102
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
98
103
  @completion_proc = p
99
104
  end
100
105
 
101
106
  def output_modifier_proc=(p)
102
- raise ArgumentError unless p.respond_to?(:call)
107
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
103
108
  @output_modifier_proc = p
104
109
  end
105
110
 
106
111
  def prompt_proc=(p)
107
- raise ArgumentError unless p.respond_to?(:call)
112
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
108
113
  @prompt_proc = p
109
114
  end
110
115
 
111
116
  def auto_indent_proc=(p)
112
- raise ArgumentError unless p.respond_to?(:call)
117
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
113
118
  @auto_indent_proc = p
114
119
  end
115
120
 
@@ -118,7 +123,7 @@ module Reline
118
123
  end
119
124
 
120
125
  def dig_perfect_match_proc=(p)
121
- raise ArgumentError unless p.respond_to?(:call)
126
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
122
127
  @dig_perfect_match_proc = p
123
128
  end
124
129
 
@@ -171,7 +176,7 @@ module Reline
171
176
 
172
177
  whole_buffer = line_editor.whole_buffer.dup
173
178
  whole_buffer.taint if RUBY_VERSION < '2.7'
174
- if add_hist and whole_buffer and whole_buffer.chomp.size > 0
179
+ if add_hist and whole_buffer and whole_buffer.chomp("\n").size > 0
175
180
  Reline::HISTORY << whole_buffer
176
181
  end
177
182
 
@@ -184,8 +189,8 @@ module Reline
184
189
 
185
190
  line = line_editor.line.dup
186
191
  line.taint if RUBY_VERSION < '2.7'
187
- if add_hist and line and line.chomp.size > 0
188
- Reline::HISTORY << line.chomp
192
+ if add_hist and line and line.chomp("\n").size > 0
193
+ Reline::HISTORY << line.chomp("\n")
189
194
  end
190
195
 
191
196
  line_editor.reset_line if line_editor.line.nil?
@@ -201,7 +206,7 @@ module Reline
201
206
  otio = Reline::IOGate.prep
202
207
 
203
208
  may_req_ambiguous_char_width
204
- line_editor.reset(prompt)
209
+ line_editor.reset(prompt, encoding: Reline::IOGate.encoding)
205
210
  if multiline
206
211
  line_editor.multiline_on
207
212
  if block_given?
@@ -218,7 +223,6 @@ module Reline
218
223
  line_editor.auto_indent_proc = auto_indent_proc
219
224
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
220
225
  line_editor.pre_input_hook = pre_input_hook
221
- line_editor.rerender
222
226
 
223
227
  unless config.test_mode
224
228
  config.read
@@ -228,17 +232,27 @@ module Reline
228
232
  end
229
233
  end
230
234
 
235
+ line_editor.rerender
236
+
231
237
  begin
238
+ prev_pasting_state = false
232
239
  loop do
240
+ prev_pasting_state = Reline::IOGate.in_pasting?
233
241
  read_io(config.keyseq_timeout) { |inputs|
234
242
  inputs.each { |c|
235
243
  line_editor.input_key(c)
236
244
  line_editor.rerender
237
245
  }
238
246
  }
247
+ if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
248
+ prev_pasting_state = false
249
+ line_editor.rerender_all
250
+ end
239
251
  break if line_editor.finished?
240
252
  end
241
253
  Reline::IOGate.move_cursor_column(0)
254
+ rescue Errno::EIO
255
+ # Maybe the I/O has been closed.
242
256
  rescue StandardError => e
243
257
  line_editor.finalize
244
258
  Reline::IOGate.deprep(otio)
@@ -332,8 +346,14 @@ module Reline
332
346
  @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
333
347
  return if ambiguous_width
334
348
  Reline::IOGate.move_cursor_column(0)
335
- print "\u{25bd}"
336
- @ambiguous_width = Reline::IOGate.cursor_pos.x
349
+ begin
350
+ output.write "\u{25bd}"
351
+ rescue Encoding::UndefinedConversionError
352
+ # LANG=C
353
+ @ambiguous_width = 1
354
+ else
355
+ @ambiguous_width = Reline::IOGate.cursor_pos.x
356
+ end
337
357
  Reline::IOGate.move_cursor_column(0)
338
358
  Reline::IOGate.erase_after_cursor
339
359
  end
@@ -387,11 +407,15 @@ module Reline
387
407
  def_instance_delegators self, :readmultiline
388
408
  private :readmultiline
389
409
 
410
+ def self.encoding_system_needs
411
+ self.core.encoding
412
+ end
413
+
390
414
  def self.core
391
415
  @core ||= Core.new { |core|
392
416
  core.config = Reline::Config.new
393
417
  core.key_stroke = Reline::KeyStroke.new(core.config)
394
- core.line_editor = Reline::LineEditor.new(core.config)
418
+ core.line_editor = Reline::LineEditor.new(core.config, Reline::IOGate.encoding)
395
419
 
396
420
  core.basic_word_break_characters = " \t\n`><=;|&{("
397
421
  core.completer_word_break_characters = " \t\n`><=;|&{("
@@ -405,14 +429,11 @@ module Reline
405
429
  def self.line_editor
406
430
  core.line_editor
407
431
  end
408
-
409
- HISTORY = History.new(core.config)
410
432
  end
411
433
 
412
434
  if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
413
435
  require 'reline/windows'
414
- if Reline::Windows.get_screen_size == [0, 0]
415
- # Maybe Mintty on Cygwin
436
+ if Reline::Windows.msys_tty?
416
437
  require 'reline/ansi'
417
438
  Reline::IOGate = Reline::ANSI
418
439
  else
@@ -422,4 +443,5 @@ else
422
443
  require 'reline/ansi'
423
444
  Reline::IOGate = Reline::ANSI
424
445
  end
446
+ Reline::HISTORY = Reline::History.new(Reline.core.config)
425
447
  require 'reline/general_io'
@@ -1,20 +1,59 @@
1
1
  require 'io/console'
2
2
 
3
3
  class Reline::ANSI
4
+ def self.encoding
5
+ Encoding.default_external
6
+ end
7
+
8
+ def self.win?
9
+ false
10
+ end
11
+
4
12
  RAW_KEYSTROKE_CONFIG = {
13
+ # Console (80x25)
14
+ [27, 91, 49, 126] => :ed_move_to_beg, # Home
15
+ [27, 91, 52, 126] => :ed_move_to_end, # End
16
+ [27, 91, 51, 126] => :key_delete, # Del
5
17
  [27, 91, 65] => :ed_prev_history, # ↑
6
18
  [27, 91, 66] => :ed_next_history, # ↓
7
19
  [27, 91, 67] => :ed_next_char, # →
8
20
  [27, 91, 68] => :ed_prev_char, # ←
9
- [27, 91, 51, 126] => :key_delete, # Del
10
- [27, 91, 49, 126] => :ed_move_to_beg, # Home
11
- [27, 91, 52, 126] => :ed_move_to_end, # End
21
+
22
+ # KDE
12
23
  [27, 91, 72] => :ed_move_to_beg, # Home
13
24
  [27, 91, 70] => :ed_move_to_end, # End
25
+ # Del is 0x08
26
+ [27, 71, 65] => :ed_prev_history, # ↑
27
+ [27, 71, 66] => :ed_next_history, # ↓
28
+ [27, 71, 67] => :ed_next_char, # →
29
+ [27, 71, 68] => :ed_prev_char, # ←
30
+
31
+ # urxvt / exoterm
32
+ [27, 91, 55, 126] => :ed_move_to_beg, # Home
33
+ [27, 91, 56, 126] => :ed_move_to_end, # End
34
+
35
+ # GNOME
36
+ [27, 79, 72] => :ed_move_to_beg, # Home
37
+ [27, 79, 70] => :ed_move_to_end, # End
38
+ # Del is 0x08
39
+ # Arrow keys are the same of KDE
40
+
41
+ # iTerm2
42
+ [27, 27, 91, 67] => :em_next_word, # Option+→
43
+ [27, 27, 91, 68] => :ed_prev_word, # Option+←
44
+ [195, 166] => :em_next_word, # Option+f
45
+ [195, 162] => :ed_prev_word, # Option+b
46
+
47
+ # others
14
48
  [27, 32] => :em_set_mark, # M-<space>
15
49
  [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
16
50
  [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→
17
51
  [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←
52
+
53
+ [27, 79, 65] => :ed_prev_history, # ↑
54
+ [27, 79, 66] => :ed_next_history, # ↓
55
+ [27, 79, 67] => :ed_next_char, # →
56
+ [27, 79, 68] => :ed_prev_char, # ←
18
57
  }
19
58
 
20
59
  @@input = STDIN
@@ -32,8 +71,29 @@ class Reline::ANSI
32
71
  unless @@buf.empty?
33
72
  return @@buf.shift
34
73
  end
35
- c = @@input.raw(intr: true, &:getbyte)
74
+ until c = @@input.raw(intr: true, &:getbyte)
75
+ sleep 0.1
76
+ end
36
77
  (c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
78
+ rescue Errno::EIO
79
+ # Maybe the I/O has been closed.
80
+ nil
81
+ end
82
+
83
+ def self.in_pasting?
84
+ not Reline::IOGate.empty_buffer?
85
+ end
86
+
87
+ def self.empty_buffer?
88
+ unless @@buf.empty?
89
+ return false
90
+ end
91
+ rs, = IO.select([@@input], [], [], 0.00001)
92
+ if rs and rs[0]
93
+ false
94
+ else
95
+ true
96
+ end
37
97
  end
38
98
 
39
99
  def self.ungetc(c)
@@ -41,16 +101,23 @@ class Reline::ANSI
41
101
  end
42
102
 
43
103
  def self.retrieve_keybuffer
104
+ begin
44
105
  result = select([@@input], [], [], 0.001)
45
106
  return if result.nil?
46
107
  str = @@input.read_nonblock(1024)
47
108
  str.bytes.each do |c|
48
109
  @@buf.push(c)
49
110
  end
111
+ rescue EOFError
112
+ end
50
113
  end
51
114
 
52
115
  def self.get_screen_size
53
- @@input.winsize
116
+ s = @@input.winsize
117
+ return s if s[0] > 0 && s[1] > 0
118
+ s = [ENV["LINES"].to_i, ENV["COLUMNS"].to_i]
119
+ return s if s[0] > 0 && s[1] > 0
120
+ [24, 80]
54
121
  rescue Errno::ENOTTY
55
122
  [24, 80]
56
123
  end
@@ -69,10 +136,13 @@ class Reline::ANSI
69
136
  @@input.raw do |stdin|
70
137
  @@output << "\e[6n"
71
138
  @@output.flush
72
- while (c = stdin.getc) != 'R'
73
- res << c if c
139
+ loop do
140
+ c = stdin.getc
141
+ next if c.nil?
142
+ res << c
143
+ m = res.match(/\e\[(?<row>\d+);(?<column>\d+)R/)
144
+ break if m
74
145
  end
75
- m = res.match(/\e\[(?<row>\d+);(?<column>\d+)/)
76
146
  (m.pre_match + m.post_match).chars.reverse_each do |ch|
77
147
  stdin.ungetc ch
78
148
  end
@@ -80,20 +150,27 @@ class Reline::ANSI
80
150
  column = m[:column].to_i - 1
81
151
  row = m[:row].to_i - 1
82
152
  rescue Errno::ENOTTY
83
- buf = @@output.pread(@@output.pos, 0)
84
- row = buf.count("\n")
85
- column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
153
+ begin
154
+ buf = @@output.pread(@@output.pos, 0)
155
+ row = buf.count("\n")
156
+ column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
157
+ rescue Errno::ESPIPE
158
+ # Just returns column 1 for ambiguous width because this I/O is not
159
+ # tty and can't seek.
160
+ row = 0
161
+ column = 1
162
+ end
86
163
  end
87
164
  Reline::CursorPos.new(column, row)
88
165
  end
89
166
 
90
167
  def self.move_cursor_column(x)
91
- print "\e[#{x + 1}G"
168
+ @@output.write "\e[#{x + 1}G"
92
169
  end
93
170
 
94
171
  def self.move_cursor_up(x)
95
172
  if x > 0
96
- print "\e[#{x}A" if x > 0
173
+ @@output.write "\e[#{x}A" if x > 0
97
174
  elsif x < 0
98
175
  move_cursor_down(-x)
99
176
  end
@@ -101,24 +178,24 @@ class Reline::ANSI
101
178
 
102
179
  def self.move_cursor_down(x)
103
180
  if x > 0
104
- print "\e[#{x}B" if x > 0
181
+ @@output.write "\e[#{x}B" if x > 0
105
182
  elsif x < 0
106
183
  move_cursor_up(-x)
107
184
  end
108
185
  end
109
186
 
110
187
  def self.erase_after_cursor
111
- print "\e[K"
188
+ @@output.write "\e[K"
112
189
  end
113
190
 
114
191
  def self.scroll_down(x)
115
192
  return if x.zero?
116
- print "\e[#{x}S"
193
+ @@output.write "\e[#{x}S"
117
194
  end
118
195
 
119
196
  def self.clear_screen
120
- print "\e[2J"
121
- print "\e[1;1H"
197
+ @@output.write "\e[2J"
198
+ @@output.write "\e[1;1H"
122
199
  end
123
200
 
124
201
  @@old_winch_handler = nil
@@ -1,10 +1,6 @@
1
- require 'pathname'
2
-
3
1
  class Reline::Config
4
2
  attr_reader :test_mode
5
3
 
6
- DEFAULT_PATH = '~/.inputrc'
7
-
8
4
  KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]|\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]|\\e|\\[\\\"\'abdfnrtv]|\\\d{1,3}|\\x\h{1,2}|./
9
5
 
10
6
  class InvalidInputrc < RuntimeError
@@ -37,6 +33,10 @@ class Reline::Config
37
33
  show-all-if-ambiguous
38
34
  show-all-if-unmodified
39
35
  visible-stats
36
+ show-mode-in-prompt
37
+ vi-cmd-mode-icon
38
+ vi-ins-mode-icon
39
+ emacs-mode-string
40
40
  }
41
41
  VARIABLE_NAME_SYMBOLS = VARIABLE_NAMES.map { |v| :"#{v.tr(?-, ?_)}" }
42
42
  VARIABLE_NAME_SYMBOLS.each do |v|
@@ -54,7 +54,11 @@ class Reline::Config
54
54
  @key_actors[:emacs] = Reline::KeyActor::Emacs.new
55
55
  @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
56
56
  @key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
57
- @history_size = 500
57
+ @vi_cmd_mode_icon = '(cmd)'
58
+ @vi_ins_mode_icon = '(ins)'
59
+ @emacs_mode_string = '@'
60
+ # https://tiswww.case.edu/php/chet/readline/readline.html#IDX25
61
+ @history_size = -1 # unlimited
58
62
  @keyseq_timeout = 500
59
63
  @test_mode = false
60
64
  end
@@ -83,8 +87,34 @@ class Reline::Config
83
87
  @key_actors[@keymap_label]
84
88
  end
85
89
 
90
+ def inputrc_path
91
+ case ENV['INPUTRC']
92
+ when nil, ''
93
+ else
94
+ return File.expand_path(ENV['INPUTRC'])
95
+ end
96
+
97
+ # In the XDG Specification, if ~/.config/readline/inputrc exists, then
98
+ # ~/.inputrc should not be read, but for compatibility with GNU Readline,
99
+ # if ~/.inputrc exists, then it is given priority.
100
+ home_rc_path = File.expand_path('~/.inputrc')
101
+ return home_rc_path if File.exist?(home_rc_path)
102
+
103
+ case path = ENV['XDG_CONFIG_HOME']
104
+ when nil, ''
105
+ else
106
+ path = File.join(path, 'readline/inputrc')
107
+ return path if File.exist?(path) and path == File.expand_path(path)
108
+ end
109
+
110
+ path = File.expand_path('~/.config/readline/inputrc')
111
+ return path if File.exist?(path)
112
+
113
+ return home_rc_path
114
+ end
115
+
86
116
  def read(file = nil)
87
- file ||= File.expand_path(ENV['INPUTRC'] || DEFAULT_PATH)
117
+ file ||= inputrc_path
88
118
  begin
89
119
  if file.respond_to?(:readlines)
90
120
  lines = file.readlines
@@ -135,7 +165,7 @@ class Reline::Config
135
165
 
136
166
  case line
137
167
  when /^set +([^ ]+) +([^ ]+)/i
138
- var, value = $1.downcase, $2.downcase
168
+ var, value = $1.downcase, $2
139
169
  bind_variable(var, value)
140
170
  next
141
171
  when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
@@ -184,9 +214,12 @@ class Reline::Config
184
214
 
185
215
  def bind_variable(name, value)
186
216
  case name
187
- when *VARIABLE_NAMES then
188
- variable_name = :"@#{name.tr(?-, ?_)}"
189
- instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
217
+ when 'history-size'
218
+ begin
219
+ @history_size = Integer(value)
220
+ rescue ArgumentError
221
+ @history_size = 500
222
+ end
190
223
  when 'bell-style'
191
224
  @bell_style =
192
225
  case value
@@ -225,6 +258,32 @@ class Reline::Config
225
258
  end
226
259
  when 'keyseq-timeout'
227
260
  @keyseq_timeout = value.to_i
261
+ when 'show-mode-in-prompt'
262
+ case value
263
+ when 'off'
264
+ @show_mode_in_prompt = false
265
+ when 'on'
266
+ @show_mode_in_prompt = true
267
+ else
268
+ @show_mode_in_prompt = false
269
+ end
270
+ when 'vi-cmd-mode-string'
271
+ @vi_cmd_mode_icon = retrieve_string(value)
272
+ when 'vi-ins-mode-string'
273
+ @vi_ins_mode_icon = retrieve_string(value)
274
+ when 'emacs-mode-string'
275
+ @emacs_mode_string = retrieve_string(value)
276
+ when *VARIABLE_NAMES then
277
+ variable_name = :"@#{name.tr(?-, ?_)}"
278
+ instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
279
+ end
280
+ end
281
+
282
+ def retrieve_string(str)
283
+ if str =~ /\A"(.*)"\z/
284
+ parse_keyseq($1).map(&:chr).join
285
+ else
286
+ parse_keyseq(str).map(&:chr).join
228
287
  end
229
288
  end
230
289