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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7612855f90bdd7b5602c7d9e7b2b4de200d6986c45ff64b5a356380c1211b83e
4
- data.tar.gz: 58406022787d706f400e828e31fde79b3e90c09a106b82a84218e5b748725db4
3
+ metadata.gz: d6e100afbceacfa4a270221c7e5158f211dc9972efbbec251108661a380d43b1
4
+ data.tar.gz: 051ef05dac8d446be2b0268202da3762d702287650dc05a3041aa7910f481624
5
5
  SHA512:
6
- metadata.gz: dcd02ec0e09c6dafa6cf3e9e1dbbeab9befabe033a1ae581628ff01fb4435fce3bb4cbe4cccb5890761e1a58a6637379a5f154ed67f28edaf46a71ba6a00d5c1
7
- data.tar.gz: 55a61bcab1c41d35002462c80f11d4406426a815bc84ddb680f73c023139fea95e89bc5cc3d1a9e25f1b77ed0986923e5152cfdd489b9e484bd2e459a6602256
6
+ metadata.gz: '0068601561915d76b2ce265c7de49252941dbe1e57464db9b08876dfd49da6fc12c3821d90d8240b4f64728a4a55a469726233689fa077ef7296796495025295'
7
+ data.tar.gz: 4385616c29cba0596c84d98219d658361cae3dc8c9427e8460d1703972995a9adcb242ab252c30fe2abcf712d2d824a4cb41691b44e241a9d711a89da3e0d62d
@@ -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(Encoding::default_external)
56
+ @completion_append_character = val.encode(Reline::IOGate.encoding)
53
57
  elsif val.size > 1
54
- @completion_append_character = val[0].encode(Encoding::default_external)
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(Encoding::default_external)
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(Encoding::default_external)
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(Encoding::default_external)
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(Encoding::default_external)
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(Encoding::default_external)
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(Encoding::default_external)
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
- print "\u{25bd}"
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.get_screen_size == [0, 0]
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'
@@ -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
- [27, 91, 51, 126] => :key_delete, # Del
10
- [27, 91, 49, 126] => :ed_move_to_beg, # Home
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
- print "\e[#{x + 1}G"
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
- print "\e[#{x}A" if x > 0
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
- print "\e[#{x}B" if x > 0
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
- print "\e[K"
147
+ @@output.write "\e[K"
112
148
  end
113
149
 
114
150
  def self.scroll_down(x)
115
151
  return if x.zero?
116
- print "\e[#{x}S"
152
+ @@output.write "\e[#{x}S"
117
153
  end
118
154
 
119
155
  def self.clear_screen
120
- print "\e[2J"
121
- print "\e[1;1H"
156
+ @@output.write "\e[2J"
157
+ @@output.write "\e[1;1H"
122
158
  end
123
159
 
124
160
  @@old_winch_handler = nil
@@ -184,9 +184,8 @@ class Reline::Config
184
184
 
185
185
  def bind_variable(name, value)
186
186
  case name
187
- when *VARIABLE_NAMES then
188
- variable_name = :"@#{name.tr(?-, ?_)}"
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
 
@@ -1,6 +1,14 @@
1
1
  require 'timeout'
2
2
 
3
3
  class Reline::GeneralIO
4
+ def self.encoding
5
+ RUBY_PLATFORM =~ /mswin|mingw/ ? Encoding::UTF_8 : Encoding::default_external
6
+ end
7
+
8
+ def self.win?
9
+ false
10
+ end
11
+
4
12
  RAW_KEYSTROKE_CONFIG = {}
5
13
 
6
14
  @@buf = []
@@ -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: Encoding::default_external))
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: Encoding::default_external) }))
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: Encoding::default_external))
47
+ super(String.new(val, encoding: Reline.encoding_system_needs))
48
48
  end
49
49
 
50
50
  private def check_index(index)
@@ -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 = Encoding.default_external)
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 = Encoding.default_external)
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.print item
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.erase_after_cursor
511
- move_cursor_down(1)
512
- Reline::IOGate.move_cursor_column(0)
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.print line
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 search_next_char(key, arg)
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
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.3'
3
3
  end
@@ -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).encode(Encoding.default_external).bytes
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.first, get_screen_size.last].pack('s4')
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
- print "\e[2J"
217
- print "\e[1;1H"
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.2
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: 2019-12-25 00:00:00.000000000 Z
11
+ date: 2020-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console