reline 0.1.3 → 0.1.8

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: 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