|
|
- ;;; readme.org --- org-ref-citeproc - Citation processor for org-mode
- ;;; Commentary: This code is style agnostic. It will get information from the
- ;;; information in `citation-style' and `bibliography-style'. These are defined
- ;;; in a csl.el file.
- ;;
- ;; Conventions:
- ;; an "entry" is the result of `bibtex-parse-entry'.
- ;;
-
- ;;; Commentary:
- ;;
-
- (declare-function org-ref-get-bibtex-key-and-file "org-ref-core")
- (declare-function org-ref-get-bibtex-keys "org-ref-core")
- (declare-function parsebib-find-bibtex-dialect "parsebib")
- (defvar org-export-current-backend)
- (defvar org-ref-cite-types)
-
- (require 'org-element)
-
-
-
- ;;; Code:
- (defvar *orcp-citation-links* '()
- "List of citation links in the text.
- A link may have more than one citation in it. These links get
- replaced by the new citation text.")
-
-
- (defvar *orcp-unique-entries* '()
- "List of unique (key . entry) parsed bibtex entries in the document.
- The list is sorted according to the style. This list eventually
- makes up the bibliography.")
-
-
- (defvar citation-style '()
- "Style data for an in-text citation.
- For unsrt, a regular cite is superscripted, sorted,
- range-collapsed numbers.
-
- LABEL is a function that is run to get the label.
- PREFIX goes before a citation.
- SUFFIX goes after a citation.
- citations are separated by DELIMITER.
- SORT specifies that 3, 1, 2 be converted to 1,2,3
- COLLAPSE is a function to collapse multiple citations, e.g. 1,2,3 becomes 1-3.
- VERTICAL-ALIGN is a function that places the citation, e.g.
- superscript, nil for baseline, etc...
-
- Additional entries provide overrides for special citation types.")
-
-
- (defvar bibliography-style '()
- "Bibliography style data.
- SORT is a function that sorts the entries, e.g. by author, or
- year, or nil. It should take one argument, the list of unique
- entries (key . entry).
-
- LABEL is a function that returns how the entry is numbered, or
- referenced in the text.
-
- HANGING-INDENT is for the indentation of the entry on the left.
-
- JUSTIFICATION is the overall justification on the right.
-
- SPACING is the number of lines between entries.
-
- HEADER is a string that is inserted above the bibliography.
-
- ENTRIES is a alist of entry type and fields to make the entry from.")
-
-
- ;;* Collect citations
-
- (defun orcp-collect-citations ()
- "Return a list of citation links in the document."
- (setq *orcp-citation-links*
- (cl-loop for link in (org-element-map
- (org-element-parse-buffer) 'link 'identity)
- if (-contains?
- org-ref-cite-types
- (org-element-property :type link))
- collect link)))
-
-
- (defun orcp-key-to-entry (key)
- "Return a parsed bibtex entry for KEY.
- The KEY is found for the bibliography in the file."
- (let* ((results (org-ref-get-bibtex-key-and-file key))
- (bibfile (cdr results)))
-
- (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
- (bibtex-search-entry key)
- (let ((entry (bibtex-parse-entry t)))
- (dolist (cons-cell entry)
- (setf (car cons-cell) (downcase (car cons-cell))))
- (setf (cdr (assoc "=type=" entry))
- (downcase (cdr (assoc "=type=" entry))))
- entry)))))
-
-
- (defun orcp-collect-unique-entries ()
- "Return a list of unique entries, sorted as required by the style.
- Each entry is (key . entry)."
- (let ((keys (org-ref-get-bibtex-keys))
- sort-func
- entries)
- (setq entries
- (cl-loop for key in keys
- collect (cons key (orcp-key-to-entry key))))
- ;; Now we should sort them if the style requires it
- (setq sort-func (cdr (assoc 'sort bibliography-style)))
-
- (when sort-func
- (setq entries (funcall sort-func entries)))
- (setq *orcp-unique-entries* entries)))
-
-
- ;;** Unique entry sorting functions
-
- (defun orcp-sort-entries-increasing-year (unique-entries)
- "Sort UNIQUE-ENTRIES in increasing year of publication.
- i.e. oldest stuff first."
- (sort unique-entries
- (lambda (a b)
- (let* ((e1 (cdr a))
- (e2 (cdr b))
- (year1 (string-to-number (cdr (assoc "year" e1))))
- (year2 (string-to-number (cdr (assoc "year" e2)))))
- (> year2 year1)))))
-
- (defun orcp-sort-entries-decreasing-year (unique-entries)
- "Sort UNIQUE-ENTRIES in decreasing year.
- i.e. most current first."
- (reverse (orcp-sort-entries-increasing-year unique-entries)))
-
- (defun orcp-get-entry-field (field entry)
- "RETURN FIELD from ENTRY.
- Strip extra spaces and carriage returns."
- (let ((result (cdr (assoc field entry))))
- (when result
- (while (string-match "[\n\t\r]\\|[ \t][ \t]+" result)
- (setq result (replace-match " " nil t result))))
- result))
-
-
- (defun orcp-sort-entries-alphabetical (unique-entries)
- "Sort UNIQUE-ENTRIES alphabetically by first author last name."
- (sort unique-entries
- (lambda (a b)
- (let* ((e1 (cdr a))
- (e2 (cdr b))
- (authors1 (s-split
- " and "
- (orcp-get-entry-field "author" e1)))
- (author1 (orcp-parse-authorname (car authors1)))
- ;; lastname is "von last"
- (last1 (concat (nth 1 author1) " " (nth 2 author1)))
- (authors2 (s-split
- " and "
- (orcp-get-entry-field "author" e2)))
- (author2 (orcp-parse-authorname (car authors2)))
- (last2 (concat (nth 1 author2) " " (nth 2 author2))))
- (string-lessp last1 last2)))))
-
-
- ;;* Citation labels for one citation key
- ;; No styling is done here.
-
- (defun orcp-citation-number-label (key unique-entries)
- "Find the numeric index of KEY in UNIQUE-ENTRIES and return as a string.
- Indexing starts at 0 so we add one."
- (number-to-string
- (+ 1
- (-find-index
- (lambda (entry)
- (string= key (car entry)))
- unique-entries))))
-
-
- (defun orcp-footnote-label (key unique-entries)
- "Return an org footnote label for KEY in UNIQUE-ENTRIES."
- (format "[fn:%s]" (orcp-citation-number-label key unique-entries)))
-
-
- (defun orcp-citation-author-label (key unique-entries)
- "Return an author last name label for KEY.
- KEY is found in UNIQUE-ENTRIES."
- (let* ((i (-find-index
- (lambda (entry)
- (string= key (car entry)))
- unique-entries))
- (entry (cdr (nth i unique-entries)))
- (authors (s-split
- " and "
- (orcp-get-entry-field "author" entry)))
- (first-author (orcp-parse-authorname (car authors))))
- (format "%s" (concat (nth 1 first-author)
- (nth 2 first-author)))))
-
- (defun orcp-citation-year-label (key unique-entries)
- "Return a year label for KEY.
- KEY is found in UNIQUE-ENTRIES."
- (let* ((i (-find-index
- (lambda (entry)
- (string= key (car entry)))
- unique-entries))
- (entry (cdr (nth i unique-entries)))
- (year (orcp-get-entry-field "year" entry)))
- (format "%s" year)))
-
-
- (defun orcp-citation-author-year-label (key unique-entries)
- "Return an author last name year label for KEY.
- KEY is found in UNIQUE-ENTRIES.
- We do not have a disambiguation strategy yet."
- (let* ((i (-find-index
- (lambda (entry)
- (string= key (car entry)))
- unique-entries))
- (entry (cdr (nth i unique-entries)))
- (authors (s-split
- " and "
- (orcp-get-entry-field "author" entry)))
- (first-author (orcp-parse-authorname (car authors)))
- (year (orcp-get-entry-field "year" entry)))
- (format "%s %s" (concat (nth 1 first-author)
- (nth 2 first-author))
- year)))
-
- ;;* Replacements for citation links
-
- ;; Here we have to map over the keys in a citation, sort them according to the
- ;; style, get replacement labels, concat them together with the style delimiter,
- ;; add the prefix and suffix, and finally format for the type and output
- ;; backend.
-
- (defun orcp-get-citation-style (symbol type)
- "Get the style info for SYMBOL for a citation TYPE from `citation-style'.
- Styles have a default, but allow TYPE overrides. This function
- returns the style with the override."
- (let (style)
- ;; first get default style
- (setq style (cdr (assoc symbol citation-style)))
-
- ;; now check for an override
- ;; we need to find the type, and the symbol in the type
- (when (and (assoc type citation-style)
- (assoc symbol (assoc type citation-style)))
- (setq style (cdr (assoc symbol (assoc type citation-style)))))
- style))
-
-
- (defun orcp-get-text-replacement (citation-link)
- "Return replacement string for CITATION-LINK."
- (let* ((type (intern (org-element-property :type citation-link)))
- (path (org-element-property :path citation-link))
- (keys (s-split "," path))
- (entries (mapcar 'orcp-key-to-entry keys))
- (label-func (orcp-get-citation-style 'label type))
- (delimiter (orcp-get-citation-style 'delimiter type))
- (sort-func (orcp-get-citation-style 'sort type))
- labels
- replacement-text)
-
- ;; sort is not coded yet. I am not sure the best data to sort here. the keys?
- (when sort-func
- (setq keys (sort keys sort-func)))
-
- ;; get labels. This function is where you would, for example, create
- ;; hyperlinks to the bibliography. This function should return a list of
- ;; strings
- (setq labels
- (mapcar
- (lambda (key)
- (funcall label-func key *orcp-unique-entries*))
- keys))
-
- ;; collapse range - not used yet.
-
- ;; now get a string collecting everything
- (setq labels (mapconcat 'identity labels delimiter))
-
- (setq replacement-text (concat
- (orcp-get-citation-style 'prefix type)
- labels
- (orcp-get-citation-style 'suffix type)))
-
- ;; finally, call formatter
- (funcall (or (orcp-get-citation-style 'vertical-align type)
- 'baseline)
- replacement-text)))
-
-
- (defun orcp-get-citation-replacements ()
- "Get a list of replacements for all links in `*orcp-citation-links*'."
- (mapcar 'orcp-get-text-replacement *orcp-citation-links*))
-
- ;;* Formatted bibliography
-
- (defun orcp-formatted-bibliography ()
- "Return the formatted bibliography."
- (let* ((spacing (or (cdr (assoc 'spacing bibliography-style)) 1))
- (label-func (cdr (assoc 'label bibliography-style)))
- (label-prefix (cdr (assoc 'label-prefix bibliography-style)))
- (label-suffix (cdr (assoc 'label-suffix bibliography-style)))
- (justification (cdr (assoc 'justification bibliography-style)))
- (hanging-indent (cdr (assoc 'hanging-indent bibliography-style)))
- (header (cdr (assoc 'header bibliography-style)))
- (unique-entries (orcp-collect-unique-entries))
- (adaptive-fill-function '(lambda () " "))
- (indent-tabs-mode nil)
- bibliography-string)
-
- (setq bibliography-string
- (mapconcat
- 'identity
- ;; loop over the entries in the bibliography
- (cl-loop for entry in unique-entries
- collect
- (progn
- (let* ((entry-type (downcase
- (cdr (assoc "=type=" (cdr entry)))))
- (key (cdr (assoc "=key=" (cdr entry))))
- (entry-styles (cdr (assoc 'entries bibliography-style)))
- (entry-fields
- (progn
- (if (cdr (assoc (intern entry-type) entry-styles))
- (cdr (assoc (intern entry-type) entry-styles))
- (warn "%s not found. Using default." entry-type)
- (cdr (assoc 't entry-styles))
- )))
- (funcs (mapcar
- (lambda (field)
- (if (fboundp (intern
- (format "orcp-%s" field)))
- (intern
- (format "orcp-%s" field))
- ;; No formatter found. just get the data
- `(lambda (entry)
- (orcp-get-entry-field
- ,(symbol-name field) entry))))
- entry-fields))
- (label (concat label-prefix
- (funcall label-func key unique-entries)
- label-suffix)))
-
- ;; this is the entry. We do this in a buffer to make it
- ;; easy to indent, fill, etc...
- (with-temp-buffer
- (insert label)
- (insert (mapconcat (lambda (field-func)
- (funcall field-func entry))
- funcs
- ""))
- (goto-char (point-min))
- (forward-word)
- ;; It doesn't make sense to do this for all formats, e.g.HTML.
- ;; commenting out for now.
- ;; (increase-left-margin
- ;; (point-min) (point-max) hanging-indent)
- ;; (fill-region (point-min) (point-max) justification)
- (buffer-string)))))
- ;; Here we put in the separator between entries
- (cond
- ;; placeholder for other formats
- ((eq org-export-current-backend 'html)
- " @@html:<br>@@\n")
- (t
- ;; put in a \n for each spacing
- (mapconcat 'identity
- (cl-loop for i to spacing
- collect "\n")
- "")))))
- ;; TODO: figure out header. how do we insert it properly formatted?
- bibliography-string))
-
- ;;* Text formatting functions.
-
- ;; These take text, and format them according to a backend. We derive the
- ;; backend from `org-export-current-backend' because I anticipate using this
- ;; during export.
-
- (defun baseline (text)
- "Return TEXT."
- text)
-
-
- (defun superscript (text)
- "Format TEXT as superscripted."
- (cond
- ((eq org-export-current-backend 'html)
- (format "@@html:<sup>%s</sup>@@" text))
- ;; the catch-all case is org-syntax
- (t
- (format "^{%s}" text))))
-
-
- (defun italics (text)
- "Format TEXT as italics."
- (cond
- ((eq org-export-current-backend 'html)
- (format "@@html:<i>%s</i>@@" text))
- ;; the catch-all case is org-syntax
- (t
- (format "/%s/" text))))
-
-
- (defun bold (text)
- "Format TEXT in bold."
- (cond
- ((eq org-export-current-backend 'html)
- (format "@@html:<b>%s</b>@@" text))
- ;; the catch-all case is org-syntax
- (t
- (format "*%s*" text))))
-
- ;;* Field formatting functions
-
- ;;These should be style agnostic functions. They take an entry and return a
- ;; formatted field for the entry, using information from the csl file.
-
- (defun firstname (author-cell)
- "Return firstname from AUTHOR-CELL."
- (car author-cell))
-
-
- (defun lastname (author-cell)
- "Return lastname from AUTHOR-CELL."
- (cdr author-cell))
-
-
- (defun orcp-author (entry)
- "Return formatted author string from the ENTRY.
- ENTRY is from `bibtex-parse-entry'.
- Style information comes from `bibliography'"
- (let* ((style (cdr (assoc 'author bibliography-style)))
- (delimiter (cdr (assoc 'delimiter style)))
- (name1 (nth 0 (cdr (assoc 'name-order style))))
- (name2 (nth 1 (cdr (assoc 'name-order style))))
- (name-separator (cdr (assoc 'name-separator style)))
- (suffix (cdr (assoc 'suffix style)))
- (field-separator (cdr (assoc 'field-separator style)))
- (et-al (cdr (assoc 'et-al style)))
- (authors (s-split
- " and "
- (or
- (orcp-get-entry-field "author" entry)
- "")))
- ;; parse to list of (first von last jr)
- (author-data (mapcar
- (lambda (x)
- (let ((aud (orcp-parse-authorname x)))
- (cons (nth 0 aud)
- (concat
- (or (nth 1 aud) "")
- (or (nth 2 aud) "")
- (or (nth 3 aud) "")))))
- authors))
- ;; map first and last names, in order specified in style with separator
- (author-names
- (mapcar
- (lambda (x)
- (concat
- (funcall name1 x)
- name-separator
- (funcall name2 x)))
- author-data)))
- ;; check on et-al - not implemented yet
-
- ;; work on initialize - not implemented yet
-
- ;; mapconcat on delimiter then last author.
- (if (= 1 (length author-names))
- (concat (car author-names) suffix field-separator)
- (concat
- (mapconcat
- 'identity
- (butlast author-names)
- delimiter)
- (cdr (assoc 'last-author-delimiter style))
- (car (last author-names))
- suffix
- field-separator))))
-
-
- (defun orcp-title (entry)
- "Return formatted title for the bibtex ENTRY."
- (let* ((style (cdr (assoc 'title bibliography-style)))
- (font-style (cdr (assoc 'font-style style)))
- (suffix (cdr (assoc 'suffix style)))
- (field-separator (cdr (assoc 'field-separator style)))
- (title (orcp-get-entry-field "title" entry)))
-
- (concat
- (if font-style
- (funcall font-style title)
- title)
- suffix
- field-separator)))
-
-
- (defun orcp-journal (entry)
- "Return formatted journal for the bibtex ENTRY."
- (let* ((style (cdr (assoc 'journal bibliography-style)))
- (font-style (cdr (assoc 'font-style style)))
- (suffix (cdr (assoc 'suffix style)))
- (field-separator (cdr (assoc 'field-separator style)))
- (journal (orcp-get-entry-field "journal" entry)))
-
- (concat
- (if font-style
- (funcall font-style journal)
- journal)
- suffix
- field-separator)))
-
-
- (defun orcp-volume (entry)
- "Return formatted volume for the bibtex ENTRY."
- (let* ((style (cdr (assoc 'volume bibliography-style)))
- (font-style (cdr (assoc 'font-style style)))
- (prefix (cdr (assoc 'prefix style)))
- (suffix (eval (cdr (assoc 'suffix style))))
- (field-separator (cdr (assoc 'field-separator style)))
- (volume (orcp-get-entry-field "volume" entry)))
-
- (setq volume (concat prefix volume suffix))
- (concat
- (if font-style
- (funcall font-style volume)
- volume)
- field-separator)))
-
-
- (defun orcp-issue (entry)
- "Return formatted issue for the bibtex ENTRY."
- (let* ((style (cdr (assoc 'issue bibliography-style)))
- (font-style (cdr (assoc 'font-style style)))
- (prefix (cdr (assoc 'prefix style)))
- (suffix (cdr (assoc 'suffix style)))
- (field-separator (cdr (assoc 'field-separator style)))
- (issue (orcp-get-entry-field "number" entry)))
-
- ;; issue is optional and isn't always present.
- (if (not issue)
- field-separator
- (setq issue (concat prefix issue suffix))
- (if font-style
- (funcall font-style
- issue)
- issue))))
-
-
- (defun orcp-pages (entry)
- "Return formatted pages for the bibtex ENTRY."
- (let* ((style (cdr (assoc 'pages bibliography-style)))
- (font-style (cdr (assoc 'font-style style)))
- (prefix (cdr (assoc 'prefix style)))
- (suffix (cdr (assoc 'suffix style)))
- (field-separator (cdr (assoc 'field-separator style)))
- (pages (orcp-get-entry-field "pages" entry)))
-
- (setq pages (concat prefix pages suffix))
-
- ;; collapse-range not supported yet
- (concat (if font-style
- (funcall font-style
- pages)
- pages)
- field-separator)))
-
-
- (defun orcp-year (entry)
- "Return formatted year for the bibtex ENTRY."
- (let* ((style (cdr (assoc 'year bibliography-style)))
- (font-style (cdr (assoc 'font-style style)))
- (prefix (cdr (assoc 'prefix style)))
- (suffix (cdr (assoc 'suffix style)))
- (field-separator (cdr (assoc 'field-separator style)))
- (year (orcp-get-entry-field "year" entry)))
-
- (setq year (concat prefix year suffix))
-
- ;; collapse-range not supported yet
- (concat
- (if font-style
- (funcall font-style
- year)
- year)
- field-separator)))
-
-
- (defun orcp-doi-formatter (doi)
- "Return formatted DOI for different backends."
- (cond
- ((eq org-export-current-backend 'html)
- (format "http://dx.doi.org/%s" doi))
- (t
- (format "doi:%s" doi))))
-
-
- (defun orcp-doi (entry)
- "Return formatted doi for the bibtex ENTRY."
- (let* ((style (cdr (assoc 'doi bibliography-style)))
- (font-style (cdr (assoc 'font-style style)))
- (prefix (cdr (assoc 'prefix style)))
- (suffix (cdr (assoc 'suffix style)))
- (formatter (cdr (assoc 'formatter style)))
- (doi (orcp-get-entry-field "doi" entry)))
-
- (when formatter
- (setq doi (funcall formatter doi)))
- (setq doi (concat prefix doi suffix))
-
- (if font-style
- (funcall font-style
- (concat prefix doi suffix))
- doi)))
-
-
- (defun orcp-url (entry)
- "Return formatted url for the bibtex ENTRY."
- (let* ((style (cdr (assoc 'doi bibliography-style)))
- (font-style (cdr (assoc 'font-style style)))
- (prefix (cdr (assoc 'prefix style)))
- (suffix (cdr (assoc 'suffix style)))
- (formatter (cdr (assoc 'formatter style)))
- (url (orcp-get-entry-field "url" entry)))
-
- (when formatter
- (setq url (funcall formatter url)))
- (setq url (concat prefix url suffix))
-
- (if font-style
- (funcall font-style
- (concat prefix url suffix))
- url)))
-
- ;;* Data structures for Author names
-
- (defun orcp-unprotect-brackets (piece protected-strings)
- "Unprotect PIECE with the information in PROTECTED-STRINGS.
- PROTECTED-STRINGS is a list of cons-cells (\"protection\" .
- original text)."
- (when piece
- (mapc
- (lambda (cons-cell)
- (when (string-match (car cons-cell) piece)
- (setq piece (replace-match (cdr cons-cell) t t piece))))
- protected-strings))
- piece)
-
-
- ;; See http://maverick.inria.fr/~Xavier.Decoret/resources/xdkbibtex/bibtex_summary.html#names for the parsing rules.
- (defun orcp-parse-authorname (name)
- "Convert an author NAME to (first von last jr) data structure.
- Valid name forms are:
- First1 First2 Last
- First1 First 2 {Last1 Last2}
-
- First1 First2 von1 von2 Last1 Last2
- von1 von2 Last1 Last2, Jr., First1 First2
-
- Last1, First1 First2
- {Von Last1}, First1 First2
-
- We try to protect strings in curly brackets."
- (let* (protected-strings
- uuid
- ncommas
- fields
- first von last jr)
-
- ;; protect bracketed strings
- (while (string-match "{\\(.*\\)}" name)
- ;; We want our substitute to look like a name, not a von part so we add a
- ;; capital letter to the front.
- (setq uuid (concat "A" (md5 (format "%s%s%s%s%s%s%s"
- (random)
- (current-time)
- (user-uid)
- (emacs-pid)
- (user-full-name)
- user-mail-address
- (recent-keys)))))
- (add-to-list 'protected-strings (cons uuid (match-string 0 name)))
- (setq name (replace-match uuid nil nil name)))
-
- (setq ncommas (s-count-matches "," name))
-
- (cond
- ;; "First von Last"
- ((= 0 ncommas)
- (setq fields (s-split " " name))
- (while (and (s-capitalized? (car fields)) (> (length fields) 1))
- (setq first (append first (list (pop fields)))))
- (when first
- (setq first (mapconcat 'identity first " ")))
-
- ;; Next, we get the von part. this is the longest white space delimited
- ;; string that ends with a lowercase word, and is not the rest of the
- ;; string.
- (let ((last-lower-index nil))
- (cl-loop for i to (length fields)
- for word in (butlast fields)
- if (s-lowercase? word)
- do (setq last-lower-index i))
- (when last-lower-index
- (setq von (mapconcat
- 'identity
- (-slice fields 0 (+ 1 last-lower-index)) " "))
- (setq fields (-slice fields (+ 1 last-lower-index)))))
-
- ;; all that should be left is the last name but it might be more than one
- ;; word, e.g. with a Jr. or a two work last name.
- (setq last (mapconcat 'identity fields " "))
- (mapcar
- (lambda (x)
- (orcp-unprotect-brackets x protected-strings))
- (list first von last jr)))
-
- ;; "von Last, First"
- ((= 1 ncommas)
- (setq fields (s-split "," name))
- (setq first (nth 1 fields))
- ;; split first field which could be von Lastname.
- (setq fields (s-split " " (car fields)))
- (let ((last-lower-index nil))
- (cl-loop for i to (length fields)
- for word in fields
- if (s-lowercase? word)
- do (setq last-lower-index i))
- (when last-lower-index
- (setq von (mapconcat
- 'identity
- (-slice fields 0 (+ 1 last-lower-index)) " "))
- (setq fields (-slice fields (+ 1 last-lower-index)))))
- ;; all that should be left is the last name
- (setq last (mapconcat 'identity fields " "))
- (mapcar
- (lambda (x)
- (orcp-unprotect-brackets x protected-strings))
- (list first von last jr)))
-
- ;; "von Last, Jr, First"
- ((= 2 ncommas)
- (setq fields (s-split "," name))
- (setq first (nth 2 fields))
- (setq jr (nth 1 fields))
- ;; split first field which could be von Lastname.
- (setq fields (s-split " " (car fields)))
- (let ((last-lower-index nil))
- (cl-loop for i to (length fields)
- for word in fields
- if (s-lowercase? word)
- do (setq last-lower-index i))
- (when last-lower-index
- (setq von (mapconcat 'identity (-slice fields 0 (+ 1 last-lower-index)) " "))
- (setq fields (-slice fields (+ 1 last-lower-index)))))
- ;; all that should be left is the last name
- (setq last (mapconcat 'identity fields " "))
- (mapcar
- (lambda (x)
- (orcp-unprotect-brackets x protected-strings))
- (list first von last jr))))))
-
-
- ;;* Collapse numeric range
-
- (defun orcp-collapse-numeric-range (cites delimiter)
- "TODO use style info.
- Collapse a numeric list of CITES into a range.
- Collapsed ranges are separated by DELIMITER."
- (let (n
- (groups '()))
- (while cites
- (setq n (pop cites))
- (if (and (caar groups) (= (- n 1) (elt (car groups) 0)))
- (setf (car groups) (append `(,n) (car groups)))
- (setf groups (append `((,n)) groups))))
- ;; Now for each group
- (mapconcat 'identity
- (mapcar
- (lambda (lst)
- (cond
- ((>= (length lst) 3)
- (format "%s-%s" (car lst) (car (last lst))))
- ((= (length lst) 2)
- (format "%s,%s" (nth 0 lst) (nth 1 lst)))
- (t
- (number-to-string (car lst)))))
- (mapcar 'reverse (reverse groups)))
- delimiter)))
-
-
- ;;* Putting it all together
-
- (defun sentence-beginning-p ()
- "Determine if point is at the beginning of a sentence.
- The idea is to move forward a sentence, then back. If the point
- doesn't move, it means you were at the beginning of a sentence."
- (let ((cp (point)))
- (save-excursion
- (forward-sentence)
- (backward-sentence)
- (= cp (point)))))
-
-
- (defun orcp-citeproc (&optional backend)
- "Format citations and bibliography for BACKEND.
- Warning. Destructive to your document! Will replace links.
- Meant to be used in export on a temporary version of the
- documents."
-
- ;; Get the style from bibliographystyle link
- ;; and eliminate bibliography style links
- ;; This will load all style modules
- (cl-loop for link in (org-element-map
- (org-element-parse-buffer) 'link 'identity)
- if (string= "bibliographystyle"
- (org-element-property :type link))
- do
- ;; get path for style and load it
- (load-library (org-element-property :path link))
- ;; get rid of the link in the buffer
- (setf (buffer-substring (org-element-property :begin link)
- (org-element-property :end link))
- ""))
-
- (orcp-collect-citations)
- (orcp-collect-unique-entries)
-
- (let ((link-replacements (cl-loop for link in *orcp-citation-links*
- for repl in (orcp-get-citation-replacements)
- collect
- (list repl
- (org-element-property :begin link)
- (org-element-property :end link))))
- (bibliography-string (orcp-formatted-bibliography))
- punctuation
- trailing-space
- bibliography-link)
-
- ;; replace citation links
- (cl-loop for (repl start end) in (reverse link-replacements)
- for link in (reverse *orcp-citation-links*)
- do
- ;; chomp leading spaces if needed
- (when (orcp-get-citation-style
- 'chomp-leading-space
- (intern (org-element-property :type link)))
- (goto-char start)
- (while (and (not (sentence-beginning-p))
- (looking-back " " (- (point) 2)))
- (delete-char -1)
- (setq start (- start 1))
- (setq end (- end 1))))
-
- ;; chomp trailing spaces if needed
- (when (orcp-get-citation-style
- 'chomp-trailing-space
- (intern (org-element-property :type link)))
- (goto-char end)
- (while (looking-back " " (- (point) 2))
- (delete-char 1)))
-
- ;; Check for transposing punctuation
- (setq punctuation nil)
- (when (orcp-get-citation-style
- 'transpose-punctuation
- (intern (org-element-property :type link)))
- ;; goto end of link
- (goto-char end)
- (when (looking-at "\\.\\|,\\|;")
- (setq punctuation (buffer-substring end (+ 1 end)))
- (delete-char 1)))
-
- ;; preserve trailing space
- (goto-char end)
- (setq trailing-space (if (looking-back " " (line-beginning-position)) " " ""))
-
- (setf (buffer-substring start end) (concat repl trailing-space))
-
- (when punctuation
- (goto-char start)
- ;; I can't figure out why this is necessary. I would have thought
- ;; the chomp leading spaces would get it.
- (when (thing-at-point 'whitespace)
- (delete-char -1))
- (insert punctuation)))
-
- ;; Insert bibliography section at the bibliography link
- (setq bibliography-link (cl-loop for link
- in (org-element-map
- (org-element-parse-buffer)
- 'link 'identity)
- if (string= "bibliography"
- (org-element-property :type link))
- collect link))
- (pcase (length bibliography-link)
- ((pred (< 1)) (error "Only one bibliography link allowed"))
- ((pred (= 1))
- ;; replace bibliography link
- (setq bibliography-link (car bibliography-link))
- (setf (buffer-substring (org-element-property :begin bibliography-link)
- (org-element-property :end bibliography-link))
- bibliography-string))
- ((pred (= 0))
- ;; no bibliography link in document
- (when link-replacements
- (message "Warning: No bibliography link found although there are citations to process"))))))
-
- ;; * the end
- (provide 'org-ref-citeproc)
- ;;; org-ref-citeproc.el ends here
|