reline 0.1.5 → 0.3.1

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