reline 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/reline.rb +21 -15
- data/lib/reline/ansi.rb +47 -11
- data/lib/reline/config.rb +5 -3
- data/lib/reline/general_io.rb +8 -0
- data/lib/reline/history.rb +3 -3
- data/lib/reline/line_editor.rb +117 -15
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +41 -4
- 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: d6e100afbceacfa4a270221c7e5158f211dc9972efbbec251108661a380d43b1
|
4
|
+
data.tar.gz: 051ef05dac8d446be2b0268202da3762d702287650dc05a3041aa7910f481624
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0068601561915d76b2ce265c7de49252941dbe1e57464db9b08876dfd49da6fc12c3821d90d8240b4f64728a4a55a469726233689fa077ef7296796495025295'
|
7
|
+
data.tar.gz: 4385616c29cba0596c84d98219d658361cae3dc8c9427e8460d1703972995a9adcb242ab252c30fe2abcf712d2d824a4cb41691b44e241a9d711a89da3e0d62d
|
data/lib/reline.rb
CHANGED
@@ -45,40 +45,44 @@ module Reline
|
|
45
45
|
@completion_quote_character = nil
|
46
46
|
end
|
47
47
|
|
48
|
+
def encoding
|
49
|
+
Reline::IOGate.encoding
|
50
|
+
end
|
51
|
+
|
48
52
|
def completion_append_character=(val)
|
49
53
|
if val.nil?
|
50
54
|
@completion_append_character = nil
|
51
55
|
elsif val.size == 1
|
52
|
-
@completion_append_character = val.encode(
|
56
|
+
@completion_append_character = val.encode(Reline::IOGate.encoding)
|
53
57
|
elsif val.size > 1
|
54
|
-
@completion_append_character = val[0].encode(
|
58
|
+
@completion_append_character = val[0].encode(Reline::IOGate.encoding)
|
55
59
|
else
|
56
60
|
@completion_append_character = nil
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
60
64
|
def basic_word_break_characters=(v)
|
61
|
-
@basic_word_break_characters = v.encode(
|
65
|
+
@basic_word_break_characters = v.encode(Reline::IOGate.encoding)
|
62
66
|
end
|
63
67
|
|
64
68
|
def completer_word_break_characters=(v)
|
65
|
-
@completer_word_break_characters = v.encode(
|
69
|
+
@completer_word_break_characters = v.encode(Reline::IOGate.encoding)
|
66
70
|
end
|
67
71
|
|
68
72
|
def basic_quote_characters=(v)
|
69
|
-
@basic_quote_characters = v.encode(
|
73
|
+
@basic_quote_characters = v.encode(Reline::IOGate.encoding)
|
70
74
|
end
|
71
75
|
|
72
76
|
def completer_quote_characters=(v)
|
73
|
-
@completer_quote_characters = v.encode(
|
77
|
+
@completer_quote_characters = v.encode(Reline::IOGate.encoding)
|
74
78
|
end
|
75
79
|
|
76
80
|
def filename_quote_characters=(v)
|
77
|
-
@filename_quote_characters = v.encode(
|
81
|
+
@filename_quote_characters = v.encode(Reline::IOGate.encoding)
|
78
82
|
end
|
79
83
|
|
80
84
|
def special_prefixes=(v)
|
81
|
-
@special_prefixes = v.encode(
|
85
|
+
@special_prefixes = v.encode(Reline::IOGate.encoding)
|
82
86
|
end
|
83
87
|
|
84
88
|
def completion_case_fold=(v)
|
@@ -201,7 +205,7 @@ module Reline
|
|
201
205
|
otio = Reline::IOGate.prep
|
202
206
|
|
203
207
|
may_req_ambiguous_char_width
|
204
|
-
line_editor.reset(prompt)
|
208
|
+
line_editor.reset(prompt, encoding: Reline::IOGate.encoding)
|
205
209
|
if multiline
|
206
210
|
line_editor.multiline_on
|
207
211
|
if block_given?
|
@@ -332,7 +336,7 @@ module Reline
|
|
332
336
|
@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
|
333
337
|
return if ambiguous_width
|
334
338
|
Reline::IOGate.move_cursor_column(0)
|
335
|
-
|
339
|
+
output.write "\u{25bd}"
|
336
340
|
@ambiguous_width = Reline::IOGate.cursor_pos.x
|
337
341
|
Reline::IOGate.move_cursor_column(0)
|
338
342
|
Reline::IOGate.erase_after_cursor
|
@@ -387,11 +391,15 @@ module Reline
|
|
387
391
|
def_instance_delegators self, :readmultiline
|
388
392
|
private :readmultiline
|
389
393
|
|
394
|
+
def self.encoding_system_needs
|
395
|
+
self.core.encoding
|
396
|
+
end
|
397
|
+
|
390
398
|
def self.core
|
391
399
|
@core ||= Core.new { |core|
|
392
400
|
core.config = Reline::Config.new
|
393
401
|
core.key_stroke = Reline::KeyStroke.new(core.config)
|
394
|
-
core.line_editor = Reline::LineEditor.new(core.config)
|
402
|
+
core.line_editor = Reline::LineEditor.new(core.config, Reline::IOGate.encoding)
|
395
403
|
|
396
404
|
core.basic_word_break_characters = " \t\n`><=;|&{("
|
397
405
|
core.completer_word_break_characters = " \t\n`><=;|&{("
|
@@ -405,14 +413,11 @@ module Reline
|
|
405
413
|
def self.line_editor
|
406
414
|
core.line_editor
|
407
415
|
end
|
408
|
-
|
409
|
-
HISTORY = History.new(core.config)
|
410
416
|
end
|
411
417
|
|
412
418
|
if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
413
419
|
require 'reline/windows'
|
414
|
-
if Reline::Windows.
|
415
|
-
# Maybe Mintty on Cygwin
|
420
|
+
if Reline::Windows.msys_tty?
|
416
421
|
require 'reline/ansi'
|
417
422
|
Reline::IOGate = Reline::ANSI
|
418
423
|
else
|
@@ -422,4 +427,5 @@ else
|
|
422
427
|
require 'reline/ansi'
|
423
428
|
Reline::IOGate = Reline::ANSI
|
424
429
|
end
|
430
|
+
Reline::HISTORY = Reline::History.new(Reline.core.config)
|
425
431
|
require 'reline/general_io'
|
data/lib/reline/ansi.rb
CHANGED
@@ -1,20 +1,49 @@
|
|
1
1
|
require 'io/console'
|
2
2
|
|
3
3
|
class Reline::ANSI
|
4
|
+
def self.encoding
|
5
|
+
Encoding.default_external
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.win?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
4
12
|
RAW_KEYSTROKE_CONFIG = {
|
13
|
+
# Console (80x25)
|
14
|
+
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
15
|
+
[27, 91, 52, 126] => :ed_move_to_end, # End
|
16
|
+
[27, 91, 51, 126] => :key_delete, # Del
|
5
17
|
[27, 91, 65] => :ed_prev_history, # ↑
|
6
18
|
[27, 91, 66] => :ed_next_history, # ↓
|
7
19
|
[27, 91, 67] => :ed_next_char, # →
|
8
20
|
[27, 91, 68] => :ed_prev_char, # ←
|
9
|
-
|
10
|
-
|
11
|
-
[27, 91, 52, 126] => :ed_move_to_end, # End
|
21
|
+
|
22
|
+
# KDE
|
12
23
|
[27, 91, 72] => :ed_move_to_beg, # Home
|
13
24
|
[27, 91, 70] => :ed_move_to_end, # End
|
25
|
+
# Del is 0x08
|
26
|
+
[27, 71, 65] => :ed_prev_history, # ↑
|
27
|
+
[27, 71, 66] => :ed_next_history, # ↓
|
28
|
+
[27, 71, 67] => :ed_next_char, # →
|
29
|
+
[27, 71, 68] => :ed_prev_char, # ←
|
30
|
+
|
31
|
+
# GNOME
|
32
|
+
[27, 79, 72] => :ed_move_to_beg, # Home
|
33
|
+
[27, 79, 70] => :ed_move_to_end, # End
|
34
|
+
# Del is 0x08
|
35
|
+
# Arrow keys are the same of KDE
|
36
|
+
|
37
|
+
# others
|
14
38
|
[27, 32] => :em_set_mark, # M-<space>
|
15
39
|
[24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
|
16
40
|
[27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→
|
17
41
|
[27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←
|
42
|
+
|
43
|
+
[27, 79, 65] => :ed_prev_history, # ↑
|
44
|
+
[27, 79, 66] => :ed_next_history, # ↓
|
45
|
+
[27, 79, 67] => :ed_next_char, # →
|
46
|
+
[27, 79, 68] => :ed_prev_char, # ←
|
18
47
|
}
|
19
48
|
|
20
49
|
@@input = STDIN
|
@@ -41,16 +70,23 @@ class Reline::ANSI
|
|
41
70
|
end
|
42
71
|
|
43
72
|
def self.retrieve_keybuffer
|
73
|
+
begin
|
44
74
|
result = select([@@input], [], [], 0.001)
|
45
75
|
return if result.nil?
|
46
76
|
str = @@input.read_nonblock(1024)
|
47
77
|
str.bytes.each do |c|
|
48
78
|
@@buf.push(c)
|
49
79
|
end
|
80
|
+
rescue EOFError
|
81
|
+
end
|
50
82
|
end
|
51
83
|
|
52
84
|
def self.get_screen_size
|
53
|
-
@@input.winsize
|
85
|
+
s = @@input.winsize
|
86
|
+
return s if s[0] > 0 && s[1] > 0
|
87
|
+
s = [ENV["LINES"].to_i, ENV["COLUMNS"].to_i]
|
88
|
+
return s if s[0] > 0 && s[1] > 0
|
89
|
+
[24, 80]
|
54
90
|
rescue Errno::ENOTTY
|
55
91
|
[24, 80]
|
56
92
|
end
|
@@ -88,12 +124,12 @@ class Reline::ANSI
|
|
88
124
|
end
|
89
125
|
|
90
126
|
def self.move_cursor_column(x)
|
91
|
-
|
127
|
+
@@output.write "\e[#{x + 1}G"
|
92
128
|
end
|
93
129
|
|
94
130
|
def self.move_cursor_up(x)
|
95
131
|
if x > 0
|
96
|
-
|
132
|
+
@@output.write "\e[#{x}A" if x > 0
|
97
133
|
elsif x < 0
|
98
134
|
move_cursor_down(-x)
|
99
135
|
end
|
@@ -101,24 +137,24 @@ class Reline::ANSI
|
|
101
137
|
|
102
138
|
def self.move_cursor_down(x)
|
103
139
|
if x > 0
|
104
|
-
|
140
|
+
@@output.write "\e[#{x}B" if x > 0
|
105
141
|
elsif x < 0
|
106
142
|
move_cursor_up(-x)
|
107
143
|
end
|
108
144
|
end
|
109
145
|
|
110
146
|
def self.erase_after_cursor
|
111
|
-
|
147
|
+
@@output.write "\e[K"
|
112
148
|
end
|
113
149
|
|
114
150
|
def self.scroll_down(x)
|
115
151
|
return if x.zero?
|
116
|
-
|
152
|
+
@@output.write "\e[#{x}S"
|
117
153
|
end
|
118
154
|
|
119
155
|
def self.clear_screen
|
120
|
-
|
121
|
-
|
156
|
+
@@output.write "\e[2J"
|
157
|
+
@@output.write "\e[1;1H"
|
122
158
|
end
|
123
159
|
|
124
160
|
@@old_winch_handler = nil
|
data/lib/reline/config.rb
CHANGED
@@ -184,9 +184,8 @@ class Reline::Config
|
|
184
184
|
|
185
185
|
def bind_variable(name, value)
|
186
186
|
case name
|
187
|
-
when
|
188
|
-
|
189
|
-
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
|
187
|
+
when 'history-size'
|
188
|
+
@history_size = value.to_i
|
190
189
|
when 'bell-style'
|
191
190
|
@bell_style =
|
192
191
|
case value
|
@@ -225,6 +224,9 @@ class Reline::Config
|
|
225
224
|
end
|
226
225
|
when 'keyseq-timeout'
|
227
226
|
@keyseq_timeout = value.to_i
|
227
|
+
when *VARIABLE_NAMES then
|
228
|
+
variable_name = :"@#{name.tr(?-, ?_)}"
|
229
|
+
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
|
228
230
|
end
|
229
231
|
end
|
230
232
|
|
data/lib/reline/general_io.rb
CHANGED
data/lib/reline/history.rb
CHANGED
@@ -19,7 +19,7 @@ class Reline::History < Array
|
|
19
19
|
|
20
20
|
def []=(index, val)
|
21
21
|
index = check_index(index)
|
22
|
-
super(index, String.new(val, encoding:
|
22
|
+
super(index, String.new(val, encoding: Reline.encoding_system_needs))
|
23
23
|
end
|
24
24
|
|
25
25
|
def concat(*val)
|
@@ -39,12 +39,12 @@ class Reline::History < Array
|
|
39
39
|
val.shift(diff)
|
40
40
|
end
|
41
41
|
end
|
42
|
-
super(*(val.map{ |v| String.new(v, encoding:
|
42
|
+
super(*(val.map{ |v| String.new(v, encoding: Reline.encoding_system_needs) }))
|
43
43
|
end
|
44
44
|
|
45
45
|
def <<(val)
|
46
46
|
shift if size + 1 > @config.history_size
|
47
|
-
super(String.new(val, encoding:
|
47
|
+
super(String.new(val, encoding: Reline.encoding_system_needs))
|
48
48
|
end
|
49
49
|
|
50
50
|
private def check_index(index)
|
data/lib/reline/line_editor.rb
CHANGED
@@ -57,10 +57,10 @@ class Reline::LineEditor
|
|
57
57
|
NON_PRINTING_END = "\2"
|
58
58
|
WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/
|
59
59
|
|
60
|
-
def initialize(config)
|
60
|
+
def initialize(config, encoding)
|
61
61
|
@config = config
|
62
62
|
@completion_append_character = ''
|
63
|
-
reset_variables
|
63
|
+
reset_variables(encoding: encoding)
|
64
64
|
end
|
65
65
|
|
66
66
|
private def check_multiline_prompt(buffer, prompt)
|
@@ -85,10 +85,10 @@ class Reline::LineEditor
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
def reset(prompt = '', encoding
|
88
|
+
def reset(prompt = '', encoding:)
|
89
89
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
90
90
|
@screen_size = Reline::IOGate.get_screen_size
|
91
|
-
reset_variables(prompt, encoding)
|
91
|
+
reset_variables(prompt, encoding: encoding)
|
92
92
|
@old_trap = Signal.trap('SIGINT') {
|
93
93
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
94
94
|
raise Interrupt
|
@@ -139,7 +139,7 @@ class Reline::LineEditor
|
|
139
139
|
@eof
|
140
140
|
end
|
141
141
|
|
142
|
-
def reset_variables(prompt = '', encoding
|
142
|
+
def reset_variables(prompt = '', encoding:)
|
143
143
|
@prompt = prompt
|
144
144
|
@mark_pointer = nil
|
145
145
|
@encoding = encoding
|
@@ -317,9 +317,9 @@ class Reline::LineEditor
|
|
317
317
|
if @menu_info
|
318
318
|
scroll_down(@highest_in_all - @first_line_started_from)
|
319
319
|
@rerender_all = true
|
320
|
-
@menu_info.list.each do |item|
|
320
|
+
@menu_info.list.sort!.each do |item|
|
321
321
|
Reline::IOGate.move_cursor_column(0)
|
322
|
-
@output.
|
322
|
+
@output.write item
|
323
323
|
@output.flush
|
324
324
|
scroll_down(1)
|
325
325
|
end
|
@@ -507,12 +507,20 @@ class Reline::LineEditor
|
|
507
507
|
Reline::IOGate.move_cursor_column(0)
|
508
508
|
visual_lines.each_with_index do |line, index|
|
509
509
|
if line.nil?
|
510
|
-
Reline::IOGate.
|
511
|
-
|
512
|
-
|
510
|
+
if Reline::IOGate.win? and calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
511
|
+
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
512
|
+
else
|
513
|
+
Reline::IOGate.erase_after_cursor
|
514
|
+
move_cursor_down(1)
|
515
|
+
Reline::IOGate.move_cursor_column(0)
|
516
|
+
end
|
513
517
|
next
|
514
518
|
end
|
515
|
-
@output.
|
519
|
+
@output.write line
|
520
|
+
if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
|
521
|
+
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
522
|
+
@rest_height -= 1 if @rest_height > 0
|
523
|
+
end
|
516
524
|
@output.flush
|
517
525
|
if @first_prompt
|
518
526
|
@first_prompt = false
|
@@ -905,7 +913,6 @@ class Reline::LineEditor
|
|
905
913
|
quote = nil
|
906
914
|
i += 1
|
907
915
|
rest = nil
|
908
|
-
break_pointer = nil
|
909
916
|
elsif quote and slice.start_with?(escaped_quote)
|
910
917
|
# skip
|
911
918
|
i += 2
|
@@ -915,7 +922,7 @@ class Reline::LineEditor
|
|
915
922
|
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
916
923
|
escaped_quote = /\\#{Regexp.escape(quote)}/
|
917
924
|
i += 1
|
918
|
-
break_pointer = i
|
925
|
+
break_pointer = i - 1
|
919
926
|
elsif not quote and slice =~ word_break_regexp
|
920
927
|
rest = $'
|
921
928
|
i += 1
|
@@ -937,6 +944,11 @@ class Reline::LineEditor
|
|
937
944
|
end
|
938
945
|
else
|
939
946
|
preposing = ''
|
947
|
+
if break_pointer
|
948
|
+
preposing = @line.byteslice(0, break_pointer)
|
949
|
+
else
|
950
|
+
preposing = ''
|
951
|
+
end
|
940
952
|
target = before
|
941
953
|
end
|
942
954
|
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
|
@@ -1091,6 +1103,11 @@ class Reline::LineEditor
|
|
1091
1103
|
|
1092
1104
|
private def ed_insert(key)
|
1093
1105
|
if key.instance_of?(String)
|
1106
|
+
begin
|
1107
|
+
key.encode(Encoding::UTF_8)
|
1108
|
+
rescue Encoding::UndefinedConversionError
|
1109
|
+
return
|
1110
|
+
end
|
1094
1111
|
width = Reline::Unicode.get_mbchar_width(key)
|
1095
1112
|
if @cursor == @cursor_max
|
1096
1113
|
@line += key
|
@@ -1101,6 +1118,11 @@ class Reline::LineEditor
|
|
1101
1118
|
@cursor += width
|
1102
1119
|
@cursor_max += width
|
1103
1120
|
else
|
1121
|
+
begin
|
1122
|
+
key.chr.encode(Encoding::UTF_8)
|
1123
|
+
rescue Encoding::UndefinedConversionError
|
1124
|
+
return
|
1125
|
+
end
|
1104
1126
|
if @cursor == @cursor_max
|
1105
1127
|
@line += key.chr
|
1106
1128
|
else
|
@@ -1876,6 +1898,16 @@ class Reline::LineEditor
|
|
1876
1898
|
end
|
1877
1899
|
end
|
1878
1900
|
|
1901
|
+
private def vi_insert_at_bol(key)
|
1902
|
+
ed_move_to_beg(key)
|
1903
|
+
@config.editing_mode = :vi_insert
|
1904
|
+
end
|
1905
|
+
|
1906
|
+
private def vi_add_at_eol(key)
|
1907
|
+
ed_move_to_end(key)
|
1908
|
+
@config.editing_mode = :vi_insert
|
1909
|
+
end
|
1910
|
+
|
1879
1911
|
private def ed_delete_prev_char(key, arg: 1)
|
1880
1912
|
deleted = ''
|
1881
1913
|
arg.times do
|
@@ -1898,6 +1930,18 @@ class Reline::LineEditor
|
|
1898
1930
|
end
|
1899
1931
|
|
1900
1932
|
private def vi_change_meta(key)
|
1933
|
+
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
1934
|
+
if byte_pointer_diff > 0
|
1935
|
+
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
1936
|
+
elsif byte_pointer_diff < 0
|
1937
|
+
@line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
1938
|
+
end
|
1939
|
+
copy_for_vi(cut)
|
1940
|
+
@cursor += cursor_diff if cursor_diff < 0
|
1941
|
+
@cursor_max -= cursor_diff.abs
|
1942
|
+
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
1943
|
+
@config.editing_mode = :vi_insert
|
1944
|
+
}
|
1901
1945
|
end
|
1902
1946
|
|
1903
1947
|
private def vi_delete_meta(key)
|
@@ -2063,12 +2107,17 @@ class Reline::LineEditor
|
|
2063
2107
|
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
|
2064
2108
|
end
|
2065
2109
|
|
2066
|
-
private def
|
2110
|
+
private def vi_to_next_char(key, arg: 1)
|
2111
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
|
2112
|
+
end
|
2113
|
+
|
2114
|
+
private def search_next_char(key, arg, need_prev_char = false)
|
2067
2115
|
if key.instance_of?(String)
|
2068
2116
|
inputed_char = key
|
2069
2117
|
else
|
2070
2118
|
inputed_char = key.chr
|
2071
2119
|
end
|
2120
|
+
prev_total = nil
|
2072
2121
|
total = nil
|
2073
2122
|
found = false
|
2074
2123
|
@line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar|
|
@@ -2086,13 +2135,66 @@ class Reline::LineEditor
|
|
2086
2135
|
end
|
2087
2136
|
end
|
2088
2137
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2138
|
+
prev_total = total
|
2089
2139
|
total = [total.first + mbchar.bytesize, total.last + width]
|
2090
2140
|
end
|
2091
2141
|
end
|
2092
|
-
if found and total
|
2142
|
+
if not need_prev_char and found and total
|
2093
2143
|
byte_size, width = total
|
2094
2144
|
@byte_pointer += byte_size
|
2095
2145
|
@cursor += width
|
2146
|
+
elsif need_prev_char and found and prev_total
|
2147
|
+
byte_size, width = prev_total
|
2148
|
+
@byte_pointer += byte_size
|
2149
|
+
@cursor += width
|
2150
|
+
end
|
2151
|
+
@waiting_proc = nil
|
2152
|
+
end
|
2153
|
+
|
2154
|
+
private def vi_prev_char(key, arg: 1)
|
2155
|
+
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) }
|
2156
|
+
end
|
2157
|
+
|
2158
|
+
private def vi_to_prev_char(key, arg: 1)
|
2159
|
+
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) }
|
2160
|
+
end
|
2161
|
+
|
2162
|
+
private def search_prev_char(key, arg, need_next_char = false)
|
2163
|
+
if key.instance_of?(String)
|
2164
|
+
inputed_char = key
|
2165
|
+
else
|
2166
|
+
inputed_char = key.chr
|
2167
|
+
end
|
2168
|
+
prev_total = nil
|
2169
|
+
total = nil
|
2170
|
+
found = false
|
2171
|
+
@line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar|
|
2172
|
+
# total has [byte_size, cursor]
|
2173
|
+
unless total
|
2174
|
+
# skip cursor point
|
2175
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2176
|
+
total = [mbchar.bytesize, width]
|
2177
|
+
else
|
2178
|
+
if inputed_char == mbchar
|
2179
|
+
arg -= 1
|
2180
|
+
if arg.zero?
|
2181
|
+
found = true
|
2182
|
+
break
|
2183
|
+
end
|
2184
|
+
end
|
2185
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2186
|
+
prev_total = total
|
2187
|
+
total = [total.first + mbchar.bytesize, total.last + width]
|
2188
|
+
end
|
2189
|
+
end
|
2190
|
+
if not need_next_char and found and total
|
2191
|
+
byte_size, width = total
|
2192
|
+
@byte_pointer -= byte_size
|
2193
|
+
@cursor -= width
|
2194
|
+
elsif need_next_char and found and prev_total
|
2195
|
+
byte_size, width = prev_total
|
2196
|
+
@byte_pointer -= byte_size
|
2197
|
+
@cursor -= width
|
2096
2198
|
end
|
2097
2199
|
@waiting_proc = nil
|
2098
2200
|
end
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
require 'fiddle/import'
|
2
2
|
|
3
3
|
class Reline::Windows
|
4
|
+
def self.encoding
|
5
|
+
Encoding::UTF_8
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.win?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
4
12
|
RAW_KEYSTROKE_CONFIG = {
|
5
13
|
[224, 72] => :ed_prev_history, # ↑
|
6
14
|
[224, 80] => :ed_next_history, # ↓
|
@@ -68,6 +76,8 @@ class Reline::Windows
|
|
68
76
|
STD_INPUT_HANDLE = -10
|
69
77
|
STD_OUTPUT_HANDLE = -11
|
70
78
|
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
79
|
+
FILE_TYPE_PIPE = 0x0003
|
80
|
+
FILE_NAME_INFO = 2
|
71
81
|
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
72
82
|
@@kbhit = Win32API.new('msvcrt', '_kbhit', [], 'I')
|
73
83
|
@@GetKeyState = Win32API.new('user32', 'GetKeyState', ['L'], 'L')
|
@@ -80,9 +90,36 @@ class Reline::Windows
|
|
80
90
|
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
81
91
|
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
82
92
|
@@ReadConsoleInput = Win32API.new('kernel32', 'ReadConsoleInput', ['L', 'P', 'L', 'P'], 'L')
|
93
|
+
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
94
|
+
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
95
|
+
|
83
96
|
@@input_buf = []
|
84
97
|
@@output_buf = []
|
85
98
|
|
99
|
+
def self.msys_tty?(io=@@hConsoleInputHandle)
|
100
|
+
# check if fd is a pipe
|
101
|
+
if @@GetFileType.call(io) != FILE_TYPE_PIPE
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
|
105
|
+
bufsize = 1024
|
106
|
+
p_buffer = "\0" * bufsize
|
107
|
+
res = @@GetFileInformationByHandleEx.call(io, FILE_NAME_INFO, p_buffer, bufsize - 2)
|
108
|
+
return false if res == 0
|
109
|
+
|
110
|
+
# get pipe name: p_buffer layout is:
|
111
|
+
# struct _FILE_NAME_INFO {
|
112
|
+
# DWORD FileNameLength;
|
113
|
+
# WCHAR FileName[1];
|
114
|
+
# } FILE_NAME_INFO
|
115
|
+
len = p_buffer[0, 4].unpack("L")[0]
|
116
|
+
name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
|
117
|
+
|
118
|
+
# Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX')
|
119
|
+
# or a cygwin pty pipe ('\cygwin-XXXX-ptyN-XX')
|
120
|
+
name =~ /(msys-|cygwin-).*-pty/ ? true : false
|
121
|
+
end
|
122
|
+
|
86
123
|
def self.getwch
|
87
124
|
unless @@input_buf.empty?
|
88
125
|
return @@input_buf.shift
|
@@ -99,7 +136,7 @@ class Reline::Windows
|
|
99
136
|
return @@input_buf.shift
|
100
137
|
end
|
101
138
|
begin
|
102
|
-
bytes = ret.chr(Encoding::UTF_8).
|
139
|
+
bytes = ret.chr(Encoding::UTF_8).bytes
|
103
140
|
@@input_buf.push(*bytes)
|
104
141
|
rescue Encoding::UndefinedConversionError
|
105
142
|
@@input_buf << ret
|
@@ -205,7 +242,7 @@ class Reline::Windows
|
|
205
242
|
|
206
243
|
def self.scroll_down(val)
|
207
244
|
return if val.zero?
|
208
|
-
scroll_rectangle = [0, val, get_screen_size.
|
245
|
+
scroll_rectangle = [0, val, get_screen_size.last, get_screen_size.first].pack('s4')
|
209
246
|
destination_origin = 0 # y * 65536 + x
|
210
247
|
fill = [' '.ord, 0].pack('SS')
|
211
248
|
@@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
|
@@ -213,8 +250,8 @@ class Reline::Windows
|
|
213
250
|
|
214
251
|
def self.clear_screen
|
215
252
|
# TODO: Use FillConsoleOutputCharacter and FillConsoleOutputAttribute
|
216
|
-
|
217
|
-
|
253
|
+
write "\e[2J"
|
254
|
+
write "\e[1;1H"
|
218
255
|
end
|
219
256
|
|
220
257
|
def self.set_screen_size(rows, columns)
|
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.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|