asciidoctor-bibliography 0.0.1.dev

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +50 -0
  3. data/Gemfile +21 -0
  4. data/Gemfile.lock +41 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.adoc +101 -0
  7. data/SYNTAX.adoc +117 -0
  8. data/asciidoctor-bibliography.gemspec +37 -0
  9. data/deprecated/asciidoctor-bibliography/asciidoctor/bibliographer_postprocessor.rb +23 -0
  10. data/deprecated/asciidoctor-bibliography/asciidoctor/bibliography_block_macro.rb +77 -0
  11. data/deprecated/asciidoctor-bibliography/asciidoctor/citation_processor.rb +144 -0
  12. data/deprecated/asciidoctor-bibliography/asciidoctor/cite_inline_macro.rb +30 -0
  13. data/deprecated/asciidoctor-bibliography/citationdata.rb +23 -0
  14. data/deprecated/asciidoctor-bibliography/citations.rb +45 -0
  15. data/deprecated/asciidoctor-bibliography/citationutils.rb +67 -0
  16. data/deprecated/asciidoctor-bibliography/extensions.rb +64 -0
  17. data/deprecated/asciidoctor-bibliography/filehandlers.rb +32 -0
  18. data/deprecated/asciidoctor-bibliography/index.rb +31 -0
  19. data/deprecated/asciidoctor-bibliography/processor.rb +208 -0
  20. data/deprecated/asciidoctor-bibliography/processorutils.rb +34 -0
  21. data/deprecated/asciidoctor-bibliography/styles.rb +27 -0
  22. data/lib/asciidoctor-bibliography.rb +3 -0
  23. data/lib/asciidoctor-bibliography/asciidoctor.rb +17 -0
  24. data/lib/asciidoctor-bibliography/asciidoctor/bibliographer_preprocessor.rb +68 -0
  25. data/lib/asciidoctor-bibliography/bibliographer.rb +31 -0
  26. data/lib/asciidoctor-bibliography/citation.rb +73 -0
  27. data/lib/asciidoctor-bibliography/database.rb +20 -0
  28. data/lib/asciidoctor-bibliography/databases/bibtex.rb +43 -0
  29. data/lib/asciidoctor-bibliography/formatters/csl.rb +12 -0
  30. data/lib/asciidoctor-bibliography/formatters/tex.rb +164 -0
  31. data/lib/asciidoctor-bibliography/helpers.rb +40 -0
  32. data/lib/asciidoctor-bibliography/index.rb +43 -0
  33. data/lib/asciidoctor-bibliography/version.rb +3 -0
  34. data/samples/.byebug_history +245 -0
  35. data/samples/biblio.bib +31 -0
  36. data/samples/latex_macros_in_bibtex/reference.bib +16 -0
  37. data/samples/latex_macros_in_bibtex/sample.adoc +13 -0
  38. data/samples/sample-authoryear.adoc +72 -0
  39. data/samples/sample-authoryear.html +550 -0
  40. data/samples/sample-numbers.adoc +72 -0
  41. data/samples/sample-numbers.html +550 -0
  42. metadata +187 -0
