reline 0.1.0 → 0.1.5

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: d28ea6c28099d6ee05888e139f26785b7f524b2d43b97169a262d3039fe4b12d
4
- data.tar.gz: c929cedf8040597cdaf6d19ab14aa6724cf4b47dbf4c78b6a1fc2e74637bfc09
3
+ metadata.gz: cb0a1da0d5f51d50b82a78a3084a69ca37821b541e3c362bcb2b9a10a04664a3
4
+ data.tar.gz: bc1860cf76efa34984aef1dbafb60d1f7781b86ba44a9392dc5fbd8025631121
5
5
  SHA512:
6
- metadata.gz: f9c4c8c540077105e8c326dd7ee0de350dbf8cd7a0aba14ad467f28b2dbb9c52eade5601e56ada651d2ea6f713e2a47891545bccdd9ea11c43d01dfe315fdd27
7
- data.tar.gz: 7e8a2c186e0b4c84e3a7ddc62ab08dce768d1ea768ffc91bcf880e0227bb003c2b8fa288e45044cf1ab73144bd7ec46e9ac0faa582721acaf55726fc111706d4
6
+ metadata.gz: 4691bf1855429c5f1c25b16f66bc1e4793290579ae9a7345d1dd97c2694863bec59bab30edb232665b38a1f302a4e601c99111c84e5c277651ab2e53d4353982
7
+ data.tar.gz: 9ba7b27a82ce1d823f394c78ba1d995b6c4f71ddecb13c17c27fb39a13b7503c3b84402feb0239eb33d19d87f00d06c0608e35439413f6c72d2bdea50e84d10e
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,6 +232,8 @@ module Reline
228
232
  end
229
233
  end
230
234
 
235
+ line_editor.rerender
236
+
231
237
  begin
232
238
  loop do
