metanorma-plugin-glossarist 0.3.9 → 0.3.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c239554930b872ac67bfa16144dbc77cabd558ff26f623c0b4cdfe53d56541c1
4
- data.tar.gz: 03a9282d88ea2ed4087d70bc23d4cd41d2e31327890d0c427ca48d4e403a3a65
3
+ metadata.gz: 0e98a4e95056510d3c6071f7c46e4bd378b2898e7fa75b44e14d5ca423efc4ca
4
+ data.tar.gz: ebbf812935c1fb98b523675892813de831c1ec87d152d0de78d1fd9103d6342d
5
5
  SHA512:
6
- metadata.gz: e1fcb83d207dfc99e857e4648cc69033c22072661d60081e9f001399666aad2534b84a9efafc95f3cdd324bcaa8446de37bb19ad2b9efb364c107a349d7cb27b
7
- data.tar.gz: ebb50f86f097af9e82c93fc8f4112971ba407b66a793e9ed662ea9151eca3e91105e62fbded44a42a9d8cf86b6d738bd57e5331be84f1f2a2a657bb992cc77e7
6
+ metadata.gz: 99d6b54e76a006d51f7f657e5d408139e0dbcb1af14545f17d8f08f157d7c197c52a88934c84238560d5860dc0131fe78b4b4933c155934aea11410239a8cf6e
7
+ data.tar.gz: 34f5d1c25bb615c2a2a3efdac7710b9dd8e06d0c98c4f63bc4f8846475a952715fdf2269ca4dbc970a9bd5e0bdc17327f9f8204ef2c3a5ba42ed30a75d299747
@@ -15,6 +15,7 @@ module Metanorma
15
15
  BLOCK_REGEX = /^\[glossarist,(.+?),(.+?)\]$/m
16
16
  BIBLIOGRAPHY_REGEX = /^glossarist::render_bibliography\[(.*?)\]$/m
17
17
  BIBLIOGRAPHY_ENTRY_REGEX = /^glossarist::render_bibliography_entry\[(.*?)\]$/m
18
+ NON_VERBAL_REGEX = /^glossarist::render_(figures|tables|formulas)\[(.*?)\]$/m
18
19
 
