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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b897d6327d17f704a62f21f7649a2ae1a4f2643bf7f8f41445c126aafbf77e2
4
- data.tar.gz: d3c11b63175f9f235f459a2c8822d0ee2d08e2762f2cab5273f30c238797db49
3
+ metadata.gz: 8e5bfad3f3b343a1fd2741938d31812feac3218e8b6998d43b3614b6800874b0
4
+ data.tar.gz: 443bcb83342bb51cf3537b9b5cba497b7a1f5340eb7e7fb0956e85746344f917
5
5
  SHA512:
6
- metadata.gz: 421524a636890420cebe721455f2ba348cb514b8b576d125c1baa2d9e474297a2763b3c325d7c8948e7bd12d28276f8e27b5171cfecc0d92be62b1f1e8094f11
7
- data.tar.gz: 7c148e99347aaf86474d376b80e8beb7a35cb96fe211ace49c5ba6a8e8a8bfb40716225c995107407ff3c13c9f3e850f8fc3b83ce9e4e172102b719a2ef48dca
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
- = Asciidoctor Reducer
1
+ = {project-name}
2
2
  Dan Allen <https://github.com/mojavelinux[@mojavelinux]>
3
- v1.0.0.alpha.1, 2022-01-12
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
- Asciidoctor Reducer is a tool that reduces an AsciiDoc document with includes to a single document by expanding the includes reachable from the specified parent document.
17
- If the document does not contain any includes, the tool returns the original source.
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
- To install and run Asciidoctor Reducer, you need Ruby 2.5 or better installed and the Asciidoctor gem.
22
- The instructions for installing the gems is covered in the next section.
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
- Asciidoctor Reducer is published to RubyGems.org as the gem named *asciidoctor-reducer*.
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.
@@ -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
- # Reduces a set of AsciiDoc documents into a single document by expanding all includes reachable from the parent document.
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 an AsciiDoc document with includes to a single AsciiDoc document.
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', 'Set the output filename or stream' do |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 '-h', '--help', 'Display this help text and exit' do
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', %(Display version information and exit) do
32
- $stdout.write %(#{opts.program_name} #{::Asciidoctor::Reducer::VERSION}\n)
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, safe: :safe).source + ?\n
88
+ reduced = (::Asciidoctor.load $stdin, options).source + ?\n
68
89
  else
69
- reduced = (Asciidoctor.load_file input_file, safe: :safe, to_file: false).source + ?\n
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 ::IOError
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 'ext/asciidoctor/preprocessor_reader'
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
- return if doc.options[:reduced]
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.instance_variable_get :@x_include_replacements).empty?
8
- resolved_source_lines = doc.source_lines.dup
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
- # when into is -1, it indicates this is a top-level include
11
- target_lines = (into = it[:into]) < 0 ? resolved_source_lines : inc_replacements[into][:lines]
12
- # adds extra bit of assurance that we're replacing the correct line
13
- if target_lines[(index = it[:index])] == it[:replace]
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module Reducer
5
- VERSION = '1.0.0.alpha.1'
5
+ VERSION = '1.0.0.alpha.5'
6
6
  end
7
7
  end
@@ -3,7 +3,7 @@
3
3
  require 'asciidoctor'
4
4
  require_relative 'reducer/extensions'
5
5
 
6
- Asciidoctor::Extensions.register do
6
+ Asciidoctor::Extensions.register :reducer do
7
7
  preprocessor Asciidoctor::Reducer::Preprocessor
8
8
  tree_processor Asciidoctor::Reducer::TreeProcessor
9
9
  end
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.1
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-01-12 00:00:00.000000000 Z
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 an AsciiDoc document with includes to a single AsciiDoc
56
- document by expanding the includes reachable from the specified parent document.
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 an AsciiDoc document with includes to a single AsciiDoc document.
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