boondoc 0.1.0 → 0.2.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/CHANGELOG.md +7 -0
- data/README.md +3 -1
- data/exe/boondoc +1 -1
- data/lib/boondoc/assets.rb +10 -10
- data/lib/boondoc/cli.rb +34 -32
- data/lib/boondoc/config.rb +22 -20
- data/lib/boondoc/containment.rb +3 -3
- data/lib/boondoc/context.rb +9 -2
- data/lib/boondoc/core_constants.rb +154 -14
- data/lib/boondoc/docstring_parser.rb +17 -11
- data/lib/boondoc/frontend/comments.rb +3 -3
- data/lib/boondoc/frontend/prism_parser.rb +1 -1
- data/lib/boondoc/frontend/visitor.rb +11 -11
- data/lib/boondoc/generator.rb +15 -9
- data/lib/boondoc/linker.rb +8 -8
- data/lib/boondoc/markdown.rb +6 -6
- data/lib/boondoc/model/code_object.rb +7 -7
- data/lib/boondoc/model/docstring.rb +16 -7
- data/lib/boondoc/model/registry.rb +5 -5
- data/lib/boondoc/path_map.rb +8 -8
- data/lib/boondoc/preview.rb +11 -11
- data/lib/boondoc/rake_task.rb +5 -5
- data/lib/boondoc/references.rb +1 -1
- data/lib/boondoc/renderer/base.rb +3 -3
- data/lib/boondoc/renderer/docstring.rb +33 -27
- data/lib/boondoc/renderer/index.rb +3 -3
- data/lib/boondoc/renderer/method_section.rb +6 -6
- data/lib/boondoc/renderer/namespace_page.rb +30 -20
- data/lib/boondoc/stitcher.rb +4 -4
- data/lib/boondoc/type_parser.rb +6 -6
- data/lib/boondoc/version.rb +1 -1
- data/lib/boondoc/writer.rb +12 -9
- data/lib/boondoc/yard_bridge.rb +13 -8
- data/lib/boondoc/yardopts.rb +51 -24
- data/lib/boondoc.rb +34 -34
- metadata +12 -6
|
@@ -4,11 +4,11 @@ module Boondoc
|
|
|
4
4
|
module Renderer
|
|
5
5
|
# Renders a docstring: callouts, body, then one section per kind of tag.
|
|
6
6
|
class Docstring < Base
|
|
7
|
-
CALLOUTS = {
|
|
8
|
-
|
|
9
|
-
TYPED = {
|
|
10
|
-
|
|
11
|
-
META = {
|
|
7
|
+
CALLOUTS = { "deprecated" => "Deprecated", "abstract" => "Abstract", "note" => "Note",
|
|
8
|
+
"todo" => "Todo" }.freeze
|
|
9
|
+
TYPED = { "yieldparam" => "Yield parameters", "yieldreturn" => "Yield returns",
|
|
10
|
+
"return" => "Returns", "raise" => "Raises" }.freeze
|
|
11
|
+
META = { "since" => "Since", "version" => "Version", "author" => "Author", "api" => "API" }.freeze
|
|
12
12
|
HANDLED = (CALLOUTS.keys + TYPED.keys + META.keys +
|
|
13
13
|
%w[param option yield example see overload private attr attr_reader attr_writer]).freeze
|
|
14
14
|
|
|
@@ -25,9 +25,15 @@ module Boondoc
|
|
|
25
25
|
# @return [Array<String>] markdown blocks, in order
|
|
26
26
|
def render(docstring)
|
|
27
27
|
[
|
|
28
|
-
callouts(docstring),
|
|
28
|
+
callouts(docstring),
|
|
29
|
+
text(docstring.text),
|
|
30
|
+
parameters(docstring),
|
|
31
|
+
yields(docstring),
|
|
29
32
|
TYPED.map { |name, title| section(title, docstring.tags_named(name).map { |tag| typed(tag) }) },
|
|
30
|
-
examples(docstring),
|
|
33
|
+
examples(docstring),
|
|
34
|
+
see_also(docstring),
|
|
35
|
+
meta(docstring),
|
|
36
|
+
overloads(docstring)
|
|
31
37
|
].flatten.compact.reject(&:empty?)
|
|
32
38
|
end
|
|
33
39
|
|
|
@@ -44,7 +50,7 @@ module Boondoc
|
|
|
44
50
|
def callouts(docstring)
|
|
45
51
|
docstring.tags.select { |tag| CALLOUTS.key?(tag.tag_name) }.map do |tag|
|
|
46
52
|
body = text(tag.text)
|
|
47
|
-
label = "**#{CALLOUTS[tag.tag_name]}#{
|
|
53
|
+
label = "**#{CALLOUTS[tag.tag_name]}#{":" unless body.empty?}** #{body}".strip
|
|
48
54
|
label.lines.map { |line| "> #{line}".rstrip }.join("\n")
|
|
49
55
|
end
|
|
50
56
|
end
|
|
@@ -53,53 +59,53 @@ module Boondoc
|
|
|
53
59
|
"**#{title}:**\n\n#{items.join("\n")}" unless items.empty?
|
|
54
60
|
end
|
|
55
61
|
|
|
56
|
-
def item(lead, description, indent:
|
|
57
|
-
line = [lead, description].compact.reject(&:empty?).join(
|
|
62
|
+
def item(lead, description, indent: "")
|
|
63
|
+
line = [lead, description].compact.reject(&:empty?).join(" — ")
|
|
58
64
|
"#{indent}- #{line.gsub("\n", "\n#{indent} ")}"
|
|
59
65
|
end
|
|
60
66
|
|
|
61
67
|
def typed(tag)
|
|
62
68
|
types = types(tag)
|
|
63
|
-
item([tag.name && code(tag.name), types && "(#{types})"].compact.join(
|
|
69
|
+
item([tag.name && code(tag.name), types && "(#{types})"].compact.join(" "), text(tag.text))
|
|
64
70
|
end
|
|
65
71
|
|
|
66
72
|
def parameters(docstring)
|
|
67
|
-
params = docstring.tags_named(
|
|
68
|
-
options = docstring.tags_named(
|
|
69
|
-
items = params.flat_map { |tag| [typed(tag), *option_items(options, tag.name,
|
|
70
|
-
[section(
|
|
73
|
+
params = docstring.tags_named("param")
|
|
74
|
+
options = docstring.tags_named("option")
|
|
75
|
+
items = params.flat_map { |tag| [typed(tag), *option_items(options, tag.name, " ")] }
|
|
76
|
+
[section("Parameters", items), *orphan_options(options, params.map(&:name))]
|
|
71
77
|
end
|
|
72
78
|
|
|
73
79
|
# Options whose hash is not itself documented with @param get their own section.
|
|
74
80
|
def orphan_options(options, param_names)
|
|
75
81
|
orphans = options.reject { |option| param_names.include?(option.name) }
|
|
76
82
|
orphans.map(&:name).uniq.map do |name|
|
|
77
|
-
section("Options (#{code(name)})", option_items(orphans, name,
|
|
83
|
+
section("Options (#{code(name)})", option_items(orphans, name, ""))
|
|
78
84
|
end
|
|
79
85
|
end
|
|
80
86
|
|
|
81
87
|
def option_items(options, name, indent)
|
|
82
88
|
options.select { |option| option.name == name }.map do |option|
|
|
83
89
|
types = types(option)
|
|
84
|
-
lead = [code(option.key), types && "(#{types})"].compact.join(
|
|
90
|
+
lead = [code(option.key), types && "(#{types})"].compact.join(" ")
|
|
85
91
|
default = option.default && "(default: #{code(option.default)})"
|
|
86
|
-
item(lead, [text(option.text), default].compact.reject(&:empty?).join(
|
|
92
|
+
item(lead, [text(option.text), default].compact.reject(&:empty?).join(" "), indent: indent)
|
|
87
93
|
end
|
|
88
94
|
end
|
|
89
95
|
|
|
90
96
|
def yields(docstring)
|
|
91
|
-
items = docstring.tags_named(
|
|
92
|
-
names = tag.types&.map { |name| code(name) }&.join(
|
|
97
|
+
items = docstring.tags_named("yield").map do |tag|
|
|
98
|
+
names = tag.types&.map { |name| code(name) }&.join(", ")
|
|
93
99
|
item(names && "(#{names})", text(tag.text))
|
|
94
100
|
end
|
|
95
|
-
section(
|
|
101
|
+
section("Yields", items)
|
|
96
102
|
end
|
|
97
103
|
|
|
98
104
|
def examples(docstring)
|
|
99
|
-
tags = docstring.tags_named(
|
|
105
|
+
tags = docstring.tags_named("example")
|
|
100
106
|
return nil if tags.empty?
|
|
101
107
|
|
|
102
|
-
[
|
|
108
|
+
["**Examples:**", *tags.flat_map { |tag| [("*#{tag.name}*" unless tag.name.to_s.empty?), fenced(tag.text)] }]
|
|
103
109
|
end
|
|
104
110
|
|
|
105
111
|
def fenced(source)
|
|
@@ -107,7 +113,7 @@ module Boondoc
|
|
|
107
113
|
end
|
|
108
114
|
|
|
109
115
|
def see_also(docstring)
|
|
110
|
-
section(
|
|
116
|
+
section("See also", docstring.tags_named("see").map { |tag| item(see_link(tag.name.to_s), text(tag.text)) })
|
|
111
117
|
end
|
|
112
118
|
|
|
113
119
|
def see_link(name)
|
|
@@ -120,18 +126,18 @@ module Boondoc
|
|
|
120
126
|
def meta(docstring)
|
|
121
127
|
known = META.flat_map { |name, title| docstring.tags_named(name).map { |tag| [title, tag] } }
|
|
122
128
|
other = docstring.tags.reject { |tag| HANDLED.include?(tag.tag_name) }
|
|
123
|
-
|
|
129
|
+
.map { |tag| [tag.tag_name.tr("_", " ").capitalize, tag] }
|
|
124
130
|
items = (known + other).map { |title, tag| meta_item(title, tag) }
|
|
125
131
|
items.join("\n") unless items.empty?
|
|
126
132
|
end
|
|
127
133
|
|
|
128
134
|
def meta_item(title, tag)
|
|
129
135
|
types = types(tag)
|
|
130
|
-
"- **#{title}:** #{[types && "(#{types})", text(tag.text)].compact.join(
|
|
136
|
+
"- **#{title}:** #{[types && "(#{types})", text(tag.text)].compact.join(" ")}".rstrip
|
|
131
137
|
end
|
|
132
138
|
|
|
133
139
|
def overloads(docstring)
|
|
134
|
-
docstring.tags_named(
|
|
140
|
+
docstring.tags_named("overload").flat_map do |tag|
|
|
135
141
|
["**Overload:**\n\n#{ruby("def #{tag.signature}")}", *render(tag.docstring)]
|
|
136
142
|
end
|
|
137
143
|
end
|
|
@@ -28,13 +28,13 @@ module Boondoc
|
|
|
28
28
|
lines = tree(registry.root, from, 0)
|
|
29
29
|
top = NamespacePage.new(context, registry.root)
|
|
30
30
|
lines << "- [Top level](#{path_map.relative(PathMap::TOP_LEVEL, from)})" if top.content?
|
|
31
|
-
[
|
|
31
|
+
["## API reference", lines.join("\n")] unless lines.empty?
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def tree(namespace, from, depth)
|
|
35
35
|
namespace.namespaces.select { |child| filter.visible?(child) }.flat_map do |child|
|
|
36
36
|
entry = bullet(linker.link(child, from: from), summary(child, from))
|
|
37
|
-
["#{
|
|
37
|
+
["#{" " * depth}#{entry}", *tree(child, from, depth + 1)]
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -42,7 +42,7 @@ module Boondoc
|
|
|
42
42
|
extras = context.stitcher.files.reject(&:readme)
|
|
43
43
|
return nil if extras.empty?
|
|
44
44
|
|
|
45
|
-
[
|
|
45
|
+
["## Files", extras.map { |file| "- [#{file.title}](#{path_map.relative(file.output, from)})" }.join("\n")]
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -30,8 +30,8 @@ module Boondoc
|
|
|
30
30
|
reader = accessors.find { |meth| meth.attribute == :reader }
|
|
31
31
|
writer = accessors.find { |meth| meth.attribute == :writer }
|
|
32
32
|
primary = reader || writer
|
|
33
|
-
name = primary.name.delete_suffix(
|
|
34
|
-
macro = reader && writer ?
|
|
33
|
+
name = primary.name.delete_suffix("=")
|
|
34
|
+
macro = (reader && writer) ? "attr_accessor" : "attr_#{primary.attribute}"
|
|
35
35
|
[
|
|
36
36
|
heading("#{primary.separator}#{name}", accessors),
|
|
37
37
|
ruby("#{macro} :#{name}"),
|
|
@@ -43,8 +43,8 @@ module Boondoc
|
|
|
43
43
|
# @param meth [Model::MethodObject]
|
|
44
44
|
# @return [String] the method as it would be defined, such as `def self.build(name)`
|
|
45
45
|
def signature(meth)
|
|
46
|
-
params = meth.parameters ? "(#{meth.parameters})" :
|
|
47
|
-
"def #{
|
|
46
|
+
params = meth.parameters ? "(#{meth.parameters})" : ""
|
|
47
|
+
"def #{"self." if meth.scope == :class}#{meth.name}#{params}"
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
private
|
|
@@ -64,7 +64,7 @@ module Boondoc
|
|
|
64
64
|
lines << "- **Visibility:** #{meth.visibility}" unless meth.visibility == :public
|
|
65
65
|
lines << "- **Alias for:** #{alias_link(meth)}" if meth.alias_for
|
|
66
66
|
aliases = aliases_of(meth)
|
|
67
|
-
lines << "- **Also known as:** #{aliases.join(
|
|
67
|
+
lines << "- **Also known as:** #{aliases.join(", ")}" unless aliases.empty?
|
|
68
68
|
lines.join("\n") unless lines.empty?
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -77,7 +77,7 @@ module Boondoc
|
|
|
77
77
|
def aliases_of(meth)
|
|
78
78
|
aliases = context.aliases(meth.namespace, meth.scope).fetch(meth.name, [])
|
|
79
79
|
aliases.select { |other| filter.visible?(other) }
|
|
80
|
-
|
|
80
|
+
.map { |other| linker.link(other, from: @from, label: code(other.display_name)) }
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
83
|
end
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module Boondoc
|
|
4
4
|
module Renderer
|
|
5
5
|
# Renders the page for one class or module, or for the top-level namespace.
|
|
6
|
-
class NamespacePage < Base
|
|
7
|
-
SCOPES = { class:
|
|
8
|
-
MIXINS = { include:
|
|
6
|
+
class NamespacePage < Base
|
|
7
|
+
SCOPES = { class: "Class methods", instance: "Instance methods" }.freeze
|
|
8
|
+
MIXINS = { include: "Includes", extend: "Extended by", prepend: "Prepended by" }.freeze
|
|
9
9
|
MAX_CONSTANT_LINES = 12
|
|
10
10
|
# A guard against a superclass chain that never ends; real chains are far shorter.
|
|
11
11
|
MAX_SUPERCLASS_CHAIN = 20
|
|
@@ -27,10 +27,15 @@ module Boondoc
|
|
|
27
27
|
# @return [String] the whole page
|
|
28
28
|
def render
|
|
29
29
|
document([
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
"# #{title}",
|
|
31
|
+
facts,
|
|
32
|
+
Docstring.new(context, @namespace, @from).render(@namespace.docstring),
|
|
33
|
+
namespaces,
|
|
34
|
+
constants,
|
|
35
|
+
attributes,
|
|
36
|
+
SCOPES.keys.map { |scope| method_sections(scope) },
|
|
37
|
+
inherited_methods
|
|
38
|
+
])
|
|
34
39
|
end
|
|
35
40
|
|
|
36
41
|
private
|
|
@@ -40,7 +45,7 @@ module Boondoc
|
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
def title
|
|
43
|
-
return
|
|
48
|
+
return "Top level" if @namespace.root?
|
|
44
49
|
|
|
45
50
|
"#{@namespace.kind.to_s.capitalize}: #{@namespace.path}"
|
|
46
51
|
end
|
|
@@ -49,16 +54,16 @@ module Boondoc
|
|
|
49
54
|
|
|
50
55
|
def facts
|
|
51
56
|
lines = []
|
|
52
|
-
lines << "- **Inherits:** #{superclass_chain.join(
|
|
57
|
+
lines << "- **Inherits:** #{superclass_chain.join(" < ")}" if @namespace.superclass
|
|
53
58
|
lines.concat(mixin_facts)
|
|
54
|
-
lines << "- **Defined in:** #{source_links.join(
|
|
59
|
+
lines << "- **Defined in:** #{source_links.join(", ")}" unless @namespace.files.empty?
|
|
55
60
|
lines.join("\n") unless lines.empty?
|
|
56
61
|
end
|
|
57
62
|
|
|
58
63
|
def mixin_facts
|
|
59
64
|
MIXINS.filter_map do |kind, label|
|
|
60
65
|
names = @namespace.mixins[kind]
|
|
61
|
-
"- **#{label}:** #{names.map { |name| constant_link(name) }.join(
|
|
66
|
+
"- **#{label}:** #{names.map { |name| constant_link(name) }.join(", ")}" unless names.empty?
|
|
62
67
|
end
|
|
63
68
|
end
|
|
64
69
|
|
|
@@ -89,15 +94,20 @@ module Boondoc
|
|
|
89
94
|
children = visible(@namespace.namespaces)
|
|
90
95
|
return nil if children.empty?
|
|
91
96
|
|
|
92
|
-
[
|
|
93
|
-
|
|
97
|
+
["## Namespaces",
|
|
98
|
+
children.map { |child|
|
|
99
|
+
bullet(
|
|
100
|
+
linker.link(child, from: @from),
|
|
101
|
+
summary(child, @from)
|
|
102
|
+
)
|
|
103
|
+
}.join("\n")]
|
|
94
104
|
end
|
|
95
105
|
|
|
96
106
|
def constants
|
|
97
107
|
constants = visible(@namespace.constants)
|
|
98
108
|
return nil if constants.empty?
|
|
99
109
|
|
|
100
|
-
[
|
|
110
|
+
["## Constants", *constants.flat_map { |constant| constant_section(constant) }]
|
|
101
111
|
end
|
|
102
112
|
|
|
103
113
|
def constant_section(constant)
|
|
@@ -118,11 +128,11 @@ module Boondoc
|
|
|
118
128
|
def attributes
|
|
119
129
|
groups = SCOPES.keys.flat_map do |scope|
|
|
120
130
|
visible(context.meths(@namespace, scope)).select(&:attribute)
|
|
121
|
-
|
|
131
|
+
.group_by { |meth| meth.name.delete_suffix("=") }.values
|
|
122
132
|
end
|
|
123
133
|
return nil if groups.empty?
|
|
124
134
|
|
|
125
|
-
[
|
|
135
|
+
["## Attributes", *groups.flat_map { |accessors| @methods.render_attribute(accessors) }]
|
|
126
136
|
end
|
|
127
137
|
|
|
128
138
|
def method_sections(scope)
|
|
@@ -134,10 +144,10 @@ module Boondoc
|
|
|
134
144
|
|
|
135
145
|
def summary_table(meths)
|
|
136
146
|
rows = meths.map do |meth|
|
|
137
|
-
label = Markdown.table_cell(@methods.signature(meth).delete_prefix(
|
|
147
|
+
label = Markdown.table_cell(@methods.signature(meth).delete_prefix("def ").delete_prefix("self."))
|
|
138
148
|
"| [#{code("#{meth.separator}#{label}")}](#{path_map.href(meth, from: @from)}) | #{summary(meth, @from)} |"
|
|
139
149
|
end
|
|
140
|
-
[
|
|
150
|
+
["| Method | Summary |", "| --- | --- |", *rows].join("\n")
|
|
141
151
|
end
|
|
142
152
|
|
|
143
153
|
# Each method is listed under the nearest ancestor that defines it, the
|
|
@@ -145,7 +155,7 @@ module Boondoc
|
|
|
145
155
|
def inherited_methods
|
|
146
156
|
seen = context.meths(@namespace, :instance).to_set(&:name)
|
|
147
157
|
items = registry.ancestors(@namespace).drop(1).filter_map { |ancestor| inherited_from_ancestor(ancestor, seen) }
|
|
148
|
-
[
|
|
158
|
+
["## Inherited methods", items.join("\n")] unless items.empty?
|
|
149
159
|
end
|
|
150
160
|
|
|
151
161
|
def inherited_from_ancestor(ancestor, seen)
|
|
@@ -158,7 +168,7 @@ module Boondoc
|
|
|
158
168
|
return nil if meths.empty?
|
|
159
169
|
|
|
160
170
|
links = meths.map { |meth| linker.link(meth, from: @from, label: code(meth.display_name)) }
|
|
161
|
-
"- From #{linker.link(ancestor, from: @from)}: #{links.join(
|
|
171
|
+
"- From #{linker.link(ancestor, from: @from)}: #{links.join(", ")}"
|
|
162
172
|
end
|
|
163
173
|
end
|
|
164
174
|
end
|
data/lib/boondoc/stitcher.rb
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "pathname"
|
|
4
4
|
|
|
5
5
|
module Boondoc
|
|
6
6
|
# A non-API file (README, changelog, guide) that is copied into the output.
|
|
7
7
|
ExtraFile = Struct.new(:source, :output, :content, :readme, keyword_init: true) do
|
|
8
8
|
# @return [String] the first top-level heading, or the file name
|
|
9
9
|
def title
|
|
10
|
-
content[/^#\s+(.+?)\s*#*\s*$/, 1] || File.basename(source,
|
|
10
|
+
content[/^#\s+(.+?)\s*#*\s*$/, 1] || File.basename(source, ".*")
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -39,7 +39,7 @@ module Boondoc
|
|
|
39
39
|
# @return [ExtraFile, nil]
|
|
40
40
|
def find(name)
|
|
41
41
|
files.find { |file| file.source == name } ||
|
|
42
|
-
files.find { |file| [File.basename(file.source), File.basename(file.source,
|
|
42
|
+
files.find { |file| [File.basename(file.source), File.basename(file.source, ".*")].include?(name) }
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# Rewrites relative links and image paths so they still point at the same
|
|
@@ -62,7 +62,7 @@ module Boondoc
|
|
|
62
62
|
|
|
63
63
|
# Sources are relative to the project root; {Config#extra_files} rejects any outside it.
|
|
64
64
|
def output_name(source)
|
|
65
|
-
MARKDOWN.match?(source) ? source.sub(MARKDOWN,
|
|
65
|
+
MARKDOWN.match?(source) ? source.sub(MARKDOWN, ".md") : "#{source}.md"
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def relocate(target, file, embedded:)
|
data/lib/boondoc/type_parser.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Boondoc
|
|
4
4
|
# Splits a YARD type list such as `[String, Hash{Symbol => Array<Integer>}]`.
|
|
5
5
|
module TypeParser
|
|
6
|
-
OPENERS = {
|
|
6
|
+
OPENERS = { "[" => "]", "<" => ">", "(" => ")", "{" => "}" }.freeze
|
|
7
7
|
# Duck types naming an operator method that contains bracket characters.
|
|
8
8
|
OPERATOR_DUCK_TYPE = /#(?:<=>|<<|>>|[<>]=?|\[\]=?)/
|
|
9
9
|
|
|
@@ -16,7 +16,7 @@ module Boondoc
|
|
|
16
16
|
# when the text does not start with `[`) and the remaining text
|
|
17
17
|
def extract(text)
|
|
18
18
|
text = text.lstrip
|
|
19
|
-
return [nil, text] unless text.start_with?(
|
|
19
|
+
return [nil, text] unless text.start_with?("[")
|
|
20
20
|
|
|
21
21
|
close = closing_index(text)
|
|
22
22
|
return [nil, text] unless close
|
|
@@ -29,9 +29,9 @@ module Boondoc
|
|
|
29
29
|
# @param list [String] the text between the outer brackets
|
|
30
30
|
# @return [Array<String>]
|
|
31
31
|
def split(list)
|
|
32
|
-
types = [
|
|
32
|
+
types = [""]
|
|
33
33
|
each_with_depth(list) do |char, _, depth|
|
|
34
|
-
next types << +
|
|
34
|
+
next types << +"" if char == "," && depth.zero?
|
|
35
35
|
|
|
36
36
|
types[-1] += char
|
|
37
37
|
end
|
|
@@ -52,7 +52,7 @@ module Boondoc
|
|
|
52
52
|
depth = 0
|
|
53
53
|
text.each_char.with_index do |char, index|
|
|
54
54
|
depth += depth_change(text, char, index) unless skip.include?(index)
|
|
55
|
-
yield
|
|
55
|
+
yield(char, index, depth)
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
@@ -72,7 +72,7 @@ module Boondoc
|
|
|
72
72
|
return 0 unless OPENERS.value?(char)
|
|
73
73
|
|
|
74
74
|
# The `>` of a hash rocket (`=>`) does not close anything.
|
|
75
|
-
char ==
|
|
75
|
+
(char == ">" && index.positive? && text[index - 1] == "=") ? 0 : -1
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
end
|
data/lib/boondoc/version.rb
CHANGED
data/lib/boondoc/writer.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "fileutils"
|
|
4
4
|
|
|
5
5
|
module Boondoc
|
|
6
6
|
# Writes generated pages and removes pages left over from earlier runs.
|
|
@@ -9,7 +9,7 @@ module Boondoc
|
|
|
9
9
|
# own files are ever deleted or silently overwritten. A page whose content has
|
|
10
10
|
# not changed is not written again.
|
|
11
11
|
class Writer
|
|
12
|
-
MANIFEST =
|
|
12
|
+
MANIFEST = ".boondoc-manifest"
|
|
13
13
|
|
|
14
14
|
# @param output_dir [String] absolute path of the output directory
|
|
15
15
|
# @param reporter [Reporter]
|
|
@@ -84,10 +84,10 @@ module Boondoc
|
|
|
84
84
|
# outside the output directory
|
|
85
85
|
def unsafe(path)
|
|
86
86
|
full = File.expand_path(path, @output_dir)
|
|
87
|
-
return "it is outside #{@output_dir}" unless full.start_with?(File.join(@output_dir,
|
|
87
|
+
return "it is outside #{@output_dir}" unless full.start_with?(File.join(@output_dir, ""))
|
|
88
88
|
|
|
89
89
|
# The symlink check below resolves `..` by name, but the file system follows a symlink first.
|
|
90
|
-
return
|
|
90
|
+
return "it contains `..`" if path.split("/").include?("..")
|
|
91
91
|
|
|
92
92
|
link = Containment.symlink_between(full, @output_dir)
|
|
93
93
|
"#{link} is a symlink" if link
|
|
@@ -102,9 +102,10 @@ module Boondoc
|
|
|
102
102
|
end
|
|
103
103
|
return if conflicts.empty?
|
|
104
104
|
|
|
105
|
-
raise Error,
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
raise Error,
|
|
106
|
+
"refusing to overwrite #{conflicts.size} file(s) that boondoc did not generate " \
|
|
107
|
+
"(#{conflicts.first(5).join(", ")}#{", ..." if conflicts.size > 5}). " \
|
|
108
|
+
"Use --force to overwrite, or choose another --output directory."
|
|
108
109
|
end
|
|
109
110
|
|
|
110
111
|
# True when a case-insensitive filesystem maps the path onto a page Boondoc wrote before.
|
|
@@ -120,8 +121,10 @@ module Boondoc
|
|
|
120
121
|
paths.group_by(&:downcase).each_value do |group|
|
|
121
122
|
next if group.size == 1
|
|
122
123
|
|
|
123
|
-
@reporter.warn(
|
|
124
|
-
|
|
124
|
+
@reporter.warn(
|
|
125
|
+
"#{group.sort.join(" and ")} differ only by case and will collide on case-insensitive " \
|
|
126
|
+
"filesystems; rename one of them"
|
|
127
|
+
)
|
|
125
128
|
end
|
|
126
129
|
end
|
|
127
130
|
|
data/lib/boondoc/yard_bridge.rb
CHANGED
|
@@ -41,13 +41,13 @@ module Boondoc
|
|
|
41
41
|
private
|
|
42
42
|
|
|
43
43
|
def load_yard
|
|
44
|
-
require
|
|
44
|
+
require "yard"
|
|
45
45
|
rescue LoadError
|
|
46
46
|
raise Error, "--yard-macros needs the yard gem. Add `gem 'yard'` to your Gemfile or run `gem install yard`."
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def yard_objects
|
|
50
|
-
namespaces = ::YARD::Registry.all(:module, :class).sort_by { |object| object.path.count(
|
|
50
|
+
namespaces = ::YARD::Registry.all(:module, :class).sort_by { |object| object.path.count(":") }
|
|
51
51
|
namespaces + ::YARD::Registry.all(:constant, :method)
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -71,7 +71,7 @@ module Boondoc
|
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def relative(file)
|
|
74
|
-
prefix = File.join(@root,
|
|
74
|
+
prefix = File.join(@root, "")
|
|
75
75
|
file.start_with?(prefix) ? file.delete_prefix(prefix) : file
|
|
76
76
|
end
|
|
77
77
|
|
|
@@ -92,18 +92,23 @@ module Boondoc
|
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
def build_method(yard_object, name, namespace)
|
|
95
|
-
attribute = (name.end_with?(
|
|
96
|
-
Model::MethodObject.new(
|
|
97
|
-
|
|
95
|
+
attribute = (name.end_with?("=") ? :writer : :reader) if yard_object.is_attribute?
|
|
96
|
+
Model::MethodObject.new(
|
|
97
|
+
name,
|
|
98
|
+
namespace,
|
|
99
|
+
scope: yard_object.scope,
|
|
100
|
+
attribute: attribute,
|
|
101
|
+
parameters: parameters(yard_object)
|
|
102
|
+
)
|
|
98
103
|
end
|
|
99
104
|
|
|
100
105
|
def parameters(yard_object)
|
|
101
106
|
list = yard_object.parameters.map do |name, default|
|
|
102
107
|
next name.to_s unless default
|
|
103
108
|
|
|
104
|
-
name.to_s.end_with?(
|
|
109
|
+
name.to_s.end_with?(":") ? "#{name} #{default}" : "#{name} = #{default}"
|
|
105
110
|
end
|
|
106
|
-
list.empty? ? nil : list.join(
|
|
111
|
+
list.empty? ? nil : list.join(", ")
|
|
107
112
|
end
|
|
108
113
|
|
|
109
114
|
def docstring(yard_object)
|