19
20
  BIB_ANCHOR_REGEX = /^\*\s*\[\[\[([^,]+)/
20
21
 
@@ -69,32 +70,47 @@ module Metanorma
69
70
  end
70
71
 
71
72
  def process_line(document, input_lines, current_line, liquid_doc)
72
- if (match = current_line.match(DATASET_ATTR_REGEX))
73
- process_dataset_tag(document, input_lines, liquid_doc, match)
74
- elsif (match = current_line.match(RENDER_REGEX))
75
- process_render_tag(liquid_doc, match)
76
- elsif (match = current_line.match(IMPORT_SECTIONS_REGEX))
77
- process_import_sections_tag(document, liquid_doc, match)
78
- elsif (match = current_line.match(IMPORT_REGEX))
79
- process_import_tag(liquid_doc, match)
80
- elsif (match = current_line.match(BIBLIOGRAPHY_REGEX))
81
- process_bibliography(document, liquid_doc, match)
82
- elsif (match = current_line.match(BIBLIOGRAPHY_ENTRY_REGEX))
83
- process_bibliography_entry(document, liquid_doc, match)
84
- elsif (match = current_line.match(BLOCK_REGEX))
85
- process_glossarist_block(document, liquid_doc, input_lines, match)
73
+ handler = directive_handler(current_line)
74
+ if handler
75
+ handler.call(document, input_lines, liquid_doc)
86
76
  else
87
- if /^==+ \S/.match?(current_line)
88
- @title_depth = current_line.sub(/ .*$/,
89
- "").size
90
- end
91
- if (match = current_line.match(BIB_ANCHOR_REGEX))
92
- @existing_bib_anchors << match[1]
93
- end
94
- liquid_doc.add_content(current_line)
77
+ handle_plain_line(current_line, liquid_doc)
78
+ end
79
+ end
80
+
81
+ # Returns a callable that handles the directive on +line+, or nil
82
+ # for plain content. Keeping the dispatch table small (regex →
83
+ # block) lets us add a new directive by appending one entry.
84
+ def directive_handler(line)
85
+ if (m = line.match(DATASET_ATTR_REGEX))
86
+ ->(doc, lines, ldoc) { process_dataset_tag(doc, lines, ldoc, m) }
87
+ elsif (m = line.match(RENDER_REGEX))
88
+ ->(_, _, ldoc) { process_render_tag(ldoc, m) }
89
+ elsif (m = line.match(IMPORT_SECTIONS_REGEX))
90
+ ->(doc, _, ldoc) { process_import_sections_tag(doc, ldoc, m) }
91
+ elsif (m = line.match(IMPORT_REGEX))
92
+ ->(_, _, ldoc) { process_import_tag(ldoc, m) }
93
+ elsif (m = line.match(BIBLIOGRAPHY_REGEX))
94
+ ->(doc, _, ldoc) { process_bibliography(doc, ldoc, m) }
95
+ elsif (m = line.match(BIBLIOGRAPHY_ENTRY_REGEX))
96
+ ->(doc, _, ldoc) { process_bibliography_entry(doc, ldoc, m) }
97
+ elsif (m = line.match(NON_VERBAL_REGEX))
98
+ ->(_, _, ldoc) { process_non_verbal(ldoc, m) }
99
+ elsif (m = line.match(BLOCK_REGEX))
100
+ ->(doc, lines, ldoc) { process_glossarist_block(doc, ldoc, lines, m) }
95
101
  end
96
102
  end
97
103
 
104
+ def handle_plain_line(current_line, liquid_doc)
105
+ if /^==+ \S/.match?(current_line)
106
+ @title_depth = current_line.sub(/ .*$/, "").size
107
+ end
108
+ if (match = current_line.match(BIB_ANCHOR_REGEX))
109
+ @existing_bib_anchors << match[1]
110
+ end
111
+ liquid_doc.add_content(current_line)
112
+ end
113
+
98
114
  def process_dataset_tag(document, input_lines, liquid_doc, match)
99
115
  @seen_glossarist = true
100
116
  @registry.register(document, match[1])
@@ -157,7 +173,8 @@ module Metanorma
157
173
  renderer = @renderer
158
174
  rendered = renderer.render_concept(concept,
159
175
  depth: @title_depth,
160
- anchor_prefix: options["anchor-prefix"])
176
+ anchor_prefix: options["anchor-prefix"],
177
+ non_verbal: non_verbal_for(context_name))
161
178
  liquid_doc.add_content("\n#{rendered}")
162
179
  end
163
180
 
@@ -179,7 +196,8 @@ module Metanorma
179
196
  renderer = @renderer
180
197
  rendered = renderer.render_concepts(concepts,
181
198
  depth: @title_depth,
182
- anchor_prefix: options["anchor-prefix"])
199
+ anchor_prefix: options["anchor-prefix"],
200
+ non_verbal: non_verbal_for(context_name))
183
201
  liquid_doc.add_content("\n#{rendered}")
184
202
  end
185
203
 
@@ -196,11 +214,11 @@ module Metanorma
196
214
  dataset = @registry.resolve_dataset(nil, context_name)
197
215
  return unless dataset
198
216
 
199
- parts = render_sections(dataset, register, sections, options)
217
+ parts = render_sections(dataset, register, sections, context_name, options)
200
218
  liquid_doc.add_content("\n#{parts.join("\n\n")}")
201
219
  end
202
220
 
203
- def render_sections(dataset, register, sections, options)
221
+ def render_sections(dataset, register, sections, context_name, options)
204
222
  section_filter = SectionFilter.new(
205
223
  exclude: (options["section_exclude"] || "").split("|"),
206
224
  include: (options["section_include"] || "").split("|"),
@@ -213,12 +231,25 @@ module Metanorma
213
231
  depth: @title_depth,
214
232
  sort_by: options["sort_by"] || SectionRenderer::DEFAULT_SORT_BY,
215
233
  anchor_prefix: options["anchor-prefix"],
234
+ non_verbal: non_verbal_for(context_name),
216
235
  )
217
236
  renderer.render(filtered) do |concepts|
218
237
  @rendered_concepts.concat(concepts)
219
238
  end
220
239
  end
221
240
 
241
+ # Builds a NonVerbalRenderer scoped to a dataset context, or
242
+ # returns nil if the dataset has no figures/tables/formulas. The
243
+ # resulting renderer is passed through to TemplateRenderer and
244
+ # SectionRenderer so concept-attached refs render as AsciiDoc
245
+ # blocks alongside the concept body.
246
+ def non_verbal_for(context_name)
247
+ collections = @registry.non_verbal_collections(context_name)
248
+ return nil if collections.empty?
249
+
250
+ NonVerbalRenderer.new(collections: collections)
251
+ end
252
+
222
253
  def process_bibliography(document, liquid_doc, match)
223
254
  @seen_glossarist = true
224
255
  dataset_name = match[1].strip
@@ -252,6 +283,21 @@ module Metanorma
252
283
  liquid_doc.add_content(entry) if entry
253
284
  end
254
285
 
286
+ # Renders all dataset-level entities of a non-verbal kind
287
+ # (figures, tables, formulas) as AsciiDoc blocks. The directive
288
+ # shape is +glossarist::render_<kind>[dataset]+.
289
+ def process_non_verbal(liquid_doc, match)
290
+ @seen_glossarist = true
291
+ kind = :"#{match[1]}"
292
+ dataset_name = match[2].strip
293
+ collection = @registry.non_verbal_collection(dataset_name, kind)
294
+ return unless collection
295
+
296
+ renderer = NonVerbalRenderer.new(collections: { kind => collection })
297
+ rendered = renderer.render_kind(kind)
298
+ liquid_doc.add_content("\n#{rendered}") unless rendered.empty?
299
+ end
300
+
255
301
  def relative_file_path(document, file_path)
256
302
  return file_path if File.absolute_path?(file_path)
257
303
 
@@ -8,17 +8,28 @@ module Metanorma
8
8
  # Resolves, caches, and exposes dataset models for a document.
9
9
  #
10
10
  # Single source of truth for everything the preprocessor needs from a
11
- # glossarist dataset: concepts, section hierarchy, and bibliography.
11
+ # glossarist dataset: concepts, section hierarchy, bibliography, and
12
+ # dataset-level non-verbal entities (figures, tables, formulas).
12
13
  # Each is exposed as the typed Glossarist model object so callers
13
14
  # never poke at raw YAML hashes.
14
15
  class DatasetRegistry
15
16
  BIBLIOGRAPHY_FILENAME = "bibliography.yaml"
16
17
  REGISTER_FILENAME = "register.yaml"
17
18
 
19
+ # Map of plural kind symbol → (collection class, subdirectory name).
20
+ # Adding a new non-verbal kind = adding one entry here. The accessor
21
+ # `{kind}_for` and loader are derived from this table.
22
+ NON_VERBAL_KINDS = {
23
+ figures: [::Glossarist::Collections::FigureCollection, "figures"],
24
+ tables: [::Glossarist::Collections::TableCollection, "tables"],
25
+ formulas: [::Glossarist::Collections::FormulaCollection, "formulas"],
26
+ }.freeze
27
+
18
28
  def initialize
19
29
  @stores = {}
20
30
  @registers = {}
21
31
  @bibliographies = {}
32
+ @non_verbal = {}
22
33
  @context_paths = {}
23
34
  end
24
35
 
@@ -81,6 +92,38 @@ module Metanorma
81
92
  store_for(path).concepts
82
93
  end
83
94
 
95
+ # Returns the typed NonVerbalCollection for a registered context
96
+ # (e.g. FigureCollection), or nil if the dataset has no such
97
+ # subdirectory. +kind+ is one of the keys of NON_VERBAL_KINDS.
98
+ def non_verbal_collection(context_name, kind)
99
+ unless NON_VERBAL_KINDS.key?(kind)
100
+ raise ArgumentError, "unknown non-verbal kind: #{kind.inspect}"
101
+ end
102
+
103
+ path = @context_paths[context_name]
104
+ return nil unless path
105
+
106
+ collection_class, subdir = NON_VERBAL_KINDS.fetch(kind)
107
+ non_verbal_at(path, kind, subdir, collection_class)
108
+ end
109
+
110
+ NON_VERBAL_KINDS.each_key do |kind|
111
+ define_method("#{kind}_for") do |context_name|
112
+ non_verbal_collection(context_name, kind)
113
+ end
114
+ end
115
+
116
+ # Returns all available non-verbal collections for a context as a
117
+ # hash keyed by kind symbol (e.g. +{ figures: FigureCollection }+).
118
+ # Kinds whose subdirectory doesn't exist are omitted. Convenient
119
+ # for building a NonVerbalRenderer in one call.
120
+ def non_verbal_collections(context_name)
121
+ NON_VERBAL_KINDS.each_with_object({}) do |(kind, _), memo|
122
+ collection = non_verbal_collection(context_name, kind)
123
+ memo[kind] = collection if collection
124
+ end
125
+ end
126
+
84
127
  private
85
128
 
86
129
  def concepts_for(context_name)
@@ -108,6 +151,14 @@ module Metanorma
108
151
  end
109
152
  end
110
153
 
154
+ def non_verbal_at(path, kind, subdir, collection_class)
155
+ dir = File.join(path, subdir)
156
+ return nil unless File.directory?(dir)
157
+
158
+ @non_verbal[path] ||= {}
159
+ @non_verbal[path][kind] ||= collection_class.from_directory(dir)
160
+ end
161
+
111
162
  def relative_file_path(document, file_path)
112
163
  return file_path if File.absolute_path?(file_path)
113
164
 
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Plugin
5
+ module Glossarist
6
+ module NonVerbalFormatters
7
+ # Shared rendering helpers for non-verbal entity formatters.
8
+ #
9
+ # Each formatter owns the kind-specific body (image block, table
10
+ # block, stem block) and delegates anchor, caption, and a11y
11
+ # framing to this base. Localized text is resolved by ISO 639 code
12
+ # with graceful fallback to the first available value.
13
+ class Base
14
+ def initialize(entity, lang: "eng")
15
+ @entity = entity
16
+ @lang = lang
17
+ end
18
+
19
+ def to_asciidoc
20
+ parts = [anchor_line, caption_line, body].compact.reject(&:empty?)
21
+ "#{parts.join("\n")}\n"
22
+ end
23
+
24
+ protected
25
+
26
+ attr_reader :entity, :lang
27
+
28
+ # Subclasses implement this with the kind-specific AsciiDoc body
29
+ # (e.g. image::, table, stem block).
30
+ def body
31
+ raise NotImplementedError
32
+ end
33
+
34
+ def anchor_line
35
+ id = entity.id
36
+ id ? "[[#{id}]]" : nil
37
+ end
38
+
39
+ def caption_line
40
+ text = localized(entity.caption)
41
+ text ? ".#{text}" : nil
42
+ end
43
+
44
+ def alt_text
45
+ localized(entity.alt)
46
+ end
47
+
48
+ def description_text
49
+ localized(entity.description)
50
+ end
51
+
52
+ # Picks the value for the requested language, falling back to
53
+ # the first available value if missing. Returns nil for empty
54
+ # or absent hashes.
55
+ def localized(hash)
56
+ return nil if hash.nil? || hash.empty?
57
+
58
+ hash[lang] || hash.values.first
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Plugin
5
+ module Glossarist
6
+ module NonVerbalFormatters
7
+ # Renders a Glossarist::Figure as an AsciiDoc image block.
8
+ #
9
+ # Picks a single best image variant: vector (SVG) preferred for
10
+ # resolution-independence, then any first image. Subfigures are
11
+ # rendered recursively as separate image blocks so each carries
12
+ # its own anchor and caption.
13
+ class Figure < Base
14
+ ROLE_PRIORITY = %w[vector raster print light dark].freeze
15
+
16
+ protected
17
+
18
+ def body
19
+ image = best_image
20
+ return subfigure_blocks if image.nil?
21
+
22
+ line = "image::#{image.src}[#{image_attrs(image)}]"
23
+ subfigure_blocks ? "#{line}\n\n#{subfigure_blocks}" : line
24
+ end
25
+
26
+ private
27
+
28
+ def best_image
29
+ images = Array(entity.images)
30
+ return nil if images.empty?
31
+
32
+ ROLE_PRIORITY.each do |role|
33
+ found = images.find { |img| img.role == role }
34
+ return found if found
35
+ end
36
+ images.first
37
+ end
38
+
39
+ def image_attrs(image)
40
+ attrs = []
41
+ attrs << alt_text if alt_text
42
+ attrs << "width=#{image.width}" if image.width
43
+ attrs << "height=#{image.height}" if image.height
44
+ attrs.join(",")
45
+ end
46
+
47
+ def subfigure_blocks
48
+ subs = Array(entity.subfigures)
49
+ return nil if subs.empty?
50
+
51
+ subs.map do |sub|
52
+ self.class.new(sub, lang: lang).to_asciidoc
53
+ end.join("\n").strip
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Plugin
5
+ module Glossarist
6
+ module NonVerbalFormatters
7
+ # Renders a Glossarist::Formula as an AsciiDoc stem block.
8
+ #
9
+ # The expression hash is keyed by language (matching caption/alt);
10
+ # the +notation+ field carries the markup language (latex, mathml,
11
+ # asciimath) but the body is format-agnostic — Metanorma's stem
12
+ # block accepts any notation supported by the renderer.
13
+ class Formula < Base
14
+ protected
15
+
16
+ def body
17
+ expr = localized(entity.expression)
18
+ return "" if expr.nil? || expr.empty?
19
+
20
+ <<~STEM
21
+ [stem]
22
+ ++++
23
+ #{expr}
24
+ ++++
25
+ STEM
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Plugin
5
+ module Glossarist
6
+ module NonVerbalFormatters
7
+ # Renders a Glossarist::Table as an AsciiDoc table block.
8
+ #
9
+ # Two payload shapes are supported:
10
+ # - +format: structured+ — +content+ has +headers+ and +rows+
11
+ # arrays, rendered as an AsciiDoc table.
12
+ # - +format: asciidoc+ (or any non-structured) — +content+ is a
13
+ # raw markup string emitted verbatim between caption and the
14
+ # next block.
15
+ class Table < Base
16
+ STRUCTURED = "structured"
17
+
18
+ protected
19
+
20
+ def body
21
+ entity.format == STRUCTURED ? structured_table : raw_block
22
+ end
23
+
24
+ private
25
+
26
+ def structured_table
27
+ lines = ["|==="]
28
+ append_headers(lines)
29
+ Array(entity.content&.dig("rows")).each do |row|
30
+ lines << "|#{Array(row).join(' |')}"
31
+ end
32
+ lines << "|==="
33
+ lines.join("\n")
34
+ end
35
+
36
+ def append_headers(lines)
37
+ headers = Array(entity.content&.dig("headers"))
38
+ lines << "|#{headers.join(' |')}" unless headers.empty?
39
+ end
40
+
41
+ def raw_block
42
+ content = entity.content
43
+ (content&.dig("asciidoc") || content&.dig("text")).to_s
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Plugin
5
+ module Glossarist
6
+ # Namespace for per-kind AsciiDoc formatters for dataset-level
7
+ # non-verbal entities. Adding a new kind = adding a new formatter
8
+ # class and registering it in NonVerbalRenderer::FORMATTERS.
9
+ module NonVerbalFormatters
10
+ autoload :Base, "metanorma/plugin/glossarist/non_verbal_formatters/base"
11
+ autoload :Figure,
12
+ "metanorma/plugin/glossarist/non_verbal_formatters/figure"
13
+ autoload :Table,
14
+ "metanorma/plugin/glossarist/non_verbal_formatters/table"
15
+ autoload :Formula,
16
+ "metanorma/plugin/glossarist/non_verbal_formatters/formula"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Plugin
5
+ module Glossarist
6
+ # Renders dataset-level non-verbal entities (Figure, Table, Formula)
7
+ # as AsciiDoc blocks. MECE sibling to BibliographyRenderer: where
8
+ # BibliographyRenderer owns citation provenance, NonVerbalRenderer
9
+ # owns the rendering of authored figures/tables/formulas.
10
+ #
11
+ # Per-kind formatting is delegated to a formatter class registered
12
+ # in +FORMATTERS+. Adding a new kind = adding one formatter class
13
+ # and one entry here; the dispatcher itself never changes shape.
14
+ class NonVerbalRenderer
15
+ FORMATTERS = {
16
+ figures: NonVerbalFormatters::Figure,
17
+ tables: NonVerbalFormatters::Table,
18
+ formulas: NonVerbalFormatters::Formula,
19
+ }.freeze
20
+
21
+ # @param collections [Hash{Symbol => NonVerbalCollection, nil}]
22
+ # one entry per non-verbal kind, e.g.
23
+ # `{ figures: FigureCollection, tables: ..., formulas: ... }`.
24
+ # Missing or nil entries are silently skipped.
25
+ def initialize(collections:, lang: "eng")
26
+ @collections = collections
27
+ @lang = lang
28
+ end
29
+
30
+ # Render every entity in the named collection.
31
+ #
32
+ # @param kind [Symbol] key in FORMATTERS (e.g. +:figures+)
33
+ # @return [String] AsciiDoc blocks joined by blank lines, or ""
34
+ def render_kind(kind)
35
+ collection = @collections[kind]
36
+ return "" if collection.nil? || collection.entries.empty?
37
+
38
+ entries = collection.entries
39
+ "#{entries.map { |e| format_one(kind, e) }.join("\n\n")}\n"
40
+ end
41
+
42
+ # Render the non-verbal entities referenced by a concept's
43
+ # figures/tables/formulas ref collections, in deterministic order
44
+ # (figures, tables, formulas). Unknown refs are skipped silently —
45
+ # they will surface as missing anchors during Metanorma rendering.
46
+ #
47
+ # @param concept [Glossarist::ManagedConcept]
48
+ # @return [String]
49
+ def render_concept_refs(concept)
50
+ FORMATTERS.keys.filter_map do |kind|
51
+ refs = concept_refs(concept, kind)
52
+ next if refs.empty?
53
+
54
+ blocks = refs.filter_map { |ref| render_ref(kind, ref) }
55
+ next if blocks.empty?
56
+
57
+ "#{blocks.join("\n\n")}\n"
58
+ end.join("\n")
59
+ end
60
+
61
+ private
62
+
63
+ def render_ref(kind, ref)
64
+ collection = @collections[kind]
65
+ return nil unless collection
66
+
67
+ entity = collection.by_id(ref.entity_id)
68
+ return nil unless entity
69
+
70
+ format_one(kind, entity)
71
+ end
72
+
73
+ def format_one(kind, entity)
74
+ FORMATTERS.fetch(kind).new(entity, lang: @lang).to_asciidoc
75
+ end
76
+
77
+ def concept_refs(concept, kind)
78
+ refs = concept.data&.public_send(kind)
79
+ Array(refs)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -18,7 +18,7 @@ module Metanorma
18
18
  # @param register [Glossarist::DatasetRegister, nil] for cascading
19
19
  # @param renderer [TemplateRenderer] concept renderer
20
20
  # @param depth [Integer] base heading depth for sections
21
- # @param options [Hash] :sort_by, :anchor_prefix
21
+ # @param options [Hash] :sort_by, :anchor_prefix, :non_verbal
22
22
  def initialize(dataset:, register:, renderer:, depth:, **options)
23
23
  @dataset = dataset
24
24
  @register = register
@@ -26,6 +26,7 @@ module Metanorma
26
26
  @depth = depth
27
27
  @sort_by = options[:sort_by] || DEFAULT_SORT_BY
28
28
  @anchor_prefix = options[:anchor_prefix]
29
+ @non_verbal = options[:non_verbal]
29
30
  end
30
31
 
31
32
  # @param sections [Array<Glossarist::Section>]
@@ -55,7 +56,8 @@ module Metanorma
55
56
  heading = "#{'=' * (@depth + 1)} #{section.name || section.id}"
56
57
  body = @renderer.render_concepts(concepts,
57
58
  depth: @depth + 1,
58
- anchor_prefix: @anchor_prefix)
59
+ anchor_prefix: @anchor_prefix,
60
+ non_verbal: @non_verbal)
59
61
  "#{heading}\n\n#{body}"
