paru 1.5.0 → 1.5.1

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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/lib/paru/error.rb +6 -4
  3. data/lib/paru/filter/ast_manipulation.rb +90 -91
  4. data/lib/paru/filter/attr.rb +75 -69
  5. data/lib/paru/filter/block.rb +15 -14
  6. data/lib/paru/filter/block_quote.rb +14 -12
  7. data/lib/paru/filter/bullet_list.rb +17 -16
  8. data/lib/paru/filter/caption.rb +50 -48
  9. data/lib/paru/filter/cell.rb +52 -50
  10. data/lib/paru/filter/citation.rb +53 -51
  11. data/lib/paru/filter/cite.rb +34 -33
  12. data/lib/paru/filter/code.rb +51 -49
  13. data/lib/paru/filter/code_block.rb +76 -76
  14. data/lib/paru/filter/col_spec.rb +58 -56
  15. data/lib/paru/filter/definition_list.rb +51 -52
  16. data/lib/paru/filter/definition_list_item.rb +45 -43
  17. data/lib/paru/filter/div.rb +37 -35
  18. data/lib/paru/filter/document.rb +112 -115
  19. data/lib/paru/filter/emph.rb +7 -5
  20. data/lib/paru/filter/empty_block.rb +17 -16
  21. data/lib/paru/filter/empty_inline.rb +23 -22
  22. data/lib/paru/filter/figure.rb +41 -39
  23. data/lib/paru/filter/header.rb +41 -39
  24. data/lib/paru/filter/horizontal_rule.rb +7 -5
  25. data/lib/paru/filter/image.rb +13 -12
  26. data/lib/paru/filter/inline.rb +27 -26
  27. data/lib/paru/filter/inner_markdown.rb +60 -62
  28. data/lib/paru/filter/int_value.rb +19 -18
  29. data/lib/paru/filter/line_block.rb +13 -11
  30. data/lib/paru/filter/line_break.rb +7 -5
  31. data/lib/paru/filter/link.rb +34 -33
  32. data/lib/paru/filter/list.rb +37 -37
  33. data/lib/paru/filter/list_attributes.rb +52 -51
  34. data/lib/paru/filter/math.rb +66 -64
  35. data/lib/paru/filter/meta.rb +40 -39
  36. data/lib/paru/filter/meta_blocks.rb +7 -5
  37. data/lib/paru/filter/meta_bool.rb +7 -5
  38. data/lib/paru/filter/meta_inlines.rb +9 -7
  39. data/lib/paru/filter/meta_list.rb +7 -5
  40. data/lib/paru/filter/meta_map.rb +50 -49
  41. data/lib/paru/filter/meta_string.rb +7 -6
  42. data/lib/paru/filter/meta_value.rb +26 -25
  43. data/lib/paru/filter/metadata.rb +150 -88
  44. data/lib/paru/filter/node.rb +400 -406
  45. data/lib/paru/filter/note.rb +29 -29
  46. data/lib/paru/filter/null.rb +7 -5
  47. data/lib/paru/filter/ordered_list.rb +50 -49
  48. data/lib/paru/filter/para.rb +21 -20
  49. data/lib/paru/filter/plain.rb +23 -21
  50. data/lib/paru/filter/quoted.rb +28 -26
  51. data/lib/paru/filter/short_caption.rb +7 -5
  52. data/lib/paru/filter/small_caps.rb +8 -7
  53. data/lib/paru/filter/soft_break.rb +7 -5
  54. data/lib/paru/filter/space.rb +7 -5
  55. data/lib/paru/filter/span.rb +29 -27
  56. data/lib/paru/filter/str.rb +33 -32
  57. data/lib/paru/filter/strikeout.rb +7 -6
  58. data/lib/paru/filter/strong.rb +7 -6
  59. data/lib/paru/filter/subscript.rb +7 -6
  60. data/lib/paru/filter/superscript.rb +7 -6
  61. data/lib/paru/filter/table.rb +201 -210
  62. data/lib/paru/filter/table_body.rb +67 -67
  63. data/lib/paru/filter/table_end.rb +53 -55
  64. data/lib/paru/filter/table_foot.rb +8 -7
  65. data/lib/paru/filter/table_head.rb +8 -7
  66. data/lib/paru/filter/target.rb +29 -27
  67. data/lib/paru/filter/underline.rb +7 -5
  68. data/lib/paru/filter/value.rb +74 -75
  69. data/lib/paru/filter/version.rb +23 -22
  70. data/lib/paru/filter.rb +355 -331
  71. data/lib/paru/filter_error.rb +7 -5
  72. data/lib/paru/info.rb +29 -30
  73. data/lib/paru/pandoc.rb +241 -248
  74. data/lib/paru/pandoc2yaml.rb +51 -42
  75. data/lib/paru/selector.rb +193 -184
  76. data/lib/paru.rb +3 -1
  77. metadata +4 -73
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #--
2
- # Copyright 2015, 2016, 2017 Huub de Beer <Huub@heerdebeer.org>
4
+ # Copyright 2015--2025 Huub de Beer <Huub@heerdebeer.org>
3
5
  #
