reline 0.3.9 → 0.5.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 +4 -4
- data/README.md +8 -1
- data/lib/reline/ansi.rb +38 -39
- data/lib/reline/config.rb +35 -56
- data/lib/reline/face.rb +199 -0
- data/lib/reline/general_io.rb +4 -9
- data/lib/reline/history.rb +1 -1
- data/lib/reline/key_actor/base.rb +0 -4
- data/lib/reline/key_actor/emacs.rb +13 -13
- data/lib/reline/key_actor/vi_command.rb +23 -23
- data/lib/reline/key_actor/vi_insert.rb +5 -5
- data/lib/reline/kill_ring.rb +2 -2
- data/lib/reline/line_editor.rb +1038 -1784
- data/lib/reline/terminfo.rb +7 -14
- data/lib/reline/unicode.rb +66 -12
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +4 -2
- data/lib/reline.rb +75 -80
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcccc644594f9c2e4b9ae0eb743c2ae293ef084e96a8d9bd032b76825111c01b
|
4
|
+
data.tar.gz: b5ab239a3d20925d4fbd8fb827908fa3e4fa70298a172e32e9b2b4de59dc767e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bce894711da0253bf9b3f0eb192ff64947d591edd00f9094430431eb470fade4d02f3f8b529d364cdd7124d7f5a5203ea755be41dbfda890e37654aca430706
|
7
|
+
data.tar.gz: eed500ad148a1f83e3de03e6d7daaf5d250ba3ea25d47d24070e749dfd44a7d900b83474dd0b427faee74bad883dab97b7af1984a7fb7209e1819fd416d4381b
|
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/ansi.rb
CHANGED
@@ -3,6 +3,8 @@ require 'io/wait'
|
|
3
3
|
require_relative 'terminfo'
|
4
4
|
|
5
5
|
class Reline::ANSI
|
6
|
+
RESET_COLOR = "\e[0m"
|
7
|
+
|
6
8
|
CAPNAME_KEY_BINDINGS = {
|
7
9
|
'khome' => :ed_move_to_beg,
|
8
10
|
'kend' => :ed_move_to_end,
|
@@ -43,6 +45,7 @@ class Reline::ANSI
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def self.set_default_key_bindings(config, allow_terminfo: true)
|
48
|
+
set_bracketed_paste_key_bindings(config)
|
46
49
|
set_default_key_bindings_ansi_cursor(config)
|
47
50
|
if allow_terminfo && Reline::Terminfo.enabled?
|
48
51
|
set_default_key_bindings_terminfo(config)
|
@@ -64,6 +67,12 @@ class Reline::ANSI
|
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
70
|
+
def self.set_bracketed_paste_key_bindings(config)
|
71
|
+
[:emacs, :vi_insert, :vi_command].each do |keymap|
|
72
|
+
config.add_default_key_binding_by_keymap(keymap, START_BRACKETED_PASTE.bytes, :bracketed_paste_start)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
67
76
|
def self.set_default_key_bindings_ansi_cursor(config)
|
68
77
|
ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
|
69
78
|
bindings = [["\e[#{char}", default_func]] # CSI + char
|
@@ -149,7 +158,11 @@ class Reline::ANSI
|
|
149
158
|
end
|
150
159
|
|
151
160
|
def self.with_raw_input
|
152
|
-
@@input.
|
161
|
+
if @@input.tty?
|
162
|
+
@@input.raw(intr: true) { yield }
|
163
|
+
else
|
164
|
+
yield
|
165
|
+
end
|
153
166
|
end
|
154
167
|
|
155
168
|
@@buf = []
|
@@ -157,11 +170,13 @@ class Reline::ANSI
|
|
157
170
|
unless @@buf.empty?
|
158
171
|
return @@buf.shift
|
159
172
|
end
|
160
|
-
until
|
161
|
-
timeout_second -= 0.
|
173
|
+
until @@input.wait_readable(0.01)
|
174
|
+
timeout_second -= 0.01
|
162
175
|
return nil if timeout_second <= 0
|
163
|
-
|
176
|
+
|
177
|
+
Reline.core.line_editor.handle_signal
|
164
178
|
end
|
179
|
+
c = @@input.getbyte
|
165
180
|
(c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
|
166
181
|
rescue Errno::EIO
|
167
182
|
# Maybe the I/O has been closed.
|
@@ -170,46 +185,26 @@ class Reline::ANSI
|
|
170
185
|
nil
|
171
186
|
end
|
172
187
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
def self.getc_with_bracketed_paste(timeout_second)
|
188
|
+
START_BRACKETED_PASTE = String.new("\e[200~", encoding: Encoding::ASCII_8BIT)
|
189
|
+
END_BRACKETED_PASTE = String.new("\e[201~", encoding: Encoding::ASCII_8BIT)
|
190
|
+
def self.read_bracketed_paste
|
177
191
|
buffer = String.new(encoding: Encoding::ASCII_8BIT)
|
178
|
-
buffer
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
return inner_getc(timeout_second)
|
183
|
-
elsif END_BRACKETED_PASTE == buffer
|
184
|
-
@@in_bracketed_paste_mode = false
|
185
|
-
ungetc(-1)
|
186
|
-
return inner_getc(timeout_second)
|
187
|
-
end
|
188
|
-
succ_c = inner_getc(Reline.core.config.keyseq_timeout)
|
189
|
-
|
190
|
-
if succ_c
|
191
|
-
buffer << succ_c
|
192
|
-
else
|
193
|
-
break
|
194
|
-
end
|
192
|
+
until buffer.end_with?(END_BRACKETED_PASTE)
|
193
|
+
c = inner_getc(Float::INFINITY)
|
194
|
+
break unless c
|
195
|
+
buffer << c
|
195
196
|
end
|
196
|
-
buffer.
|
197
|
-
|
198
|
-
end
|
199
|
-
inner_getc(timeout_second)
|
197
|
+
string = buffer.delete_suffix(END_BRACKETED_PASTE).force_encoding(encoding)
|
198
|
+
string.valid_encoding? ? string : ''
|
200
199
|
end
|
201
200
|
|
202
201
|
# if the usage expects to wait indefinitely, use Float::INFINITY for timeout_second
|
203
202
|
def self.getc(timeout_second)
|
204
|
-
|
205
|
-
getc_with_bracketed_paste(timeout_second)
|
206
|
-
else
|
207
|
-
inner_getc(timeout_second)
|
208
|
-
end
|
203
|
+
inner_getc(timeout_second)
|
209
204
|
end
|
210
205
|
|
211
206
|
def self.in_pasting?
|
212
|
-
|
207
|
+
not empty_buffer?
|
213
208
|
end
|
214
209
|
|
215
210
|
def self.empty_buffer?
|
@@ -240,7 +235,7 @@ class Reline::ANSI
|
|
240
235
|
s = [ENV["LINES"].to_i, ENV["COLUMNS"].to_i]
|
241
236
|
return s if s[0] > 0 && s[1] > 0
|
242
237
|
[24, 80]
|
243
|
-
rescue Errno::ENOTTY
|
238
|
+
rescue Errno::ENOTTY, Errno::ENODEV
|
244
239
|
[24, 80]
|
245
240
|
end
|
246
241
|
|
@@ -276,7 +271,7 @@ class Reline::ANSI
|
|
276
271
|
buf = @@output.pread(@@output.pos, 0)
|
277
272
|
row = buf.count("\n")
|
278
273
|
column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
|
279
|
-
rescue Errno::ESPIPE
|
274
|
+
rescue Errno::ESPIPE, IOError
|
280
275
|
# Just returns column 1 for ambiguous width because this I/O is not
|
281
276
|
# tty and can't seek.
|
282
277
|
row = 0
|
@@ -307,7 +302,7 @@ class Reline::ANSI
|
|
307
302
|
end
|
308
303
|
|
309
304
|
def self.hide_cursor
|
310
|
-
if Reline::Terminfo.enabled?
|
305
|
+
if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
|
311
306
|
begin
|
312
307
|
@@output.write Reline::Terminfo.tigetstr('civis')
|
313
308
|
rescue Reline::Terminfo::TerminfoError
|
@@ -319,7 +314,7 @@ class Reline::ANSI
|
|
319
314
|
end
|
320
315
|
|
321
316
|
def self.show_cursor
|
322
|
-
if Reline::Terminfo.enabled?
|
317
|
+
if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
|
323
318
|
begin
|
324
319
|
@@output.write Reline::Terminfo.tigetstr('cnorm')
|
325
320
|
rescue Reline::Terminfo::TerminfoError
|
@@ -353,11 +348,15 @@ class Reline::ANSI
|
|
353
348
|
end
|
354
349
|
|
355
350
|
def self.prep
|
351
|
+
# Enable bracketed paste
|
352
|
+
@@output.write "\e[?2004h" if Reline.core.config.enable_bracketed_paste
|
356
353
|
retrieve_keybuffer
|
357
354
|
nil
|
358
355
|
end
|
359
356
|
|
360
357
|
def self.deprep(otio)
|
358
|
+
# Disable bracketed paste
|
359
|
+
@@output.write "\e[?2004l" if Reline.core.config.enable_bracketed_paste
|
361
360
|
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
|
362
361
|
end
|
363
362
|
end
|
data/lib/reline/config.rb
CHANGED
@@ -8,31 +8,12 @@ class Reline::Config
|
|
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
|
@@ -53,8 +34,6 @@ class Reline::Config
|
|
53
34
|
@additional_key_bindings[:vi_insert] = {}
|
54
35
|
@additional_key_bindings[:vi_command] = {}
|
55
36
|
@oneshot_key_bindings = {}
|
56
|
-
@skip_section = nil
|
57
|
-
@if_stack = nil
|
58
37
|
@editing_mode_label = :emacs
|
59
38
|
@keymap_label = :emacs
|
60
39
|
@keymap_prefix = []
|
@@ -71,17 +50,15 @@ class Reline::Config
|
|
71
50
|
@test_mode = false
|
72
51
|
@autocompletion = false
|
73
52
|
@convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
|
53
|
+
@loaded = false
|
54
|
+
@enable_bracketed_paste = true
|
74
55
|
end
|
75
56
|
|
76
57
|
def reset
|
77
58
|
if editing_mode_is?(:vi_command)
|
78
59
|
@editing_mode_label = :vi_insert
|
79
60
|
end
|
80
|
-
@additional_key_bindings.keys.each do |key|
|
81
|
-
@additional_key_bindings[key].clear
|
82
|
-
end
|
83
61
|
@oneshot_key_bindings.clear
|
84
|
-
reset_default_key_bindings
|
85
62
|
end
|
86
63
|
|
87
64
|
def editing_mode
|
@@ -100,6 +77,10 @@ class Reline::Config
|
|
100
77
|
@key_actors[@keymap_label]
|
101
78
|
end
|
102
79
|
|
80
|
+
def loaded?
|
81
|
+
@loaded
|
82
|
+
end
|
83
|
+
|
103
84
|
def inputrc_path
|
104
85
|
case ENV['INPUTRC']
|
105
86
|
when nil, ''
|
@@ -131,6 +112,7 @@ class Reline::Config
|
|
131
112
|
end
|
132
113
|
|
133
114
|
def read(file = nil)
|
115
|
+
@loaded = true
|
134
116
|
file ||= default_inputrc_path
|
135
117
|
begin
|
136
118
|
if file.respond_to?(:readlines)
|
@@ -173,12 +155,6 @@ class Reline::Config
|
|
173
155
|
@key_actors[@keymap_label].default_key_bindings[keystroke] = target
|
174
156
|
end
|
175
157
|
|
176
|
-
def reset_default_key_bindings
|
177
|
-
@key_actors.values.each do |ka|
|
178
|
-
ka.reset_default_key_bindings
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
158
|
def read_lines(lines, file = nil)
|
183
159
|
if not lines.empty? and lines.first.encoding != Reline.encoding_system_needs
|
184
160
|
begin
|
@@ -190,9 +166,7 @@ class Reline::Config
|
|
190
166
|
end
|
191
167
|
end
|
192
168
|
end
|
193
|
-
|
194
|
-
@skip_section = nil
|
195
|
-
@if_stack = []
|
169
|
+
if_stack = []
|
196
170
|
|
197
171
|
lines.each_with_index do |line, no|
|
198
172
|
next if line.match(/\A\s*#/)
|
@@ -201,62 +175,67 @@ class Reline::Config
|
|
201
175
|
|
202
176
|
line = line.chomp.lstrip
|
203
177
|
if line.start_with?('$')
|
204
|
-
handle_directive(line[1..-1], file, no)
|
178
|
+
handle_directive(line[1..-1], file, no, if_stack)
|
205
179
|
next
|
206
180
|
end
|
207
181
|
|
208
|
-
next if
|
182
|
+
next if if_stack.any? { |_no, skip| skip }
|
209
183
|
|
210
184
|
case line
|
211
|
-
when /^set +([^ ]+) +(
|
212
|
-
|
213
|
-
|
185
|
+
when /^set +([^ ]+) +(.+)/i
|
186
|
+
# value ignores everything after a space, raw_value does not.
|
187
|
+
var, value, raw_value = $1.downcase, $2.partition(' ').first, $2
|
188
|
+
bind_variable(var, value, raw_value)
|
214
189
|
next
|
215
190
|
when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
|
216
191
|
key, func_name = $1, $2
|
192
|
+
func_name = func_name.split.first
|
217
193
|
keystroke, func = bind_key(key, func_name)
|
218
194
|
next unless keystroke
|
219
195
|
@additional_key_bindings[@keymap_label][@keymap_prefix + keystroke] = func
|
220
196
|
end
|
221
197
|
end
|
222
|
-
unless
|
223
|
-
raise InvalidInputrc, "#{file}:#{
|
198
|
+
unless if_stack.empty?
|
199
|
+
raise InvalidInputrc, "#{file}:#{if_stack.last[0]}: unclosed if"
|
224
200
|
end
|
225
|
-
ensure
|
226
|
-
@skip_section, @if_stack = conditions
|
227
201
|
end
|
228
202
|
|
229
|
-
def handle_directive(directive, file, no)
|
203
|
+
def handle_directive(directive, file, no, if_stack)
|
230
204
|
directive, args = directive.split(' ')
|
231
205
|
case directive
|
232
206
|
when 'if'
|
233
207
|
condition = false
|
234
208
|
case args
|
235
|
-
when
|
209
|
+
when /^mode=(vi|emacs)$/i
|
210
|
+
mode = $1.downcase
|
211
|
+
# NOTE: mode=vi means vi-insert mode
|
212
|
+
mode = 'vi_insert' if mode == 'vi'
|
213
|
+
if @editing_mode_label == mode.to_sym
|
214
|
+
condition = true
|
215
|
+
end
|
236
216
|
when 'term'
|
237
217
|
when 'version'
|
238
218
|
else # application name
|
239
219
|
condition = true if args == 'Ruby'
|
240
220
|
condition = true if args == 'Reline'
|
241
221
|
end
|
242
|
-
|
243
|
-
@skip_section = !condition
|
222
|
+
if_stack << [no, !condition]
|
244
223
|
when 'else'
|
245
|
-
if
|
224
|
+
if if_stack.empty?
|
246
225
|
raise InvalidInputrc, "#{file}:#{no}: unmatched else"
|
247
226
|
end
|
248
|
-
|
227
|
+
if_stack.last[1] = !if_stack.last[1]
|
249
228
|
when 'endif'
|
250
|
-
if
|
229
|
+
if if_stack.empty?
|
251
230
|
raise InvalidInputrc, "#{file}:#{no}: unmatched endif"
|
252
231
|
end
|
253
|
-
|
232
|
+
if_stack.pop
|
254
233
|
when 'include'
|
255
234
|
read(File.expand_path(args))
|
256
235
|
end
|
257
236
|
end
|
258
237
|
|
259
|
-
def bind_variable(name, value)
|
238
|
+
def bind_variable(name, value, raw_value)
|
260
239
|
case name
|
261
240
|
when 'history-size'
|
262
241
|
begin
|
@@ -281,7 +260,7 @@ class Reline::Config
|
|
281
260
|
when 'completion-query-items'
|
282
261
|
@completion_query_items = value.to_i
|
283
262
|
when 'isearch-terminators'
|
284
|
-
@isearch_terminators = retrieve_string(
|
263
|
+
@isearch_terminators = retrieve_string(raw_value)
|
285
264
|
when 'editing-mode'
|
286
265
|
case value
|
287
266
|
when 'emacs'
|
@@ -323,11 +302,11 @@ class Reline::Config
|
|
323
302
|
@show_mode_in_prompt = false
|
324
303
|
end
|
325
304
|
when 'vi-cmd-mode-string'
|
326
|
-
@vi_cmd_mode_string = retrieve_string(
|
305
|
+
@vi_cmd_mode_string = retrieve_string(raw_value)
|
327
306
|
when 'vi-ins-mode-string'
|
328
|
-
@vi_ins_mode_string = retrieve_string(
|
307
|
+
@vi_ins_mode_string = retrieve_string(raw_value)
|
329
308
|
when 'emacs-mode-string'
|
330
|
-
@emacs_mode_string = retrieve_string(
|
309
|
+
@emacs_mode_string = retrieve_string(raw_value)
|
331
310
|
when *VARIABLE_NAMES then
|
332
311
|
variable_name = :"@#{name.tr(?-, ?_)}"
|
333
312
|
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
|
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, hight 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/general_io.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'io/wait'
|
2
2
|
|
3
3
|
class Reline::GeneralIO
|
4
|
+
RESET_COLOR = '' # Do not send color reset sequence
|
5
|
+
|
4
6
|
def self.reset(encoding: nil)
|
5
7
|
@@pasting = false
|
6
8
|
if encoding
|
@@ -44,6 +46,7 @@ class Reline::GeneralIO
|
|
44
46
|
end
|
45
47
|
c = nil
|
46
48
|
loop do
|
49
|
+
Reline.core.line_editor.handle_signal
|
47
50
|
result = @@input.wait_readable(0.1)
|
48
51
|
next if result.nil?
|
49
52
|
c = @@input.read(1)
|
@@ -57,7 +60,7 @@ class Reline::GeneralIO
|
|
57
60
|
end
|
58
61
|
|
59
62
|
def self.get_screen_size
|
60
|
-
[
|
63
|
+
[24, 80]
|
61
64
|
end
|
62
65
|
|
63
66
|
def self.cursor_pos
|
@@ -100,14 +103,6 @@ class Reline::GeneralIO
|
|
100
103
|
@@pasting
|
101
104
|
end
|
102
105
|
|
103
|
-
def self.start_pasting
|
104
|
-
@@pasting = true
|
105
|
-
end
|
106
|
-
|
107
|
-
def self.finish_pasting
|
108
|
-
@@pasting = false
|
109
|
-
end
|
110
|
-
|
111
106
|
def self.prep
|
112
107
|
end
|
113
108
|
|
data/lib/reline/history.rb
CHANGED
@@ -62,7 +62,7 @@ class Reline::History < Array
|
|
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?
|