60
62
  end
61
63
  end
@@ -12,13 +12,17 @@ module Metanorma
12
12
  @template_cache = {}
13
13
  end
14
14
 
15
- def render_concepts(concepts, depth:, anchor_prefix: nil)
15
+ def render_concepts(concepts, depth:, anchor_prefix: nil,
16
+ non_verbal: nil)
16
17
  tree = build_concept_tree(concepts)
17
- parts = tree.map { |c| render_tree_node(c, depth, anchor_prefix) }
18
+ parts = tree.map do |node|
19
+ render_tree_node(node, depth, anchor_prefix, non_verbal)
20
+ end
18
21
  normalize_whitespace(parts.join("\n\n"))
19
22
  end
20
23
 
21
- def render_concept(concept, depth:, anchor_prefix: nil)
24
+ def render_concept(concept, depth:, anchor_prefix: nil,
25
+ non_verbal: nil)
22
26
  l10n = concept.localization(@lang)
23
27
  context = {
24
28
  "concept" => concept.to_liquid,
@@ -28,6 +32,9 @@ module Metanorma
28
32
  }
29
33
  template_content = cached_template(concept)
30
34
  rendered = render_template(template_content, context)
35
+ if non_verbal
36
+ rendered += "\n\n#{non_verbal.render_concept_refs(concept)}"
37
+ end
31
38
  normalize_whitespace(rendered)
