10
\$\begingroup\$

This is a collection of reasonably useful functions I put together for writing my blog.

(require 'htmlize)

(defvar blog-mode-map nil
  "Keymap for blog minor mode")

(unless blog-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\C-cl" 'insert-link)
    (define-key map "\C-cp" 'insert-code-block)
    (define-key map "\C-cc" 'insert-inline-code)
    (define-key map "\C-cb" 'insert-bold)
    (define-key map "\C-cq" 'insert-quote)
    (define-key map "\C-cs" 'insert-sig)
    (define-key map "\C-cf" 'insert-footnote)

    (define-key map "\C-c\C-l" 'region-to-link)
    (define-key map "\C-c\C-p" 'region-to-code-block)
    (define-key map "\C-c\C-c" 'region-to-inline-code)
    (define-key map "\C-c\C-b" 'region-to-bold)
    (define-key map "\C-c\C-q" 'region-to-quote)
    (define-key map "\C-c\C-s" 'region-to-sig)
    (define-key map "\C-c\C-f" 'region-to-footnote)

    (define-key map "/" 'smart-backslash)
    (setq blog-mode-map map)))

(define-minor-mode blog-mode
  "This is a collection of useful keyboard macros for editing Langnostic"
  nil
  " Blog"
  (use-local-map blog-mode-map))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; simple definitions
(defun insert-tag (start-tag &optional end-tag)
  "Inserts a tag at point"
  (interactive)
  (insert start-tag)
  (save-excursion
    (insert (or end-tag ""))))

(defun wrap-region (start end start-tag &optional end-tag)
  "Inserts end tag at the end of the region, and start tag at point"
  (goto-char end)
  (insert (or end-tag ""))
  (goto-char start)
  (insert start-tag))

(defmacro definsert (tag-name start-tag end-tag)
  "Defines insert function."
  `(defun ,(make-symbol (concat "insert-" (symbol-name tag-name))) ()
     (interactive)
     (insert-tag ,start-tag ,end-tag)))

(defmacro defregion (tag-name start-tag end-tag)
  "Defines region wrapper function."
  `(defun ,(make-symbol (concat "region-to-" (symbol-name tag-name))) ()
     (interactive)
     (wrap-region (region-beginning) (region-end) ,start-tag ,end-tag)))

(definsert link (concat "<a href=\"" (x-get-clipboard) "\">") "</a>")
(defregion link (concat "<a href=\"" (x-get-clipboard) "\">") "</a>")
(definsert bold "<b>" "</b>")
(defregion bold "<b>" "</b>")
(definsert quote "<blockquote>" "</blockquote>")
(defregion quote "<blockquote>" "</blockquote>")
(definsert sig "<span class=\"sig\">" "</span>")
(defregion sig "<span class=\"sig\">" "</span>")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; <pre> and <code> definitions
(definsert code-block "<pre>" "</pre>")
(definsert inline-code "<code>" "</code>")

;; region versions are more complicated to accomodate htmlize
(defun region-to-inline-code ()
  "HTMLize just the current region and wrap it in a <code> block"
  (interactive)
  (let((htmlified (substring (htmlize-region-for-paste (region-beginning) (region-end)) 6 -6)))
    (delete-region (region-beginning) (region-end))
    (insert-inline-code)
    (insert htmlified)))

(defun region-to-code-block ()
  "HTMLize the current region and wrap it in a <pre> block"
  (interactive)
  (let ((htmlified (htmlize-region-for-paste (region-beginning) (region-end))))
    (delete-region (region-beginning) (region-end))
    (insert (concat "<pre>" (substring htmlified 6)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; footnote definitions
(defun insert-footnote ()
  "Inserts footnote, and a return link at the bottom of the file. 
   Moves point to footnote location."
  (interactive)
  (progn (footnotes-header)
     (let ((footnote-name (format-time-string "%a-%b-%d-%H%M%S%Z-%Y" (current-time))))
       (insert "<a href=\"#foot-" footnote-name "\" name=\"note-" footnote-name "\">[note]</a>")
       (goto-char (point-max))
       (insert "\n\n<a href=\"#note-" footnote-name "\" name=\"foot-" footnote-name "\">[back]</a> - "))))

(defun region-to-footnote ()
  "Inserts a footnote at point and return link at the bottom. Moves the current region to the end of the file. 
   Leaves point where it is."
      (interactive)
  (save-excursion (kill-region (region-beginning) (region-end))
          (insert-footnote)
          (yank)))

(defun footnotes-header ()
  "Inserts footnote header if not already present"
  (unless (save-excursion (search-forward "<hr />\n<h5>Footnotes</h5>" nil t))
    (save-excursion 
      (goto-char (point-max))
      (insert "\n\n<hr />\n<h5>Footnotes</h5>"))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; utility
(defun smart-backslash ()
  "Backslash closes previous tag when used in the combination </. Self-inserts otherwise."
  (interactive)
  (if (equal (save-excursion (backward-char) (thing-at-point 'char)) "<")
      (progn (backward-delete-char 1)
         (sgml-close-tag))
    (insert "/")))

(provide 'blog-mode)

I'd like some advice on how to remove duplication in a few obvious places. To my mind, I should be able to do something like (deftag bold "b" "<b>" "</b>") which would expand into a definsert, defregion and define-key. I'm not sure how to have a macro define multiple functions in Elisp though; progn (quoted or not) doesn't seem to help.

Feel free to point out any components I could replace with pre-built Emacs23 functions, or anything I could do more elegantly.

EDIT: Updated version of this code can be found here.

\$\endgroup\$
3
\$\begingroup\$

Not sure if this is relevant as it is a year old but, you can look at https://github.com/Neil-Smithline/defassoclist to see how to create multiple functions in a macro.

You didn't include your failed attempts with progn so I can't tell you what you were doing wrong but progn does work.

\$\endgroup\$
  • \$\begingroup\$ Yup; the updated version linked in that EDIT line contains the progn approach. It was a while ago, but I think it was to do with improper quoting. \$\endgroup\$ – Inaimathi May 11 '12 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.