reline 0.3.5 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +23 -0
- data/lib/reline/ansi.rb +44 -37
- data/lib/reline/key_stroke.rb +50 -7
- data/lib/reline/line_editor.rb +18 -9
- data/lib/reline/unicode.rb +0 -20
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +56 -42
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67ad796ede485308abba9038bb03672537e8cba1409ef61e921d165db277846d
|
4
|
+
data.tar.gz: 9692f5e08f6ed30722262d70c89e413b4f7d7651f98698d6253f720aea600125
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d69f27ac94aecc2f9628e824d4ded200cf0e0e1939b00d28db99b30f5b21baed78bba0edc7ab68105af327803c0e5f8f03853fb629079ae9017be6016bff0d7
|
7
|
+
data.tar.gz: 62af3574af7223c038e3b078708b116af92bcecf1ec9301e32f8653c008fdfea082253f9308aa99067f60aa9c98deecb5382552deee6847966f4e4ceb387f8f3
|
data/README.md
CHANGED
@@ -55,6 +55,29 @@ 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
|
+
## Contributing
|
59
|
+
|
60
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/reline.
|
61
|
+
|
62
|
+
### Run tests
|
63
|
+
|
64
|
+
> **Note**
|
65
|
+
> Please make sure you have `libvterm` installed for `yamatanooroti` tests (integration tests).
|
66
|
+
|
67
|
+
If you use Homebrew, you can install it by running `brew install libvterm`.
|
68
|
+
|
69
|
+
```bash
|
70
|
+
WITH_VTERM=1 bundle install
|
71
|
+
WITH_VTERM=1 bundle exec rake test test_yamatanooroti
|
72
|
+
```
|
73
|
+
|
74
|
+
## Releasing
|
75
|
+
|
76
|
+
```bash
|
77
|
+
rake release
|
78
|
+
gh release create vX.Y.Z --generate-notes
|
79
|
+
```
|
80
|
+
|
58
81
|
## License
|
59
82
|
|
60
83
|
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/ansi.rb
CHANGED
@@ -14,10 +14,21 @@ class Reline::ANSI
|
|
14
14
|
'kcud1' => :ed_next_history,
|
15
15
|
'kcuf1' => :ed_next_char,
|
16
16
|
'kcub1' => :ed_prev_char,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
}
|
18
|
+
|
19
|
+
ANSI_CURSOR_KEY_BINDINGS = {
|
20
|
+
# Up
|
21
|
+
'A' => [:ed_prev_history, {}],
|
22
|
+
# Down
|
23
|
+
'B' => [:ed_next_history, {}],
|
24
|
+
# Right
|
25
|
+
'C' => [:ed_next_char, { ctrl: :em_next_word, meta: :em_next_word }],
|
26
|
+
# Left
|
27
|
+
'D' => [:ed_prev_char, { ctrl: :ed_prev_word, meta: :ed_prev_word }],
|
28
|
+
# End
|
29
|
+
'F' => [:ed_move_to_end, {}],
|
30
|
+
# Home
|
31
|
+
'H' => [:ed_move_to_beg, {}],
|
21
32
|
}
|
22
33
|
|
23
34
|
if Reline::Terminfo.enabled?
|
@@ -33,22 +44,12 @@ class Reline::ANSI
|
|
33
44
|
end
|
34
45
|
|
35
46
|
def self.set_default_key_bindings(config, allow_terminfo: true)
|
47
|
+
set_default_key_bindings_ansi_cursor(config)
|
36
48
|
if allow_terminfo && Reline::Terminfo.enabled?
|
37
49
|
set_default_key_bindings_terminfo(config)
|
38
50
|
else
|
39
51
|
set_default_key_bindings_comprehensive_list(config)
|
40
52
|
end
|
41
|
-
{
|
42
|
-
# extended entries of terminfo
|
43
|
-
[27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→, extended entry
|
44
|
-
[27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←, extended entry
|
45
|
-
[27, 91, 49, 59, 51, 67] => :em_next_word, # Meta+→, extended entry
|
46
|
-
[27, 91, 49, 59, 51, 68] => :ed_prev_word, # Meta+←, extended entry
|
47
|
-
}.each_pair do |key, func|
|
48
|
-
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
49
|
-
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
50
|
-
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
51
|
-
end
|
52
53
|
{
|
53
54
|
[27, 91, 90] => :completion_journey_up, # S-Tab
|
54
55
|
}.each_pair do |key, func|
|
@@ -64,18 +65,33 @@ class Reline::ANSI
|
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
68
|
+
def self.set_default_key_bindings_ansi_cursor(config)
|
69
|
+
ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
|
70
|
+
bindings = [["\e[#{char}", default_func]] # CSI + char
|
71
|
+
if modifiers[:ctrl]
|
72
|
+
# CSI + ctrl_key_modifier + char
|
73
|
+
bindings << ["\e[1;5#{char}", modifiers[:ctrl]]
|
74
|
+
end
|
75
|
+
if modifiers[:meta]
|
76
|
+
# CSI + meta_key_modifier + char
|
77
|
+
bindings << ["\e[1;3#{char}", modifiers[:meta]]
|
78
|
+
# Meta(ESC) + CSI + char
|
79
|
+
bindings << ["\e\e[#{char}", modifiers[:meta]]
|
80
|
+
end
|
81
|
+
bindings.each do |sequence, func|
|
82
|
+
key = sequence.bytes
|
83
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
84
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
85
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
67
90
|
def self.set_default_key_bindings_terminfo(config)
|
68
91
|
key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding|
|
69
92
|
begin
|
70
93
|
key_code = Reline::Terminfo.tigetstr(capname)
|
71
|
-
|
72
|
-
# Escape sequences that omit the move distance and are set to defaults
|
73
|
-
# value 1 may be sometimes sent by pressing the arrow-key.
|
74
|
-
when 'cuu', 'cud', 'cuf', 'cub'
|
75
|
-
[ key_code.sub(/%p1%d/, '').bytes, key_binding ]
|
76
|
-
else
|
77
|
-
[ key_code.bytes, key_binding ]
|
78
|
-
end
|
94
|
+
[ key_code.bytes, key_binding ]
|
79
95
|
rescue Reline::Terminfo::TerminfoError
|
80
96
|
# capname is undefined
|
81
97
|
end
|
@@ -94,14 +110,8 @@ class Reline::ANSI
|
|
94
110
|
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
95
111
|
[27, 91, 52, 126] => :ed_move_to_end, # End
|
96
112
|
[27, 91, 51, 126] => :key_delete, # Del
|
97
|
-
[27, 91, 65] => :ed_prev_history, # ↑
|
98
|
-
[27, 91, 66] => :ed_next_history, # ↓
|
99
|
-
[27, 91, 67] => :ed_next_char, # →
|
100
|
-
[27, 91, 68] => :ed_prev_char, # ←
|
101
113
|
|
102
114
|
# KDE
|
103
|
-
[27, 91, 72] => :ed_move_to_beg, # Home
|
104
|
-
[27, 91, 70] => :ed_move_to_end, # End
|
105
115
|
# Del is 0x08
|
106
116
|
[27, 71, 65] => :ed_prev_history, # ↑
|
107
117
|
[27, 71, 66] => :ed_next_history, # ↓
|
@@ -118,12 +128,6 @@ class Reline::ANSI
|
|
118
128
|
# Del is 0x08
|
119
129
|
# Arrow keys are the same of KDE
|
120
130
|
|
121
|
-
# iTerm2
|
122
|
-
[27, 27, 91, 67] => :em_next_word, # Option+→, extended entry
|
123
|
-
[27, 27, 91, 68] => :ed_prev_word, # Option+←, extended entry
|
124
|
-
[195, 166] => :em_next_word, # Option+f
|
125
|
-
[195, 162] => :ed_prev_word, # Option+b
|
126
|
-
|
127
131
|
[27, 79, 65] => :ed_prev_history, # ↑
|
128
132
|
[27, 79, 66] => :ed_next_history, # ↓
|
129
133
|
[27, 79, 67] => :ed_next_char, # →
|
@@ -206,7 +210,7 @@ class Reline::ANSI
|
|
206
210
|
end
|
207
211
|
|
208
212
|
def self.in_pasting?
|
209
|
-
@@in_bracketed_paste_mode or (not
|
213
|
+
@@in_bracketed_paste_mode or (not empty_buffer?)
|
210
214
|
end
|
211
215
|
|
212
216
|
def self.empty_buffer?
|
@@ -331,9 +335,12 @@ class Reline::ANSI
|
|
331
335
|
@@output.write "\e[K"
|
332
336
|
end
|
333
337
|
|
338
|
+
# This only works when the cursor is at the bottom of the scroll range
|
339
|
+
# For more details, see https://github.com/ruby/reline/pull/577#issuecomment-1646679623
|
334
340
|
def self.scroll_down(x)
|
335
341
|
return if x.zero?
|
336
|
-
|
342
|
+
# We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576
|
343
|
+
@@output.write "\n" * x
|
337
344
|
end
|
338
345
|
|
339
346
|
def self.clear_screen
|
data/lib/reline/key_stroke.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
class Reline::KeyStroke
|
2
|
+
ESC_BYTE = 27
|
3
|
+
CSI_PARAMETER_BYTES_RANGE = 0x30..0x3f
|
4
|
+
CSI_INTERMEDIATE_BYTES_RANGE = (0x20..0x2f)
|
5
|
+
|
2
6
|
def initialize(config)
|
3
7
|
@config = config
|
4
8
|
end
|
@@ -73,17 +77,26 @@ class Reline::KeyStroke
|
|
73
77
|
return :matched if it.max_by(&:size)&.size&.< input.size
|
74
78
|
return :matching if it.size > 1
|
75
79
|
}
|
76
|
-
key_mapping.keys.
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
80
|
+
if key_mapping.keys.any? { |lhs| start_with?(input, lhs) }
|
81
|
+
:matched
|
82
|
+
else
|
83
|
+
match_unknown_escape_sequence(input).first
|
84
|
+
end
|
81
85
|
end
|
82
86
|
|
83
87
|
def expand(input)
|
84
|
-
input = compress_meta_key(input)
|
85
88
|
lhs = key_mapping.keys.select { |item| start_with?(input, item) }.sort_by(&:size).last
|
86
|
-
|
89
|
+
unless lhs
|
90
|
+
status, size = match_unknown_escape_sequence(input)
|
91
|
+
case status
|
92
|
+
when :matched
|
93
|
+
return [:ed_unassigned] + expand(input.drop(size))
|
94
|
+
when :matching
|
95
|
+
return [:ed_unassigned]
|
96
|
+
else
|
97
|
+
return input
|
98
|
+
end
|
99
|
+
end
|
87
100
|
rhs = key_mapping[lhs]
|
88
101
|
|
89
102
|
case rhs
|
@@ -99,6 +112,36 @@ class Reline::KeyStroke
|
|
99
112
|
|
100
113
|
private
|
101
114
|
|
115
|
+
# returns match status of CSI/SS3 sequence and matched length
|
116
|
+
def match_unknown_escape_sequence(input)
|
117
|
+
idx = 0
|
118
|
+
return [:unmatched, nil] unless input[idx] == ESC_BYTE
|
119
|
+
idx += 1
|
120
|
+
idx += 1 if input[idx] == ESC_BYTE
|
121
|
+
|
122
|
+
case input[idx]
|
123
|
+
when nil
|
124
|
+
return [:matching, nil]
|
125
|
+
when 91 # == '['.ord
|
126
|
+
# CSI sequence
|
127
|
+
idx += 1
|
128
|
+
idx += 1 while idx < input.size && CSI_PARAMETER_BYTES_RANGE.cover?(input[idx])
|
129
|
+
idx += 1 while idx < input.size && CSI_INTERMEDIATE_BYTES_RANGE.cover?(input[idx])
|
130
|
+
input[idx] ? [:matched, idx + 1] : [:matching, nil]
|
131
|
+
when 79 # == 'O'.ord
|
132
|
+
# SS3 sequence
|
133
|
+
input[idx + 1] ? [:matched, idx + 2] : [:matching, nil]
|
134
|
+
else
|
135
|
+
if idx == 1
|
136
|
+
# `ESC char`, make it :unmatched so that it will be handled correctly in `read_2nd_character_of_key_sequence`
|
137
|
+
[:unmatched, nil]
|
138
|
+
else
|
139
|
+
# `ESC ESC char`
|
140
|
+
[:matched, idx + 1]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
102
145
|
def key_mapping
|
103
146
|
@config.key_bindings
|
104
147
|
end
|
data/lib/reline/line_editor.rb
CHANGED
@@ -48,8 +48,8 @@ class Reline::LineEditor
|
|
48
48
|
PERFECT_MATCH = :perfect_match
|
49
49
|
end
|
50
50
|
|
51
|
-
CompletionJourneyData = Struct.new(
|
52
|
-
MenuInfo = Struct.new(
|
51
|
+
CompletionJourneyData = Struct.new(:preposing, :postposing, :list, :pointer)
|
52
|
+
MenuInfo = Struct.new(:target, :list)
|
53
53
|
|
54
54
|
PROMPT_LIST_CACHE_TIMEOUT = 0.5
|
55
55
|
MINIMUM_SCROLLBAR_HEIGHT = 1
|
@@ -60,6 +60,10 @@ class Reline::LineEditor
|
|
60
60
|
reset_variables(encoding: encoding)
|
61
61
|
end
|
62
62
|
|
63
|
+
def io_gate
|
64
|
+
Reline::IOGate
|
65
|
+
end
|
66
|
+
|
63
67
|
def set_pasting_state(in_pasting)
|
64
68
|
@in_pasting = in_pasting
|
65
69
|
end
|
@@ -1510,11 +1514,13 @@ class Reline::LineEditor
|
|
1510
1514
|
return if key.char >= 128 # maybe, first byte of multi byte
|
1511
1515
|
method_symbol = @config.editing_mode.get_method(key.combined_char)
|
1512
1516
|
if key.with_meta and method_symbol == :ed_unassigned
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1517
|
+
if @config.editing_mode_is?(:vi_command, :vi_insert)
|
1518
|
+
# split ESC + key in vi mode
|
1519
|
+
method_symbol = @config.editing_mode.get_method("\e".ord)
|
1520
|
+
process_key("\e".ord, method_symbol)
|
1521
|
+
method_symbol = @config.editing_mode.get_method(key.char)
|
1522
|
+
process_key(key.char, method_symbol)
|
1523
|
+
end
|
1518
1524
|
else
|
1519
1525
|
process_key(key.combined_char, method_symbol)
|
1520
1526
|
end
|
@@ -1599,7 +1605,7 @@ class Reline::LineEditor
|
|
1599
1605
|
else
|
1600
1606
|
@just_cursor_moving = false
|
1601
1607
|
end
|
1602
|
-
if @is_multiline and @auto_indent_proc and not simplified_rendering?
|
1608
|
+
if @is_multiline and @auto_indent_proc and not simplified_rendering? and @line
|
1603
1609
|
process_auto_indent
|
1604
1610
|
end
|
1605
1611
|
end
|
@@ -1645,7 +1651,7 @@ class Reline::LineEditor
|
|
1645
1651
|
@line = ' ' * new_indent + @line.lstrip
|
1646
1652
|
|
1647
1653
|
new_indent = nil
|
1648
|
-
result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, (new_lines[-
|
1654
|
+
result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, (new_lines[@line_index - 1].bytesize + 1), false)
|
1649
1655
|
if result
|
1650
1656
|
new_indent = result
|
1651
1657
|
end
|
@@ -3286,4 +3292,7 @@ class Reline::LineEditor
|
|
3286
3292
|
@mark_pointer = new_pointer
|
3287
3293
|
end
|
3288
3294
|
alias_method :exchange_point_and_mark, :em_exchange_mark
|
3295
|
+
|
3296
|
+
private def em_meta_next(key)
|
3297
|
+
end
|
3289
3298
|
end
|
data/lib/reline/unicode.rb
CHANGED
@@ -41,26 +41,6 @@ class Reline::Unicode
|
|
41
41
|
OSC_REGEXP = /\e\]\d+(?:;[^;\a\e]+)*(?:\a|\e\\)/
|
42
42
|
WIDTH_SCANNER = /\G(?:(#{NON_PRINTING_START})|(#{NON_PRINTING_END})|(#{CSI_REGEXP})|(#{OSC_REGEXP})|(\X))/o
|
43
43
|
|
44
|
-
def self.get_mbchar_byte_size_by_first_char(c)
|
45
|
-
# Checks UTF-8 character byte size
|
46
|
-
case c.ord
|
47
|
-
# 0b0xxxxxxx
|
48
|
-
when ->(code) { (code ^ 0b10000000).allbits?(0b10000000) } then 1
|
49
|
-
# 0b110xxxxx
|
50
|
-
when ->(code) { (code ^ 0b00100000).allbits?(0b11100000) } then 2
|
51
|
-
# 0b1110xxxx
|
52
|
-
when ->(code) { (code ^ 0b00010000).allbits?(0b11110000) } then 3
|
53
|
-
# 0b11110xxx
|
54
|
-
when ->(code) { (code ^ 0b00001000).allbits?(0b11111000) } then 4
|
55
|
-
# 0b111110xx
|
56
|
-
when ->(code) { (code ^ 0b00000100).allbits?(0b11111100) } then 5
|
57
|
-
# 0b1111110x
|
58
|
-
when ->(code) { (code ^ 0b00000010).allbits?(0b11111110) } then 6
|
59
|
-
# successor of mbchar
|
60
|
-
else 0
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
44
|
def self.escape_for_print(str)
|
65
45
|
str.chars.map! { |gr|
|
66
46
|
escaped = EscapedPairs[gr.ord]
|
data/lib/reline/version.rb
CHANGED
data/lib/reline.rb
CHANGED
@@ -17,7 +17,7 @@ module Reline
|
|
17
17
|
|
18
18
|
class ConfigEncodingConversionError < StandardError; end
|
19
19
|
|
20
|
-
Key = Struct.new(
|
20
|
+
Key = Struct.new(:char, :combined_char, :with_meta) do
|
21
21
|
def match?(other)
|
22
22
|
case other
|
23
23
|
when Reline::Key
|
@@ -83,44 +83,48 @@ module Reline
|
|
83
83
|
@bracketed_paste_finished = false
|
84
84
|
end
|
85
85
|
|
86
|
+
def io_gate
|
87
|
+
Reline::IOGate
|
88
|
+
end
|
89
|
+
|
86
90
|
def encoding
|
87
|
-
|
91
|
+
io_gate.encoding
|
88
92
|
end
|
89
93
|
|
90
94
|
def completion_append_character=(val)
|
91
95
|
if val.nil?
|
92
96
|
@completion_append_character = nil
|
93
97
|
elsif val.size == 1
|
94
|
-
@completion_append_character = val.encode(
|
98
|
+
@completion_append_character = val.encode(encoding)
|
95
99
|
elsif val.size > 1
|
96
|
-
@completion_append_character = val[0].encode(
|
100
|
+
@completion_append_character = val[0].encode(encoding)
|
97
101
|
else
|
98
102
|
@completion_append_character = nil
|
99
103
|
end
|
100
104
|
end
|
101
105
|
|
102
106
|
def basic_word_break_characters=(v)
|
103
|
-
@basic_word_break_characters = v.encode(
|
107
|
+
@basic_word_break_characters = v.encode(encoding)
|
104
108
|
end
|
105
109
|
|
106
110
|
def completer_word_break_characters=(v)
|
107
|
-
@completer_word_break_characters = v.encode(
|
111
|
+
@completer_word_break_characters = v.encode(encoding)
|
108
112
|
end
|
109
113
|
|
110
114
|
def basic_quote_characters=(v)
|
111
|
-
@basic_quote_characters = v.encode(
|
115
|
+
@basic_quote_characters = v.encode(encoding)
|
112
116
|
end
|
113
117
|
|
114
118
|
def completer_quote_characters=(v)
|
115
|
-
@completer_quote_characters = v.encode(
|
119
|
+
@completer_quote_characters = v.encode(encoding)
|
116
120
|
end
|
117
121
|
|
118
122
|
def filename_quote_characters=(v)
|
119
|
-
@filename_quote_characters = v.encode(
|
123
|
+
@filename_quote_characters = v.encode(encoding)
|
120
124
|
end
|
121
125
|
|
122
126
|
def special_prefixes=(v)
|
123
|
-
@special_prefixes = v.encode(
|
127
|
+
@special_prefixes = v.encode(encoding)
|
124
128
|
end
|
125
129
|
|
126
130
|
def completion_case_fold=(v)
|
@@ -181,20 +185,16 @@ module Reline
|
|
181
185
|
|
182
186
|
def input=(val)
|
183
187
|
raise TypeError unless val.respond_to?(:getc) or val.nil?
|
184
|
-
if val.respond_to?(:getc)
|
185
|
-
|
186
|
-
Reline::ANSI.input = val
|
187
|
-
elsif Reline::IOGate == Reline::GeneralIO
|
188
|
-
Reline::GeneralIO.input = val
|
189
|
-
end
|
188
|
+
if val.respond_to?(:getc) && io_gate.respond_to?(:input=)
|
189
|
+
io_gate.input = val
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
193
193
|
def output=(val)
|
194
194
|
raise TypeError unless val.respond_to?(:write) or val.nil?
|
195
195
|
@output = val
|
196
|
-
if
|
197
|
-
|
196
|
+
if io_gate.respond_to?(:output=)
|
197
|
+
io_gate.output = val
|
198
198
|
end
|
199
199
|
end
|
200
200
|
|
@@ -217,7 +217,7 @@ module Reline
|
|
217
217
|
end
|
218
218
|
|
219
219
|
def get_screen_size
|
220
|
-
|
220
|
+
io_gate.get_screen_size
|
221
221
|
end
|
222
222
|
|
223
223
|
Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
|
@@ -270,7 +270,8 @@ module Reline
|
|
270
270
|
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
|
271
271
|
|
272
272
|
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
|
273
|
-
Reline
|
273
|
+
Reline.update_iogate
|
274
|
+
io_gate.with_raw_input do
|
274
275
|
unless confirm_multiline_termination
|
275
276
|
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
276
277
|
end
|
@@ -288,6 +289,7 @@ module Reline
|
|
288
289
|
end
|
289
290
|
|
290
291
|
def readline(prompt = '', add_hist = false)
|
292
|
+
Reline.update_iogate
|
291
293
|
inner_readline(prompt, add_hist, false)
|
292
294
|
|
293
295
|
line = line_editor.line.dup
|
@@ -302,7 +304,7 @@ module Reline
|
|
302
304
|
|
303
305
|
private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
|
304
306
|
if ENV['RELINE_STDERR_TTY']
|
305
|
-
if
|
307
|
+
if io_gate.win?
|
306
308
|
$stderr = File.open(ENV['RELINE_STDERR_TTY'], 'a')
|
307
309
|
else
|
308
310
|
$stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
|
@@ -310,10 +312,10 @@ module Reline
|
|
310
312
|
$stderr.sync = true
|
311
313
|
$stderr.puts "Reline is used by #{Process.pid}"
|
312
314
|
end
|
313
|
-
otio =
|
315
|
+
otio = io_gate.prep
|
314
316
|
|
315
317
|
may_req_ambiguous_char_width
|
316
|
-
line_editor.reset(prompt, encoding:
|
318
|
+
line_editor.reset(prompt, encoding: encoding)
|
317
319
|
if multiline
|
318
320
|
line_editor.multiline_on
|
319
321
|
if block_given?
|
@@ -337,7 +339,7 @@ module Reline
|
|
337
339
|
unless config.test_mode
|
338
340
|
config.read
|
339
341
|
config.reset_default_key_bindings
|
340
|
-
|
342
|
+
io_gate.set_default_key_bindings(config)
|
341
343
|
end
|
342
344
|
|
343
345
|
line_editor.rerender
|
@@ -346,9 +348,9 @@ module Reline
|
|
346
348
|
line_editor.set_signal_handlers
|
347
349
|
prev_pasting_state = false
|
348
350
|
loop do
|
349
|
-
prev_pasting_state =
|
351
|
+
prev_pasting_state = io_gate.in_pasting?
|
350
352
|
read_io(config.keyseq_timeout) { |inputs|
|
351
|
-
line_editor.set_pasting_state(
|
353
|
+
line_editor.set_pasting_state(io_gate.in_pasting?)
|
352
354
|
inputs.each { |c|
|
353
355
|
line_editor.input_key(c)
|
354
356
|
line_editor.rerender
|
@@ -358,29 +360,29 @@ module Reline
|
|
358
360
|
@bracketed_paste_finished = false
|
359
361
|
end
|
360
362
|
}
|
361
|
-
if prev_pasting_state == true and not
|
363
|
+
if prev_pasting_state == true and not io_gate.in_pasting? and not line_editor.finished?
|
362
364
|
line_editor.set_pasting_state(false)
|
363
365
|
prev_pasting_state = false
|
364
366
|
line_editor.rerender_all
|
365
367
|
end
|
366
368
|
break if line_editor.finished?
|
367
369
|
end
|
368
|
-
|
370
|
+
io_gate.move_cursor_column(0)
|
369
371
|
rescue Errno::EIO
|
370
372
|
# Maybe the I/O has been closed.
|
371
373
|
rescue StandardError => e
|
372
374
|
line_editor.finalize
|
373
|
-
|
375
|
+
io_gate.deprep(otio)
|
374
376
|
raise e
|
375
377
|
rescue Exception
|
376
378
|
# Including Interrupt
|
377
379
|
line_editor.finalize
|
378
|
-
|
380
|
+
io_gate.deprep(otio)
|
379
381
|
raise
|
380
382
|
end
|
381
383
|
|
382
384
|
line_editor.finalize
|
383
|
-
|
385
|
+
io_gate.deprep(otio)
|
384
386
|
end
|
385
387
|
|
386
388
|
# GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
|
@@ -395,7 +397,7 @@ module Reline
|
|
395
397
|
private def read_io(keyseq_timeout, &block)
|
396
398
|
buffer = []
|
397
399
|
loop do
|
398
|
-
c =
|
400
|
+
c = io_gate.getc
|
399
401
|
if c == -1
|
400
402
|
result = :unmatched
|
401
403
|
@bracketed_paste_finished = true
|
@@ -435,7 +437,7 @@ module Reline
|
|
435
437
|
begin
|
436
438
|
succ_c = nil
|
437
439
|
Timeout.timeout(keyseq_timeout / 1000.0) {
|
438
|
-
succ_c =
|
440
|
+
succ_c = io_gate.getc
|
439
441
|
}
|
440
442
|
rescue Timeout::Error # cancel matching only when first byte
|
441
443
|
block.([Reline::Key.new(c, c, false)])
|
@@ -450,7 +452,7 @@ module Reline
|
|
450
452
|
end
|
451
453
|
return :break
|
452
454
|
when :matching
|
453
|
-
|
455
|
+
io_gate.ungetc(succ_c)
|
454
456
|
return :next
|
455
457
|
when :matched
|
456
458
|
buffer << succ_c
|
@@ -467,7 +469,7 @@ module Reline
|
|
467
469
|
begin
|
468
470
|
escaped_c = nil
|
469
471
|
Timeout.timeout(keyseq_timeout / 1000.0) {
|
470
|
-
escaped_c =
|
472
|
+
escaped_c = io_gate.getc
|
471
473
|
}
|
472
474
|
rescue Timeout::Error # independent ESC
|
473
475
|
block.([Reline::Key.new(c, c, false)])
|
@@ -490,19 +492,19 @@ module Reline
|
|
490
492
|
end
|
491
493
|
|
492
494
|
private def may_req_ambiguous_char_width
|
493
|
-
@ambiguous_width = 2 if
|
495
|
+
@ambiguous_width = 2 if io_gate == Reline::GeneralIO or !STDOUT.tty?
|
494
496
|
return if defined? @ambiguous_width
|
495
|
-
|
497
|
+
io_gate.move_cursor_column(0)
|
496
498
|
begin
|
497
499
|
output.write "\u{25bd}"
|
498
500
|
rescue Encoding::UndefinedConversionError
|
499
501
|
# LANG=C
|
500
502
|
@ambiguous_width = 1
|
501
503
|
else
|
502
|
-
@ambiguous_width =
|
504
|
+
@ambiguous_width = io_gate.cursor_pos.x
|
503
505
|
end
|
504
|
-
|
505
|
-
|
506
|
+
io_gate.move_cursor_column(0)
|
507
|
+
io_gate.erase_after_cursor
|
506
508
|
end
|
507
509
|
end
|
508
510
|
|
@@ -565,7 +567,7 @@ module Reline
|
|
565
567
|
@core ||= Core.new { |core|
|
566
568
|
core.config = Reline::Config.new
|
567
569
|
core.key_stroke = Reline::KeyStroke.new(core.config)
|
568
|
-
core.line_editor = Reline::LineEditor.new(core.config,
|
570
|
+
core.line_editor = Reline::LineEditor.new(core.config, core.encoding)
|
569
571
|
|
570
572
|
core.basic_word_break_characters = " \t\n`><=;|&{("
|
571
573
|
core.completer_word_break_characters = " \t\n`><=;|&{("
|
@@ -578,12 +580,24 @@ module Reline
|
|
578
580
|
end
|
579
581
|
|
580
582
|
def self.ungetc(c)
|
581
|
-
|
583
|
+
core.io_gate.ungetc(c)
|
582
584
|
end
|
583
585
|
|
584
586
|
def self.line_editor
|
585
587
|
core.line_editor
|
586
588
|
end
|
589
|
+
|
590
|
+
def self.update_iogate
|
591
|
+
return if core.config.test_mode
|
592
|
+
|
593
|
+
# Need to change IOGate when `$stdout.tty?` change from false to true by `$stdout.reopen`
|
594
|
+
# Example: rails/spring boot the application in non-tty, then run console in tty.
|
595
|
+
if ENV['TERM'] != 'dumb' && core.io_gate == Reline::GeneralIO && $stdout.tty?
|
596
|
+
require 'reline/ansi'
|
597
|
+
remove_const(:IOGate)
|
598
|
+
const_set(:IOGate, Reline::ANSI)
|
599
|
+
end
|
600
|
+
end
|
587
601
|
end
|
588
602
|
|
589
603
|
require 'reline/general_io'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|