asciidoctor-bibliography 0.0.1.dev

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