ansiterm 0.3.0 → 0.4
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 +108 -0
- data/lib/ansiterm/string.rb +102 -16
- data/lib/ansiterm/version.rb +1 -1
- data/lib/ansiterm.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 92d53d94db9dcd9ed7d7369d4b7df040c5da231bfb2985399b783716f12c7fa0
|
4
|
+
data.tar.gz: 7a50dcdbadfe4cf828de6b98ac674f36052525e0fc5c390a4d018167ccffb4cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0c1b315496296dd0a9c6015c5907a0d08eb6d4c4a17756d8ce22f4929b6ff571e448bf9b83a902949be6b625706e9d796d9c59d742f90fb5b0f7b2ffec3c1a9
|
7
|
+
data.tar.gz: c595e0ca1516a21f033317f997f31981bc6899263ce8407065244d76f8bb41bfe339a5435df048be64a149da36e692bcfc04e695e95cc9664e379a5ed8e7e4e6
|
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,108 @@
|
|
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 } #AnsiTerm::String.new } #AnsiTerm::String.new("\e[37;40;0m"+(" "*@w)) }
|
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 scroll
|
50
|
+
while @y >= @h
|
51
|
+
@lines.shift
|
52
|
+
@lines << nil #AnsiTerm::String.new #"" #AnsiTerm::String.new("\e[37;40;0m"+(" "*@w))
|
53
|
+
@y -= 1
|
54
|
+
end
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def print *args
|
59
|
+
args.each do |str|
|
60
|
+
@lines[@y] ||= AnsiTerm::String.new("\e[0m")
|
61
|
+
@dirty << @y
|
62
|
+
l = @lines[@y]
|
63
|
+
|
64
|
+
if l.length < @x
|
65
|
+
l << (" "*(@x - l.length))
|
66
|
+
end
|
67
|
+
l[@x..@x+str.length] = str
|
68
|
+
# l[@x] << str
|
69
|
+
# if @x + s.length > @w
|
70
|
+
# l[@x .. @w-1] = s[0 .. @w - @x]
|
71
|
+
# @x = 0
|
72
|
+
# @y += 1
|
73
|
+
# scroll if @y >= @h
|
74
|
+
# end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
out = ""
|
80
|
+
cachehit=0
|
81
|
+
cachemiss=0
|
82
|
+
@lines.each_with_index do |line,y|
|
83
|
+
line ||= ""
|
84
|
+
line = line[0..(@w-1)]
|
85
|
+
l = line.length
|
86
|
+
#if l > @w
|
87
|
+
# $editor&.log("WARNING: #{l} > @w #{@w}")
|
88
|
+
#end
|
89
|
+
s = line.to_str
|
90
|
+
if @cache[y] != s
|
91
|
+
out << ANSI.cup(y,0) << s << ANSI.sgr(:reset) << ANSI.el #<< X\n"
|
92
|
+
cachemiss += s.length
|
93
|
+
old = @cache[y]
|
94
|
+
@cache[y] = s
|
95
|
+
#$editor.pry([y,old, s])
|
96
|
+
else
|
97
|
+
cachehit += @cache[y].length
|
98
|
+
end
|
99
|
+
# FIXME: This is only worthwhile if a background colour is set
|
100
|
+
#if l < @w
|
101
|
+
# out << ANSI.csi("m",0,38,48,2,48,48,64) << "-"*(@w-l)
|
102
|
+
#end
|
103
|
+
end
|
104
|
+
@dirty = Set[]
|
105
|
+
out
|
106
|
+
end
|
107
|
+
end
|
108
|
+
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
|
@@ -20,6 +29,10 @@ module AnsiTerm
|
|
20
29
|
out
|
21
30
|
end
|
22
31
|
|
32
|
+
def to_s
|
33
|
+
to_str
|
34
|
+
end
|
35
|
+
|
23
36
|
def encoding
|
24
37
|
@str.encoding
|
25
38
|
end
|
@@ -36,6 +49,53 @@ module AnsiTerm
|
|
36
49
|
@str, @attrs = str,Array(attrs)
|
37
50
|
end
|
38
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
|
93
|
+
a = @attrs
|
94
|
+
parse(str)
|
95
|
+
@str = s[0..(range.min-1)].to_s + @str + s[(range.max)..-1].to_s
|
96
|
+
@attrs = a[0..(range.min-1)].to_a + @attrs + a[(range.max)..-1].to_a
|
97
|
+
end
|
98
|
+
|
39
99
|
def[] i
|
40
100
|
str = @str[i]
|
41
101
|
if str
|
@@ -47,13 +107,27 @@ module AnsiTerm
|
|
47
107
|
end
|
48
108
|
end
|
49
109
|
|
110
|
+
def char_at(index)
|
111
|
+
@str[index]
|
112
|
+
end
|
113
|
+
|
114
|
+
def attr_at(index)
|
115
|
+
@attrs[index]
|
116
|
+
end
|
117
|
+
|
50
118
|
def << str
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
119
|
+
return self if str.nil?
|
120
|
+
if !str.kind_of?(self.class)
|
121
|
+
parse(self.to_str + "\e[0m" + str.to_str)
|
122
|
+
return self
|
123
|
+
end
|
124
|
+
|
125
|
+
s,a = str.raw
|
126
|
+
@str.concat(s)
|
127
|
+
@attrs.concat(a)
|
128
|
+
self
|
55
129
|
end
|
56
|
-
|
130
|
+
|
57
131
|
private
|
58
132
|
|
59
133
|
def parse_color(par, params, a, attr_name)
|
@@ -63,12 +137,13 @@ module AnsiTerm
|
|
63
137
|
if par == "5"
|
64
138
|
col = [col,5,params.shift].join(";")
|
65
139
|
elsif par == "2"
|
66
|
-
col = [col,
|
140
|
+
col = [col,2,*params.slice!(0..2)].join(";")
|
141
|
+
# ,params.shift,params.shift, params.shift].join(";")
|
67
142
|
end
|
68
143
|
end
|
69
144
|
a.merge(attr_name => col)
|
70
145
|
end
|
71
|
-
|
146
|
+
|
72
147
|
def parse(str)
|
73
148
|
@str = ""
|
74
149
|
@attrs = []
|
@@ -76,20 +151,26 @@ module AnsiTerm
|
|
76
151
|
|
77
152
|
max = str.length
|
78
153
|
i = 0
|
79
|
-
|
154
|
+
|
80
155
|
while i < max
|
81
156
|
c = str[i]
|
82
157
|
if c == "\e" && str[i+1] == "[" # CSI
|
83
|
-
params =
|
158
|
+
params = []
|
84
159
|
i += 2
|
160
|
+
par = ""
|
85
161
|
while i < max && str[i].ord < 0x40
|
86
|
-
|
162
|
+
if str[i] == ";"
|
163
|
+
params << par
|
164
|
+
par = ""
|
165
|
+
else
|
166
|
+
par << str[i]
|
167
|
+
end
|
87
168
|
i+=1
|
88
169
|
end
|
170
|
+
params << par if !par.empty?
|
89
171
|
final = str[i]
|
90
172
|
|
91
173
|
if final == "m"
|
92
|
-
params = params.split(";")
|
93
174
|
while par = params.shift
|
94
175
|
par = par.to_i
|
95
176
|
case par
|
@@ -104,12 +185,13 @@ module AnsiTerm
|
|
104
185
|
when 22
|
105
186
|
a = a.normal
|
106
187
|
when 24
|
188
|
+
old = a
|
107
189
|
a = a.clear_flag(Attr::UNDERLINE)
|
108
190
|
when 29
|
109
191
|
a = a.clear_flag(Attr::CROSSED_OUT)
|
110
|
-
when 30..39
|
192
|
+
when 30..39, 90..98
|
111
193
|
a = parse_color(par, params, a, :fgcol)
|
112
|
-
when 40..49
|
194
|
+
when 40..49, 100..107
|
113
195
|
a = parse_color(par, params, a, :bgcol)
|
114
196
|
else
|
115
197
|
@str << "[unknown escape: #{par}]"
|
@@ -125,4 +207,8 @@ module AnsiTerm
|
|
125
207
|
self
|
126
208
|
end
|
127
209
|
end
|
210
|
+
|
211
|
+
def self.str(*args)
|
212
|
+
AnsiTerm::String.new(*args)
|
213
|
+
end
|
128
214
|
end
|
data/lib/ansiterm/version.rb
CHANGED
data/lib/ansiterm.rb
CHANGED
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'
|
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-11 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,6 +70,7 @@ 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
|
75
76
|
homepage: https://github.com/vidarh/ansiterm
|
@@ -91,8 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
92
|
- !ruby/object:Gem::Version
|
92
93
|
version: '0'
|
93
94
|
requirements: []
|
94
|
-
|
95
|
-
rubygems_version: 2.5.2
|
95
|
+
rubygems_version: 3.1.4
|
96
96
|
signing_key:
|
97
97
|
specification_version: 4
|
98
98
|
summary: ANSI/VT102 terminal output with windowing
|