;;; wiki-nav.el --- Simple file navigation using [[WikiStrings]] ;; ;; Copyright (c) 2011-15 Roland Walker ;; ;; Author: Roland Walker ;; Homepage: http://github.com/rolandwalker/button-lock ;; URL: http://raw.githubusercontent.com/rolandwalker/button-lock/master/wiki-nav.el ;; Package-Version: 1.0.2 ;; Package-Commit: cd0bf4a3c2f224d851e6ed8a54a6e80c129b225f ;; Version: 1.0.2 ;; Last-Updated: 23 Feb 2015 ;; EmacsWiki: WikiNavMode ;; Keywords: mouse, button, hypermedia, navigation ;; Package-Requires: ((button-lock "1.0.2") (nav-flash "1.0.0")) ;; ;; Simplified BSD License ;; ;;; Commentary: ;; ;;;;;;;;;;;;;;;;;;; ;; ;; Table of Contents ;; ;; [[Quickstart]] ;; [[Explanation]] ;; [[Example usage]] ;; [[See Also]] ;; [[Prior Art]] ;; [[Notes]] ;; [[Bugs]] ;; [[Compatibility and Requirements]] ;; [[Todo]] ;; [[License]] ;; [[Code]] ;; ;;;;;;;;;;;;;;;;;;; ;; ;; [[' symbol, too, but that simply indicates ;; the default forward-search navigation. ;; ;; Both forward and backward navigation will wrap around the ends ;; of the file without prompting. ;; ;; Leading and trailing space inside a link is ignored. ;; ;; From the keyboard: ;; ;; control-c control-w ;; ;; skip forward in the buffer to the next link ;; ;; control-c control-W ;; ;; skip backward in the buffer to the previous link ;; ;; return if positioned on a link, activate it ;; ;; tab if positioned on a link, skip forward in the ;; buffer to the next link of any kind (need not ;; match the current link) ;; ;; S-tab if positioned on a link, skip backward in the ;; buffer to the previous link of any kind (need not ;; not match the current link) ;; ;; Advanced usage: ;; ;; Bracketed links may contain external URLs ;; ;; [[http://google.com]] ;; ;; Or they may use various internally-recognized URI schemes: ;; ;; visit: navigates to another file ;; ;; [[visit:/etc/hosts]] ;; ;; [[visit:/path/to/another/file:NameOfLink]] ;; ;; func: navigates to the definition of a function ;; ;; [[func:main]] ;; ;; line: navigates to a line number ;; ;; [[line:12]] ;; ;; visit: may be combined with other schemes: ;; ;; [[visit:/path/to/another/file:func:main]] ;; ;; [[visit:/etc/hosts:line:5]] ;; ;; Path names and similar strings are subjected to URI-style ;; unescaping before lookup. To link a filename which contains a ;; colon, substitute "%3A" for the colon character. ;; ;; See the documentation for the function wiki-nav for more ;; information. ;; ;; [[ ;; ;; org-mode ;; Carsten Dominik ;; ;; [[>> together within a file ;; like org-mode radio links ;; ;; patch font-lock to support keyword searching in comment only, ;; like 'keep, only different - maybe not needed if using a func ;; instead of a regexp in keyword ;; ;; raised button style option ;; ;; break down monolithic dispatch function wiki-nav-action-1 ;; ;; schemes to add ;; search: ;; regexp: ;; elisp: ;; ;; wiki-nav-links can be optimized by tracking which buffers are ;; completely fontified - doesn't font-lock do that? ;; ;; similarly, speed up wiki-nav-find-any-link by remembering if ;; the buffer is fontified, or switch to searching by regexp ;; ;; version of wiki-nav-find-any-link that does not wrap ;; ;; wiki-nav-ido can only go to one occurrence of a duplicate - ;; may not always be first ;; ;; toggle key for switching to all buffers within wiki-nav-ido ;; prompt ;; ;; sort recently-used items first in wiki-nav-ido - see ;; yas/insert-snippet for example ;; ;; [[") "List of key sequences to skip forward from a wiki-nav link to the next link. The key binding is active only when the point is on a wiki-nav link. The format for key sequences is as defined by `kbd'." :type '(repeat string) :group 'wiki-nav-keys) (defcustom wiki-nav-skip-to-previous-keys '("S-TAB" "S-" "" "S-") "List of key sequences to skip back from a wiki-nav link to the previous link. The key binding is active only when the point is on a wiki-nav link. The format for key sequences is as defined by `kbd'." :type '(repeat string) :group 'wiki-nav-keys) ;;;###autoload (defgroup wiki-nav-faces nil "Faces used by wiki-nav." :group 'wiki-nav) (defface wiki-nav-link-face '((t (:inherit link))) "Face to show wiki-nav links" :group 'wiki-nav-faces) (defface wiki-nav-mouse-face '((t (:inherit button-lock-mouse-face))) "Face to highlight wiki-nav link mouseovers" :group 'wiki-nav-faces) ;;;###autoload (defgroup wiki-nav-parsing nil "Strings and regular expressions used by wiki-nav to define links." :group 'wiki-nav) (defcustom wiki-nav-link-start "[[" "A string (not a regular expression) which open a wiki-style navigation link. Since the construct [[text]] can show up for other reasons, you might change this to \"[[[\"." :type 'string :group 'wiki-nav-parsing) (defcustom wiki-nav-link-stop "]]" "A string (not a regexp) which closes a wiki-style navigation link. Since the construct [[text]] can show up for other reasons, you might change this to \"]]]\"." :type 'string :group 'wiki-nav-parsing) (defcustom wiki-nav-link-text "[^][\n]+" "A regular expression defining the text inside wiki-style navigation links. The value should exclude newlines and start/stop delimiters." :type 'regexp :group 'wiki-nav-parsing) (defcustom wiki-nav-external-link-pattern "\\`[a-zA-Z]+:[^[:space:]]+" "A regular expression for recognizing URLs inside wiki-style navigation links. The default is very permissive. To be more strict, try \"\\\\`[a-zA-Z]+://[^[:space:]]+\", or \"\\\\`http://[^[:space:]]+\". Setting the value to the empty string will disable the feature entirely, suppressing the recognition of external URLs." :type 'regexp :group 'wiki-nav-parsing) (defcustom wiki-nav-visit-link-pattern "\\`visit:\\([^:\n]+?\\)\\(?:\\|:\\([^\n]*\\)\\)\\'" "A regular expression for recognizing wiki-nav links outside the current file. The format defined by the default expression is delimited by colons visit:/posix/path/to/another/file or visit:/posix/path/to/another/file:WikiString Other internally recognized link schemes may be substituted for the WikiString visit:/posix/path/to/another/file:line:10 Set this value to the empty string to disable the feature entirely." :type 'regexp :group 'wiki-nav-parsing) (defcustom wiki-nav-function-link-pattern "\\`func\\(?:tion\\)?:\\([^\n]+\\)\\'" "A regexp identifying wiki-nav links which point to function definitions. The format defined by the default expression is delimited by colons func:function_name Imenu is used to find the function definition. Set this value to the empty string to disable the feature entirely." :type 'regexp :group 'wiki-nav-parsing) (defcustom wiki-nav-line-number-link-pattern "\\`line:\\([0-9]+\\)\\'" "A regexp for identifying wiki-nav links which point to line numbers. The format defined by the default expression is delimited by colons line:111 Set this value to the empty string to disable the feature entirely." :type 'regexp :group 'wiki-nav-parsing) ;;;###autoload (defgroup wiki-nav-global nil "Settings for `global-wiki-nav-mode'." :group 'wiki-nav) (defcustom wiki-nav-exclude-modes '( fundamental-mode org-mode ) "List of major modes in which global wiki-nav will not be activated. A buffer will also be excluded if its major mode is derived from a mode in this list. This is in addition to any modes listed in `button-lock-exclude-modes'. Modes may be excluded for reasons of security (since buttons can execute arbitrary functions), efficiency, or to avoid conflicts with modes that provide similar functionality." :type '(repeat symbol) :group 'wiki-nav-global) (defcustom wiki-nav-buffer-name-exclude-pattern "\\`[* ]" "Do not activate minor made in buffers matching this regular expression. Buffers may be excluded for reasons of security (since buttons can execute arbitrary functions), efficiency, or to avoid conflicts with modes that provide similar functionality. The default pattern is designed to match buffers which are programatically generated or internal to Emacs." :type 'regexp :group 'wiki-nav-global) (defcustom wiki-nav-buffer-include-functions '() "Do not activate minor mode in a buffer unless all functions evaluate non-nil. Each function should take a single argument (a buffer). Set this value to nil to disable." :type '(repeat function) :group 'wiki-nav-global) (defcustom wiki-nav-buffer-exclude-functions '() "Do not activate minor mode in a buffer if any function evaluates non-nil. Each function should take a single argument (a buffer). Set this value to nil to disable." :type '(repeat function) :group 'wiki-nav-global) ;;; variables (defvar wiki-nav-mode nil "Mode variable for wiki-nav.") (make-variable-buffer-local 'wiki-nav-mode) (defvar wiki-nav-button nil "Holds the buffer-local button definition when the mode is active.") (make-variable-buffer-local 'wiki-nav-button) (defvar wiki-nav-mode-keymap (let ((map (make-sparse-keymap))) (dolist (key wiki-nav-find-any-link-keys) (define-key map (read-kbd-macro key) 'wiki-nav-find-any-link)) (dolist (key wiki-nav-find-any-previous-link-keys) (define-key map (read-kbd-macro key) 'wiki-nav-find-any-previous-link)) map)) ;;; macros (defmacro wiki-nav-called-interactively-p (&optional kind) "A backward-compatible version of `called-interactively-p'. Optional KIND is as documented at `called-interactively-p' in GNU Emacs 24.1 or higher." (cond ((not (fboundp 'called-interactively-p)) '(interactive-p)) ((condition-case nil (progn (called-interactively-p 'any) t) (error nil)) `(called-interactively-p ,kind)) (t '(called-interactively-p)))) ;;; compatibility functions (unless (fboundp 'back-button-push-mark-local-and-global) (fset 'back-button-push-mark (symbol-function 'push-mark)) (defun back-button-push-mark-local-and-global (&optional location nomsg activate consecutives) "Push mark at LOCATION, and unconditionally add to `global-mark-ring'. This function differs from `push-mark' in that `global-mark-ring' is always updated. LOCATION is optional, and defaults to the current point. NOMSG and ACTIVATE are as documented at `push-mark'. When CONSECUTIVES is set to 'limit and the new mark is in the same buffer as the first entry in `global-mark-ring', the first entry in `global-mark-ring' will be replaced. Otherwise, a new entry is pushed onto `global-mark-ring'. When CONSECUTIVES is set to 'allow-dupes, it is possible to push an exact duplicate of the current topmost mark onto `global-mark-ring'." (callf or location (point)) (back-button-push-mark location nomsg activate) (when (or (eq consecutives 'allow-dupes) (not (equal (mark-marker) (car global-mark-ring)))) (when (and (eq consecutives 'limit) (eq (marker-buffer (car global-mark-ring)) (current-buffer))) (move-marker (car global-mark-ring) nil) (pop global-mark-ring)) (push (copy-marker (mark-marker)) global-mark-ring) (when (> (length global-mark-ring) global-mark-ring-max) (move-marker (car (nthcdr global-mark-ring-max global-mark-ring)) nil) (setcdr (nthcdr (1- global-mark-ring-max) global-mark-ring) nil))))) ;;; utility functions ;; generic utility functions (defun wiki-nav-point-before () "Return the position before the current point. Returns `point-min' if the point is at the minimum." (max (point-min) (1- (point)))) (defun wiki-nav-comment-only-mode-p () "Return true if links should be constrained to comments in the current mode." (delq nil (mapcar 'derived-mode-p wiki-nav-comment-only-modes))) (defun wiki-nav-alist-flatten (list) "Flatten LIST which may contain other lists. Do not flatten cons cells." (cond ((null list) nil) ((and (consp list) (nthcdr (safe-length list) list)) (list list)) ((and (listp list) (consp (car list))) (append (wiki-nav-alist-flatten (car list)) (wiki-nav-alist-flatten (cdr list)))) ((listp list) (cons (car list) (wiki-nav-alist-flatten (cdr list)))) (t (list list)))) ;; buffer functions (defun wiki-nav-buffer-included-p (buf) "Return BUF if global wiki-nav should enable wiki-nav in BUF." (when (and (not noninteractive) (bufferp buf) (buffer-name buf)) (require 'button-lock) (with-current-buffer buf (when (and (not (minibufferp buf)) (not (eq (aref (buffer-name) 0) ?\s)) ; overlaps with exclude-pattern (not (memq major-mode button-lock-exclude-modes)) (not (memq major-mode wiki-nav-exclude-modes)) (not (intersection (button-lock-parent-modes) button-lock-exclude-modes)) (not (intersection (button-lock-parent-modes) wiki-nav-exclude-modes)) (not (string-match-p wiki-nav-buffer-name-exclude-pattern (buffer-name buf))) (catch 'success (dolist (filt wiki-nav-buffer-exclude-functions) (when (funcall filt buf) (throw 'success nil))) t) (catch 'failure (dolist (filt wiki-nav-buffer-include-functions) (unless (funcall filt buf) (throw 'failure nil))) t)) buf)))) ;; link functions (defun wiki-nav-link-set (&optional arg) "Use `button-lock-mode' to set up wiki-nav links in a buffer. If called with negative ARG, remove the links." (callf or arg 1) (when (and (>= arg 0) (or (not (boundp 'button-lock-mode)) (not button-lock-mode))) (button-lock-mode 1)) (if (< arg 0) (button-lock-unset-button wiki-nav-button) (setq wiki-nav-button (button-lock-set-button (concat (if (wiki-nav-comment-only-mode-p) "\\s<\\S>*?" "") (regexp-quote wiki-nav-link-start) "\\(" wiki-nav-link-text "\\)" (regexp-quote wiki-nav-link-stop)) 'wiki-nav-mouse-action :double-mouse-1 wiki-nav-multi-action-function :face 'wiki-nav-link-face :mouse-face 'wiki-nav-mouse-face :face-policy 'prepend :additional-property 'wiki-nav :rear-sticky wiki-nav-rear-nonsticky :grouping 1)) (dolist (key wiki-nav-activate-keys) (button-lock-extend-binding wiki-nav-button 'wiki-nav-keyboard-action nil key)) (dolist (key wiki-nav-skip-to-next-keys) (button-lock-extend-binding wiki-nav-button 'wiki-nav-find-any-link nil key)) (dolist (key wiki-nav-skip-to-previous-keys) (button-lock-extend-binding wiki-nav-button 'wiki-nav-find-any-previous-link nil key)))) (defun wiki-nav-links (&optional buffer) "Return an alist of all wiki-nav links in BUFFER (defaults to current buffer). The return value is an alist of cells in the form (\"text\" buffer . start-pos)." (callf or buffer (current-buffer)) (with-current-buffer buffer (when wiki-nav-mode (let ((font-lock-fontify-buffer-function 'font-lock-default-fontify-buffer) (pos nil) (links nil)) (font-lock-fontify-buffer) (setq pos (next-single-property-change (point-min) 'wiki-nav)) (while (and pos (< pos (point-max))) (when (get-text-property pos 'wiki-nav) (let ((start pos)) (while (and pos (< pos (point-max)) (get-text-property pos 'wiki-nav)) (callf next-single-property-change pos 'wiki-nav)) (when (not (get-text-property pos 'wiki-nav)) (push (cons (buffer-substring-no-properties start pos) (cons buffer start)) links)))) (callf next-single-property-change pos 'wiki-nav)) links)))) (defun wiki-nav-links-all-buffers () "Return an alist of wiki-nav links in all buffers. See `wiki-nav-links'. Note that this function fontifies every buffer, which can take seconds to complete." (let ((reporter (make-progress-reporter "Searching ..." 0 (length (buffer-list)))) (counter 0) (l-alist nil)) (dolist (buf (buffer-list)) (unless wiki-nav-less-feedback (progress-reporter-update reporter (incf counter))) (push (wiki-nav-links buf) l-alist)) (progress-reporter-done reporter) (delq nil (wiki-nav-alist-flatten l-alist)))) ;; bindable action dispatch commands ;;;###autoload (defun wiki-nav-default-multi-action (event) "Dispatch the default double-click navigation action. The link used is that identified by the position at EVENT, a mouse event." (interactive "e") (let ((bounds (button-lock-find-extent (posn-point (event-end event)) 'wiki-nav)) (str-val nil) (case-fold-search t) (search-upper-case nil)) (when bounds (setq str-val (replace-regexp-in-string "\\(^[[:space:]<>]*\\|[[:space:]]*\\'\\)" "" (buffer-substring-no-properties (car bounds) (cdr bounds)))) (when (fboundp 'multi-occur-in-matching-buffers) (multi-occur-in-matching-buffers "\\`[^ *]" (concat (regexp-quote wiki-nav-link-start) "[[:space:]<>]*" str-val "[[:space:]]*" (regexp-quote wiki-nav-link-stop)) t))))) ;; Monolithic function to dispatch any link action. (defun wiki-nav-action-1 (pos) "Dispatch the default navigation action for the wiki-nav link at POS." (let ((bounds (button-lock-find-extent pos 'wiki-nav)) (str-val nil) (found nil) (visit nil) (search-function 're-search-forward) (wrap-point (point-min)) (wrap-message "Search wrapped past end of file") (buffer-start (current-buffer)) (point-start (point)) (new-point nil) (case-fold-search t) (search-upper-case nil)) (when bounds (setq str-val (buffer-substring-no-properties (car bounds) (cdr bounds))) (when (string-match-p "^[[:space:]]*<" str-val) (setq search-function 're-search-backward) (setq wrap-point (point-max)) (setq wrap-message "Search wrapped past beginning of file")) (setq str-val (replace-regexp-in-string "\\(^[[:space:]<>]*\\|[[:space:]]*\\'\\)" "" str-val)) (if (and wiki-nav-external-link-pattern (and (> (length wiki-nav-visit-link-pattern) 0) (not (string-match-p wiki-nav-visit-link-pattern str-val))) (and (> (length wiki-nav-function-link-pattern) 0) (not (string-match-p wiki-nav-function-link-pattern str-val))) (and (> (length wiki-nav-line-number-link-pattern) 0) (not (string-match-p wiki-nav-line-number-link-pattern str-val))) (and (> (length wiki-nav-external-link-pattern) 0) (string-match-p wiki-nav-external-link-pattern str-val))) (progn (message "browsing to external URL...") (browse-url str-val)) (save-match-data (when (and (> (length wiki-nav-visit-link-pattern) 0) (string-match wiki-nav-visit-link-pattern str-val)) (let ((tmp "")) (setq tmp (match-string-no-properties 2 str-val)) (switch-to-buffer (find-file (expand-file-name (url-unhex-string (match-string-no-properties 1 str-val))))) (setq str-val tmp)) (back-button-push-mark-local-and-global point-start t) (when (and (= (length str-val) 0) (fboundp 'nav-flash-show)) (nav-flash-show)) (setq visit t)) (when (> (length str-val) 0) (cond ((and (> (length wiki-nav-function-link-pattern) 0) (string-match wiki-nav-function-link-pattern str-val)) ;; imenu return value is not helpful. It also sometimes changes the mark. Wrap it in an excursion (when (and (setq new-point (save-excursion (imenu (url-unhex-string (match-string-no-properties 1 str-val))) (point))) (not (= new-point (point)))) (back-button-push-mark-local-and-global point-start t) (goto-char new-point) (when (fboundp 'nav-flash-show) (nav-flash-show)) (setq found :func)) ;; return to the original buffer on failure (unless found (when (and visit (> (length str-val) 0)) (switch-to-buffer buffer-start) (setq visit nil)) (goto-char point-start))) ((and (> (length wiki-nav-line-number-link-pattern) 0) (string-match wiki-nav-line-number-link-pattern str-val)) ;; For line-number scheme, go as far as possible, but don't set found unless successful. ;; Don't worry about returning to original buffer on failure. (let ((ln (string-to-number (match-string-no-properties 1 str-val)))) (widen) (back-button-push-mark-local-and-global point-start t) (goto-char (point-min)) (forward-line (1- ln)) (when (fboundp 'nav-flash-show) (nav-flash-show)) (if (= (line-number-at-pos) ln) (setq found :line)))) (t (setq str-val (regexp-quote (url-unhex-string str-val))) (deactivate-mark) (if (funcall search-function (concat (if (wiki-nav-comment-only-mode-p) "\\s<\\S>*?" "") "\\(" (regexp-quote wiki-nav-link-start) "\\(" "[[:space:]<>]*" str-val "[[:space:]]*" "\\)" (regexp-quote wiki-nav-link-stop) "\\)") nil t) (progn (setq found :jump) (back-button-push-mark-local-and-global point-start t) (goto-char (match-beginning 2)) (when (fboundp 'nav-flash-show) (nav-flash-show))) ;; else (goto-char wrap-point) (deactivate-mark) (if (funcall search-function (concat (if (wiki-nav-comment-only-mode-p) "\\s<\\S>*?" "") "\\(" (regexp-quote wiki-nav-link-start) "\\(" "[[:space:]<>]*" str-val "[[:space:]]*" "\\)" (regexp-quote wiki-nav-link-stop) "\\)") nil t) (progn (setq found :wrap) (back-button-push-mark-local-and-global point-start t) (goto-char (match-beginning 2)) (when (fboundp 'nav-flash-show) (nav-flash-show))))) ;; return to the original buffer on failure (unless found (when (and visit (> (length str-val) 0)) (switch-to-buffer buffer-start) (setq visit nil)) (goto-char point-start)))) (cond (visit (unless wiki-nav-less-feedback (message "followed link to new file"))) ((or (not found) (and (>= (point) (- (car bounds) (length wiki-nav-link-start))) (<= (point) (+ (cdr bounds) (length wiki-nav-link-stop))))) ;; give failure message even when wiki-nav-less-feedback is set (message "no matching link found")) ((eq found :wrap) (unless wiki-nav-less-feedback (message wrap-message)))))))) found)) ;;;###autoload (defun wiki-nav-mouse-action (event) "Dispatch the default action for the wiki-nav link at the mouse location. Mouse location is defined by the mouse event EVENT." (interactive "e") (wiki-nav-action-1 (posn-point (event-end event)))) ;;;###autoload (defun wiki-nav-keyboard-action () "Dispatch the default navigation action for the wiki-nav link under the point." (interactive) (wiki-nav-action-1 (point))) ;;; minor mode definition ;;;###autoload (define-minor-mode wiki-nav-mode "Turn on navigation by bracketed [[WikiStrings]] within a document. When wiki-nav links are activated, clicking on a bracketed link causes emacs to search the document for another link with text matching the inner string. If a match is found, the cursor is moved to the location of the match. If the string looks like it might be a URL (starts with alphabetical characters followed by a colon), an external browser will be spawned on the URL. This behavior can be controlled by the customizable variable `wiki-nav-external-link-pattern'. If `multi-occur' is installed (standard with recent Emacs), double-clicking a wiki-nav link will search for matching links in all open file buffers. If the link follows the form visit:/path/name:WikiString Emacs will visit the named file, and search for the navigation string there. This behavior can be controlled by the customizable variable `wiki-nav-visit-link-pattern'. If the link follows the form func:FunctionName the link will lead to the definition of the given function, as defined by imenu. This behavior can be controlled by the customizable variable `wiki-nav-function-link-pattern'. If the link follows the form line: the link will lead to the given line number. This behavior can be controlled by the customizable variable `wiki-nav-line-number-link-pattern'. The leading and trailing delimiters which define the navigation links may be customized, as may the regular expressions that match URLs and non-URL inner text. With no argument, this command toggles the mode. Non-null prefix argument turns on the mode. Null prefix argument turns off the mode." nil wiki-nav-mode-lighter wiki-nav-mode-keymap (cond ((and wiki-nav-mode (or noninteractive ; never turn on wiki-nav where (eq (aref (buffer-name) 0) ?\s))) ; there can be no font-lock (setq wiki-nav-mode nil)) (wiki-nav-mode (wiki-nav-link-set) (when (wiki-nav-called-interactively-p 'interactive) (message "wiki-nav mode enabled"))) (t (wiki-nav-link-set -1) (when (wiki-nav-called-interactively-p 'interactive) (message "wiki-nav mode disabled"))))) ;;; global minor-mode definition (defun wiki-nav-maybe-turn-on (&optional arg) "Called by `global-wiki-nav-mode' to activate `wiki-nav-mode' in a buffer. `wiki-nav-mode' will be activated in every buffer, except minibuffers buffers with names that begin with space buffers excluded by `wiki-nav-exclude-modes' buffers excluded by `button-lock-exclude-modes' buffers excluded by `wiki-nav-buffer-name-exclude-pattern' buffers excluded by `button-lock-buffer-name-exclude-pattern' If called with a negative ARG, deactivate `wiki-nav-mode' in the buffer." (callf or arg 1) (when (or (< arg 0) (wiki-nav-buffer-included-p (current-buffer))) (wiki-nav-mode arg))) ;;;###autoload (define-globalized-minor-mode global-wiki-nav-mode wiki-nav-mode wiki-nav-maybe-turn-on :group 'wiki-nav) ;;; interactive commands ;;;###autoload (defun wiki-nav-find-any-link (&optional arg) "Skip forward to the next defined wiki-nav link. Automatically wraps past the end of the buffer. With a negative prefix argument ARG, skip backward to the previous defined wiki-nav link." (interactive "p") (when wiki-nav-mode (let ((font-lock-fontify-buffer-function 'font-lock-default-fontify-buffer) (newpos nil) (orig-pos (point)) (skip-function 'next-single-property-change) (search-function 're-search-forward) (look-function 'point) (wrap-point (point-min)) (bounds nil)) ;; This is slow, but otherwise links get missed. There ;; must be a better way. (font-lock-fontify-buffer) (when (and arg (< arg 0)) (setq skip-function 'previous-single-property-change) (setq search-function 're-search-backward) (setq wrap-point (point-max)) (setq look-function 'wiki-nav-point-before)) ;; get out of the current link if we are in one (when (and (get-text-property (funcall look-function) 'wiki-nav) (setq newpos (funcall skip-function (point) 'wiki-nav))) (goto-char newpos)) ;; find the next link (deactivate-mark) (if (funcall search-function (concat (if (wiki-nav-comment-only-mode-p) "\\s<\\S>*?" "") "\\(" (regexp-quote wiki-nav-link-start) "\\(" wiki-nav-link-text "\\)" (regexp-quote wiki-nav-link-stop) "\\)") nil t) (progn (back-button-push-mark-local-and-global orig-pos t) (goto-char (match-beginning 2)) (when (fboundp 'nav-flash-show) (nav-flash-show))) ;; else (goto-char wrap-point) (deactivate-mark) (when (funcall search-function (concat (if (wiki-nav-comment-only-mode-p) "\\s<\\S>*?" "") "\\(" (regexp-quote wiki-nav-link-start) "\\(" wiki-nav-link-text "\\)" (regexp-quote wiki-nav-link-stop) "\\)") nil t) (back-button-push-mark-local-and-global orig-pos t) (goto-char (match-beginning 2)) (when (fboundp 'nav-flash-show) (nav-flash-show))))))) ;;;###autoload (defun wiki-nav-find-any-previous-link () "Skip backward to the previous defined wiki-nav link. Automatically wraps past the beginning of the buffer. With a negative prefix argument ARG, skip backward to the previous defined wiki-nav link." (interactive) (wiki-nav-find-any-link -1)) ;;;###autoload (defun wiki-nav-ido (arg) "Navigate to wiki-nav strings using `ido-completing-read'. With universal prefix ARG, navigate to wiki-nav strings in all buffers." (interactive "P") (let* ((links (if (consp arg) (wiki-nav-links-all-buffers) (wiki-nav-links))) (str-list (delete-dups (mapcar 'car links))) (choice nil) (ido-mode t)) (setq choice (ido-completing-read "WikiString: " str-list)) (when (and choice (assoc choice links)) (back-button-push-mark-local-and-global nil t) (setq choice (cdr (assoc choice links))) (switch-to-buffer (car choice)) (goto-char (cdr choice)) (when (fboundp 'nav-flash-show) (nav-flash-show))))) (provide 'wiki-nav) ;; ;; Emacs ;; ;; Local Variables: ;; indent-tabs-mode: nil ;; mangle-whitespace: t ;; require-final-newline: t ;; coding: utf-8 ;; byte-compile-warnings: (not cl-functions redefine) ;; End: ;; ;;; wiki-nav.el ends here