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
|
(in-package #:cl-markdown)
#|
To do:
- allow footnotes to appear on a completely separate page
- do footnotes as a popup window with mouse over
- handle footnotes 'out of band' a la links
Footnotes
{note foo}
{note "This is a note"}
{note "Foo"}
{note This is a note}
(markdown
"That is what he thought.{footnote foo}
[foo]> \"This is a longer note with
linefeeds, *mark-up*, and \\\"escaped\\\" quotes.
I'll be wicked surprised if it works out of the
box.\"
")
Need to
1. get a number
2. add link where the footnote starts
3. add anchor where the footnote starts
4. add footnote text at bottom of document / separate page
5. add link back to anchor in footnote
Our footnote HTML is so heavily influenced by DF that you might think
we just copied it all.
(markdown "
Maybe people{footnote Well, at least one person} find CL-Markdown
to be the bees knees, the cats pajamas and the gnats goulash. In
fact, if computers could dance, you could tell that one had
CL-Markdown installed on it just by watching.{footnote Not really.}
{footnotes}
This was generated {today} at {now}.")
|#
(defclass* footnote-info ()
((id nil ia)
(text nil ia)
(reference-name nil ia)
(name nil ia)))
(eval-when (:load-toplevel :execute)
(setf *extensions* (remove 'footnote *extensions* :key #'first))
(push (list 'footnote t) *extensions*)
(setf *extensions* (remove 'footnotes *extensions* :key #'first))
(push (list 'footnotes t) *extensions*))
;; provides an example of using result during render phase
(defun footnote (phase args result)
;; {documentation text}
(let ((footnotes
(or (document-property :footnote)
(setf (document-property :footnote)
(make-instance 'vector-container)))))
(cond ((eq phase :parse)
(let* ((text (format nil "~{~a ~}" args)))
(when text
(bind ((id (size footnotes))
(fn-basename
(format nil "~d-~a"
id
(format-date "%Y-%m-%d"
(document-property
:date-modified
(get-universal-time)))))
(fn-name (format nil "fn~a" fn-basename))
(ref-name (format nil "fnr~a" fn-basename)))
(insert-item footnotes
(make-instance
'footnote-info
:id id
:name fn-name
:reference-name ref-name
:text text))
(values id)))))
((eq phase :render)
(let ((footnote (item-at footnotes (first result))))
(output-anchor (reference-name footnote))
(format *output-stream*
"<sup><a href=\"#~a\">~d</a></sup>"
(name footnote)
(1+ (id footnote))))))))
(defun footnotes (phase args result)
(declare (ignore args result))
(ecase phase
(:parse)
(:render
(unless (empty-p (document-property :footnote))
(format *output-stream* "~&<div class=\"footnotes\">")
(format *output-stream* "~&<ol>")
(iterate-elements
(document-property :footnote)
(lambda (footnote)
(format *output-stream* "~&<li>")
(output-anchor (name footnote))
(markdown (text footnote)
:stream *output-stream*
:format *current-format*
:properties '((:html . nil)
(:omit-final-paragraph . t)
(:omit-initial-paragraph . t))
:document-class 'included-document)
(format *output-stream* "<a href=\"#~a\" class=\"footnoteBacklink\""
(reference-name footnote))
(format *output-stream*
" title=\"Jump back to footnote ~d in the text\""
(1+ (id footnote)))
(format *output-stream* ">↩</a></li>")))
(format *output-stream*
"~&</ol>~&</div>")))))
;; not yet
#|
(defun handle-footnote-links (document)
(iterate-elements
(chunks document)
(lambda (chunk)
(when (line-is-footnote-text-p)
(bind (((values nil link-info)
(scan-to-strings '(:sequence footnote-text)
(first-element (lines chunk))))
(id (aref link-info 0))
(text (aref link-info 1)))
(setf (item-at (link-info document) id)
(make-instance 'footnote-text
:id id :title text)
(ignore? chunk) t)))))
;; now remove the unneeded chunks
(removed-ignored-chunks? document)
document)
(defun line-is-footnote-text-p (line)
(scan #.(ppcre:create-scanner '(:sequence footnote-text)) line))
(define-parse-tree-synonym
footnote-label
(:sequence
:start-anchor
(:greedy-repetition 0 3 :whitespace-char-class)
bracketed
#\>
(:greedy-repetition 0 nil :whitespace-char-class)
(:register
(:alternation
(:sequence
#\" (:greedy-repetition 0 nil (:inverted-char-class #\") #\"))
(:greedy-repetition 0 nil :everything)))))
#+(or)
(scan-to-strings
(create-scanner 'footnote-label)
" [a]> why are you here
ok")
#+(or)
(scan-to-strings
(create-scanner 'footnote-label)
" [a]> \"why are you here?
I am here because that is why.
OK? ok!\"")
|#
|