reline 0.1.0 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,6 +35,12 @@ class Reline::Unicode
35
35
  }
36
36
  EscapedChars = EscapedPairs.keys.map(&:chr)
37
37
 
38
+ CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
39
+ OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
40
+ NON_PRINTING_START = "\1"
41
+ NON_PRINTING_END = "\2"
42
+ WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/
43
+
38
44
  def self.get_mbchar_byte_size_by_first_char(c)
39
45
  # Checks UTF-8 character byte size
40
46
  case c.ord
@@ -85,6 +91,68 @@ class Reline::Unicode
85
91
  end
86
92
  end
87
93
 
94
+ def self.calculate_width(str, allow_escape_code = false)
95
+ if allow_escape_code
96
+ width = 0
97
+ rest = str.encode(Encoding::UTF_8)
98
+ in_zero_width = false
99
+ rest.scan(WIDTH_SCANNER) do |gc|
100
+ case gc
101
+ when NON_PRINTING_START
102
+ in_zero_width = true
103
+ when NON_PRINTING_END
104
+ in_zero_width = false
105
+ when CSI_REGEXP, OSC_REGEXP
106
+ else
107
+ unless in_zero_width
108
+ width += get_mbchar_width(gc)
109
+ end
110
+ end
111
+ end
112
+ width
113
+ else
114
+ str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
115
+ w + get_mbchar_width(gc)
116
+ }
117
+ end
118
+ end
119
+
120
+ def self.split_by_width(str, max_width, encoding = str.encoding)
121
+ lines = [String.new(encoding: encoding)]
122
+ height = 1
123
+ width = 0
124
+ rest = str.encode(Encoding::UTF_8)
125
+ in_zero_width = false
126
+ rest.scan(WIDTH_SCANNER) do |gc|
127
+ case gc
128
+ when NON_PRINTING_START
129
+ in_zero_width = true
130
+ when NON_PRINTING_END
131
+ in_zero_width = false
132
+ when CSI_REGEXP, OSC_REGEXP
133
+ lines.last << gc
134
+ else
135
+ unless in_zero_width
136
+ mbchar_width = get_mbchar_width(gc)
137
+ if (width += mbchar_width) > max_width
138
+ width = mbchar_width
139
+ lines << nil
140
+ lines << String.new(encoding: encoding)
141
+ height += 1
142
+ end
143
+ end
144
+ lines.last << gc
145
+ end
146
+ end
147
+ # The cursor moves to next line in first
148
+ if width == max_width
149
+ lines << nil
150
+ lines << String.new(encoding: encoding)
151
+ height += 1
152
+ end
153
+ [lines, height]
154
+ end
155
+
88
156
  def self.get_next_mbchar_size(line, byte_pointer)
89
157
  grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
90
158
  grapheme ? grapheme.bytesize : 0
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.5'
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,37 @@ 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
+ @@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
96
+
83
97
  @@input_buf = []
84
98
  @@output_buf = []
85
99
 
100
+ def self.msys_tty?(io=@@hConsoleInputHandle)
101
+ # check if fd is a pipe
102
+ if @@GetFileType.call(io) != FILE_TYPE_PIPE
103
+ return false
104
+ end
105
+
106
+ bufsize = 1024
107
+ p_buffer = "\0" * bufsize
108
+ res = @@GetFileInformationByHandleEx.call(io, FILE_NAME_INFO, p_buffer, bufsize - 2)
109
+ return false if res == 0
110
+
111
+ # get pipe name: p_buffer layout is:
112
+ # struct _FILE_NAME_INFO {
113
+ # DWORD FileNameLength;
114
+ # WCHAR FileName[1];
115
+ # } FILE_NAME_INFO
116
+ len = p_buffer[0, 4].unpack("L")[0]
117
+ name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
118
+
119
+ # Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX')
120
+ # or a cygwin pty pipe ('\cygwin-XXXX-ptyN-XX')
121
+ name =~ /(msys-|cygwin-).*-pty/ ? true : false
122
+ end
123
+
86
124
  def self.getwch
87
125
  unless @@input_buf.empty?
88
126
  return @@input_buf.shift
@@ -99,7 +137,7 @@ class Reline::Windows
99
137
  return @@input_buf.shift
100
138
  end
101
139
  begin
102
- bytes = ret.chr(Encoding::UTF_8).encode(Encoding.default_external).bytes
140
+ bytes = ret.chr(Encoding::UTF_8).bytes
103
141
  @@input_buf.push(*bytes)
104
142
  rescue Encoding::UndefinedConversionError
105
143
  @@input_buf << ret
@@ -205,16 +243,24 @@ class Reline::Windows
205
243
 
206
244
  def self.scroll_down(val)
207
245
  return if val.zero?
208
- scroll_rectangle = [0, val, get_screen_size.first, get_screen_size.last].pack('s4')
246
+ scroll_rectangle = [0, val, get_screen_size.last, get_screen_size.first].pack('s4')
209
247
  destination_origin = 0 # y * 65536 + x
210
248
  fill = [' '.ord, 0].pack('SS')
211
249
  @@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
212
250
  end
213
251
 
214
252
  def self.clear_screen
215
- # TODO: Use FillConsoleOutputCharacter and FillConsoleOutputAttribute
216
- print "\e[2J"
217
- print "\e[1;1H"
253
+ csbi = 0.chr * 22
254
+ return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0
255
+ buffer_width = csbi[0, 2].unpack('S').first
256
+ attributes = csbi[8, 2].unpack('S').first
257
+ _window_left, window_top, _window_right, window_bottom = *csbi[10,8].unpack('S*')
258
+ fill_length = buffer_width * (window_bottom - window_top + 1)
259
+ screen_topleft = window_top * 65536
260
+ written = 0.chr * 4
261
+ @@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, fill_length, screen_topleft, written)
262
+ @@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, fill_length, screen_topleft, written)
263
+ @@SetConsoleCursorPosition.call(@@hConsoleHandle, screen_topleft)
218
264
  end
219
265
 
220
266
  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.0
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-24 00:00:00.000000000 Z
11
+ date: 2020-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -95,9 +95,9 @@ files:
95
95
  - lib/reline/windows.rb
96
96
  homepage: https://github.com/ruby/reline
97
97
  licenses:
98
- - Ruby License
98
+ - Ruby
99
99
  metadata: {}
100
- post_install_message:
100
+ post_install_message:
101
101
  rdoc_options: []
102
102
  require_paths:
103
103
  - lib
@@ -112,8 +112,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
112
  - !ruby/object:Gem::Version
113
113
  version: '0'
114
114
  requirements: []
115
- rubygems_version: 3.1.0.pre3
116
- signing_key:
115
+ rubygems_version: 3.1.2
116
+ signing_key:
117
117
  specification_version: 4
118
118
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.
119
119
  test_files: []