32
39
  end
33
40
 
@@ -41,12 +48,14 @@ module Metanorma
41
48
  end
42
49
  end
43
50
 
44
- def render_tree_node((concept, children), depth, anchor_prefix)
51
+ def render_tree_node((concept, children), depth, anchor_prefix,
52
+ non_verbal)
45
53
  result = render_concept(concept, depth: depth,
46
- anchor_prefix: anchor_prefix)
54
+ anchor_prefix: anchor_prefix,
55
+ non_verbal: non_verbal)
47
56
  children.each do |child_node|
48
57
  result += "\n" + render_tree_node(child_node, depth + 1,
49
- anchor_prefix)
58
+ anchor_prefix, non_verbal)
50
59
  end
51
60
  result
52
61
  end
@@ -3,7 +3,7 @@
3
3
  module Metanorma
4
4
  module Plugin
5
5
  module Glossarist
6
- VERSION = "0.3.9"
6
+ VERSION = "0.3.10"
7
7
  end
8
8
  end
9
9
  end
@@ -21,6 +21,10 @@ module Metanorma
21
21
  autoload :Document, "metanorma/plugin/glossarist/document"
22
22
  autoload :Liquid, "metanorma/plugin/glossarist/liquid"
23
23
  autoload :LiquidRendering, "metanorma/plugin/glossarist/liquid_rendering"
