;;; md4tj --- Summary
;;; Commentary:
;;; Code:
(require 'cl-lib)
(require 'subr-x)
;; Add the dash directory to the load-path, since running emacs in batch mode does not define the
;; load-path
(add-to-list 'load-path (concat "~/.emacs.d/elpa/" (nth 0 (cl-remove-if-not (lambda (x) (string-match "dash-.*" x)) (directory-files "~/.emacs.d/elpa")))))
(require 'dash) ;; and then require it
;; Basic utilities for subsequent stuff
(defun md4tj-util-getline ()
"Get current line from loaded buffer."
(buffer-substring-no-properties (line-beginning-position) (line-end-position)))
(defun md4tj-util-clean-multiline (line)
"Clean LINE of markdown syntax for ul's, ol's and code's."
(replace-regexp-in-string
"^```" "" ;; eliminate the 3 backticks for start of code
(replace-regexp-in-string
"^[0-9]+\\. " "" ;; eliminate the numbering for ordered lists
(replace-regexp-in-string
"^- " "" ;; eliminate the dashes for unordered lists
(replace-regexp-in-string
"^>" "" ;; eliminate the right-caret for quotes
line)))))
(defun md4tj-util-escape-chars (line)
"Escape characters in LINE that would be misinterpreted by the browser."
(string-replace
"<" "<"
(string-replace ">" ">" line)))
(defun md4tj-util-zip (ls)
"Turn list of lists LS into list of tuples."
(cl-labels ((zip-help (ll) (if (not (car ll)) nil (cons (cl-map 'listp #'car ll) (zip-help (cl-map 'listp 'cdr ll))))))
(zip-help ls)))
;; End basic utilities
;; Normal md4tj stuff
(defun md4tj-begin-tag (tag &optional attrs)
"Return beginning html tag for TAG with optional ATTRS."
(concat "<" tag
(mapconcat 'identity (cl-map 'listp (lambda (l) (concat " " (nth 0 l) "=" "\"" (nth 1 l) "\"")) attrs) "")
">"))
(defun md4tj-end-tag (tag)
"Return end html tag for TAG."
(concat "" tag ">"))
(defun md4tj-process-header (line)
"Process LINE known to be header, return HTML."
(let ((level (length (nth 0 (split-string line)))))
(if (or (< level 0) (> level 6))
(error (concat "Error parsing: " line "\n"))
(concat (md4tj-begin-tag (concat "h" (number-to-string level)))
(mapconcat 'identity (cdr (split-string line)) " ")
(md4tj-end-tag (concat "h" (number-to-string level)))))))
(defun md4tj-process-paragraph (line)
"Process LINE that is paragraph, return HTML."
(concat (md4tj-begin-tag "p") line (md4tj-end-tag "p")))
(defun md4tj-process-line (line)
"Process all inline elements of the LINE, return HTML."
;; Finally strikethrough
(replace-regexp-in-string
"~~\\(.*?\\)~~"
"\\1"
;; Then highlight
(replace-regexp-in-string
"==\\(.*?\\)=="
"\\1"
;; Then links
(replace-regexp-in-string
"\\[\\([^\\[]*\\)\\](\\([^ ]*\\))"
(concat (md4tj-begin-tag "a" (list '("href" "\\2"))) "\\1" (md4tj-end-tag "a"))
;; Then images
(replace-regexp-in-string
"!\\[\\([^\\[]*\\)](\\([^\\[(]*\\))"
(md4tj-begin-tag "img" (list '("src" "\\2") '("alt" "\\1")))
;; Then videos
(replace-regexp-in-string
"!!\\[\\([^\\[]*\\)](\\([^\\[(]*\\))"
(concat (md4tj-begin-tag "video" (list '("src" "\\2") '("type" "video/webm") '("controls" "true"))) "\\1" (md4tj-end-tag "video"))
;; Then emphasis
(replace-regexp-in-string
"\\*\\(.*+\\)\\*"
"\\1"
;; Then strong
(replace-regexp-in-string
"\\*\\*\\(.*+\\)\\*\\*"
"\\1"
;; First code
(replace-regexp-in-string
"`\\(.*\\)`"
"\\1
" line)))))))))
(defun md4tj-handle-table-row (line header)
"Return the string for a table row that is a HEADER from LINE."
(let ((opentag (if header "
\n")
((eq state 'ul) "- ")
((eq state 'ol) "
- ")
((eq state 'code) "")
((eq state 'endul) "
") ((eq state 'endquote) "") (t ""))) (defun md4tj-next-state (currline prevstate) "Return the state based on CURRLINE and PREVSTATE." (list ;; End state (cond ((and (string-match "^- " currline) (or (eq prevstate 'ol) (eq prevstate 'beginol))) 'endol) ((and (string-match "^[0-9]+\\. " currline) (or (eq prevstate 'ul) (eq prevstate 'beginul))) 'endul) ((and (not (string-match "^- " currline)) (or (eq prevstate 'beginul) (eq prevstate 'ul))) 'endul) ((and (not (string-match "[0-9]+\\. " currline)) (or (eq prevstate 'beginol) (eq prevstate 'ol))) 'endol) ((and (string-match "```$" currline) (or (eq prevstate 'code) (eq prevstate 'begincode))) 'endcode) ((and (not (string-match "^|.*|$" currline)) (or (eq prevstate 'begintable) (eq prevstate 'headtable) (eq prevstate 'table))) 'endtable) ((and (not (string-match "^>.*" currline)) (or (eq prevstate 'beginquote) (eq prevstate 'quote))) 'endquote) (t 'nothing)) ;; Begin state (or next line's prevstate) (cond ((and (string-match "^- " currline) (not (or (eq prevstate 'beginul) (eq prevstate 'ul))) 'beginul)) ((and (string-match "^- " currline) (or (eq prevstate 'beginul) (eq prevstate 'ul)) 'ul)) ((and (string-match "^[0-9]+\\. " currline) (not (or (eq prevstate 'beginol) (eq prevstate 'ol))) 'beginol)) ((and (string-match "^[0-9]+\\. " currline) (or (eq prevstate 'beginol) (eq prevstate 'ol)) 'ol)) ((and (string-match "^```" currline) (not (or (eq prevstate 'begincode) (eq prevstate 'code))) 'begincode)) ((and (not (string-match "```$" currline)) (or (eq prevstate 'begincode) (eq prevstate 'code)) 'code)) ((and (string-match "^|.*|$" currline) (not (or (eq prevstate 'table) (eq prevstate 'begintable)))) 'begintable) ((and (string-match "^|.*|$" currline) (or (eq prevstate 'begintable) (eq prevstate 'table))) 'table) ((and (string-match "^>.*" currline) (not (or (eq prevstate 'quote) (eq prevstate 'beginquote)))) 'beginquote) ((and (string-match "^>.*" currline) (or (eq prevstate 'beginquote) (eq prevstate 'quote))) 'quote) (t 'normal)))) (defun md4tj-begin () "Insert beginning code for all html, initialize." (shell-command "mkdir -p ./teximg") ;; Ensure ./teximg folder exists (concat "\n" (md4tj-begin-tag "html" (list (list "lang" (or (nth 1 (car (cl-remove-if-not (lambda (meta) (string= (car meta) "@@LANG")) (md4tj-find-metas)))) "en")))) "\n")) (defun md4tj-finalize (state) "Finalizes HTML document by inserting missing end tags based on STATE." (concat (cond ((or (eq state 'beginul) (eq state 'ul)) "") ((or (eq state 'beginol) (eq state 'ol)) "") ((or (eq state 'begincode) (eq state 'code)) "") ((or (eq state 'begintable) (eq state 'table)) "") ((or (eq state 'beginquote) (eq state 'quote)) "") (t "")) "