branch: externals/denote-markdown commit 93a901a1c8c46baf09b3ca7968f1ae7fb9ea902a Author: Protesilaos Stavrou <i...@protesilaos.com> Commit: Protesilaos Stavrou <i...@protesilaos.com>
Move file out of the main Denote directory WORK-IN-PROGRESS --- README.md | 3 ++ denote-markdown.el | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) diff --git a/README.md b/README.md new file mode 100644 index 0000000000..c4485428a3 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Denote Markdown extras + +WORK-IN-PROGRESS: <https://protesilaos.com/codelog/2025-02-11-emacs-splitting-denote-many-packages/>. diff --git a/denote-markdown.el b/denote-markdown.el new file mode 100644 index 0000000000..d2242fdbe1 --- /dev/null +++ b/denote-markdown.el @@ -0,0 +1,148 @@ +;;; denote-md-extras.el --- Denote extensions for Markdown mode -*- lexical-binding: t -*- + +;; Copyright (C) 2024-2025 Free Software Foundation, Inc. + +;; Author: Protesilaos Stavrou <i...@protesilaos.com> +;; Maintainer: Protesilaos Stavrou <i...@protesilaos.com> +;; URL: https://github.com/protesilaos/denote + +;; This file is NOT part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; Optional extensions to Denote that work specifically with Markdown files. + +;;; Code: + +(require 'denote) + +;;;; Register a new file type + +(add-to-list + 'denote-file-types + '(markdown-obsidian + :extension ".md" + :front-matter "# %s\n\n" + :title-key-regexp "^# " + :title-value-function identity + :title-value-reverse-function identity + :link denote-md-link-format + :link-in-context-regexp denote-md-link-in-context-regexp)) + +;;;; Convert links + +(defun denote-md-extras--get-regexp (type) + "Return regular expression to match link TYPE. +TYPE is a symbol among `denote', `file', `obsidian', and `reverse-obsidian'." + (pcase type + ('denote "(denote:\\(?1:.*?\\))") + ('file (format "(.*?\\(?1:%s\\).*?)" denote-id-regexp)) + ('obsidian "\\(?2:\\[.*?\\]\\)(denote:\\(?1:.*?\\))") + ('reverse-obsidian (format "\\(?2:\\[.*?\\(?:%s\\).*?\\]\\)(\\(?1:.*?\\(?:%s\\).*?\\))" denote-id-regexp denote-id-regexp)) + (_ (error "`%s' is an unknown type of link" type)))) + +;;;###autoload +(defun denote-md-extras-convert-links-to-file-paths (&optional absolute) + "Convert denote: links to file paths. +Ignore all other link types. Also ignore links that do not +resolve to a file in the variable `denote-directory'. + +With optional ABSOLUTE, format paths as absolute, otherwise do them +relative to the variable `denote-directory'." + (interactive "P" markdown-mode) + (if (derived-mode-p 'markdown-mode) + (save-excursion + (let ((count 0)) + (goto-char (point-min)) + (while (re-search-forward (denote-md-extras--get-regexp 'denote) nil :no-error) + (when-let* ((id (match-string-no-properties 1)) + (file (save-match-data + (if absolute + (denote-get-path-by-id id) + (denote-get-relative-path-by-id id))))) + (replace-match (format "(%s)" file) :fixed-case :literal) + (setq count (1+ count)))) + (message "Converted %d `denote:' links to %s paths" count (if absolute "ABSOLUTE" "RELATIVE")))) + (user-error "The current file is not using Markdown mode"))) + +;;;###autoload +(defun denote-md-extras-convert-links-to-denote-type () + "Convert generic file links to denote: links in the current Markdown buffer. +Ignore all other link types. Also ignore file links that do not point +to a file with a Denote file name. + +Also see `denote-md-extras-convert-obsidian-links-to-denote-type'." + (interactive nil markdown-mode) + (if (derived-mode-p 'markdown-mode) + (save-excursion + (let ((count 0)) + (goto-char (point-min)) + (while (re-search-forward (denote-md-extras--get-regexp 'file) nil :no-error) + (let* ((file (match-string-no-properties 1)) + (id (save-match-data (denote-retrieve-filename-identifier file)))) + (when id + (replace-match (format "(denote:%s)" id) :fixed-case :literal) + (setq count (1+ count))))) + (message "Converted %d file links to `denote:' links" count))) + (user-error "The current file is not using Markdown mode"))) + +;;;###autoload +(defun denote-md-extras-convert-links-to-obsidian-type () + "Convert denote: links to Obsidian-style file paths. +Ignore all other link types. Also ignore links that do not +resolve to a file in the variable `denote-directory'." + (interactive nil markdown-mode) + (if (derived-mode-p 'markdown-mode) + (save-excursion + (let ((count 0)) + (goto-char (point-min)) + (while (re-search-forward (denote-md-extras--get-regexp 'obsidian) nil :no-error) + (when-let* ((id (match-string-no-properties 1)) + (path (save-match-data (denote-get-relative-path-by-id id))) + (name (file-name-sans-extension path))) + (replace-match (format "[%s](%s)" name path) :fixed-case :literal) + (setq count (1+ count)))) + (message "Converted %d `denote:' links to Obsidian-style format" count))) + (user-error "The current file is not using Markdown mode"))) + +;;;###autoload +(defun denote-md-extras-convert-obsidian-links-to-denote-type () + "Convert Obsidian-style links to denote: links in the current Markdown buffer. +Ignore all other link types. Also ignore file links that do not point +to a file with a Denote file name. + +Also see `denote-md-extras-convert-links-to-denote-type'." + (interactive nil markdown-mode) + (if (derived-mode-p 'markdown-mode) + (save-excursion + (let ((count 0)) + (goto-char (point-min)) + (while (re-search-forward (denote-md-extras--get-regexp 'reverse-obsidian) nil :no-error) + (let ((file nil) + (id nil) + (description nil)) + (save-match-data + (setq file (expand-file-name (match-string-no-properties 1) (denote-directory)) + id (denote-retrieve-filename-identifier file) + description (denote-get-link-description file))) + (when id + (replace-match (format "[%s](denote:%s)" description id) :fixed-case :literal) + (setq count (1+ count))))) + (message "Converted %d Obsidian-style links to `denote:' links" count))) + (user-error "The current file is not using Markdown mode"))) + +(provide 'denote-md-extras) +;;; denote-md-extras.el ends here