ansiterm 0.3.1 → 0.4.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.
- checksums.yaml +5 -5
- data/ansiterm.gemspec +1 -1
- data/lib/ansiterm/attr.rb +50 -23
- data/lib/ansiterm/buffer.rb +91 -0
- data/lib/ansiterm/string.rb +99 -12
- data/lib/ansiterm/version.rb +1 -1
- data/lib/ansiterm.rb +1 -1
- data/visual-spec/buffer.rb +25 -0
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 67a711d64ca029c1b3fb2a2cea32da16996f76ad440e901c4a2edad13564b7fd
|
4
|
+
data.tar.gz: dd675c62ede4b0530ffa53fcd9208f312bc6e44757d62ce3fa01b14d5ac9c392
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 053de29389ed592194707f8406d83f7b56d09932657288d43a679a63bdd14300d73520160d932957c4f61919cfad4e1633519eb583a24a6a765f22f3d0edeca1
|
7
|
+
data.tar.gz: edd85d4d45baed56de37b1637ff4111e8e4687610fb91a4ea2206a4d92b0542899285da0766640b3eda2c8bb1bf0e05228d0473f5f0dc0cc7495251151d557ae
|
data/ansiterm.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.add_development_dependency "bundler"
|
24
|
+
spec.add_development_dependency "bundler"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
27
27
|
end
|
data/lib/ansiterm/attr.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
2
1
|
module AnsiTerm
|
3
2
|
|
3
|
+
# # Attr #
|
4
|
+
#
|
4
5
|
# Attr represents the attributes of a given character.
|
5
6
|
# It is intended to follow the "Flyweight" GOF pattern
|
6
7
|
# in that any object representing a given combination
|
@@ -10,7 +11,7 @@ module AnsiTerm
|
|
10
11
|
# whether to e.g. encode a string as spans with one Attr,
|
11
12
|
# or characters with one Attr per character.
|
12
13
|
#
|
13
|
-
# Use Attr#transition(other_attr) to retrieve an ANSI
|
14
|
+
# Use `Attr#transition(other_attr)` to retrieve an ANSI
|
14
15
|
# sequence that represents the changes from self to
|
15
16
|
# other_attr.
|
16
17
|
#
|
@@ -20,22 +21,45 @@ module AnsiTerm
|
|
20
21
|
ITALICS = 2
|
21
22
|
UNDERLINE = 4
|
22
23
|
CROSSED_OUT = 8
|
23
|
-
|
24
|
+
|
24
25
|
attr_reader :fgcol, :bgcol, :flags
|
25
26
|
|
26
|
-
def initialize(fgcol: nil, bgcol: nil, flags:
|
27
|
+
def initialize(fgcol: nil, bgcol: nil, flags: nil)
|
27
28
|
@fgcol = fgcol
|
28
29
|
@bgcol = bgcol
|
29
|
-
@flags = flags
|
30
|
+
@flags = flags
|
30
31
|
freeze
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
-
|
34
|
+
def ==(other)
|
35
|
+
return false if !other.kind_of?(self.class)
|
36
|
+
return fgcol == other.fgcol &&
|
37
|
+
bgcol == other.bgcol &&
|
38
|
+
flags == other.flags
|
35
39
|
end
|
36
|
-
|
37
|
-
def
|
38
|
-
|
40
|
+
|
41
|
+
def to_h
|
42
|
+
attrs = {}
|
43
|
+
attrs[:bgcol] = @bgcol if @bgcol
|
44
|
+
attrs[:fgcol] = @fgcol if @fgcol
|
45
|
+
attrs[:flags] = @flags if @flags
|
46
|
+
attrs
|
47
|
+
end
|
48
|
+
|
49
|
+
def merge(attrs, ignore: nil)
|
50
|
+
return self if self == attrs
|
51
|
+
if attrs.respond_to?(:to_h)
|
52
|
+
attrs = attrs.to_h
|
53
|
+
end
|
54
|
+
if ignore
|
55
|
+
attrs.delete(ignore)
|
56
|
+
end
|
57
|
+
attrs = to_h.merge(attrs)
|
58
|
+
self.class.new(attrs)
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_flag(flags); merge({flags: @flags.to_i | flags.to_i}); end
|
62
|
+
def clear_flag(flags); merge({flags: @flags.to_i & ~flags.to_i}); end
|
39
63
|
|
40
64
|
def reset; self.class.new; end
|
41
65
|
def normal; clear_flag(BOLD); end
|
@@ -43,41 +67,44 @@ module AnsiTerm
|
|
43
67
|
def underline; add_flag(UNDERLINE); end
|
44
68
|
def crossed_out; add_flag(CROSSED_OUT); end
|
45
69
|
|
46
|
-
def bold?; (@flags & BOLD) != 0; end
|
47
|
-
def underline?; (@flags & UNDERLINE) != 0; end
|
48
|
-
def crossed_out?; (@flags & CROSSED_OUT) != 0; end
|
70
|
+
def bold?; (@flags.to_i & BOLD) != 0; end
|
71
|
+
def underline?; (@flags.to_i & UNDERLINE) != 0; end
|
72
|
+
def crossed_out?; (@flags.to_i & CROSSED_OUT) != 0; end
|
49
73
|
|
50
74
|
def normal?
|
51
|
-
@flags == NORMAL &&
|
52
|
-
@fgcol.nil? &&
|
53
|
-
@bgcol.nil?
|
75
|
+
(@flags == NORMAL || @flags.nil?) &&
|
76
|
+
(@fgcol.nil? || @fgcol == 39) &&
|
77
|
+
(@bgcol.nil? || @bgcol == 49)
|
54
78
|
end
|
55
79
|
|
56
80
|
def transition_to(other)
|
57
81
|
t = []
|
58
|
-
t << [other.fgcol] if other.fgcol != self.fgcol
|
59
|
-
t << [other.bgcol] if other.bgcol != self.bgcol
|
60
|
-
|
82
|
+
t << [other.fgcol] if other.fgcol != self.fgcol && other.fgcol
|
83
|
+
t << [other.bgcol] if other.bgcol != self.fgcol && other.bgcol
|
84
|
+
|
61
85
|
if other.bold? != self.bold?
|
62
86
|
t << [other.bold? ? 1 : 22]
|
63
87
|
end
|
64
|
-
|
88
|
+
|
65
89
|
if other.underline? != self.underline?
|
66
90
|
t << [other.underline? ? 4 : 24]
|
67
91
|
end
|
68
|
-
|
92
|
+
|
69
93
|
if other.crossed_out? != self.crossed_out?
|
70
94
|
t << [other.crossed_out? ? 9 : 29]
|
71
95
|
end
|
72
96
|
|
73
97
|
return "\e[0m" if other.normal? && !self.normal? && t.length != 1
|
74
|
-
|
98
|
+
|
75
99
|
if t.empty?
|
76
100
|
""
|
77
101
|
else
|
78
|
-
"\e[#{t.join(";")}m"
|
102
|
+
"\e[#{t.flatten.join(";")}m"
|
79
103
|
end
|
80
104
|
end
|
81
105
|
end
|
82
106
|
|
107
|
+
def self.attr(*args)
|
108
|
+
Attr.new(*args)
|
109
|
+
end
|
83
110
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
module AnsiTerm
|
3
|
+
|
4
|
+
# # AnsiTerm::Buffer #
|
5
|
+
#
|
6
|
+
# A terminal buffer that will eventually handle ANSI style strings
|
7
|
+
# fully. For now it will handle the sequences AnsiTerm::String handles
|
8
|
+
# but e.g. cursor movement etc. needs to be explicitly handled.
|
9
|
+
#
|
10
|
+
# Extracted out of https://github.com/vidarh/re
|
11
|
+
#
|
12
|
+
# FIXME: Provide method of setting default background color
|
13
|
+
#
|
14
|
+
class Buffer
|
15
|
+
attr_reader :w,:h, :lines
|
16
|
+
|
17
|
+
def initialize(w=80, h=25)
|
18
|
+
@lines = []
|
19
|
+
@x = 0
|
20
|
+
@y = 0
|
21
|
+
@w = w
|
22
|
+
@h = h
|
23
|
+
@cache = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def cls
|
27
|
+
@lines = (1..@h).map { nil }
|
28
|
+
end
|
29
|
+
|
30
|
+
def reset
|
31
|
+
cls
|
32
|
+
@cache=[]
|
33
|
+
end
|
34
|
+
|
35
|
+
def move_cursor(x,y)
|
36
|
+
@x = x
|
37
|
+
@y = y
|
38
|
+
@x = @w-1 if @x >= @w
|
39
|
+
@y = @y-1 if @y >= @h
|
40
|
+
end
|
41
|
+
|
42
|
+
def resize(w,h)
|
43
|
+
if @w != w || @h != h
|
44
|
+
@w, @h = w,h
|
45
|
+
@cache = []
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def print *args
|
50
|
+
args.each do |str|
|
51
|
+
@lines[@y] ||= AnsiTerm::String.new
|
52
|
+
l = @lines[@y]
|
53
|
+
|
54
|
+
if l.length < @x
|
55
|
+
l << (" "*(@x - l.length))
|
56
|
+
end
|
57
|
+
|
58
|
+
r=@x..@x+str.length-1
|
59
|
+
#p [r, str]
|
60
|
+
l[r] = str
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
out = ""
|
66
|
+
cachehit=0
|
67
|
+
cachemiss=0
|
68
|
+
@lines.each_with_index do |line,y|
|
69
|
+
line ||= ""
|
70
|
+
line = line[0..@w]
|
71
|
+
l = line.length
|
72
|
+
s = line.to_str
|
73
|
+
if @cache[y] != s
|
74
|
+
# Move to start of line; output line; clear to end
|
75
|
+
#if l > 0
|
76
|
+
out << "\e[#{y+1};1H" << s
|
77
|
+
if l < @w
|
78
|
+
out << "\e[0m\e[0K"
|
79
|
+
end
|
80
|
+
#end
|
81
|
+
cachemiss += s.length
|
82
|
+
old = @cache[y]
|
83
|
+
@cache[y] = s
|
84
|
+
else
|
85
|
+
cachehit += @cache[y].length
|
86
|
+
end
|
87
|
+
end
|
88
|
+
out
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/ansiterm/string.rb
CHANGED
@@ -1,9 +1,18 @@
|
|
1
|
-
|
1
|
+
# coding: utf-8
|
2
2
|
module AnsiTerm
|
3
3
|
|
4
4
|
class String
|
5
|
-
def initialize(str=
|
6
|
-
|
5
|
+
def initialize(str=nil)
|
6
|
+
if str
|
7
|
+
parse(str)
|
8
|
+
else
|
9
|
+
@str = ""
|
10
|
+
@attrs = []
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_plain_str
|
15
|
+
@str.dup
|
7
16
|
end
|
8
17
|
|
9
18
|
def to_str
|
@@ -40,6 +49,55 @@ module AnsiTerm
|
|
40
49
|
@str, @attrs = str,Array(attrs)
|
41
50
|
end
|
42
51
|
|
52
|
+
def raw
|
53
|
+
return @str, Array(@attrs)
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_attr(range, attr)
|
57
|
+
min = [range.first - 1,0].max
|
58
|
+
fattr = @attrs[min]
|
59
|
+
#attr = fattr.merge(attr)
|
60
|
+
r = Array(@attrs[range]).count # Inefficient, but saves dealing with negative offsets etc. "manually"
|
61
|
+
last = nil
|
62
|
+
@attrs[range] = @attrs[range]&.map do |a|
|
63
|
+
n = a ? a.merge(attr) : attr
|
64
|
+
last == n ? last : n
|
65
|
+
end || []
|
66
|
+
rm = range.last+1
|
67
|
+
if a = @attrs[rm]
|
68
|
+
@attrs[rm] = Attr.new.merge(fattr).merge(a)
|
69
|
+
if !a.bgcol
|
70
|
+
@attrs[rm] = @attrs[rm].merge({bgcol: 49})
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def merge_attr_below(range, attr)
|
76
|
+
min = [0,range.first - 1].max
|
77
|
+
fattr = @attrs[min]
|
78
|
+
r = Array(@attrs[range]).count # Inefficient, but saves dealing with negative offsets etc. "manually"
|
79
|
+
last = nil
|
80
|
+
@attrs[range] = @attrs[range].map do |a|
|
81
|
+
if a.bgcol == 49
|
82
|
+
n = attr.merge(a, ignore: :bgcol)
|
83
|
+
else
|
84
|
+
n = attr.merge(a)
|
85
|
+
end
|
86
|
+
last == n ? last : n
|
87
|
+
end
|
88
|
+
#fattr #@attrs[min+r].merge(fattr)
|
89
|
+
end
|
90
|
+
|
91
|
+
def[]= range, str
|
92
|
+
s = @str.dup
|
93
|
+
a = @attrs.dup
|
94
|
+
parse(str)
|
95
|
+
s[range] = @str
|
96
|
+
@str = s
|
97
|
+
a[range] = @attrs
|
98
|
+
@attrs = a
|
99
|
+
end
|
100
|
+
|
43
101
|
def[] i
|
44
102
|
str = @str[i]
|
45
103
|
if str
|
@@ -51,8 +109,25 @@ module AnsiTerm
|
|
51
109
|
end
|
52
110
|
end
|
53
111
|
|
112
|
+
def char_at(index)
|
113
|
+
@str[index]
|
114
|
+
end
|
115
|
+
|
116
|
+
def attr_at(index)
|
117
|
+
@attrs[index]
|
118
|
+
end
|
119
|
+
|
54
120
|
def << str
|
55
|
-
|
121
|
+
return self if str.nil?
|
122
|
+
if !str.kind_of?(self.class)
|
123
|
+
parse(self.to_str + "\e[0m" + str.to_str)
|
124
|
+
return self
|
125
|
+
end
|
126
|
+
|
127
|
+
s,a = str.raw
|
128
|
+
@str.concat(s)
|
129
|
+
@attrs.concat(a)
|
130
|
+
self
|
56
131
|
end
|
57
132
|
|
58
133
|
private
|
@@ -64,12 +139,13 @@ module AnsiTerm
|
|
64
139
|
if par == "5"
|
65
140
|
col = [col,5,params.shift].join(";")
|
66
141
|
elsif par == "2"
|
67
|
-
col = [col,
|
142
|
+
col = [col,2,*params.slice!(0..2)].join(";")
|
143
|
+
# ,params.shift,params.shift, params.shift].join(";")
|
68
144
|
end
|
69
145
|
end
|
70
146
|
a.merge(attr_name => col)
|
71
147
|
end
|
72
|
-
|
148
|
+
|
73
149
|
def parse(str)
|
74
150
|
@str = ""
|
75
151
|
@attrs = []
|
@@ -77,20 +153,26 @@ module AnsiTerm
|
|
77
153
|
|
78
154
|
max = str.length
|
79
155
|
i = 0
|
80
|
-
|
156
|
+
|
81
157
|
while i < max
|
82
158
|
c = str[i]
|
83
159
|
if c == "\e" && str[i+1] == "[" # CSI
|
84
|
-
params =
|
160
|
+
params = []
|
85
161
|
i += 2
|
162
|
+
par = ""
|
86
163
|
while i < max && str[i].ord < 0x40
|
87
|
-
|
164
|
+
if str[i] == ";"
|
165
|
+
params << par
|
166
|
+
par = ""
|
167
|
+
else
|
168
|
+
par << str[i]
|
169
|
+
end
|
88
170
|
i+=1
|
89
171
|
end
|
172
|
+
params << par if !par.empty?
|
90
173
|
final = str[i]
|
91
174
|
|
92
175
|
if final == "m"
|
93
|
-
params = params.split(";")
|
94
176
|
while par = params.shift
|
95
177
|
par = par.to_i
|
96
178
|
case par
|
@@ -105,12 +187,13 @@ module AnsiTerm
|
|
105
187
|
when 22
|
106
188
|
a = a.normal
|
107
189
|
when 24
|
190
|
+
old = a
|
108
191
|
a = a.clear_flag(Attr::UNDERLINE)
|
109
192
|
when 29
|
110
193
|
a = a.clear_flag(Attr::CROSSED_OUT)
|
111
|
-
when 30..39
|
194
|
+
when 30..39, 90..98
|
112
195
|
a = parse_color(par, params, a, :fgcol)
|
113
|
-
when 40..49
|
196
|
+
when 40..49, 100..107
|
114
197
|
a = parse_color(par, params, a, :bgcol)
|
115
198
|
else
|
116
199
|
@str << "[unknown escape: #{par}]"
|
@@ -126,4 +209,8 @@ module AnsiTerm
|
|
126
209
|
self
|
127
210
|
end
|
128
211
|
end
|
212
|
+
|
213
|
+
def self.str(*args)
|
214
|
+
AnsiTerm::String.new(*args)
|
215
|
+
end
|
129
216
|
end
|
data/lib/ansiterm/version.rb
CHANGED
data/lib/ansiterm.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
$: << File.dirname(__FILE__)+"/../lib"
|
3
|
+
|
4
|
+
require 'ansiterm'
|
5
|
+
require 'io/console'
|
6
|
+
|
7
|
+
h,w = IO.console.winsize
|
8
|
+
|
9
|
+
t = AnsiTerm::Buffer.new(w,h)
|
10
|
+
|
11
|
+
t.cls
|
12
|
+
t.move_cursor(1,1)
|
13
|
+
t.print([h,w].inspect)
|
14
|
+
t.move_cursor(0,0)
|
15
|
+
t.print("UL")
|
16
|
+
t.move_cursor(w-2,0)
|
17
|
+
t.print("UR")
|
18
|
+
t.move_cursor(w-2,h-2)
|
19
|
+
t.print("LR")
|
20
|
+
t.move_cursor(0,h-2)
|
21
|
+
t.print("LL")
|
22
|
+
t.move_cursor(1,2)
|
23
|
+
t.print ("Hello world")
|
24
|
+
print t.to_s
|
25
|
+
gets
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ansiterm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vidar Hokstad
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,8 +70,10 @@ files:
|
|
70
70
|
- bin/setup
|
71
71
|
- lib/ansiterm.rb
|
72
72
|
- lib/ansiterm/attr.rb
|
73
|
+
- lib/ansiterm/buffer.rb
|
73
74
|
- lib/ansiterm/string.rb
|
74
75
|
- lib/ansiterm/version.rb
|
76
|
+
- visual-spec/buffer.rb
|
75
77
|
homepage: https://github.com/vidarh/ansiterm
|
76
78
|
licenses:
|
77
79
|
- MIT
|
@@ -91,8 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
93
|
- !ruby/object:Gem::Version
|
92
94
|
version: '0'
|
93
95
|
requirements: []
|
94
|
-
|
95
|
-
rubygems_version: 2.5.2
|
96
|
+
rubygems_version: 3.1.4
|
96
97
|
signing_key:
|
97
98
|
specification_version: 4
|
98
99
|
summary: ANSI/VT102 terminal output with windowing
|