kramdown-ansi 0.4.1 β 0.6.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/.utilsrc +96 -0
- data/CHANGES.md +96 -0
- data/README.md +10 -5
- data/Rakefile +21 -1
- data/kramdown-ansi.gemspec +9 -7
- data/lib/kramdown/ansi/styles.rb +9 -4
- data/lib/kramdown/ansi/width.rb +132 -16
- data/lib/kramdown/ansi.rb +94 -10
- data/lib/kramdown/version.rb +7 -2
- data/spec/assets/html.ansi +28 -0
- data/spec/assets/html.md +28 -0
- data/spec/assets/tables.ansi +348 -0
- data/spec/assets/tables.md +236 -0
- data/spec/kramdown/ansi/pager_spec.rb +0 -2
- data/spec/kramdown/ansi/styles_spec.rb +0 -3
- data/spec/kramdown/ansi/width_spec.rb +90 -2
- data/spec/kramdown/ansi_golden_spec.rb +19 -0
- data/spec/kramdown/ansi_spec.rb +8 -6
- data/spec/kramdown/ansi_table_spec.rb +51 -0
- data/spec/kramdown/render_spec.rb +156 -0
- data/spec/spec_helper.rb +96 -0
- data/tmp/.gitkeep +0 -0
- metadata +46 -6
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
1
|
describe Kramdown::ANSI::Width do
|
|
2
|
+
let :ansi do
|
|
3
|
+
Term::ANSIColor
|
|
4
|
+
end
|
|
5
|
+
|
|
4
6
|
before do
|
|
5
7
|
allow(Tins::Terminal).to receive(:columns).and_return 80
|
|
6
8
|
end
|
|
@@ -60,12 +62,52 @@ describe Kramdown::ANSI::Width do
|
|
|
60
62
|
expect(truncated.size).to eq 64
|
|
61
63
|
end
|
|
62
64
|
|
|
65
|
+
it 'can truncate ansi escape sequences with length' do
|
|
66
|
+
truncated = described_class.truncate([ ansi.red { ?A * 10 }, ansi.green { ?A * 10 } ] * 5 * ' ', length: 64)
|
|
67
|
+
expect(truncated).to eq(
|
|
68
|
+
"\e[31mAAAAAAAAAA\e[0m \e[32mAAAAAAAAAA\e[0m \e[31mAAAAAAAAAA\e[0m \e[32mAAAAAAAAAA\e[0m \e[31mAAAAAAAAAA\e[0m \e[32mAAAAAAAAβ¦\e[0m"
|
|
69
|
+
)
|
|
70
|
+
expect(Term::ANSIColor.uncolor {truncated }.size).to eq 64
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'can truncate wide emojis with length' do
|
|
74
|
+
truncated = described_class.truncate('ABC' + ?π * 10 + 'DEF', length: 10)
|
|
75
|
+
expect(truncated).to eq("ABCπππβ¦")
|
|
76
|
+
expect(truncated.size).to eq 7
|
|
77
|
+
expect(described_class.display_width(truncated)).to eq 10
|
|
78
|
+
truncated = described_class.truncate('AB' + ?π * 10 + 'DEF', length: 10)
|
|
79
|
+
expect(truncated).to eq("ABπππβ¦")
|
|
80
|
+
expect(truncated.size).to eq 6
|
|
81
|
+
expect(described_class.display_width(truncated)).to eq 9
|
|
82
|
+
truncated = described_class.truncate('A' + ?π * 10 + 'DEF', length: 10)
|
|
83
|
+
expect(truncated).to eq("Aππππβ¦")
|
|
84
|
+
expect(truncated.size).to eq 6
|
|
85
|
+
expect(described_class.display_width(truncated)).to eq 10
|
|
86
|
+
truncated = described_class.truncate('A' * 5 + ?π + 'B' * 5, length: 10)
|
|
87
|
+
expect(truncated).to eq("AAAAAπBBβ¦")
|
|
88
|
+
expect(truncated.size).to eq 9
|
|
89
|
+
expect(described_class.display_width(truncated)).to eq 10
|
|
90
|
+
truncated = described_class.truncate('A' * 3 + ?π + 'B' + ?π + 'C' * 5, length: 10)
|
|
91
|
+
expect(truncated).to eq("AAAπBπCβ¦")
|
|
92
|
+
expect(truncated.size).to eq 8
|
|
93
|
+
expect(described_class.display_width(truncated)).to eq 10
|
|
94
|
+
truncated = described_class.truncate('A' * 3 + ?π + ansi.red { 'B' } + ?π + 'C' * 5, length: 10)
|
|
95
|
+
expect(truncated).to eq("AAAπ\e[31mB\e[0mπCβ¦\e[0m")
|
|
96
|
+
expect(truncated.size).to eq 21
|
|
97
|
+
expect(described_class.display_width(truncated)).to eq 10
|
|
98
|
+
end
|
|
99
|
+
|
|
63
100
|
it 'cannot truncate if not necessary' do
|
|
64
101
|
text = [ ?A * 10 ] * 5 * ' '
|
|
65
102
|
truncated = described_class.truncate(text, length: 54)
|
|
66
103
|
expect(truncated).to eq text
|
|
67
104
|
end
|
|
68
105
|
|
|
106
|
+
it 'can truncate empty strings' do
|
|
107
|
+
truncated = described_class.truncate('', length: 23)
|
|
108
|
+
expect(truncated).to be_empty
|
|
109
|
+
end
|
|
110
|
+
|
|
69
111
|
it 'can truncate with length 0' do
|
|
70
112
|
truncated = described_class.truncate([ ?A * 10 ] * 10 * ' ', length: 0)
|
|
71
113
|
expect(truncated).to be_empty
|
|
@@ -78,5 +120,51 @@ describe Kramdown::ANSI::Width do
|
|
|
78
120
|
)
|
|
79
121
|
expect(truncated.size).to eq 64
|
|
80
122
|
end
|
|
123
|
+
|
|
124
|
+
it 'keeps pure ANSI strings (no chars)' do
|
|
125
|
+
truncated = described_class.truncate(ansi.red { '' }, length: 10)
|
|
126
|
+
expect(truncated).to eq "\e[31m\e[0m"
|
|
127
|
+
expect(truncated.size).to eq 9
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'handles zero-width characters correctly' do
|
|
131
|
+
src = "A#{("\u200d"*10)}B"
|
|
132
|
+
truncated = described_class.truncate(src, length: 3)
|
|
133
|
+
expect(described_class.display_width(truncated)).to eq(2)
|
|
134
|
+
expect(truncated).to match "A.*B"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it 'preserves newlines in multi-line text' do
|
|
138
|
+
truncated = described_class.truncate("hello\nworld", length: 8)
|
|
139
|
+
expect(truncated).to include("\n")
|
|
140
|
+
expect(described_class.display_width(truncated)).to eq 8
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'supports multiβcell ellipsis' do
|
|
144
|
+
truncated = described_class.truncate('ABC' + ?π * 10, length: 8, ellipsis: 'β¦ (more)')
|
|
145
|
+
expect(truncated).to eq "β¦ (more)"
|
|
146
|
+
expect(described_class.display_width(truncated)).to eq 8
|
|
147
|
+
expect(truncated.length).to eq 8
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'appends reset if truncated after an escape that lacks reset' do
|
|
151
|
+
truncated = described_class.truncate(ansi.red { 'Hello' }, length: 4)
|
|
152
|
+
expect(truncated).to eq "\e[31mHelβ¦\e[0m"
|
|
153
|
+
expect(described_class.remove_ansi_escapes(truncated).size).to eq 4
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'appends single reset for nested styles when truncated' do
|
|
157
|
+
truncated = described_class.truncate(ansi.red { ansi.bold { 'Hi' } }, length: 2)
|
|
158
|
+
expect(truncated).to eq "\e[31m\e[1mHβ¦\e[0m"
|
|
159
|
+
expect(described_class.display_width(truncated)).to eq 2
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'omits escapes that fall after the truncation point' do
|
|
163
|
+
src = "#{ansi.red { 'A' }}B#{ansi.green { 'C' }}D"
|
|
164
|
+
truncated = described_class.truncate(src, length: 2)
|
|
165
|
+
expect(truncated).not_to include "\e[32m"
|
|
166
|
+
expect(truncated).to eq "\e[31mAβ¦\e[0m"
|
|
167
|
+
expect(described_class.display_width(truncated)).to eq 2
|
|
168
|
+
end
|
|
81
169
|
end
|
|
82
170
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Kramdown::ANSI golden files' do
|
|
4
|
+
before do
|
|
5
|
+
allow(Tins::Terminal).to receive(:columns).and_return 80
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
Dir[File.join(__dir__, '../assets/*.md')].each do |md|
|
|
9
|
+
name = File.basename(md, '.md')
|
|
10
|
+
expected_path = md.sub('.md', '.ansi')
|
|
11
|
+
|
|
12
|
+
it "renders #{name} matching golden file" do
|
|
13
|
+
skip "no golden file at #{expected_path}" unless File.exist?(expected_path)
|
|
14
|
+
expected = File.read(expected_path)
|
|
15
|
+
actual = Kramdown::ANSI.parse(File.read(md))
|
|
16
|
+
expect(actual).to eq(expected)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/spec/kramdown/ansi_spec.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'pathname'
|
|
3
|
-
|
|
4
1
|
describe Kramdown::ANSI do
|
|
2
|
+
before do
|
|
3
|
+
allow(Tins::Terminal).to receive(:columns).and_return 80
|
|
4
|
+
end
|
|
5
|
+
|
|
5
6
|
let :source do
|
|
6
7
|
File.read(Pathname.new(__dir__) + '..' + '..' + 'README.md')
|
|
7
8
|
end
|
|
@@ -11,9 +12,9 @@ describe Kramdown::ANSI do
|
|
|
11
12
|
ansi = described_class.parse(source)
|
|
12
13
|
expect(ansi).to include("\e[1m\e[4mDescription\e[0m\e[0m")
|
|
13
14
|
expect(ansi).to include("\e[1mKramdown::ANSI\e[0m")
|
|
14
|
-
expect(ansi).to include("\e[3min the terminal\e[0m")
|
|
15
15
|
expect(ansi).to include("\e[34mgem install kramdown-ansi\n\e[0m")
|
|
16
16
|
expect(ansi).to include("\e[34mbundle install\e[0m")
|
|
17
|
+
expect(ansi).to include("Dialog:\n β\e[2mWhat's that, Fluffy?\n βThat's my new friend.β\e[0mβ")
|
|
17
18
|
expect(ansi).to include("This is the end.")
|
|
18
19
|
output.puts ansi
|
|
19
20
|
end
|
|
@@ -26,14 +27,15 @@ describe Kramdown::ANSI do
|
|
|
26
27
|
strong: %i[ green ],
|
|
27
28
|
em: %i[ yellow ],
|
|
28
29
|
code: %i[ bold color208 ],
|
|
30
|
+
blockquote: %i[ magenta ],
|
|
29
31
|
)
|
|
30
32
|
ansi = described_class.parse(source, ansi_styles:)
|
|
31
33
|
expect(ansi).to include("\e[31m\e[4mDescription\e[0m\e[0")
|
|
32
34
|
expect(ansi).to include("\e[32mKramdown::ANSI\e[0m")
|
|
33
|
-
expect(ansi).to include("\e[33min the terminal\e[0m")
|
|
34
35
|
expect(ansi).to include("\e[1m\e[38;5;208mgem install kramdown-ansi\n\e[0m\e[0m")
|
|
35
|
-
expect(ansi).to include("
|
|
36
|
+
expect(ansi).to include("Dialog:\n β\e[35mWhat's that, Fluffy?\n βThat's my new friend.β\e[0mβ")
|
|
36
37
|
expect(ansi).to include("This is the end.")
|
|
38
|
+
output.puts ansi
|
|
37
39
|
end
|
|
38
40
|
end
|
|
39
41
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
describe 'Kramdown::ANSI table' do
|
|
2
|
+
before do
|
|
3
|
+
allow(Tins::Terminal).to receive(:columns).and_return 80
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
lorem_cell = -> seed do
|
|
7
|
+
LoremIpsum.generate(paragraphs: 1, words_per_sentence: 5, seed:).strip
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
ansi_lorem_cell = -> seed do
|
|
11
|
+
lorem_cell.(seed).gsub(/\S+/) { |word|
|
|
12
|
+
Term::ANSIColor.send(%i[ bold italic color208 color248 ].sample) { word }
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
let(:no_ansi_markdown) do
|
|
17
|
+
<<~EOT
|
|
18
|
+
| Header 1 | Header 2 | Header 3 |
|
|
19
|
+
|----------|----------|----------|
|
|
20
|
+
| #{lorem_cell.(23)} | #{lorem_cell.(42)} | #{lorem_cell.(25)} |
|
|
21
|
+
EOT
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:ansi_markdown) do
|
|
25
|
+
<<~EOT
|
|
26
|
+
| Header 1 | Header 2 | Header 3 |
|
|
27
|
+
|----------|----------|----------|
|
|
28
|
+
| #{ansi_lorem_cell.(23)} | #{ansi_lorem_cell.(42)} | #{ansi_lorem_cell.(25)} |
|
|
29
|
+
EOT
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'renders a table w/o ANSI styling and correct borders' do
|
|
33
|
+
no_ansi = Kramdown::ANSI.parse(no_ansi_markdown)
|
|
34
|
+
# The table should contain the table separator character
|
|
35
|
+
expect(no_ansi).to match(/β/)
|
|
36
|
+
# The generated LoremIpsum text should appear in the output
|
|
37
|
+
expect(no_ansi).to include(lorem_cell.(23)[0, 10])
|
|
38
|
+
# No raw markdown markers should remain
|
|
39
|
+
expect(no_ansi).not_to include('|')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'renders a table with ANSI styling and correct borders' do
|
|
43
|
+
ansi = Kramdown::ANSI.parse(ansi_markdown)
|
|
44
|
+
# The table should contain the table separator character
|
|
45
|
+
expect(ansi).to match(/β/)
|
|
46
|
+
# The generated LoremIpsum text should appear in the output
|
|
47
|
+
expect(ansi).to include(lorem_cell.(23)[0, 10])
|
|
48
|
+
# No raw markdown markers should remain
|
|
49
|
+
expect(ansi).not_to include('|')
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Kramdown::ANSI rendering' do
|
|
4
|
+
before do
|
|
5
|
+
allow(Tins::Terminal).to receive(:columns).and_return 80
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
subject(:render) { Kramdown::ANSI.parse(md) }
|
|
9
|
+
|
|
10
|
+
describe 'headers' do
|
|
11
|
+
context 'level 1' do
|
|
12
|
+
let(:md) { "# Hello\n" }
|
|
13
|
+
it 'applies bold+underline' do
|
|
14
|
+
expect(render).to eq "\e[1m\e[4mHello\e[0m\e[0m\n"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context 'level 2' do
|
|
19
|
+
let(:md) { "## World\n" }
|
|
20
|
+
it 'applies bold+underline' do
|
|
21
|
+
expect(render).to eq "\e[1m\e[4mWorld\e[0m\e[0m\n"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'level 3' do
|
|
26
|
+
let(:md) { "### Deep\n" }
|
|
27
|
+
it 'applies bold+underline' do
|
|
28
|
+
expect(render).to eq "\e[1m\e[4mDeep\e[0m\e[0m\n"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe 'inline emphasis' do
|
|
34
|
+
context 'strong' do
|
|
35
|
+
let(:md) { "This is **bold** text.\n" }
|
|
36
|
+
it 'wraps in bold' do
|
|
37
|
+
expect(render).to eq "This is \e[1mbold\e[0m text.\n"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'emphasis' do
|
|
42
|
+
let(:md) { "This is *italic* text.\n" }
|
|
43
|
+
it 'wraps in italic' do
|
|
44
|
+
expect(render).to eq "This is \e[3mitalic\e[0m text.\n"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context 'strikethrough' do
|
|
49
|
+
let(:md) { "This is ~~gone~~ text.\n" }
|
|
50
|
+
it 'wraps in strikethrough' do
|
|
51
|
+
expect(render).to eq "This is \e[9mgone\e[0m text.\n"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context 'code span' do
|
|
56
|
+
let(:md) { "Use `puts` here.\n" }
|
|
57
|
+
it 'wraps in blue' do
|
|
58
|
+
expect(render).to eq "Use \e[34mputs\e[0m here.\n"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context 'mixed inline' do
|
|
63
|
+
let(:md) { "A **bold** and *em* and `code` word.\n" }
|
|
64
|
+
it 'applies each style independently' do
|
|
65
|
+
expect(render).to eq(
|
|
66
|
+
"A \e[1mbold\e[0m and \e[3mem\e[0m and \e[34mcode\e[0m word.\n"
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe 'code blocks' do
|
|
73
|
+
context 'fenced with language' do
|
|
74
|
+
let(:md) { "```ruby\nputs \"hi\"\n```\n" }
|
|
75
|
+
it 'applies code style to content' do
|
|
76
|
+
expect(render).to eq "\e[34mputs \"hi\"\n\e[0m"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
context 'fenced without language' do
|
|
81
|
+
let(:md) { "```\nplain code\n```\n" }
|
|
82
|
+
it 'applies code style to content' do
|
|
83
|
+
expect(render).to eq "\e[34mplain code\n\e[0m"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe 'blockquotes' do
|
|
89
|
+
context 'single level' do
|
|
90
|
+
let(:md) { "> quoted line\n" }
|
|
91
|
+
it 'wraps in faint + double quotes' do
|
|
92
|
+
expect(render).to eq " β\e[2mquoted line\e[0mβ\n"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
context 'nested' do
|
|
97
|
+
let(:md) { "> > nested\n" }
|
|
98
|
+
it 'cycles to single quotes for inner level' do
|
|
99
|
+
expect(render).to eq " β\e[2mβnestedβ\e[0mβ\n"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe 'horizontal rule' do
|
|
105
|
+
let(:md) { "---\n" }
|
|
106
|
+
it 'renders full-width unicode line' do
|
|
107
|
+
expect(render).to eq((?β * 80) + "\n")
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe 'lists' do
|
|
112
|
+
context 'unordered' do
|
|
113
|
+
let(:md) { "- one\n- two\n" }
|
|
114
|
+
it 'prefixes with bullet' do
|
|
115
|
+
expect(render).to eq "Β· one\n\nΒ· two\n\n"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
context 'ordered' do
|
|
120
|
+
let(:md) { "1. first\n2. second\n" }
|
|
121
|
+
it 'prefixes with sequential numbers' do
|
|
122
|
+
expect(render).to eq "1. first\n\n2. second\n\n"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context 'nested unordered' do
|
|
127
|
+
let(:md) { "- one\n - sub\n- two\n" }
|
|
128
|
+
it 'indents nested items by 2 spaces' do
|
|
129
|
+
expect(render).to eq "Β· one\n\n Β· sub\n \n\nΒ· two\n\n"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
describe 'links' do
|
|
135
|
+
let(:md) { "[click](https://example.com)\n" }
|
|
136
|
+
it 'emits OSC 8 hyperlink' do
|
|
137
|
+
expect(render).to eq "\e]8;;https://example.com\e\\click\e]8;;\e\\\n"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe 'images' do
|
|
142
|
+
let(:md) { "\n" }
|
|
143
|
+
it 'prepends emoji and emits OSC 8 hyperlink' do
|
|
144
|
+
expect(render).to eq "\e]8;;kitten.jpg\e\\πΌ kitten\e]8;;\e\\\n"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
describe 'paragraph' do
|
|
149
|
+
context 'plain text' do
|
|
150
|
+
let(:md) { "Just a paragraph.\n" }
|
|
151
|
+
it 'outputs verbatim with trailing newline' do
|
|
152
|
+
expect(render).to eq "Just a paragraph.\n"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -7,6 +7,102 @@ rescue LoadError
|
|
|
7
7
|
end
|
|
8
8
|
require 'kramdown/ansi'
|
|
9
9
|
|
|
10
|
+
# A lightweight Loremβ―Ipsum generator.
|
|
11
|
+
#
|
|
12
|
+
# The generator uses a small pool of classic Latin words and
|
|
13
|
+
# randomly assembles them into sentences and paragraphs.
|
|
14
|
+
class LoremIpsum
|
|
15
|
+
# Generates Lorem Ipsum text with the specified number of paragraphs and
|
|
16
|
+
# average words per sentence.
|
|
17
|
+
#
|
|
18
|
+
# @param paragraphs [Integer] the number of paragraphs to generate (default: 3)
|
|
19
|
+
# @param words_per_sentence [Integer] the average number of words per
|
|
20
|
+
# sentence (default: 10)
|
|
21
|
+
# @return [String] the generated Lorem Ipsum text
|
|
22
|
+
def self.generate(paragraphs: 3, words_per_sentence: 10, seed: nil)
|
|
23
|
+
new.generate(paragraphs:, words_per_sentence:, seed:)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Default word pool (classic Loremβ―Ipsum source)
|
|
27
|
+
WORDS = %w[
|
|
28
|
+
lorem ipsum dolor sit amet consectetur adipiscing elit sed do
|
|
29
|
+
eiusmod tempor incididunt ut labore et dolore magna aliqua
|
|
30
|
+
enim ad minim veniam quis nostrud exercitation ullamco
|
|
31
|
+
laboris nisi ut aliquip ex ea commodo consequat duis aute
|
|
32
|
+
irure dolor in reprehenderit in voluptate velit esse
|
|
33
|
+
cillum dolore eu fugiat nulla pariatur
|
|
34
|
+
excepteur sint occaecat cupidatat non proident sunt
|
|
35
|
+
culpa qui officia deserunt mollit anim id est laborum
|
|
36
|
+
].freeze
|
|
37
|
+
|
|
38
|
+
# Generate a random word from the pool
|
|
39
|
+
def random_word
|
|
40
|
+
WORDS.sample random: @random
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Generate a sentence of a given length (in words)
|
|
44
|
+
# @param words [Integer] number of words in the sentence
|
|
45
|
+
# @return [String] a sentence ending with a period
|
|
46
|
+
def sentence(words: 8)
|
|
47
|
+
words.times.map { random_word }.join(' ').capitalize + '.'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Generate a paragraph of a given number of sentences
|
|
51
|
+
# @param sentences [Integer] number of sentences in the paragraph
|
|
52
|
+
# @return [String] a paragraph ending with a newline
|
|
53
|
+
def paragraph(sentences: 4)
|
|
54
|
+
sentences.times.map { sentence }.join(' ') + "\n"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Generate a full Loremβ―Ipsum text
|
|
58
|
+
#
|
|
59
|
+
# @param paragraphs [Integer] number of paragraphs
|
|
60
|
+
# @param words_per_sentence [Integer] average words per sentence
|
|
61
|
+
# @return [String] the generated text
|
|
62
|
+
def generate(paragraphs: 3, words_per_sentence: 10, seed: nil)
|
|
63
|
+
@random = seed ? Random.new(seed) : Random.new
|
|
64
|
+
paragraphs.times.map do
|
|
65
|
+
# For a bit of natural variation, pick a random number
|
|
66
|
+
# of sentences around the requested average.
|
|
67
|
+
sentences = @random.rand(words_per_sentence / 2..words_per_sentence * 2)
|
|
68
|
+
paragraph(sentences: sentences)
|
|
69
|
+
end.join("\n")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
10
73
|
def asset(name)
|
|
11
74
|
File.join(__dir__, 'assets', name)
|
|
12
75
|
end
|
|
76
|
+
|
|
77
|
+
# A module that provides functionality for protecting environment variables
|
|
78
|
+
# during tests.
|
|
79
|
+
#
|
|
80
|
+
# This module ensures that environment variable changes made during test
|
|
81
|
+
# execution are automatically restored to their original values after the test
|
|
82
|
+
# completes. It is designed to prevent side effects between tests that modify
|
|
83
|
+
# environment variables, maintaining a clean testing environment.
|
|
84
|
+
module ProtectEnvVars
|
|
85
|
+
# The apply method creates a lambda that protects environment variables
|
|
86
|
+
# during test execution.
|
|
87
|
+
#
|
|
88
|
+
# @return [Proc] a lambda that wraps test execution with environment variable
|
|
89
|
+
# preservation
|
|
90
|
+
def self.apply
|
|
91
|
+
-> example do
|
|
92
|
+
if example.metadata[:protect_env]
|
|
93
|
+
begin
|
|
94
|
+
stored_env = ENV.to_h
|
|
95
|
+
example.run
|
|
96
|
+
ensure
|
|
97
|
+
ENV.replace(stored_env)
|
|
98
|
+
end
|
|
99
|
+
else
|
|
100
|
+
example.run
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
RSpec.configure do |config|
|
|
107
|
+
config.around(&ProtectEnvVars.apply)
|
|
108
|
+
end
|
data/tmp/.gitkeep
ADDED
|
File without changes
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kramdown-ansi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Florian Frank
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 2.
|
|
18
|
+
version: 2.20.1
|
|
19
19
|
type: :development
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 2.
|
|
25
|
+
version: 2.20.1
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: all_images
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -113,14 +113,28 @@ dependencies:
|
|
|
113
113
|
requirements:
|
|
114
114
|
- - "~>"
|
|
115
115
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '
|
|
116
|
+
version: '4.0'
|
|
117
117
|
type: :runtime
|
|
118
118
|
prerelease: false
|
|
119
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
120
|
requirements:
|
|
121
121
|
- - "~>"
|
|
122
122
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '
|
|
123
|
+
version: '4.0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: tins
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '1.61'
|
|
131
|
+
type: :runtime
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '1.61'
|
|
124
138
|
- !ruby/object:Gem::Dependency
|
|
125
139
|
name: json
|
|
126
140
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -135,6 +149,20 @@ dependencies:
|
|
|
135
149
|
- - "~>"
|
|
136
150
|
- !ruby/object:Gem::Version
|
|
137
151
|
version: '2.0'
|
|
152
|
+
- !ruby/object:Gem::Dependency
|
|
153
|
+
name: unicode-display_width
|
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
|
155
|
+
requirements:
|
|
156
|
+
- - "~>"
|
|
157
|
+
- !ruby/object:Gem::Version
|
|
158
|
+
version: '3.0'
|
|
159
|
+
type: :runtime
|
|
160
|
+
prerelease: false
|
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
162
|
+
requirements:
|
|
163
|
+
- - "~>"
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
165
|
+
version: '3.0'
|
|
138
166
|
description: |
|
|
139
167
|
Kramdown::ANSI: A library for rendering Markdown(ish) documents with
|
|
140
168
|
beautiful ANSI escape sequences in the terminal.
|
|
@@ -151,6 +179,7 @@ extra_rdoc_files:
|
|
|
151
179
|
- lib/kramdown/ansi/width.rb
|
|
152
180
|
- lib/kramdown/version.rb
|
|
153
181
|
files:
|
|
182
|
+
- ".utilsrc"
|
|
154
183
|
- CHANGES.md
|
|
155
184
|
- Gemfile
|
|
156
185
|
- LICENSE
|
|
@@ -167,12 +196,20 @@ files:
|
|
|
167
196
|
- lib/kramdown/ansi/width.rb
|
|
168
197
|
- lib/kramdown/version.rb
|
|
169
198
|
- spec/assets/README.ansi
|
|
199
|
+
- spec/assets/html.ansi
|
|
200
|
+
- spec/assets/html.md
|
|
170
201
|
- spec/assets/kitten.jpg
|
|
202
|
+
- spec/assets/tables.ansi
|
|
203
|
+
- spec/assets/tables.md
|
|
171
204
|
- spec/kramdown/ansi/pager_spec.rb
|
|
172
205
|
- spec/kramdown/ansi/styles_spec.rb
|
|
173
206
|
- spec/kramdown/ansi/width_spec.rb
|
|
207
|
+
- spec/kramdown/ansi_golden_spec.rb
|
|
174
208
|
- spec/kramdown/ansi_spec.rb
|
|
209
|
+
- spec/kramdown/ansi_table_spec.rb
|
|
210
|
+
- spec/kramdown/render_spec.rb
|
|
175
211
|
- spec/spec_helper.rb
|
|
212
|
+
- tmp/.gitkeep
|
|
176
213
|
homepage: https://github.com/flori/kramdown-ansi
|
|
177
214
|
licenses:
|
|
178
215
|
- MIT
|
|
@@ -195,12 +232,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
195
232
|
- !ruby/object:Gem::Version
|
|
196
233
|
version: '0'
|
|
197
234
|
requirements: []
|
|
198
|
-
rubygems_version: 4.0.
|
|
235
|
+
rubygems_version: 4.0.22
|
|
199
236
|
specification_version: 4
|
|
200
237
|
summary: Output markdown in the terminal with ANSI escape sequences
|
|
201
238
|
test_files:
|
|
202
239
|
- spec/kramdown/ansi/pager_spec.rb
|
|
203
240
|
- spec/kramdown/ansi/styles_spec.rb
|
|
204
241
|
- spec/kramdown/ansi/width_spec.rb
|
|
242
|
+
- spec/kramdown/ansi_golden_spec.rb
|
|
205
243
|
- spec/kramdown/ansi_spec.rb
|
|
244
|
+
- spec/kramdown/ansi_table_spec.rb
|
|
245
|
+
- spec/kramdown/render_spec.rb
|
|
206
246
|
- spec/spec_helper.rb
|