233
239
  read_io(config.keyseq_timeout) { |inputs|
@@ -239,6 +245,8 @@ module Reline
239
245
  break if line_editor.finished?
240
246
  end
241
247
  Reline::IOGate.move_cursor_column(0)
248
+ rescue Errno::EIO
249
+ # Maybe the I/O has been closed.
242
250
  rescue StandardError => e
243
251
  line_editor.finalize
244
252
  Reline::IOGate.deprep(otio)
@@ -332,8 +340,14 @@ module Reline
332
340
  @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
333
341
  return if ambiguous_width
334
342
  Reline::IOGate.move_cursor_column(0)
335
- print "\u{25bd}"
336
- @ambiguous_width = Reline::IOGate.cursor_pos.x
343
+ begin
344
+ output.write "\u{25bd}"
345
+ rescue Encoding::UndefinedConversionError
346
+ # LANG=C
347
+ @ambiguous_width = 1
348
+ else
349
+ @ambiguous_width = Reline::IOGate.cursor_pos.x
350
+ end
337
351
  Reline::IOGate.move_cursor_column(0)
338
352
  Reline::IOGate.erase_after_cursor
339
353
  end
@@ -387,11 +401,15 @@ module Reline
387
401
  def_instance_delegators self, :readmultiline
388
402
  private :readmultiline
389
403
 
404
+ def self.encoding_system_needs
405
+ self.core.encoding
406
+ end
407
+
390
408
  def self.core
391
409
  @core ||= Core.new { |core|
392
410
  core.config = Reline::Config.new
393
411
  core.key_stroke = Reline::KeyStroke.new(core.config)
394
- core.line_editor = Reline::LineEditor.new(core.config)
412
+ core.line_editor = Reline::LineEditor.new(core.config, Reline::IOGate.encoding)
395
413
 
396
414
  core.basic_word_break_characters = " \t\n`><=;|&{("
397
415
  core.completer_word_break_characters = " \t\n`><=;|&{("
@@ -405,14 +423,11 @@ module Reline
405
423
  def self.line_editor
406
424
  core.line_editor
407
425
  end
408
-
409
- HISTORY = History.new(core.config)
410
426
  end
411
427
 
412
428
  if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
413
429
  require 'reline/windows'
414
- if Reline::Windows.get_screen_size == [0, 0]
415
- # Maybe Mintty on Cygwin
430
+ if Reline::Windows.msys_tty?
416
431
  require 'reline/ansi'
417
432
  Reline::IOGate = Reline::ANSI
418
433
  else
@@ -422,4 +437,5 @@ else
422
437
  require 'reline/ansi'
423
438
  Reline::IOGate = Reline::ANSI
424
439
  end
440
+ Reline::HISTORY = Reline::History.new(Reline.core.config)
425
441
  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,13 @@ 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
37
81
  end
38
82
 
39
83
  def self.ungetc(c)
@@ -41,16 +85,23 @@ class Reline::ANSI
41
85
  end
42
86
 
43
87
  def self.retrieve_keybuffer
88
+ begin
44
89
  result = select([@@input], [], [], 0.001)
45
90
  return if result.nil?
46
91
  str = @@input.read_nonblock(1024)
47
92
  str.bytes.each do |c|
48
93
  @@buf.push(c)
49
94
  end
95
+ rescue EOFError
96
+ end
50
97
  end
51
98
 
52
99
  def self.get_screen_size
53
- @@input.winsize
100
+ s = @@input.winsize
101
+ return s if s[0] > 0 && s[1] > 0
102
+ s = [ENV["LINES"].to_i, ENV["COLUMNS"].to_i]
103
+ return s if s[0] > 0 && s[1] > 0
104
+ [24, 80]
54
105
  rescue Errno::ENOTTY
55
106
  [24, 80]
56
107
  end
@@ -69,10 +120,13 @@ class Reline::ANSI
69
120
  @@input.raw do |stdin|
70
121
  @@output << "\e[6n"
71
122
  @@output.flush
72
- while (c = stdin.getc) != 'R'
73
- res << c if c
123
+ loop do
124
+ c = stdin.getc
125
+ next if c.nil?
126
+ res << c
127
+ m = res.match(/\e\[(?<row>\d+);(?<column>\d+)R/)
128
+ break if m
74
129
  end
75
- m = res.match(/\e\[(?<row>\d+);(?<column>\d+)/)
76
130
  (m.pre_match + m.post_match).chars.reverse_each do |ch|
77
131
  stdin.ungetc ch
78
132
  end
@@ -80,20 +134,27 @@ class Reline::ANSI
80
134
  column = m[:column].to_i - 1
81
135
  row = m[:row].to_i - 1
82
136
  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
137
+ begin
138
+ buf = @@output.pread(@@output.pos, 0)
139
+ row = buf.count("\n")
140
+ column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
141
+ rescue Errno::ESPIPE
142
+ # Just returns column 1 for ambiguous width because this I/O is not
143
+ # tty and can't seek.
144
+ row = 0
145
+ column = 1
146
+ end
86
147
  end
87
148
  Reline::CursorPos.new(column, row)
88
149
  end
89
150
 
90
151
  def self.move_cursor_column(x)
91
- print "\e[#{x + 1}G"
152
+ @@output.write "\e[#{x + 1}G"
92
153
  end
93
154
 
94
155
  def self.move_cursor_up(x)
95
156
  if x > 0
96
- print "\e[#{x}A" if x > 0
157
+ @@output.write "\e[#{x}A" if x > 0
97
158
  elsif x < 0
98
159
  move_cursor_down(-x)
99
160
  end
@@ -101,24 +162,24 @@ class Reline::ANSI
101
162
 
102
163
  def self.move_cursor_down(x)
103
164
  if x > 0
104
- print "\e[#{x}B" if x > 0
165
+ @@output.write "\e[#{x}B" if x > 0
105
166
  elsif x < 0
106
167
  move_cursor_up(-x)
107
168
  end
108
169
  end
109
170
 
110
171
  def self.erase_after_cursor
111
- print "\e[K"
172
+ @@output.write "\e[K"
112
173
  end
113
174
 
114
175
  def self.scroll_down(x)
115
176
  return if x.zero?
116
- print "\e[#{x}S"
177
+ @@output.write "\e[#{x}S"
117
178
  end
118
179
 
119
180
  def self.clear_screen
120
- print "\e[2J"
121
- print "\e[1;1H"
181
+ @@output.write "\e[2J"
182
+ @@output.write "\e[1;1H"
122
183
  end
123
184
 
124
185
  @@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