24
+ autoload :NonVerbalFormatters,
25
+ "metanorma/plugin/glossarist/non_verbal_formatters"
26
+ autoload :NonVerbalRenderer,
27
+ "metanorma/plugin/glossarist/non_verbal_renderer"
24
28
  autoload :Sanitize, "metanorma/plugin/glossarist/sanitize"
25
29
  autoload :SectionCascade, "metanorma/plugin/glossarist/section_cascade"
26
30
  autoload :SectionFilter, "metanorma/plugin/glossarist/section_filter"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma-plugin-glossarist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.9
4
+ version: 0.3.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-18 00:00:00.000000000 Z
11
+ date: 2026-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -111,6 +111,12 @@ files:
111
111
  - lib/metanorma/plugin/glossarist/liquid/multiply_local_file_system.rb
112
112
  - lib/metanorma/plugin/glossarist/liquid_rendering.rb
113
113
  - lib/metanorma/plugin/glossarist/liquid_templates/_concept.liquid
114
+ - lib/metanorma/plugin/glossarist/non_verbal_formatters.rb
115
+ - lib/metanorma/plugin/glossarist/non_verbal_formatters/base.rb
116
+ - lib/metanorma/plugin/glossarist/non_verbal_formatters/figure.rb
117
+ - lib/metanorma/plugin/glossarist/non_verbal_formatters/formula.rb
118
+ - lib/metanorma/plugin/glossarist/non_verbal_formatters/table.rb
119
+ - lib/metanorma/plugin/glossarist/non_verbal_renderer.rb
114
120
  - lib/metanorma/plugin/glossarist/sanitize.rb
115
121
  - lib/metanorma/plugin/glossarist/section_cascade.rb
116
122
  - lib/metanorma/plugin/glossarist/section_filter.rb