eskimo 1.0.0 → 2.0.0
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 +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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7882c146ce7e288df43b38765be92f216137d746c1efb13cf76b1ac7e1ec13ad
|
4
|
+
data.tar.gz: 1e2bff1433d29f448a74bf39b54b083b6bc79869af86b00c293fefaa09493624
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77d8528c5d5f839c7bbf8daf9bb049fe752c5077b34107e007bdba14b1192723760763750ea4061d2a4e60bf8573c11133ff3d174363d3eadb8f2fe1252eaa11
|
7
|
+
data.tar.gz: 8dfa377b6eb2e05e09eb8e5d70b2a747fc5b9073e5859055145bf66c413e3051239ea55ca9d386936ee5905e1382dd2938140c512aaf4360af9010fb16986a49
|
data/lib/eskimo.rb
CHANGED
@@ -2,9 +2,25 @@ require 'pastel'
|
|
2
2
|
require 'strings'
|
3
3
|
require 'tty-screen'
|
4
4
|
|
5
|
-
|
5
|
+
require 'eskimo/version'
|
6
6
|
|
7
|
-
|
8
|
-
require_relative './eskimo/renderer'
|
7
|
+
require 'eskimo/constants'
|
9
8
|
|
10
|
-
|
9
|
+
require 'eskimo/component'
|
10
|
+
require 'eskimo/renderer'
|
11
|
+
|
12
|
+
require 'eskimo/components/did_you_mean'
|
13
|
+
require 'eskimo/components/either'
|
14
|
+
require 'eskimo/components/highlight'
|
15
|
+
require 'eskimo/components/highlight_column'
|
16
|
+
require 'eskimo/components/indent'
|
17
|
+
require 'eskimo/components/line_break'
|
18
|
+
require 'eskimo/components/soft_break'
|
19
|
+
require 'eskimo/components/squeeze'
|
20
|
+
require 'eskimo/components/strip'
|
21
|
+
require 'eskimo/components/strip_left'
|
22
|
+
require 'eskimo/components/strip_right'
|
23
|
+
require 'eskimo/components/style'
|
24
|
+
require 'eskimo/components/truncate'
|
25
|
+
require 'eskimo/components/truncate_rear'
|
26
|
+
require 'eskimo/components/wrap'
|
data/lib/eskimo/component.rb
CHANGED
@@ -20,8 +20,8 @@ module Eskimo
|
|
20
20
|
# `render` prop provided by {Renderer#apply} with the tracked children
|
21
21
|
# which converts them to a String and returns them.
|
22
22
|
class Component
|
23
|
-
def initialize(&
|
24
|
-
@children =
|
23
|
+
def initialize(*, **, &children_gen)
|
24
|
+
@children = children_gen
|
25
25
|
end
|
26
26
|
|
27
27
|
def render(render:, **)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Present the user with the closest possible correction, if any.
|
6
|
+
#
|
7
|
+
# DidYouMean.new(dictionary: [ 'abc', 'bca' ], item: 'abd')
|
8
|
+
# # => "hint: Did you mean? abc"
|
9
|
+
#
|
10
|
+
# DidYouMean.new(dictionary: [ 'abc', 'bca' ], item: 'asdfasdf')
|
11
|
+
# # => ""
|
12
|
+
#
|
13
|
+
# See https://github.com/yuki24/did_you_mean#using-the-didyoumeanspellchecker
|
14
|
+
class DidYouMean < Component
|
15
|
+
attr_reader :corrections, :separator
|
16
|
+
|
17
|
+
def initialize(dictionary:, item:, separator: " or ", &children)
|
18
|
+
@corrections = ::DidYouMean::SpellChecker.new(
|
19
|
+
dictionary: dictionary
|
20
|
+
).correct(item)
|
21
|
+
|
22
|
+
@separator = separator
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def render(**)
|
28
|
+
if corrections.any?
|
29
|
+
"Did you mean? #{corrections.join(separator)}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Render a fallback component in case the first one evaluates to an empty
|
6
|
+
# String.
|
7
|
+
#
|
8
|
+
# Either.new(->(**) { false }, "Fallback here")
|
9
|
+
# # => "Fallback here"
|
10
|
+
#
|
11
|
+
# Either.new(->(**) { "Hi!" }, "Fallback here")
|
12
|
+
# # => "Hi!"
|
13
|
+
#
|
14
|
+
class Either
|
15
|
+
attr_reader :children
|
16
|
+
|
17
|
+
def initialize(primary, fallback)
|
18
|
+
@children = [ primary, fallback ]
|
19
|
+
end
|
20
|
+
|
21
|
+
def render(render:, **)
|
22
|
+
for child in children do
|
23
|
+
rendered = render[child]
|
24
|
+
|
25
|
+
return rendered unless rendered.empty?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Highlight a substring with ASCII arrows.
|
6
|
+
#
|
7
|
+
# Highlight.new(pattern: /lol/) do
|
8
|
+
# "- include: lol://file.yml"
|
9
|
+
# end
|
10
|
+
# # => "- include: lol://file.yml"
|
11
|
+
# # ^^^
|
12
|
+
#
|
13
|
+
class Highlight < Component
|
14
|
+
attr_reader :pastel, :pattern, :style
|
15
|
+
|
16
|
+
def initialize(pattern:, style: [:red, :bold, :underline], &children)
|
17
|
+
@pastel = Pastel.new
|
18
|
+
@pattern = pattern
|
19
|
+
@style = style
|
20
|
+
|
21
|
+
super(&children)
|
22
|
+
end
|
23
|
+
|
24
|
+
def render(**)
|
25
|
+
super.sub(pattern) do |substring|
|
26
|
+
pastel.decorate(substring, *style)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Eskimo
|
2
|
+
module Components
|
3
|
+
# Highlight a particular character of a string with an ASCII arrow.
|
4
|
+
#
|
5
|
+
# HighlightColumn.new(line: 0, column: 14) do
|
6
|
+
# "- include: lol://wut.yml"
|
7
|
+
# end
|
8
|
+
# => "- include: lol://wut.yml"
|
9
|
+
# " ^ "
|
10
|
+
# " here "
|
11
|
+
class HighlightColumn < Component
|
12
|
+
def initialize(
|
13
|
+
column:,
|
14
|
+
line:,
|
15
|
+
markers: ['^', 'here'],
|
16
|
+
style: [:bold, :red],
|
17
|
+
&children
|
18
|
+
)
|
19
|
+
pastel = Pastel.new
|
20
|
+
|
21
|
+
@colorize = ->(str) { pastel.decorate(str, *style) }
|
22
|
+
@column = column
|
23
|
+
@line = line
|
24
|
+
@marker_padding = ' ' * @column
|
25
|
+
@markers = markers
|
26
|
+
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def render(**)
|
31
|
+
lines = super.lines
|
32
|
+
line = lines[@line]
|
33
|
+
|
34
|
+
unless line.nil? || line[@column].nil?
|
35
|
+
lines[@line] = transform_line!(line, @column, &@colorize)
|
36
|
+
end
|
37
|
+
|
38
|
+
lines.join
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def create_markers()
|
44
|
+
buf = ''
|
45
|
+
|
46
|
+
for marker in @markers do
|
47
|
+
buf << @marker_padding + @colorize[marker] + "\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
buf
|
51
|
+
end
|
52
|
+
|
53
|
+
def transform_line!(line, column, &fn)
|
54
|
+
line[column] = fn[line[column]]
|
55
|
+
line << "\n" unless line.end_with?("\n")
|
56
|
+
line << create_markers
|
57
|
+
line
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Indent text from the left.
|
6
|
+
#
|
7
|
+
# Indent.new(width: 8) do
|
8
|
+
# [ "Hello", SoftBreak.new, "World!" ]
|
9
|
+
# end
|
10
|
+
# # => " Hello"
|
11
|
+
# # " World!"
|
12
|
+
class Indent < Component
|
13
|
+
attr_reader :width
|
14
|
+
|
15
|
+
def initialize(width: 4, &children)
|
16
|
+
@width = width
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(**)
|
21
|
+
Strings.pad(super, [0,0,0,width])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Insert a paragraph-like line-break.
|
6
|
+
#
|
7
|
+
# [ "Hello", LineBreak.new, "World!" ]
|
8
|
+
# # => "Hello"
|
9
|
+
# # ""
|
10
|
+
# # ""
|
11
|
+
# # "World!"
|
12
|
+
class LineBreak
|
13
|
+
def render(**)
|
14
|
+
"\n \n"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Squeeze immediate consecutive soft breaks.
|
6
|
+
#
|
7
|
+
# Squeeze.new do
|
8
|
+
# [
|
9
|
+
# SoftBreak.new,
|
10
|
+
# ConditionalComponent.new,
|
11
|
+
#
|
12
|
+
# SoftBreak.new,
|
13
|
+
# false && SomeOtherComponent.new,
|
14
|
+
#
|
15
|
+
# SoftBreak.new,
|
16
|
+
# 'hello',
|
17
|
+
# ]
|
18
|
+
# end
|
19
|
+
# # => ""
|
20
|
+
# # "hello"
|
21
|
+
#
|
22
|
+
# The soft breaks for each conditional component will be preserved only
|
23
|
+
# if they do render some content.
|
24
|
+
class Squeeze
|
25
|
+
def initialize(children)
|
26
|
+
if !children.is_a?(Array) || block_given?
|
27
|
+
raise ArgumentError.new("Squeeze works only with an Array of components")
|
28
|
+
end
|
29
|
+
|
30
|
+
@children = children
|
31
|
+
end
|
32
|
+
|
33
|
+
def render(**props)
|
34
|
+
rendered = @children.map(&props[:render])
|
35
|
+
|
36
|
+
without_blanks = rendered.reject(&:empty?)
|
37
|
+
without_blanks.reject.with_index do |element, index|
|
38
|
+
element == "\n" && without_blanks[index+1] == "\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Remove whitespace from the beginning.
|
6
|
+
#
|
7
|
+
# StripLeft.new { " hello world " }
|
8
|
+
# # => "hello world "
|
9
|
+
class StripLeft < Component
|
10
|
+
def render(**)
|
11
|
+
super.lstrip
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Remove whitespace from the end.
|
6
|
+
#
|
7
|
+
# StripRight.new { " hello world " }
|
8
|
+
# # => " hello world"
|
9
|
+
class StripRight < Component
|
10
|
+
def render(**)
|
11
|
+
super.rstrip
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Style text with colors and custom formatting.
|
6
|
+
#
|
7
|
+
# See [Pastel's documentation][pastel] for the accepted styles.
|
8
|
+
#
|
9
|
+
# [pastel]: https://github.com/piotrmurach/pastel
|
10
|
+
class Style < Component
|
11
|
+
attr_reader :pastel, :style
|
12
|
+
|
13
|
+
def initialize(*style, &children)
|
14
|
+
@style = style.flatten
|
15
|
+
@pastel = Pastel.new
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(**)
|
21
|
+
pastel.decorate(super, *style)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eskimo
|
4
|
+
module Components
|
5
|
+
# Truncate text from the beginning if it exceeds a certain width.
|
6
|
+
#
|
7
|
+
# Truncate.new(width: 3) do
|
8
|
+
# "foo bar"
|
9
|
+
# end
|
10
|
+
# # => "... bar"
|
11
|
+
class Truncate < Component
|
12
|
+
attr_reader :maxlen
|
13
|
+
|
14
|
+
def initialize(reserve: 0, width: Constants::SCREEN_COLUMNS, &children)
|
15
|
+
@maxlen = [0, width - reserve].max
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(**)
|
21
|
+
text = super
|
22
|
+
|
23
|
+
if text.length >= maxlen
|
24
|
+
'...' + text[text.length - maxlen - 1 .. -1]
|
25
|
+
else
|
26
|
+
text
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'eskimo/components/truncate'
|
4
|
+
|
5
|
+
module Eskimo
|
6
|
+
module Components
|
7
|
+
# Truncate text from the rear if it exceeds a certain width.
|
8
|
+
#
|
9
|
+
# TruncateRear.new(width: 3) do
|
10
|
+
# "foo bar"
|
11
|
+
# end
|
12
|
+
# # => "foo..."
|
13
|
+
class TruncateRear < Truncate
|
14
|
+
def render(render:, **)
|
15
|
+
text = render[@children]
|
16
|
+
|
17
|
+
if text.length >= maxlen
|
18
|
+
text[0..maxlen - 1] + '...'
|
19
|
+
else
|
20
|
+
text
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|