reline 0.2.5 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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