codeless_code 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitmodules +1 -1
- data/.reek.yml +14 -0
- data/.rubocop.yml +18 -0
- data/.ruby-version +1 -0
- data/{test/test_codeless_code.rb → .travis.yml} +12 -10
- data/Gemfile +5 -0
- data/Gemfile.lock +21 -0
- data/Guardfile +13 -1
- data/README.md +6 -0
- data/Rakefile +26 -11
- data/VERSION +1 -1
- data/bin/codeless_code +3 -1
- data/codeless_code.gemspec +33 -12
- data/lib/codeless_code/catalog.rb +3 -1
- data/lib/codeless_code/cli.rb +56 -24
- data/lib/codeless_code/commands/filter_fables.rb +15 -10
- data/lib/codeless_code/commands/list_translations.rb +3 -0
- data/lib/codeless_code/fable.rb +42 -53
- data/lib/codeless_code/fable_set.rb +8 -6
- data/lib/codeless_code/filters/builders.rb +4 -2
- data/lib/codeless_code/filters/composite.rb +3 -1
- data/lib/codeless_code/filters/date.rb +40 -30
- data/lib/codeless_code/filters/from_options.rb +45 -28
- data/lib/codeless_code/filters/headers/base.rb +61 -0
- data/lib/codeless_code/filters/{header_integer.rb → headers/integer.rb} +16 -19
- data/lib/codeless_code/filters/{header_string.rb → headers/string.rb} +12 -22
- data/lib/codeless_code/filters/lang.rb +3 -0
- data/lib/codeless_code/filters/translator.rb +7 -2
- data/lib/codeless_code/filters.rb +16 -8
- data/lib/codeless_code/formats/base.rb +20 -7
- data/lib/codeless_code/formats/parsers/base.rb +106 -0
- data/lib/codeless_code/formats/parsers/plain.rb +49 -0
- data/lib/codeless_code/formats/parsers/term.rb +79 -0
- data/lib/codeless_code/formats/plain.rb +21 -12
- data/lib/codeless_code/formats/raw.rb +2 -0
- data/lib/codeless_code/formats/term.rb +21 -15
- data/lib/codeless_code/language_set.rb +7 -4
- data/lib/codeless_code/options.rb +5 -3
- data/lib/codeless_code/renderers/fable.rb +13 -8
- data/lib/codeless_code/renderers/term_page.rb +46 -31
- data/lib/codeless_code.rb +119 -106
- data/test/codeless_code/commands/test_filter_fables.rb +89 -0
- data/test/codeless_code/filters/headers/test_integer.rb +86 -0
- data/test/codeless_code/filters/headers/test_string.rb +86 -0
- data/test/codeless_code/filters/test_builders.rb +4 -2
- data/test/codeless_code/filters/test_composite.rb +70 -0
- data/test/codeless_code/filters/test_date.rb +99 -0
- data/test/codeless_code/filters/test_langs.rb +50 -0
- data/test/codeless_code/filters/test_translator.rb +51 -0
- data/test/codeless_code/renderers/test_fable.rb +98 -0
- data/test/codeless_code/renderers/test_term_page.rb +87 -0
- data/test/codeless_code/test_catalog.rb +12 -5
- data/test/codeless_code/test_cli.rb +85 -0
- data/test/codeless_code/test_fable.rb +19 -10
- data/test/codeless_code/test_fable_set.rb +17 -5
- data/test/codeless_code/test_language_set.rb +16 -3
- data/test/codeless_code/test_options.rb +3 -11
- data/test/helper.rb +36 -10
- data/test/support/fs.rb +103 -0
- metadata +65 -11
- data/.document +0 -5
- data/data/README.md +0 -34
- data/lib/codeless_code/formats/plain_generator.rb +0 -157
- data/lib/codeless_code/formats/term_generator.rb +0 -167
- data/test/codeless_code/filters/test_header_integer.rb +0 -82
- data/test/codeless_code/filters/test_header_string.rb +0 -82
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# codeless_code filters and prints fables from http://thecodelesscode.com
|
2
4
|
# Copyright (C) 2018 Jon Sangster
|
3
5
|
#
|
@@ -14,15 +16,21 @@
|
|
14
16
|
# You should have received a copy of the GNU General Public License along with
|
15
17
|
# this program. If not, see <https://www.gnu.org/licenses/>.
|
16
18
|
module CodelessCode
|
19
|
+
# Functors which test whether a given {Fable} matches some criteria.
|
17
20
|
module Filters
|
18
|
-
autoload :Builders,
|
19
|
-
autoload :Composite,
|
20
|
-
autoload :Date,
|
21
|
-
autoload :FromOptions,
|
22
|
-
autoload :
|
23
|
-
autoload :
|
24
|
-
|
25
|
-
|
21
|
+
autoload :Builders, 'codeless_code/filters/builders'
|
22
|
+
autoload :Composite, 'codeless_code/filters/composite'
|
23
|
+
autoload :Date, 'codeless_code/filters/date'
|
24
|
+
autoload :FromOptions, 'codeless_code/filters/from_options'
|
25
|
+
autoload :Lang, 'codeless_code/filters/lang'
|
26
|
+
autoload :Translator, 'codeless_code/filters/translator'
|
27
|
+
|
28
|
+
# Filters which inspect a {Fable fable's} headers
|
29
|
+
module Headers
|
30
|
+
autoload :Base, 'codeless_code/filters/headers/base'
|
31
|
+
autoload :Integer, 'codeless_code/filters/headers/integer'
|
32
|
+
autoload :String, 'codeless_code/filters/headers/string'
|
33
|
+
end
|
26
34
|
|
27
35
|
extend Builders
|
28
36
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# codeless_code filters and prints fables from http://thecodelesscode.com
|
2
4
|
# Copyright (C) 2018 Jon Sangster
|
3
5
|
#
|
@@ -18,8 +20,9 @@ require 'nokogiri'
|
|
18
20
|
|
19
21
|
module CodelessCode
|
20
22
|
module Formats
|
23
|
+
# Abstract base class for all formats.
|
21
24
|
class Base
|
22
|
-
|
25
|
+
attr_reader :raw
|
23
26
|
|
24
27
|
def initialize(raw)
|
25
28
|
@raw = raw
|
@@ -27,14 +30,24 @@ module CodelessCode
|
|
27
30
|
|
28
31
|
protected
|
29
32
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
+
def from_wiki(doc, parser_name)
|
34
|
+
parser = Parsers.const_get(parser_name).new(self)
|
35
|
+
MediaCloth.wiki_to_html(doc.to_s, generator: parser)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# MediaCloth expects XHTML-ish pages and chokes on ommited end tags
|
40
|
+
class XhtmlDoc
|
41
|
+
def self.parse(html)
|
42
|
+
new(Nokogiri::HTML(html))
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(doc)
|
46
|
+
@doc = doc
|
33
47
|
end
|
34
48
|
|
35
|
-
def
|
36
|
-
|
37
|
-
MediaCloth::wiki_to_html(str, generator: generator)
|
49
|
+
def to_s
|
50
|
+
@doc.css('body > *').to_xhtml
|
38
51
|
end
|
39
52
|
end
|
40
53
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# codeless_code filters and prints fables from http://thecodelesscode.com
|
4
|
+
# Copyright (C) 2018 Jon Sangster
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify it under
|
7
|
+
# the terms of the GNU General Public License as published by the Free Software
|
8
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
9
|
+
# version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
13
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
14
|
+
# details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along with
|
17
|
+
# this program. If not, see <https://www.gnu.org/licenses/>.
|
18
|
+
require 'mediacloth'
|
19
|
+
|
20
|
+
module CodelessCode
|
21
|
+
module Formats
|
22
|
+
module Parsers
|
23
|
+
# An abstract base class for the custom parsers for the syntax tree
|
24
|
+
# generated by the +MediaCloth+ gem.
|
25
|
+
class Base < MediaWikiWalker
|
26
|
+
# Reimplement these
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# def parse_paragraph(ast)
|
30
|
+
# parse_wiki_ast(ast)
|
31
|
+
# end
|
32
|
+
ABSTRACT_METHODS = %i[
|
33
|
+
parse_paragraph
|
34
|
+
parse_paste
|
35
|
+
parse_formatted
|
36
|
+
parse_list_item
|
37
|
+
parse_list_term
|
38
|
+
parse_list_definition
|
39
|
+
parse_preformatted
|
40
|
+
parse_section
|
41
|
+
parse_link
|
42
|
+
parse_internal_link
|
43
|
+
parse_internal_link_item
|
44
|
+
parse_table
|
45
|
+
parse_table_row
|
46
|
+
parse_table_cell
|
47
|
+
parse_element
|
48
|
+
parse_template
|
49
|
+
parse_category
|
50
|
+
parse_keyword
|
51
|
+
].freeze
|
52
|
+
|
53
|
+
attr_reader :ctx
|
54
|
+
|
55
|
+
def initialize(ctx)
|
56
|
+
@ctx = ctx
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def parse_wiki_ast(ast)
|
62
|
+
super(ast).join
|
63
|
+
end
|
64
|
+
|
65
|
+
ABSTRACT_METHODS.each do |meth|
|
66
|
+
define_method(meth) { |ast| parse_wiki_ast(ast) }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Reimplement this
|
70
|
+
# :reek:UtilityFunction
|
71
|
+
def parse_text(ast)
|
72
|
+
ast.contents
|
73
|
+
end
|
74
|
+
|
75
|
+
# Reimplement this
|
76
|
+
def parse_list(ast)
|
77
|
+
ast.children.map(&method(:parse_list_child))
|
78
|
+
end
|
79
|
+
|
80
|
+
# Reimplement this
|
81
|
+
def parse_resource_link(ast)
|
82
|
+
ast.children.map do |child|
|
83
|
+
parse_internal_link_item(child) if child.is_a?(InternalLinkItemAST)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Reimplement this
|
88
|
+
def parse_category_link(ast)
|
89
|
+
ast.children.map do |child|
|
90
|
+
parse_internal_link_item(child) if child.is_a?(InternalLinkItemAST)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def parse_list_child(child)
|
97
|
+
case child
|
98
|
+
when ListItemAST then parse_list_item(child)
|
99
|
+
when ListTermAST then parse_list_term(child)
|
100
|
+
when ListDefinitionAST then parse_list_definition(child)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# codeless_code filters and prints fables from http://thecodelesscode.com
|
4
|
+
# Copyright (C) 2018 Jon Sangster
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify it under
|
7
|
+
# the terms of the GNU General Public License as published by the Free Software
|
8
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
9
|
+
# version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
13
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
14
|
+
# details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along with
|
17
|
+
# this program. If not, see <https://www.gnu.org/licenses/>.
|
18
|
+
require 'mediacloth'
|
19
|
+
|
20
|
+
module CodelessCode
|
21
|
+
module Formats
|
22
|
+
module Parsers
|
23
|
+
# A custom parser for the syntax tree generated by the +MediaCloth+ gem
|
24
|
+
# which removes all formatting such that the rendered document should
|
25
|
+
# appear as plain text.
|
26
|
+
class Plain < Base
|
27
|
+
protected
|
28
|
+
|
29
|
+
def parse_section(ast)
|
30
|
+
parse_wiki_ast(ast).strip
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_internal_link(ast)
|
34
|
+
text = parse_wiki_ast(ast)
|
35
|
+
!text.empty? ? text : ast.locator
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_element(ast)
|
39
|
+
str = parse_wiki_ast(ast)
|
40
|
+
if ast.name == 'pre'
|
41
|
+
ctx.generate(str.gsub(/\A\n*(.*?)\n*\z/m, '\1'))
|
42
|
+
else
|
43
|
+
str
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# codeless_code filters and prints fables from http://thecodelesscode.com
|
4
|
+
# Copyright (C) 2018 Jon Sangster
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify it under
|
7
|
+
# the terms of the GNU General Public License as published by the Free Software
|
8
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
9
|
+
# version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
13
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
14
|
+
# details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along with
|
17
|
+
# this program. If not, see <https://www.gnu.org/licenses/>.
|
18
|
+
require 'mediacloth'
|
19
|
+
|
20
|
+
module CodelessCode
|
21
|
+
module Formats
|
22
|
+
module Parsers
|
23
|
+
# A custom parser for the syntax tree generated by the +MediaCloth+ gem
|
24
|
+
# which formats a document using ANSI codes for color, bold, italics, and
|
25
|
+
# other styles.
|
26
|
+
class Term < Base
|
27
|
+
protected
|
28
|
+
|
29
|
+
def parse_formatted(ast)
|
30
|
+
if ast.formatting == :Bold
|
31
|
+
color(parse_wiki_ast(ast)).bold
|
32
|
+
else
|
33
|
+
color(parse_wiki_ast(ast)).italic
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_preformatted(ast)
|
38
|
+
color(parse_wiki_ast(ast)).green
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_section(ast)
|
42
|
+
color(parse_wiki_ast(ast).strip).blue
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_link(ast)
|
46
|
+
color(parse_wiki_ast(ast)).underline
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_internal_link(ast)
|
50
|
+
text = parse_wiki_ast(ast)
|
51
|
+
color(!text.empty? ? text : ast.locator).underline
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_element(ast)
|
55
|
+
str = parse_wiki_ast(ast)
|
56
|
+
if ast.name == 'pre'
|
57
|
+
color(ctx.generate(str.gsub(/\A\n*(.*?)\n*\z/m, '\1'))).red
|
58
|
+
else
|
59
|
+
str
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_template(ast)
|
64
|
+
color(ast.template_name).yellow
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_keyword(ast)
|
68
|
+
color(parse_wiki_ast(ast)).underline
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def color(str)
|
74
|
+
ctx.color(str)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# codeless_code filters and prints fables from http://thecodelesscode.com
|
2
4
|
# Copyright (C) 2018 Jon Sangster
|
3
5
|
#
|
@@ -21,24 +23,31 @@ module CodelessCode
|
|
21
23
|
class Plain < Base
|
22
24
|
def call
|
23
25
|
raw.split("\n\n")
|
24
|
-
.map { |str| from_wiki(
|
26
|
+
.map { |str| from_wiki(CleanupBody.new(str)) }
|
25
27
|
.join("\n\n")
|
26
28
|
end
|
27
29
|
|
28
|
-
|
30
|
+
protected
|
29
31
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
def from_wiki(str)
|
33
|
+
super(XhtmlDoc.parse(str.to_s), :Plain)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Tidies up the mixed syntax found in fables
|
38
|
+
class CleanupBody
|
39
|
+
def initialize(body)
|
40
|
+
@body = body
|
38
41
|
end
|
39
42
|
|
40
|
-
def
|
41
|
-
|
43
|
+
def to_s
|
44
|
+
[
|
45
|
+
[%r{//\w*$}, ''],
|
46
|
+
[%r{<i>([^<]+)</i>}mi, '\1'],
|
47
|
+
[%r{<b>([^<]+)</b>}mi, '\1'],
|
48
|
+
[%r{<a[^>]+>([^<]+)</a>}mi, '\1'],
|
49
|
+
[%r{/(\w+)/}, '\1']
|
50
|
+
].inject(@body) { |str, args| str.gsub(*args) }
|
42
51
|
end
|
43
52
|
end
|
44
53
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# codeless_code filters and prints fables from http://thecodelesscode.com
|
2
4
|
# Copyright (C) 2018 Jon Sangster
|
3
5
|
#
|
@@ -15,7 +17,6 @@
|
|
15
17
|
# this program. If not, see <https://www.gnu.org/licenses/>.
|
16
18
|
require 'colorized_string'
|
17
19
|
require 'mediacloth'
|
18
|
-
require 'nokogiri'
|
19
20
|
|
20
21
|
module CodelessCode
|
21
22
|
module Formats
|
@@ -23,26 +24,31 @@ module CodelessCode
|
|
23
24
|
# colors, etc.
|
24
25
|
class Term < Base
|
25
26
|
def call
|
26
|
-
from_wiki
|
27
|
+
from_wiki
|
27
28
|
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
[/^\| .*/, c('\\0').green],
|
33
|
-
[/<i>([^<]+)<\/i>/mi, "''\\1''"],
|
34
|
-
[/<b>([^<]+)<\/b>/mi, "'''\\1'''"],
|
35
|
-
[/<a[^>]+>([^<]+)<\/a>/mi, '[[\1]]'],
|
36
|
-
[/\/(\w+)\//, "''\\1''"],
|
37
|
-
].inject(str) { |str, args| str = str.gsub(*args) }
|
30
|
+
# :reek:UtilityFunction
|
31
|
+
def color(str)
|
32
|
+
ColorizedString.new(str)
|
38
33
|
end
|
39
34
|
|
40
|
-
|
41
|
-
|
35
|
+
protected
|
36
|
+
|
37
|
+
def from_wiki
|
38
|
+
super(XhtmlDoc.parse(regex_raw), :Term)
|
42
39
|
end
|
43
40
|
|
44
|
-
|
45
|
-
|
41
|
+
private
|
42
|
+
|
43
|
+
def regex_raw
|
44
|
+
[
|
45
|
+
[%r{//\w*$}, ''],
|
46
|
+
[/^\| .*/, ColorizedString.new('\\0').green],
|
47
|
+
[%r{<i>([^<]+)</i>}mi, "''\\1''"],
|
48
|
+
[%r{<b>([^<]+)</b>}mi, "'''\\1'''"],
|
49
|
+
[%r{<a[^>]+>([^<]+)</a>}mi, '[[\1]]'],
|
50
|
+
[%r{/(\w+)/}, "''\\1''"]
|
51
|
+
].inject(raw) { |str, args| str.gsub(*args) }
|
46
52
|
end
|
47
53
|
end
|
48
54
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# codeless_code filters and prints fables from http://thecodelesscode.com
|
2
4
|
# Copyright (C) 2018 Jon Sangster
|
3
5
|
#
|
@@ -22,16 +24,17 @@ module CodelessCode
|
|
22
24
|
extend Forwardable
|
23
25
|
include Enumerable
|
24
26
|
|
27
|
+
# If the requested language set doesn't exist
|
25
28
|
NotFoundError = Class.new(StandardError)
|
26
29
|
|
27
|
-
LANG_PATTERN = '%s-*'
|
30
|
+
LANG_PATTERN = '%s-*'
|
28
31
|
|
29
|
-
|
32
|
+
attr_reader :lang, :root_dir
|
30
33
|
def_delegator :fable_sets, :each
|
31
34
|
|
32
35
|
def initialize(lang, root_dir:)
|
33
|
-
|
34
|
-
|
36
|
+
@lang = lang
|
37
|
+
@root_dir = root_dir
|
35
38
|
end
|
36
39
|
|
37
40
|
def fable_sets
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# codeless_code filters and prints fables from http://thecodelesscode.com
|
2
4
|
# Copyright (C) 2018 Jon Sangster
|
3
5
|
#
|
@@ -62,9 +64,9 @@ module CodelessCode
|
|
62
64
|
args = [@command_name] + @argv
|
63
65
|
opts = CodelessCode::OPTIONS.curry[@command_name]
|
64
66
|
|
65
|
-
Slop.parse(args, suppress_errors: suppress_errors, &opts).tap do |
|
66
|
-
if !suppress_errors &&
|
67
|
-
raise format('too many arguments: %p',
|
67
|
+
Slop.parse(args, suppress_errors: suppress_errors, &opts).tap do |opt|
|
68
|
+
if !suppress_errors && opt.arguments.size > MAX_ARGS + 1
|
69
|
+
raise format('too many arguments: %p', opt.arguments[1..-1])
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# codeless_code filters and prints fables from http://thecodelesscode.com
|
2
4
|
# Copyright (C) 2018 Jon Sangster
|
3
5
|
#
|
@@ -24,7 +26,7 @@ module CodelessCode
|
|
24
26
|
page.title = best_title
|
25
27
|
page.body = render_with(format, fallback: fallback)
|
26
28
|
|
27
|
-
headers_no_best_title.map { |
|
29
|
+
headers_no_best_title.map { |key, val| page.add_header(key, val) }
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
@@ -42,14 +44,17 @@ module CodelessCode
|
|
42
44
|
private
|
43
45
|
|
44
46
|
def render_slice(keys)
|
45
|
-
headers.slice(*keys)
|
47
|
+
headers.slice(*keys)
|
48
|
+
.map { |key, val| format('%s: %p', key, val) }
|
49
|
+
.join(', ')
|
46
50
|
end
|
47
51
|
|
48
52
|
def render_with(format, fallback:)
|
49
53
|
format.new(body).call
|
50
|
-
rescue =>
|
51
|
-
raise
|
52
|
-
|
54
|
+
rescue StandardError => err
|
55
|
+
raise err unless fallback
|
56
|
+
|
57
|
+
warn format('Error parsing %s: %s', file, err.message.strip)
|
53
58
|
fallback.new(body).call
|
54
59
|
end
|
55
60
|
|
@@ -58,13 +63,13 @@ module CodelessCode
|
|
58
63
|
end
|
59
64
|
|
60
65
|
def headers_no_best_title
|
61
|
-
sorted_headers.dup.delete_if { |_,
|
66
|
+
sorted_headers.dup.delete_if { |_, val| val&.strip == title&.strip }
|
62
67
|
end
|
63
68
|
|
64
69
|
def sorted_headers
|
65
70
|
Hash[
|
66
|
-
headers.each_with_index.sort_by do |(
|
67
|
-
HEADER_SORT.index(
|
71
|
+
headers.each_with_index.sort_by do |(key, _), index|
|
72
|
+
HEADER_SORT.index(key) || HEADER_SORT.size + index
|
68
73
|
end.map(&:first)
|
69
74
|
]
|
70
75
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# codeless_code filters and prints fables from http://thecodelesscode.com
|
2
4
|
# Copyright (C) 2018 Jon Sangster
|
3
5
|
#
|
@@ -24,16 +26,21 @@ module CodelessCode
|
|
24
26
|
'%<title>s', '%<sep1>s', '%<headers>s', '%<sep2>s', '', '%<body>s'
|
25
27
|
].join("\n").freeze
|
26
28
|
|
29
|
+
# :reek:Attribute
|
27
30
|
attr_accessor :title, :body
|
31
|
+
attr_reader :headers
|
32
|
+
|
33
|
+
# :reek:ControlParameter
|
34
|
+
def initialize(max_width: nil, width_func: nil)
|
35
|
+
@max_width = max_width
|
36
|
+
@max_width ||= (width_func || TermWidth.new).call
|
28
37
|
|
29
|
-
def initialize(max_width: nil)
|
30
|
-
@max_width = max_width || term_width
|
31
38
|
@headers = {}
|
32
39
|
@key_width = 0
|
33
40
|
end
|
34
41
|
|
35
42
|
def to_s
|
36
|
-
format(PAGER_FORMAT, title: title.center(width),
|
43
|
+
format(PAGER_FORMAT, title: title.center(width).rstrip,
|
37
44
|
sep1: seperator('='), sep2: seperator('-'),
|
38
45
|
headers: header_section, body: body)
|
39
46
|
end
|
@@ -45,38 +52,31 @@ module CodelessCode
|
|
45
52
|
|
46
53
|
private
|
47
54
|
|
48
|
-
def
|
49
|
-
|
50
|
-
w
|
51
|
-
end
|
52
|
-
rescue Errno::ENOENT
|
53
|
-
nil
|
54
|
-
end
|
55
|
-
|
56
|
-
def seperator(ch = '=')
|
57
|
-
(@seperator ||= {})[ch] ||= ch * width
|
55
|
+
def seperator(char = '=')
|
56
|
+
(@seperator ||= {})[char] ||= char * width
|
58
57
|
end
|
59
58
|
|
60
59
|
def header_section
|
61
|
-
lines =
|
62
|
-
max_line_width = lines.join("\n").lines.map
|
60
|
+
lines = format_header_section
|
61
|
+
max_line_width = lines.join("\n").lines.map(&:size).max || 0
|
63
62
|
padding = [0, (width - max_line_width)].max / 2
|
64
63
|
|
65
|
-
lines.map { |
|
64
|
+
lines.map { |line| [' ' * padding, line].join }.join("\n")
|
66
65
|
end
|
67
66
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
67
|
+
def format_header_section
|
68
|
+
headers.map { |key, value| format_header(key, wrap_header(value)) }
|
69
|
+
end
|
70
|
+
|
71
|
+
def format_header(key, value)
|
72
|
+
format("% #{@key_width}s: %s", key, value)
|
73
|
+
end
|
74
|
+
|
75
|
+
def wrap_header(head)
|
76
|
+
head.chars
|
77
|
+
.each_slice(width - @key_width - 2)
|
78
|
+
.map { |str| str.join.strip }
|
79
|
+
.inject { |str, part| str + "\n" + ' ' * (@key_width + 2) + part }
|
80
80
|
end
|
81
81
|
|
82
82
|
def width
|
@@ -85,12 +85,27 @@ module CodelessCode
|
|
85
85
|
|
86
86
|
def content_width
|
87
87
|
[
|
88
|
-
body.lines.map do |
|
89
|
-
ColorizedString[
|
88
|
+
body.lines.map do |line|
|
89
|
+
ColorizedString[line].uncolorize.strip.size
|
90
90
|
end.max || 0,
|
91
|
-
|
91
|
+
headers.map(&method(:format_header)).map(&:size).max || 0
|
92
92
|
].max
|
93
93
|
end
|
94
94
|
end
|
95
|
+
|
96
|
+
# Ask an external application how wide our terminal is
|
97
|
+
class TermWidth
|
98
|
+
def initialize(cmd = 'tputs cols')
|
99
|
+
@cmd = cmd
|
100
|
+
end
|
101
|
+
|
102
|
+
def call
|
103
|
+
if (tput_width = `#{@cmd}`.strip.to_i).positive?
|
104
|
+
tput_width
|
105
|
+
end
|
106
|
+
rescue Errno::ENOENT
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
end
|
95
110
|
end
|
96
111
|
end
|