reline 0.1.3 → 0.1.8

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: d6e100afbceacfa4a270221c7e5158f211dc9972efbbec251108661a380d43b1
4
- data.tar.gz: 051ef05dac8d446be2b0268202da3762d702287650dc05a3041aa7910f481624
3
+ metadata.gz: 89f055454b502262f43495a2d8de12ba033259287a4c09e7c93afeb0462c9c33
4
+ data.tar.gz: 6c6f081e14d699faed9ac7ba08001dac978318535f58458fd2c777db56b57b50
5
5
  SHA512:
6
- metadata.gz: '0068601561915d76b2ce265c7de49252941dbe1e57464db9b08876dfd49da6fc12c3821d90d8240b4f64728a4a55a469726233689fa077ef7296796495025295'
7
- data.tar.gz: 4385616c29cba0596c84d98219d658361cae3dc8c9427e8460d1703972995a9adcb242ab252c30fe2abcf712d2d824a4cb41691b44e241a9d711a89da3e0d62d
6
+ metadata.gz: 6bd17793312ff9f2996ce08ebada091005c7efd9ff1d0ef007ea982ee5e74ee03a83fb0bb7c975c6da747db712a3837d14c108fd7de087094f1db37e051876aa
7
+ data.tar.gz: 148dd6767b1aae9a295d334445957b64e6f6a46b8941a10517e5d4567a29eac369fe3843c6c8c1bb5fd66d1165c82988316b030eb05080517bb7105bc5174281
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
@@ -98,22 +99,22 @@ module Reline
98
99
  end
99
100
 
100
101
  def completion_proc=(p)
101
- raise ArgumentError unless p.respond_to?(:call)
102
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
102
103
  @completion_proc = p
103
104
  end
104
105
 
105
106
  def output_modifier_proc=(p)
106
- raise ArgumentError unless p.respond_to?(:call)
107
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
107
108
  @output_modifier_proc = p
108
109
  end
109
110
 
110
111
  def prompt_proc=(p)
111
- raise ArgumentError unless p.respond_to?(:call)
112
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
112
113
  @prompt_proc = p
113
114
  end
114
115
 
115
116
  def auto_indent_proc=(p)
116
- raise ArgumentError unless p.respond_to?(:call)
117
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
117
118
  @auto_indent_proc = p
118
119
  end
119
120
 
@@ -122,7 +123,7 @@ module Reline
122
123
  end
123
124
 
124
125
  def dig_perfect_match_proc=(p)
125
- raise ArgumentError unless p.respond_to?(:call)
126
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
126
127
  @dig_perfect_match_proc = p
127
128
  end
128
129
 
@@ -175,7 +176,7 @@ module Reline
175
176
 
176
177
  whole_buffer = line_editor.whole_buffer.dup
177
178
  whole_buffer.taint if RUBY_VERSION < '2.7'
178
- 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
179
180
  Reline::HISTORY << whole_buffer
180
181
  end
181
182
 
@@ -188,8 +189,8 @@ module Reline
188
189
 
189
190
  line = line_editor.line.dup
190
191
  line.taint if RUBY_VERSION < '2.7'
191
- if add_hist and line and line.chomp.size > 0
192
- Reline::HISTORY << line.chomp
192
+ if add_hist and line and line.chomp("\n").size > 0
193
+ Reline::HISTORY << line.chomp("\n")
193
194
  end
194
195
 
195
196
  line_editor.reset_line if line_editor.line.nil?
@@ -222,7 +223,6 @@ module Reline
222
223
  line_editor.auto_indent_proc = auto_indent_proc
223
224
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
224
225
  line_editor.pre_input_hook = pre_input_hook
225
- line_editor.rerender
226
226
 
227
227
  unless config.test_mode
228
228
  config.read
@@ -232,17 +232,27 @@ module Reline
232
232
  end
233
233
  end
234
234
 
235
+ line_editor.rerender
236
+
235
237
  begin
238
+ prev_pasting_state = false
236
239
  loop do
