Wiki Mode Code

First crack at a modeless wiki browser for this wiki, not a local wiki as on the WikiMode page. The mode skeleton was ripped from asm-mode and there's still bits of it there (but not much, and I don't need it anyway).

Try loading it and then do M-x wiki-fix. Right now there's a bug that you have to do M-x font-lock-mode after the new page comes up. Take a look at the highlighted links ... put the cursor over them and do M-x wiki-browse-thing-at-point. Group edits gratefully accepted, as I don't claim to speak lisp. I've only tested this in XEmacs 21. I don't know if w3-mode exists in GNUEmacs, so YMMV.

 ;;; wiki-mode.el --- mode for interacting with the wiki

;; usable under WikiCopyRights ;; Original Author: BrianEwins <> ;; Add WikiStyle comments below. If the code doesn't look right due ;; to TextFormattingRules click on EditPage and look at the source!!!

;;; Code:

(require 'w3)

(defcustom wiki-mode-syntax-table nil "Syntax table used while in Wiki mode.")

(defvar wiki-mode-abbrev-table nil "Abbrev table used while in Wiki mode.") (define-abbrev-table 'wiki-mode-abbrev-table ())

(defvar wiki-mode-map nil "Keymap for Wiki mode.")

(if wiki-mode-map nil ;; XEmacs change (setq wiki-mode-map (make-sparse-keymap 'wiki-mode-map)) ;; Note that the comment character isn't set up until wiki-mode is called. ; need to define some keys for things like wiki-browse-thing-at-point )

(defun wiki-fix () "Mainline on RecentChanges" (interactive) (wiki-browse-thing "RecentChanges"))

(defun wiki-browse-thing (thing) "Browse to a wiki link, or an URL. Just supply the WikiName maam, not the full URL, unless you want this to open netscape..." (interactive "sWikiName or url:") (let (wiki-new-buffer) (if (string-match "^[A-Z][a-z]+\\([A-Z][a-z]+\\)+$" thing) (progn (setq wiki-new-buffer (cdr (url-retrieve (concat "" thing)))) (switch-to-buffer wiki-new-buffer) (wiki-mode) (font-lock-mode 1) (rename-buffer thing)) (browse-url thing))))

(defun wiki-browse-thing-at-point (&optional pt) "Determine if the local face text property is 'font-lock-keyword-face. if it is then examine the region using this face. If its an wiki, load up the wiki page; if its not, it must be an URL, so defer to browse-url-at-point. Relying on font-lock-mode is an ugly design but it just might work..." (interactive) (save-excursion (if pt (goto-char pt)) (let (bounds local-face) (setq local-face (get-char-property (point) 'face)) (if (eq local-face 'font-lock-keyword-face) (progn (setq bounds (text-property-bounds (point) 'face)) (wiki-browse-thing (buffer-substring (car bounds) (cdr bounds))))))))

; the character ranges below cover the ascii chars allowable in URIs. ; see the definition of 'uric' in ; the scheme part omits three chars which I've never seen used this way, ; and could cause the matcher problems. Using character ranges is dodgy ; in an I18N environment, but it saved me typing... ; DON'T ADD PATTERNS FOR MULTIPLE-' FORMATTING. ; Please.:o) All that happens is that one or t'other of the ; font lock rules is ignored - people regularly put links inside emphasis. (defconst wiki-font-lock-keywords (list '("\\b[A-Z][a-z]+\\([A-Z][a-z]+\\)+\\b" . font-lock-keyword-face) '("\\b[a-zA-Z][a-zA-Z0-9]+:[$-:=?-Z_a-z~]+[$-+/-Z_a-z~-]\\b" . font-lock-keyword-face) ) "Additional expressions to highlight in Wiki mode.")

(put 'wiki-mode 'font-lock-defaults '(wiki-font-lock-keywords))

;;;###autoload (defun wiki-mode () "Major mode for editing typical a wiki. Features a private abbrev table and the following bindings: Turning on Wiki mode runs the hook `wiki-mode-hook' at the end of initialization.

Special commands: \\{wiki-mode-map} " (interactive) (kill-all-local-variables) (setq mode-name "Wiki") (setq major-mode 'wiki-mode) (setq local-abbrev-table wiki-mode-abbrev-table) (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '(wiki-font-lock-keywords)) (make-local-variable 'wiki-mode-syntax-table) (setq wiki-mode-syntax-table (make-syntax-table)) (set-syntax-table wiki-mode-syntax-table) (let ((ourmap (make-sparse-keymap))) (set-keymap-parents ourmap (list wiki-mode-map)) (use-local-map ourmap)) (run-hooks 'wiki-mode-hook) ;; this doesn't work and I have no idea how to make it work, yet. (turn-on-font-lock))

Here's my take on wiki-mode.

 ;;;; Frank Gerhardt's wiki-mode Version 0.1.1 (19990827)
 ;; 19 May, 2000: VC Support and Template for a new file by
 ;; 27 July, 2000: Wiki minor mode
 ;; Gerrit Riessen,

;; The latest version can be found at ;; ;; ;; Note: wiki-mode has only been used with NTEmacs 20.3.1 and ;; 20.4. The setup instructions refer to WinNT. It should not ;; be a problem to use it on Unix. ;; Also works with NTEmacs 20.7.1 ;; ;; Also required are the vc and tempo libraries. These are included ;; in the standard distributions of the Emacs. ;; VC library requires CVS/RCS of some form. For WinNT, CVS can be ;; obtained by installing the RKTools -- ;; ;; ;; Prerequisites (skip them if you don't want to browse backlinks): ;; ;; For the browsing of backlinks "agrep", the approximative ;; grep, is required. The ususal grep would work as well, but ;; agrep is better at searching for wiki-names if not all files ;; have been converted to the Wiki file name convention. ;; -> get agrep.exe and put it somewhere in your shell path ;; ( ;; ;; agrep is called by the superior igrep-mode. ;; -> get igrep.el ( and put it ;; somewhere in you elisp load-path. If you like, ;; byte-compile it. ;; ;; -> insert the following code into your .emacs ;; ;; (setq grep-null-device null-device) ;; (autoload 'agrep "igrep" "*Run `agrep`..." t) ;; (setq igrep-options "-i") ;; ;; Installation: ;; ;; -> save wiki-mode.el somewhere in your load-path. Byte-compile it! ;; (M-x byte-compile-file ...) ;; ;; -> insert the following code into you .emacs. The directory ;; has to exist. ;; ;; (setq wiki-directory (expand-file-name "~/wiki")) ;; (setq auto-mode-alist ;; (cons ;; (cons (concat "^" wiki-directory "/") 'wiki-mode) ;; auto-mode-alist)) ;; (require 'wiki-mode) ;; ;; -> restart Emacs or evaluate the elisp expressions above ;; immediately (C-x C-e). Open a file in the wiki-directory. ;; You should now see a welcome message. Create a few files ;; in the wiki-director mentioning the other files' names. ;; Call wiki-names-init to re-initialize the regular ;; expression used for highlighting. Call ;; wiki-highlight-buffer to re-highlight a buffer. ;; Note: use only alpha-numerical characters for the file ;; names. No spaces. Case doesn't matter.

(setq wiki-mode-name " Wiki")

(or (assq 'wiki-mode minor-mode-alist) (setq minor-mode-alist (cons '(wiki-mode wiki-mode-name) minor-mode-alist)))

(defgroup wiki nil "Minor mode to provide a Wiki like environment for Emacs" :group 'editing)

; otherwise regexp-opt fails (setq max-specpdl-size 80000) (setq max-lisp-eval-depth 80000)

(require 'vc) (require 'tempo)

;; A wrapper around the vc-checkin. I wrote this so that I could use ;; vc-checkin in a checkin-all-buffers function. Vc-checkin has the ;; annoying habit that it exits any loop in which it might be in when ;; an error occurs. But an error can be as simple as the file is not ;; checked out or not registered with RCS, the result is this function ;; which checks a buffer to see whether a) it is under RCS control ;; b) it is currently checked out. If so, then and only then, call vc-checkin. (defun wiki-checkin-buffer (msg buffer) (and (buffer-file-name buffer) (vc-latest-version (buffer-file-name buffer)) ;; vc-status returns useful information in a crypt manner! (eq (car (string-to-list (vc-status (buffer-file-name buffer)))) 58) (vc-checkin (buffer-file-name buffer) nil msg)))

(defvar wiki-new-file-template '((beginning-of-buffer) (file-name-nondirectory (buffer-file-name)) " -*- mode:" wiki-mode-name " -*-" n "-- " n 'p n "-- " n "$" "Id" "$ " n "Created on " (current-time-string) n ;;";;; Local Variables: ***" n ;;";;; mode: Wiki ***" n ;;";;; End: ***" n (tempo-forward-mark) ) "Template for a new Wiki file. The default is to add a Creation date to the bottom and the name of the file to the top of the newly created file. This value is used in a call to tempo-define-template.") (tempo-define-template "wiki-new-file-template" wiki-new-file-template)

(defcustom wiki-link-colour "orange" "Links are this colour.")

(defvar wiki-quote-region '("``" r "''")) (tempo-define-template "wiki-quote-region" wiki-quote-region)

(defun wiki-quote-region () (interactive) (tempo-template-wiki-quote-region))

(defvar wiki-names nil "A regexp of all wiki-names in the wiki directory. If the contents of the Wiki directory has changed, this variable can be reinitialized by calling interactively wiki-names-init.")

(defcustom wiki-directory "~/Wiki" "The directory where the Wiki files are located. The Wiki files should be plain text files. This directory will be checked immediately afer a find-file.")

(defcustom wiki-max-file-size 16384 "Wiki doesn't highlight files larger that this because highlighting of very long files takes time. If you want to hightlight anyway you can increase this limit or call wiki-highlight-buffer.")

(defun wiki-mode-define-keys () (interactive) (local-set-key "\C-xwh" 'wiki-highlight-buffer) (local-set-key "\C-xwb" 'wiki-browse-backlinks) (local-set-key "\C-xwn" 'next-error) (local-set-key "\C-xwf" 'wiki-skip-to-next-file) (local-set-key "\C-xwj" 'find-file-at-point))

(defcustom wiki-mode-hook nil "*Hook called by `wiki-mode'." :type 'hook :group 'wiki)

;; when a Wiki buffer is killed, then check it back in. (defun wiki-kill-buffer-hook () (if (wiki-is-wiki-file) (progn (wiki-checkin-buffer "Buffer was killed" (current-buffer)))))

(defvar wiki-mode nil "Non-nil when Wiki mode is enabled") (make-variable-buffer-local 'wiki-mode) (put 'wiki-mode 'permanent-local t)

(make-variable-buffer-local 'wiki-directory) (put 'wiki-directory 'permanent-local t)

(make-variable-buffer-local 'wiki-names) (put 'wiki-names 'permanent-local t)

(defvar wiki-buffer-was-read-only nil "Private variable to indicate whether a buffer was read-only")

(make-variable-buffer-local 'wiki-buffer-was-read-only) (put 'wiki-buffer-was-read-only 'permanent-local t)

;; when a Wiki buffer is killed, then it needs to be checked back in (add-hook 'kill-buffer-hook 'wiki-kill-buffer-hook)

;; When changing the major mode, the re-define the Wiki keys ;; This appears to be called _before_ the mode change, i.e. all ;; local key bindings are deleted -- this needs a fix. font-lock.el ;; does something similar by adding a hook to the post-command-hook ;; and checking each command. ;; Current solution is to call wiki-mode-define-keys directly after ;; reseting the major mode. (add-hook 'change-major-mode-hook 'wiki-mode-define-keys)

(defun wiki-mode (&optional arg) " Wiki mode for Emacs.

What is Wiki? I have no idea ;-) I discovered Wiki at

Basically all that Wiki does is scan a directory for file names and highlights those names when they appear in the text of a Wiki file. " (interactive)

(copy-face 'font-lock-keyword-face 'wiki-quote-face) (copy-face 'font-lock-string-face 'wiki-quotation-face) (copy-face 'underline 'wiki-link-face) (set-face-foreground 'wiki-link-face wiki-link-colour)

(let ((newmode (if (null arg) (not wiki-mode) (> (prefix-numeric-value arg) 0)))) (if newmode

;; turn it on (if (not wiki-mode) (progn (wiki-mode-define-keys)

(set (make-local-variable 'wiki-directory) (file-name-directory (buffer-file-name)))

(make-local-variable 'wiki-names) (wiki-names-init)

(set (make-local-variable 'default-case-fold-search) t)

(if (= buffer-saved-size 0) ;; is this file new? (progn ;; New file, run the new file template and register ;; the file with the VC system (tempo-template-wiki-new-file-template) (save-buffer)))

;; Version control register of the file. (if (not (vc-latest-version (buffer-file-name))) (vc-register))

(setq wiki-mode 't) (wiki-highlight-buffer) (run-hooks 'wiki-mode-hook)))

;; turn it off (if wiki-mode (progn (wiki-checkin-buffer "Wiki mode was turned off" (current-buffer)) (kill-local-variable 'wiki-directory) (kill-local-variable 'wiki-names) (kill-local-variable 'default-case-fold-search) (setq wiki-mode nil) ))))

(force-mode-line-update nil))

(defun wiki-names-init () "Re-Initialise the wiki-names variable" (interactive) ;; If the directory is empty, then initialise to some bogus value (if (directory-files wiki-directory nil "^[A-z0-9.]+$" t) (set-variable 'wiki-names ;; ToDo: the new version would be much faster if word boundary ;; not checked (concat "\\<" (regexp-opt (directory-files wiki-directory nil "^[A-z0-9.]+$" t) t) "\\>")) (set-variable 'wiki-names "\\<\\(NoMatch?\\)\\>")))

(defun wiki-is-buffer-wiki-file (buffer) "Return true if the given buffer object represents a Wiki buffer" (local-variable-p 'wiki-mode buffer))

(defun wiki-is-wiki-file () (wiki-is-buffer-wiki-file (current-buffer)))

(defmacro wiki-save-buffer-modified (&rest body) "Execute the BODY forms, restoring buffer modification status." `(let ((wiki-buffer-modified (buffer-modified-p))) (save-excursion (unwind-protect (progn ,@body) (set-buffer-modified-p wiki-buffer-modified)))))

(defun wiki-highlight-buffer () (interactive)

;; This is a quick hack: when the buffer is read-only, then ;; wiki-highlight-buffer complains that the buffer is read only and ;; refuses to do anything. ;; Solution: If the buffer is read-only then reset that to read for ;; the length of this function, i.e. reset back at the end of the function! (if buffer-read-only (progn (toggle-read-only) (setq wiki-buffer-was-read-only 't)) (setq wiki-buffer-was-read-only nil))

(wiki-save-buffer-modified (goto-char 1) (save-match-data (while (re-search-forward wiki-names nil t) (add-text-properties (match-beginning 0) (match-end 0) '(face wiki-link-face)) (add-text-properties (- (match-end 0) 1) (match-end 0) '(rear-nonsticky t)))) (goto-char 1) (save-match-data ;; Highlight things between >> and << (while (re-search-forward "\\(>>\\)\\([^<]\\|[<][^<]\\)*\\(<<\\)" nil t) (add-text-properties (+ (match-beginning 0) 2) (- (match-end 0) 2) '(face wiki-quotation-face)))) (goto-char 1) (save-match-data ;; Highlight things between `` and '' (while (re-search-forward "\\(``\\)\\([^']\\|['][^']\\)*\\(''\\)" nil t) (add-text-properties (+ (match-beginning 0) 2) (- (match-end 0) 2) '(face wiki-quote-face)))))

;; Was the buffer originally read only? (if wiki-buffer-was-read-only (toggle-read-only)))

;; only works on with a writable buffer. (defun wiki-rehighlight-buffer () "Call wiki-init-names before calling wiki-highlight-buffer. Only works with a writable buffer." (interactive) (wiki-names-init) (wiki-highlight-buffer))

;; when Emacs is shut down, then check the Wiki buffers back in. ;; This returns true to indicate to the shutdown process that it ;; can continue. (defun wiki-cleanup-on-kill-emacs () (setq all-buffers (buffer-list)) (while all-buffers (let ((buffer (car all-buffers))) (if (wiki-is-buffer-wiki-file buffer) (wiki-checkin-buffer "Emacs was killed" buffer))) (setq all-buffers (cdr all-buffers))) 't)

;; Append the hook to the end so that accidental C-xC-c don't ;; cause all the buffers to be checked in. That is, I have a ;; query function so that accidental C-xC-c don't shutdown my emacs. (add-hook 'kill-emacs-query-functions 'wiki-cleanup-on-kill-emacs 't)

(defun wiki-browse-backlinks (arg) "With C-u it looks for exact matches." (interactive "P") (save-some-buffers t) (agrep nil (buffer-name) "*" ; Todo: match "[A-Za-z0-9][A-Za-z0-9]*" (concat "-i -V0 " (if (null arg) "-1" "-0"))))

; Todo: write a function to find fuzzy backlinks spanning line ; ends. Using agrep with these options: ; -n -i -1 -d "$$" -e "todo" * NUL

(defun wiki-skip-to-next-file () (interactive) (select-window (get-buffer-window (get-buffer "*igrep*") 1)) (compilation-next-file 1) (compile-goto-error))

; ToDo: browsing by clicking on hyperlinks. This function could ; be bound to a mouse event... ; (defun wiki-jump () ; (interactive) ; (set-window-point (get-buffer-window (current-buffer)) ; (next-property-change (point))) ; (if (eq 'wiki-link-face (get-text-property (point) 'face)) ; (set-window-point (get-buffer-window (current-buffer)) ; (next-property-change (point)))))

(provide 'wiki-mode)


View edit of August 6, 2005 or FindPage with title or text search