;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+\f
;;; Commentary:
;;
;; Or, register `pelican-mode' or `pelican-mode-enable-if-site'
;; as hook functions for more direct control.
+\f
;;; Code:
(require 'seq)
(require 'subr-x)
+;; Mode Definition
+
+;;;###autoload
+(define-minor-mode pelican-mode
+ "Toggle Pelican mode.
+With a prefix argument ARG, enable Pelican mode if ARG is
+positive, and disable it otherwise. If called from Lisp, enable
+the mode if ARG is omitted or nil.
+
+Pelican is a static site generator which can process a variety of
+text file formats. For more information, see URL
+https://blog.getpelican.com/.
+
+Rather than manually enabling this mode, you may wish to use
+`pelican-global-mode' or `pelican-mode-enable-if-site'.
+
+When Pelican mode is enabled, additional commands are available
+for editing articles or pages:
+
+\\{pelican-mode-map}"
+ :lighter " Pelican"
+ :keymap `((,(kbd "C-c P d") . pelican-mode-update-date)
+ (,(kbd "C-c P f") . pelican-set-field)
+ (,(kbd "C-c P h") . pelican-make-html)
+ (,(kbd "C-c P n") . pelican-mode-insert-header)
+ (,(kbd "C-c P p") . pelican-mode-publish-draft)
+ (,(kbd "C-c P u") . pelican-make-rsync-upload)))
+
+;;;###autoload
+(define-minor-mode pelican-global-mode
+ "Toggle Pelican global mode.
+With a prefix argument ARG, enable Pelican global mode if ARG is
+positive, and disable it otherwise. If called from Lisp, enable
+the mode if ARG is omitted or nil.
+
+Pelican is a static site generator which can process a variety of
+text file formats. For more information, see URL
+https://blog.getpelican.com/.
+
+When Pelican global mode is enabled, text files which seem to
+be part of a Pelican site will have `pelican-mode' automatically
+enabled.
+
+If you disable this, you may still enable `pelican-mode' manually
+or add `pelican-mode-enable-if-site' to more specific mode
+hooks."
+ :global t
+ :group 'pelican-mode
+ (if pelican-global-mode
+ (add-hook 'text-mode-hook #'pelican-mode-enable-if-site)
+ (remove-hook 'text-mode-hook #'pelican-mode-enable-if-site)))
+
+;;;###autoload
+(defun pelican-mode-enable-if-site ()
+ "Enable `pelican-mode' if this buffer is part of a Pelican site.
+
+Pelican sites are detected by looking for a file named `pelicanconf.py'
+in an ancestor directory."
+ (when (pelican-mode-find-root)
+ (pelican-mode)))
+
+\f
+
+;; Customizations
+
(defgroup pelican-mode nil
"Support for Pelican articles and pages.
:type '(plist))
(defcustom pelican-mode-formats
- '((markdown-mode . pelican-mode-set-field-markdown-mode)
- (adoc-mode . pelican-mode-set-field-adoc-mode)
+ '((adoc-mode . pelican-mode-set-field-adoc-mode)
+ (markdown-mode . pelican-mode-set-field-markdown-mode)
(org-mode . pelican-mode-set-field-org-mode)
(rst-mode . pelican-mode-set-field-rst-mode))
"Functions to handle setting metadata, based on major mode.
:group 'pelican-mode
:type '(alist :key-type function :value-type function))
-(defun pelican-mode-timestamp (&optional time)
- "Generate a pelican-mode-compatible timestamp for TIME."
- (format-time-string "%Y-%m-%d %H:%M" time))
+\f
-(defun pelican-mode-set-fields (&rest fields)
- "Insert a Pelican header for an article with metadata FIELDS."
- (mapc (apply-partially #'apply #'pelican-mode-set-field)
- (seq-partition fields 2)))
+;; User Commands
+
+(defun pelican-mode-set-field (field value)
+ "Set FIELD to VALUE.
+
+FIELD may be a string or a symbol; if it is a symbol, the
+symbol name is used (removing a leading ':' if present).
+
+When called from Lisp, 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-mode-timestamp'.
+
+- `slug' means the file's path relative to the document root sans
+ extension; see `pelican-mode-default-slug'.
+
+- nil or an empty string removes the field.
+
+The buffer must be in a format listed in `pelican-mode-formats'
+for this function to work correctly."
+ (interactive "sField: \nsValue: ")
+ (setq value (pcase value
+ ('now (pelican-mode-timestamp))
+ ('slug (pelican-mode-default-slug))
+ ('"" nil)
+ (_ value)))
+ (when (symbolp field)
+ (setq field (string-remove-prefix ":" (symbol-name field))))
+ (let ((set-field
+ (assoc-default nil pelican-mode-formats #'derived-mode-p)))
+ (unless set-field
+ (error "Unsupported major mode %S" major-mode))
+ (save-excursion
+ (goto-char 0)
+ (funcall set-field field value))))
+
+(defun pelican-mode-remove-field (field)
+ "Remove FIELD."
+ (interactive "sField: ")
+ (pelican-mode-set-field field nil))
+
+(defun pelican-mode-set-title (title)
+ "Set the title to TITLE."
+ (interactive "sTitle: ")
+ (pelican-mode-set-field :title title))
+
+(defun pelican-mode-update-date (&optional original)
+ "Update the document's modification date.
+
+If ORIGINAL is non-nil, the publication date is updated rather
+than the modification date."
+ (interactive "P")
+ (pelican-mode-set-field (if original :date :modified) 'now))
+
+(defun pelican-mode-publish-draft ()
+ "Remove draft status from a Pelican article."
+ (interactive)
+ (pelican-mode-remove-field :status)
+ (pelican-mode-update-date :date))
(defun pelican-mode-insert-draft-article-header (title tags)
"Insert a Pelican header for a draft with a TITLE and TAGS."
#'pelican-mode-insert-page-header
#'pelican-mode-insert-draft-article-header)))
+(defun pelican-make (target)
+ "Execute TARGET in a Makefile at the root of the site."
+ (interactive "sMake Pelican target: ")
+ (if-let (default-directory (pelican-mode-find-root))
+ (compilation-start (format "make %s" target)
+ nil (lambda (_) "*pelican*"))
+ (user-error "No Pelican site root could be found")))
+
+(defun pelican-make-html ()
+ "Generate HTML via a Makefile at the root of the site."
+ (interactive)
+ (pelican-make "html"))
+
+(defun pelican-make-rsync-upload ()
+ "Upload with rsync via a Makefile at the root of the site."
+ (interactive)
+ (pelican-make "rsync_upload"))
+
+\f
+
+(defun pelican-mode-timestamp (&optional time)
+ "Generate a pelican-mode-compatible timestamp for TIME."
+ (format-time-string "%Y-%m-%d %H:%M" time))
+
+(defun pelican-mode-set-fields (&rest fields)
+ "Insert a Pelican header for an article with metadata FIELDS."
+ (mapc (apply-partially #'apply #'pelican-mode-set-field)
+ (seq-partition fields 2)))
+
(defun pelican-mode-set-field-rst-mode (field value)
"Set reStructuredText metadata FIELD to VALUE."
(setq field (downcase field))
(replace-match text)
(insert text))))))
-(defun pelican-mode-set-field (field value)
- "Set FIELD to VALUE.
-
-FIELD may be a string or a symbol; if it is a symbol, the
-symbol name is used (removing a leading ':' if present).
-
-When called from Lisp, 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-mode-timestamp'.
-
-- `slug' means the file's path relative to the document root sans
- extension; see `pelican-mode-default-slug'.
-
-- nil or an empty string removes the field.
-
-The buffer must be in a format listed in `pelican-mode-formats'
-for this function to work correctly."
- (interactive "sField: \nsValue: ")
- (setq value (pcase value
- ('now (pelican-mode-timestamp))
- ('slug (pelican-mode-default-slug))
- ('"" nil)
- (_ value)))
- (when (symbolp field)
- (setq field (string-remove-prefix ":" (symbol-name field))))
- (let ((set-field
- (assoc-default nil pelican-mode-formats #'derived-mode-p)))
- (unless set-field
- (error "Unsupported major mode %S" major-mode))
- (save-excursion
- (goto-char 0)
- (funcall set-field field value))))
-
-(defun pelican-mode-remove-field (field)
- "Remove FIELD."
- (pelican-mode-set-field field nil))
-
-(defun pelican-mode-set-title (title)
- "Set the title to TITLE."
- (interactive "sTitle: ")
- (pelican-mode-set-field :title title))
-
-(defun pelican-mode-update-date ()
- "Update a Pelican date header."
- (interactive)
- (pelican-mode-set-field :date 'now))
-
-(defun pelican-mode-publish-draft ()
- "Remove draft status from a Pelican article."
- (interactive)
- (pelican-mode-remove-field :status)
- (pelican-mode-update-date))
-
(defun pelican-mode-page-p ()
"Return non-nil the current buffer is a Pelican page."
(when-let (pelican-mode-base (pelican-mode-find-root))
"Return the root of the buffer's Pelican site, or nil."
(locate-dominating-file default-directory "pelicanconf.py"))
-(defun pelican-make (target)
- "Execute TARGET in a Makefile at the root of the site."
- (interactive "sMake Pelican target: ")
- (if-let (default-directory (pelican-mode-find-root))
- (compilation-start (format "make %s" target)
- nil (lambda (_) "*pelican*"))
- (user-error "No Pelican site root could be found")))
-
-(defun pelican-make-html ()
- "Generate HTML via a Makefile at the root of the site."
- (interactive)
- (pelican-make "html"))
-
-(defun pelican-make-rsync-upload ()
- "Upload with rsync via a Makefile at the root of the site."
- (interactive)
- (pelican-make "rsync_upload"))
-
-;;;###autoload
-(define-minor-mode pelican-mode
- "Toggle Pelican mode.
-With a prefix argument ARG, enable Pelican mode if ARG is
-positive, and disable it otherwise. If called from Lisp, enable
-the mode if ARG is omitted or nil.
-
-Pelican is a static site generator which can process a variety of
-text file formats. For more information, see URL
-https://blog.getpelican.com/.
-
-Rather than manually enabling this mode, you may wish to use
-`pelican-global-mode' or `pelican-mode-enable-if-site'.
-
-When Pelican mode is enabled, additional commands are available
-for editing articles or pages:
-
-\\{pelican-mode-map}"
- :lighter " Pelican"
- :keymap `((,(kbd "C-c P f") . pelican-set-field)
- (,(kbd "C-c P h") . pelican-make-html)
- (,(kbd "C-c P n") . pelican-mode-insert-header)
- (,(kbd "C-c P p") . pelican-mode-publish-draft)
- (,(kbd "C-c P t") . pelican-mode-update-date)
- (,(kbd "C-c P u") . pelican-make-rsync-upload)))
-
-;;;###autoload
-(define-minor-mode pelican-global-mode
- "Toggle Pelican global mode.
-With a prefix argument ARG, enable Pelican global mode if ARG is
-positive, and disable it otherwise. If called from Lisp, enable
-the mode if ARG is omitted or nil.
-
-Pelican is a static site generator which can process a variety of
-text file formats. For more information, see URL
-https://blog.getpelican.com/.
-
-When Pelican global mode is enabled, text files which seem to
-be part of a Pelican site will have `pelican-mode' automatically
-enabled.
-
-If you disable this, you may still enable `pelican-mode' manually
-or add `pelican-mode-enable-if-site' to more specific mode
-hooks."
- :global t
- :group 'pelican-mode
- (if pelican-global-mode
- (add-hook 'text-mode-hook #'pelican-mode-enable-if-site)
- (remove-hook 'text-mode-hook #'pelican-mode-enable-if-site)))
-
-;;;###autoload
-(defun pelican-mode-enable-if-site ()
- "Enable `pelican-mode' if this buffer is part of a Pelican site."
- (when (pelican-mode-find-root)
- (pelican-mode 1)))
-
(provide 'pelican-mode)
;;; pelican-mode.el ends here
+\f
+
;; Local Variables:
;; sentence-end-double-space: t
;; End: