asciidoctor-reducer 1.0.0.alpha.1 → 1.0.0.alpha.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +63 -0
- data/README.adoc +145 -10
- data/asciidoctor-reducer.gemspec +2 -4
- data/lib/asciidoctor/reducer/cli.rb +33 -10
- data/lib/asciidoctor/reducer/preprocessor.rb +2 -10
- data/lib/asciidoctor/reducer/preprocessor_directive_tracker.rb +92 -0
- data/lib/asciidoctor/reducer/tree_processor.rb +20 -11
- data/lib/asciidoctor/reducer/version.rb +1 -1
- data/lib/asciidoctor/reducer.rb +1 -1
- metadata +8 -6
- data/lib/asciidoctor/reducer/ext/asciidoctor/preprocessor_reader.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e5bfad3f3b343a1fd2741938d31812feac3218e8b6998d43b3614b6800874b0
|
4
|
+
data.tar.gz: 443bcb83342bb51cf3537b9b5cba497b7a1f5340eb7e7fb0956e85746344f917
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b52ca3132349b059d672bab08dd1b1c5a2586d88e8f4f31eb8d2ae7f0f9891917f0ee0cd46bd63826e7127cb8a64fda650bc5a13815760d299c06c6e5181e02b
|
7
|
+
data.tar.gz: 6bdb49618d5adb7b1ce702d266ee8d6a229545b6ca8676931083df9362b21aaedabf539c7d1c4ea5c2fabecbb25426a9bcc140d80ffce93cd9b6da52bdc0e4cd
|
data/CHANGELOG.adoc
CHANGED
@@ -4,6 +4,53 @@
|
|
4
4
|
This document provides a high-level view of the changes to the Asciidoctor Reducer by release.
|
5
5
|
For a detailed view of what has changed, refer to the {url-repo}/commits/main[commit history] on GitHub.
|
6
6
|
|
7
|
+
== 1.0.0.alpha.5 (2022-02-06) - @mojavelinux
|
8
|
+
|
9
|
+
=== Changed
|
10
|
+
|
11
|
+
* Removing trailing empty lines after reducing when sourcemap is not enabled
|
12
|
+
* Remove unnecessary override of lineno in preprocess_include_directive override
|
13
|
+
* Simplify how include replacement target is tracked
|
14
|
+
* Classify extensions in group named `:reducer`
|
15
|
+
|
16
|
+
=== Fixed
|
17
|
+
|
18
|
+
* Suppress log messages when reloading document (#14)
|
19
|
+
|
20
|
+
== 1.0.0.alpha.4 (2022-02-03) - @mojavelinux
|
21
|
+
|
22
|
+
=== Fixed
|
23
|
+
|
24
|
+
* Fix replacement of nested empty and unresolved includes
|
25
|
+
|
26
|
+
== 1.0.0.alpha.3 (2022-02-02) - @mojavelinux
|
27
|
+
|
28
|
+
=== Changed
|
29
|
+
|
30
|
+
* Rename PreprocessorReader ext module to PreprocessorReaderTracker
|
31
|
+
* Encapsulate logic to enhance PreprocessorReader inside PreprocessorReaderTracker module
|
32
|
+
* Only reload document if source lines have changed; otherwise, update source lines on reader directly
|
33
|
+
* Change default safe mode for CLI to :unsafe
|
34
|
+
|
35
|
+
== 1.0.0.alpha.2 (2022-01-27) - @mojavelinux
|
36
|
+
|
37
|
+
=== Added
|
38
|
+
|
39
|
+
* Add `-a` / `--attribute` option to CLI for setting an AsciiDoc document attribute at runtime (#6)
|
40
|
+
|
41
|
+
=== Changed
|
42
|
+
|
43
|
+
* Reduce preprocessor conditionals by default; add option (`--preserve-conditionals` / `:preserve_conditionals`) to preserve them (#8)
|
44
|
+
* Don't enable sourcemap automatically (#4)
|
45
|
+
* Don't override logger by default; instead, rely on `:logger` API option to change logger
|
46
|
+
* Add `--log-level` option to CLI to set severity level on logger (#9)
|
47
|
+
* Add `-q` / `--quiet` option to CLI to suppress log messages (#9)
|
48
|
+
* Reserve zero index in include replacements for top-level document
|
49
|
+
|
50
|
+
=== Fixed
|
51
|
+
|
52
|
+
* Preserve return value when overridding `preprocess_include_directive` method
|
53
|
+
|
7
54
|
== 1.0.0.alpha.1 (2022-01-12) - @mojavelinux
|
8
55
|
|
9
56
|
Initial release.
|
@@ -11,3 +58,19 @@ Initial release.
|
|
11
58
|
=== Details
|
12
59
|
|
13
60
|
{url-repo}/releases/tag/v1.0.0.alpha.1[git tag]
|
61
|
+
|
62
|
+
=== Details
|
63
|
+
|
64
|
+
{url-repo}/releases/tag/v1.0.0.alpha.2[git tag]
|
65
|
+
|
66
|
+
=== Details
|
67
|
+
|
68
|
+
{url-repo}/releases/tag/v1.0.0.alpha.3[git tag]
|
69
|
+
|
70
|
+
=== Details
|
71
|
+
|
72
|
+
{url-repo}/releases/tag/v1.0.0.alpha.4[git tag]
|
73
|
+
|
74
|
+
=== Details
|
75
|
+
|
76
|
+
{url-repo}/releases/tag/v1.0.0.alpha.5[git tag]
|
data/README.adoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
=
|
1
|
+
= {project-name}
|
2
2
|
Dan Allen <https://github.com/mojavelinux[@mojavelinux]>
|
3
|
-
v1.0.0.alpha.
|
3
|
+
v1.0.0.alpha.5, 2022-02-06
|
4
4
|
:idprefix:
|
5
5
|
:idseparator: -
|
6
6
|
ifndef::env-github[:icons: font]
|
@@ -11,15 +11,19 @@ ifdef::env-github[]
|
|
11
11
|
:tip-caption: :bulb:
|
12
12
|
:warning-caption: :warning:
|
13
13
|
endif::[]
|
14
|
+
:project-name: Asciidoctor Reducer
|
15
|
+
:project-handle: asciidoctor-reducer
|
14
16
|
:url-rvm: https://rvm.io
|
17
|
+
:url-repo: https://github.com/asciidoctor/{project-handle}
|
15
18
|
|
16
|
-
|
17
|
-
|
19
|
+
{project-name} is a tool that reduces a composite AsciiDoc document containing includes to a single AsciiDoc document by expanding any includes reachable from the parent document.
|
20
|
+
The tool also applies preprocessor conditionals (unless the option to preserve them is specified), leaving behind only the enabled lines.
|
21
|
+
If the document does not contain any preprocessor directives, the tool returns the original source.
|
18
22
|
|
19
23
|
== Prerequisites
|
20
24
|
|
21
|
-
|
22
|
-
|
25
|
+
{project-name} is a Ruby program that you install using Ruby packaging.
|
26
|
+
Therefore, to install and run {project-name}, you need Ruby 2.5 or better installed.
|
23
27
|
|
24
28
|
To check whether you have Ruby installed, and which version, run the following command:
|
25
29
|
|
@@ -30,14 +34,30 @@ We generally recommend using RVM because it allows you to install gems without r
|
|
30
34
|
|
31
35
|
== Installation
|
32
36
|
|
33
|
-
|
37
|
+
{project-name} is published to RubyGems.org as the gem named *{project-handle}*.
|
34
38
|
|
35
39
|
You can install the latest version of the gem using the following command:
|
36
40
|
|
37
41
|
$ gem install asciidoctor-reducer --pre
|
38
42
|
|
39
43
|
Installing this gem makes the `asciidoctor-reducer` command available on your $PATH.
|
40
|
-
You can also require the gem to use it as an Asciidoctor extension.
|
44
|
+
You can also require the gem into the Ruby runtime to use it as an Asciidoctor extension.
|
45
|
+
|
46
|
+
If you prefer to manage the application as a project-scoped dependency, you can declare the gem in the project's [.path]_Gemfile_:
|
47
|
+
|
48
|
+
.Gemfile
|
49
|
+
[,ruby]
|
50
|
+
----
|
51
|
+
source 'https://rubygems.org'
|
52
|
+
|
53
|
+
gem 'asciidoctor-reducer'
|
54
|
+
----
|
55
|
+
|
56
|
+
You then install the gem using the `bundle` command:
|
57
|
+
|
58
|
+
$ bundle --path=.bundle/gems
|
59
|
+
|
60
|
+
Installing the gem this way makes the `bundle exec asciidoctor-reducer` command available on your $PATH.
|
41
61
|
|
42
62
|
== Usage
|
43
63
|
|
@@ -60,7 +80,7 @@ To use the command, pass the AsciiDoc file as the sole argument:
|
|
60
80
|
$ asciidoctor-reducer input.adoc
|
61
81
|
|
62
82
|
By default, the command will output the reduced AsciiDoc document to the terminal (via stdout).
|
63
|
-
To write the output to a file, specify an output file using the `-o` option
|
83
|
+
To write the output to a file, specify an output file using the `-o` option:
|
64
84
|
|
65
85
|
$ asciidoctor-reducer -o output.adoc input.adoc
|
66
86
|
|
@@ -69,7 +89,7 @@ To use the command in this way, pass `-` as the first argument:
|
|
69
89
|
|
70
90
|
$ cat input.adoc | asciidoctor-reducer -
|
71
91
|
|
72
|
-
To write the output to a file, also specify the `-o` option:
|
92
|
+
To write the output to a file instead of stdout, also specify an output file using the `-o` option:
|
73
93
|
|
74
94
|
$ cat input.adoc | asciidoctor-reducer -o output.adoc -
|
75
95
|
|
@@ -101,6 +121,121 @@ puts doc.source
|
|
101
121
|
|
102
122
|
You can write this source to a file to save the reduced document.
|
103
123
|
|
124
|
+
== How it Works
|
125
|
+
|
126
|
+
{project-name} uses a collection of Asciidoctor extensions to rebuild the AsciiDoc source as a single document.
|
127
|
+
|
128
|
+
It starts by using a preprocessor extension to enhance the PreprocessorReader class to be notified each time an include is entered (pushed) or exited (popped).
|
129
|
+
When an include directive is encountered, the enhanced reader stores the resolved lines and location of the include directive, thus keeping track of where those lines should be inserted in the original source.
|
130
|
+
This information is stored as a stack, where each successive entry contains lines to be inserted into a parent entry.
|
131
|
+
The enhanced reader also stores the location of preprocessor conditionals and whether the lines they enclose should be kept or dropped.
|
132
|
+
|
133
|
+
The reducer then uses a tree processor extension to fold the include stack into a single sequence of lines.
|
134
|
+
It does so by working from the end of the stack and inserting the lines into the parent until the stack has been flattened.
|
135
|
+
As it goes, it also removes lines that have been excluded by the preprocessor conditionals as well as the directive lines themselves (unless the option to preserve conditionals has been specified).
|
136
|
+
|
137
|
+
Finally, it loads the document again and returns it.
|
138
|
+
The reduced source is available on the reconstructed document (via `Document#source` or `Document#source_lines`).
|
139
|
+
|
140
|
+
=== Impact on Extensions
|
141
|
+
|
142
|
+
If the sourcemap is enabled, and the reducer finds lines to replace or filter, the reducer will load the document again using `Asciidoctor.load`.
|
143
|
+
This step is necessary to synchronize the sourcemap with the reduced source.
|
144
|
+
This call will cause extensions that run during the load phase to be invoked again.
|
145
|
+
An extension can check for this secondary load by checking for the `:reduced` option in the `Document#options` hash.
|
146
|
+
If this option is set (the value of which will be `true`), then Asciidoctor is loading the reduced document.
|
147
|
+
|
148
|
+
== Development
|
149
|
+
|
150
|
+
Follow the instructions below to learn how to help develop the project or test-drive the development version.
|
151
|
+
|
152
|
+
=== Retrieve the source code
|
153
|
+
|
154
|
+
Copy the {url-repo}[GitHub repository URL] and pass it to the `git clone` command:
|
155
|
+
|
156
|
+
[subs=attributes+]
|
157
|
+
$ git clone {url-repo}
|
158
|
+
|
159
|
+
Next, switch to the project directory:
|
160
|
+
|
161
|
+
[subs=attributes+]
|
162
|
+
$ cd {project-handle}
|
163
|
+
|
164
|
+
=== Install the dependencies
|
165
|
+
|
166
|
+
The dependencies needed to use {project-name} are defined in the [.path]_Gemfile_ at the root of the project.
|
167
|
+
You'll use Bundler to install these dependencies.
|
168
|
+
|
169
|
+
Use the `bundle` command to install the project dependencies under the project directory:
|
170
|
+
|
171
|
+
$ bundle --path=.bundle/gems
|
172
|
+
|
173
|
+
You must invoke `bundle` from the project's root directory so it can locate the [.path]_Gemfile_.
|
174
|
+
|
175
|
+
=== Run the tests
|
176
|
+
|
177
|
+
The test suite is located in the [.path]_spec_ directory.
|
178
|
+
The tests are based on RSpec.
|
179
|
+
|
180
|
+
==== Run all tests
|
181
|
+
|
182
|
+
You can run all of the tests using Rake:
|
183
|
+
|
184
|
+
$ bundle exec spec
|
185
|
+
|
186
|
+
For more fine-grained control, you can also run the tests directly using RSpec:
|
187
|
+
|
188
|
+
$ bundle exec rspec
|
189
|
+
|
190
|
+
To run all tests in a single spec, point RSpec at the spec file:
|
191
|
+
|
192
|
+
$ bundle exec rspec spec/asciidoctor_reducer_spec.rb
|
193
|
+
|
194
|
+
==== Run specific tests
|
195
|
+
|
196
|
+
If you only want to run a single test, or a group of tests, you can do so by tagging the test cases, then filtering the test run using that tag.
|
197
|
+
|
198
|
+
Start by adding the `only` tag to one or more specifications:
|
199
|
+
|
200
|
+
[source,ruby]
|
201
|
+
----
|
202
|
+
it 'should do something new', only: true do
|
203
|
+
expect(true).to be true
|
204
|
+
end
|
205
|
+
----
|
206
|
+
|
207
|
+
Next, run RSpec with the `only` flag enabled:
|
208
|
+
|
209
|
+
$ bundle exec rspec -t only
|
210
|
+
|
211
|
+
RSpec will only run the specifications that contain this flag.
|
212
|
+
|
213
|
+
You can also filter tests by keyword.
|
214
|
+
Let's assume we want to run all the tests that have `leveloffset` in the description.
|
215
|
+
Run RSpec with the example filter:
|
216
|
+
|
217
|
+
$ bundle exec rspec -e leveloffset
|
218
|
+
|
219
|
+
RSpec will only run the specifications that have a description containing the text `leveloffset`.
|
220
|
+
|
221
|
+
=== Generate code coverage
|
222
|
+
|
223
|
+
To generate a code coverage report when running tests using simplecov, set the `COVERAGE` environment variable as follows when running the tests:
|
224
|
+
|
225
|
+
$ COVERAGE=deep bundle exec rake
|
226
|
+
|
227
|
+
You'll see a total coverage score, a detailed coverage report, and a link to HTML report in the output.
|
228
|
+
The HTML report helps you understand which lines and branches were missed, if any.
|
229
|
+
|
230
|
+
=== Run the development version
|
231
|
+
|
232
|
+
When running the `asciidoctor-reducer` command from source, you must prefix the command with `bundle exec`:
|
233
|
+
|
234
|
+
[subs=attributes+]
|
235
|
+
$ bundle exec asciidoctor-reducer sample.adoc
|
236
|
+
|
237
|
+
To avoid having to do this, or make the `asciidoctor-reducer` command available from anywhere, you need to build the development gem and install it.
|
238
|
+
|
104
239
|
== Copyright and License
|
105
240
|
|
106
241
|
Copyright (C) 2021-present Dan Allen.
|
data/asciidoctor-reducer.gemspec
CHANGED
@@ -7,10 +7,8 @@ end
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = 'asciidoctor-reducer'
|
9
9
|
s.version = Asciidoctor::Reducer::VERSION
|
10
|
-
|
11
|
-
|
12
|
-
s.summary = 'Reduces an AsciiDoc document with includes to a single AsciiDoc document.'
|
13
|
-
s.description = 'A tool that reduces an AsciiDoc document with includes to a single AsciiDoc document by expanding the includes reachable from the specified parent document.'
|
10
|
+
s.summary = 'Reduces a composite AsciiDoc document containing includes and conditionals to a single AsciiDoc document.'
|
11
|
+
s.description = 'A tool that reduces a composite AsciiDoc document containing preprocessor directives (includes and conditionals) to a single AsciiDoc document by expanding the includes and applying the conditionals.'
|
14
12
|
s.authors = ['Dan Allen']
|
15
13
|
s.email = 'dan.j.allen@gmail.com'
|
16
14
|
s.homepage = 'https://asciidoctor.org'
|
@@ -8,28 +8,48 @@ module Asciidoctor::Reducer
|
|
8
8
|
|
9
9
|
class Cli
|
10
10
|
def parse args
|
11
|
-
options = {}
|
11
|
+
options = { attributes: {}, safe: :unsafe }
|
12
12
|
|
13
13
|
opt_parser = ::OptionParser.new do |opts|
|
14
14
|
opts.program_name = 'asciidoctor-reducer'
|
15
15
|
opts.banner = <<~EOS
|
16
16
|
Usage: #{opts.program_name} [OPTION]... FILE
|
17
17
|
|
18
|
-
Reduces
|
18
|
+
Reduces a composite AsciiDoc document containing includes and conditionals to a single AsciiDoc document.
|
19
19
|
|
20
20
|
EOS
|
21
21
|
|
22
|
-
opts.on '-o FILE', '--output=FILE', '
|
22
|
+
opts.on '-o FILE', '--output=FILE', 'set the output filename or stream' do |file|
|
23
23
|
options[:output_file] = file
|
24
24
|
end
|
25
25
|
|
26
|
-
opts.on '-
|
26
|
+
opts.on '-a KEY[=VALUE]', '--attribute=KEY[=VALUE]',
|
27
|
+
'set a document attribute in the AsciiDoc document: [key, key!, key=value]' do |attr|
|
28
|
+
key, val = attr.split '=', 2
|
29
|
+
val ||= ''
|
30
|
+
options[:attributes][key] = val
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on '--preserve-conditionals', 'preserve preprocessor conditional directives in the reduced source' do
|
34
|
+
options[:preserve_conditionals] = true
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on '--log-level LEVEL', %w(debug info warn error fatal),
|
38
|
+
'set the minimum level of messages to log: [debug, info, warn, error, fatal] (default: warn)' do |level|
|
39
|
+
(options[:logger] = ::Asciidoctor::Logger.new $stderr).level = level unless level == 'warn'
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on '-q', '--quiet', 'suppress all application log messages' do
|
43
|
+
options[:logger] = ::Asciidoctor::NullLogger.new
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on '-h', '--help', 'display this help text and exit' do
|
27
47
|
$stdout.write opts.help
|
28
48
|
return 0
|
29
49
|
end
|
30
50
|
|
31
|
-
opts.on '-v', '--version',
|
32
|
-
$stdout.write %(#{opts.program_name} #{
|
51
|
+
opts.on '-v', '--version', 'display the version information and exit' do
|
52
|
+
$stdout.write %(#{opts.program_name} #{VERSION}\n)
|
33
53
|
return 0
|
34
54
|
end
|
35
55
|
end
|
@@ -56,6 +76,7 @@ module Asciidoctor::Reducer
|
|
56
76
|
end
|
57
77
|
|
58
78
|
def self.run args = ARGV
|
79
|
+
old_logger = ::Asciidoctor::LoggerManager.logger
|
59
80
|
code, options = new.parse (Array args)
|
60
81
|
return code unless code == 0 && options
|
61
82
|
if (output_file = options.delete :output_file) == '-'
|
@@ -64,15 +85,17 @@ module Asciidoctor::Reducer
|
|
64
85
|
(to = ::Pathname.new output_file).dirname.mkpath
|
65
86
|
end
|
66
87
|
if (input_file = options.delete :input_file) == '-'
|
67
|
-
reduced = (Asciidoctor.load $stdin,
|
88
|
+
reduced = (::Asciidoctor.load $stdin, options).source + ?\n
|
68
89
|
else
|
69
|
-
reduced = (Asciidoctor.load_file input_file,
|
90
|
+
reduced = (::Asciidoctor.load_file input_file, (options.merge to_file: false)).source + ?\n
|
70
91
|
end
|
71
|
-
Pathname === to ? (to.write reduced, encoding: ::Encoding::UTF_8) : (to.write reduced)
|
92
|
+
::Pathname === to ? (to.write reduced, encoding: ::Encoding::UTF_8) : (to.write reduced)
|
72
93
|
0
|
73
|
-
rescue
|
94
|
+
rescue
|
74
95
|
$stderr.write %(asciidoctor-reducer: #{$!.message}\n)
|
75
96
|
1
|
97
|
+
ensure
|
98
|
+
::Asciidoctor::LoggerManager.logger = old_logger
|
76
99
|
end
|
77
100
|
end
|
78
101
|
end
|
@@ -1,19 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'preprocessor_directive_tracker'
|
4
4
|
|
5
5
|
module Asciidoctor::Reducer
|
6
6
|
class Preprocessor < ::Asciidoctor::Extensions::Preprocessor
|
7
7
|
def process doc, reader
|
8
|
-
|
9
|
-
doc.sourcemap = true
|
10
|
-
# Q: is there a better place we can store the original logger?
|
11
|
-
::Asciidoctor::LoggerManager.instance_variable_set :@original_logger, ::Asciidoctor::LoggerManager.logger
|
12
|
-
::Asciidoctor::LoggerManager.logger = ::Asciidoctor::NullLogger.new
|
13
|
-
reader.singleton_class.prepend AsciidoctorExt::PreprocessorReader
|
14
|
-
reader.instance_variable_set :@x_include_replacements, []
|
15
|
-
reader.instance_variable_set :@x_parents, [-1]
|
16
|
-
nil
|
8
|
+
doc.options[:reduced] ? reader : (reader.extend PreprocessorDirectiveTracker)
|
17
9
|
end
|
18
10
|
end
|
19
11
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor::Reducer
|
4
|
+
module PreprocessorDirectiveTracker
|
5
|
+
attr_writer :source_lines
|
6
|
+
attr_reader :x_include_replacements
|
7
|
+
|
8
|
+
def self.extended instance
|
9
|
+
instance.instance_variable_set :@x_include_replacements, ([{ drop: [] }].extend CurrentPosition)
|
10
|
+
instance.instance_variable_set :@x_include_directive_line, nil
|
11
|
+
instance.instance_variable_set :@x_include_pushed, nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def preprocess_conditional_directive keyword, target, delimiter, text
|
15
|
+
return super if (opts = @document.options)[:preserve_conditionals] || opts[:reduced]
|
16
|
+
skip_active = @skipping
|
17
|
+
depth = @conditional_stack.length
|
18
|
+
cond_lineno = @lineno - 1
|
19
|
+
result = super
|
20
|
+
return result if @skipping && skip_active
|
21
|
+
drop = @x_include_replacements.current[:drop]
|
22
|
+
if (depth_change = @conditional_stack.length - depth) < 0
|
23
|
+
if skip_active
|
24
|
+
drop.push(*(drop.pop..cond_lineno))
|
25
|
+
else
|
26
|
+
drop << cond_lineno
|
27
|
+
end
|
28
|
+
elsif depth_change > 0 || cond_lineno == @lineno - 1
|
29
|
+
drop << cond_lineno
|
30
|
+
else
|
31
|
+
drop << [cond_lineno, text]
|
32
|
+
end
|
33
|
+
result
|
34
|
+
end
|
35
|
+
|
36
|
+
def preprocess_include_directive target, attrlist
|
37
|
+
@x_include_directive_line = %(include::#{target}[#{attrlist}])
|
38
|
+
@x_include_pushed = false
|
39
|
+
inc_lineno = @lineno - 1 # we're currently on the include line, which is 1-based
|
40
|
+
result = super
|
41
|
+
if @x_include_pushed
|
42
|
+
@x_include_directive_line = @x_include_pushed = nil
|
43
|
+
return result
|
44
|
+
end
|
45
|
+
inc_lines = ((line = lines[0].to_s).start_with? 'Unresolved directive in ') && (line.end_with? ']') ? [line] : []
|
46
|
+
push_include_replacement inc_lineno, inc_lines
|
47
|
+
@x_include_directive_line = @x_include_pushed = nil
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
def push_include data, file, path, lineno, attrs
|
52
|
+
@x_include_pushed = true
|
53
|
+
inc_lineno = @lineno - 2 # we're below the include line, which is 1-based
|
54
|
+
prev_inc_depth = @include_stack.length
|
55
|
+
result = super
|
56
|
+
inc_lines = lines if @include_stack.length > prev_inc_depth
|
57
|
+
push_include_replacement inc_lineno, inc_lines
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
def pop_include
|
62
|
+
@x_include_replacements.pos = @x_include_replacements.current[:into] unless @x_include_pushed
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def push_include_replacement inc_lineno, inc_lines
|
69
|
+
@x_include_replacements << {
|
70
|
+
into: @x_include_replacements.pos,
|
71
|
+
lineno: inc_lineno,
|
72
|
+
line: @x_include_directive_line,
|
73
|
+
lines: inc_lines || [],
|
74
|
+
drop: [],
|
75
|
+
}
|
76
|
+
@x_include_replacements.pos = @x_include_replacements.length - 1 if inc_lines
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module CurrentPosition
|
82
|
+
attr_accessor :pos
|
83
|
+
|
84
|
+
def self.extended instance
|
85
|
+
instance.pos = instance.length - 1
|
86
|
+
end
|
87
|
+
|
88
|
+
def current
|
89
|
+
self[@pos]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -4,21 +4,30 @@ module Asciidoctor::Reducer
|
|
4
4
|
class TreeProcessor < ::Asciidoctor::Extensions::TreeProcessor
|
5
5
|
def process doc
|
6
6
|
return if doc.options[:reduced]
|
7
|
-
unless (inc_replacements = doc.reader.
|
8
|
-
|
7
|
+
unless (inc_replacements = doc.reader.x_include_replacements).length == 1 && inc_replacements[0][:drop].empty?
|
8
|
+
inc_replacements[0][:lines] = doc.source_lines.dup
|
9
9
|
inc_replacements.reverse_each do |it|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
target_lines[index..index] = it[:lines]
|
10
|
+
if (into = it[:into])
|
11
|
+
target_lines = inc_replacements[into][:lines]
|
12
|
+
# adds extra bit of assurance that we're replacing the correct line
|
13
|
+
next unless target_lines[(index = it[:lineno])] == it[:line]
|
15
14
|
end
|
15
|
+
lines = it[:lines]
|
16
|
+
unless (drop = it[:drop]).empty?
|
17
|
+
drop.reverse_each {|idx| ::Array === idx ? (lines[idx[0]] = idx[1]) : (lines.delete_at idx) }
|
18
|
+
end
|
19
|
+
target_lines[index] = lines if target_lines
|
20
|
+
end
|
21
|
+
source_lines = inc_replacements[0][:lines].flatten
|
22
|
+
if doc.sourcemap
|
23
|
+
logger = ::Asciidoctor::LoggerManager.logger
|
24
|
+
doc = ::Asciidoctor.load source_lines, (doc.options.merge logger: false, reduced: true)
|
25
|
+
::Asciidoctor::LoggerManager.logger = logger
|
26
|
+
else
|
27
|
+
source_lines.pop while (last = source_lines[-1]) && last.empty?
|
28
|
+
doc.reader.source_lines = source_lines
|
16
29
|
end
|
17
|
-
# WARNING: if include directives remain that can still be resolved, the sourcemap won't match the source lines
|
18
|
-
doc = ::Asciidoctor.load resolved_source_lines, (doc.options.merge sourcemap: true, reduced: true)
|
19
30
|
end
|
20
|
-
::Asciidoctor::LoggerManager.logger = ::Asciidoctor::LoggerManager.instance_variable_get :@original_logger
|
21
|
-
::Asciidoctor::LoggerManager.remove_instance_variable :@original_logger
|
22
31
|
doc
|
23
32
|
end
|
24
33
|
end
|
data/lib/asciidoctor/reducer.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: asciidoctor-reducer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.alpha.
|
4
|
+
version: 1.0.0.alpha.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Allen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: asciidoctor
|
@@ -52,8 +52,9 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 3.10.0
|
55
|
-
description: A tool that reduces
|
56
|
-
|
55
|
+
description: A tool that reduces a composite AsciiDoc document containing preprocessor
|
56
|
+
directives (includes and conditionals) to a single AsciiDoc document by expanding
|
57
|
+
the includes and applying the conditionals.
|
57
58
|
email: dan.j.allen@gmail.com
|
58
59
|
executables:
|
59
60
|
- asciidoctor-reducer
|
@@ -68,9 +69,9 @@ files:
|
|
68
69
|
- lib/asciidoctor-reducer.rb
|
69
70
|
- lib/asciidoctor/reducer.rb
|
70
71
|
- lib/asciidoctor/reducer/cli.rb
|
71
|
-
- lib/asciidoctor/reducer/ext/asciidoctor/preprocessor_reader.rb
|
72
72
|
- lib/asciidoctor/reducer/extensions.rb
|
73
73
|
- lib/asciidoctor/reducer/preprocessor.rb
|
74
|
+
- lib/asciidoctor/reducer/preprocessor_directive_tracker.rb
|
74
75
|
- lib/asciidoctor/reducer/tree_processor.rb
|
75
76
|
- lib/asciidoctor/reducer/version.rb
|
76
77
|
homepage: https://asciidoctor.org
|
@@ -99,5 +100,6 @@ requirements: []
|
|
99
100
|
rubygems_version: 3.2.32
|
100
101
|
signing_key:
|
101
102
|
specification_version: 4
|
102
|
-
summary: Reduces
|
103
|
+
summary: Reduces a composite AsciiDoc document containing includes and conditionals
|
104
|
+
to a single AsciiDoc document.
|
103
105
|
test_files: []
|
@@ -1,62 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Asciidoctor::Reducer
|
4
|
-
module AsciidoctorExt
|
5
|
-
module PreprocessorReader
|
6
|
-
def preprocess_include_directive target, attrlist
|
7
|
-
@x_include_directive_line = %(include::#{target}[#{attrlist}])
|
8
|
-
@x_push_include_called = false
|
9
|
-
inc_lineno = @lineno - 1 # we're currently on the include line, which is 1-based
|
10
|
-
result = super
|
11
|
-
return result if @x_push_include_called
|
12
|
-
parent_depth = (parents = @x_parents).length
|
13
|
-
depth_change = @include_stack.length - (parent_depth - 1)
|
14
|
-
parent_depth -= (parents.slice! parent_depth + depth_change, -depth_change).length if depth_change < 0
|
15
|
-
lines = ((line = @lines[-1].to_s).start_with? 'Unresolved directive in ') && (line.end_with? ']') ? [line] : []
|
16
|
-
@x_include_replacements << {
|
17
|
-
lines: lines,
|
18
|
-
into: parents[parent_depth - 1],
|
19
|
-
index: inc_lineno,
|
20
|
-
replace: @x_include_directive_line,
|
21
|
-
}
|
22
|
-
end
|
23
|
-
|
24
|
-
def push_include data, file, path, lineno, attrs
|
25
|
-
@x_push_include_called = true
|
26
|
-
inc_lineno = @lineno - 2 # we're below the include line, which is 1-based
|
27
|
-
prev_inc_depth = @include_stack.length
|
28
|
-
# Q: can we do this without resetting the lineno?
|
29
|
-
lineno = 1 # rubocop:disable Lint/ShadowedArgument
|
30
|
-
super
|
31
|
-
inc_depth = @include_stack.length
|
32
|
-
parent_depth = (parents = @x_parents).length
|
33
|
-
# push_include did not push to the stack
|
34
|
-
if inc_depth == prev_inc_depth
|
35
|
-
depth_change = inc_depth - (parent_depth - 1)
|
36
|
-
parent_depth -= (parents.slice! parent_depth + depth_change, -depth_change).length if depth_change < 0
|
37
|
-
@x_include_replacements << {
|
38
|
-
lines: [],
|
39
|
-
into: parents[parent_depth - 1],
|
40
|
-
index: inc_lineno,
|
41
|
-
replace: @x_include_directive_line,
|
42
|
-
}
|
43
|
-
else
|
44
|
-
depth_change = inc_depth - parent_depth
|
45
|
-
if depth_change > 0
|
46
|
-
parents << @x_include_replacements.length.pred
|
47
|
-
parent_depth += 1
|
48
|
-
elsif depth_change < 0
|
49
|
-
parent_depth -= (parents.slice! parent_depth + depth_change, -depth_change).length
|
50
|
-
end
|
51
|
-
@x_include_replacements << {
|
52
|
-
lines: @lines.reverse,
|
53
|
-
into: parents[parent_depth - 1],
|
54
|
-
index: inc_lineno,
|
55
|
-
replace: @x_include_directive_line,
|
56
|
-
}
|
57
|
-
end
|
58
|
-
self
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|