Merge pull request #5 from Tankanow/add-asciidoc-support

Add asciidoc support
This commit is contained in:
Carmen La 2015-01-09 10:19:32 -05:00
commit 142f0908a0
4 changed files with 158 additions and 66 deletions

View file

@ -13,4 +13,5 @@
[hiccup "1.0.5"]
[selmer "0.7.8"]
[markdown-clj "0.9.62"]
[pandect "0.4.1"]])
[pandect "0.4.1"]
[org.asciidoctor/asciidoctorj "1.5.2"]])

View file

@ -9,10 +9,9 @@
[clojure.java.io :refer [copy file reader writer]]
[clojure.string :as s]
[text-decoration.core :refer :all]
[markdown.core :refer [md-to-html-string]]
[markdown.transformers :refer [transformer-vector]]
[cryogen-core.toc :refer [generate-toc]]
[cryogen-core.sass :as sass]))
[cryogen-core.sass :as sass]
[cryogen-core.markup :as m]))
(cache-off!)
@ -31,13 +30,13 @@
(defn find-posts
"Returns a list of markdown files representing posts under the post root in templates/md"
[{:keys [post-root ignored-files]}]
(find-assets (str "templates/md" post-root) ".md" ignored-files))
[{:keys [post-root ignored-files]} mu]
(find-assets (str "templates/" (m/dir mu) post-root) (m/ext mu) ignored-files))
(defn find-pages
"Returns a list of markdown files representing pages under the page root in templates/md"
[{:keys [page-root ignored-files]}]
(find-assets (str "templates/md" page-root) ".md" ignored-files))
[{:keys [page-root ignored-files]} mu]
(find-assets (str "templates/" (m/dir mu) page-root) (m/ext mu) ignored-files))
(defn parse-post-date
"Parses the post date from the post's file name and returns the corresponding java date object"
@ -47,13 +46,13 @@
(defn post-uri
"Creates a post uri from the post file name"
[file-name {:keys [blog-prefix post-root]}]
(str blog-prefix post-root (s/replace file-name #".md" ".html")))
[file-name {:keys [blog-prefix post-root]} mu]
(str blog-prefix post-root (s/replace file-name (re-pattern (m/ext mu)) ".html")))
(defn page-uri
"Creates a page uri from the page file name"
[page-name {:keys [blog-prefix page-root]}]
(str blog-prefix page-root (s/replace page-name #".md" ".html")))
[page-name {:keys [blog-prefix page-root]} mu]
(str blog-prefix page-root (s/replace page-name (re-pattern (m/ext mu)) ".html")))
(defn read-page-meta
"Returns the clojure map from the top of a markdown page/post"
@ -63,65 +62,76 @@
(catch Exception _
(throw (IllegalArgumentException. (str "Malformed metadata on page: " page))))))
(defn rewrite-hrefs
"Injects the blog prefix in front of any local links
(defn page-content
"Returns a map with the given page's file-name, metadata and content parsed from
the file with the given markup."
[page config markup]
(with-open [rdr (java.io.PushbackReader. (reader page))]
(let [page-name (.getName page)
file-name (s/replace page-name (re-pattern (m/ext markup)) ".html")
page-meta (read-page-meta page-name rdr)
content ((m/render-fn markup) rdr config)]
{:file-name file-name
:page-meta page-meta
:content content})))
ex. <img src='/img/cryogen.png'/> becomes <img src='/blog/img/cryogen.png'/>"
[{:keys [blog-prefix]} text state]
[(clojure.string/replace text #"href=.?/|src=.?/" #(str (subs % 0 (dec (count %))) blog-prefix "/"))
state])
(defn parse-content
"Parses the markdown content in a post/page into html"
[rdr config]
(md-to-html-string
(->> (java.io.BufferedReader. rdr)
(line-seq)
(s/join "\n"))
:reference-links? true
:heading-anchors true
:replacement-transformers (conj transformer-vector (partial rewrite-hrefs config))))
(defn merge-meta-and-content
"Merges the page metadata and content maps, adding :toc if necessary."
[file-name page-meta content]
(merge
(update-in page-meta [:layout] #(str (name %) ".html"))
{:file-name file-name
:content content
:toc (if (:toc page-meta) (generate-toc content))}))
(defn parse-page
"Parses a page/post and returns a map of the content, uri, date etc."
[is-post? page config]
(with-open [rdr (java.io.PushbackReader. (reader page))]
(let [page-name (.getName page)
file-name (s/replace page-name #".md" ".html")
page-meta (read-page-meta page-name rdr)
content (parse-content rdr config)]
(merge
(update-in page-meta [:layout] #(str (name %) ".html"))
{:file-name file-name
:content content
:toc (if (:toc page-meta) (generate-toc content))}
(if is-post?
(let [date (parse-post-date file-name (:post-date-format config))
archive-fmt (java.text.SimpleDateFormat. "yyyy MMMM" (java.util.Locale. "en"))
formatted-group (.format archive-fmt date)]
{:date date
:formatted-archive-group formatted-group
:parsed-archive-group (.parse archive-fmt formatted-group)
:uri (post-uri file-name config)
:tags (set (:tags page-meta))})
{:uri (page-uri file-name config)
:page-index (:page-index page-meta)})))))
[page config markup]
(let [{:keys [file-name page-meta content]} (page-content page config markup)]
(merge
(merge-meta-and-content file-name page-meta content)
{:uri (page-uri file-name config markup)
:page-index (:page-index page-meta)})))
(defn parse-post
"Return a map with the given post's information."
[page config markup]
(let [{:keys [file-name page-meta content]} (page-content page config markup)]
(merge
(merge-meta-and-content file-name page-meta content)
(let [date (parse-post-date file-name (:post-date-format config))
archive-fmt (java.text.SimpleDateFormat. "yyyy MMMM" (java.util.Locale. "en"))
formatted-group (.format archive-fmt date)]
{:date date
:formatted-archive-group formatted-group
:parsed-archive-group (.parse archive-fmt formatted-group)
:uri (post-uri file-name config markup)
:tags (set (:tags page-meta))})
)))
(defn read-posts
"Returns a sequence of maps representing the data from markdown files of posts.
Sorts the sequence by post date."
[config]
(->> (find-posts config)
(map #(parse-page true % config))
(->> (mapcat
(fn [mu]
(->>
(find-posts config mu)
(map #(parse-post % config mu))))
(m/markups))
(sort-by :date)
reverse))
(defn read-pages
"Returns a sequence of maps representing the data from markdown files of pages.
Sorts the sequence by post date."
Sorts the sequence by post date."
[config]
(->> (find-pages config)
(map #(parse-page false % config))
(->> (mapcat
(fn [mu]
(->>
(find-pages config mu)
(map #(parse-page % config mu))))
(m/markups))
(sort-by :page-index)))
(defn tag-post

View file

@ -0,0 +1,68 @@
(ns cryogen-core.markup
(:require [markdown.core :refer [md-to-html-string]]
[markdown.transformers :refer [transformer-vector]]
[clojure.string :as s])
(:import org.asciidoctor.Asciidoctor$Factory
java.util.Collections))
(defprotocol Markup
"A markup engine comprising a dir(ectory) containing markup files,
an ext(ension) for finding markup file names, and a render-fn that returns
a fn with the signature [java.io.Reader config] -> String (HTML)."
(dir [this])
(ext [this])
(render-fn [this]))
(defn- rewrite-hrefs
"Injects the blog prefix in front of any local links
ex. <img src='/img/cryogen.png'/> becomes <img src='/blog/img/cryogen.png'/>"
[blog-prefix text]
(clojure.string/replace text #"href=.?/|src=.?/" #(str (subs % 0 (dec (count %))) blog-prefix "/")))
(defn- rewrite-hrefs-transformer
"A :replacement-transformer for use in markdown.core that will inject the
given blog prefix in front of local links."
[{:keys [blog-prefix]} text state]
[(rewrite-hrefs blog-prefix text) state])
(defn- markdown
"Returns a Markdown (https://daringfireball.net/projects/markdown/)
implementation of the Markup protocol."
[]
(reify Markup
(dir [this] "md")
(ext [this] ".md")
(render-fn [this]
(fn [rdr config]
(md-to-html-string
(->> (java.io.BufferedReader. rdr)
(line-seq)
(s/join "\n"))
:reference-links? true
:heading-anchors true
:replacement-transformers (conj transformer-vector (partial rewrite-hrefs-transformer config)))))))
(defn- asciidoc
"Returns an Asciidoc (http://asciidoc.org/) implementation of the
Markup protocol."
[]
(reify Markup
(dir [this] "asc")
(ext [this] ".asc")
(render-fn [this]
(fn [rdr config]
(->>
(.convert (Asciidoctor$Factory/create)
(->> (java.io.BufferedReader. rdr)
(line-seq)
(s/join "\n"))
(Collections/emptyMap))
(rewrite-hrefs (:blog-prefix config)))))))
(defn markups
"Return a vector of Markup implementations. This is the primary entry point
for a client of this ns. This vector should be used to iterate over supported
Markups."
[]
[(markdown) (asciidoc)])

View file

@ -5,7 +5,10 @@
(def _h [:h1 :h2 :h3 :h4 :h5 :h6])
(defn- compare_index [i1 i2] (- (.indexOf _h i2) (.indexOf _h i1)))
(defn get-headings [content]
(defn- get-headings
"Turn a body of html content into a vector of elements whose tags are
headings."
[content]
(reduce
(fn [headings {:keys [tag attrs content] :as elm}]
(if (some #{tag} _h)
@ -15,16 +18,26 @@
headings)))
[] content))
(defn make-links [headings]
(defn make-links
"Create a table of contents from the given headings. This function will look
for either:
(1) headings with a child anchor with a non-nil name attribute, e.g.
<h1><a name=\"reference\">Reference Title</a></h1>
or
(2) headings with an id attribute, e.g. <h1 id=\"reference\">Reference Title</h1>
In both cases above, the anchor reference becomes \"#reference\" and the
anchor text is \"Reference Title\"."
[headings]
(loop [items headings acc nil _last nil]
(if-let [{tag :tag [{{name :name} :attrs} title] :content} (first items)]
(if (nil? name) (recur (rest items) acc nil)
(let [entry [:li [:a {:href (str "#" name)} title]]
jump (compare_index _last tag)]
(cond (> jump 0) (recur (rest items) (str acc "<ol>" (hiccup/html entry)) tag)
(= jump 0) (recur (rest items) (str acc (hiccup/html entry)) tag)
(< jump 0) (recur (rest items) (str acc (apply str (repeat (* -1 jump) "</ol>"))
(hiccup/html entry)) tag))))
(if-let [{tag :tag {id :id} :attrs [{{name :name} :attrs} title :as htext] :content} (first items)]
(let [anchor (or id name)]
(if (nil? anchor) (recur (rest items) acc nil)
(let [entry [:li [:a {:href (str "#" anchor)} (or title (first htext))]]
jump (compare_index _last tag)]
(cond (> jump 0) (recur (rest items) (str acc "<ol>" (hiccup/html entry)) tag)
(= jump 0) (recur (rest items) (str acc (hiccup/html entry)) tag)
(< jump 0) (recur (rest items) (str acc (apply str (repeat (* -1 jump) "</ol>"))
(hiccup/html entry)) tag)))))
(str acc "</ol>"))))
(defn generate-toc [html]