ansiterm 0.3.1 → 0.4.1

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: 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