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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e87a8b37e0488b859cc89f8ebcc0ed199518d3fc
4
- data.tar.gz: 738b1172d4ea87b8e4da9fc40eec476a5b0ba6a4
2
+ SHA256:
3
+ metadata.gz: 92d53d94db9dcd9ed7d7369d4b7df040c5da231bfb2985399b783716f12c7fa0
4
+ data.tar.gz: 7a50dcdbadfe4cf828de6b98ac674f36052525e0fc5c390a4d018167ccffb4cb
5
5
  SHA512:
6
- metadata.gz: e91d53b54b15a24a69944992c4da4882d2c17b549af25dab471c7b19ded12eeef36c986d2fd8f70f078cac64689aff72ec7a1071b0393cb4cad06b380e3a2f24
7
- data.tar.gz: d6b17caf80f4e3a46d478a2d31dc3562817b7da6ed541ccec5fc44d44db7462d9fe34900070d0666da28766b98ca8a2237c8898d92d8043654b990c7bbf1c7ea
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", "~> 1.13"
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: 0)
27
+ def initialize(fgcol: nil, bgcol: nil, flags: nil)
27
28
  @fgcol = fgcol
28
29
  @bgcol = bgcol
29
- @flags = flags || 0
30
+ @flags = flags
30
31
  freeze
31
32
  end
32
33
 
33
- def merge(attrs)
34
- self.class.new({bgcol: @bgcol, fgcol: @fgcol, flags: @flags}.merge(attrs))
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 add_flag(flags); merge({flags: @flags | flags}); end
38
- def clear_flag(flags); merge({flags: @flags & ~BOLD}); end
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
@@ -1,9 +1,18 @@
1
-
1
+ # coding: utf-8
2
2
  module AnsiTerm
3
3
 
4
4
  class String
5
- def initialize(str="")
6
- parse(str)
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
- str = self.class.new(str)
52
- @str << str.raw
53
- @attrs << str.attrs
54
- # parse(self.to_str + "\e[0m" + str.to_str)
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,5,params.shift,params.shift, params.shift].join(";")
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
- params << str[i]
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
@@ -1,3 +1,3 @@
1
1
  module AnsiTerm
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4"
3
3
  end
data/lib/ansiterm.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  require "ansiterm/version"
2
2
  require "ansiterm/attr"
3
3
  require "ansiterm/string"
4
-
4
+ require "ansiterm/buffer"
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.3.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: 2018-01-30 00:00:00.000000000 Z
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: '1.13'
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: '1.13'
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
- rubyforge_project:
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