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 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