Download
(require 'derived)
(require 'font-lock)
(defvar wikitext-url-protocols
'("ftp" "gopher" "http" "https" "mailto" "news")
"Valid protocols for URLs in Wikitext articles.")
(defface wikitext-strong-emphasis-face '((t (:inherit bold-italic)))
"`wikitext-mode' face used to highlight text marked with strong and emhasis markup (e.g. //**FOO**// or **//FOO//**.)")
(defvar wikitext-strong-emphasis-face 'wikitext-strong-emphasis-face)
(defface wikitext-strong-face '((t (:inherit bold)))
"`wikitext-mode' face used to highlight text marked with two asteriscs (e.g. **FOO**.)")
(defvar wikitext-strong-face 'wikitext-strong-face)
(defface wikitext-emphasis-face '((t (:inherit italic)))
"`wikitext-mode' face used to highlight text marked with two
slashes (e.g. //FOO//.)")
(defvar wikitext-emphasis-face 'wikitext-emphasis-face)
(defface wikitext-header-face '((t (:inherit bold)))
"`wikitext-mode' face used to highlight section and subsection
headers (e.g. == FOO ==.)")
(defvar wikitext-header-face 'wikitext-header-face)
(defvar wikitext-font-lock-keywords
(list
(cons "\\(//\\*\\*\\)\\(.*?\\|\n\\)\\(\\*\\*//\\)"
'wikitext-strong-emphasis-face)
(cons "\\(\\*\\*//\\)\\(.*?\\|\n\\)\\(//\\*\\*\\)"
'wikitext-strong-emphasis-face)
(cons "\\*\\*\\([^*]\\|[^*]\\*\\)*?\\*\\*"
'wikitext-strong-face)
(list "\\([^:]\\)\\(//\\([^/]\\|[^/]/\\)*?//\\)"
'(1 font-lock-doc-face)
'(2 wikitext-emphasis-face))
(list "^\\(=+\\) \\(.*\\) \\(\\1\\)"
'(1 font-lock-builtin-face)
'(2 wikitext-header-face)
'(3 font-lock-builtin-face))
(cons "^-----*" 'font-lock-builtin-face)
(cons (concat "\\(^\\|[ \t]\\)" (regexp-opt wikitext-url-protocols t)
"://[-A-Za-z0-9._\/~%+?!=()@]+")
'font-lock-variable-name-face)
(cons "^\\(> +\\|[*#]+ +\\)" 'font-lock-builtin-face)
(cons "^[ \t]+\\(.*\\)$" 'font-lock-constant-face)
(cons "||" 'font-lock-builtin-face)
(list (concat "\\(\\[\\)\\(\\(?:"
(regexp-opt wikitext-url-protocols)
"\\)://[-A-Za-z0-9._\/~%-+?!=()@]+\\)\\(\\(?: [^]\n]*\\)?\\)\\(\\]\\)")
'(1 font-lock-builtin-face t t)
'(2 font-lock-variable-name-face t t)
'(3 font-lock-doc-face t t)
'(4 font-lock-builtin-face t t))
'("\\(\\[\\[\\)\\([^]\n|]*\\)\\([| ]?\\)\\([^]\n]*\\)\\(\\]\\]\\)"
(1 font-lock-builtin-face t t)
(2 font-lock-variable-name-face t t)
(3 font-lock-builtin-face t t)
(4 font-lock-doc-face t t)
(5 font-lock-builtin-face t t))
'("\\({{\\)\\(\\(.\\|\n\\)*?\\)\\(}}\\)"
(1 font-lock-builtin-face t t)
(2 font-lock-constant-face t t)
(4 font-lock-builtin-face t t))
'("\\(``\\)\\(.*?\\|\n\\)\\(``\\)"
(1 font-lock-builtin-face t t)
(2 font-lock-constant-face t t)
(3 font-lock-builtin-face t t))
))
(defvar wikitext-imenu-generic-expression
(list '(nil "^=+ *\\(.*[^\n=]\\)=+" 1))
"Imenu expression for wikitext-mode. See `imenu-generic-expression'.")
(defun wikitext-next-header ()
"Move point to the end of the next section header."
(interactive)
(let ((oldpoint (point)))
(end-of-line)
(if (re-search-forward "\\(^=+\\) .*\\1" (point-max) t)
(beginning-of-line)
(goto-char oldpoint)
(message "No section headers after point."))))
(defun wikitext-prev-header ()
"Move point to the start of the previous section header."
(interactive)
(unless (re-search-backward "\\(^=+\\) .*\\1" (point-min) t)
(message "No section headers before point.")))
(defun wikitext-terminate-paragraph ()
"In a list, start a new list item. In a paragraph, start a new
paragraph; if the current paragraph is colon indented, the new
paragraph will be indented in the same way."
(interactive)
(let (indent-chars)
(save-excursion
(beginning-of-line)
(while (cond ((looking-at "^$") nil)
((looking-at "^\\([#*> ]+\\)")
(setq indent-chars (match-string 1)) nil)
((eq (point) (point-min)) nil)
((progn (forward-line -1) t)))
t))
(insert (concat "\n" (or indent-chars "\n")))))
(defun wikitext-link-fill-nobreak-p ()
"When filling, don't break the line for preformatted (fixed-width)
text or inside a Wiki link. See `fill-nobreak-predicate'."
(save-excursion
(let ((pos (point)))
(or (eq (char-after (line-beginning-position)) ? )
(if (re-search-backward "\\[\\[" (line-beginning-position) t)
(null (or (> (- pos (point)) fill-column)
(re-search-forward "\\]\\]" pos t))))))))
(defun wikitext-fill-article ()
"Fill the entire article."
(interactive)
(save-excursion
(fill-region (point-min) (point-max))))
(defun wikitext-unfill-article ()
"Undo filling, deleting stand-alone newlines (newlines that do not
end paragraphs, list entries, etc.)"
(interactive)
(save-excursion
(goto-char (point-min))
(while (re-search-forward ".\\(\n\\)\\(\\[\\[code\\]\\]\\|[^# *|\n>]\\)" nil t)
(if (string= (match-string 2) "[[code]]") nil
(replace-match " " nil nil nil 1))))
(message "Stand-alone newlines deleted"))
(define-derived-mode wikitext-mode text-mode "Wikitext"
"Major mode for editing articles written in the markup language used by some wikis.
There are several ways to use wikitext-mode. One is to copy articles
between Emacs and your web browser's text box. Another way is to use
MozEx, a Mozilla/Firefox web browser extension that allows you to
call Emacs from a text box (http://mozex.mozdev.org/), or use any
other browser plug-in or extension, which allow save and load
textareas to files.
Wikitext articles are usually unfilled: newline characters are not
used for breaking paragraphs into lines. Unfortunately, Emacs does
not handle word wrapping yet. As a workaround, wikitext-mode turns
on longlines-mode automatically. In case something goes wrong, the
following commands may come in handy:
\\[wikitext-fill-article] fills the buffer.
\\[wikitext-unfill-article] unfills the buffer.
The following commands are also defined:
\\[wikitext-terminate-paragraph] starts a new list item or paragraph in a context-aware manner.
\\[wikitext-next-header] moves to the next (sub)section header.
\\[wikitext-prev-header] moves to the previous (sub)section header."
(set (make-local-variable 'adaptive-fill-regexp) "[ ]*")
(set (make-local-variable 'comment-start-skip) "\\(?:<!\\)?-- *")
(set (make-local-variable 'comment-end-skip) " *--\\([ \n]*>\\)?")
(set (make-local-variable 'comment-start) "<!-- ")
(set (make-local-variable 'comment-end) " -->")
(set (make-local-variable 'paragraph-start)
"> \\|\\*\\| \\|=+ \\|#\\|;\\|:\\||\\|!\\|$")
(set (make-local-variable 'sentence-end-double-space) nil)
(set (make-local-variable 'font-lock-multiline) t)
(set (make-local-variable 'font-lock-defaults)
'(wikitext-font-lock-keywords t nil nil nil))
(set (make-local-variable 'fill-nobreak-predicate)
'wikitext-link-fill-nobreak-p)
(set (make-local-variable 'auto-fill-inhibit-regexp)
"^\\([ *#:|;>]\\|=+ .* =+\\)")
(set (make-local-variable 'outline-regexp) "=+")
(set (make-local-variable 'outline-minor-mode-prefix) "\C-c")
(when menu-bar-mode
(set (make-local-variable 'imenu-generic-expression)
wikitext-imenu-generic-expression)
(imenu-add-to-menubar "Contents"))
(modify-syntax-entry ?< "(>" wikitext-mode-syntax-table)
(modify-syntax-entry ?> ")<" wikitext-mode-syntax-table)
(define-key wikitext-mode-map "\M-n" 'wikitext-next-header)
(define-key wikitext-mode-map "\M-p" 'wikitext-prev-header)
(define-key wikitext-mode-map [M-down] 'wikitext-next-header)
(define-key wikitext-mode-map [M-up] 'wikitext-prev-header)
(define-key wikitext-mode-map "\C-j" 'wikitext-terminate-paragraph)
(define-key wikitext-mode-map [(control return)]
'wikitext-terminate-paragraph)
(let ((map (make-sparse-keymap "Wikitext")))
(define-key wikitext-mode-map [menu-bar wikitext]
(cons "Wikitext" map))
(define-key map [unfill-article]
'("Unfill article" . wikitext-unfill-article))
(define-key map [fill-article]
'("Fill article" . wikitext-fill-article))
(define-key map [separator-fill] '("--"))
(define-key map [next-header]
'("Next header" . wikitext-next-header))
(define-key map [prev-header]
'("Previous header" . wikitext-prev-header))
(define-key map [separator-header] '("--"))
(define-key map [outline]
'("Toggle Outline Mode..." . outline-minor-mode)))
(define-key wikitext-mode-map "\C-c\C-q"
'wikitext-unfill-article)
(define-key wikitext-mode-map "\C-c\M-q"
'wikitext-fill-article)
(make-local-variable 'change-major-mode-hook))
(defun wikitext-turn-on-longlines ()
"Turn on longlines-mode if it is defined."
(if (functionp 'longlines-mode)
(longlines-mode 1)))
(add-hook 'wikitext-mode-hook 'wikitext-turn-on-longlines)
(provide 'wikitext-mode)