metanorma-plugin-glossarist 0.3.9 → 0.4.0
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 +4 -4
- data/.rubocop_todo.yml +28 -112
- data/Gemfile +8 -5
- data/lib/metanorma/plugin/glossarist/bibliography_renderer.rb +1 -1
- data/lib/metanorma/plugin/glossarist/concept_filter.rb +8 -27
- data/lib/metanorma/plugin/glossarist/dataset_preprocessor.rb +83 -38
- data/lib/metanorma/plugin/glossarist/dataset_registry.rb +52 -3
- data/lib/metanorma/plugin/glossarist/document.rb +12 -13
- data/lib/metanorma/plugin/glossarist/field_filter.rb +51 -0
- data/lib/metanorma/plugin/glossarist/liquid/custom_blocks/with_glossarist_context.rb +9 -5
- data/lib/metanorma/plugin/glossarist/liquid_rendering.rb +4 -3
- data/lib/metanorma/plugin/glossarist/liquid_templates/_concept.liquid +7 -2
- data/lib/metanorma/plugin/glossarist/mention_extractor.rb +37 -0
- data/lib/metanorma/plugin/glossarist/non_verbal_formatters/base.rb +64 -0
- data/lib/metanorma/plugin/glossarist/non_verbal_formatters/figure.rb +59 -0
- data/lib/metanorma/plugin/glossarist/non_verbal_formatters/formula.rb +31 -0
- data/lib/metanorma/plugin/glossarist/non_verbal_formatters/table.rb +49 -0
- data/lib/metanorma/plugin/glossarist/non_verbal_formatters.rb +20 -0
- data/lib/metanorma/plugin/glossarist/non_verbal_renderer.rb +84 -0
- data/lib/metanorma/plugin/glossarist/sanitize.rb +0 -7
- data/lib/metanorma/plugin/glossarist/section_renderer.rb +4 -2
- data/lib/metanorma/plugin/glossarist/template_renderer.rb +16 -17
- data/lib/metanorma/plugin/glossarist/version.rb +1 -1
- data/lib/metanorma-plugin-glossarist.rb +7 -0
- data/metanorma-plugin-glossarist.gemspec +1 -1
- metadata +12 -4
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Glossarist
|
|
6
|
+
# A single field-based filter specification: a path through the concept
|
|
7
|
+
# graph, a matcher kind, and the comparison value.
|
|
8
|
+
#
|
|
9
|
+
# FieldFilter is the structured representation behind ConceptFilter's
|
|
10
|
+
# field-filter DSL. ConceptFilter parses the legacy pseudo-syntax
|
|
11
|
+
# (e.g. +key.start_with(pattern)=ignored+) into FieldFilter instances;
|
|
12
|
+
# future syntaxes (regex, contains) only need a new matcher symbol.
|
|
13
|
+
FieldFilter = Struct.new(:path, :matcher, :value, keyword_init: true) do
|
|
14
|
+
START_WITH_SUFFIX = /\.start_with\(([^)]+)\)\z/
|
|
15
|
+
|
|
16
|
+
# Parses a single filter hash entry (path-key, optional value) into
|
|
17
|
+
# a FieldFilter. Two shapes are recognized:
|
|
18
|
+
#
|
|
19
|
+
# - "data.path" => "value" → matcher :eq, value "value"
|
|
20
|
+
# - "data.path.start_with(pattern)" => _ → matcher :start_with,
|
|
21
|
+
# value "pattern" (the hash value is ignored)
|
|
22
|
+
#
|
|
23
|
+
# @param path [String] raw filter key
|
|
24
|
+
# @param raw_value [String, nil] raw filter value
|
|
25
|
+
# @return [FieldFilter]
|
|
26
|
+
def self.from_options_entry(path, raw_value)
|
|
27
|
+
if (match = path.match(START_WITH_SUFFIX))
|
|
28
|
+
FieldFilter.new(
|
|
29
|
+
path: match.pre_match,
|
|
30
|
+
matcher: :start_with,
|
|
31
|
+
value: match[1],
|
|
32
|
+
)
|
|
33
|
+
else
|
|
34
|
+
FieldFilter.new(path: path, matcher: :eq, value: raw_value)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Applies the filter to a single concept via the given path resolver.
|
|
39
|
+
# @param resolver [ConceptPathResolver]
|
|
40
|
+
# @param concept [ManagedConcept]
|
|
41
|
+
def match?(resolver, concept)
|
|
42
|
+
actual = resolver.resolve(concept, path)
|
|
43
|
+
case matcher
|
|
44
|
+
when :start_with then actual&.start_with?(value.to_s)
|
|
45
|
+
else actual == value
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -31,7 +31,7 @@ module Metanorma
|
|
|
31
31
|
|
|
32
32
|
@contexts.each do |local_context|
|
|
33
33
|
path = local_context[:file_path].strip
|
|
34
|
-
collection = registry
|
|
34
|
+
collection = concepts_for(registry, path)
|
|
35
35
|
filtered = ConceptFilter.new(@raw_filters).apply(collection)
|
|
36
36
|
context[local_context[:name]] = filtered.map do |c|
|
|
37
37
|
ManagedConceptDrop.new(c)
|
|
@@ -51,10 +51,14 @@ module Metanorma
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
def concepts_for(registry, path)
|
|
55
|
+
return registry.concepts_at(path) if registry
|
|
56
|
+
|
|
57
|
+
fallback_store(path).concepts
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def fallback_store(path)
|
|
61
|
+
::Glossarist::GlossaryStore.new.load_directory(path)
|
|
58
62
|
end
|
|
59
63
|
end
|
|
60
64
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "liquid"
|
|
4
|
-
|
|
5
3
|
module Metanorma
|
|
6
4
|
module Plugin
|
|
7
5
|
module Glossarist
|
|
@@ -16,7 +14,10 @@ module Metanorma
|
|
|
16
14
|
Liquid::LocalFileSystem.new(include_paths, patterns)
|
|
17
15
|
template.registers[:dataset_registry] = registry if registry
|
|
18
16
|
rendered = template.render(assigns)
|
|
19
|
-
|
|
17
|
+
if template.errors.any?
|
|
18
|
+
error = template.errors.first
|
|
19
|
+
raise error.cause || error
|
|
20
|
+
end
|
|
20
21
|
|
|
21
22
|
rendered
|
|
22
23
|
end
|
|
@@ -29,9 +29,14 @@ domain:[{{ l10n.data.domain }}]
|
|
|
29
29
|
{% for note in l10n.data.notes.definitions -%}
|
|
30
30
|
[NOTE]
|
|
31
31
|
====
|
|
32
|
-
{{ note.content | sanitize_references }}
|
|
33
|
-
====
|
|
32
|
+
{{ note.content | sanitize_references }}{% for example in note.examples %}
|
|
34
33
|
|
|
34
|
+
[example]
|
|
35
|
+
====
|
|
36
|
+
{{ example.content | sanitize_references }}
|
|
37
|
+
====
|
|
38
|
+
{% endfor %}
|
|
39
|
+
====
|
|
35
40
|
{% endfor %}
|
|
36
41
|
{% if l10n.data.annotations -%}
|
|
37
42
|
{% for annotation in l10n.data.annotations.definitions -%}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Glossarist
|
|
6
|
+
# Extracts bibliography-citable mention IDs from concept text using
|
|
7
|
+
# +Glossarist::ReferenceExtractor+ as the single canonical parser.
|
|
8
|
+
#
|
|
9
|
+
# Bibliography relevance = +BibliographicReference+ (from +<<anchor>>+
|
|
10
|
+
# xrefs) and +ConceptReference+ instances whose +#cite?+ is true (from
|
|
11
|
+
# +{{cite:id}}+ mentions). Other mention kinds (fig/table/formula/urn)
|
|
12
|
+
# reference assets or concepts, not bibliography entries.
|
|
13
|
+
class MentionExtractor
|
|
14
|
+
def initialize(text)
|
|
15
|
+
@text = text
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def bibliography_ids
|
|
19
|
+
refs = ::Glossarist::ReferenceExtractor.new
|
|
20
|
+
.extract_from_text(@text.to_s)
|
|
21
|
+
refs.filter_map { |ref| bibliography_id(ref) }.uniq
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def bibliography_id(ref)
|
|
27
|
+
case ref
|
|
28
|
+
when ::Glossarist::BibliographicReference
|
|
29
|
+
ref.anchor
|
|
30
|
+
when ::Glossarist::ConceptReference
|
|
31
|
+
ref.concept_id if ref.cite?
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -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
|
|
@@ -5,7 +5,6 @@ module Metanorma
|
|
|
5
5
|
module Glossarist
|
|
6
6
|
module Sanitize
|
|
7
7
|
REF_REGEX = /{{(urn:[^,{}]+),([^}]+?)}}(.*)$/m
|
|
8
|
-
XREF_REGEX = /<<((?>[^,>\n]+))(?:,[^>\n]*)?>>/
|
|
9
8
|
|
|
10
9
|
def self.references(str)
|
|
11
10
|
return str unless str&.match?(REF_REGEX)
|
|
@@ -16,12 +15,6 @@ module Metanorma
|
|
|
16
15
|
"{{#{urn},#{m[2]}}}#{m[3]}"
|
|
17
16
|
end
|
|
18
17
|
end
|
|
19
|
-
|
|
20
|
-
def self.extract_xrefs(text)
|
|
21
|
-
return [] unless text
|
|
22
|
-
|
|
23
|
-
text.scan(XREF_REGEX).map(&:first).uniq
|
|
24
|
-
end
|
|
25
18
|
end
|
|
26
19
|
end
|
|
27
20
|
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
|
|
@@ -9,16 +9,19 @@ module Metanorma
|
|
|
9
9
|
def initialize(file_system:, lang: "eng")
|
|
10
10
|
@file_system = file_system
|
|
11
11
|
@lang = lang
|
|
12
|
-
@template_cache = {}
|
|
13
12
|
end
|
|
14
13
|
|
|
15
|
-
def render_concepts(concepts, depth:, anchor_prefix: nil
|
|
14
|
+
def render_concepts(concepts, depth:, anchor_prefix: nil,
|
|
15
|
+
non_verbal: nil)
|
|
16
16
|
tree = build_concept_tree(concepts)
|
|
17
|
-
parts = tree.map
|
|
17
|
+
parts = tree.map do |node|
|
|
18
|
+
render_tree_node(node, depth, anchor_prefix, non_verbal)
|
|
19
|
+
end
|
|
18
20
|
normalize_whitespace(parts.join("\n\n"))
|
|
19
21
|
end
|
|
20
22
|
|
|
21
|
-
def render_concept(concept, depth:, anchor_prefix: nil
|
|
23
|
+
def render_concept(concept, depth:, anchor_prefix: nil,
|
|
24
|
+
non_verbal: nil)
|
|
22
25
|
l10n = concept.localization(@lang)
|
|
23
26
|
context = {
|
|
24
27
|
"concept" => concept.to_liquid,
|
|
@@ -26,27 +29,23 @@ module Metanorma
|
|
|
26
29
|
"depth_marker" => "=" * (depth + 1),
|
|
27
30
|
"anchor" => build_anchor(concept.data.id.to_s, anchor_prefix),
|
|
28
31
|
}
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
rendered = render_template(File.read(DEFAULT_TEMPLATE), context)
|
|
33
|
+
if non_verbal
|
|
34
|
+
rendered += "\n\n#{non_verbal.render_concept_refs(concept)}"
|
|
35
|
+
end
|
|
31
36
|
normalize_whitespace(rendered)
|
|
32
37
|
end
|
|
33
38
|
|
|
34
39
|
private
|
|
35
40
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
@template_cache[version] ||= begin
|
|
39
|
-
path = File.join(TEMPLATES_DIR, "_concept_#{version}.liquid")
|
|
40
|
-
File.exist?(path) ? File.read(path) : File.read(DEFAULT_TEMPLATE)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def render_tree_node((concept, children), depth, anchor_prefix)
|
|
41
|
+
def render_tree_node((concept, children), depth, anchor_prefix,
|
|
42
|
+
non_verbal)
|
|
45
43
|
result = render_concept(concept, depth: depth,
|
|
46
|
-
anchor_prefix: anchor_prefix
|
|
44
|
+
anchor_prefix: anchor_prefix,
|
|
45
|
+
non_verbal: non_verbal)
|
|
47
46
|
children.each do |child_node|
|
|
48
47
|
result += "\n" + render_tree_node(child_node, depth + 1,
|
|
49
|
-
anchor_prefix)
|
|
48
|
+
anchor_prefix, non_verbal)
|
|
50
49
|
end
|
|
51
50
|
result
|
|
52
51
|
end
|
|
@@ -19,8 +19,15 @@ module Metanorma
|
|
|
19
19
|
"metanorma/plugin/glossarist/dataset_preprocessor"
|
|
20
20
|
autoload :DatasetRegistry, "metanorma/plugin/glossarist/dataset_registry"
|
|
21
21
|
autoload :Document, "metanorma/plugin/glossarist/document"
|
|
22
|
+
autoload :FieldFilter, "metanorma/plugin/glossarist/field_filter"
|
|
22
23
|
autoload :Liquid, "metanorma/plugin/glossarist/liquid"
|
|
23
24
|
autoload :LiquidRendering, "metanorma/plugin/glossarist/liquid_rendering"
|
|
25
|
+
autoload :MentionExtractor,
|
|
26
|
+
"metanorma/plugin/glossarist/mention_extractor"
|
|
27
|
+
autoload :NonVerbalFormatters,
|
|
28
|
+
"metanorma/plugin/glossarist/non_verbal_formatters"
|
|
29
|
+
autoload :NonVerbalRenderer,
|
|
30
|
+
"metanorma/plugin/glossarist/non_verbal_renderer"
|
|
24
31
|
autoload :Sanitize, "metanorma/plugin/glossarist/sanitize"
|
|
25
32
|
autoload :SectionCascade, "metanorma/plugin/glossarist/section_cascade"
|
|
26
33
|
autoload :SectionFilter, "metanorma/plugin/glossarist/section_filter"
|
|
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
spec.required_ruby_version = ">= 3.1.0"
|
|
27
27
|
|
|
28
28
|
spec.add_dependency "asciidoctor"
|
|
29
|
-
spec.add_dependency "glossarist", "~> 2.8", ">= 2.
|
|
29
|
+
spec.add_dependency "glossarist", "~> 2.8", ">= 2.9.0"
|
|
30
30
|
spec.add_dependency "liquid"
|
|
31
31
|
spec.add_dependency "metanorma-utils"
|
|
32
32
|
spec.metadata["rubygems_mfa_required"] = "true"
|
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.
|
|
4
|
+
version: 0.4.0
|
|
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-
|
|
11
|
+
date: 2026-07-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: asciidoctor
|
|
@@ -33,7 +33,7 @@ dependencies:
|
|
|
33
33
|
version: '2.8'
|
|
34
34
|
- - ">="
|
|
35
35
|
- !ruby/object:Gem::Version
|
|
36
|
-
version: 2.
|
|
36
|
+
version: 2.9.0
|
|
37
37
|
type: :runtime
|
|
38
38
|
prerelease: false
|
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -43,7 +43,7 @@ dependencies:
|
|
|
43
43
|
version: '2.8'
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 2.
|
|
46
|
+
version: 2.9.0
|
|
47
47
|
- !ruby/object:Gem::Dependency
|
|
48
48
|
name: liquid
|
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -100,6 +100,7 @@ files:
|
|
|
100
100
|
- lib/metanorma/plugin/glossarist/dataset_preprocessor.rb
|
|
101
101
|
- lib/metanorma/plugin/glossarist/dataset_registry.rb
|
|
102
102
|
- lib/metanorma/plugin/glossarist/document.rb
|
|
103
|
+
- lib/metanorma/plugin/glossarist/field_filter.rb
|
|
103
104
|
- lib/metanorma/plugin/glossarist/liquid.rb
|
|
104
105
|
- lib/metanorma/plugin/glossarist/liquid/custom_blocks/with_glossarist_context.rb
|
|
105
106
|
- lib/metanorma/plugin/glossarist/liquid/custom_filters.rb
|
|
@@ -111,6 +112,13 @@ files:
|
|
|
111
112
|
- lib/metanorma/plugin/glossarist/liquid/multiply_local_file_system.rb
|
|
112
113
|
- lib/metanorma/plugin/glossarist/liquid_rendering.rb
|
|
113
114
|
- lib/metanorma/plugin/glossarist/liquid_templates/_concept.liquid
|
|
115
|
+
- lib/metanorma/plugin/glossarist/mention_extractor.rb
|
|
116
|
+
- lib/metanorma/plugin/glossarist/non_verbal_formatters.rb
|
|
117
|
+
- lib/metanorma/plugin/glossarist/non_verbal_formatters/base.rb
|
|
118
|
+
- lib/metanorma/plugin/glossarist/non_verbal_formatters/figure.rb
|
|
119
|
+
- lib/metanorma/plugin/glossarist/non_verbal_formatters/formula.rb
|
|
120
|
+
- lib/metanorma/plugin/glossarist/non_verbal_formatters/table.rb
|
|
121
|
+
- lib/metanorma/plugin/glossarist/non_verbal_renderer.rb
|
|
114
122
|
- lib/metanorma/plugin/glossarist/sanitize.rb
|
|
115
123
|
- lib/metanorma/plugin/glossarist/section_cascade.rb
|
|
116
124
|
- lib/metanorma/plugin/glossarist/section_filter.rb
|