asciidoctor-bibtex 0.4.1 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89fbb2a41ee997771d88259c171820124f17653e1c98ae047713a6fae009c2bd
4
- data.tar.gz: 2b9016dc63a94c2e2127977d3af7771daa92e8e07fc68043b7af609e8c6a7907
3
+ metadata.gz: cad05b0990309ce242e6f8e83a5a5c0122f07f5a23130d77f740d8c635611162
4
+ data.tar.gz: '01323158119adf765dfa92c09e0e4a4da63a3bf4ca2b7390a47d687c1b4b2855'
5
5
  SHA512:
6
- metadata.gz: 418685ec3a7539d86f932fff5d206b688cd42cbf8305f62d2aa7772fcfce7550ec4c2b7c3d1d1c2459623e046ce72bbe4bcbbd7babf9867513168cc5fb92aa2e
7
- data.tar.gz: 757ad6e419e4067800ecfa0fec230b5d8d5e99de8af2710d5cd7b955daf05bd826b43d8347031824a5f01c0a0476a06539c82e51ae5daa5a456802bad5b29460
6
+ metadata.gz: c52687565a3ac2de6a25cf020eabcf3a13732b693ec34bcc0d60c17cd2e526c53e528a08fe7a809a89604f88a027a362e526d769537e363b7de841ed7018178b
7
+ data.tar.gz: bdd90bb7544d361890da697587c0d2be118a03f7b894e579fd80bdea3d85eefa124f58e605dfcfdf895bde8ef7acc6cfaad4b41838ac9e5d13c005c540794cde
data/README.md CHANGED
@@ -6,11 +6,10 @@ replaced with formatted inline texts, and reference lists are automatically
6
6
  generated and inserted into where `bibliography::[]` is placed.
7
7
 
8
8
  asciidoctor-bibtex is designed to be used as an extension to