@@ -0,0 +1,20 @@
1
+ require_relative 'databases/bibtex'
2
+
3
+ module AsciidoctorBibliography
4
+ class Database < Array
5
+ # This is an array of citeproc entries.
6
+
7
+ def initialize(filename)
8
+ self.concat self.load(filename)
9
+ end
10
+
11
+ def load(filename)
12
+ if ['.bib', '.bibtex'].include? File.extname(filename)
13
+ Databases::BibTeX.load(filename)
14
+ else
15
+ raise StandardError, "Unknown bibliographic database format."
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,43 @@
1
+ require 'bibtex'
2
+ require 'bibtex/filters'
3
+ require 'latex/decode/base'
4
+ require 'latex/decode/maths'
5
+ require 'latex/decode/accents'
6
+ require 'latex/decode/diacritics'
7
+ require 'latex/decode/punctuation'
8
+ require 'latex/decode/symbols'
9
+ require 'latex/decode/greek'
10
+
11
+ module AsciidoctorBibliography
12
+ module Databases
13
+ module BibTeX
14
+ def self.load(filename)
15
+ ::BibTeX.open(filename, filter: [LatexFilter]).to_citeproc
16
+ end
17
+
18
+ # This filter extends the original latex filter in bibtex-ruby to handle
19
+ # unknown latex macros more gracefully. We could have used latex-decode
20
+ # gem together with our custom replacement rules, but latex-decode eats up
21
+ # all braces after it finishes all decoding. So we hack over the
22
+ # LaTeX.decode function and insert our rules before `strip_braces`.
23
+ class LatexFilter < ::BibTeX::Filter
24
+ def apply(value)
25
+ text = value.to_s
26
+ LaTeX::Decode::Base.normalize(text)
27
+ LaTeX::Decode::Maths.decode!(text)
28
+ LaTeX::Decode::Accents.decode!(text)
29
+ LaTeX::Decode::Diacritics.decode!(text)
30
+ LaTeX::Decode::Punctuation.decode!(text)
31
+ LaTeX::Decode::Symbols.decode!(text)
32
+ LaTeX::Decode::Greek.decode!(text)
33
+ text.gsub!(/\\url\{(.+?)\}/, " \\1 ")
34
+ text.gsub!(/\\\w+(?=\s+\w)/, "")
35
+ text.gsub!(/\\\w+(?:\[.+?\])?\s*\{(.+?)\}/, "\\1")
36
+ LaTeX::Decode::Base.strip_braces(text)
37
+ LaTeX.normalize_C(text)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,12 @@
1
+ require 'citeproc'
2
+ require 'csl/styles'
3
+
4
+ module AsciidoctorBibliography
5
+ module Formatters
6
+ class CSL < ::CiteProc::Processor
7
+ def initialize(style)
8
+ super style: style, format: :html
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,164 @@
1
+ module AsciidoctorBibliography
2
+ module Formatters
3
+ # This formatter emulates the behaviour of traditional Bib(La)TeX/NatBib citations.
4
+ class TeX
5
+ MACROS = {
6
+ # NOTE: cite = citet
7
+ 'cite' => { type: :textual, bracketed: true, authors: :abbreviated },
8
+ 'citet' => { type: :textual, bracketed: true, authors: :abbreviated },
9
+ 'citet*' => { type: :textual, bracketed: true, authors: :full },
10
+ 'citealt' => { type: :textual, bracketed: false, authors: :abbreviated },
11
+ 'citealt*' => { type: :textual, bracketed: false, authors: :full },
12
+ 'citep' => { type: :parenthetical, bracketed: true, authors: :abbreviated },
13
+ 'citep*' => { type: :parenthetical, bracketed: true, authors: :full },
14
+ 'citealp' => { type: :parenthetical, bracketed: false, authors: :abbreviated },
15
+ 'citealp*' => { type: :parenthetical, bracketed: false, authors: :full },
16
+ 'citeauthor' => { type: :authors_only, bracketed: false, authors: :abbreviated },
17
+ 'citeauthor*' => { type: :authors_only, bracketed: false, authors: :full },
18
+ 'citeyear' => { type: :years_only, bracketed: false },
19
+ 'citeyearpar' => { type: :years_only, bracketed: true }
20
+ }
21
+
22
+ attr_accessor :opening_bracket,
23
+ :closing_bracket,
24
+ :cites_separator,
25
+ :style,
26
+ :author_year_separator,
27
+ :years_separator
28
+
29
+ def initialize(format)
30
+ if format == 'numbers'
31
+ bibpunct = '{[}{]}{,}{n}{,}{,}'
32
+ elsif format == 'authoryear'
33
+ bibpunct = '{(}{)}{;}{a}{,}{,}'
34
+ else
35
+ raise StandardError, "Unknown TeX citation format: #{format}"
36
+ end
37
+ @opening_bracket,
38
+ @closing_bracket,
39
+ @cites_separator,
40
+ @style,
41
+ @author_year_separator,
42
+ @years_separator = bibpunct.scan(/{.*?}/).map { |s| s[1..-2] }
43
+ end
44
+
45
+ def import(database)
46
+ @database = database
47
+ end
48
+
49
+ def render(citation)
50
+ macro_options = MACROS[citation.macro]
51
+ output = []
52
+ case macro_options[:type]
53
+ when :full
54
+ # NOTE: deliberately repetitive to improve redability.
55
+ when :textual
56
+ citation.cites.each do |cite|
57
+ authors = authors(macro_options[:authors], cite)
58
+ if @style == 'n'
59
+ year = cite.occurrence_index + 1
60
+ else
61
+ year = year(cite)
62
+ end
63
+ cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + ' ')
64
+ cetera = bracket(cetera) if macro_options[:bracketed]
65
+ label = Helpers.join_nonempty([authors, cetera], ' ')
66
+ output << citation.xref(cite.key, label)
67
+ end
68
+ output = output.join(@cites_separator + ' ')
69
+ when :parenthetical
70
+ citation.cites.each do |cite|
71
+ if @style == 'n'
72
+ authors = nil
73
+ year = cite.occurrence_index + 1
74
+ else
75
+ authors = authors(macro_options[:authors], cite)
76
+ year = year(cite)
77
+ end
78
+ cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + ' ')
79
+ label = Helpers.join_nonempty([authors, cetera], @author_year_separator + ' ')
80
+ output << citation.xref(cite.key, label)
81
+ end
82
+ output = output.join(@cites_separator + ' ')
83
+ output = bracket(output) if macro_options[:bracketed]
84
+ when :authors_only
85
+ citation.cites.each do |cite|
86
+ authors = authors(macro_options[:authors], cite)
87
+ year = nil
88
+ cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + ' ')
89
+ label = Helpers.join_nonempty([authors, cetera], @author_year_separator + ' ')
90
+ output << citation.xref(cite.key, label)
91
+ end
92
+ output = output.join(@cites_separator + ' ')
93
+ output = bracket(output) if macro_options[:bracketed]
94
+ when :years_only
95
+ citation.cites.each do |cite|
96
+ authors = nil
97
+ year = year(cite)
98
+ cetera = Helpers.join_nonempty([year].concat(extra(cite)), @years_separator + ' ')
99
+ label = Helpers.join_nonempty([authors, cetera], @author_year_separator + ' ')
100
+ output << citation.xref(cite.key, label)
101
+ end
102
+ output = output.join(@cites_separator + ' ')
103
+ output = bracket(output) if macro_options[:bracketed]
104
+ else
105
+ raise StandardError, "Unknown TeX citation macro type: #{macro_options[:type]}"
106
+ end
107
+
108
+ output
109
+ end
110
+
111
+ private
112
+
113
+ def bracket(string)
114
+ [@opening_bracket, string, @closing_bracket].compact.join
115
+ end
116
+
117
+ def year(cite)
118
+ issued = @database.find{ |h| h['id'] == cite.key }['issued']['date-parts']
119
+ return "" if issued.nil?
120
+ return "" if issued.first.nil?
121
+ issued.first.first
122
+ end
123
+
124
+ def extra(cite)
125
+ na = cite.named_attributes
126
+ extra = []
127
+ return extra if na.nil?
128
+ # TODO: should this be configurable?
129
+ extra << "Chapter #{na['chapter']}" unless na['chapter'].nil?
130
+ extra << "Page #{na['page']}" unless na['page'].nil?
131
+ extra
132
+ end
133
+
134
+ def authors(mode, cite)
135
+ case mode
136
+ when :full
137
+ authors_full(cite)
138
+ when :abbreviated
139
+ authors_abbreviated(cite)
140
+ else
141
+ raise StandardError, "Unknown TeX citation authors mode: #{mode}"
142
+ end
143
+ end
144
+
145
+ def authors_list(cite)
146
+ authors = @database.find{ |h| h['id'] == cite.key }['author']
147
+ return [] if authors.nil?
148
+ authors.map{ |h| h['family'] }.compact
149
+ end
150
+
151
+ def authors_abbreviated(cite)
152
+ authors = authors_list(cite)
153
+ return "" if authors.empty?
154
+ authors.length > 1 ? "#{authors.first} et al." : authors.first
155
+ end
156
+
157
+ def authors_full(cite)
158
+ authors = authors_list(cite)
159
+ return "" if authors.empty?
160
+ Helpers.to_sentence authors
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,40 @@
1
+ module AsciidoctorBibliography
2
+ module Helpers
3
+ def self.slice(hash, *array_of_keys)
4
+ Hash[[array_of_keys, hash.values_at(*array_of_keys)].transpose]
5
+ end
6
+
7
+ def self.join_nonempty(array, separator)
8
+ array.compact.map(&:to_s).reject(&:empty?).join(separator)
9
+ end
10
+
11
+ def self.html_to_asciidoc(string)
12
+ string
13
+ .gsub(/<\/?i>/, '_')
14
+ .gsub(/<\/?b>/, '*')
15
+ .gsub(/<\/?span.*?>/, '')
16
+ .gsub(/\{|\}/, '')
17
+ end
18
+
19
+ # NOTE: mostly stolen from ActiveSupport.
20
+ def self.to_sentence(array, options = {})
21
+ default_connectors = {
22
+ :words_connector => ', ',
23
+ :two_words_connector => ' and ',
24
+ :last_word_connector => ', and '
25
+ }
26
+ options = default_connectors.merge!(options)
27
+
28
+ case array.length
29
+ when 0
30
+ ''
31
+ when 1
32
+ array[0].to_s.dup
33
+ when 2
34
+ "#{array[0]}#{options[:two_words_connector]}#{array[1]}"
35
+ else
36
+ "#{array[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{array[-1]}"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ require 'asciidoctor/attribute_list'
2
+
3
+ require_relative 'helpers'
4
+
5
+ module AsciidoctorBibliography
6
+ class Index
7
+ REGEXP = /^(bibliography)::(\S+)?\[(|.*?[^\\])\]$/
8
+
9
+ attr_reader :macro, :target, :attributes
10
+
11
+ def initialize(macro, target, attributes)
12
+ @macro = macro
13
+ @target = target
14
+ @attributes = ::Asciidoctor::AttributeList.new(attributes).parse
15
+ end
16
+
17
+ def render(bibliographer)
18
+ lines = []
19
+ bibliographer.occurring_keys.each_with_index do |target, index|
20
+ line = '{empty}'
21
+ line << "[#{index + 1}] " if bibliographer.options['citation-style'] == 'numbers'
22
+ line << render_entry(target, bibliographer.index_formatter)
23
+ lines << line
24
+ end
25
+
26
+ # Intersperse the lines with empty ones to render as paragraphs.
27
+ lines.join("\n\n").lines.map(&:strip)
28
+ end
29
+
30
+ def render_entry_id(target)
31
+ ['bibliography', target].compact.join('-')
32
+ end
33
+
34
+ def render_entry_label(target, formatter)
35
+ Helpers.html_to_asciidoc formatter.render(:bibliography, id: target).join
36
+ end
37
+
38
+ def render_entry(target, formatter)
39
+ "anchor:#{render_entry_id(target)}[]#{render_entry_label(target, formatter)}"
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,3 @@
1
+ module AsciidoctorBibliography
2
+ VERSION = '0.0.1.dev'
3
+ end
@@ -0,0 +1,245 @@
1
+ c
2
+ q
3
+ puts processed_lines
4
+ q
5
+ puts processed_lines
6
+ processed_lines
7
+ q
8
+ puts processed_lines.lines.map(&:chomp)
9
+ processed_lines.lines.map(&:chomp)
10
+ processed_lines.lines
11
+ processed_lines
12
+ q
13
+ Helpers.html_to_asciidoc form
14
+ form
15
+ q
16
+ document.bibliographer.options
17
+ q
18
+ Hash[document_attributes]
19
+ ENV
20
+ document_attributes
21
+ document_attributes.to_hash
22
+ document_attributes.to_h
23
+ document_attributes
24
+ q
25
+ cites.first.key
26
+ cites.first
27
+ cites
28
+ q
29
+ self.macro
30
+ self
31
+ q
32
+ output
33
+ q
34
+ @closing_bracket
35
+ q
36
+ bibpunct.scan(/{.*?}/)
37
+ bibpunct.scan(/{*.?}/)
38
+ puts bibpunct
39
+ bibpunct = '{[}{\\]}{,}{n}{,}{,}'
40
+ bibpunct.length
41
+ bibpunct = '{[}{]}{,}{n}{,}{,}'
42
+ bibpunct.length
43
+ bibpunct = '{[}{\\]}{,}{n}{,}{,}'
44
+ bibpunct.scan(/{*.?}/)
45
+ bibpunct = '{[}{\\]}{,}{n}{,}{,}'
46
+ '\'
47
+ bibpunct = '{[}{\]}{,}{n}{,}{,}'
48
+ bibpunct.scan(/{*.?}/)
49
+ bibpunct = '{[}{\\]}{,}{n}{,}{,}'
50
+ bibpunct.scan(/{*.?}/)
51
+ bibpunct = '{[}{\]}{,}{n}{,}{,}'
52
+ bibpunct.scan(/{*.?}/)
53
+ bibpunct.scan(/{*.?}/).map { |s| s[1..-2] }
54
+ @closing_bracket
55
+ q
56
+ @closing_bracket
57
+ @opening_bracket
58
+ bracket "wot asd"
59
+ bracket "wot"
60
+ bracket(cetera)
61
+ label
62
+ citation.xref(cite.key, label)
63
+ output
64
+ q
65
+ y
66
+ rend
67
+ q
68
+ rend
69
+ q
70
+ rend
71
+ q
72
+ rend
73
+ q
74
+ rend
75
+ q
76
+ document.bibliographer.citations.last.cites.last.occurrence_index
77
+ document.bibliographer.citations.last.cites.last
78
+ document.bibliographer.citations.last.cites
79
+ document.bibliographer.citations.last
80
+ document.bibliographer.citations
81
+ document.bibliographer
82
+ q
83
+ citations.first.cites.first
84
+ citations.first.cites.first.target = "FOO"
85
+ citations.first.cites.first.target
86
+ citations.first.cites.first
87
+ citations.first.cites
88
+ citations.first
89
+ citations
90
+ q
91
+ document.bibliographer.citations.first.uuid
92
+ document.bibliographer.citations.first
93
+ document.bibliographer.citations
94
+ reader.lines.join("\n").lines.map(&:chomp)
95
+ reader.lines.join("\n").lines
96
+ reader.lines.join("\n")
97
+ reader.lines
98
+ reader
99
+ reader.lines
100
+ reader
101
+ q
102
+ reader.processed_lines
103
+ reader.lines
104
+ reader
105
+ processed_lines.join("\n").lines
106
+ processed_lines.join('\n').lines
107
+ processed_lines.join('\n')
108
+ processed_lines.join('"\n')
109
+ processed_lines.join
110
+ q
111
+ processed_lines
112
+ q
113
+ bibliographer.cited_keys.index(cite.key)q
114
+ bibliographer.cited_keys.index(cite.key)
115
+ bibliographer.cited_keys
116
+ bibliographer
117
+ q
118
+ document
119
+ cite
120
+ q
121
+ cite.named_attributes || {}
122
+ cite.named_attributes
123
+ extra(cite)
124
+ c
125
+ cetera
126
+ c
127
+ cetera
128
+ c
129
+ cetera
130
+ n
131
+ c
132
+ [year(cite)].concat(extra(cite)).join(@years_separator + ' ')
133
+ [year(cite)].concat(extra(cite))
134
+ q
135
+ extra
136
+ extr
137
+ q
138
+ cite.named_attributes['page']
139
+ cite.named_attributes
140
+ cite
141
+ q
142
+ y
143
+ cq
144
+ c
145
+ q
146
+ ct.key
147
+ ct
148
+ q
149
+ ct
150
+ n
151
+ ct
152
+ q
153
+ ct[:attributes][:named]['pagea']
154
+ ct[:attributes][:named]['page']
155
+ ct[:attributes][:named]['page]
156
+ ct[:attributes][:named]
157
+ ct[:named]
158
+ ct[:named]['page']
159
+ ct
160
+ q
161
+ ct[:key]
162
+ ct.key
163
+ ct
164
+ cite
165
+ ct
166
+ q
167
+ document.bibliographer.citations
168
+ q
169
+ TeXFormatter::DEFAULTS
170
+ TeXFormatter
171
+ q
172
+ @opening_bracket
173
+ bibpunct
174
+ q
175
+ Citation::DEFAULTS[cite.macro][:type]
176
+ q
177
+ Citation::DEFAULTS['citet'][:type]
178
+ Citation::DEFAULTS['citet'][:tyep]
179
+ Citation::DEFAULTS['citet']
180
+ Citation::DEFAULTS[cite.macro]
181
+ Citation::DEFAULTS[cite.macro][:type]
182
+ q
183
+ continue
184
+ Citation::DEFAULTS['citet']
185
+ Citation::DEFAULTS[:cite]
186
+ Citation::DEFAULTS['cite']
187
+ Citation::DEFAULTS
188
+ Citation
189
+ cite.macro
190
+ cite
191
+ q
192
+ c
193
+ q
194
+ [].empty?
195
+ [].blank?
196
+ [].any?
197
+ @database.find{ |h| h['id'] == ct[:key] }['issued']['date-parts'].first.first
198
+ @database.find{ |h| h['id'] == ct[:key] }['issued']['date-parts'].first
199
+ @database.find{ |h| h['id'] == ct[:key] }['issued']['date-parts']
200
+ @database.find{ |h| h['id'] == ct[:key] }['issued']
201
+ @database.find{ |h| h['id'] == ct[:key] }
202
+ ct
203
+ q
204
+ continue
205
+ c
206
+ q
207
+ cite
208
+ q
209
+ Helpers.to_sentence(authors_family_names)
210
+ n
211
+ Helpers.to_sentence(authors_family_names)
212
+ q
213
+ Helpers.to_sentence(authors_family_names)
214
+ n
215
+ c
216
+ ci.key
217
+ ci
218
+ c
219
+ cite.cites
220
+ cite
221
+ c
222
+ @database.find{ |h| h['id'] == 'asd' }
223
+ @database.find{ |h| h['id'] == c.key }
224
+ @database.find{ |h| h['id'] == c.key }['author']
225
+ q
226
+ cite.cites.first[:key]
227
+ cite.cites.first['key']
228
+ cite.cites
229
+ cite.cites.key
230
+ cite
231
+ @database.find { |h| h['id'] == 'Anderson04' }['author'].map { |h| h['family'] }.compact
232
+ @database.find { |h| h['id'] == 'Anderson04' }['author'].map { |h| h['family'] }
233
+ @database.find { |h| h['id'] == 'Anderson04' }['author']
234
+ @database.find { |h| h['id'] == 'Anderson04' }
235
+ self.find { |h| h['id'] == 'Anderson04' }
236
+ @database.
237
+ cite
238
+ q
239
+ c
240
+ q
241
+ self.find { |h| h['id'] == 'Lane12a' }
242
+ self.find(id: 'Lane12a').first
243
+ self.find(id: 'Lane12a')
244
+ self.find
245
+ self