240
+ prev_pasting_state = Reline::IOGate.in_pasting?
237
241
  read_io(config.keyseq_timeout) { |inputs|
238
242
  inputs.each { |c|
239
243
  line_editor.input_key(c)
240
244
  line_editor.rerender
241
245
  }
242
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
243
251
  break if line_editor.finished?
244
252
  end
245
253
  Reline::IOGate.move_cursor_column(0)
254
+ rescue Errno::EIO
255
+ # Maybe the I/O has been closed.
246
256
  rescue StandardError => e
247
257
  line_editor.finalize
248
258
  Reline::IOGate.deprep(otio)
@@ -336,8 +346,14 @@ module Reline
336
346
  @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
337
347
  return if ambiguous_width
338
348
  Reline::IOGate.move_cursor_column(0)
339
- output.write "\u{25bd}"
340
- @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
341
357
  Reline::IOGate.move_cursor_column(0)
342
358
  Reline::IOGate.erase_after_cursor
343
359
  end
@@ -28,12 +28,22 @@ class Reline::ANSI
28
28
  [27, 71, 67] => :ed_next_char, # →
29
29
  [27, 71, 68] => :ed_prev_char, # ←
30
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
+
31
35
  # GNOME
32
36
  [27, 79, 72] => :ed_move_to_beg, # Home
33
37
  [27, 79, 70] => :ed_move_to_end, # End
34
38
  # Del is 0x08
35
39
  # Arrow keys are the same of KDE
36
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
+
37
47
  # others
38
48
  [27, 32] => :em_set_mark, # M-<space>
39
49
  [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
@@ -61,8 +71,29 @@ class Reline::ANSI
61
71
  unless @@buf.empty?
62
72
  return @@buf.shift
63
73
  end
64
- c = @@input.raw(intr: true, &:getbyte)
74
+ until c = @@input.raw(intr: true, &:getbyte)
75
+ sleep 0.1
76
+ end
65
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
66
97
  end
67
98
 
68
99
  def self.ungetc(c)
@@ -105,10 +136,13 @@ class Reline::ANSI
105
136
  @@input.raw do |stdin|
106
137
  @@output << "\e[6n"
107
138
  @@output.flush
108
- while (c = stdin.getc) != 'R'
109
- 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
110
145
  end
111
- m = res.match(/\e\[(?<row>\d+);(?<column>\d+)/)
112
146
  (m.pre_match + m.post_match).chars.reverse_each do |ch|
113
147
  stdin.ungetc ch
114
148
  end
@@ -116,9 +150,16 @@ class Reline::ANSI
116
150
  column = m[:column].to_i - 1
117
151
  row = m[:row].to_i - 1
118
152
  rescue Errno::ENOTTY
119
- buf = @@output.pread(@@output.pos, 0)
120
- row = buf.count("\n")
121
- 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
122
163
  end
123
164
  Reline::CursorPos.new(column, row)
124
165
  end
@@ -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
@@ -185,7 +215,11 @@ class Reline::Config
185
215
  def bind_variable(name, value)
186
216
  case name
187
217
  when 'history-size'
188
- @history_size = value.to_i
218
+ begin
219
+ @history_size = Integer(value)
220
+ rescue ArgumentError
221
+ @history_size = 500
222
+ end
189
223
  when 'bell-style'
190
224
  @bell_style =
191
225
  case value
@@ -224,12 +258,35 @@ class Reline::Config
224
258
  end
225
259
  when 'keyseq-timeout'
226
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)
227
276
  when *VARIABLE_NAMES then
228
277
  variable_name = :"@#{name.tr(?-, ?_)}"
229
278
  instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
230
279
  end
231
280
  end
232
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
287
+ end
288
+ end
289
+
233
290
  def bind_key(key, func_name)
234
291
  if key =~ /\A"(.*)"\z/
235
292
  keyseq = parse_keyseq($1)
@@ -1,6 +1,10 @@
1
1
  require 'timeout'
2
2
 
3
3
  class Reline::GeneralIO
4
+ def self.reset
5
+ @@pasting = false
6
+ end
7
+
4
8
  def self.encoding
5
9
  RUBY_PLATFORM =~ /mswin|mingw/ ? Encoding::UTF_8 : Encoding::default_external
6
10
  end
@@ -67,6 +71,20 @@ class Reline::GeneralIO
67
71
  def self.set_winch_handler(&handler)
68
72
  end
69
73
 
74
+ @@pasting = false
75
+
76
+ def self.in_pasting?
77
+ @@pasting
78
+ end
79
+
80
+ def self.start_pasting
81
+ @@pasting = true
82
+ end
83
+
84
+ def self.finish_pasting
85
+ @@pasting = false
86
+ end
87
+
70
88
  def self.prep
71
89
  end
72
90
 
@@ -29,27 +29,47 @@ class Reline::History < Array
29
29
  end
30
30
 
31
31
  def push(*val)
32
- diff = size + val.size - @config.history_size
33
- if diff > 0
34
- if diff <= size
35
- shift(diff)
36
- else
37
- diff -= size
38
- clear
39
- val.shift(diff)
32
+ # If history_size is zero, all histories are dropped.
33
+ return self if @config.history_size.zero?
34
+ # If history_size is negative, history size is unlimited.
35
+ if @config.history_size.positive?
36
+ diff = size + val.size - @config.history_size
37
+ if diff > 0
38
+ if diff <= size
39
+ shift(diff)
40
+ else
41
+ diff -= size
42
+ clear
43
+ val.shift(diff)
44
+ end
40
45
  end
41
46
  end
42
- super(*(val.map{ |v| String.new(v, encoding: Reline.encoding_system_needs) }))
47
+ super(*(val.map{ |v|
48
+ String.new(v, encoding: Reline.encoding_system_needs)
49
+ }))
43
50
  end
44
51
 
45
52
  def <<(val)
46
- shift if size + 1 > @config.history_size
53
+ # If history_size is zero, all histories are dropped.
54
+ return self if @config.history_size.zero?
55
+ # If history_size is negative, history size is unlimited.
56
+ if @config.history_size.positive?
57
+ shift if size + 1 > @config.history_size
58
+ end
47
59
  super(String.new(val, encoding: Reline.encoding_system_needs))
48
60
  end
49
61
 
50
62
  private def check_index(index)
51
63
  index += size if index < 0
52
- raise RangeError.new("index=<#{index}>") if index < -@config.history_size or @config.history_size < index
64
+ if index < -2147483648 or 2147483647 < index
65
+ raise RangeError.new("integer #{index} too big to convert to `int'")
66
+ end
67
+ # If history_size is negative, history size is unlimited.
68
+ if @config.history_size.positive?
69
+ if index < -@config.history_size or @config.history_size < index
70
+ raise RangeError.new("index=<#{index}>")
71
+ end
72
+ end
53
73
  raise IndexError.new("index=<#{index}>") if index < 0 or size <= index
54
74
  index
55
75
  end
@@ -37,9 +37,9 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
37
37
  # 17 ^Q
38
38
  :ed_quoted_insert,
39
39
  # 18 ^R
40
- :ed_search_prev_history,
40
+ :vi_search_prev,
41
41
  # 19 ^S
42
- :ed_search_next_history,
42
+ :vi_search_next,
43
43
  # 20 ^T
44
44
  :ed_transpose_chars,
45
45
  # 21 ^U
@@ -413,11 +413,11 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
413
413
  # 205 M-M
414
414
  :ed_unassigned,
415
415
  # 206 M-N
416
- :ed_search_next_history,
416
+ :vi_search_next,
417
417
  # 207 M-O
418
418
  :ed_sequence_lead_in,
419
419
  # 208 M-P
420
- :ed_search_prev_history,
420
+ :vi_search_prev,
421
421
  # 209 M-Q
422
422
  :ed_unassigned,
423
423
  # 210 M-R
@@ -477,11 +477,11 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
477
477
  # 237 M-m
478
478
  :ed_unassigned,
479
479
  # 238 M-n
480
- :ed_search_next_history,
480
+ :vi_search_next,
481
481
  # 239 M-o
482
482
  :ed_unassigned,
483
483
  # 240 M-p
484
- :ed_search_prev_history,
484
+ :vi_search_prev,
485
485
  # 241 M-q
486
486
  :ed_unassigned,
487
487
  # 242 M-r