reline 0.1.0 → 0.1.5

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.
@@ -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: []