9
- [asciidoctor](http://asciidoctor.org), although it supports asciidoc to
10
- asciidoc transformation at the moment. Thus this extension can be used
9
+ [asciidoctor](http://asciidoctor.org). Thus this extension can be used
11
10
  together with other asciidoctor extensions such as
12
11
  [asciidoctor-mathematical][] and [asciidoctor-pdf][] to enrich your
13
- asciidoc experience.
12
+ asciidoc experience. Note that asciidoctor-bibtex no longer support asciidoc-to-asciidoc conversion.
14
13
 
15
14
  [asciidoctor-mathematical]: https://github.com/asciidoctor/asciidoctor-mathematical
16
15
  [asciidoctor-pdf]: https://github.com/asciidoctor/asciidoctor-pdf
@@ -29,7 +28,7 @@ documents and produces new asciidoc documents, asciidoctor-bibtex focuses on
29
28
  compatibility with asciidoctor and other asciidoctor extensions at the very
30
29
  beginning. As time passes, asciidoctor-bibtex diverges significantly from its
31
30
  ancesstor. For example, asciidoctor-bibtex now supports generating real bibtex
32
- ciations and bibliography, so it can be used together with
31
+ citations and bibliography, so it can be used together with
33
32
  [asciidoctor-latex][] for native bibtex support.
34
33
 
35
34
  [asciidoc-bib]: https://github.com/petercrlane/asciidoc-bib
@@ -84,7 +83,8 @@ To add a list of formatted references, place `bibliography::[]` on a line by its
84
83
  | bibtex-file | Bibtex database file | any string, or empty | Automatic searching |
85
84
  | bibtex-style | Reference formatting style | any style supported by csl-styles | ieee |
86
85
  | bibtex-order | Order of citations | `appearance` or `alphabetical` | `appearance` |
87
- | bibtex-format | Formatting of citations and bibliography | `asciidoc` or `bibtex` or `biblatex` | `asciidoc` |
86
+ | bibtex-format | Formatting of citations and bibliography | `asciidoc`, `bibtex` or `biblatex` | `asciidoc` |
87
+ | bibtex-throw | Throw an error on unknown references | `true` or `false` | `false` |
88
88
 
89
89
  ### Commandline
90
90
 
@@ -1,21 +1,11 @@
1
1
  # asciidoctor-bibtex.rb
2
2
  #
3
3
  # Copyright (c) Peter Lane, 2012-13.
4
+ # Copyright (c) Zhang Yang, 2019.
5
+ #
4
6
  # Released under Open Works License, 0.9.2
5
7
 
6
- require 'bibtex'
7
- require 'citeproc'
8
- require 'csl/styles'
9
- require 'set'
10
-
11
- require_relative 'asciidoctor-bibtex/asciidoctor'
12
- require_relative 'asciidoctor-bibtex/citation'
13
- require_relative 'asciidoctor-bibtex/citationdata'
14
- require_relative 'asciidoctor-bibtex/citationutils'
15
- require_relative 'asciidoctor-bibtex/citations'
8
+ # Register asciidoctor extensions introduced by this package.
16
9
  require_relative 'asciidoctor-bibtex/extensions'
17
- require_relative 'asciidoctor-bibtex/filehandlers'
18
- require_relative 'asciidoctor-bibtex/processorutils'
19
- require_relative 'asciidoctor-bibtex/processor'
20
- require_relative 'asciidoctor-bibtex/styles'
10
+ # Export the package version
21
11
  require_relative 'asciidoctor-bibtex/version'
@@ -0,0 +1,96 @@
1
+ #
2
+ # CitationMacro.rb
3
+ #
4
+ # Copyright (c) Peter Lane, 2013.
5
+ # Copyright (c) Zhang Yang, 2019.
6
+ #
7
+ # Released under Open Works License, 0.9.2
8
+ #
9
+
10
+ module AsciidoctorBibtex
11
+ # CitationItem
12
+ #
13
+ # A class to hold data for a single citation item.
14
+ #
15
+ class CitationItem
16
+ attr_reader :key, :locator
17
+
18
+ def initialize(key, locator)
19
+ @key = key
20
+ @locator = locator
21
+ # clean up locator
22
+ @locator ||= ''
23
+ @locator = @locator.gsub('--', '-')
24
+ end
25
+
26
+ def to_s
27
+ "#{@key}:#{@locator}"
28
+ end
29
+ end
30
+
31
+ # CitationMacro
32
+ #
33
+ # Class to hold information about a citation macro. A citation macro has
34
+ # type, text and an array of citation items.
35
+ #
36
+ # This class also provides a class method to extract macros from a line of
37
+ # text.
38
+ #
39
+ class CitationMacro
40
+ #
41
+ # Grammar for the citation macro: cite|citenp:[Key(locator)]
42
+ #
43
+
44
+ # matches a citation type
45
+ CITATION_TYPE = /cite|citenp/.freeze
46
+ # matches a citation item (key + locator), such as 'Dan2012(99-100)'
47
+ CITATION_ITEM = /([^\s,()\[\]]+)(\([^)]*\))?/.freeze
48
+ # matches a citation list
49
+ CITATION_LIST_TAIL = /(\s*,\s*#{CITATION_ITEM})*/.freeze
50
+ CITATION_LIST = /(?:#{CITATION_ITEM}#{CITATION_LIST_TAIL})/.freeze
51
+ CITATION_PRETEXT = /[^\[]*/.freeze
52
+ # matches the full citation macro
53
+ CITATION_MACRO = /(#{CITATION_TYPE}):(#{CITATION_PRETEXT})\[(#{CITATION_LIST})\]/.freeze
54
+
55
+ # Given a line, return a list of CitationData instances
56
+ # containing information on each set of citation information
57
+ def self.extract_citations(line)
58
+ result = []
59
+ full = CITATION_MACRO.match line
60
+ while full
61
+ text = full[0]
62
+ type = full[1]
63
+ pretext = full[2]
64
+ items = []
65
+ item = CITATION_ITEM.match full[3]
66
+ while item
67
+ locator = nil
68
+ locator = item[2][1...-1] if item[2]
69
+ items << CitationItem.new(item[1], locator)
70
+ # look for next ref within citation
71
+ item = CITATION_ITEM.match item.post_match
72
+ end
73
+ result << CitationMacro.new(text, type, pretext, items)
74
+ # look for next citation on line
75
+ full = CITATION_MACRO.match full.post_match
76
+ end
77
+
78
+ result
79
+ end
80
+
81
+ attr_reader :text, :type, :pretext, :items
82
+
83
+ # Create a CitationMacro object
84
+ #
85
+ # text: the full macro text matched by CITATION_MACRO
86
+ # type: cite or citenp
87
+ # pretext: some small texts.
88
+ # items: An array of citation items
89
+ def initialize(text, type, pretext, items)
90
+ @text = text
91
+ @type = type
92
+ @pretext = pretext
93
+ @items = items
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,34 @@
1
+ #
2
+ # CitationUtils.rb
3
+ #
4
+ # Copyright (c) Peter Lane, 2013.
5
+ # Released under Open Works License, 0.9.2
6
+ #
7
+
8
+ module AsciidoctorBibtex
9
+ # Some utility functions used in Citations class
10
+ module CitationUtils
11
+ # arrange author string, flag for order of surname/initials
12
+ def self.arrange_authors(authors, surname_first)
13
+ return [] if authors.nil?
14
+
15
+ authors.split(/\band\b/).collect do |name|
16
+ if name.include?(', ')
17
+ parts = name.strip.rpartition(', ')
18
+ if surname_first
19
+ "#{parts[0]}, #{parts[2]}"
20
+ else
21
+ "#{parts[2]} #{parts[0]}"
22
+ end
23
+ else
24
+ name
25
+ end
26
+ end
27
+ end
28
+
29
+ # Arrange given author string into Chicago format
30
+ def self.author_chicago(authors)
31
+ arrange_authors authors, true
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # PathUtils.rb
3
+ #
4
+ # High-level utilities for files.
5
+ #
6
+
7
+ module AsciidoctorBibtex
8
+ module PathUtils
9
+ # Locate a bibtex file to read in given dir
10
+ def self.find_bibfile(dir)
11
+ candidates = Dir.glob("#{dir}/*.bib")
12
+ if candidates.empty?
13
+ return ''
14
+ else
15
+ return candidates.first
16
+ end
17
+ rescue StandardError # catch all errors, and return empty string
18
+ ''
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,281 @@
1
+ #
2
+ # Manage the current set of citations, the document settings,
3
+ # and main operations.
4
+ #
5
+
6
+ require 'bibtex'
7
+ require 'bibtex/filters'
8
+ require 'citeproc'
9
+ require 'csl/styles'
10
+ require 'latex/decode/base'
11
+ require 'latex/decode/maths'
12
+ require 'latex/decode/accents'
13
+ require 'latex/decode/diacritics'
14
+ require 'latex/decode/punctuation'
15
+ require 'latex/decode/symbols'
16
+ require 'latex/decode/greek'
17
+ require 'set'
18
+
19
+ require_relative 'CitationMacro'
20
+ require_relative 'CitationUtils'
21
+ require_relative 'StringUtils'
22
+ require_relative 'StyleUtils'
23
+
24
+ module AsciidoctorBibtex
25
+ # This filter extends the original latex filter in bibtex-ruby to handle
26
+ # unknown latex macros more gracefully. We could have used latex-decode
27
+ # gem together with our custom replacement rules, but latex-decode eats up
28
+ # all braces after it finishes all decoding. So we hack over the
29
+ # LaTeX.decode function and insert our rules before `strip_braces`.
30
+ class LatexFilter < ::BibTeX::Filter
31
+ def apply(value)
32
+ text = value.to_s
33
+ LaTeX::Decode::Base.normalize(text)
34
+ LaTeX::Decode::Maths.decode!(text)
35
+ LaTeX::Decode::Accents.decode!(text)
36
+ LaTeX::Decode::Diacritics.decode!(text)
37
+ LaTeX::Decode::Punctuation.decode!(text)
38
+ LaTeX::Decode::Symbols.decode!(text)
39
+ LaTeX::Decode::Greek.decode!(text)
40
+ text = text.gsub(/\\url\{(.+?)\}/, ' \\1 ').gsub(/\\\w+(?=\s+\w)/, '').gsub(/\\\w+(?:\[.+?\])?\s*\{(.+?)\}/, '\\1')
41
+ LaTeX::Decode::Base.strip_braces(text)
42
+ LaTeX.normalize_C(text)
43
+ end
44
+ end
45
+
46
+ # Class used through utility method to hold data about citations for
47
+ # current document, and run the different steps to add the citations
48
+ # and bibliography
49
+ class Processor
50
+ def initialize(bibfile, links = false, style = 'ieee', locale = 'en-US',
51
+ numeric_in_appearance_order = false, output = :asciidoc,
52
+ throw_on_unknown = false)
53
+ raise "File '#{bibfile}' is not found" unless FileTest.file? bibfile
54
+
55
+ bibtex = BibTeX.open bibfile, filter: [LatexFilter]
56
+ @biblio = bibtex
57
+ @links = links
58
+ @numeric_in_appearance_order = numeric_in_appearance_order
59
+ @style = style
60
+ @locale = locale
61
+ @citations = []
62
+ @filenames = Set.new
63
+ @output = output
64
+ @throw_on_unknown = throw_on_unknown
65
+
66
+ if (output != :latex) && (output != :bibtex) && (output != :biblatex)
67
+ @citeproc = CiteProc::Processor.new style: @style, format: :html, locale: @locale
68
+ @citeproc.import @biblio.to_citeproc
69
+ end
70
+ end
71
+
72
+ # Scan a line and process citation macros.
73
+ #
74
+ # As this function being called iteratively on the lines of the document,
75
+ # processor will build a list of all citation keys in the same order as they
76
+ # appear in the original document.
77
+ def process_citation_macros(line)
78
+ CitationMacro.extract_citations(line).each do |citation|
79
+ @citations += citation.items.collect(&:key)
80
+ end
81
+ end
82
+
83
+ # Finalize citation macro processing and build internal citation list.
84
+ #
85
+ # As this function being called, processor will clean up the list of
86
+ # citation keys to form a correct ordered citation list.
87
+ def finalize_macro_processing
88
+ @citations = @citations.uniq(&:to_s) # only keep the first occurance
89
+ return if StyleUtils.is_numeric?(@style) && @numeric_in_appearance_order
90
+
91
+ @citations = @citations.sort_by do |ref|
92
+ bibitem = @biblio[ref]
93
+ if bibitem.nil?
94
+ [ref]
95
+ else
96
+ # extract the reference, and uppercase.
97
+ # Remove { } from grouped names for sorting.
98
+ author = bibitem.author
99
+ author = bibitem.editor if author.nil?
100
+ CitationUtils.author_chicago(author).collect { |s| s.upcase.gsub('{', '').gsub('}', '') } + [bibitem.year]
101
+ end
102
+ end
103
+ nil
104
+ end
105
+
106
+ # Replace citation macros with corresponding citation texts.
107
+ #
108
+ # Return new text with all macros replaced.
109
+ def replace_citation_macros(line)
110
+ CitationMacro.extract_citations(line).each do |citation|
111
+ line = line.gsub(citation.text, build_citation_text(citation))
112
+ end
113
+ line
114
+ end
115
+
116
+ # Build the bibliography list just as bibtex.
117
+ #
118
+ # Return an array of texts representing an asciidoc list.
119
+ def build_bibliography_list
120
+ result = []
121
+ @citations.each do |ref|
122
+ result << build_bibliography_item(ref)
123
+ result << ''
124
+ end
125
+ result
126
+ end
127
+
128
+ #
129
+ # Internal functions
130
+ #
131
+
132
+ # Build bibliography text for a given reference
133
+ def build_bibliography_item(key)
134
+ result = ''
135
+ result << '. ' if StyleUtils.is_numeric? @style
136
+
137
+ begin
138
+ cptext = @citeproc.render :bibliography, id: key
139
+ rescue Exception => e
140
+ puts "Failed to render #{key}: #{e}"
141
+ end
142
+ result << "[[#{key}]]" if @links
143
+ if cptext.nil?
144
+ return result + key
145
+ else
146
+ result << cptext.first
147
+ end
148
+
149
+ StringUtils.html_to_asciidoc(result)
150
+ end
151
+
152
+ # Build the complete citation text for given citation macro
153
+ def build_citation_text(macro)
154
+ if (@output == :latex) || (@output == :bibtex) || (@output == :biblatex)
155
+ result = '+++'
156
+ macro.items.each do |cite|
157
+ # NOTE: xelatex does not support "\citenp", so we output all
158
+ # references as "cite" here unless we're using biblatex.
159
+ result << '\\' << if @output == :biblatex
160
+ if macro.type == 'citenp'
161
+ 'textcite'
162
+ else
163
+ 'parencite'
164
+ end
165
+ else
166
+ 'cite'
167
+ end
168
+ result << '[p. ' << cite.locator << ']' if cite.locator != ''
169
+ result << '{' << cite.key.to_s << '},'
170
+ end
171
+ result = result[0..-2] if result[-1] == ','
172
+ result << '+++'
173
+ result
174
+ else
175
+ result = ''
176
+ if StyleUtils.is_numeric? @style
177
+ ob = '+[+'
178
+ cb = '+]+'
179
+ separator = ','
180
+ elsif macro.type == 'cite'
181
+ ob = '('
182
+ cb = ')'
183
+ separator = ';'
184
+ else
185
+ ob = ''
186
+ cb = ''
187
+ separator = ';'
188
+ end
189
+
190
+ macro.items.each_with_index do |cite, index|
191
+ # before all items apart from the first, insert appropriate separator
192
+ result << "#{separator} " unless index.zero?
193
+
194
+ # @links requires adding hyperlink to reference
195
+ result << "<<#{cite.key}," if @links
196
+
197
+ # if found, insert reference information
198
+ if @biblio[cite.key].nil?
199
+ if @throw_on_unknown
200
+ raise "Unknown reference: #{cite.ref}"
201
+ else
202
+ puts "Unknown reference: #{cite.ref}"
203
+ cite_text = cite.ref.to_s
204
+ end
205
+ else
206
+ cite_text = citation_text(macro, cite)
207
+ end
208
+
209
+ result << StringUtils.html_to_asciidoc(cite_text)
210
+ result << '>>' if @links
211
+ end
212
+
213
+ if StyleUtils.is_numeric?(@style) && !@links
214
+ result = StringUtils.combine_consecutive_numbers(result)
215
+ end
216
+
217
+ include_pretext result, macro, ob, cb
218
+ end
219
+ end
220
+
221
+ # Format locator with pp/p as appropriate
222
+ def format_locator(cite)
223
+ result = ''
224
+ unless cite.locator.empty?
225
+ result << ',' unless StyleUtils.is_numeric? @style
226
+ result << ' '
227
+ # use p.x for single numerical page and pp.x for all others. This will
228
+ # produce pp. 1 seq for complex locators, which is the correct behavior.
229
+ if @style.include? 'chicago'
230
+ result << cite.locator
231
+ elsif /^\d+$/ =~ cite.locator
232
+ result << "p.&#160;#{cite.locator}"
233
+ else
234
+ result << "pp.&#160;#{cite.locator}"
235
+ end
236
+ end
237
+
238
+ result
239
+ end
240
+
241
+ def include_pretext(result, macro, ob, cb)
242
+ pretext = macro.pretext
243
+ pretext += ' ' unless pretext.empty? # add space after any content
244
+
245
+ if StyleUtils.is_numeric? @style
246
+ "#{pretext}#{ob}#{result}#{cb}"
247
+ elsif macro.type == 'cite'
248
+ "#{ob}#{pretext}#{result}#{cb}"
249
+ else
250
+ "#{pretext}#{result}"
251
+ end
252
+ end
253
+
254
+ # Generate a raw citation text for a single citation item
255
+ def citation_text(macro, cite)
256
+ if StyleUtils.is_numeric? @style
257
+ cite_text = (@citations.index(cite.key) + 1).to_s
258
+ cite_text << format_locator(cite)
259
+ else
260
+ # We generate the citation without locator using citeproc, then strip
261
+ # the surrounding braces, finally add the locator and add braces for
262
+ # `citenp`.
263
+ cite_text = @citeproc.render :citation, id: cite.key
264
+ cite_text = cite_text.gsub('(', '')
265
+ cite_text = cite_text.gsub(')', '')
266
+ cite_text = cite_text + format_locator(cite)
267
+ year = @biblio[cite.key].year
268
+ if !year.nil? && macro.type == 'citenp'
269
+ segs = cite_text.partition(year.to_s)
270
+ head = segs[0].gsub(', ', ' ')
271
+ tail = segs[1..-1].join
272
+ cite_text = "#{head}(#{tail})"
273
+ end
274
+ # finally escape some special chars
275
+ cite_text = cite_text.gsub(',', '&#44;') if @links # replace comma
276
+ end
277
+
278
+ cite_text
279
+ end
280
+ end
281
+ end