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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c7ee1afd97de8627d4106e6d2506c6afeeed80bd
4
- data.tar.gz: 902a4d04c8b376286e9da45bc03219e46e6f33c8
2
+ SHA256:
3
+ metadata.gz: 67a711d64ca029c1b3fb2a2cea32da16996f76ad440e901c4a2edad13564b7fd
4
+ data.tar.gz: dd675c62ede4b0530ffa53fcd9208f312bc6e44757d62ce3fa01b14d5ac9c392
5
5
  SHA512:
6
- metadata.gz: 4f0aedde0e2ec57c2ea17cb53c8749d1c144bb78d9f230e98ce215ff7e9daab83e1cf8998a1c2283f53c3ce5f7ea31bf39e5d606e76b600ea859bb518a268ede
7
- data.tar.gz: c790e5974e11c75df1216c8966119cf346878b4de6c7accce636729af3b2206e9b9eef028379dcf9b6e003921d0361312834b32f02942774c69b10f275b556ba
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", "~> 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,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
@@ -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
@@ -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
- parse(self.to_str + "\e[0m" + str.to_str)
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,5,params.shift,params.shift, params.shift].join(";")
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
- params << str[i]
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
@@ -1,3 +1,3 @@
1
1
  module AnsiTerm
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.1"
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"
@@ -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.3.1
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: 2018-01-30 00:00:00.000000000 Z
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: '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,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
- rubyforge_project:
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