4
6
  # This file is part of Paru
5
7
  #
@@ -16,99 +18,159 @@
16
18
  # You should have received a copy of the GNU General Public License
17
19
  # along with Paru. If not, see <http://www.gnu.org/licenses/>.
18
20
  #++
19
- require "yaml"
20
- require_relative "../pandoc.rb"
21
- require_relative "../filter_error.rb"
21
+ require 'yaml'
22
+ require_relative '../pandoc'
23
+ require_relative '../filter_error'
22
24
 
23
25
  module Paru
24
- module PandocFilter
25
- # A Metadata object is a Ruby Hash representation of a pandoc metadata
26
- # node.
27
- class Metadata < Hash
28
-
29
- # Create a new Metadata object based on the contents.
30
- #
31
- # @param contents [MetaMap|String|Hash] the initial contents of this
32
- # metadata. If contents is a String, it is treated as a YAML string
33
- # and converted to a Hash first.
34
- #
35
- # @raise Error when converting contents to a Hash fails
36
- def initialize(contents = {})
37
- if not contents.is_a? Hash
38
- # If not a Hash, it is either a YAML string or can be
39
- # converted to a YAML string
40
- if contents.is_a? PandocFilter::MetaMap
41
- yaml_string = meta2yaml contents
42
- elsif contents.is_a? String
43
- yaml_string = contents
44
- else
45
- raise FilterError.new("Expected a Hash, MetaMap, or String, got '#{contents}' instead.")
46
- end
47
-
48
- # Try to convert the YAML string to a Hash
49
- if yaml_string.empty?
50
- contents = {}
51
- else
52
- contents = YAML.safe_load yaml_string, permitted_classes: [Date]
53
- end
54
-
55
- if not contents
56
- # Error parsing YAML
57
- raise FilterError.new("Unable to convert YAML string '#{yaml_string}' to a Hash.")
58
- end
59
- end
60
-
61
- # Merge the contents with this newly created Metadata
62
- contents.each do |key, value|
63
- self[key] = value
64
- end
65
- end
26
+ module PandocFilter
27
+ # A Metadata object is a Ruby Hash representation of a pandoc metadata
28
+ # node.
29
+ class Metadata < Hash
30
+ # Create a new Metadata object based on the contents.
31
+ #
32
+ # @param contents [MetaMap|String|Hash] the initial contents of this
33
+ # metadata. If contents is a String, it is treated as a YAML string
34
+ # and converted to a Hash first.
35
+ # @param treat_metadata_strings_as_plain_strings [Boolean = false] treat
36
+ # metadata string values as plain strings instead of markdown strings if
37
+ # all AST leaf metadata string values have pandoc type "MetaString". This
38
+ # option is only relevant when you **only** set metadata string values via
39
+ # command-line option `--metadata` and not also via a YAML or title block.
40
+ # Using this option improves performance in this specific situation
41
+ # because metadata values don't have to be converted to string by pandoc
42
+ # in a separate process but can be collected as is.
43
+ #
44
+ # @raise Error when converting contents to a Hash fails
45
+ def initialize(contents = {}, treat_metadata_strings_as_plain_strings: false)
46
+ # When feature toggle treat_metadata_strings_as_plain_strings is on,
47
+ # try to map the MetaMap node to a Hash directly and use that as the
48
+ # contents of this Metadata object. We can only map the MetaMap node
49
+ # when all string values have pandoc type "MetaString".
50
+ if treat_metadata_strings_as_plain_strings && contents.is_a?(PandocFilter::MetaMap)
51
+ metadata_hash = meta_to_value(contents)
52
+ contents = metadata_hash unless metadata_hash.nil?
53
+ end
66
54
 
67
- # Convert this Metadata to a pandoc AST representation of
68
- # metadata: {PandocFilter::Meta}
69
- #
70
- # @return [Meta] the pandoc AST representation as a {PandocFilter::Meta} node
71
- def to_meta()
72
- if self.empty?
73
- PandocFilter::Meta.new {}
74
- else
75
- begin
76
- yaml_string = "#{clean_hash.to_yaml}..."
77
- yaml2json = Paru::Pandoc.new {from "markdown"; to "json"}
78
- json_string = yaml2json << yaml_string
79
- meta_doc = PandocFilter::Document.from_JSON json_string
80
- meta_doc.meta
81
- rescue
82
- end
83
- end
84
- end
55
+ contents = convert_to_hash_via_yaml(contents) unless contents.is_a?(Hash)
85
56
 
