Class: AsciidoctorBibliography::Citation

Inherits:
Object
  • Object
show all
Defined in:
lib/asciidoctor-bibliography/citation.rb

Constant Summary collapse

TEX_MACROS =
%w[citet citet* citealt citealt* citep citep* citealp citealp*
citeauthor citeauthor* citeyear citeyearpar].freeze
MACRO_NAME_REGEXP =
TEX_MACROS.dup.concat(%w[cite fullcite nocite]).
map { |s| Regexp.escape s }.join("|").freeze
REGEXP =
/
  \\?  (#{MACRO_NAME_REGEXP})                # macro name
  (
    (?:   :  (?:\S*?)  \[(?:|.*?[^\\])\]  )  # first target with attributes list
    (?:  \+  (?:\S*?)  \[(?:|.*?[^\\])\]  )* # other targets with wttributes lists
  )
/x
MACRO_PARAMETERS_REGEXP =
/
  \G                # restart metching from here
  (?:
    [:+]            # separator
    (\S*?)          # optional target
    \[(|.*?[^\\])\] # attributes list
  )
/x
REF_ATTRIBUTES =
%i[chapter page section clause].freeze
MISSING_ID_MARK =
"*??*".freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(macro, *target_and_attributes_list_pairs) ⇒ Citation

Returns a new instance of Citation.



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/asciidoctor-bibliography/citation.rb', line 38

def initialize(macro, *target_and_attributes_list_pairs)
  @uuid = SecureRandom.uuid
  @macro = macro
  @citation_items = []
  target_and_attributes_list_pairs.each do |target, attribute_list|
    @citation_items << CitationItem.new do |cite|
      cite.target = target.to_s.empty? ? "default" : target
      cite.parse_attribute_list attribute_list
    end
  end
  # rubocop:enable Performance/HashEachMethods
end

Instance Attribute Details

#citation_itemsObject (readonly)

Returns the value of attribute citation_items.



36
37
38
# File 'lib/asciidoctor-bibliography/citation.rb', line 36

def citation_items
  @citation_items
end

#macroObject (readonly)

Returns the value of attribute macro.



36
37
38
# File 'lib/asciidoctor-bibliography/citation.rb', line 36

def macro
  @macro
end

Instance Method Details

#any_missing_id?(bibliographer) ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
59
# File 'lib/asciidoctor-bibliography/citation.rb', line 56

def any_missing_id?(bibliographer)
  # NOTE: do not use :any? since it ignores nil
  not missing_ids(bibliographer).empty?
end

#interpolate_formatted_citation!(formatted_citation) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
# File 'lib/asciidoctor-bibliography/citation.rb', line 120

def interpolate_formatted_citation!(formatted_citation)
  citation_items.each do |citation_item|
    key = Regexp.escape citation_item.key
    formatted_citation.gsub!(/___#{key}___(?<citation>.*?)___\/#{key}___/) do
      # NOTE: this handles custom citation text (slight overkill but easy to extend)
      # NOTE: escaping ] is necessary to safely nest macros (e.g. citing in a footnote)
      (citation_item.text || "{cite}").
        sub("{cite}", Regexp.last_match[:citation].gsub("]", "&rsqb;"))
    end
  end
end

#missing_ids(bibliographer) ⇒ Object



51
52
53
54
# File 'lib/asciidoctor-bibliography/citation.rb', line 51

def missing_ids(bibliographer)
  m_ids = (@citation_items.map(&:key) - bibliographer.database.map { |entry| entry["id"] })
  m_ids.map! { |id| id.nil? ? "" : id }
end

#prepare_fullcite_item(bibliographer, formatter) ⇒ Object



108
109
110
# File 'lib/asciidoctor-bibliography/citation.rb', line 108

def prepare_fullcite_item(bibliographer, formatter)
  formatter.import([bibliographer.database.find_entry_by_id(citation_items.first.key)])
end

#prepare_item(options, item, affix: true) ⇒ Object



151
152
153
154
155
156
157
158
159
# File 'lib/asciidoctor-bibliography/citation.rb', line 151

def prepare_item(options, item, affix: true)
  # TODO: hyperlink, suppress_author and only_author options
  ci = citation_items.detect { |c| c.key == item.id }
  wrap_item item, ci.prefix, ci.suffix if affix
  id = xref_id "bibliography", ci.target, item.id
  wrap_item item, "___#{item.id}___", "___/#{item.id}___"
  wrap_item item, "<<#{id},", ">>" if options.hyperlinks?
  item.label, item.locator = ci.locator
end

#prepare_items(bibliographer, formatter, tex: false) ⇒ Object



132
133
134
135
136
137
138
139
# File 'lib/asciidoctor-bibliography/citation.rb', line 132

def prepare_items(bibliographer, formatter, tex: false)
  # NOTE: when we're using our custom TeX CSL styles prefix/suffix are used as
  #   variables for metadata instead of as parameters for citations.
  cites_with_local_attributes = citation_items.map { |cite|  bibliographer, cite, affix: tex }
  formatter.import cites_with_local_attributes
  formatter.force_sort!(mode: :citation)
  formatter.data.map(&:cite).each { |item| prepare_item bibliographer.options, item, affix: !tex }
end

#prepare_metadata(bibliographer, cite, affix: false) ⇒ Object



141
142
143
144
145
146
147
148
149
# File 'lib/asciidoctor-bibliography/citation.rb', line 141

def (bibliographer, cite, affix: false)
  bibliographer.database.find_entry_by_id(cite.key).
    merge 'citation-number': bibliographer.appearance_index_of(cite.target, cite.key),
          'citation-label': cite.key, # TODO: smart label generators
          'locator': cite.locator.nil? ? nil : " ",
          'prefix': affix ? cite.prefix : nil,
          'suffix': affix ? cite.suffix : nil
  # TODO: why is a non blank 'locator' necessary to display locators set at a later stage?
end

#render(bibliographer) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/asciidoctor-bibliography/citation.rb', line 61

def render(bibliographer)
  # NOTE: If there is any blank key we must render the entire (possibly composite)
  # NOTE: citation as missing, as we don't have that kind of control over CSL styles.
  if any_missing_id?(bibliographer)
    warn "Warning: I didn't find a database entry for #{missing_ids(bibliographer)}."
    # TODO: It would be cool to have the title attribute show the missing keys
    # TODO: as a popup above *??* but it does not work on inline quoted text.
    return MISSING_ID_MARK
  end

  formatted_citation = render_with_csl(bibliographer)
  wrap_up_citation citation: formatted_citation, bibliographer: bibliographer
end

#render_citation_with_csl(bibliographer, style: bibliographer.options.style, tex: false) ⇒ Object



112
113
114
115
116
117
118
# File 'lib/asciidoctor-bibliography/citation.rb', line 112

def render_citation_with_csl(bibliographer, style: bibliographer.options.style, tex: false)
  formatter = Formatter.new(style, locale: bibliographer.options.locale)
  items = prepare_items bibliographer, formatter, tex: tex
  formatted_citation = formatter.engine.renderer.render(items, formatter.engine.style.citation)
  interpolate_formatted_citation! formatted_citation
  formatted_citation
end

#render_fullcite_with_csl(bibliographer) ⇒ Object



102
103
104
105
106
# File 'lib/asciidoctor-bibliography/citation.rb', line 102

def render_fullcite_with_csl(bibliographer)
  formatter = Formatter.new(bibliographer.options.style, locale: bibliographer.options.locale)
  prepare_fullcite_item bibliographer, formatter
  formatter.render(:bibliography, id: citation_items.first.key).join
end

#render_texmacro_with_csl(bibliographer) ⇒ Object



96
97
98
99
100
# File 'lib/asciidoctor-bibliography/citation.rb', line 96

def render_texmacro_with_csl(bibliographer)
  filename = ["tex", macro.tr("*", "s"), bibliographer.options.tex_style].join("-")
  filepath = File.join AsciidoctorBibliography.root, "lib/csl/styles", filename
  render_citation_with_csl(bibliographer, style: filepath, tex: true)
end

#render_with_csl(bibliographer) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/asciidoctor-bibliography/citation.rb', line 75

def render_with_csl(bibliographer)
  case macro
  when "cite"
    render_citation_with_csl(bibliographer)
  when "fullcite"
    render_fullcite_with_csl(bibliographer)
  when "nocite"
    ""
  when *TEX_MACROS
    render_texmacro_with_csl(bibliographer)
  end
end

#uuidObject



166
167
168
# File 'lib/asciidoctor-bibliography/citation.rb', line 166

def uuid
  ":#{@uuid}:"
end

#wrap_item(item, prefix, suffix) ⇒ Object



161
162
163
164
# File 'lib/asciidoctor-bibliography/citation.rb', line 161

def wrap_item(item, prefix, suffix)
  item.prefix = prefix.to_s + item.prefix.to_s
  item.suffix = item.suffix.to_s + suffix.to_s
end

#wrap_up_citation(citation:, bibliographer:) ⇒ Object



88
89
90
91
92
93
94
# File 'lib/asciidoctor-bibliography/citation.rb', line 88

def wrap_up_citation(citation:, bibliographer:)
  text = citation.dup
  # TODO: handle hyperlinks here, maybe?
  text = ["+++", text, "+++"].join if bibliographer.options.passthrough?(:citation)
  text.prepend "{empty}" if bibliographer.options.prepend_empty?(:citation)
  text
end

#xref_id(*fragments) ⇒ Object



170
171
172
# File 'lib/asciidoctor-bibliography/citation.rb', line 170

def xref_id(*fragments)
  fragments.compact.join("-")
end