;;; wiki-markup.el -- A wikitext mode -*- lexical-binding: t -*- ;; Copyright (C) 2023 Free Software Foundation. ;; Author: Yuchen Pei ;; Package-Requires: ((emacs "28.2")) ;; This file is part of wiki-markup.el. ;; wiki.el is free software: you can redistribute it and/or modify it under ;; the terms of the GNU Affero General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; wiki.el 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 Affero General ;; Public License for more details. ;; You should have received a copy of the GNU Affero General Public ;; License along with wiki.el. If not, see . ;;; Commentary: ;; A wikitext mode for wiki-markup ;;; Code: (require 'wiki-faces) (defvar wiki-url-regexp "https?://[^ ]+") (defvar wiki-external-link-re (rx (seq "[[" (group (seq "http" (opt "s") "://" (+ (not " ")))) (opt (seq " " (group (+? anything)))) "]]"))) (defvar wiki-internal-link-re (rx (seq "[[" ;; Target (group (one-or-more (not (any "[|]")))) ;; Label (optional) (opt (seq "|" (group (+? anything)))) "]]")) ) (defvar wiki-font-lock-keywords (list ;; Headers (cons "^======.*======\\ *$" 'wiki-level-6) (cons "^=====.*=====\\ *$" 'wiki-level-5) (cons "^====.*====\\ *$" 'wiki-level-4) (cons "^===.*===\\ *$" 'wiki-level-3) (cons "^==.*==\\ *$" 'wiki-level-2) (cons "^=.*=\\ *$" 'wiki-level-1) (cons "^----+\\ *$" 'wiki-hr-face) (cons "^ .*$" 'wiki-pre-face) '(wiki-do-emphasis-faces) '(wiki-activate-external-links) '(wiki-activate-internal-links) )) (defvar wiki-outline-regexp "=+.*=+\ *$") (defvar-local wiki-site nil "The identifier of the wiki site") (defun wiki-follow-wikilink-action (data) "Button action to follow a wikilink" (funcall (wiki-site-fetcher wiki-site) (alist-get 'title data))) (defun wiki-outline-level () (when (looking-at "\\(=+\\).*[^=]\\(=+\\)\\ *$") (min (length (match-string 1)) (length (match-string 2)) 6))) ;; Like `org-do-emphasis-faces' (defun wiki-do-emphasis-faces (limit) (while (re-search-forward "\\(''+\\)[^ \t\n].*?[^ \t\n']\\(''+\\)" limit t) (let ((start (match-beginning 0)) (end (match-end 0))) (pcase (min (length (match-string 1)) (length (match-string 2))) (2 (add-face-text-property start end 'wiki-italic)) (3 (add-face-text-property start end 'wiki-bold)) (_ (add-face-text-property start end 'wiki-bold-italic)) )))) ;; Like `org-activate-links' (defun wiki-activate-links (link-re limit) (save-excursion (goto-char (point-min)) (while (re-search-forward link-re limit t) (let ((start (match-beginning 0)) (end (match-end 0)) (visible-start (or (match-beginning 2) (match-beginning 1))) (visible-end (or (match-end 2) (match-end 1))) (title (buffer-substring-no-properties (match-beginning 1) (match-end 1))) ) (put-text-property start visible-start 'invisible t) (make-text-button start end 'action 'wiki-follow-wikilink-action 'button-data `((title . ,title))) (add-face-text-property start end 'org-link) (put-text-property visible-end end 'invisible t) (add-text-properties (1- visible-start) visible-start '(rear-nonsticky (invisible))) (add-text-properties (1- visible-end) visible-end '(rear-nonsticky (invisible))) )))) (defun wiki-activate-internal-links (limit) (wiki-activate-links wiki-internal-link-re limit)) (defun wiki-activate-external-links (limit) (wiki-activate-links wiki-external-link-re limit)) (define-derived-mode wiki-mode outline-mode "Wiki" "A wikitext mode." (setq-local comment-start "") (setq-local font-lock-defaults '(wiki-font-lock-keywords t nil nil (font-lock-extra-managed-props invisible font-lock-face button-data action category button htmlize-link help-echo))) (setq-local outline-regexp wiki-outline-regexp) (setq-local outline-level 'wiki-outline-level) ) (defun set-wiki-site (wiki-site) (interactive (list (completing-read "Set wiki site: " (mapcar 'car wiki-sites)))) (setq-local wiki-site (intern wiki-site))) (provide 'wiki-markup) ;;; wiki-markup.el ends here