docrb 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +21 -0
- data/.rspec +3 -0
- data/.rubocop.yml +70 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +79 -0
- data/Rakefile +12 -0
- data/bin/console +16 -0
- data/bin/json +16 -0
- data/bin/md +8 -0
- data/bin/setup +8 -0
- data/docrb.gemspec +33 -0
- data/exe/docrb +205 -0
- data/lib/docrb/comment_parser/code_example_block.rb +36 -0
- data/lib/docrb/comment_parser/code_example_parser.rb +29 -0
- data/lib/docrb/comment_parser/field_block.rb +18 -0
- data/lib/docrb/comment_parser/field_list_parser.rb +90 -0
- data/lib/docrb/comment_parser/text_block.rb +43 -0
- data/lib/docrb/comment_parser.rb +272 -0
- data/lib/docrb/doc_compiler/base_container/computations.rb +178 -0
- data/lib/docrb/doc_compiler/base_container.rb +123 -0
- data/lib/docrb/doc_compiler/doc_attribute.rb +58 -0
- data/lib/docrb/doc_compiler/doc_blocks.rb +111 -0
- data/lib/docrb/doc_compiler/doc_class.rb +43 -0
- data/lib/docrb/doc_compiler/doc_method.rb +66 -0
- data/lib/docrb/doc_compiler/doc_module.rb +9 -0
- data/lib/docrb/doc_compiler/file_ref.rb +41 -0
- data/lib/docrb/doc_compiler/object_container.rb +68 -0
- data/lib/docrb/doc_compiler.rb +55 -0
- data/lib/docrb/markdown.rb +62 -0
- data/lib/docrb/module_extensions.rb +13 -0
- data/lib/docrb/resolvable.rb +178 -0
- data/lib/docrb/ruby_parser.rb +630 -0
- data/lib/docrb/spec.rb +31 -0
- data/lib/docrb/version.rb +5 -0
- data/lib/docrb.rb +71 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 74814d0dca672ec2099fcb9092660f198b93ccb3b454979201ad18be4db5fb25
|
4
|
+
data.tar.gz: 34660996e9148eebe8a1757f04f0be4c2f19300d9de1fd43f793e3d7e97502c6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6f53696d60c6bf4634dd2c2fe093c24a786b1f6cd9e87432a73e8ca4c5298d2f9c868cd8b8640dbd65bfc67b130272d2708ea6e7cdd01da142da33278dae0625
|
7
|
+
data.tar.gz: a98ca47316bdc564de37a6144dfa8613f49717313d7dee5424ce5feca10f6e91f80353a0078233461d3f6dd4202ce0ecb3635ef6641326b3e325f28b6234562c
|
data/.editorconfig
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
root = true
|
2
|
+
|
3
|
+
[*]
|
4
|
+
end_of_line = lf
|
5
|
+
indent_size = 4
|
6
|
+
indent_style = space
|
7
|
+
insert_final_newline = true
|
8
|
+
tab_width = 8
|
9
|
+
trim_trailing_whitespace = true
|
10
|
+
|
11
|
+
[*.gemspec]
|
12
|
+
indent_size = 2
|
13
|
+
|
14
|
+
[*.rb]
|
15
|
+
indent_size = 2
|
16
|
+
|
17
|
+
[*.yml]
|
18
|
+
indent_size = 2
|
19
|
+
|
20
|
+
[./bin/*]
|
21
|
+
indent_size = 2
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.2
|
3
|
+
NewCops: enable
|
4
|
+
Exclude:
|
5
|
+
- spec/**/*
|
6
|
+
|
7
|
+
Style/StringLiterals:
|
8
|
+
Enabled: true
|
9
|
+
EnforcedStyle: double_quotes
|
10
|
+
|
11
|
+
Style/StringLiteralsInInterpolation:
|
12
|
+
Enabled: true
|
13
|
+
EnforcedStyle: double_quotes
|
14
|
+
|
15
|
+
Style/Documentation:
|
16
|
+
Exclude:
|
17
|
+
- lib/docrb/module_extensions.rb
|
18
|
+
|
19
|
+
Layout/LineLength:
|
20
|
+
Max: 120
|
21
|
+
Exclude:
|
22
|
+
- exe/*
|
23
|
+
- lib/docrb/comment_parser.rb
|
24
|
+
- lib/docrb/ruby_parser.rb
|
25
|
+
|
26
|
+
Metrics/ClassLength:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Metrics/MethodLength:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Naming/MethodParameterName:
|
33
|
+
Exclude:
|
34
|
+
- spec/**/*
|
35
|
+
|
36
|
+
Metrics/BlockLength:
|
37
|
+
Exclude:
|
38
|
+
- exe/*
|
39
|
+
- lib/docrb/ruby_parser.rb
|
40
|
+
|
41
|
+
Metrics/PerceivedComplexity:
|
42
|
+
Exclude:
|
43
|
+
- exe/*
|
44
|
+
- lib/docrb/doc_compiler/base_container.rb
|
45
|
+
- lib/docrb/doc_compiler/base_container/computations.rb
|
46
|
+
- lib/docrb/doc_compiler/object_container.rb
|
47
|
+
- lib/docrb/resolvable.rb
|
48
|
+
- lib/docrb/ruby_parser.rb
|
49
|
+
|
50
|
+
Metrics/CyclomaticComplexity:
|
51
|
+
Exclude:
|
52
|
+
- exe/*
|
53
|
+
- lib/docrb/doc_compiler/base_container.rb
|
54
|
+
- lib/docrb/doc_compiler/base_container/computations.rb
|
55
|
+
- lib/docrb/doc_compiler/object_container.rb
|
56
|
+
- lib/docrb/resolvable.rb
|
57
|
+
- lib/docrb/ruby_parser.rb
|
58
|
+
|
59
|
+
Lint/ShadowingOuterLocalVariable:
|
60
|
+
Exclude:
|
61
|
+
- exe/*
|
62
|
+
|
63
|
+
Metrics/AbcSize:
|
64
|
+
Enabled: false
|
65
|
+
|
66
|
+
Metrics/ModuleLength:
|
67
|
+
Enabled: false
|
68
|
+
|
69
|
+
Lint/BooleanSymbol:
|
70
|
+
Enabled: false
|
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in docrb.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
group :development do
|
9
|
+
gem "rake", "~> 13.0"
|
10
|
+
|
11
|
+
gem "rspec", "~> 3.0"
|
12
|
+
|
13
|
+
gem "rubocop", "~> 1.8"
|
14
|
+
|
15
|
+
gem "byebug"
|
16
|
+
|
17
|
+
gem "awesome_print"
|
18
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
docrb (0.2.0)
|
5
|
+
docrb-html (~> 0.2)
|
6
|
+
parser (~> 3.2)
|
7
|
+
redcarpet (~> 3.6)
|
8
|
+
rouge (~> 4.1)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
ast (2.4.2)
|
14
|
+
awesome_print (1.9.2)
|
15
|
+
byebug (11.1.3)
|
16
|
+
diff-lcs (1.5.0)
|
17
|
+
docrb-html (0.2.5)
|
18
|
+
nokogiri (~> 1.14)
|
19
|
+
sassc (~> 2.4)
|
20
|
+
ffi (1.15.5)
|
21
|
+
json (2.6.3)
|
22
|
+
nokogiri (1.14.2-arm64-darwin)
|
23
|
+
racc (~> 1.4)
|
24
|
+
nokogiri (1.14.2-x86_64-darwin)
|
25
|
+
racc (~> 1.4)
|
26
|
+
parallel (1.22.1)
|
27
|
+
parser (3.2.1.0)
|
28
|
+
ast (~> 2.4.1)
|
29
|
+
racc (1.6.2)
|
30
|
+
rainbow (3.1.1)
|
31
|
+
rake (13.0.6)
|
32
|
+
redcarpet (3.6.0)
|
33
|
+
regexp_parser (2.7.0)
|
34
|
+
rexml (3.2.5)
|
35
|
+
rouge (4.1.0)
|
36
|
+
rspec (3.12.0)
|
37
|
+
rspec-core (~> 3.12.0)
|
38
|
+
rspec-expectations (~> 3.12.0)
|
39
|
+
rspec-mocks (~> 3.12.0)
|
40
|
+
rspec-core (3.12.1)
|
41
|
+
rspec-support (~> 3.12.0)
|
42
|
+
rspec-expectations (3.12.2)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.12.0)
|
45
|
+
rspec-mocks (3.12.3)
|
46
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
47
|
+
rspec-support (~> 3.12.0)
|
48
|
+
rspec-support (3.12.0)
|
49
|
+
rubocop (1.45.1)
|
50
|
+
json (~> 2.3)
|
51
|
+
parallel (~> 1.10)
|
52
|
+
parser (>= 3.2.0.0)
|
53
|
+
rainbow (>= 2.2.2, < 4.0)
|
54
|
+
regexp_parser (>= 1.8, < 3.0)
|
55
|
+
rexml (>= 3.2.5, < 4.0)
|
56
|
+
rubocop-ast (>= 1.24.1, < 2.0)
|
57
|
+
ruby-progressbar (~> 1.7)
|
58
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
59
|
+
rubocop-ast (1.26.0)
|
60
|
+
parser (>= 3.2.1.0)
|
61
|
+
ruby-progressbar (1.11.0)
|
62
|
+
sassc (2.4.0)
|
63
|
+
ffi (~> 1.9)
|
64
|
+
unicode-display_width (2.4.2)
|
65
|
+
|
66
|
+
PLATFORMS
|
67
|
+
arm64-darwin-22
|
68
|
+
x86_64-darwin-20
|
69
|
+
|
70
|
+
DEPENDENCIES
|
71
|
+
awesome_print
|
72
|
+
byebug
|
73
|
+
docrb!
|
74
|
+
rake (~> 13.0)
|
75
|
+
rspec (~> 3.0)
|
76
|
+
rubocop (~> 1.8)
|
77
|
+
|
78
|
+
BUNDLED WITH
|
79
|
+
2.2.22
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "docrb"
|
6
|
+
require "byebug"
|
7
|
+
|
8
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
9
|
+
# with your gem easier. You can also use a different console, if you like.
|
10
|
+
|
11
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
12
|
+
# require "pry"
|
13
|
+
# Pry.start
|
14
|
+
|
15
|
+
require "irb"
|
16
|
+
IRB.start(__FILE__)
|
data/bin/json
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "docrb"
|
6
|
+
require "byebug"
|
7
|
+
require "json"
|
8
|
+
|
9
|
+
def run
|
10
|
+
data = Docrb.parse_folder(ARGV[0])
|
11
|
+
compiler = Docrb::DocCompiler.new
|
12
|
+
data.each { |f| compiler.append(f) }
|
13
|
+
puts compiler.to_h.to_json
|
14
|
+
end
|
15
|
+
|
16
|
+
run
|
data/bin/md
ADDED
data/bin/setup
ADDED
data/docrb.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/docrb/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "docrb"
|
7
|
+
spec.version = Docrb::VERSION
|
8
|
+
spec.authors = ["Victor Gama"]
|
9
|
+
spec.email = ["hey@vito.io"]
|
10
|
+
|
11
|
+
spec.summary = "An opinionated documentation parser"
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/heyvito/docrb"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 3.2"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/trunk/lib/docrb"
|
19
|
+
spec.metadata["changelog_uri"] = spec.homepage
|
20
|
+
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
23
|
+
end
|
24
|
+
spec.bindir = "exe"
|
25
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
|
28
|
+
spec.add_dependency "docrb-html", "~> 0.2"
|
29
|
+
spec.add_dependency "parser", "~> 3.2"
|
30
|
+
spec.add_dependency "redcarpet", "~> 3.6"
|
31
|
+
spec.add_dependency "rouge", "~> 4.1"
|
32
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
33
|
+
end
|
data/exe/docrb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "json"
|
6
|
+
require "fileutils"
|
7
|
+
require "optparse"
|
8
|
+
require "open3"
|
9
|
+
require "tmpdir"
|
10
|
+
require "renderer"
|
11
|
+
|
12
|
+
require "docrb"
|
13
|
+
|
14
|
+
def exec(cmd, *args, **kwargs)
|
15
|
+
_, stdout_and_err, wait_thread = Open3.popen2e(cmd, *args, **kwargs)
|
16
|
+
status = wait_thread.value
|
17
|
+
{
|
18
|
+
code: status.exitstatus,
|
19
|
+
output: stdout_and_err.read.chomp
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def git(*args, **kwargs)
|
24
|
+
exec("git", *args, **kwargs)
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_html(*args, **kwargs)
|
28
|
+
exec("docrb-html", *args, **kwargs)
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
options = {}
|
33
|
+
opts = OptionParser.new do |opts|
|
34
|
+
opts.banner = "Usage: bin/docrb [options] [input directory] [output directory]"
|
35
|
+
gemspec_info = "When omitted, Docrb attempts to extract information from a .gemspec file in the provided input directory."
|
36
|
+
|
37
|
+
opts.on("--help", "Prints this help") do
|
38
|
+
puts opts
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("-bPATH", "--base=PATH",
|
43
|
+
"Base directory to search for source files. Defaults to the provided input directory.") do |b|
|
44
|
+
options[:base] = b
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("-rPATH", "--readme=PATH",
|
48
|
+
"Path for README.md file. When omitted, Docrb searches for a README.md file in the provided input directory.") do |r|
|
49
|
+
options[:readme] = r
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("-nNAME", "--name=NAME", "Name of the project being documented. #{gemspec_info}") do |n|
|
53
|
+
options[:name] = n
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on("-sSUMMARY", "--summary=SUMMARY", "Short summary of the project being documented. #{gemspec_info}") do |d|
|
57
|
+
options[:summary] = d
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.on("-hURL", "--host=URL", "URL for the gem's hosted URL. #{gemspec_info}") do |u|
|
61
|
+
options[:host_url] = u
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on("-gURL", "--git-repo=URL",
|
65
|
+
"URL for the repository containing the documented project. When omitted, Docrb attempts to extract this information from the .git directory present in the provided input directory, if any.") do |u|
|
66
|
+
options[:git_url] = u
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on("--authors a,b,c", "List of name of project authors. #{gemspec_info}") do |list|
|
70
|
+
options[:authors] = list
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on("-lLICENSE", "--license=LICENSE", "The project's license. #{gemspec_info}") do |license|
|
74
|
+
options[:license] = license
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.parse!
|
79
|
+
|
80
|
+
if ARGV.length != 2
|
81
|
+
puts opts
|
82
|
+
exit(1)
|
83
|
+
end
|
84
|
+
|
85
|
+
input = ARGV[0]
|
86
|
+
output = ARGV[1]
|
87
|
+
|
88
|
+
unless File.exist? input
|
89
|
+
puts "#{input}: Does not exist"
|
90
|
+
exit(1)
|
91
|
+
end
|
92
|
+
|
93
|
+
if File.exist? output
|
94
|
+
unless File.directory? output
|
95
|
+
puts "#{output}: Exists and is not a directory."
|
96
|
+
exit(1)
|
97
|
+
end
|
98
|
+
else
|
99
|
+
begin
|
100
|
+
FileUtils.mkdir_p output
|
101
|
+
rescue StandardError => e
|
102
|
+
puts e
|
103
|
+
exit(1)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
tmp_output = Dir.mktmpdir
|
108
|
+
data_path = File.join(tmp_output, "data.json")
|
109
|
+
markdown_path = File.join(tmp_output, "readme.html")
|
110
|
+
metadata_path = File.join(tmp_output, "metadata.json")
|
111
|
+
readme_path = File.join(input, "README.md")
|
112
|
+
base_path = input
|
113
|
+
|
114
|
+
if options[:readme]
|
115
|
+
readme_path = options[:readme]
|
116
|
+
unless File.exist? readme_path
|
117
|
+
puts "--readme was provided, but #{readme_path} could not be found."
|
118
|
+
exit(1)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
spec = Docrb::Spec.parse_folder(input) || {}
|
123
|
+
|
124
|
+
%i[name summary host_url git_url authors license].each do |k|
|
125
|
+
spec[k] = options[k] if options.key? k
|
126
|
+
end
|
127
|
+
|
128
|
+
if spec[:name] == "" || spec[:name].nil?
|
129
|
+
puts "Docrb could not detect the project name. Please check your .gemspec, or provide one manually using --name."
|
130
|
+
exit(1)
|
131
|
+
end
|
132
|
+
|
133
|
+
if spec[:summary] == "" || spec[:summary].nil?
|
134
|
+
puts "Docrb could not detect the project's summary. Please check your .gemspec, or provide one manually using --summary."
|
135
|
+
exit(1)
|
136
|
+
end
|
137
|
+
|
138
|
+
if options[:base]
|
139
|
+
path = File.join(input, options[:base])
|
140
|
+
unless File.exist? path
|
141
|
+
puts "--base was provided, but #{path} does not exist."
|
142
|
+
exit(1)
|
143
|
+
end
|
144
|
+
base_path = path
|
145
|
+
end
|
146
|
+
|
147
|
+
git_tip = nil
|
148
|
+
git_root = nil
|
149
|
+
git_status = git("status", "--porcelain", chdir: input)
|
150
|
+
if (git_status[:code]).zero?
|
151
|
+
if git_status[:output].length.positive?
|
152
|
+
puts "WARNING: Your local git copy seems to be dirty. Consider commiting your changes before generating docs."
|
153
|
+
end
|
154
|
+
tip_data = git("rev-parse", "HEAD", chdir: input)
|
155
|
+
if tip_data[:code] != 0
|
156
|
+
puts "ERROR: Acquiring repository information failed:"
|
157
|
+
puts tip_data[:output]
|
158
|
+
puts "------- 8< cut here"
|
159
|
+
puts "Aborting."
|
160
|
+
exit 1
|
161
|
+
end
|
162
|
+
toplevel = git("rev-parse", "--show-toplevel", chdir: input)
|
163
|
+
if toplevel[:code] != 0
|
164
|
+
puts "ERROR: Acquiring repository toplevel failed:"
|
165
|
+
puts tip_data[:output]
|
166
|
+
puts "------- 8< cut here"
|
167
|
+
puts "Aborting."
|
168
|
+
exit 1
|
169
|
+
end
|
170
|
+
|
171
|
+
git_tip = tip_data[:output]
|
172
|
+
git_root = toplevel[:output]
|
173
|
+
else
|
174
|
+
puts "git status returned status #{git_status[:code]}; avoiding git..."
|
175
|
+
end
|
176
|
+
|
177
|
+
spec[:git_tip] = git_tip if git_tip
|
178
|
+
spec[:git_root] = git_root if git_root
|
179
|
+
spec[:timestamp] = Time.now.utc.strftime("%A, %d %b %Y %l:%M %p GMT")
|
180
|
+
spec[:version] = Docrb::VERSION
|
181
|
+
|
182
|
+
data = Docrb.parse_folder(base_path)
|
183
|
+
compiler = Docrb::DocCompiler.new
|
184
|
+
data.each { |f| compiler.append(f) }
|
185
|
+
|
186
|
+
File.write(data_path, compiler.to_h.to_json)
|
187
|
+
puts "Created: #{data_path}"
|
188
|
+
|
189
|
+
if File.exist? readme_path
|
190
|
+
puts "Found #{readme_path}"
|
191
|
+
md = File.read(readme_path).to_s
|
192
|
+
result = Docrb::Markdown.render(md)
|
193
|
+
File.write(markdown_path, result)
|
194
|
+
puts "Created #{markdown_path}"
|
195
|
+
else
|
196
|
+
puts "#{readme_path} does not exist. Skipping..."
|
197
|
+
end
|
198
|
+
File.write(metadata_path, spec.to_json)
|
199
|
+
puts "Created #{metadata_path}"
|
200
|
+
|
201
|
+
puts "Generating HTML into #{output}"
|
202
|
+
Renderer.new(tmp_output, output).render
|
203
|
+
end
|
204
|
+
|
205
|
+
run
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class CommentParser
|
5
|
+
# CodeExampleBlock represents a list of characters of a code example
|
6
|
+
class CodeExampleBlock
|
7
|
+
attr_reader :code
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@code = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(text_block)
|
14
|
+
@code << "\n" unless empty?
|
15
|
+
@code << text_block.text
|
16
|
+
end
|
17
|
+
|
18
|
+
def empty?
|
19
|
+
@code.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def normalize
|
23
|
+
@code
|
24
|
+
.map { |txt| txt.split("\n") }
|
25
|
+
.map { |item| item.empty? ? "" : item }
|
26
|
+
.flatten
|
27
|
+
.map { |line| line.gsub(/^\s{2}/, "") }
|
28
|
+
.join("\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_h
|
32
|
+
{ type: :code_example, contents: normalize }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class CommentParser
|
5
|
+
# CodeExampleParser attempts to extract code examples from a documentation
|
6
|
+
# block.
|
7
|
+
class CodeExampleParser
|
8
|
+
def self.process(components)
|
9
|
+
new_components = []
|
10
|
+
code_example_group = CodeExampleBlock.new
|
11
|
+
components.each do |c|
|
12
|
+
if !c.is_a?(TextBlock) || !c.text.start_with?(" ")
|
13
|
+
unless code_example_group.empty?
|
14
|
+
new_components << code_example_group
|
15
|
+
code_example_group = CodeExampleBlock.new
|
16
|
+
end
|
17
|
+
new_components << c
|
18
|
+
next
|
19
|
+
end
|
20
|
+
|
21
|
+
code_example_group << c
|
22
|
+
end
|
23
|
+
|
24
|
+
new_components << code_example_group unless code_example_group.empty?
|
25
|
+
new_components
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class CommentParser
|
5
|
+
# FieldBlock represents a list of fields obtained by FieldListParser
|
6
|
+
class FieldBlock
|
7
|
+
attr_reader :fields
|
8
|
+
|
9
|
+
def initialize(fields)
|
10
|
+
@fields = fields
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_h
|
14
|
+
{ type: :field_block, contents: fields }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Docrb
|
4
|
+
class CommentParser
|
5
|
+
# FieldListParser parses a field block (representing arguments of an method,
|
6
|
+
# for instance) into a specialised structure.
|
7
|
+
class FieldListParser
|
8
|
+
FIELD_FORMAT_REGEXP = /^([a-z_][0-9a-z_]*:?)\s+- /i
|
9
|
+
|
10
|
+
def initialize(text)
|
11
|
+
@text = text
|
12
|
+
@data = []
|
13
|
+
@current = []
|
14
|
+
@result = {}
|
15
|
+
@dash_index = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def detect
|
19
|
+
@text.each_char do |c|
|
20
|
+
next @current << c unless c == LINE_BREAK
|
21
|
+
return false unless handle_linebreak
|
22
|
+
end
|
23
|
+
return false unless handle_linebreak
|
24
|
+
|
25
|
+
flush_current_field!
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def handle_linebreak
|
30
|
+
return true if @current.empty?
|
31
|
+
|
32
|
+
# Here's a linebreak. Handle it as needed.
|
33
|
+
@current = @current.join
|
34
|
+
if infer_field_alignment(@current)
|
35
|
+
flush_current_field!
|
36
|
+
@data << @current
|
37
|
+
else
|
38
|
+
# This is not a field. May be a continuation.
|
39
|
+
# Can it be a continuation?
|
40
|
+
return false if @data.empty?
|
41
|
+
|
42
|
+
# Yep. It may be. Is it?
|
43
|
+
return false unless continuation?(@current)
|
44
|
+
|
45
|
+
# It is. Append to data.
|
46
|
+
@data << @current.strip
|
47
|
+
end
|
48
|
+
@current = []
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def flush_current_field!
|
53
|
+
return if @data.empty?
|
54
|
+
|
55
|
+
data = @data.join(" ")
|
56
|
+
@data = []
|
57
|
+
field_name = FIELD_FORMAT_REGEXP.match(data)[1]
|
58
|
+
text = data.slice(@dash_index + 1...).strip
|
59
|
+
@result[field_name] = text
|
60
|
+
end
|
61
|
+
|
62
|
+
def continuation?(line)
|
63
|
+
return false unless @dash_index
|
64
|
+
|
65
|
+
# until dash_index, we should have spaces
|
66
|
+
return false if line.length < @dash_index
|
67
|
+
|
68
|
+
return false unless line.slice(0..@dash_index + 1).strip.empty?
|
69
|
+
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
def infer_field_alignment(line)
|
74
|
+
# is the line a field?
|
75
|
+
return false unless FIELD_FORMAT_REGEXP.match?(line)
|
76
|
+
|
77
|
+
if @dash_index
|
78
|
+
# does it match the same alignment?
|
79
|
+
return false if line[@dash_index] != DASH
|
80
|
+
else
|
81
|
+
@dash_index = line.index(DASH)
|
82
|
+
end
|
83
|
+
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
attr_reader :result
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|