reline 0.1.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,170 @@
1
+ require 'reline/unicode'
2
+
3
+ =begin
4
+
5
+ \ |
6
+ \ | <--- whipped cream
7
+ \ |
8
+ \ |
9
+ \-~~|
10
+ \ | <--- shibori kutigane (piping nozzle in Japanese)
11
+ \Ml
12
+ (\ __ __
13
+ ( \--( ) )
14
+ (__(__)__) <--- compressed whipped cream
15
+ =end
16
+
17
+ class Sibori
18
+ attr_writer :output
19
+
20
+ def initialize(width, height, cursor_pos)
21
+ @width = width
22
+ @height = height
23
+ @cursor_pos = cursor_pos
24
+ @screen = [String.new]
25
+ @line_index = 0
26
+ @byte_pointer_in_line = 0
27
+ @cleared = false
28
+ clone_screen
29
+ end
30
+
31
+ def clone_screen
32
+ @prev_screen = @screen.map { |line|
33
+ line.dup
34
+ }
35
+ @prev_cursor_pos = @cursor_pos.dup
36
+ @prev_line_index = @line_index
37
+ end
38
+
39
+ def print(str)
40
+ #$stderr.puts "print #{str.inspect}"
41
+ line = @screen[@line_index]
42
+ before = line.byteslice(0, @byte_pointer_in_line)
43
+ str_width = Reline::Unicode.calculate_width(str, true)
44
+ after_cursor = line.byteslice(@byte_pointer_in_line..-1)
45
+ after_cursor_width = Reline::Unicode.calculate_width(after_cursor, true)
46
+ rest = ''
47
+ if after_cursor_width > str_width
48
+ rest_byte_pointer = @byte_pointer_in_line + width_to_bytesize(after_cursor, str_width)
49
+ rest = line.byteslice(rest_byte_pointer..-1)
50
+ end
51
+ @screen[@line_index] = before + str + rest
52
+ @byte_pointer_in_line += str.bytesize
53
+ @cursor_pos.x += Reline::Unicode.calculate_width(str, true)
54
+ end
55
+
56
+ def move_cursor_column(col)
57
+ #$stderr.puts "move_cursor_column(#{col})"
58
+ @byte_pointer_in_line = width_to_bytesize(@screen[@line_index], col)
59
+ @cursor_pos.x = col
60
+ end
61
+
62
+ def move_cursor_up(val)
63
+ #$stderr.puts "move_cursor_up(#{val})"
64
+ if @line_index.positive?
65
+ @line_index -= val
66
+ @byte_pointer_in_line = width_to_bytesize(@screen[@line_index], @cursor_pos.x)
67
+ @cursor_pos.y -= val
68
+ end
69
+ end
70
+
71
+ def move_cursor_down(val)
72
+ #$stderr.puts "move_cursor_down(#{val})"
73
+ if @line_index < @height - 1
74
+ #$stderr.puts "@line_index #{@line_index} @screen.size #{@screen.size} @height #{@height}"
75
+ #$stderr.puts @screen.inspect
76
+ @line_index += val
77
+ @screen[@line_index] = String.new if @line_index == @screen.size
78
+ @byte_pointer_in_line = width_to_bytesize(@screen[@line_index], @cursor_pos.x)
79
+ @cursor_pos.y += val
80
+ end
81
+ end
82
+
83
+ def scroll_down(val)
84
+ #$stderr.puts "scroll_down(#{val})"
85
+ if val >= @height
86
+ clear_screen
87
+ @line_index = @screen.size - 1
88
+ return
89
+ end
90
+ @screen.size.times do |n|
91
+ if n < @screen.size - val
92
+ #$stderr.puts "A @screen[#{val} + #{n}] (#{@screen[val + n].inspect}) to @screen[#{n}]"
93
+ @screen[n] = @screen[val + n]
94
+ else
95
+ #$stderr.puts "B String.new to @screen[#{n}]"
96
+ @screen[n] = String.new
97
+ end
98
+ end
99
+ @line_index += val
100
+ end
101
+
102
+ def erase_after_cursor
103
+ #$stderr.puts "erase_after_cursor"
104
+ @screen[@line_index] = @screen[@line_index].byteslice(0, @byte_pointer_in_line)
105
+ end
106
+
107
+ def clear_screen
108
+ #$stderr.puts "clear_screen"
109
+ @screen = [String.new]
110
+ @line_index = 0
111
+ @byte_pointer_in_line = 0
112
+ @cursor_pos.x = @cursor_pos.y = 0
113
+ @cleared = true
114
+ Reline::IOGate.clear_screen
115
+ end
116
+
117
+ private def width_to_bytesize(str, width)
118
+ lines, _ = Reline::Unicode.split_by_width(str, width)
119
+ lines.first.bytesize
120
+ end
121
+
122
+ def render
123
+ #$stderr.puts ?* * 100
124
+ Reline::IOGate.move_cursor_up(@prev_line_index) if @prev_line_index.positive?
125
+ #$stderr.puts "! move_cursor_up(#{@prev_line_index})" if @prev_line_index.positive?
126
+ #$stderr.puts "@prev_line_index #{@prev_line_index} @line_index #{@line_index}"
127
+ if @screen.size > @prev_screen.size
128
+ #$stderr.puts ?a * 100
129
+ down = @screen.size - @prev_screen.size
130
+ #$stderr.puts "#{@prev_cursor_pos.y} #{down} #{@height}"
131
+ if @prev_cursor_pos.y + down > (@height - 1)
132
+ #$stderr.puts ?b * 100
133
+ scroll = (@prev_cursor_pos.y + down) - (@height - 1)
134
+ Reline::IOGate.scroll_down(scroll)
135
+ #$stderr.puts "! scroll_down(#{scroll})"
136
+ #$stderr.puts "down #{down}"
137
+ Reline::IOGate.move_cursor_up(@screen.size - 1 - scroll)
138
+ #$stderr.puts "! move_cursor_up(#{@screen.size - 1})"
139
+ else
140
+ #$stderr.puts ?c * 100
141
+ end
142
+ end
143
+ @screen.size.times do |n|
144
+ Reline::IOGate.move_cursor_column(0)
145
+ #$stderr.puts "! move_cursor_column(0)"
146
+ @output.write @screen[n]
147
+ #$stderr.puts "! print #{@screen[n].inspect}"
148
+ Reline::IOGate.erase_after_cursor
149
+ #$stderr.puts "! erase_after_cursor"
150
+ Reline::IOGate.move_cursor_down(1) if n != (@screen.size - 1)
151
+ #$stderr.puts "! move_cursor_down(1)" if n != (@screen.size - 1)
152
+ end
153
+ up = @screen.size - 1 - @line_index
154
+ Reline::IOGate.move_cursor_up(up) if up.positive?
155
+ #$stderr.puts "! move_cursor_up(#{up})" if up.positive?
156
+ column = Reline::Unicode.calculate_width(@screen[@line_index].byteslice(0, @byte_pointer_in_line), true)
157
+ Reline::IOGate.move_cursor_column(column)
158
+ #$stderr.puts "! move_cursor_column(#{column}) #{@byte_pointer_in_line}"
159
+ clone_screen
160
+ #$stderr.puts ?- * 10
161
+ end
162
+
163
+ def prep
164
+ Reline::IOGate.prep
165
+ end
166
+
167
+ def deprep
168
+ Reline::IOGate.deprep
169
+ end
170
+ end
@@ -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