asciidoctor-bibtex 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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