ansiterm 0.3.0 → 0.4

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