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.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/lib/reline.rb +41 -25
- data/lib/reline/ansi.rb +79 -18
- data/lib/reline/config.rb +69 -10
- data/lib/reline/general_io.rb +8 -0
- data/lib/reline/history.rb +33 -13
- data/lib/reline/key_actor/emacs.rb +7 -7
- data/lib/reline/key_actor/vi_command.rb +2 -2
- data/lib/reline/key_actor/vi_insert.rb +2 -2
- data/lib/reline/key_stroke.rb +2 -0
- data/lib/reline/line_editor.rb +305 -119
- data/lib/reline/unicode.rb +68 -0
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +51 -5
- metadata +7 -7
data/lib/reline/unicode.rb
CHANGED
@@ -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
|
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,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).
|
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.
|
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
|
-
|
216
|
-
|
217
|
-
|
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.
|
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:
|
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
|
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.
|
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: []
|