-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathciteproc-date.el
281 lines (250 loc) · 10.4 KB
/
citeproc-date.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
;;; citeproc-date.el --- CSL date rendering -*- lexical-binding: t; -*-
;; Copyright (C) 2017 András Simonyi
;; Author: András Simonyi <andras.simonyi@gmail.com>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Structure type and functions to render CSL date elements.
;;; Code:
(require 'subr-x)
(require 'cl-lib)
(require 'dash)
(require 'let-alist)
(require 's)
(require 'citeproc-lib)
(require 'citeproc-rt)
(require 'citeproc-context)
(require 'citeproc-number)
(cl-defstruct (citeproc-date (:constructor citeproc-date-create))
"Struct for representing dates.
Slots YEAR, MONTH, DAY are integers, while SEASON and CIRCA are
booleans. SEASON indicates whether the integer in slot MONTH is
to be interpreted as a season number."
(year nil) (month nil) (day nil) (season nil) (circa nil))
(defun citeproc-date-parse (date-rep)
"Parse CSL json date repr. DATE-REP into an internal one."
(let-alist date-rep
(--map (citeproc-date--conv it .season .circa) .date-parts)))
(defun citeproc-date--conv (dates &optional season circa)
"Convert date-part list DATES to a citeproc-date struct.
Set the remaining slots to the values SEASON and CIRCA."
(-let* ((numeric
(--map (if (stringp it) (string-to-number it) it) dates))
((year month day) numeric))
(citeproc-date-create :year year :month month :day day
:season season :circa circa)))
(defun citeproc-date--partattrs-for-sort (part-attrs)
"Return a sort-key version of PART-ATTRS."
(let (result)
(when (assoc 'day part-attrs)
(push '(day . ((form . "numeric-leading-zeros"))) result))
(when (assoc 'month part-attrs)
(push '(month . ((form . "numeric-leading-zeros"))) result))
(when (assoc 'year part-attrs)
(push '(year . ((form . "long"))) result))
result))
(defun citeproc--date (attrs context &rest body)
"Function corresponding to the date CSL element."
(-let* (((&alist 'variable var
'form form)
attrs)
(var-sym (intern var))
(parsed-dates (citeproc-var-value var-sym context))
((d1 d2) parsed-dates)
(result
(if d1
(progn
(when form
(let ((localized (citeproc-date--localized-attrs attrs body context)))
(setq attrs (car localized)
body (cdr localized))))
(when (eq (citeproc-context-render-mode context) 'sort)
(setq body (citeproc-date--partattrs-for-sort body)))
(if (citeproc-date--renders-with-attrs-p d1 body)
(progn
(push `(rendered-var . ,(intern var)) attrs)
(cons (if d2
(citeproc-date--render-range d1 d2 attrs body context)
(citeproc-date--render d1 attrs body context))
'present-var))
(cons nil 'empty-vars)))
(cons nil 'empty-vars))))
;; Handle `year' citation mode by stopping if needed
(citeproc-context-maybe-stop-rendering 'issued context result var-sym)))
(defun citeproc--date-part (attrs _context &rest _body)
"Function corresponding to the date-part CSL element."
(cons (intern (alist-get 'name attrs))
attrs))
(defun citeproc-date--renders-with-attrs-p (date part-attrs)
"Whether DATE contains date-parts corresponding to PART-ATTRS."
(let ((date-parts (mapcar #'car part-attrs)))
(or (memq 'year date-parts) ; All dates contain a year
(and (memq 'month date-parts) (citeproc-date-month date))
(and (memq 'day date-parts) (citeproc-date-day date)))))
(defun citeproc-date--localized-attrs (attrs part-attrs context)
"Return the localized date attrs merged with date ATTRS and date PART-ATTRS."
(-let* (((&alist 'form form
'date-parts date-parts)
attrs)
((loc-attrs . loc-part-attrs)
(if (string= form "text") (citeproc-context-date-text context)
(citeproc-context-date-numeric context))))
(pcase (citeproc-lib-intern date-parts)
('year
(setq loc-part-attrs
(--select (eq (car it) 'year) loc-part-attrs)))
('year-month
(setq loc-part-attrs
(--select (memq (car it) '(year month)) loc-part-attrs))))
(cons (-concat attrs loc-attrs)
(--map (cons (car it)
(-concat (alist-get (car it) part-attrs) (cdr it)))
loc-part-attrs))))
(defun citeproc-date--render (d attrs part-attrs context)
"Render citeproc-date D according to formatting in ATTRS and PART-ATTRS.
Return a rich-text content."
(if (citeproc-var-value 'suppress-date context)
(citeproc-rt-format-single attrs "<suppressed-date>" context)
(let ((rendered-date (citeproc-date--render-parts d part-attrs context)))
(citeproc-rt-join-formatted attrs rendered-date context))))
(defun citeproc-date--render-parts (d part-attrs context &optional no-last-suffix)
"Render the parts of citeproc-date D according to PART-ATTRS.
Return a list of rich-text contents. If optional NO-LAST-SUFFIX
is non-nil then remove the suffix attribute of the last rendered
element (used for date range rendering)."
(let ((result (--map (pcase (car it)
('year (citeproc-date--render-year d (cdr it) context))
('month (citeproc-date--render-month d (cdr it) context))
('day (citeproc-date--render-day d (cdr it) context)))
part-attrs)))
(-if-let* ((n-l-s no-last-suffix)
(last (car (last result)))
(wo-suffix (and (consp last)
(cons (--remove (eq 'suffix (car it)) (car last))
(cdr last)))))
(-snoc (butlast result) wo-suffix)
result)))
(defun citeproc-date--render-range-parts (d1 d2 part-attrs sep context)
"Render the parts of citeproc-dates D1 and D2 according to PART-ATTRS.
PART-ATTRS is a list containing either part-attrs or lists of part-attrs.
The formers are only rendered for D1, while the latters are rendered for both
D1 and D2. Return a list of rich-text contents."
(--mapcat (pcase (car it)
('year (list (citeproc-date--render-year d1 (cdr it) context)))
('month (list (citeproc-date--render-month d1 (cdr it) context)))
('day (list (citeproc-date--render-day d1 (cdr it) context)))
(_ (-concat (citeproc-date--render-parts d1 it context t)
(list sep)
(citeproc-date--render-parts d2 it context))))
part-attrs))
(defun citeproc-date--render-range (d1 d2 attrs part-attrs context)
"Render the range given by dates D1 D2 according to attrs."
(if (citeproc-var-value 'suppress-date context)
(citeproc-rt-format-single attrs "" context)
(let* ((gran (min (citeproc-date--gran d1)
(citeproc-date--attrs-gran part-attrs)))
(range-sep (or (alist-get 'range-delimiter
(alist-get (elt '(year month day) gran)
part-attrs))
"–"))
(range-p-attrs
(cond ((/= (citeproc-date-year d1) (citeproc-date-year d2))
(list part-attrs))
((/= (citeproc-date-month d1) (citeproc-date-month d2))
(let ((year-part (--find (eq 'year (car it))
part-attrs))
(attrs-wo-year
(--remove (eq 'year (car it))
part-attrs)))
(cond ((eq (caar part-attrs) 'year)
(list year-part attrs-wo-year))
((eq (caar (last part-attrs)) 'year)
(list attrs-wo-year year-part))
(t (list attrs-wo-year)))))
(t (--map (if (eq (car it) 'day) (list it) it)
part-attrs))))
(rendered-range (citeproc-date--render-range-parts d1 d2 range-p-attrs range-sep
context)))
(citeproc-rt-join-formatted attrs rendered-range context))))
(defun citeproc-date--attrs-gran (d-attrs)
"Return the granularity (smallest unit) of date-attrs alist D-ATTRS.
The returned value is 0, 1 or 2, corresponding to a year, month
or day granularity."
(cond ((assoc 'day d-attrs) 2)
((assoc 'month d-attrs) 1)
(t 0)))
(defun citeproc-date--gran (date)
"Return the granularity (smallest unit) in citeproc-date struct DATE.
The returned value is 0, 1 or 2, corresponding to a year, month
or day granularity."
(cond ((citeproc-date-day date) 2)
((citeproc-date-month date) 1)
(t 0)))
(defun citeproc-date--render-year (d attrs context)
"Render the year in date D according to formatting in ATTRS.
D is a citeproc-date structure. Return a rich-text content."
(-let* ((form (alist-get 'form attrs))
(year (citeproc-date-year d))
(s (number-to-string (abs year)))
(era
(cond ((> year 999) "")
((> year 0) (citeproc-term-get-text "ad" context))
(t (citeproc-term-get-text "bc" context)))))
(citeproc-rt-format-single attrs (concat (if (string= form "short")
(s-right 2 s)
s)
era)
context)))
(defun citeproc-date--render-month (d attrs context)
"Render the month in date D according to formatting in ATTRS.
D is a citeproc-date structure. Return a rich-text content."
(-if-let (month (citeproc-date-month d))
(let ((form (alist-get 'form attrs))
(term-pref (if (citeproc-date-season d)
"season-" "month-")))
(citeproc-rt-format-single
attrs
(pcase (citeproc-lib-intern form)
('numeric (number-to-string month))
('numeric-leading-zeros (format "%02d" month))
('short (citeproc-term-inflected-text
(concat term-pref (format "%02d" month))
'short nil context))
(_ (citeproc-term-inflected-text
(concat term-pref (format "%02d" month))
'long nil context)))
context))
nil))
(defun citeproc-date--render-day (d attrs context)
"Render the day in date D according to formatting in ATTRS.
D is a citeproc-date structure. Return a rich-text content."
(-if-let (day (citeproc-date-day d))
(let ((form (alist-get 'form attrs))
(month (citeproc-date-month d)))
(citeproc-rt-format-single
attrs
(cond
((string= form "numeric-leading-zeros")
(format "%02d" day))
((and (string= form "ordinal")
(or (= day 1)
(not (string= "true"
(alist-get 'limit-day-ordinals-to-day-1
(citeproc-context-locale-opts context))))))
(citeproc-number--format-as-ordinal (number-to-string day)
(concat "month-" (format "%02d" month))
context))
(t (number-to-string day)))
context))
nil))
(provide 'citeproc-date)
;;; citeproc-date.el ends here