reline 0.1.0 → 0.1.5

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