Last active
August 29, 2015 13:56
-
-
Save luxbock/8848158 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(message "THE USER CONFIG FILE WAS LOADED") | |
;; Configure: Add org-mode-dir/lisp/ to Emacs load-path | |
(add-to-list 'load-path "~/.emacs.d/plugins/org-8.2.5c/lisp") | |
(require 'org) | |
(require 'ox-publish) | |
;; Incise uses the new org-mode Export Framework (introduced in 8.0) See more | |
;; information here: | |
;; | |
;; http://orgmode.org/worg/exporters/ox-overview.html | |
;; | |
;; Configure: Uncomment the proper export libraries as required | |
(require 'ox-html) | |
(require 'ox-ascii) | |
;; (require 'ox-latex) ;; Also used for pdf-export | |
;; (require 'ox-md) | |
;; (require 'ox-beamer) | |
;; (require 'ox-icalendar) | |
;; (require 'ox-texinfo) | |
;; (require 'ox-man) | |
;; (require 'ox-odt) | |
;; (require 'ox-deck) | |
;; Config: incise.el includes a default config that attempts to publish your | |
;; org-files and static content from your this-project/resources/org/ directory | |
;; into this/project/public/. There's a lot more functionality you can achieve | |
;; by customizing `org-publish-project-alist' below. For more information use | |
;; `C-h v "org-publish-project-alist"' and the links below: | |
;; | |
;; http://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.html | |
;; http://orgmode.org/manual/Publishing.html#Publishing | |
(setq org-publish-project-alist | |
'( | |
;; You can define as many of these components as needed create the | |
;; behaviour you want from Emacs / org-mode. | |
("incise-org" ;; This component exports org files to html | |
;; Configure: This is where Emacs looks for your content: | |
:base-directory "~/Development/clojure/incise-org-parser/resources/org/" | |
;; Filename suffix without the dot. | |
:base-extension "org" | |
;; Configure: The base directory where the files get published to: | |
:publishing-directory "~/Development/clojure/incise-org-parser/public/" | |
;; If t, include subdirectories. Creates new subdirectories in the | |
;; publishing directory is they don't exist | |
:recursive t | |
;; How we want to process the files before publishing | |
:publishing-function org-html-publish-to-html | |
;; A default setting - can be overwritten on per file basis | |
:headline-levels 2 | |
;; I don't know what this does | |
:auto-preamble t) | |
("incise-static" ;; This component exports your static content | |
;; Configure: | |
:base-directory "~/Development/clojure/incise-org-parser/resources/org/" | |
;; :base-extension can be a regular expression | |
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf" | |
;; Configure: | |
:publishing-directory "~/Development/clojure/incise-org-parser/public/" | |
:recursive t | |
:publishing-function org-publish-attachment) | |
;; You can combine other components into one. This way you can publish | |
;; everything with one command. | |
("incise" | |
:components ("incise-org" "incise-static")))) | |
;; If you use org-babel and your files include source blocks, Emacs will prompt | |
;; you for if you'd like to run the code in the source blocks, which interrupts | |
;; the shell-call to the Emacs process. Since you most likely publishing your | |
;; own org files, it is assumed that the source-blocks in your files do not | |
;; contain malicious code. Setting the below setting true will cause | |
;; incise-org-parser to not work. | |
(setq org-confirm-babel-evaluate nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns incise.parsers.impl.org | |
(:require | |
[incise.parsers.core :as pc] | |
[incise.config :as conf] | |
;; [incise.parsers.parse :refer [map->Parse record-parse publish-parse?]] | |
;; [incise.parsers.utils :refer [meta->write-path]] | |
;; [incise.utils :refer [get-extension]] | |
[clojure.string :as str] | |
[clojure.java.shell :refer [sh]] | |
[clojure.java.io :refer :all] | |
;; [clj-time.coerce :refer [to-date]] | |
;; [clj-time.local :refer [local-now]] | |
[taoensso.timbre :refer [info error]])) | |
;; config ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(comment | |
;; Structure of config -- a lot shorter than I was expecting | |
{:parsers | |
{:org {:emacs-cmd "emacs" | |
:publish-config "resources/incise.el"}}} | |
) | |
(conf/load) | |
(def org-conf | |
(let [config-defaults {:emacs-cmd "emacs" | |
:publish-config "resources/incise.el"} | |
incise-default (conf/get-in [:parsers :org])] | |
(merge config-defaults incise-default))) | |
;; version check ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defn get-version-info [] | |
(let [eval-str "(message (format \"%s --- %s\" (emacs-version) (org-version)))" | |
sh-reply (sh "emacs" "--batch" "--load=org" "--eval" eval-str) | |
sh-str (if (empty? (:out sh-reply)) (:err sh-reply) (:out sh-reply)) | |
[emacs org] (str/split sh-str #" --- ")] | |
{:exit-code (:exit sh-reply) | |
:emacs emacs | |
:org (str/trimr org)})) | |
(defn- version-check | |
[ver-str needed] | |
(let [ver (-> (re-find #"\d+\.\d+\.\d+" ver-str) (str/split #"\.") first)] | |
(>= (Integer. ver) needed))) | |
(defn emacs-version-at-least-24? [] | |
(version-check (:emacs (get-version-info)) 24)) | |
(defn org-version-at-least-8? [] | |
(version-check (:org (get-version-info)) 8)) | |
;; file exists checks ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defn emacs-cmd-exists? [] | |
(= (:exit (sh "which" (:emacs-cmd org-conf))) 0)) | |
(defn publish-config-exists? [] | |
(.exists (file (:publish-config org-conf)))) | |
;; reading meta from the file ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defn- org-read-meta-from-org-str | |
[org-str] | |
(let [org-meta-map {"#+AUTHOR" :author | |
"#+TITLE" :title | |
"#+EMAIL" :email | |
"#+DATE" :date | |
"#+TAGS" :tags | |
"#+CATEGORY" :category | |
"#+PUBLISH" :publish} | |
headers (->> (str/split org-str #"\n") | |
(filter #(= (apply str (take 2 %)) "#+")) | |
(map str/trimr))] | |
(reduce (fn [m l] | |
(let [[title val] (str/split l #": ")] | |
(assoc m (get org-meta-map title) val))) | |
{} headers))) | |
(defn- remove-nil-entries [m] | |
(into {} (remove (comp nil? second) m))) | |
(defn get-org-meta [org-str] | |
(let [meta (remove-nil-entries (org-read-meta-from-org-str org-str)) | |
tags (or (:tags meta) "")] | |
(assoc meta | |
:tags (str/split tags #" ")))) | |
(defn get-org-meta-from-file [file] | |
(get-org-meta (slurp file))) | |
;; shell call ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(def emacs-sh-partial | |
"Helper function for invoking the shell call with some of the command line | |
arguments fixed." | |
(partial sh (:emacs-cmd org-conf) "-Q" "--batch" | |
(str "--load=" (:publish-config org-conf)))) | |
(defn emacs-shell-call | |
"Calls Emacs via clojure.java.shell/sh and evals a sexp of elisp defined by | |
eval-str. The `emacs' command and the `publish-condig' are defined in | |
org-conf. The full command that gets executed is as follows: | |
<emacs-cmd> --Q --batch --load=<publish-config> --eval=<eval-str" | |
[& args] | |
{:pre [(emacs-cmd-exists?) | |
(publish-config-exists?)]} | |
(try | |
(apply emacs-sh-partial args) | |
(catch java.io.IOException e | |
(println "Failed to call Emacs from shell: " (str e)) | |
(error e)))) | |
(defn exit-ok? [sh-out] (= (:exit sh-out) 0)) | |
(defn publish-project | |
"Calls Emacs through clojure.java.shell/sh to perform (org-publish <project>). | |
This publishes every file that has been modified in the :base-directory as | |
defined in the publish-config file" | |
[project] | |
(emacs-shell-call (str "--eval=(org-publish-project \"" project "\")"))) | |
(defn publish-project-forced | |
"Calls Emacs through java.shell to to perform (org-publish <project> t) on a | |
project. This publishes every file in the :base-directory regardless of | |
whether it has been modified since the last the publish command was used. | |
`project' is the name of the component defined in incise.el's (or whatever | |
file the keyword :org-publish-config is pointing to) org-publish-project-alist | |
which you'd like to invoke. Different components can be used and combined to | |
publish different projects with different configurations. Most of | |
incise-org-parser's functionality comes from configuring your :publish-config | |
option properly." | |
[project] | |
(emacs-shell-call (str "--eval=(org-publish-project \"" project "\" t)"))) | |
(defn publish-file | |
"Calls Emacs through clojure.java.shell/sh to perform | |
`org-publish-current-file' on the file specified." | |
[file] | |
(emacs-shell-call (str "--visit=" (.getCanonicalPath file) " ") | |
"--funcall" "org-publish-current-file")) | |
;; making sense of the output ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defn get-published-file-paths | |
"Takes in the output of a valid Emacs shell call, and retrieves the file paths | |
for the files that were written as a result of a succesful export" | |
[sh-output] | |
(->> (str/split sh-output #"\n") | |
(map #(str/split % #" ")) | |
(filter #(= (first %) "Wrote")) | |
(map second))) | |
;; parsing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;; TODO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defn publish-project-and-parse-with-fn | |
[project publish-fn] | |
(let [sh-out (publish-fn project)] | |
(when (exit-ok? sh-out) | |
(map #(create-parse %) (get-published-file-paths (:err sh-out)))))) | |
(defn publish-project-and-parse | |
([project force?] | |
{:pre [(= force? :force)]} | |
(publish-project-and-parse-with-fn project publish-project-forced)) | |
([project] | |
(publish-project-and-parse-with-fn project publish-project))) | |
(defn create-parse []) | |
;; TODO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defn everything-ok? [] | |
(cond | |
(emacs-cmd-exists?) | |
(println | |
(format "Unable to find %s, failed to invoke Emacs") (:emacs-cmd org-conf)) | |
(emacs-version-at-least-24?) | |
(println "incise-org-parser requires Emacs 24.x or higher") | |
(org-version-at-least-8?) | |
(println "incise-org-parser requires org-mode version of 8.x or higher") | |
(publish-config-exists?) | |
(println "Unable to find location of publish-config. Check your config-file") | |
:else true)) | |
(defn org-project-parser | |
"Takes a `project' component string defined in the :publish-config file and | |
returns an org-parser." | |
([project force?] | |
{:pre [(= force? :force)]} | |
(when (everything-ok?) | |
(if force? | |
(publish-project-and-parse project :force) | |
(publish-project-and-parse project))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment