+(defun pelican-mode-set-field-rst-mode (field value)
+ "Set reStructuredText metadata FIELD to VALUE."
+ (setq field (downcase field))
+ (if (equal field "title")
+ (let ((header (format "%s\n%s\n\n"
+ value (make-string (string-width value) ?#))))
+ (if (looking-at ".*\n#+\n+")
+ (replace-match header)
+ (insert header)))
+ (let ((text (when value (format ":%s: %s\n" field value))))
+ (when (looking-at "^.*\n#")
+ (forward-line 3))
+ (if (re-search-forward (format "^:%s:.*\n" (regexp-quote field)) nil t)
+ (replace-match (or text ""))
+ (when text
+ (if (re-search-forward "^$" nil t)
+ (replace-match text)
+ (insert text)))))))
+
+(defun pelican-mode-set-field-markdown-mode (field value)
+ "Set Markdown metadata FIELD to VALUE."
+ (setq field (capitalize field))
+ (let ((text (when value (format "%s: %s\n" field value))))
+ (if (re-search-forward (format "^%s:.*\n" (regexp-quote field)) nil t)
+ (replace-match text)
+ (when value
+ (if (re-search-forward "^$" nil t)
+ (replace-match text)
+ (insert text))))))
+
+(defun pelican-mode-set-field-adoc-mode (field value)
+ "Set AsciiDoc metadata FIELD to VALUE."
+ (setq field (downcase field))
+ (if (equal field "title")
+ (let ((header (format "= %s\n\n" value)))
+ (if (looking-at "= .*\n\n+")
+ (replace-match header)
+ (insert header)))
+ (let ((text (when value (format ":%s: %s\n" field value))))
+ (when (looking-at "^=")
+ (forward-line 2))
+ (if (re-search-forward (format "^:%s:.*\n" (regexp-quote field)) nil t)
+ (replace-match (or text ""))
+ (when text
+ (if (re-search-forward "^$" nil t)
+ (replace-match text)
+ (insert text)))))))
+
+(defun pelican-mode-set-field-org-mode (field value)
+ "Set Org global metadata FIELD to VALUE."
+ ;; None of org-mode’s functions I can find for setting properties
+ ;; operate on the global list, only a single property drawer.
+ (setq field (upcase field))
+ (setq field
+ (format (if (member field '("TITLE" "DATE" "CATEGORY" "AUTHOR"))
+ "#+%s:"
+ "#+PROPERTY: %s")
+ field))
+ (let ((text (when value (format "%s %s\n" field value))))
+ (if (re-search-forward (format "^%s .*\n" (regexp-quote field)) nil t)
+ (replace-match (or text ""))
+ (when text
+ (if (re-search-forward "^$" nil t)
+ (replace-match text)
+ (insert text))))))
+
+(defun pelican-mode-page-p ()
+ "Return non-nil the current buffer is a Pelican page."
+ (string-match-p
+ "^[^/]+/pages/"
+ (file-relative-name
+ (abbreviate-file-name (or (buffer-file-name) (buffer-name)))
+ (pelican-mode-find-root))))
+
+(defun pelican-mode-default-slug ()
+ "Generate a Pelican slug for the current buffer."
+ (file-name-sans-extension
+ (replace-regexp-in-string
+ "^[^/]+/\\(?:pages/\\)?" ""
+ (file-relative-name
+ (abbreviate-file-name (or (buffer-file-name) (buffer-name)))
+ (pelican-mode-find-root)))))
+
+(defun pelican-mode-find-root ()
+ "Return the root of the buffer’s Pelican site, or nil."
+ (locate-dominating-file default-directory "pelicanconf.py"))