reline 0.1.2 → 0.1.3
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/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
|