86
- private
87
-
88
- # Convert a {PandocFilter::Meta} node to a Metadata
89
- #
90
- # @param meta [Meta|MetaMap] the {PandocFilter::Meta} node to convert to a
91
- # MetadataHash
92
- def meta2yaml(meta)
93
- begin
94
- json2yaml = Paru::Pandoc.new {from "json"; to "markdown"; standalone}
95
- meta = PandocFilter::Meta.from_meta_map(meta) unless meta.is_a? PandocFilter::Meta
96
- meta_doc = PandocFilter::Document.new(PandocFilter::CURRENT_PANDOC_VERSION, meta.to_ast, [])
97
- yaml_string = json2yaml << meta_doc.to_JSON
98
- yaml_string.strip
99
- rescue
100
- end
101
- end
57
+ # Merge the contents with this newly created Metadata
58
+ super()
59
+
60
+ contents.each do |key, value|
61
+ self[key] = value
62
+ end
63
+ end
102
64
 
103
- # Create a true Hash from this Metadata to prevent the +to_yaml+
104
- # method from mixing in the name of this class and confusing pandoc
105
- def clean_hash
106
- hash = {}
107
- each do |key, value|
108
- hash[key] = value
109
- end
110
- hash
65
+ # Convert this Metadata to a pandoc AST representation of
66
+ # metadata: {PandocFilter::Meta}
67
+ #
68
+ # @return [Meta] the pandoc AST representation as a {PandocFilter::Meta} node
69
+ def to_meta
70
+ if empty?
71
+ PandocFilter::Meta.new({})
72
+ else
73
+ begin
74
+ yaml_string = "#{clean_hash.to_yaml}..."
75
+ yaml2json = Paru::Pandoc.new do
76
+ from 'markdown'
77
+ to 'json'
111
78
  end
79
+ json_string = yaml2json << yaml_string
80
+ meta_doc = PandocFilter::Document.from_JSON json_string
81
+ meta_doc.meta
82
+ rescue StandardError
83
+ # Ignore silently to not interfere with pandoc conversion
84
+ end
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ # Convert a {PandocFilter::Meta} node to a Metadata
91
+ #
92
+ # @param meta [Meta|MetaMap] the {PandocFilter::Meta} node to convert to a
93
+ # MetadataHash
94
+ def meta2yaml(meta)
95
+ json2yaml = Paru::Pandoc.new do
96
+ from 'json'
97
+ to 'markdown'
98
+ standalone
99
+ end
100
+ meta = PandocFilter::Meta.from_meta_map(meta) unless meta.is_a? PandocFilter::Meta
101
+ meta_doc = PandocFilter::Document.new(PandocFilter::CURRENT_PANDOC_VERSION, meta.to_ast, [])
102
+ yaml_string = json2yaml << meta_doc.to_JSON
103
+ yaml_string.strip
104
+ rescue StandardError
105
+ # Ignore silently to not interfere with pandoc conversion
106
+ end
107
+
108
+ # Create a true Hash from this Metadata to prevent the +to_yaml+
109
+ # method from mixing in the name of this class and confusing pandoc
110
+ def clean_hash
111
+ hash = {}
112
+ each do |key, value|
113
+ hash[key] = value
114
+ end
115
+ hash
116
+ end
117
+
118
+ def convert_to_hash_via_yaml(contents)
119
+ # If not a Hash, it is either a YAML string or can be
120
+ # converted to a YAML string
121
+ if contents.is_a? PandocFilter::MetaMap
122
+ yaml_string = meta2yaml contents
123
+ elsif contents.is_a? String
124
+ yaml_string = contents
125
+ else
126
+ raise FilterError, "Expected a Hash, MetaMap, or String, got '#{contents}' instead."
127
+ end
128
+
129
+ # Try to convert the YAML string to a Hash
130
+ contents = if yaml_string.empty?
131
+ {}
132
+ else
133
+ YAML.safe_load yaml_string, permitted_classes: [Date]
134
+ end
135
+
136
+ unless contents
137
+ # Error parsing YAML
138
+ raise FilterError, "Unable to convert YAML string '#{yaml_string}' to a Hash."
139
+ end
140
+
141
+ contents
142
+ end
143
+
144
+ def meta_to_value(value)
145
+ case value
146
+ when MetaBlocks, MetaInlines
147
+ # MetaBlocks and MetaInlines represent pandoc formatted markdown
148
+ # nodes. Needs to be converted by pandoc.
149
+ nil
150
+ when MetaValue
151
+ value.value.to_s
152
+ when MetaList
153
+ value.map do |m|
154
+ mapped = meta_to_value(m)
155
+
156
+ return nil if mapped.nil?
157
+
158
+ mapped
159
+ end
160
+ when MetaMap
161
+ mapped_map = {}
162
+
163
+ value.each do |k, m|
164
+ mapped = meta_to_value(m)
165
+
166
+ return nil if mapped.nil?
167
+
168
+ mapped_map[k] = mapped
169
+ end
170
+
171
+ mapped_map
112
172
  end
173
+ end
113
174
  end
175
+ end
114
176
  end