tty_string 0.1.0 → 1.1.0

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: 5cf9c99684d1bffc4b5238d85a0fbddc6c228a92
4
- data.tar.gz: 29daa63edffa32d87215419a32c0f3eb06511648
2
+ SHA256:
3
+ metadata.gz: b638ef2c66276f621f7ae5620e25af1ccfb1d79f634202aa59935e181baf53e2
4
+ data.tar.gz: ecce132aa8201f22a69c2c2a201143bb1f2775abf4ea0785553592a2f2615640
5
5
  SHA512:
6
- metadata.gz: c0184931cc63e842676e467f9cf1e3262f97a3e191f690bc100e075f389cdd54e3d94d09295da58a2e23a97b9f27836cd3eaf6772a67079184b1fc8030cd05ac
7
- data.tar.gz: 5804b7bcf35cb28a18feeb330662cac534b307badd16f2ebd5e138a772fcd8cf4743702181d33a208bf6b20f8d03013ac0f77fae7e1faba4eb1745bbb2687296
6
+ metadata.gz: 5799b2b9a14ecdb5d86cfd8d6af0d10f896c20de36dfed2801756172fe73afebb427d5cd5816a7bd18502a08640d5c9a691d6f65b2cde9cbf2e9932ca4ca55e8
7
+ data.tar.gz: 76d8856718792bcf2f4da207e6cb5aa69b8bf0cb81e9cc6e60aaf9ec1f8734ce0ca6abb4dca54ec04d97d95604badba2b9ba316ba7079fd3d6b198e34193c474
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # v1.1.0
2
+ - suppress bracketed paste mode codes
3
+
4
+ # v1.0.1
5
+ - test push to rubygems, no functional changes
6
+
7
+ # v1.0.0
8
+ - added TTYString.parse as a shortcut for .new#to_s
9
+ - added TTYString.to_proc for lols
10
+ - added jruby to travis matrix, fortunately it just works™
11
+
12
+ # v0.2.1
13
+ - fixed a bug with ignoring \a
14
+
15
+ # v0.2.0
16
+ - Stricter rendering because it turns out terminals are randomly permissive
17
+ - removed support for ruby 2.3
18
+ - added \e[S and \e[T handling
19
+
20
+ # v0.1.0
21
+ - Initial Release
data/Gemfile CHANGED
@@ -2,7 +2,5 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
-
7
5
  # Specify your gem's dependencies in tty_string.gemspec
8
6
  gemspec
data/README.md CHANGED
@@ -1,33 +1,49 @@
1
1
  # TTYString
2
2
 
3
- Render a string like your terminal does by parsing ANSI TTY codes.
4
- This is useful for testing CLI's
5
-
6
- Supported codes
7
-
8
- - \b
9
- - \e[A
10
- - \e[B
11
- - \e[C
12
- - \e[D
13
- - \e[E
14
- - \e[F
15
- - \e[G
16
- - \e[H
17
- - \e[J
18
- - \e[K
19
- - \e[f
20
- - \e[m
21
- - \n
22
- - \r
23
- - \t
3
+ [![Build Status](https://travis-ci.com/robotdana/tty_string.svg?branch=main)](https://travis-ci.com/robotdana/tty_string)
4
+ [![Gem Version](https://badge.fury.io/rb/tty_string.svg)](https://rubygems.org/gems/tty_string)
5
+
6
+ Render to a string like your terminal does by (narrowly) parsing ANSI TTY codes.
7
+ Intended for use in tests of command line interfaces.
8
+
9
+ ## Features
10
+
11
+ - supports ruby 2.4 - 3.0.0.preview1, and jruby
12
+ - has no dependencies outside ruby stdlib
13
+
14
+ ## Supported codes
15
+
16
+ | Code | Description | Default |
17
+ |------|-------------|---------|
18
+ | `\a` | bell: suppressed | |
19
+ | `\b` | backspace: clear the character to the left of the cursor and move the cursor back one column | |
20
+ | `\n` | newline: move the cursor to the start of the next line | |
21
+ | `\r` | return: move the cursor to the start of the current line | |
22
+ | `\t` | tab: move the cursor to the next multiple-of-8 column | |
23
+ | `\e[nA` | move the cursor up _n_ lines | _n_=`1` |
24
+ | `\e[nB` | move the cursor down _n_ lines | _n_=`1` |
25
+ | `\e[nC` | move the cursor right _n_ columns | _n_=`1` |
26
+ | `\e[nD` | move the cursor left _n_ columns | _n_=`1` |
27
+ | `\e[nE` | move the cursor down _n_ lines, and to the start of the line | _n_=`1` |
28
+ | `\e[nF` | move the cursor up _n_ lines, and to the start of the line | _n_=`1` |
29
+ | `\e[nG` | move the cursor to column _n_. `1` is left-most column | _n_=`1` |
30
+ | `\e[n;mH` <br> `\e[n;mf` | move the cursor to row _n_, column _m_. `1;1` is top left corner | _n_=`1` _m_=`1` |
31
+ | `\e[nJ` | _n_=`0`: clear the screen from the cursor forward <br>_n_=`1`: clear the screen from the cursor backward <br>_n_=`2` or _n_=`3`: clear the screen | _n_=`0` |
32
+ | `\e[nK` | _n_=`0`: clear the line from the cursor forward <br>_n_=`1`: clear the line from the cursor backward <br>_n_=`2`: clear the line | _n_=`0` |
33
+ | `\e[nS` | scroll up _n_ rows | _n_=`1` |
34
+ | `\e[nT` | scroll down _n_ rows | _n_=`1` |
35
+ | `\e[m` | styling codes: optionally suppressed with `clear_style: false` | |
36
+ | `\e[?2004h` | enabled bracketed paste: suppressed | |
37
+ | `\e[?2004l` | disable bracketed paste: suppressed | |
38
+ | `\e[200~` | bracketed paste start: suppressed | |
39
+ | `\e[201~` | bracketed paste end: suppressed | |
24
40
 
25
41
  ## Installation
26
42
 
27
43
  Add this line to your application's Gemfile:
28
44
 
29
45
  ```ruby
30
- gem 'tty_string'
46
+ gem 'tty_string', '~> 1.0'
31
47
  ```
32
48
 
33
49
  And then execute:
@@ -40,24 +56,41 @@ Or install it yourself as:
40
56
 
41
57
  ## Usage
42
58
 
43
- ```
44
- TTYString.new("th\ta string\e[3Gis is").to_s => "this is a string"
59
+ ```ruby
60
+ TTYString.parse("th\ta string\e[3Gis is")
61
+ => "this is a string"
45
62
  ```
46
63
 
47
64
  Styling information is suppressed by default:
48
- ```
49
- TTYString.new("th\ta \e[31mstring\e[0m\e[3Gis is", clear_style: false).to_s => "this is a string"
65
+ ```ruby
66
+ TTYString.parse("th\ta \e[31mstring\e[0m\e[3Gis is")
67
+ => "this is a string"
50
68
  ```
51
69
  But can be passed through:
70
+ ```ruby
71
+ TTYString.parse("th\ta \e[31mstring\e[0m\e[3Gis is", clear_style: false)
72
+ => "this is a \e[31mstring\e[0m"
52
73
  ```
53
- TTYString.new("th\ta \e[31mstring\e[0m\e[3Gis is", clear_style: false).to_s => "this is a \e[31mstring\e[0m"
74
+
75
+ Just for fun TTYString.to_proc provides the `parse` method as a lambda, so:
76
+ ```ruby
77
+ ["th\ta string\e[3Gis is"].each(&TTYString)
78
+ => ["this is a string"]
54
79
  ```
55
80
 
81
+ ## Limitations
82
+
83
+ - Various terminals are wildly variously permissive with what they accept,
84
+ so this doesn't even try to cover all possible cases,
85
+ instead it covers the narrowest possible case, and leaves the codes in place when unrecognized
86
+
87
+ - `clear_style: false` treats the style codes as regular text which may work differently when rendering codes that move the cursor.
88
+
56
89
  ## Development
57
90
 
58
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
91
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests and linters. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
59
92
 
60
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
93
+ To install this gem onto your local machine, run `bundle exec rake install`.
61
94
 
62
95
  ## Contributing
63
96
 
data/lib/tty_string.rb CHANGED
@@ -3,19 +3,28 @@
3
3
  require_relative 'tty_string/parser'
4
4
 
5
5
  # Renders a string taking into ANSI escape codes and \t\r\n etc
6
- # Usage: TTYString.new("this\r\e[Kthat").to_s => "that"
6
+ # Usage: TTYString.parse("This\r\e[KThat") => "That"
7
7
  class TTYString
8
+ class << self
9
+ def parse(input_string, clear_style: true)
10
+ new(input_string, clear_style: clear_style).to_s
11
+ end
12
+
13
+ def to_proc
14
+ method(:parse).to_proc
15
+ end
16
+ end
17
+
8
18
  def initialize(input_string, clear_style: true)
9
19
  @parser = Parser.new(input_string)
10
- @clear_style = clear_style
20
+ @parser.clear_style = clear_style
11
21
  end
12
22
 
13
23
  def to_s
14
- parser.render(clear_style: clear_style)
24
+ parser.render
15
25
  end
16
26
 
17
27
  private
18
28
 
19
- attr_reader :clear_style
20
- attr_reader :parser
29
+ attr_reader :clear_style, :parser
21
30
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TTYString
4
+ class Code
5
+ class << self
6
+ def descendants
7
+ @@descendants
8
+ end
9
+
10
+ def inherited(klass)
11
+ @@descendants ||= [] # rubocop:disable Style/ClassVars I want it to be shared between subclasses.
12
+ @@descendants << klass
13
+ @@descendants.uniq!
14
+
15
+ super
16
+ end
17
+
18
+ def render(parser)
19
+ return unless match?(parser)
20
+
21
+ new(parser).action(*args(parser))
22
+
23
+ true
24
+ end
25
+
26
+ def char(value = nil)
27
+ @char = value if value
28
+ @char ||= name.split('::').last
29
+ end
30
+
31
+ private
32
+
33
+ def re
34
+ @re ||= /#{char}/.freeze
35
+ end
36
+
37
+ def args(_scanner)
38
+ []
39
+ end
40
+
41
+ def match?(parser)
42
+ parser.skip(re)
43
+ end
44
+ end
45
+
46
+ def initialize(parser)
47
+ @parser = parser
48
+ end
49
+
50
+ def action; end
51
+
52
+ private
53
+
54
+ attr_reader :parser
55
+
56
+ def screen
57
+ parser.screen
58
+ end
59
+
60
+ def cursor
61
+ parser.cursor
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'code'
4
+ require_relative 'csi_code'
5
+
6
+ class TTYString
7
+ class Code
8
+ class SlashA < TTYString::Code # leftovers:allow
9
+ char "\a"
10
+ end
11
+
12
+ class SlashB < TTYString::Code # leftovers:allow
13
+ char "\b"
14
+
15
+ def self.match?(scanner)
16
+ # can't use `scan(/\b/)` because it matches everything
17
+ return false unless scanner.peek(1) == "\b"
18
+
19
+ scanner.pos += 1
20
+ true
21
+ end
22
+
23
+ def action
24
+ cursor.left
25
+ screen.clear_at_cursor
26
+ end
27
+ end
28
+
29
+ class SlashN < TTYString::Code # leftovers:allow
30
+ char "\n"
31
+
32
+ def action
33
+ cursor.down
34
+ cursor.col = 0
35
+ screen.write('')
36
+ end
37
+ end
38
+
39
+ class SlashR < TTYString::Code # leftovers:allow
40
+ char "\r"
41
+
42
+ def action
43
+ cursor.col = 0
44
+ end
45
+ end
46
+
47
+ class SlashT < TTYString::Code # leftovers:allow
48
+ char "\t"
49
+
50
+ def action
51
+ cursor.right(8 - (cursor.col % 8))
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'code'
4
+
5
+ class TTYString
6
+ class CSICode < TTYString::Code
7
+ class << self
8
+ def default_arg(value = nil)
9
+ @default_arg ||= value
10
+ @default_arg || 1
11
+ end
12
+
13
+ private
14
+
15
+ def match?(parser)
16
+ parser.scan(re)
17
+ end
18
+
19
+ def args(parser)
20
+ a = parser.matched.slice(2..-2).split(';')
21
+ a = a.slice(0, max_args) unless max_args == -1
22
+ a.map! { |n| n.empty? ? default_arg : n.to_i }
23
+ a
24
+ end
25
+
26
+ def re
27
+ @re ||= /\e\[#{args_re}#{char}/
28
+ end
29
+
30
+ def args_re # rubocop:disable Metrics/MethodLength
31
+ case max_args
32
+ when 0 then nil
33
+ when 1 then /#{arg_re}?/
34
+ when -1 then /(#{arg_re}?(;#{arg_re})*)?/
35
+ else /(#{arg_re}?(;#{arg_re}){0,#{max_args - 1}})?/
36
+ end
37
+ end
38
+
39
+ def arg_re
40
+ /\d*/
41
+ end
42
+
43
+ def max_args
44
+ @max_args ||= instance_method(:action).arity
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ TTYString::Code.descendants.pop
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'csi_code'
4
+
5
+ class TTYString
6
+ class CSICode
7
+ class A < TTYString::CSICode # leftovers:allow
8
+ def action(rows = 1)
9
+ cursor.up(rows)
10
+ end
11
+ end
12
+
13
+ class B < TTYString::CSICode # leftovers:allow
14
+ def action(rows = 1)
15
+ cursor.down(rows)
16
+ end
17
+ end
18
+
19
+ class C < TTYString::CSICode # leftovers:allow
20
+ def action(cols = 1)
21
+ cursor.right(cols)
22
+ end
23
+ end
24
+
25
+ class D < TTYString::CSICode # leftovers:allow
26
+ def action(cols = 1)
27
+ cursor.left(cols)
28
+ end
29
+ end
30
+
31
+ class E < TTYString::CSICode # leftovers:allow
32
+ def action(rows = 1)
33
+ cursor.down(rows)
34
+ cursor.col = 0
35
+ end
36
+ end
37
+
38
+ class F < TTYString::CSICode # leftovers:allow
39
+ def action(rows = 1)
40
+ cursor.up(rows)
41
+ cursor.col = 0
42
+ end
43
+ end
44
+
45
+ class G < TTYString::CSICode # leftovers:allow
46
+ def action(col = 1)
47
+ # cursor is zero indexed, arg is 1 indexed
48
+ cursor.col = col.to_i - 1
49
+ end
50
+ end
51
+
52
+ class H < TTYString::CSICode # leftovers:allow
53
+ def action(row = 1, col = 1)
54
+ # cursor is zero indexed, arg is 1 indexed
55
+ cursor.row = row.to_i - 1
56
+ cursor.col = col.to_i - 1
57
+ end
58
+ end
59
+
60
+ class LowH < TTYString::CSICode # leftovers:allow
61
+ char(/\?2004h/)
62
+ end
63
+
64
+ class LowF < TTYString::CSICode::H # leftovers:allow
65
+ char 'f'
66
+ end
67
+
68
+ class J < TTYString::CSICode # leftovers:allow
69
+ default_arg 0
70
+
71
+ def self.arg_re
72
+ /[0-3]?/
73
+ end
74
+
75
+ def action(mode = 0)
76
+ # :nocov: else won't ever be called. don't worry about it
77
+ case mode
78
+ # :nocov:
79
+ when 0 then screen.clear_forward
80
+ when 1 then screen.clear_backward
81
+ when 2, 3 then screen.clear
82
+ end
83
+ end
84
+ end
85
+
86
+ class K < TTYString::CSICode # leftovers:allow
87
+ default_arg 0
88
+
89
+ def self.arg_re
90
+ /[0-2]?/
91
+ end
92
+
93
+ def action(mode = 0)
94
+ # :nocov: else won't ever be called. don't worry about it
95
+ case mode
96
+ # :nocov:
97
+ when 0 then screen.clear_line_forward
98
+ when 1 then screen.clear_line_backward
99
+ when 2 then screen.clear_line
100
+ end
101
+ end
102
+ end
103
+
104
+ class LowL < TTYString::CSICode # leftovers:allow
105
+ char(/\?2004l/)
106
+ end
107
+
108
+ class LowM < TTYString::CSICode # leftovers:allow
109
+ char 'm'
110
+
111
+ def self.arg_re
112
+ # 0-255
113
+ /(?:\d|\d\d|1\d\d|2[0-4]\d|25[0-5])?/
114
+ end
115
+
116
+ def self.render(renderer)
117
+ super if renderer.clear_style
118
+ end
119
+
120
+ def action(*args); end
121
+ end
122
+
123
+ class S < TTYString::CSICode # leftovers:allow
124
+ def action(rows = 1)
125
+ rows.times { screen.scroll_up }
126
+ end
127
+ end
128
+
129
+ class T < TTYString::CSICode # leftovers:allow
130
+ def action(rows = 1)
131
+ rows.times { screen.scroll_down }
132
+ end
133
+ end
134
+
135
+ class Tilde < TTYString::CSICode # leftovers:allow
136
+ char(/(?:200|201)~/)
137
+ end
138
+ end
139
+ end
@@ -11,46 +11,33 @@ class TTYString
11
11
  end
12
12
 
13
13
  def row=(value)
14
- @row = value.to_i
14
+ @row = value
15
15
  @row = 0 if @row.negative?
16
16
  end
17
17
 
18
18
  def col=(value)
19
- @col = value.to_i
19
+ @col = value
20
20
  @col = 0 if @col.negative?
21
21
  end
22
22
 
23
23
  def left(count = 1)
24
- count = count.to_i
25
- raise ArgumentError if count.negative?
26
-
27
24
  self.col -= count
28
25
  end
29
26
 
30
27
  def up(count = 1)
31
- count = count.to_i
32
- raise ArgumentError if count.negative?
33
-
34
28
  self.row -= count
35
29
  end
36
30
 
37
31
  def down(count = 1)
38
- count = count.to_i
39
- raise ArgumentError unless count >= 0
40
-
41
32
  self.row += count
42
33
  end
43
34
 
44
35
  def right(count = 1)
45
- count = count.to_i
46
- raise ArgumentError unless count >= 0
47
-
48
36
  self.col += count
49
37
  end
50
38
 
51
39
  def to_ary
52
40
  [row, col]
53
41
  end
54
- alias to_a to_ary
55
42
  end
56
43
  end
@@ -1,82 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'strscan'
4
- require_relative 'renderer'
4
+ require_relative 'code_definitions'
5
+ require_relative 'csi_code_definitions'
6
+
7
+ require_relative 'screen'
5
8
 
6
9
  class TTYString
7
10
  # Reads the text string a
8
11
  class Parser < StringScanner
9
- def render(clear_style: true)
12
+ attr_accessor :clear_style
13
+ attr_reader :screen
14
+
15
+ def render
10
16
  reset
11
- @clear_style = clear_style
12
- @renderer = Renderer.new
17
+ @screen = Screen.new
13
18
  read until eos?
14
- renderer.to_s
19
+ screen.to_s
15
20
  end
16
21
 
17
- private
22
+ def cursor
23
+ screen.cursor
24
+ end
18
25
 
19
- attr_reader :renderer
20
- attr_reader :clear_style
26
+ private
21
27
 
22
28
  def write(string)
23
- renderer.write(string)
29
+ screen.write(string)
24
30
  end
25
31
 
26
32
  def read
27
- text || slash_n || slash_r || slash_t || slash_b || slash_e
28
- end
29
-
30
- def text
31
- write(matched) if scan(text_regexp)
32
- end
33
-
34
- def slash_n
35
- renderer.slash_n if skip(/\n/)
36
- end
37
-
38
- def slash_r
39
- renderer.slash_r if skip(/\r/)
40
- end
41
-
42
- def slash_t
43
- renderer.slash_t if skip(/\t/)
44
- end
45
-
46
- def slash_b
47
- # can't use `scan(/\b/)` because it matches everything
48
- return unless peek(1) == "\b"
49
-
50
- self.pos += 1
51
- renderer.slash_b
52
- end
53
-
54
- def slash_e
55
- return unless scan(csi_regexp)
56
-
57
- args = matched.slice(2..-2).split(';')
58
- command = matched.slice(-1)
59
- render_csi(:"csi_#{command}", *args)
60
- end
61
-
62
- def csi_regexp
63
- @csi_regexp ||= Regexp.new("\e#{csi_pattern}")
64
- end
65
-
66
- def csi_pattern
67
- "\\[(\\d+;?|;)*[ABCDEFGHJKf#{'m' if clear_style}]"
68
- end
69
-
70
- def text_regexp
71
- @text_regexp ||= Regexp.new("[^\b\t\r\n\e]|\e(?!#{csi_pattern})")
33
+ TTYString::Code.descendants.any? { |c| c.render(self) } || default
72
34
  end
73
35
 
74
- def render_csi(method, *args)
75
- method = renderer.method(method)
76
- params = method.parameters
77
- args = args.take(params.length) unless params.assoc(:rest)
78
- args = [] if args.all?(&:empty?)
79
- method.call(*args)
36
+ def default
37
+ write(getch)
80
38
  end
81
39
  end
82
40
  end
@@ -46,6 +46,16 @@ class TTYString
46
46
  @screen = []
47
47
  end
48
48
 
49
+ def scroll_up
50
+ screen.push([])
51
+ screen.shift
52
+ end
53
+
54
+ def scroll_down
55
+ screen.unshift([])
56
+ screen.pop
57
+ end
58
+
49
59
  def clear_lines_before
50
60
  screen.fill([], 0...row)
51
61
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class TTYString
4
- VERSION = '0.1.0'
4
+ VERSION = '1.1.0'
5
5
  end
data/tty_string.gemspec CHANGED
@@ -11,21 +11,35 @@ Gem::Specification.new do |spec|
11
11
  spec.email = ['robot@dana.sh']
12
12
 
13
13
  spec.summary = 'Render a string using ANSI TTY codes'
14
- spec.homepage = "https://github.com/robotdana/tty_string"
14
+ spec.homepage = 'https://github.com/robotdana/tty_string'
15
15
  spec.license = 'MIT'
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
- f.match(%r{^(test|spec|features|\.?rubocop|\.travis|\.git|\.rspec)/})
17
+ if spec.respond_to?(:metadata)
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = spec.homepage
20
+ spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
19
21
  end
20
- spec.bindir = 'exe'
21
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+
23
+ spec.files = Dir.glob('{lib}/**/*') + %w{
24
+ CHANGELOG.md
25
+ Gemfile
26
+ LICENSE.txt
27
+ README.md
28
+ tty_string.gemspec
29
+ }
30
+ spec.required_ruby_version = '>= 2.4'
22
31
  spec.require_paths = ['lib']
23
32
 
24
- spec.add_development_dependency 'bundler', '~> 1.16'
25
- spec.add_development_dependency 'pry', '~> 0.12.2'
26
- spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'bundler', '~> 2.0'
34
+ spec.add_development_dependency 'fast_ignore', '>= 0.15.1'
35
+ spec.add_development_dependency 'leftovers', '>= 0.2.0'
36
+ spec.add_development_dependency 'pry', '~> 0.12'
37
+ spec.add_development_dependency 'rake', '>= 12.3.3'
27
38
  spec.add_development_dependency 'rspec', '~> 3.0'
28
- spec.add_development_dependency 'rubocop', '~> 0.74.0'
29
- spec.add_development_dependency 'rubocop-performance', '~> 1.4.1'
30
- spec.add_development_dependency 'rubocop-rspec', '~> 1.35.0'
39
+ spec.add_development_dependency 'rubocop', '~> 0.93.1'
40
+ spec.add_development_dependency 'rubocop-performance', '~> 1.8.1'
41
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.44.1'
42
+ spec.add_development_dependency 'simplecov', '~> 0.18.5'
43
+ spec.add_development_dependency 'simplecov-console'
44
+ spec.add_development_dependency 'spellr', '>= 0.8.1'
31
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tty_string
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dana Sherson
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-21 00:00:00.000000000 Z
11
+ date: 2021-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,42 +16,70 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.16'
19
+ version: '2.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.16'
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fast_ignore
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.15.1
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.15.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: leftovers
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.2.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.2.0
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: pry
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - "~>"
32
60
  - !ruby/object:Gem::Version
33
- version: 0.12.2
61
+ version: '0.12'
34
62
  type: :development
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
38
66
  - - "~>"
39
67
  - !ruby/object:Gem::Version
40
- version: 0.12.2
68
+ version: '0.12'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: rake
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
- - - "~>"
73
+ - - ">="
46
74
  - !ruby/object:Gem::Version
47
- version: '10.0'
75
+ version: 12.3.3
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
- - - "~>"
80
+ - - ">="
53
81
  - !ruby/object:Gem::Version
54
- version: '10.0'
82
+ version: 12.3.3
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rspec
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -72,42 +100,84 @@ dependencies:
72
100
  requirements:
73
101
  - - "~>"
74
102
  - !ruby/object:Gem::Version
75
- version: 0.74.0
103
+ version: 0.93.1
76
104
  type: :development
77
105
  prerelease: false
78
106
  version_requirements: !ruby/object:Gem::Requirement
79
107
  requirements:
80
108
  - - "~>"
81
109
  - !ruby/object:Gem::Version
82
- version: 0.74.0
110
+ version: 0.93.1
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: rubocop-performance
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
115
  - - "~>"
88
116
  - !ruby/object:Gem::Version
89
- version: 1.4.1
117
+ version: 1.8.1
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
122
  - - "~>"
95
123
  - !ruby/object:Gem::Version
96
- version: 1.4.1
124
+ version: 1.8.1
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: rubocop-rspec
99
127
  requirement: !ruby/object:Gem::Requirement
100
128
  requirements:
101
129
  - - "~>"
102
130
  - !ruby/object:Gem::Version
103
- version: 1.35.0
131
+ version: 1.44.1
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 1.44.1
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.18.5
104
146
  type: :development
105
147
  prerelease: false
106
148
  version_requirements: !ruby/object:Gem::Requirement
107
149
  requirements:
108
150
  - - "~>"
109
151
  - !ruby/object:Gem::Version
110
- version: 1.35.0
152
+ version: 0.18.5
153
+ - !ruby/object:Gem::Dependency
154
+ name: simplecov-console
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: spellr
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: 0.8.1
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: 0.8.1
111
181
  description:
112
182
  email:
113
183
  - robot@dana.sh
@@ -115,29 +185,27 @@ executables: []
115
185
  extensions: []
116
186
  extra_rdoc_files: []
117
187
  files:
118
- - ".gitignore"
119
- - ".gitmodules"
120
- - ".rspec"
121
- - ".rubocop.yml"
122
- - ".travis.yml"
188
+ - CHANGELOG.md
123
189
  - Gemfile
124
- - Gemfile.lock
125
190
  - LICENSE.txt
126
191
  - README.md
127
- - Rakefile
128
- - bin/console
129
- - bin/setup
130
192
  - lib/tty_string.rb
193
+ - lib/tty_string/code.rb
194
+ - lib/tty_string/code_definitions.rb
195
+ - lib/tty_string/csi_code.rb
196
+ - lib/tty_string/csi_code_definitions.rb
131
197
  - lib/tty_string/cursor.rb
132
198
  - lib/tty_string/parser.rb
133
- - lib/tty_string/renderer.rb
134
199
  - lib/tty_string/screen.rb
135
200
  - lib/tty_string/version.rb
136
201
  - tty_string.gemspec
137
202
  homepage: https://github.com/robotdana/tty_string
138
203
  licenses:
139
204
  - MIT
140
- metadata: {}
205
+ metadata:
206
+ homepage_uri: https://github.com/robotdana/tty_string
207
+ source_code_uri: https://github.com/robotdana/tty_string
208
+ changelog_uri: https://github.com/robotdana/tty_string/blob/main/CHANGELOG.md
141
209
  post_install_message:
142
210
  rdoc_options: []
143
211
  require_paths:
@@ -146,15 +214,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
146
214
  requirements:
147
215
  - - ">="
148
216
  - !ruby/object:Gem::Version
149
- version: '0'
217
+ version: '2.4'
150
218
  required_rubygems_version: !ruby/object:Gem::Requirement
151
219
  requirements:
152
220
  - - ">="
153
221
  - !ruby/object:Gem::Version
154
222
  version: '0'
155
223
  requirements: []
156
- rubyforge_project:
157
- rubygems_version: 2.5.2.1
224
+ rubygems_version: 3.0.3
158
225
  signing_key:
159
226
  specification_version: 4
160
227
  summary: Render a string using ANSI TTY codes
data/.gitignore DELETED
@@ -1,11 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- # rspec failure tracking
11
- .rspec_status
data/.gitmodules DELETED
@@ -1,3 +0,0 @@
1
- [submodule "rubocop_yml"]
2
- path = rubocop_yml
3
- url = https://github.com/robotdana/rubocop_yml.git
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/.rubocop.yml DELETED
@@ -1 +0,0 @@
1
- rubocop_yml/rubocop.yml
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.5.1
5
- before_install: gem install bundler -v 1.16.0
data/Gemfile.lock DELETED
@@ -1,63 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- tty_string (0.1.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- ast (2.4.0)
10
- coderay (1.1.2)
11
- diff-lcs (1.3)
12
- jaro_winkler (1.5.3)
13
- method_source (0.9.2)
14
- parallel (1.17.0)
15
- parser (2.6.4.1)
16
- ast (~> 2.4.0)
17
- pry (0.12.2)
18
- coderay (~> 1.1.0)
19
- method_source (~> 0.9.0)
20
- rainbow (3.0.0)
21
- rake (10.5.0)
22
- rspec (3.8.0)
23
- rspec-core (~> 3.8.0)
24
- rspec-expectations (~> 3.8.0)
25
- rspec-mocks (~> 3.8.0)
26
- rspec-core (3.8.2)
27
- rspec-support (~> 3.8.0)
28
- rspec-expectations (3.8.4)
29
- diff-lcs (>= 1.2.0, < 2.0)
30
- rspec-support (~> 3.8.0)
31
- rspec-mocks (3.8.1)
32
- diff-lcs (>= 1.2.0, < 2.0)
33
- rspec-support (~> 3.8.0)
34
- rspec-support (3.8.2)
35
- rubocop (0.74.0)
36
- jaro_winkler (~> 1.5.1)
37
- parallel (~> 1.10)
38
- parser (>= 2.6)
39
- rainbow (>= 2.2.2, < 4.0)
40
- ruby-progressbar (~> 1.7)
41
- unicode-display_width (>= 1.4.0, < 1.7)
42
- rubocop-performance (1.4.1)
43
- rubocop (>= 0.71.0)
44
- rubocop-rspec (1.35.0)
45
- rubocop (>= 0.60.0)
46
- ruby-progressbar (1.10.1)
47
- unicode-display_width (1.6.0)
48
-
49
- PLATFORMS
50
- ruby
51
-
52
- DEPENDENCIES
53
- bundler (~> 1.16)
54
- pry (~> 0.12.2)
55
- rake (~> 10.0)
56
- rspec (~> 3.0)
57
- rubocop (~> 0.74.0)
58
- rubocop-performance (~> 1.4.1)
59
- rubocop-rspec (~> 1.35.0)
60
- tty_string!
61
-
62
- BUNDLED WITH
63
- 1.16.0
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task default: :spec
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'tty_string'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require 'irb'
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'screen'
4
- class TTYString
5
- # turns the text string into screen instructions
6
- class Renderer
7
- attr_reader :screen
8
-
9
- def initialize
10
- @screen = Screen.new
11
- end
12
-
13
- def to_s
14
- screen.to_s
15
- end
16
-
17
- def write(string)
18
- screen.write(string)
19
- end
20
-
21
- # rubocop:disable Naming/MethodName
22
- def csi_A(rows = 1)
23
- cursor.up(rows)
24
- end
25
-
26
- def csi_B(rows = 1)
27
- cursor.down(rows)
28
- end
29
-
30
- def csi_C(cols = 1)
31
- cursor.right(cols)
32
- end
33
-
34
- def csi_D(cols = 1)
35
- cursor.left(cols)
36
- end
37
-
38
- def csi_E(rows = 1)
39
- cursor.down(rows)
40
- cursor.col = 0
41
- end
42
-
43
- def csi_F(rows = 1)
44
- cursor.up(rows)
45
- cursor.col = 0
46
- end
47
-
48
- def csi_G(col = 1)
49
- cursor.col = col.to_i - 1 # cursor is zero indexed, arg is 1 indexed
50
- end
51
-
52
- def csi_H(row = 1, col = 1)
53
- # cursor is zero indexed, arg is 1 indexed
54
- cursor.row = row.to_i - 1
55
- cursor.col = col.to_i - 1
56
- end
57
- alias csi_f csi_H
58
-
59
- def csi_J(mode = 0)
60
- case mode.to_i
61
- when 0 then screen.clear_forward
62
- when 1 then screen.clear_backward
63
- when 2, 3 then screen.clear
64
- end
65
- end
66
-
67
- def csi_K(mode = 0)
68
- case mode.to_i
69
- when 0 then screen.clear_line_forward
70
- when 1 then screen.clear_line_backward
71
- when 2 then screen.clear_line
72
- end
73
- end
74
-
75
- def csi_m(*args)
76
- end
77
- # rubocop:enable Naming/MethodName
78
-
79
- def slash_b
80
- cursor.left
81
- screen.clear_at_cursor
82
- end
83
-
84
- def slash_n
85
- cursor.down
86
- cursor.col = 0
87
- screen.write('')
88
- end
89
-
90
- def slash_r
91
- cursor.col = 0
92
- end
93
-
94
- def slash_t
95
- cursor.right(8 - (cursor.col % 8))
96
- end
97
-
98
- private
99
-
100
- def cursor
101
- screen.cursor
102
- end
103
- end
104
- end