reline 0.2.4 → 0.2.8.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -0
- data/lib/reline/ansi.rb +119 -56
- data/lib/reline/config.rb +30 -13
- data/lib/reline/general_io.rb +11 -3
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/line_editor.rb +395 -23
- data/lib/reline/terminfo.rb +126 -0
- data/lib/reline/unicode.rb +30 -0
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +164 -80
- data/lib/reline.rb +78 -13
- metadata +6 -62
- data/lib/reline/line_editor.rb.orig +0 -2696
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c1dd7202ef6de3ebf89e2ed39c83af1e48bcd46d5f967037edc0097301b8286
|
4
|
+
data.tar.gz: 4d2047341862d83b785903974e4766b5a3d481a033208bfcc1d8891a0b116e65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9109bb68de735bd5dba6de8dffecfd23fecd8f09aa0b1f92d0bca6b008ef7c3d3785c16292db8ea90c9baf4d275ef382cd6d0d15487fbe6e491c74a670004bd
|
7
|
+
data.tar.gz: af8f8b99ceb34416253857caf4d87a07bde1fddf72d80ff14caeb2a5940178566bd63fc4a6884eddd60959ac3f352c056bb73bbe646915582b5b3b4d091beeed
|
data/README.md
CHANGED
@@ -8,6 +8,52 @@ This is a screen capture of *IRB improved by Reline*.
|
|
8
8
|
|
9
9
|
Reline is compatible with the API of Ruby's stdlib 'readline', GNU Readline and Editline by pure Ruby implementation.
|
10
10
|
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
### Single line editing mode
|
14
|
+
|
15
|
+
It's compatible with the readline standard library.
|
16
|
+
|
17
|
+
See [the document of readline stdlib](https://ruby-doc.org/stdlib/libdoc/readline/rdoc/Readline.html) or [bin/example](https://github.com/ruby/reline/blob/master/bin/example).
|
18
|
+
|
19
|
+
### Multi-line editing mode
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require "reline"
|
23
|
+
|
24
|
+
prompt = 'prompt> '
|
25
|
+
use_history = true
|
26
|
+
|
27
|
+
begin
|
28
|
+
while true
|
29
|
+
text = Reline.readmultiline(prompt, use_history) do |multiline_input|
|
30
|
+
# Accept the input until `end` is entered
|
31
|
+
multiline_input.split.last == "end"
|
32
|
+
end
|
33
|
+
|
34
|
+
puts 'You entered:'
|
35
|
+
puts text
|
36
|
+
end
|
37
|
+
# If you want to exit, type Ctrl-C
|
38
|
+
rescue Interrupt
|
39
|
+
puts '^C'
|
40
|
+
exit 0
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
```bash
|
45
|
+
$ ruby example.rb
|
46
|
+
prompt> aaa
|
47
|
+
prompt> bbb
|
48
|
+
prompt> end
|
49
|
+
You entered:
|
50
|
+
aaa
|
51
|
+
bbb
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
See also: [test/reline/yamatanooroti/multiline_repl](https://github.com/ruby/reline/blob/master/test/reline/yamatanooroti/multiline_repl)
|
56
|
+
|
11
57
|
## License
|
12
58
|
|
13
59
|
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
@@ -1,7 +1,13 @@
|
|
1
1
|
require 'io/console'
|
2
|
+
require 'io/wait'
|
2
3
|
require 'timeout'
|
4
|
+
require_relative 'terminfo'
|
3
5
|
|
4
6
|
class Reline::ANSI
|
7
|
+
if Reline::Terminfo.enabled?
|
8
|
+
Reline::Terminfo.setupterm(0, 2)
|
9
|
+
end
|
10
|
+
|
5
11
|
def self.encoding
|
6
12
|
Encoding.default_external
|
7
13
|
end
|
@@ -10,52 +16,99 @@ class Reline::ANSI
|
|
10
16
|
false
|
11
17
|
end
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
19
|
+
def self.set_default_key_bindings(config)
|
20
|
+
if Reline::Terminfo.enabled?
|
21
|
+
set_default_key_bindings_terminfo(config)
|
22
|
+
else
|
23
|
+
set_default_key_bindings_comprehensive_list(config)
|
24
|
+
end
|
25
|
+
{
|
26
|
+
# extended entries of terminfo
|
27
|
+
[27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→, extended entry
|
28
|
+
[27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←, extended entry
|
29
|
+
[27, 91, 49, 59, 51, 67] => :em_next_word, # Meta+→, extended entry
|
30
|
+
[27, 91, 49, 59, 51, 68] => :ed_prev_word, # Meta+←, extended entry
|
31
|
+
}.each_pair do |key, func|
|
32
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
33
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
34
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
35
|
+
end
|
36
|
+
{
|
37
|
+
# default bindings
|
38
|
+
[27, 32] => :em_set_mark, # M-<space>
|
39
|
+
[24, 24] => :em_exchange_mark, # C-x C-x
|
40
|
+
}.each_pair do |key, func|
|
41
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.set_default_key_bindings_terminfo(config)
|
46
|
+
{
|
47
|
+
Reline::Terminfo.tigetstr('khome').bytes => :ed_move_to_beg,
|
48
|
+
Reline::Terminfo.tigetstr('kend').bytes => :ed_move_to_end,
|
49
|
+
Reline::Terminfo.tigetstr('kcuu1').bytes => :ed_prev_history,
|
50
|
+
Reline::Terminfo.tigetstr('kcud1').bytes => :ed_next_history,
|
51
|
+
Reline::Terminfo.tigetstr('kcuf1').bytes => :ed_next_char,
|
52
|
+
Reline::Terminfo.tigetstr('kcub1').bytes => :ed_prev_char,
|
53
|
+
# Escape sequences that omit the move distance and are set to defaults
|
54
|
+
# value 1 may be sometimes sent by pressing the arrow-key.
|
55
|
+
Reline::Terminfo.tigetstr('cuu').sub(/%p1%d/, '').bytes => :ed_prev_history,
|
56
|
+
Reline::Terminfo.tigetstr('cud').sub(/%p1%d/, '').bytes => :ed_next_history,
|
57
|
+
Reline::Terminfo.tigetstr('cuf').sub(/%p1%d/, '').bytes => :ed_next_char,
|
58
|
+
Reline::Terminfo.tigetstr('cub').sub(/%p1%d/, '').bytes => :ed_prev_char,
|
59
|
+
}.each_pair do |key, func|
|
60
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
61
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
62
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.set_default_key_bindings_comprehensive_list(config)
|
67
|
+
{
|
68
|
+
# Console (80x25)
|
69
|
+
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
70
|
+
[27, 91, 52, 126] => :ed_move_to_end, # End
|
71
|
+
[27, 91, 51, 126] => :key_delete, # Del
|
72
|
+
[27, 91, 65] => :ed_prev_history, # ↑
|
73
|
+
[27, 91, 66] => :ed_next_history, # ↓
|
74
|
+
[27, 91, 67] => :ed_next_char, # →
|
75
|
+
[27, 91, 68] => :ed_prev_char, # ←
|
76
|
+
|
77
|
+
# KDE
|
78
|
+
[27, 91, 72] => :ed_move_to_beg, # Home
|
79
|
+
[27, 91, 70] => :ed_move_to_end, # End
|
80
|
+
# Del is 0x08
|
81
|
+
[27, 71, 65] => :ed_prev_history, # ↑
|
82
|
+
[27, 71, 66] => :ed_next_history, # ↓
|
83
|
+
[27, 71, 67] => :ed_next_char, # →
|
84
|
+
[27, 71, 68] => :ed_prev_char, # ←
|
85
|
+
|
86
|
+
# urxvt / exoterm
|
87
|
+
[27, 91, 55, 126] => :ed_move_to_beg, # Home
|
88
|
+
[27, 91, 56, 126] => :ed_move_to_end, # End
|
89
|
+
|
90
|
+
# GNOME
|
91
|
+
[27, 79, 72] => :ed_move_to_beg, # Home
|
92
|
+
[27, 79, 70] => :ed_move_to_end, # End
|
93
|
+
# Del is 0x08
|
94
|
+
# Arrow keys are the same of KDE
|
95
|
+
|
96
|
+
# iTerm2
|
97
|
+
[27, 27, 91, 67] => :em_next_word, # Option+→, extended entry
|
98
|
+
[27, 27, 91, 68] => :ed_prev_word, # Option+←, extended entry
|
99
|
+
[195, 166] => :em_next_word, # Option+f
|
100
|
+
[195, 162] => :ed_prev_word, # Option+b
|
101
|
+
|
102
|
+
[27, 79, 65] => :ed_prev_history, # ↑
|
103
|
+
[27, 79, 66] => :ed_next_history, # ↓
|
104
|
+
[27, 79, 67] => :ed_next_char, # →
|
105
|
+
[27, 79, 68] => :ed_prev_char, # ←
|
106
|
+
}.each_pair do |key, func|
|
107
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
108
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
109
|
+
config.add_default_key_binding_by_keymap(:vi_command, key, func)
|
110
|
+
end
|
111
|
+
end
|
59
112
|
|
60
113
|
@@input = STDIN
|
61
114
|
def self.input=(val)
|
@@ -79,6 +132,8 @@ class Reline::ANSI
|
|
79
132
|
rescue Errno::EIO
|
80
133
|
# Maybe the I/O has been closed.
|
81
134
|
nil
|
135
|
+
rescue Errno::ENOTTY
|
136
|
+
nil
|
82
137
|
end
|
83
138
|
|
84
139
|
@@in_bracketed_paste_mode = false
|
@@ -129,12 +184,7 @@ class Reline::ANSI
|
|
129
184
|
unless @@buf.empty?
|
130
185
|
return false
|
131
186
|
end
|
132
|
-
|
133
|
-
if rs and rs[0]
|
134
|
-
false
|
135
|
-
else
|
136
|
-
true
|
137
|
-
end
|
187
|
+
!@@input.wait_readable(0)
|
138
188
|
end
|
139
189
|
|
140
190
|
def self.ungetc(c)
|
@@ -143,8 +193,7 @@ class Reline::ANSI
|
|
143
193
|
|
144
194
|
def self.retrieve_keybuffer
|
145
195
|
begin
|
146
|
-
|
147
|
-
return if result.nil?
|
196
|
+
return unless @@input.wait_readable(0.001)
|
148
197
|
str = @@input.read_nonblock(1024)
|
149
198
|
str.bytes.each do |c|
|
150
199
|
@@buf.push(c)
|
@@ -225,6 +274,22 @@ class Reline::ANSI
|
|
225
274
|
end
|
226
275
|
end
|
227
276
|
|
277
|
+
def self.hide_cursor
|
278
|
+
if Reline::Terminfo.enabled?
|
279
|
+
@@output.write Reline::Terminfo.tigetstr('civis')
|
280
|
+
else
|
281
|
+
# ignored
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def self.show_cursor
|
286
|
+
if Reline::Terminfo.enabled?
|
287
|
+
@@output.write Reline::Terminfo.tigetstr('cnorm')
|
288
|
+
else
|
289
|
+
# ignored
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
228
293
|
def self.erase_after_cursor
|
229
294
|
@@output.write "\e[K"
|
230
295
|
end
|
@@ -246,8 +311,6 @@ class Reline::ANSI
|
|
246
311
|
|
247
312
|
def self.prep
|
248
313
|
retrieve_keybuffer
|
249
|
-
int_handle = Signal.trap('INT', 'IGNORE')
|
250
|
-
Signal.trap('INT', int_handle)
|
251
314
|
nil
|
252
315
|
end
|
253
316
|
|
data/lib/reline/config.rb
CHANGED
@@ -47,7 +47,9 @@ class Reline::Config
|
|
47
47
|
|
48
48
|
def initialize
|
49
49
|
@additional_key_bindings = {} # from inputrc
|
50
|
-
@
|
50
|
+
@additional_key_bindings[:emacs] = {}
|
51
|
+
@additional_key_bindings[:vi_insert] = {}
|
52
|
+
@additional_key_bindings[:vi_command] = {}
|
51
53
|
@skip_section = nil
|
52
54
|
@if_stack = nil
|
53
55
|
@editing_mode_label = :emacs
|
@@ -69,8 +71,10 @@ class Reline::Config
|
|
69
71
|
if editing_mode_is?(:vi_command)
|
70
72
|
@editing_mode_label = :vi_insert
|
71
73
|
end
|
72
|
-
@additional_key_bindings
|
73
|
-
|
74
|
+
@additional_key_bindings.keys.each do |key|
|
75
|
+
@additional_key_bindings[key].clear
|
76
|
+
end
|
77
|
+
reset_default_key_bindings
|
74
78
|
end
|
75
79
|
|
76
80
|
def editing_mode
|
@@ -135,19 +139,35 @@ class Reline::Config
|
|
135
139
|
end
|
136
140
|
|
137
141
|
def key_bindings
|
138
|
-
# override @default_key_bindings with @additional_key_bindings
|
139
|
-
@default_key_bindings.merge(@additional_key_bindings)
|
142
|
+
# override @key_actors[@editing_mode_label].default_key_bindings with @additional_key_bindings[@editing_mode_label]
|
143
|
+
@key_actors[@editing_mode_label].default_key_bindings.merge(@additional_key_bindings[@editing_mode_label])
|
144
|
+
end
|
145
|
+
|
146
|
+
def add_default_key_binding_by_keymap(keymap, keystroke, target)
|
147
|
+
@key_actors[keymap].default_key_bindings[keystroke] = target
|
140
148
|
end
|
141
149
|
|
142
150
|
def add_default_key_binding(keystroke, target)
|
143
|
-
@default_key_bindings[keystroke] = target
|
151
|
+
@key_actors[@keymap_label].default_key_bindings[keystroke] = target
|
144
152
|
end
|
145
153
|
|
146
154
|
def reset_default_key_bindings
|
147
|
-
@
|
155
|
+
@key_actors.values.each do |ka|
|
156
|
+
ka.reset_default_key_bindings
|
157
|
+
end
|
148
158
|
end
|
149
159
|
|
150
160
|
def read_lines(lines, file = nil)
|
161
|
+
if not lines.empty? and lines.first.encoding != Reline.encoding_system_needs
|
162
|
+
begin
|
163
|
+
lines = lines.map do |l|
|
164
|
+
l.encode(Reline.encoding_system_needs)
|
165
|
+
rescue Encoding::UndefinedConversionError
|
166
|
+
mes = "The inputrc encoded in #{lines.first.encoding.name} can't be converted to the locale #{Reline.encoding_system_needs.name}."
|
167
|
+
raise Reline::ConfigEncodingConversionError.new(mes)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
151
171
|
conditions = [@skip_section, @if_stack]
|
152
172
|
@skip_section = nil
|
153
173
|
@if_stack = []
|
@@ -174,7 +194,7 @@ class Reline::Config
|
|
174
194
|
key, func_name = $1, $2
|
175
195
|
keystroke, func = bind_key(key, func_name)
|
176
196
|
next unless keystroke
|
177
|
-
@additional_key_bindings[keystroke] = func
|
197
|
+
@additional_key_bindings[@keymap_label][keystroke] = func
|
178
198
|
end
|
179
199
|
end
|
180
200
|
unless @if_stack.empty?
|
@@ -282,11 +302,8 @@ class Reline::Config
|
|
282
302
|
end
|
283
303
|
|
284
304
|
def retrieve_string(str)
|
285
|
-
if str =~ /\A"(.*)"\z/
|
286
|
-
|
287
|
-
else
|
288
|
-
parse_keyseq(str).map(&:chr).join
|
289
|
-
end
|
305
|
+
str = $1 if str =~ /\A"(.*)"\z/
|
306
|
+
parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join
|
290
307
|
end
|
291
308
|
|
292
309
|
def bind_key(key, func_name)
|
data/lib/reline/general_io.rb
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
require 'timeout'
|
2
2
|
|
3
3
|
class Reline::GeneralIO
|
4
|
-
def self.reset
|
4
|
+
def self.reset(encoding: nil)
|
5
5
|
@@pasting = false
|
6
|
+
@@encoding = encoding
|
6
7
|
end
|
7
8
|
|
8
9
|
def self.encoding
|
9
|
-
|
10
|
+
if defined?(@@encoding)
|
11
|
+
@@encoding
|
12
|
+
elsif RUBY_PLATFORM =~ /mswin|mingw/
|
13
|
+
Encoding::UTF_8
|
14
|
+
else
|
15
|
+
Encoding::default_external
|
16
|
+
end
|
10
17
|
end
|
11
18
|
|
12
19
|
def self.win?
|
13
20
|
false
|
14
21
|
end
|
15
22
|
|
16
|
-
|
23
|
+
def self.set_default_key_bindings(_)
|
24
|
+
end
|
17
25
|
|
18
26
|
@@buf = []
|
19
27
|
|
@@ -4,4 +4,16 @@ class Reline::KeyActor::Base
|
|
4
4
|
def get_method(key)
|
5
5
|
self.class::MAPPING[key]
|
6
6
|
end
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@default_key_bindings = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def default_key_bindings
|
13
|
+
@default_key_bindings
|
14
|
+
end
|
15
|
+
|
16
|
+
def reset_default_key_bindings
|
17
|
+
@default_key_bindings.clear
|
18
|
+
end
|
7
19
|
end
|
data/lib/reline/line_editor.rb
CHANGED
@@ -150,7 +150,8 @@ class Reline::LineEditor
|
|
150
150
|
@screen_size = Reline::IOGate.get_screen_size
|
151
151
|
@screen_height = @screen_size.first
|
152
152
|
reset_variables(prompt, encoding: encoding)
|
153
|
-
@old_trap = Signal.trap(
|
153
|
+
@old_trap = Signal.trap(:INT) {
|
154
|
+
clear_dialog
|
154
155
|
if @scroll_partial_screen
|
155
156
|
move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
|
156
157
|
else
|
@@ -158,8 +159,16 @@ class Reline::LineEditor
|
|
158
159
|
end
|
159
160
|
Reline::IOGate.move_cursor_column(0)
|
160
161
|
scroll_down(1)
|
161
|
-
|
162
|
-
|
162
|
+
case @old_trap
|
163
|
+
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
164
|
+
raise Interrupt
|
165
|
+
when 'IGNORE'
|
166
|
+
# Do nothing
|
167
|
+
when 'EXIT'
|
168
|
+
exit
|
169
|
+
else
|
170
|
+
@old_trap.call
|
171
|
+
end
|
163
172
|
}
|
164
173
|
Reline::IOGate.set_winch_handler do
|
165
174
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
@@ -241,6 +250,7 @@ class Reline::LineEditor
|
|
241
250
|
@drop_terminate_spaces = false
|
242
251
|
@in_pasting = false
|
243
252
|
@auto_indent_proc = nil
|
253
|
+
@dialogs = []
|
244
254
|
reset_line
|
245
255
|
end
|
246
256
|
|
@@ -406,10 +416,10 @@ class Reline::LineEditor
|
|
406
416
|
Reline::IOGate.erase_after_cursor
|
407
417
|
end
|
408
418
|
@output.flush
|
419
|
+
clear_dialog
|
409
420
|
return
|
410
421
|
end
|
411
422
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
412
|
-
# FIXME: end of logical line sometimes breaks
|
413
423
|
rendered = false
|
414
424
|
if @add_newline_to_end_of_buffer
|
415
425
|
rerender_added_newline(prompt, prompt_width)
|
@@ -417,6 +427,7 @@ class Reline::LineEditor
|
|
417
427
|
else
|
418
428
|
if @just_cursor_moving and not @rerender_all
|
419
429
|
rendered = just_move_cursor
|
430
|
+
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
420
431
|
@just_cursor_moving = false
|
421
432
|
return
|
422
433
|
elsif @previous_line_index or new_highest_in_this != @highest_in_this
|
@@ -439,18 +450,20 @@ class Reline::LineEditor
|
|
439
450
|
new_lines = whole_lines
|
440
451
|
end
|
441
452
|
line = modify_lines(new_lines)[@line_index]
|
453
|
+
clear_dialog
|
442
454
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
443
455
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
444
456
|
move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
|
445
457
|
scroll_down(1)
|
446
458
|
Reline::IOGate.move_cursor_column(0)
|
447
459
|
Reline::IOGate.erase_after_cursor
|
448
|
-
|
449
|
-
|
460
|
+
else
|
461
|
+
if not rendered and not @in_pasting
|
450
462
|
line = modify_lines(whole_lines)[@line_index]
|
451
463
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
452
464
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
453
465
|
end
|
466
|
+
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
454
467
|
end
|
455
468
|
@buffer_of_lines[@line_index] = @line
|
456
469
|
@rest_height = 0 if @scroll_partial_screen
|
@@ -465,6 +478,289 @@ class Reline::LineEditor
|
|
465
478
|
end
|
466
479
|
end
|
467
480
|
|
481
|
+
class DialogProcScope
|
482
|
+
def initialize(line_editor, proc_to_exec, context)
|
483
|
+
@line_editor = line_editor
|
484
|
+
@proc_to_exec = proc_to_exec
|
485
|
+
@context = context
|
486
|
+
@cursor_pos = Reline::CursorPos.new
|
487
|
+
end
|
488
|
+
|
489
|
+
def context
|
490
|
+
@context
|
491
|
+
end
|
492
|
+
|
493
|
+
def retrieve_completion_block(set_completion_quote_character = false)
|
494
|
+
@line_editor.retrieve_completion_block(set_completion_quote_character)
|
495
|
+
end
|
496
|
+
|
497
|
+
def call_completion_proc_with_checking_args(pre, target, post)
|
498
|
+
@line_editor.call_completion_proc_with_checking_args(pre, target, post)
|
499
|
+
end
|
500
|
+
|
501
|
+
def set_cursor_pos(col, row)
|
502
|
+
@cursor_pos.x = col
|
503
|
+
@cursor_pos.y = row
|
504
|
+
end
|
505
|
+
|
506
|
+
def cursor_pos
|
507
|
+
@cursor_pos
|
508
|
+
end
|
509
|
+
|
510
|
+
def just_cursor_moving
|
511
|
+
@line_editor.instance_variable_get(:@just_cursor_moving)
|
512
|
+
end
|
513
|
+
|
514
|
+
def screen_width
|
515
|
+
@line_editor.instance_variable_get(:@screen_size).last
|
516
|
+
end
|
517
|
+
|
518
|
+
def completion_journey_data
|
519
|
+
@line_editor.instance_variable_get(:@completion_journey_data)
|
520
|
+
end
|
521
|
+
|
522
|
+
def call
|
523
|
+
instance_exec(&@proc_to_exec)
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
class Dialog
|
528
|
+
attr_reader :name
|
529
|
+
attr_accessor :column, :vertical_offset, :contents, :lines_backup
|
530
|
+
|
531
|
+
def initialize(name, proc_scope)
|
532
|
+
@name = name
|
533
|
+
@proc_scope = proc_scope
|
534
|
+
end
|
535
|
+
|
536
|
+
def set_cursor_pos(col, row)
|
537
|
+
@proc_scope.set_cursor_pos(col, row)
|
538
|
+
end
|
539
|
+
|
540
|
+
def call
|
541
|
+
@proc_scope.call
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
def add_dialog_proc(name, p, context = nil)
|
546
|
+
return if @dialogs.any? { |d| d.name == name }
|
547
|
+
@dialogs << Dialog.new(name, DialogProcScope.new(self, p, context))
|
548
|
+
end
|
549
|
+
|
550
|
+
DIALOG_HEIGHT = 20
|
551
|
+
DIALOG_WIDTH = 40
|
552
|
+
private def render_dialog(cursor_column)
|
553
|
+
@dialogs.each do |dialog|
|
554
|
+
render_each_dialog(dialog, cursor_column)
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
private def render_each_dialog(dialog, cursor_column)
|
559
|
+
if @in_pasting
|
560
|
+
dialog.contents = nil
|
561
|
+
return
|
562
|
+
end
|
563
|
+
dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
|
564
|
+
pos, result, pointer, bg = dialog.call
|
565
|
+
old_dialog_contents = dialog.contents
|
566
|
+
old_dialog_column = dialog.column
|
567
|
+
old_dialog_vertical_offset = dialog.vertical_offset
|
568
|
+
if result and not result.empty?
|
569
|
+
dialog.contents = result
|
570
|
+
dialog.contents = dialog.contents[0...DIALOG_HEIGHT] if dialog.contents.size > DIALOG_HEIGHT
|
571
|
+
else
|
572
|
+
dialog.lines_backup = {
|
573
|
+
lines: modify_lines(whole_lines),
|
574
|
+
line_index: @line_index,
|
575
|
+
first_line_started_from: @first_line_started_from,
|
576
|
+
started_from: @started_from,
|
577
|
+
byte_pointer: @byte_pointer
|
578
|
+
}
|
579
|
+
clear_each_dialog(dialog)
|
580
|
+
dialog.contents = nil
|
581
|
+
return
|
582
|
+
end
|
583
|
+
upper_space = @first_line_started_from - @started_from
|
584
|
+
lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
|
585
|
+
dialog.column = pos.x
|
586
|
+
diff = (dialog.column + DIALOG_WIDTH) - (@screen_size.last - 1)
|
587
|
+
if diff > 0
|
588
|
+
dialog.column -= diff
|
589
|
+
end
|
590
|
+
if (lower_space + @rest_height) >= DIALOG_HEIGHT
|
591
|
+
dialog.vertical_offset = pos.y + 1
|
592
|
+
elsif upper_space >= DIALOG_HEIGHT
|
593
|
+
dialog.vertical_offset = pos.y + -(DIALOG_HEIGHT + 1)
|
594
|
+
else
|
595
|
+
if (lower_space + @rest_height) < DIALOG_HEIGHT
|
596
|
+
scroll_down(DIALOG_HEIGHT)
|
597
|
+
move_cursor_up(DIALOG_HEIGHT)
|
598
|
+
end
|
599
|
+
dialog.vertical_offset = pos.y + 1
|
600
|
+
end
|
601
|
+
Reline::IOGate.hide_cursor
|
602
|
+
reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
|
603
|
+
move_cursor_down(dialog.vertical_offset)
|
604
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
605
|
+
dialog.contents.each_with_index do |item, i|
|
606
|
+
if i == pointer
|
607
|
+
bg_color = '45'
|
608
|
+
else
|
609
|
+
if bg
|
610
|
+
bg_color = bg
|
611
|
+
else
|
612
|
+
bg_color = '46'
|
613
|
+
end
|
614
|
+
end
|
615
|
+
@output.write "\e[#{bg_color}m%-#{DIALOG_WIDTH}s\e[49m" % item.slice(0, DIALOG_WIDTH)
|
616
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
617
|
+
move_cursor_down(1) if i < (dialog.contents.size - 1)
|
618
|
+
end
|
619
|
+
Reline::IOGate.move_cursor_column(cursor_column)
|
620
|
+
move_cursor_up(dialog.vertical_offset + dialog.contents.size - 1)
|
621
|
+
Reline::IOGate.show_cursor
|
622
|
+
dialog.lines_backup = {
|
623
|
+
lines: modify_lines(whole_lines),
|
624
|
+
line_index: @line_index,
|
625
|
+
first_line_started_from: @first_line_started_from,
|
626
|
+
started_from: @started_from,
|
627
|
+
byte_pointer: @byte_pointer
|
628
|
+
}
|
629
|
+
end
|
630
|
+
|
631
|
+
private def reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
|
632
|
+
return if dialog.lines_backup.nil? or old_dialog_contents.nil?
|
633
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
|
634
|
+
visual_lines = []
|
635
|
+
visual_start = nil
|
636
|
+
dialog.lines_backup[:lines].each_with_index { |l, i|
|
637
|
+
pr = prompt_list ? prompt_list[i] : prompt
|
638
|
+
vl, _ = split_by_width(pr + l, @screen_size.last)
|
639
|
+
vl.compact!
|
640
|
+
if i == dialog.lines_backup[:line_index]
|
641
|
+
visual_start = visual_lines.size + dialog.lines_backup[:started_from]
|
642
|
+
end
|
643
|
+
visual_lines.concat(vl)
|
644
|
+
}
|
645
|
+
old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from]
|
646
|
+
y = @first_line_started_from + @started_from
|
647
|
+
y_diff = y - old_y
|
648
|
+
if (old_y + old_dialog_vertical_offset) < (y + dialog.vertical_offset)
|
649
|
+
# rerender top
|
650
|
+
move_cursor_down(old_dialog_vertical_offset - y_diff)
|
651
|
+
start = visual_start + old_dialog_vertical_offset
|
652
|
+
line_num = dialog.vertical_offset - old_dialog_vertical_offset
|
653
|
+
line_num.times do |i|
|
654
|
+
Reline::IOGate.move_cursor_column(old_dialog_column)
|
655
|
+
if visual_lines[start + i].nil?
|
656
|
+
s = ' ' * DIALOG_WIDTH
|
657
|
+
else
|
658
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
|
659
|
+
end
|
660
|
+
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
|
661
|
+
move_cursor_down(1) if i < (line_num - 1)
|
662
|
+
end
|
663
|
+
move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
|
664
|
+
end
|
665
|
+
if (old_y + old_dialog_vertical_offset + old_dialog_contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
|
666
|
+
# rerender bottom
|
667
|
+
move_cursor_down(dialog.vertical_offset + dialog.contents.size - y_diff)
|
668
|
+
start = visual_start + dialog.vertical_offset + dialog.contents.size
|
669
|
+
line_num = (old_dialog_vertical_offset + old_dialog_contents.size) - (dialog.vertical_offset + dialog.contents.size)
|
670
|
+
line_num.times do |i|
|
671
|
+
Reline::IOGate.move_cursor_column(old_dialog_column)
|
672
|
+
if visual_lines[start + i].nil?
|
673
|
+
s = ' ' * DIALOG_WIDTH
|
674
|
+
else
|
675
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
|
676
|
+
end
|
677
|
+
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
|
678
|
+
move_cursor_down(1) if i < (line_num - 1)
|
679
|
+
end
|
680
|
+
move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
|
681
|
+
end
|
682
|
+
if old_dialog_column < dialog.column
|
683
|
+
# rerender left
|
684
|
+
move_cursor_down(old_dialog_vertical_offset - y_diff)
|
685
|
+
width = dialog.column - old_dialog_column
|
686
|
+
start = visual_start + old_dialog_vertical_offset
|
687
|
+
line_num = old_dialog_contents.size
|
688
|
+
line_num.times do |i|
|
689
|
+
Reline::IOGate.move_cursor_column(old_dialog_column)
|
690
|
+
if visual_lines[start + i].nil?
|
691
|
+
s = ' ' * width
|
692
|
+
else
|
693
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, width)
|
694
|
+
end
|
695
|
+
@output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
|
696
|
+
move_cursor_down(1) if i < (line_num - 1)
|
697
|
+
end
|
698
|
+
move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
|
699
|
+
end
|
700
|
+
if (old_dialog_column + DIALOG_WIDTH) > (dialog.column + DIALOG_WIDTH)
|
701
|
+
# rerender right
|
702
|
+
move_cursor_down(old_dialog_vertical_offset + y_diff)
|
703
|
+
width = (old_dialog_column + DIALOG_WIDTH) - (dialog.column + DIALOG_WIDTH)
|
704
|
+
start = visual_start + old_dialog_vertical_offset
|
705
|
+
line_num = old_dialog_contents.size
|
706
|
+
line_num.times do |i|
|
707
|
+
Reline::IOGate.move_cursor_column(old_dialog_column + DIALOG_WIDTH)
|
708
|
+
if visual_lines[start + i].nil?
|
709
|
+
s = ' ' * width
|
710
|
+
else
|
711
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column + DIALOG_WIDTH, width)
|
712
|
+
end
|
713
|
+
Reline::IOGate.move_cursor_column(dialog.column + DIALOG_WIDTH)
|
714
|
+
@output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
|
715
|
+
move_cursor_down(1) if i < (line_num - 1)
|
716
|
+
end
|
717
|
+
move_cursor_up(old_dialog_vertical_offset + line_num - 1 + y_diff)
|
718
|
+
end
|
719
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
720
|
+
end
|
721
|
+
|
722
|
+
private def clear_dialog
|
723
|
+
@dialogs.each do |dialog|
|
724
|
+
clear_each_dialog(dialog)
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
private def clear_each_dialog(dialog)
|
729
|
+
return unless dialog.contents
|
730
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
|
731
|
+
visual_lines = []
|
732
|
+
visual_lines_under_dialog = []
|
733
|
+
visual_start = nil
|
734
|
+
dialog.lines_backup[:lines].each_with_index { |l, i|
|
735
|
+
pr = prompt_list ? prompt_list[i] : prompt
|
736
|
+
vl, _ = split_by_width(pr + l, @screen_size.last)
|
737
|
+
vl.compact!
|
738
|
+
if i == dialog.lines_backup[:line_index]
|
739
|
+
visual_start = visual_lines.size + dialog.lines_backup[:started_from] + dialog.vertical_offset
|
740
|
+
end
|
741
|
+
visual_lines.concat(vl)
|
742
|
+
}
|
743
|
+
visual_lines_under_dialog = visual_lines[visual_start, dialog.contents.size]
|
744
|
+
visual_lines_under_dialog = [] if visual_lines_under_dialog.nil?
|
745
|
+
Reline::IOGate.hide_cursor
|
746
|
+
move_cursor_down(dialog.vertical_offset)
|
747
|
+
dialog_vertical_size = dialog.contents.size
|
748
|
+
dialog_vertical_size.times do |i|
|
749
|
+
if i < visual_lines_under_dialog.size
|
750
|
+
Reline::IOGate.move_cursor_column(0)
|
751
|
+
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % visual_lines_under_dialog[i]
|
752
|
+
else
|
753
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
754
|
+
@output.write "\e[39m\e[49m#{' ' * DIALOG_WIDTH}\e[39m\e[49m"
|
755
|
+
end
|
756
|
+
Reline::IOGate.erase_after_cursor
|
757
|
+
move_cursor_down(1) if i < (dialog_vertical_size - 1)
|
758
|
+
end
|
759
|
+
move_cursor_up(dialog_vertical_size - 1 + dialog.vertical_offset)
|
760
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
761
|
+
Reline::IOGate.show_cursor
|
762
|
+
end
|
763
|
+
|
468
764
|
private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
|
469
765
|
if @screen_height < highest_in_all
|
470
766
|
old_scroll_partial_screen = @scroll_partial_screen
|
@@ -678,7 +974,6 @@ class Reline::LineEditor
|
|
678
974
|
private def render_partial(prompt, prompt_width, line_to_render, this_started_from, with_control: true)
|
679
975
|
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
680
976
|
cursor_up_from_last_line = 0
|
681
|
-
# TODO: This logic would be sometimes buggy if this logical line isn't the current @line_index.
|
682
977
|
if @scroll_partial_screen
|
683
978
|
last_visual_line = this_started_from + (height - 1)
|
684
979
|
last_screen_line = @scroll_partial_screen + (@screen_height - 1)
|
@@ -813,6 +1108,7 @@ class Reline::LineEditor
|
|
813
1108
|
end
|
814
1109
|
move_cursor_up(back)
|
815
1110
|
move_cursor_down(@first_line_started_from + @started_from)
|
1111
|
+
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
816
1112
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
817
1113
|
end
|
818
1114
|
|
@@ -925,6 +1221,16 @@ class Reline::LineEditor
|
|
925
1221
|
@completion_journey_data = CompletionJourneyData.new(
|
926
1222
|
preposing, postposing,
|
927
1223
|
[target] + list.select{ |item| item.start_with?(target) }, 0)
|
1224
|
+
if @completion_journey_data.list.size == 1
|
1225
|
+
@completion_journey_data.pointer = 0
|
1226
|
+
else
|
1227
|
+
case direction
|
1228
|
+
when :up
|
1229
|
+
@completion_journey_data.pointer = @completion_journey_data.list.size - 1
|
1230
|
+
when :down
|
1231
|
+
@completion_journey_data.pointer = 1
|
1232
|
+
end
|
1233
|
+
end
|
928
1234
|
@completion_state = CompletionState::JOURNEY
|
929
1235
|
else
|
930
1236
|
case direction
|
@@ -939,13 +1245,13 @@ class Reline::LineEditor
|
|
939
1245
|
@completion_journey_data.pointer = 0
|
940
1246
|
end
|
941
1247
|
end
|
942
|
-
completed = @completion_journey_data.list[@completion_journey_data.pointer]
|
943
|
-
@line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
|
944
|
-
line_to_pointer = @completion_journey_data.preposing + completed
|
945
|
-
@cursor_max = calculate_width(@line)
|
946
|
-
@cursor = calculate_width(line_to_pointer)
|
947
|
-
@byte_pointer = line_to_pointer.bytesize
|
948
1248
|
end
|
1249
|
+
completed = @completion_journey_data.list[@completion_journey_data.pointer]
|
1250
|
+
@line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
|
1251
|
+
line_to_pointer = @completion_journey_data.preposing + completed
|
1252
|
+
@cursor_max = calculate_width(@line)
|
1253
|
+
@cursor = calculate_width(line_to_pointer)
|
1254
|
+
@byte_pointer = line_to_pointer.bytesize
|
949
1255
|
end
|
950
1256
|
|
951
1257
|
private def run_for_operators(key, method_symbol, &block)
|
@@ -1139,6 +1445,7 @@ class Reline::LineEditor
|
|
1139
1445
|
end
|
1140
1446
|
unless completion_occurs
|
1141
1447
|
@completion_state = CompletionState::NORMAL
|
1448
|
+
@completion_journey_data = nil
|
1142
1449
|
end
|
1143
1450
|
if not @in_pasting and @just_cursor_moving.nil?
|
1144
1451
|
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
|
@@ -1158,12 +1465,34 @@ class Reline::LineEditor
|
|
1158
1465
|
|
1159
1466
|
def call_completion_proc
|
1160
1467
|
result = retrieve_completion_block(true)
|
1161
|
-
|
1162
|
-
result =
|
1468
|
+
pre, target, post = result
|
1469
|
+
result = call_completion_proc_with_checking_args(pre, target, post)
|
1163
1470
|
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
1164
1471
|
result
|
1165
1472
|
end
|
1166
1473
|
|
1474
|
+
def call_completion_proc_with_checking_args(pre, target, post)
|
1475
|
+
if @completion_proc and target
|
1476
|
+
argnum = @completion_proc.parameters.inject(0) { |result, item|
|
1477
|
+
case item.first
|
1478
|
+
when :req, :opt
|
1479
|
+
result + 1
|
1480
|
+
when :rest
|
1481
|
+
break 3
|
1482
|
+
end
|
1483
|
+
}
|
1484
|
+
case argnum
|
1485
|
+
when 1
|
1486
|
+
result = @completion_proc.(target)
|
1487
|
+
when 2
|
1488
|
+
result = @completion_proc.(target, pre)
|
1489
|
+
when 3..Float::INFINITY
|
1490
|
+
result = @completion_proc.(target, pre, post)
|
1491
|
+
end
|
1492
|
+
end
|
1493
|
+
result
|
1494
|
+
end
|
1495
|
+
|
1167
1496
|
private def process_auto_indent
|
1168
1497
|
return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
|
1169
1498
|
if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
|
@@ -1207,8 +1536,16 @@ class Reline::LineEditor
|
|
1207
1536
|
end
|
1208
1537
|
|
1209
1538
|
def retrieve_completion_block(set_completion_quote_character = false)
|
1210
|
-
|
1211
|
-
|
1539
|
+
if Reline.completer_word_break_characters.empty?
|
1540
|
+
word_break_regexp = nil
|
1541
|
+
else
|
1542
|
+
word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
|
1543
|
+
end
|
1544
|
+
if Reline.completer_quote_characters.empty?
|
1545
|
+
quote_characters_regexp = nil
|
1546
|
+
else
|
1547
|
+
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
|
1548
|
+
end
|
1212
1549
|
before = @line.byteslice(0, @byte_pointer)
|
1213
1550
|
rest = nil
|
1214
1551
|
break_pointer = nil
|
@@ -1229,14 +1566,14 @@ class Reline::LineEditor
|
|
1229
1566
|
elsif quote and slice.start_with?(escaped_quote)
|
1230
1567
|
# skip
|
1231
1568
|
i += 2
|
1232
|
-
elsif slice =~ quote_characters_regexp # find new "
|
1569
|
+
elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
|
1233
1570
|
rest = $'
|
1234
1571
|
quote = $&
|
1235
1572
|
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
1236
1573
|
escaped_quote = /\\#{Regexp.escape(quote)}/
|
1237
1574
|
i += 1
|
1238
1575
|
break_pointer = i - 1
|
1239
|
-
elsif not quote and slice =~ word_break_regexp
|
1576
|
+
elsif word_break_regexp and not quote and slice =~ word_break_regexp
|
1240
1577
|
rest = $'
|
1241
1578
|
i += 1
|
1242
1579
|
before = @line.byteslice(i, @byte_pointer - i)
|
@@ -1264,6 +1601,19 @@ class Reline::LineEditor
|
|
1264
1601
|
end
|
1265
1602
|
target = before
|
1266
1603
|
end
|
1604
|
+
if @is_multiline
|
1605
|
+
if @previous_line_index
|
1606
|
+
lines = whole_lines(index: @previous_line_index, line: @line)
|
1607
|
+
else
|
1608
|
+
lines = whole_lines
|
1609
|
+
end
|
1610
|
+
if @line_index > 0
|
1611
|
+
preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
|
1612
|
+
end
|
1613
|
+
if (lines.size - 1) > @line_index
|
1614
|
+
postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
|
1615
|
+
end
|
1616
|
+
end
|
1267
1617
|
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
|
1268
1618
|
end
|
1269
1619
|
|
@@ -1291,10 +1641,32 @@ class Reline::LineEditor
|
|
1291
1641
|
|
1292
1642
|
def delete_text(start = nil, length = nil)
|
1293
1643
|
if start.nil? and length.nil?
|
1294
|
-
@
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1644
|
+
if @is_multiline
|
1645
|
+
if @buffer_of_lines.size == 1
|
1646
|
+
@line&.clear
|
1647
|
+
@byte_pointer = 0
|
1648
|
+
@cursor = 0
|
1649
|
+
@cursor_max = 0
|
1650
|
+
elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
|
1651
|
+
@buffer_of_lines.pop
|
1652
|
+
@line_index -= 1
|
1653
|
+
@line = @buffer_of_lines[@line_index]
|
1654
|
+
@byte_pointer = 0
|
1655
|
+
@cursor = 0
|
1656
|
+
@cursor_max = calculate_width(@line)
|
1657
|
+
elsif @line_index < (@buffer_of_lines.size - 1)
|
1658
|
+
@buffer_of_lines.delete_at(@line_index)
|
1659
|
+
@line = @buffer_of_lines[@line_index]
|
1660
|
+
@byte_pointer = 0
|
1661
|
+
@cursor = 0
|
1662
|
+
@cursor_max = calculate_width(@line)
|
1663
|
+
end
|
1664
|
+
else
|
1665
|
+
@line&.clear
|
1666
|
+
@byte_pointer = 0
|
1667
|
+
@cursor = 0
|
1668
|
+
@cursor_max = 0
|
1669
|
+
end
|
1298
1670
|
elsif not start.nil? and not length.nil?
|
1299
1671
|
if @line
|
1300
1672
|
before = @line.byteslice(0, start)
|