reline 0.3.5 → 0.3.7
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 +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
|