reline 0.3.5 → 0.6.2
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 +4 -4
- data/README.md +31 -1
- data/lib/reline/config.rb +101 -124
- data/lib/reline/face.rb +199 -0
- data/lib/reline/history.rb +4 -4
- data/lib/reline/io/ansi.rb +322 -0
- data/lib/reline/io/dumb.rb +120 -0
- data/lib/reline/{windows.rb → io/windows.rb} +182 -153
- data/lib/reline/io.rb +55 -0
- data/lib/reline/key_actor/base.rb +27 -9
- data/lib/reline/key_actor/composite.rb +17 -0
- data/lib/reline/key_actor/emacs.rb +103 -103
- data/lib/reline/key_actor/vi_command.rb +188 -188
- data/lib/reline/key_actor/vi_insert.rb +144 -144
- data/lib/reline/key_actor.rb +1 -0
- data/lib/reline/key_stroke.rb +94 -80
- data/lib/reline/kill_ring.rb +2 -2
- data/lib/reline/line_editor.rb +1177 -2110
- data/lib/reline/unicode/east_asian_width.rb +1288 -1192
- data/lib/reline/unicode.rb +224 -465
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +167 -247
- metadata +13 -11
- data/lib/reline/ansi.rb +0 -357
- data/lib/reline/general_io.rb +0 -113
- data/lib/reline/terminfo.rb +0 -160
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec7aa526103ceb5eff3fa7dbbfff8c33f296f9cfb28ec4cc925b28e649dc18c8
|
4
|
+
data.tar.gz: 5c86a58b7769f390a81e9f6ca19a1e64eefad4ad05091544e5a01ef752b21983
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4e586ed0f631e402d0b4b58fb05f0eec016f1290624021e957de1ced06c15e8638dac34e91c72d4b53a83e2c6b5eb5aadec2b749271d47201773dd220c63827
|
7
|
+
data.tar.gz: 6dcd53d5a30722bb4884f79eb9c70f49ab267b5e285b231237d339a52efdaa597377d308a264ed2cef23802b27aabb38d5ccbfd0c2dce40d631e15a6da5ede68
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ Reline is compatible with the API of Ruby's stdlib 'readline', GNU Readline and
|
|
15
15
|
|
16
16
|
It's compatible with the readline standard library.
|
17
17
|
|
18
|
-
See [the document of readline stdlib](https://ruby-doc.org/stdlib/
|
18
|
+
See [the document of readline stdlib](https://ruby-doc.org/stdlib/exts/readline/Readline.html) or [bin/example](https://github.com/ruby/reline/blob/master/bin/example).
|
19
19
|
|
20
20
|
### Multi-line editing mode
|
21
21
|
|
@@ -55,6 +55,36 @@ end
|
|
55
55
|
|
56
56
|
See also: [test/reline/yamatanooroti/multiline_repl](https://github.com/ruby/reline/blob/master/test/reline/yamatanooroti/multiline_repl)
|
57
57
|
|
58
|
+
## Documentation
|
59
|
+
|
60
|
+
### Reline::Face
|
61
|
+
|
62
|
+
You can modify the text color and text decorations in your terminal emulator.
|
63
|
+
See [doc/reline/face.md](./doc/reline/face.md)
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/reline.
|
68
|
+
|
69
|
+
### Run tests
|
70
|
+
|
71
|
+
> **Note**
|
72
|
+
> Please make sure you have `libvterm` installed for `yamatanooroti` tests (integration tests).
|
73
|
+
|
74
|
+
If you use Homebrew, you can install it by running `brew install libvterm`.
|
75
|
+
|
76
|
+
```bash
|
77
|
+
WITH_VTERM=1 bundle install
|
78
|
+
WITH_VTERM=1 bundle exec rake test test_yamatanooroti
|
79
|
+
```
|
80
|
+
|
81
|
+
## Releasing
|
82
|
+
|
83
|
+
```bash
|
84
|
+
rake release
|
85
|
+
gh release create vX.Y.Z --generate-notes
|
86
|
+
```
|
87
|
+
|
58
88
|
## License
|
59
89
|
|
60
90
|
The gem is available as open source under the terms of the [Ruby License](https://www.ruby-lang.org/en/about/license.txt).
|
data/lib/reline/config.rb
CHANGED
@@ -1,38 +1,19 @@
|
|
1
1
|
class Reline::Config
|
2
2
|
attr_reader :test_mode
|
3
3
|
|
4
|
-
KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)
|
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}|./
|
5
5
|
|
6
6
|
class InvalidInputrc < RuntimeError
|
7
7
|
attr_accessor :file, :lineno
|
8
8
|
end
|
9
9
|
|
10
10
|
VARIABLE_NAMES = %w{
|
11
|
-
bind-tty-special-chars
|
12
|
-
blink-matching-paren
|
13
|
-
byte-oriented
|
14
11
|
completion-ignore-case
|
15
12
|
convert-meta
|
16
13
|
disable-completion
|
17
|
-
enable-keypad
|
18
|
-
expand-tilde
|
19
|
-
history-preserve-point
|
20
14
|
history-size
|
21
|
-
horizontal-scroll-mode
|
22
|
-
input-meta
|
23
15
|
keyseq-timeout
|
24
|
-
mark-directories
|
25
|
-
mark-modified-lines
|
26
|
-
mark-symlinked-directories
|
27
|
-
match-hidden-files
|
28
|
-
meta-flag
|
29
|
-
output-meta
|
30
|
-
page-completions
|
31
|
-
prefer-visible-bell
|
32
|
-
print-completions-horizontally
|
33
16
|
show-all-if-ambiguous
|
34
|
-
show-all-if-unmodified
|
35
|
-
visible-stats
|
36
17
|
show-mode-in-prompt
|
37
18
|
vi-cmd-mode-string
|
38
19
|
vi-ins-mode-string
|
@@ -48,20 +29,31 @@ class Reline::Config
|
|
48
29
|
attr_accessor :autocompletion
|
49
30
|
|
50
31
|
def initialize
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
32
|
+
reset_variables
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset
|
36
|
+
if editing_mode_is?(:vi_command)
|
37
|
+
@editing_mode_label = :vi_insert
|
38
|
+
end
|
39
|
+
@oneshot_key_bindings.clear
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset_variables
|
43
|
+
@additional_key_bindings = { # from inputrc
|
44
|
+
emacs: Reline::KeyActor::Base.new,
|
45
|
+
vi_insert: Reline::KeyActor::Base.new,
|
46
|
+
vi_command: Reline::KeyActor::Base.new
|
47
|
+
}
|
48
|
+
@oneshot_key_bindings = Reline::KeyActor::Base.new
|
58
49
|
@editing_mode_label = :emacs
|
59
50
|
@keymap_label = :emacs
|
60
51
|
@keymap_prefix = []
|
61
|
-
@
|
62
|
-
|
63
|
-
|
64
|
-
|
52
|
+
@default_key_bindings = {
|
53
|
+
emacs: Reline::KeyActor::Base.new(Reline::KeyActor::EMACS_MAPPING),
|
54
|
+
vi_insert: Reline::KeyActor::Base.new(Reline::KeyActor::VI_INSERT_MAPPING),
|
55
|
+
vi_command: Reline::KeyActor::Base.new(Reline::KeyActor::VI_COMMAND_MAPPING)
|
56
|
+
}
|
65
57
|
@vi_cmd_mode_string = '(cmd)'
|
66
58
|
@vi_ins_mode_string = '(ins)'
|
67
59
|
@emacs_mode_string = '@'
|
@@ -70,22 +62,15 @@ class Reline::Config
|
|
70
62
|
@keyseq_timeout = 500
|
71
63
|
@test_mode = false
|
72
64
|
@autocompletion = false
|
73
|
-
@convert_meta =
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
@editing_mode_label = :vi_insert
|
79
|
-
end
|
80
|
-
@additional_key_bindings.keys.each do |key|
|
81
|
-
@additional_key_bindings[key].clear
|
82
|
-
end
|
83
|
-
@oneshot_key_bindings.clear
|
84
|
-
reset_default_key_bindings
|
65
|
+
@convert_meta = seven_bit_encoding?(Reline::IOGate.encoding)
|
66
|
+
@loaded = false
|
67
|
+
@enable_bracketed_paste = true
|
68
|
+
@show_mode_in_prompt = false
|
69
|
+
@default_inputrc_path = nil
|
85
70
|
end
|
86
71
|
|
87
72
|
def editing_mode
|
88
|
-
@
|
73
|
+
@default_key_bindings[@editing_mode_label]
|
89
74
|
end
|
90
75
|
|
91
76
|
def editing_mode=(val)
|
@@ -97,7 +82,11 @@ class Reline::Config
|
|
97
82
|
end
|
98
83
|
|
99
84
|
def keymap
|
100
|
-
@
|
85
|
+
@default_key_bindings[@keymap_label]
|
86
|
+
end
|
87
|
+
|
88
|
+
def loaded?
|
89
|
+
@loaded
|
101
90
|
end
|
102
91
|
|
103
92
|
def inputrc_path
|
@@ -131,6 +120,7 @@ class Reline::Config
|
|
131
120
|
end
|
132
121
|
|
133
122
|
def read(file = nil)
|
123
|
+
@loaded = true
|
134
124
|
file ||= default_inputrc_path
|
135
125
|
begin
|
136
126
|
if file.respond_to?(:readlines)
|
@@ -151,14 +141,14 @@ class Reline::Config
|
|
151
141
|
|
152
142
|
def key_bindings
|
153
143
|
# The key bindings for each editing mode will be overwritten by the user-defined ones.
|
154
|
-
|
155
|
-
kb.merge!(@additional_key_bindings[@editing_mode_label])
|
156
|
-
kb.merge!(@oneshot_key_bindings)
|
157
|
-
kb
|
144
|
+
Reline::KeyActor::Composite.new([@oneshot_key_bindings, @additional_key_bindings[@editing_mode_label], @default_key_bindings[@editing_mode_label]])
|
158
145
|
end
|
159
146
|
|
160
147
|
def add_oneshot_key_binding(keystroke, target)
|
161
|
-
|
148
|
+
# IRB sets invalid keystroke [Reline::Key]. We should ignore it.
|
149
|
+
return unless keystroke.all? { |c| c.is_a?(Integer) }
|
150
|
+
|
151
|
+
@oneshot_key_bindings.add(keystroke, target)
|
162
152
|
end
|
163
153
|
|
164
154
|
def reset_oneshot_key_bindings
|
@@ -166,17 +156,11 @@ class Reline::Config
|
|
166
156
|
end
|
167
157
|
|
168
158
|
def add_default_key_binding_by_keymap(keymap, keystroke, target)
|
169
|
-
@
|
159
|
+
@default_key_bindings[keymap].add(keystroke, target)
|
170
160
|
end
|
171
161
|
|
172
162
|
def add_default_key_binding(keystroke, target)
|
173
|
-
@
|
174
|
-
end
|
175
|
-
|
176
|
-
def reset_default_key_bindings
|
177
|
-
@key_actors.values.each do |ka|
|
178
|
-
ka.reset_default_key_bindings
|
179
|
-
end
|
163
|
+
add_default_key_binding_by_keymap(@keymap_label, keystroke, target)
|
180
164
|
end
|
181
165
|
|
182
166
|
def read_lines(lines, file = nil)
|
@@ -190,73 +174,82 @@ class Reline::Config
|
|
190
174
|
end
|
191
175
|
end
|
192
176
|
end
|
193
|
-
|
194
|
-
@skip_section = nil
|
195
|
-
@if_stack = []
|
177
|
+
if_stack = []
|
196
178
|
|
197
179
|
lines.each_with_index do |line, no|
|
180
|
+
# Even after encoding conversion, we need to verify the encoding is valid
|
181
|
+
# as some invalid byte sequences might pass through the conversion.
|
182
|
+
unless line.valid_encoding?
|
183
|
+
raise InvalidInputrc, "#{file}:#{no + 1}: can't be converted to the locale #{Reline.encoding_system_needs.name}"
|
184
|
+
end
|
198
185
|
next if line.match(/\A\s*#/)
|
199
186
|
|
200
187
|
no += 1
|
201
188
|
|
202
189
|
line = line.chomp.lstrip
|
203
190
|
if line.start_with?('$')
|
204
|
-
handle_directive(line[1..-1], file, no)
|
191
|
+
handle_directive(line[1..-1], file, no, if_stack)
|
205
192
|
next
|
206
193
|
end
|
207
194
|
|
208
|
-
next if
|
195
|
+
next if if_stack.any? { |_no, skip| skip }
|
209
196
|
|
210
197
|
case line
|
211
|
-
when /^set +([^ ]+) +(
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
when
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
198
|
+
when /^set +([^ ]+) +(.+)/i
|
199
|
+
# value ignores everything after a space, raw_value does not.
|
200
|
+
var, value, raw_value = $1.downcase, $2.partition(' ').first, $2
|
201
|
+
bind_variable(var, value, raw_value)
|
202
|
+
when /^\s*(?:M|Meta)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
203
|
+
bind_key("\"\\M-#$1\"", $2)
|
204
|
+
when /^\s*(?:C|Control)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
205
|
+
bind_key("\"\\C-#$1\"", $2)
|
206
|
+
when /^\s*(?:(?:C|Control)-(?:M|Meta)|(?:M|Meta)-(?:C|Control))-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
207
|
+
bind_key("\"\\M-\\C-#$1\"", $2)
|
208
|
+
when /^\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
|
209
|
+
bind_key($1, $2)
|
220
210
|
end
|
221
211
|
end
|
222
|
-
unless
|
223
|
-
raise InvalidInputrc, "#{file}:#{
|
212
|
+
unless if_stack.empty?
|
213
|
+
raise InvalidInputrc, "#{file}:#{if_stack.last[0]}: unclosed if"
|
224
214
|
end
|
225
|
-
ensure
|
226
|
-
@skip_section, @if_stack = conditions
|
227
215
|
end
|
228
216
|
|
229
|
-
def handle_directive(directive, file, no)
|
217
|
+
def handle_directive(directive, file, no, if_stack)
|
230
218
|
directive, args = directive.split(' ')
|
231
219
|
case directive
|
232
220
|
when 'if'
|
233
221
|
condition = false
|
234
222
|
case args
|
235
|
-
when
|
223
|
+
when /^mode=(vi|emacs)$/i
|
224
|
+
mode = $1.downcase
|
225
|
+
# NOTE: mode=vi means vi-insert mode
|
226
|
+
mode = 'vi_insert' if mode == 'vi'
|
227
|
+
if @editing_mode_label == mode.to_sym
|
228
|
+
condition = true
|
229
|
+
end
|
236
230
|
when 'term'
|
237
231
|
when 'version'
|
238
232
|
else # application name
|
239
233
|
condition = true if args == 'Ruby'
|
240
234
|
condition = true if args == 'Reline'
|
241
235
|
end
|
242
|
-
|
243
|
-
@skip_section = !condition
|
236
|
+
if_stack << [no, !condition]
|
244
237
|
when 'else'
|
245
|
-
if
|
238
|
+
if if_stack.empty?
|
246
239
|
raise InvalidInputrc, "#{file}:#{no}: unmatched else"
|
247
240
|
end
|
248
|
-
|
241
|
+
if_stack.last[1] = !if_stack.last[1]
|
249
242
|
when 'endif'
|
250
|
-
if
|
243
|
+
if if_stack.empty?
|
251
244
|
raise InvalidInputrc, "#{file}:#{no}: unmatched endif"
|
252
245
|
end
|
253
|
-
|
246
|
+
if_stack.pop
|
254
247
|
when 'include'
|
255
|
-
read(args)
|
248
|
+
read(File.expand_path(args))
|
256
249
|
end
|
257
250
|
end
|
258
251
|
|
259
|
-
def bind_variable(name, value)
|
252
|
+
def bind_variable(name, value, raw_value)
|
260
253
|
case name
|
261
254
|
when 'history-size'
|
262
255
|
begin
|
@@ -264,24 +257,8 @@ class Reline::Config
|
|
264
257
|
rescue ArgumentError
|
265
258
|
@history_size = 500
|
266
259
|
end
|
267
|
-
when 'bell-style'
|
268
|
-
@bell_style =
|
269
|
-
case value
|
270
|
-
when 'none', 'off'
|
271
|
-
:none
|
272
|
-
when 'audible', 'on'
|
273
|
-
:audible
|
274
|
-
when 'visible'
|
275
|
-
:visible
|
276
|
-
else
|
277
|
-
:audible
|
278
|
-
end
|
279
|
-
when 'comment-begin'
|
280
|
-
@comment_begin = value.dup
|
281
|
-
when 'completion-query-items'
|
282
|
-
@completion_query_items = value.to_i
|
283
260
|
when 'isearch-terminators'
|
284
|
-
@isearch_terminators = retrieve_string(
|
261
|
+
@isearch_terminators = retrieve_string(raw_value)
|
285
262
|
when 'editing-mode'
|
286
263
|
case value
|
287
264
|
when 'emacs'
|
@@ -323,11 +300,11 @@ class Reline::Config
|
|
323
300
|
@show_mode_in_prompt = false
|
324
301
|
end
|
325
302
|
when 'vi-cmd-mode-string'
|
326
|
-
@vi_cmd_mode_string = retrieve_string(
|
303
|
+
@vi_cmd_mode_string = retrieve_string(raw_value)
|
327
304
|
when 'vi-ins-mode-string'
|
328
|
-
@vi_ins_mode_string = retrieve_string(
|
305
|
+
@vi_ins_mode_string = retrieve_string(raw_value)
|
329
306
|
when 'emacs-mode-string'
|
330
|
-
@emacs_mode_string = retrieve_string(
|
307
|
+
@emacs_mode_string = retrieve_string(raw_value)
|
331
308
|
when *VARIABLE_NAMES then
|
332
309
|
variable_name = :"@#{name.tr(?-, ?_)}"
|
333
310
|
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
|
@@ -339,7 +316,12 @@ class Reline::Config
|
|
339
316
|
parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join
|
340
317
|
end
|
341
318
|
|
342
|
-
def bind_key(key,
|
319
|
+
def bind_key(key, value)
|
320
|
+
keystroke, func = parse_key_binding(key, value)
|
321
|
+
@additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func) if keystroke
|
322
|
+
end
|
323
|
+
|
324
|
+
def parse_key_binding(key, func_name)
|
343
325
|
if key =~ /\A"(.*)"\z/
|
344
326
|
keyseq = parse_keyseq($1)
|
345
327
|
else
|
@@ -348,27 +330,19 @@ class Reline::Config
|
|
348
330
|
if func_name =~ /"(.*)"/
|
349
331
|
func = parse_keyseq($1)
|
350
332
|
else
|
351
|
-
func = func_name.tr(?-, ?_).to_sym # It must be macro.
|
333
|
+
func = func_name.split.first.tr(?-, ?_).to_sym # It must be macro.
|
352
334
|
end
|
353
335
|
[keyseq, func]
|
354
336
|
end
|
355
337
|
|
356
338
|
def key_notation_to_code(notation)
|
357
339
|
case notation
|
340
|
+
when /(?:\\(?:C|Control)-\\(?:M|Meta)|\\(?:M|Meta)-\\(?:C|Control))-([A-Za-z_])/
|
341
|
+
[?\e.ord, $1.ord % 32]
|
358
342
|
when /\\(?:C|Control)-([A-Za-z_])/
|
359
|
-
(
|
343
|
+
($1.upcase.ord % 32)
|
360
344
|
when /\\(?:M|Meta)-([0-9A-Za-z_])/
|
361
|
-
|
362
|
-
case $1
|
363
|
-
when /[0-9]/
|
364
|
-
?\M-0.bytes.first + (modified_key.ord - ?0.ord)
|
365
|
-
when /[A-Z]/
|
366
|
-
?\M-A.bytes.first + (modified_key.ord - ?A.ord)
|
367
|
-
when /[a-z]/
|
368
|
-
?\M-a.bytes.first + (modified_key.ord - ?a.ord)
|
369
|
-
end
|
370
|
-
when /\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]/, /\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]/
|
371
|
-
# 129 M-^A
|
345
|
+
[?\e.ord, $1.ord]
|
372
346
|
when /\\(\d{1,3})/ then $1.to_i(8) # octal
|
373
347
|
when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
|
374
348
|
when "\\e" then ?\e.ord
|
@@ -388,11 +362,14 @@ class Reline::Config
|
|
388
362
|
end
|
389
363
|
|
390
364
|
def parse_keyseq(str)
|
391
|
-
|
392
|
-
|
393
|
-
ret << key_notation_to_code($&)
|
365
|
+
str.scan(KEYSEQ_PATTERN).flat_map do |notation|
|
366
|
+
key_notation_to_code(notation)
|
394
367
|
end
|
395
|
-
|
368
|
+
end
|
369
|
+
|
370
|
+
def reload
|
371
|
+
reset_variables
|
372
|
+
read
|
396
373
|
end
|
397
374
|
|
398
375
|
private def seven_bit_encoding?(encoding)
|
data/lib/reline/face.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Reline::Face
|
4
|
+
SGR_PARAMETERS = {
|
5
|
+
foreground: {
|
6
|
+
black: 30,
|
7
|
+
red: 31,
|
8
|
+
green: 32,
|
9
|
+
yellow: 33,
|
10
|
+
blue: 34,
|
11
|
+
magenta: 35,
|
12
|
+
cyan: 36,
|
13
|
+
white: 37,
|
14
|
+
bright_black: 90,
|
15
|
+
gray: 90,
|
16
|
+
bright_red: 91,
|
17
|
+
bright_green: 92,
|
18
|
+
bright_yellow: 93,
|
19
|
+
bright_blue: 94,
|
20
|
+
bright_magenta: 95,
|
21
|
+
bright_cyan: 96,
|
22
|
+
bright_white: 97
|
23
|
+
},
|
24
|
+
background: {
|
25
|
+
black: 40,
|
26
|
+
red: 41,
|
27
|
+
green: 42,
|
28
|
+
yellow: 43,
|
29
|
+
blue: 44,
|
30
|
+
magenta: 45,
|
31
|
+
cyan: 46,
|
32
|
+
white: 47,
|
33
|
+
bright_black: 100,
|
34
|
+
gray: 100,
|
35
|
+
bright_red: 101,
|
36
|
+
bright_green: 102,
|
37
|
+
bright_yellow: 103,
|
38
|
+
bright_blue: 104,
|
39
|
+
bright_magenta: 105,
|
40
|
+
bright_cyan: 106,
|
41
|
+
bright_white: 107,
|
42
|
+
},
|
43
|
+
style: {
|
44
|
+
reset: 0,
|
45
|
+
bold: 1,
|
46
|
+
faint: 2,
|
47
|
+
italicized: 3,
|
48
|
+
underlined: 4,
|
49
|
+
slowly_blinking: 5,
|
50
|
+
blinking: 5,
|
51
|
+
rapidly_blinking: 6,
|
52
|
+
negative: 7,
|
53
|
+
concealed: 8,
|
54
|
+
crossed_out: 9
|
55
|
+
}
|
56
|
+
}.freeze
|
57
|
+
|
58
|
+
class Config
|
59
|
+
ESSENTIAL_DEFINE_NAMES = %i(default enhanced scrollbar).freeze
|
60
|
+
RESET_SGR = "\e[0m".freeze
|
61
|
+
|
62
|
+
def initialize(name, &block)
|
63
|
+
@definition = {}
|
64
|
+
block.call(self)
|
65
|
+
ESSENTIAL_DEFINE_NAMES.each do |name|
|
66
|
+
@definition[name] ||= { style: :reset, escape_sequence: RESET_SGR }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_reader :definition
|
71
|
+
|
72
|
+
def define(name, **values)
|
73
|
+
values[:escape_sequence] = format_to_sgr(values.to_a).freeze
|
74
|
+
@definition[name] = values
|
75
|
+
end
|
76
|
+
|
77
|
+
def reconfigure
|
78
|
+
@definition.each_value do |values|
|
79
|
+
values.delete(:escape_sequence)
|
80
|
+
values[:escape_sequence] = format_to_sgr(values.to_a).freeze
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def [](name)
|
85
|
+
@definition.dig(name, :escape_sequence) or raise ArgumentError, "unknown face: #{name}"
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def sgr_rgb(key, value)
|
91
|
+
return nil unless rgb_expression?(value)
|
92
|
+
if Reline::Face.truecolor?
|
93
|
+
sgr_rgb_truecolor(key, value)
|
94
|
+
else
|
95
|
+
sgr_rgb_256color(key, value)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def sgr_rgb_truecolor(key, value)
|
100
|
+
case key
|
101
|
+
when :foreground
|
102
|
+
"38;2;"
|
103
|
+
when :background
|
104
|
+
"48;2;"
|
105
|
+
end + value[1, 6].scan(/../).map(&:hex).join(";")
|
106
|
+
end
|
107
|
+
|
108
|
+
def sgr_rgb_256color(key, value)
|
109
|
+
# 256 colors are
|
110
|
+
# 0..15: standard colors, high intensity colors
|
111
|
+
# 16..232: 216 colors (R, G, B each 6 steps)
|
112
|
+
# 233..255: grayscale colors (24 steps)
|
113
|
+
# This methods converts rgb_expression to 216 colors
|
114
|
+
rgb = value[1, 6].scan(/../).map(&:hex)
|
115
|
+
# Color steps are [0, 95, 135, 175, 215, 255]
|
116
|
+
r, g, b = rgb.map { |v| v <= 95 ? v / 48 : (v - 35) / 40 }
|
117
|
+
color = (16 + 36 * r + 6 * g + b)
|
118
|
+
case key
|
119
|
+
when :foreground
|
120
|
+
"38;5;#{color}"
|
121
|
+
when :background
|
122
|
+
"48;5;#{color}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def format_to_sgr(ordered_values)
|
127
|
+
sgr = "\e[" + ordered_values.map do |key_value|
|
128
|
+
key, value = key_value
|
129
|
+
case key
|
130
|
+
when :foreground, :background
|
131
|
+
case value
|
132
|
+
when Symbol
|
133
|
+
SGR_PARAMETERS[key][value]
|
134
|
+
when String
|
135
|
+
sgr_rgb(key, value)
|
136
|
+
end
|
137
|
+
when :style
|
138
|
+
[ value ].flatten.map do |style_name|
|
139
|
+
SGR_PARAMETERS[:style][style_name]
|
140
|
+
end.then do |sgr_parameters|
|
141
|
+
sgr_parameters.include?(nil) ? nil : sgr_parameters
|
142
|
+
end
|
143
|
+
end.then do |rendition_expression|
|
144
|
+
unless rendition_expression
|
145
|
+
raise ArgumentError, "invalid SGR parameter: #{value.inspect}"
|
146
|
+
end
|
147
|
+
rendition_expression
|
148
|
+
end
|
149
|
+
end.join(';') + "m"
|
150
|
+
sgr == RESET_SGR ? RESET_SGR : RESET_SGR + sgr
|
151
|
+
end
|
152
|
+
|
153
|
+
def rgb_expression?(color)
|
154
|
+
color.respond_to?(:match?) and color.match?(/\A#[0-9a-fA-F]{6}\z/)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private_constant :SGR_PARAMETERS, :Config
|
159
|
+
|
160
|
+
def self.truecolor?
|
161
|
+
@force_truecolor || %w[truecolor 24bit].include?(ENV['COLORTERM'])
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.force_truecolor
|
165
|
+
@force_truecolor = true
|
166
|
+
@configs&.each_value(&:reconfigure)
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.[](name)
|
170
|
+
@configs[name]
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.config(name, &block)
|
174
|
+
@configs ||= {}
|
175
|
+
@configs[name] = Config.new(name, &block)
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.configs
|
179
|
+
@configs.transform_values(&:definition)
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.load_initial_configs
|
183
|
+
config(:default) do |conf|
|
184
|
+
conf.define :default, style: :reset
|
185
|
+
conf.define :enhanced, style: :reset
|
186
|
+
conf.define :scrollbar, style: :reset
|
187
|
+
end
|
188
|
+
config(:completion_dialog) do |conf|
|
189
|
+
conf.define :default, foreground: :bright_white, background: :gray
|
190
|
+
conf.define :enhanced, foreground: :black, background: :white
|
191
|
+
conf.define :scrollbar, foreground: :white, background: :gray
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.reset_to_initial_configs
|
196
|
+
@configs = {}
|
197
|
+
load_initial_configs
|
198
|
+
end
|
199
|
+
end
|
data/lib/reline/history.rb
CHANGED
@@ -19,7 +19,7 @@ class Reline::History < Array
|
|
19
19
|
|
20
20
|
def []=(index, val)
|
21
21
|
index = check_index(index)
|
22
|
-
super(index,
|
22
|
+
super(index, Reline::Unicode.safe_encode(val, Reline.encoding_system_needs))
|
23
23
|
end
|
24
24
|
|
25
25
|
def concat(*val)
|
@@ -45,7 +45,7 @@ class Reline::History < Array
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
super(*(val.map{ |v|
|
48
|
-
|
48
|
+
Reline::Unicode.safe_encode(v, Reline.encoding_system_needs)
|
49
49
|
}))
|
50
50
|
end
|
51
51
|
|
@@ -56,13 +56,13 @@ class Reline::History < Array
|
|
56
56
|
if @config.history_size.positive?
|
57
57
|
shift if size + 1 > @config.history_size
|
58
58
|
end
|
59
|
-
super(
|
59
|
+
super(Reline::Unicode.safe_encode(val, Reline.encoding_system_needs))
|
60
60
|
end
|
61
61
|
|
62
62
|
private def check_index(index)
|
63
63
|
index += size if index < 0
|
64
64
|
if index < -2147483648 or 2147483647 < index
|
65
|
-
raise RangeError.new("integer #{index} too big to convert to
|
65
|
+
raise RangeError.new("integer #{index} too big to convert to 'int'")
|
66
66
|
end
|
67
67
|
# If history_size is negative, history size is unlimited.
|
68
68
|
if @config.history_size.positive?
|