X-Git-Url: https://git.korewanetadesu.com/?p=pelican-mode.git;a=blobdiff_plain;f=pelican-mode.el;h=3691c2fe4095a95dc6468c5001e2cd69235fd834;hp=df0503f3a785931bb4f02a0b67c55585e8c60ca1;hb=4cf099e3b9e3e10f1f2f32df7f4b890b6d103657;hpb=993581b3d5ab84df353d312cac09b803c782795b diff --git a/pelican-mode.el b/pelican-mode.el index df0503f..3691c2f 100644 --- a/pelican-mode.el +++ b/pelican-mode.el @@ -3,7 +3,7 @@ ;; Copyright 2013-2017 Joe Wreschnig ;; ;; Author: Joe Wreschnig -;; Package-Version: 20170618 +;; Package-Version: 20170730 ;; Package-Requires: ((emacs "25")) ;; Keywords: convenience, editing ;; @@ -29,18 +29,58 @@ ;;; Code: +(require 'seq) (require 'subr-x) +(defgroup pelican-mode nil + "Support for Pelican posts and pages." + :group 'convenience) + +(defcustom pelican-mode-default-page-fields + '(:slug slug) + "Fields to include when creating a new page. + +See the documentation for `pelican-field' for more information +about metadata fields and special values." + :group 'pelican-mode + :type '(plist)) + +(defcustom pelican-mode-default-post-fields + '(:date now :status "draft" :slug slug) + "Fields to include when creating a new post. + +See the documentation for `pelican-field' for more information +about metadata fields and special values." + :group 'pelican-mode + :type '(plist)) + (defun pelican-timestamp (&optional time) "Generate a Pelican-compatible timestamp for TIME." (format-time-string "%Y-%m-%d %H:%M" time)) -(defun pelican-is-markdown () - "Check if the buffer is likely using Markdown." - (derived-mode-p 'markdown-mode)) - (defun pelican-field (name value) - "Format a line for a field NAME with a VALUE." + "Format a line for a field NAME with a VALUE. + +NAME may be a string or a symbol; if it is a symbol, the +symbol name is used (removing a leading ':' if present). + +VALUE may be any value; except for the following special values, +the unquoted printed representation of it is used: + +- `now' means the current time; see `pelican-timestamp'. + +- `slug' means the file's path relative to the document root sans + extension; see `pelican-default-slug'. + +- nil or an empty strings means return an empty string, without + any name or value." + (setq value (pcase value + ('now (pelican-timestamp)) + ('slug (pelican-default-slug)) + ('"" nil) + (_ value))) + (when (symbolp name) + (setq name (string-remove-prefix ":" (symbol-name name)))) (if value (cond ((derived-mode-p 'markdown-mode) (format "%s: %s\n" (capitalize name) value)) @@ -50,7 +90,7 @@ "")) (defun pelican-rst-title (title) - "Create a ReSt version of TITLE." + "Format a reStructureText version of TITLE." (concat title "\n" (make-string (string-width title) ?#) "\n\n")) (defun pelican-title (title) @@ -61,62 +101,64 @@ (pelican-rst-title title)) (t (error "Unsupported major mode %S" major-mode)))) -(defun pelican-header (title date status category tags slug) - "Create a Pelican header." - ;; TODO: Use a property list (-> alist via seq-partition) instead. - (when (eq date t) - (setq date (pelican-timestamp))) - +(defun pelican-header (title &rest fields) + "Generate a Pelican header for a post with a TITLE and metadata FIELDS." (concat (pelican-title title) - (pelican-field "date" date) - (pelican-field "status" status) - (pelican-field "tags" tags) - (pelican-field "category" category) - (pelican-field "slug" slug) + (mapconcat (apply-partially #'apply #'pelican-field) + (seq-partition fields 2) "") "\n")) +(defun pelican-insert-header (title &rest fields) + "Insert a Pelican header for a post with a TITLE and metadata FIELDS." + (save-excursion + (goto-char 0) + (insert (apply #'pelican-header (cons title fields))))) + (defun pelican-insert-draft-post-header (title tags) - "Insert a Pelican header for a draft post." + "Insert a Pelican header for a draft with a TITLE and TAGS." (interactive "sPost title: \nsTags: ") - (let ((slug (pelican-default-slug))) - (save-excursion - (goto-char 0) - (insert (pelican-header title 't "draft" nil tags slug))))) + (apply #'pelican-insert-header + `(,title ,@pelican-mode-default-post-fields :tags ,tags))) -(defun pelican-insert-page-header (title hidden) - "Insert a Pelican header for a page." +(defun pelican-insert-page-header (title &optional hidden) + "Insert a Pelican header for a page with a TITLE, potentially HIDDEN." (interactive (list (read-string "Page title: ") (y-or-n-p "Hidden? "))) - (let ((slug (pelican-default-slug)) - (hidden (if hidden "hidden" nil))) - (save-excursion - (goto-char 0) - (insert (pelican-header title nil hidden nil nil slug))))) + (apply #'pelican-insert-header + `(,title ,@pelican-mode-default-page-fields + :hidden ,(when hidden "hidden")))) -(defun pelican-insert-header () +(defun pelican-insert-auto-header () "Insert a Pelican header for a page or post." (interactive) - (call-interactively (if (pelican-is-page) - 'pelican-insert-page-header - 'pelican-insert-draft-post-header))) + (call-interactively + (if (pelican-page-p) + #'pelican-insert-page-header + #'pelican-insert-draft-post-header))) (defun pelican-set-field (field value) "Set FIELD to VALUE." (interactive "sField: \nsValue: ") (save-excursion (goto-char 0) + (when (and (derived-mode-p 'rst-mode) + (re-search-forward "^#" nil t)) + (forward-line 2)) (if (re-search-forward (concat "^" (pelican-field field ".+*")) nil t) (replace-match (pelican-field field value)) - (re-search-forward "#") - (forward-line 2) - (re-search-forward "^$") - (replace-match (pelican-field field value))))) + (when value + (re-search-forward "^$") + (replace-match (pelican-field field value)))))) + +(defun pelican-remove-field (field) + "Remove FIELD." + (pelican-set-field field nil)) (defun pelican-set-title (title) "Set the title to TITLE." (interactive "sTitle: ") - (if (pelican-is-markdown) + (if (derived-mode-p 'markdown-mode) (pelican-set-field "title" title) (save-excursion (goto-char 0) @@ -128,15 +170,15 @@ (defun pelican-update-date () "Update a Pelican date header." (interactive) - (pelican-set-field "date" (pelican-timestamp))) + (pelican-set-field :date 'now)) (defun pelican-publish-draft () "Remove draft status from a Pelican post." (interactive) - (pelican-set-field "status" nil) + (pelican-remove-field :status) (pelican-update-date)) -(defun pelican-is-page () +(defun pelican-page-p () "Guess the current buffer is a Pelican page (vs. a post or neither)." (when-let (pelican-base (pelican-find-root)) (let* ((relative (file-relative-name buffer-file-name pelican-base)) @@ -172,7 +214,7 @@ (when-let (conf (pelican-find-in-parents "pelicanconf.py")) (file-name-directory conf))) -(defun pelican-is-in-site () +(defun pelican-site-p () "Check if this buffer is under a Pelican site." (not (null (pelican-find-root)))) @@ -182,7 +224,7 @@ (if-let (default-directory (pelican-find-root)) (compilation-start (format "make %s" target) nil (lambda (_) "*pelican*")) - (message "This doesn't look like a Pelican site."))) + (user-error "This doesn't look like a Pelican site"))) (defun pelican-make-html () "Generate HTML via a Makefile at the root of the site." @@ -194,35 +236,24 @@ (interactive) (pelican-make "rsync_upload")) -(defconst pelican-keymap (make-sparse-keymap) - "The default keymap used in Pelican mode.") -(define-key pelican-keymap (kbd "C-c P n") - 'pelican-insert-header) -(define-key pelican-keymap (kbd "C-c P p") - 'pelican-publish-draft) -(define-key pelican-keymap (kbd "C-c P t") - 'pelican-update-date) -(define-key pelican-keymap (kbd "C-c P h") - 'pelican-make-html) -(define-key pelican-keymap (kbd "C-c P u") - 'pelican-make-rsync-upload) - - ;;;###autoload (define-minor-mode pelican-mode "Toggle Pelican mode. Interactively with no argument, this command toggles the mode. for editing Pelican site files." - :init-value nil :lighter " Pelican" - :keymap pelican-keymap - :group 'pelican) + :group 'pelican + :keymap `((,(kbd "C-c P n") . pelican-insert-auto-header) + (,(kbd "C-c P p") . pelican-publish-draft) + (,(kbd "C-c P t") . pelican-update-date) + (,(kbd "C-c P h") . pelican-make-html) + (,(kbd "C-c P u") . pelican-make-rsync-upload))) ;;;###autoload (defun pelican-enable-if-site () "Enable `pelican-mode' if this buffer is under a Pelican site." - (when (pelican-is-in-site) + (when (pelican-site-p) (pelican-mode 1))) ;;;###autoload