reline 0.2.5 → 0.3.1

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.
@@ -0,0 +1,171 @@
1
+ begin
2
+ require 'fiddle'
3
+ require 'fiddle/import'
4
+ rescue LoadError
5
+ module Reline::Terminfo
6
+ def self.curses_dl
7
+ false
8
+ end
9
+ end
10
+ end
11
+
12
+ module Reline::Terminfo
13
+ extend Fiddle::Importer
14
+
15
+ class TerminfoError < StandardError; end
16
+
17
+ def self.curses_dl_files
18
+ case RUBY_PLATFORM
19
+ when /mingw/, /mswin/
20
+ # aren't supported
21
+ []
22
+ when /cygwin/
23
+ %w[cygncursesw-10.dll cygncurses-10.dll]
24
+ when /darwin/
25
+ %w[libncursesw.dylib libcursesw.dylib libncurses.dylib libcurses.dylib]
26
+ else
27
+ %w[libncursesw.so libcursesw.so libncurses.so libcurses.so]
28
+ end
29
+ end
30
+
31
+ @curses_dl = false
32
+ def self.curses_dl
33
+ return @curses_dl unless @curses_dl == false
34
+ if RUBY_VERSION >= '3.0.0'
35
+ # Gem module isn't defined in test-all of the Ruby repository, and
36
+ # Fiddle in Ruby 3.0.0 or later supports Fiddle::TYPE_VARIADIC.
37
+ fiddle_supports_variadic = true
38
+ elsif Fiddle.const_defined?(:VERSION) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1')
39
+ # Fiddle::TYPE_VARIADIC is supported from Fiddle 1.0.1.
40
+ fiddle_supports_variadic = true
41
+ else
42
+ fiddle_supports_variadic = false
43
+ end
44
+ if fiddle_supports_variadic and not Fiddle.const_defined?(:TYPE_VARIADIC)
45
+ # If the libffi version is not 3.0.5 or higher, there isn't TYPE_VARIADIC.
46
+ fiddle_supports_variadic = false
47
+ end
48
+ if fiddle_supports_variadic
49
+ curses_dl_files.each do |curses_name|
50
+ result = Fiddle::Handle.new(curses_name)
51
+ rescue Fiddle::DLError
52
+ next
53
+ else
54
+ @curses_dl = result
55
+ break
56
+ end
57
+ end
58
+ @curses_dl = nil if @curses_dl == false
59
+ @curses_dl
60
+ end
61
+ end if not Reline.const_defined?(:Terminfo) or not Reline::Terminfo.respond_to?(:curses_dl)
62
+
63
+ module Reline::Terminfo
64
+ dlload curses_dl
65
+ #extern 'int setupterm(char *term, int fildes, int *errret)'
66
+ @setupterm = Fiddle::Function.new(curses_dl['setupterm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
67
+ #extern 'char *tigetstr(char *capname)'
68
+ @tigetstr = Fiddle::Function.new(curses_dl['tigetstr'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP)
69
+ begin
70
+ #extern 'char *tiparm(const char *str, ...)'
71
+ @tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
72
+ rescue Fiddle::DLError
73
+ # OpenBSD lacks tiparm
74
+ #extern 'char *tparm(const char *str, ...)'
75
+ @tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
76
+ end
77
+ begin
78
+ #extern 'int tigetflag(char *str)'
79
+ @tigetflag = Fiddle::Function.new(curses_dl['tigetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
80
+ rescue Fiddle::DLError
81
+ # OpenBSD lacks tigetflag
82
+ #extern 'int tgetflag(char *str)'
83
+ @tigetflag = Fiddle::Function.new(curses_dl['tgetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
84
+ end
85
+ begin
86
+ #extern 'int tigetnum(char *str)'
87
+ @tigetnum = Fiddle::Function.new(curses_dl['tigetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
88
+ rescue Fiddle::DLError
89
+ # OpenBSD lacks tigetnum
90
+ #extern 'int tgetnum(char *str)'
91
+ @tigetnum = Fiddle::Function.new(curses_dl['tgetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
92
+ end
93
+
94
+ def self.setupterm(term, fildes)
95
+ errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT')
96
+ ret = @setupterm.(term, fildes, errret_int)
97
+ errret = errret_int.unpack1('i')
98
+ case ret
99
+ when 0 # OK
100
+ 0
101
+ when -1 # ERR
102
+ case errret
103
+ when 1
104
+ raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.')
105
+ when 0
106
+ raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.')
107
+ when -1
108
+ raise TerminfoError.new('The terminfo database could not be found.')
109
+ else # unknown
110
+ -1
111
+ end
112
+ else # unknown
113
+ -2
114
+ end
115
+ end
116
+
117
+ class StringWithTiparm < String
118
+ def tiparm(*args) # for method chain
119
+ Reline::Terminfo.tiparm(self, *args)
120
+ end
121
+ end
122
+
123
+ def self.tigetstr(capname)
124
+ capability = @tigetstr.(capname)
125
+ case capability.to_i
126
+ when 0, -1
127
+ raise TerminfoError, "can't find capability: #{capname}"
128
+ end
129
+ StringWithTiparm.new(capability.to_s)
130
+ end
131
+
132
+ def self.tiparm(str, *args)
133
+ new_args = []
134
+ args.each do |a|
135
+ new_args << Fiddle::TYPE_INT << a
136
+ end
137
+ @tiparm.(str, *new_args).to_s
138
+ end
139
+
140
+ def self.tigetflag(capname)
141
+ flag = @tigetflag.(capname).to_i
142
+ case flag
143
+ when -1
144
+ raise TerminfoError, "not boolean capability: #{capname}"
145
+ when 0
146
+ raise TerminfoError, "can't find capability: #{capname}"
147
+ end
148
+ flag
149
+ end
150
+
151
+ def self.tigetnum(capname)
152
+ num = @tigetnum.(capname).to_i
153
+ case num
154
+ when -2
155
+ raise TerminfoError, "not numeric capability: #{capname}"
156
+ when -1
157
+ raise TerminfoError, "can't find capability: #{capname}"
158
+ end
159
+ num
160
+ end
161
+
162
+ def self.enabled?
163
+ true
164
+ end
165
+ end if Reline::Terminfo.curses_dl
166
+
167
+ module Reline::Terminfo
168
+ def self.enabled?
169
+ false
170
+ end
171
+ end unless Reline::Terminfo.curses_dl
@@ -79,6 +79,8 @@ class Reline::Unicode
79
79
 
80
80
  require 'reline/unicode/east_asian_width'
81
81
 
82
+ HalfwidthDakutenHandakuten = /[\u{FF9E}\u{FF9F}]/
83
+
82
84
  MBCharWidthRE = /
83
85
  (?<width_2_1>
84
86
  [#{ EscapedChars.map {|c| "\\x%02x" % c.ord }.join }] (?# ^ + char, such as ^M, ^H, ^[, ...)
@@ -93,6 +95,12 @@ class Reline::Unicode
93
95
  #{ EastAsianWidth::TYPE_H }
94
96
  | #{ EastAsianWidth::TYPE_NA }
95
97
  | #{ EastAsianWidth::TYPE_N }
98
+ )(?!#{ HalfwidthDakutenHandakuten })
99
+ | (?<width_2_3>
100
+ (?: #{ EastAsianWidth::TYPE_H }
101
+ | #{ EastAsianWidth::TYPE_NA }
102
+ | #{ EastAsianWidth::TYPE_N })
103
+ #{ HalfwidthDakutenHandakuten }
96
104
  )
97
105
  | (?<ambiguous_width>
98
106
  #{EastAsianWidth::TYPE_A}
@@ -101,15 +109,15 @@ class Reline::Unicode
101
109
 
102
110
  def self.get_mbchar_width(mbchar)
103
111
  ord = mbchar.ord
104
- if (0x00 <= ord and ord <= 0x1F)
112
+ if (0x00 <= ord and ord <= 0x1F) # in EscapedPairs
105
113
  return 2
106
- elsif (0x20 <= ord and ord <= 0x7E)
114
+ elsif (0x20 <= ord and ord <= 0x7E) # printable ASCII chars
107
115
  return 1
108
116
  end
109
117
  m = mbchar.encode(Encoding::UTF_8).match(MBCharWidthRE)
110
118
  case
111
119
  when m.nil? then 1 # TODO should be U+FFFD � REPLACEMENT CHARACTER
112
- when m[:width_2_1], m[:width_2_2] then 2
120
+ when m[:width_2_1], m[:width_2_2], m[:width_2_3] then 2
113
121
  when m[:width_3] then 3
114
122
  when m[:width_0] then 0
115
123
  when m[:width_1] then 1
@@ -185,6 +193,37 @@ class Reline::Unicode
185
193
  [lines, height]
186
194
  end
187
195
 
196
+ # Take a chunk of a String cut by width with escape sequences.
197
+ def self.take_range(str, start_col, max_width, encoding = str.encoding)
198
+ chunk = String.new(encoding: encoding)
199
+ total_width = 0
200
+ rest = str.encode(Encoding::UTF_8)
201
+ in_zero_width = false
202
+ rest.scan(WIDTH_SCANNER) do |gc|
203
+ case
204
+ when gc[NON_PRINTING_START_INDEX]
205
+ in_zero_width = true
206
+ when gc[NON_PRINTING_END_INDEX]
207
+ in_zero_width = false
208
+ when gc[CSI_REGEXP_INDEX]
209
+ chunk << gc[CSI_REGEXP_INDEX]
210
+ when gc[OSC_REGEXP_INDEX]
211
+ chunk << gc[OSC_REGEXP_INDEX]
212
+ when gc[GRAPHEME_CLUSTER_INDEX]
213
+ gc = gc[GRAPHEME_CLUSTER_INDEX]
214
+ if in_zero_width
215
+ chunk << gc
216
+ else
217
+ mbchar_width = get_mbchar_width(gc)
218
+ total_width += mbchar_width
219
+ break if (start_col + max_width) < total_width
220
+ chunk << gc if start_col < total_width
221
+ end
222
+ end
223
+ end
224
+ chunk
225
+ end
226
+
188
227
  def self.get_next_mbchar_size(line, byte_pointer)
189
228
  grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
190
229
  grapheme ? grapheme.bytesize : 0
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.2.5'
2
+ VERSION = '0.3.1'
3
3
  end