eskimo 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/eskimo.rb +20 -4
- data/lib/eskimo/component.rb +2 -2
- data/lib/eskimo/components/did_you_mean.rb +34 -0
- data/lib/eskimo/components/either.rb +30 -0
- data/lib/eskimo/components/highlight.rb +31 -0
- data/lib/eskimo/components/highlight_column.rb +61 -0
- data/lib/eskimo/components/indent.rb +25 -0
- data/lib/eskimo/components/line_break.rb +18 -0
- data/lib/eskimo/components/soft_break.rb +16 -0
- data/lib/eskimo/components/squeeze.rb +43 -0
- data/lib/eskimo/components/strip.rb +15 -0
- data/lib/eskimo/components/strip_left.rb +15 -0
- data/lib/eskimo/components/strip_right.rb +15 -0
- data/lib/eskimo/components/style.rb +25 -0
- data/lib/eskimo/components/truncate.rb +31 -0
- data/lib/eskimo/components/truncate_rear.rb +25 -0
- data/lib/eskimo/components/wrap.rb +26 -0
- data/lib/eskimo/constants.rb +7 -0
- data/lib/eskimo/renderer.rb +15 -17
- data/lib/eskimo/version.rb +1 -1
- data/spec/component_spec.rb +13 -0
- data/spec/components/did_you_mean_spec.rb +21 -0
- data/spec/components/either_spec.rb +21 -0
- data/spec/components/highlight_column_spec.rb +63 -0
- data/spec/components/highlight_spec.rb +25 -0
- data/spec/components/indent_spec.rb +15 -0
- data/spec/components/line_break_spec.rb +17 -0
- data/spec/components/soft_break_spec.rb +17 -0
- data/spec/components/squeeze_spec.rb +61 -0
- data/spec/components/strip_left_spec.rb +15 -0
- data/spec/components/strip_right_spec.rb +15 -0
- data/spec/components/strip_spec.rb +15 -0
- data/spec/components/style_spec.rb +25 -0
- data/spec/components/truncate_rear_spec.rb +35 -0
- data/spec/components/truncate_spec.rb +35 -0
- data/spec/components/wrap_spec.rb +15 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/component_suite.rb +15 -0
- metadata +52 -5
- data/lib/eskimo/components.rb +0 -114
- data/spec/components_spec.rb +0 -159
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Wrap a block text with newlines at a certain threshold.
|
6
|
+
#
|
7
|
+
# Wrap.new(width: 3) do
|
8
|
+
# "foobar"
|
9
|
+
# end
|
10
|
+
# # => "foo"
|
11
|
+
# # "bar"
|
12
|
+
#
|
13
|
+
class Wrap < Component
|
14
|
+
attr_reader :width
|
15
|
+
|
16
|
+
def initialize(width: Constants::SCREEN_COLUMNS, &children)
|
17
|
+
@width = width
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def render(**)
|
22
|
+
Strings.wrap(super, width)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/eskimo/renderer.rb
CHANGED
@@ -18,22 +18,23 @@ module Eskimo
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def render(*components)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
21
|
+
buf = +''
|
22
|
+
|
23
|
+
for component in components do
|
24
|
+
if component.is_a?(String)
|
25
|
+
buf << component
|
26
|
+
elsif component.is_a?(Array)
|
27
|
+
buf << render(*component)
|
28
|
+
elsif component.is_a?(Proc)
|
29
|
+
buf << render(component[**@props])
|
30
|
+
elsif component.respond_to?(:render)
|
31
|
+
buf << render(component.render(**@props))
|
32
|
+
elsif component
|
33
|
+
bail(component)
|
35
34
|
end
|
36
35
|
end
|
36
|
+
|
37
|
+
buf
|
37
38
|
end
|
38
39
|
|
39
40
|
def bail(component)
|
@@ -41,8 +42,5 @@ module Eskimo
|
|
41
42
|
"Eskimo: don't know how to render #{component.class} => #{component}"
|
42
43
|
)
|
43
44
|
end
|
44
|
-
|
45
|
-
NOT_FALSEY = ->(x) { !!x }.freeze
|
46
|
-
private_constant :NOT_FALSEY
|
47
45
|
end
|
48
46
|
end
|
data/lib/eskimo/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Component do
|
6
|
+
it 'accepts a block of children' do
|
7
|
+
expect(
|
8
|
+
Eskimo::Renderer.new.apply {
|
9
|
+
Eskimo::Component.new { ['hello', "\n", 'world!'] }
|
10
|
+
}
|
11
|
+
).to eq("hello\nworld!")
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::DidYouMean do
|
6
|
+
it 'renders a suggestion' do
|
7
|
+
expect(
|
8
|
+
renderer.apply {
|
9
|
+
described_class.new(dictionary: [ 'abc', 'bca' ], item: 'abd')
|
10
|
+
}
|
11
|
+
).to eq("Did you mean? abc")
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'renders nothing if no suggestions were found' do
|
15
|
+
expect(
|
16
|
+
renderer.apply {
|
17
|
+
described_class.new(dictionary: [ 'abc', 'bca' ], item: 'asdf')
|
18
|
+
}
|
19
|
+
).to eq("")
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::Either do
|
6
|
+
it 'renders the primary component' do
|
7
|
+
expect(
|
8
|
+
renderer.apply {
|
9
|
+
described_class.new(->(**) { false }, "Fallback here")
|
10
|
+
}
|
11
|
+
).to eq("Fallback here")
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'renders the fallback if the primary does not' do
|
15
|
+
expect(
|
16
|
+
renderer.apply {
|
17
|
+
described_class.new(->(**) { "Hi!" }, "Fallback here")
|
18
|
+
}
|
19
|
+
).to eq("Hi!")
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::HighlightColumn do
|
6
|
+
it 'highlights the character' do
|
7
|
+
style = [ :bold, :red ]
|
8
|
+
|
9
|
+
expect(
|
10
|
+
renderer.apply {
|
11
|
+
described_class.new(column: 1, line: 1, style: style) { "number:\nfour" }
|
12
|
+
}
|
13
|
+
).to include("f#{pastel.decorate('o', *style)}ur")
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'highlights the first character' do
|
17
|
+
style = [ :bold, :red ]
|
18
|
+
|
19
|
+
expect(
|
20
|
+
renderer.apply {
|
21
|
+
described_class.new(column: 0, line: 0, style: style) { "four" }
|
22
|
+
}
|
23
|
+
).to include("#{pastel.decorate('f', *style)}our")
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'highlights the last character' do
|
27
|
+
style = [ :bold, :red ]
|
28
|
+
|
29
|
+
expect(
|
30
|
+
renderer.apply {
|
31
|
+
described_class.new(column: 3, line: 0, style: style) { "four" }
|
32
|
+
}
|
33
|
+
).to include("fou#{pastel.decorate('r', *style)}")
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'highlights nothing' do
|
37
|
+
expect(
|
38
|
+
renderer.apply {
|
39
|
+
described_class.new(column: 1234, line: 0) { "four" }
|
40
|
+
}
|
41
|
+
).to eq("four")
|
42
|
+
|
43
|
+
expect(
|
44
|
+
renderer.apply {
|
45
|
+
described_class.new(column: 0, line: 1234) { "four" }
|
46
|
+
}
|
47
|
+
).to eq("four")
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'inserts the arrow' do
|
51
|
+
expect(
|
52
|
+
strip_styles(
|
53
|
+
renderer.apply {
|
54
|
+
described_class.new(column: 1, line: 0) { "four" }
|
55
|
+
}
|
56
|
+
)
|
57
|
+
).to eq(
|
58
|
+
"four" + "\n" +
|
59
|
+
" ^" + "\n" +
|
60
|
+
" here" + "\n"
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::Highlight do
|
6
|
+
it 'styles a substring' do
|
7
|
+
style = [ :bold, :red ]
|
8
|
+
|
9
|
+
expect(
|
10
|
+
renderer.apply {
|
11
|
+
described_class.new(pattern: /ou/, style: style) { 'four' }
|
12
|
+
}
|
13
|
+
).to eq("f#{Pastel.new.decorate('ou', *style)}r")
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'is a no-op if no match' do
|
17
|
+
input = 'four'
|
18
|
+
|
19
|
+
expect(
|
20
|
+
renderer.apply {
|
21
|
+
described_class.new(pattern: /asdf/) { input }
|
22
|
+
}
|
23
|
+
).to eq(input)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::Indent do
|
6
|
+
it 'indents each line' do
|
7
|
+
expect(
|
8
|
+
renderer.apply do
|
9
|
+
ESK::Indent.new(width: 2) do
|
10
|
+
"hello\nworld"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
).to eq(" hello\n world")
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::LineBreak do
|
6
|
+
it 'inserts a hard linebreak' do
|
7
|
+
expect(
|
8
|
+
renderer.apply {
|
9
|
+
[
|
10
|
+
'hello',
|
11
|
+
ESK::LineBreak.new,
|
12
|
+
'world'
|
13
|
+
]
|
14
|
+
}
|
15
|
+
).to eq("hello\n \nworld")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::SoftBreak do
|
6
|
+
it 'inserts a soft linebreak' do
|
7
|
+
expect(
|
8
|
+
renderer.apply {
|
9
|
+
[
|
10
|
+
'hello',
|
11
|
+
ESK::SoftBreak.new,
|
12
|
+
'world'
|
13
|
+
]
|
14
|
+
}
|
15
|
+
).to eq("hello\nworld")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::Squeeze do
|
6
|
+
it 'swallows consecutive newlines' do
|
7
|
+
expect(
|
8
|
+
renderer.apply do
|
9
|
+
ESK::Squeeze.new([
|
10
|
+
ESK::SoftBreak.new,
|
11
|
+
false && '1',
|
12
|
+
|
13
|
+
ESK::SoftBreak.new,
|
14
|
+
false && '2',
|
15
|
+
|
16
|
+
ESK::SoftBreak.new,
|
17
|
+
'3',
|
18
|
+
|
19
|
+
ESK::SoftBreak.new,
|
20
|
+
'4',
|
21
|
+
])
|
22
|
+
end
|
23
|
+
).to eq("\n3\n4")
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'does not swallow consecutive newlines in inner children' do
|
27
|
+
expect(
|
28
|
+
renderer.apply do
|
29
|
+
ESK::Squeeze.new([
|
30
|
+
ESK::Indent.new(width: 0) do
|
31
|
+
[
|
32
|
+
ESK::SoftBreak.new,
|
33
|
+
false && '1',
|
34
|
+
|
35
|
+
ESK::SoftBreak.new,
|
36
|
+
false && '2',
|
37
|
+
|
38
|
+
ESK::SoftBreak.new,
|
39
|
+
'3',
|
40
|
+
|
41
|
+
ESK::SoftBreak.new,
|
42
|
+
'4',
|
43
|
+
]
|
44
|
+
end
|
45
|
+
])
|
46
|
+
end
|
47
|
+
).to eq("\n\n\n3\n4")
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'bails if not given an array of children' do
|
51
|
+
expect { ESK::Squeeze.new('') }.to raise_error(ArgumentError,
|
52
|
+
"Squeeze works only with an Array of components"
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'bails if given a block' do
|
57
|
+
expect { ESK::Squeeze.new([]) {} }.to raise_error(ArgumentError,
|
58
|
+
"Squeeze works only with an Array of components"
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::StripLeft do
|
6
|
+
it 'strips whitespace from the head' do
|
7
|
+
expect(
|
8
|
+
renderer.apply do
|
9
|
+
ESK::StripLeft.new do
|
10
|
+
" hello world "
|
11
|
+
end
|
12
|
+
end
|
13
|
+
).to eq("hello world ")
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::StripRight do
|
6
|
+
it 'strips whitespace from the rear' do
|
7
|
+
expect(
|
8
|
+
renderer.apply do
|
9
|
+
ESK::StripRight.new do
|
10
|
+
" hello world "
|
11
|
+
end
|
12
|
+
end
|
13
|
+
).to eq(" hello world")
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::Strip do
|
6
|
+
it 'strips surrounding whitespace' do
|
7
|
+
expect(
|
8
|
+
renderer.apply do
|
9
|
+
ESK::Strip.new do
|
10
|
+
" hello world "
|
11
|
+
end
|
12
|
+
end
|
13
|
+
).to eq("hello world")
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::Style do
|
6
|
+
it 'applies ANSI styling to text' do
|
7
|
+
expect(
|
8
|
+
renderer.apply {
|
9
|
+
[
|
10
|
+
ESK::Style.new([:bold, :green]) { 'hai' },
|
11
|
+
]
|
12
|
+
}
|
13
|
+
).to eq("\e[1;32mhai\e[0m")
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'accepts a splat' do
|
17
|
+
expect(
|
18
|
+
renderer.apply {
|
19
|
+
[
|
20
|
+
ESK::Style.new(:bold, :green) { 'hai' },
|
21
|
+
]
|
22
|
+
}
|
23
|
+
).to eq("\e[1;32mhai\e[0m")
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Eskimo::Components::TruncateRear do
|
6
|
+
it 'truncates a string from the rear if it exceeds the length' do
|
7
|
+
expect(
|
8
|
+
renderer.apply do
|
9
|
+
ESK::TruncateRear.new(width: 5) do
|
10
|
+
"hello world"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
).to eq("hello...")
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'reserves chars from the width' do
|
17
|
+
expect(
|
18
|
+
renderer.apply do
|
19
|
+
ESK::TruncateRear.new(width: 10, reserve: 5) do
|
20
|
+
("hello world")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
).to eq("hello...")
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'is a no-op if text fits' do
|
27
|
+
expect(
|
28
|
+
renderer.apply do
|
29
|
+
ESK::TruncateRear.new(width: 62, reserve: 5) do
|
30
|
+
("hello world")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
).to eq("hello world")
|
34
|
+
end
|
35
|
+
end
|