reline 0.3.9 → 0.6.1
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 +8 -1
- data/lib/reline/config.rb +95 -123
- data/lib/reline/face.rb +199 -0
- data/lib/reline/history.rb +4 -4
- data/lib/reline/io/ansi.rb +318 -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 +75 -104
- data/lib/reline/kill_ring.rb +2 -2
- data/lib/reline/line_editor.rb +1175 -2121
- data/lib/reline/unicode/east_asian_width.rb +1289 -1192
- data/lib/reline/unicode.rb +218 -445
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +143 -224
- metadata +13 -11
- data/lib/reline/ansi.rb +0 -363
- data/lib/reline/general_io.rb +0 -116
- 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: 2fb74f487afccdfb3b0530a043cfb7090f2a4860f2fdd168bc94112818b5f57d
|
4
|
+
data.tar.gz: 96b74e0f611f24022f204e9551d8692ef017c52a197ae169fbfdc1074a130857
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffdcec13818f2445bd9f639e75b36405c76a439378335fa7c05fad90a26f06965e54cd9dce1b8e1411e16df9ffc80c3c0ac356320654af2ebd40a33fd28cb247
|
7
|
+
data.tar.gz: 24be10266aebce2d07f1519010f6ce0e6b92b816b17e526ede204943880b54388aa5ebf5da086bde0212b5e1f837936e4972c09a57a2d6c94f7c9a41412966d9
|
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,13 @@ 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
|
+
|
58
65
|
## Contributing
|
59
66
|
|
60
67
|
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/reline.
|
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,9 +174,7 @@ 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|
|
198
180
|
next if line.match(/\A\s*#/)
|
@@ -201,62 +183,68 @@ class Reline::Config
|
|
201
183
|
|
202
184
|
line = line.chomp.lstrip
|
203
185
|
if line.start_with?('$')
|
204
|
-
handle_directive(line[1..-1], file, no)
|
186
|
+
handle_directive(line[1..-1], file, no, if_stack)
|
205
187
|
next
|
206
188
|
end
|
207
189
|
|
208
|
-
next if
|
190
|
+
next if if_stack.any? { |_no, skip| skip }
|
209
191
|
|
210
192
|
case line
|
211
|
-
when /^set +([^ ]+) +(
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
when
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
193
|
+
when /^set +([^ ]+) +(.+)/i
|
194
|
+
# value ignores everything after a space, raw_value does not.
|
195
|
+
var, value, raw_value = $1.downcase, $2.partition(' ').first, $2
|
196
|
+
bind_variable(var, value, raw_value)
|
197
|
+
when /^\s*(?:M|Meta)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
198
|
+
bind_key("\"\\M-#$1\"", $2)
|
199
|
+
when /^\s*(?:C|Control)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
200
|
+
bind_key("\"\\C-#$1\"", $2)
|
201
|
+
when /^\s*(?:(?:C|Control)-(?:M|Meta)|(?:M|Meta)-(?:C|Control))-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
202
|
+
bind_key("\"\\M-\\C-#$1\"", $2)
|
203
|
+
when /^\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
|
204
|
+
bind_key($1, $2)
|
220
205
|
end
|
221
206
|
end
|
222
|
-
unless
|
223
|
-
raise InvalidInputrc, "#{file}:#{
|
207
|
+
unless if_stack.empty?
|
208
|
+
raise InvalidInputrc, "#{file}:#{if_stack.last[0]}: unclosed if"
|
224
209
|
end
|
225
|
-
ensure
|
226
|
-
@skip_section, @if_stack = conditions
|
227
210
|
end
|
228
211
|
|
229
|
-
def handle_directive(directive, file, no)
|
212
|
+
def handle_directive(directive, file, no, if_stack)
|
230
213
|
directive, args = directive.split(' ')
|
231
214
|
case directive
|
232
215
|
when 'if'
|
233
216
|
condition = false
|
234
217
|
case args
|
235
|
-
when
|
218
|
+
when /^mode=(vi|emacs)$/i
|
219
|
+
mode = $1.downcase
|
220
|
+
# NOTE: mode=vi means vi-insert mode
|
221
|
+
mode = 'vi_insert' if mode == 'vi'
|
222
|
+
if @editing_mode_label == mode.to_sym
|
223
|
+
condition = true
|
224
|
+
end
|
236
225
|
when 'term'
|
237
226
|
when 'version'
|
238
227
|
else # application name
|
239
228
|
condition = true if args == 'Ruby'
|
240
229
|
condition = true if args == 'Reline'
|
241
230
|
end
|
242
|
-
|
243
|
-
@skip_section = !condition
|
231
|
+
if_stack << [no, !condition]
|
244
232
|
when 'else'
|
245
|
-
if
|
233
|
+
if if_stack.empty?
|
246
234
|
raise InvalidInputrc, "#{file}:#{no}: unmatched else"
|
247
235
|
end
|
248
|
-
|
236
|
+
if_stack.last[1] = !if_stack.last[1]
|
249
237
|
when 'endif'
|
250
|
-
if
|
238
|
+
if if_stack.empty?
|
251
239
|
raise InvalidInputrc, "#{file}:#{no}: unmatched endif"
|
252
240
|
end
|
253
|
-
|
241
|
+
if_stack.pop
|
254
242
|
when 'include'
|
255
243
|
read(File.expand_path(args))
|
256
244
|
end
|
257
245
|
end
|
258
246
|
|
259
|
-
def bind_variable(name, value)
|
247
|
+
def bind_variable(name, value, raw_value)
|
260
248
|
case name
|
261
249
|
when 'history-size'
|
262
250
|
begin
|
@@ -264,24 +252,8 @@ class Reline::Config
|
|
264
252
|
rescue ArgumentError
|
265
253
|
@history_size = 500
|
266
254
|
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
255
|
when 'isearch-terminators'
|
284
|
-
@isearch_terminators = retrieve_string(
|
256
|
+
@isearch_terminators = retrieve_string(raw_value)
|
285
257
|
when 'editing-mode'
|
286
258
|
case value
|
287
259
|
when 'emacs'
|
@@ -323,11 +295,11 @@ class Reline::Config
|
|
323
295
|
@show_mode_in_prompt = false
|
324
296
|
end
|
325
297
|
when 'vi-cmd-mode-string'
|
326
|
-
@vi_cmd_mode_string = retrieve_string(
|
298
|
+
@vi_cmd_mode_string = retrieve_string(raw_value)
|
327
299
|
when 'vi-ins-mode-string'
|
328
|
-
@vi_ins_mode_string = retrieve_string(
|
300
|
+
@vi_ins_mode_string = retrieve_string(raw_value)
|
329
301
|
when 'emacs-mode-string'
|
330
|
-
@emacs_mode_string = retrieve_string(
|
302
|
+
@emacs_mode_string = retrieve_string(raw_value)
|
331
303
|
when *VARIABLE_NAMES then
|
332
304
|
variable_name = :"@#{name.tr(?-, ?_)}"
|
333
305
|
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
|
@@ -339,7 +311,12 @@ class Reline::Config
|
|
339
311
|
parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join
|
340
312
|
end
|
341
313
|
|
342
|
-
def bind_key(key,
|
314
|
+
def bind_key(key, value)
|
315
|
+
keystroke, func = parse_key_binding(key, value)
|
316
|
+
@additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func) if keystroke
|
317
|
+
end
|
318
|
+
|
319
|
+
def parse_key_binding(key, func_name)
|
343
320
|
if key =~ /\A"(.*)"\z/
|
344
321
|
keyseq = parse_keyseq($1)
|
345
322
|
else
|
@@ -348,27 +325,19 @@ class Reline::Config
|
|
348
325
|
if func_name =~ /"(.*)"/
|
349
326
|
func = parse_keyseq($1)
|
350
327
|
else
|
351
|
-
func = func_name.tr(?-, ?_).to_sym # It must be macro.
|
328
|
+
func = func_name.split.first.tr(?-, ?_).to_sym # It must be macro.
|
352
329
|
end
|
353
330
|
[keyseq, func]
|
354
331
|
end
|
355
332
|
|
356
333
|
def key_notation_to_code(notation)
|
357
334
|
case notation
|
335
|
+
when /(?:\\(?:C|Control)-\\(?:M|Meta)|\\(?:M|Meta)-\\(?:C|Control))-([A-Za-z_])/
|
336
|
+
[?\e.ord, $1.ord % 32]
|
358
337
|
when /\\(?:C|Control)-([A-Za-z_])/
|
359
|
-
(
|
338
|
+
($1.upcase.ord % 32)
|
360
339
|
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
|
340
|
+
[?\e.ord, $1.ord]
|
372
341
|
when /\\(\d{1,3})/ then $1.to_i(8) # octal
|
373
342
|
when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
|
374
343
|
when "\\e" then ?\e.ord
|
@@ -388,11 +357,14 @@ class Reline::Config
|
|
388
357
|
end
|
389
358
|
|
390
359
|
def parse_keyseq(str)
|
391
|
-
|
392
|
-
|
393
|
-
ret << key_notation_to_code($&)
|
360
|
+
str.scan(KEYSEQ_PATTERN).flat_map do |notation|
|
361
|
+
key_notation_to_code(notation)
|
394
362
|
end
|
395
|
-
|
363
|
+
end
|
364
|
+
|
365
|
+
def reload
|
366
|
+
reset_variables
|
367
|
+
read
|
396
368
|
end
|
397
369
|
|
398
370
|
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?
|