summaryrefslogtreecommitdiffstats
path: root/emacs.d/nxhtml/util/mlinks.el
diff options
context:
space:
mode:
Diffstat (limited to 'emacs.d/nxhtml/util/mlinks.el')
-rw-r--r--emacs.d/nxhtml/util/mlinks.el1367
1 files changed, 1367 insertions, 0 deletions
diff --git a/emacs.d/nxhtml/util/mlinks.el b/emacs.d/nxhtml/util/mlinks.el
new file mode 100644
index 0000000..0f81654
--- /dev/null
+++ b/emacs.d/nxhtml/util/mlinks.el
@@ -0,0 +1,1367 @@
+;;; mlinks.el --- Minor mode making major mode dependent links
+;;
+;; Author: Lennar Borgman
+;; Created: Tue Jan 16 2007
+(defconst mlinks:version "0.28") ;;Version:
+;; Last-Updated: 2010-01-05 Tue
+;; Keywords:
+;; Compatibility:
+;;
+;; Fxeatures that might be required by this library:
+;;
+;; `appmenu', `cl', `mail-prsvr', `mm-util', `ourcomments-util',
+;; `url-expand', `url-methods', `url-parse', `url-util',
+;; `url-vars'.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Commentary:
+;;
+;; This file implements the minor mode `mlinks-mode' that create
+;; hyperlinks for different major modes. Such links can be visible or
+;; invisible. The meanings of the links are defined per mode.
+;;
+;; Examples:
+;;
+;; - In in html style modes the links are visible they can mean either
+;; open a file for editing, go to an achnor or view the link in a
+;; web browser etc.
+;;
+;; - In emacs lisp mode the links are invisible, but maybe highlighed
+;; when point or mouse is on them. (Having them highlighted when
+;; point is on them can be a quick way to check that you have
+;; spelled a symbol correct.) The meanings of the links in emacs
+;; lisp mode are go to definition.
+;;
+;; Common to links that open a buffer in Emacs is that you can the
+;; buffer opened in the same window, the other window or in a new
+;; frame. The same key binding is used in all major modes for this.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Change log:
+;;
+;; FIX-ME: url-hexify-string etc
+;;
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; 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 2, 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; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Code:
+
+(eval-when-compile (require 'cl))
+(eval-when-compile (require 'appmenu nil t))
+(eval-when-compile (require 'mumamo nil t))
+(eval-when-compile (require 'ourcomments-util nil t))
+
+(require 'rx)
+(require 'url-parse)
+(require 'url-expand)
+
+(defvar mlinks-point-hilighter-overlay nil)
+(make-variable-buffer-local 'mlinks-point-hilighter-overlay)
+(put 'mlinks-point-hilighter-overlay 'permanent-local t)
+
+;;;###autoload
+(defgroup mlinks nil
+ "Customization group for `mlinks-mode'."
+ :group 'nxhtml
+ :group 'hypermedia)
+
+(defvar mlinks-link-face 'mlinks-link-face)
+(defface mlinks-link-face
+ '((t (:inherit highlight)))
+ "Face normally active links have on them."
+ :group 'mlinks)
+
+(defvar mlinks-hyperactive-link-face 'mlinks-hyperactive-link-face)
+(defface mlinks-hyperactive-link-face
+ '((t (:inherit isearch)))
+ "Face hyper active links have on them."
+ :group 'mlinks)
+
+(defvar mlinks-font-lock-face 'mlinks-font-lock-face)
+(defface mlinks-font-lock-face
+ '((t :inherit link))
+ "Default face for MLinks' links."
+ :group 'mlinks)
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Mode function bindings
+
+;;(customize-option mlinks-mode-functions)
+(defcustom mlinks-mode-functions
+ '(
+ ;; For message buffer etc.
+ (fundamental-mode
+ ((goto mlinks-elisp-goto)
+ (hili mlinks-elisp-hili)
+ (hion t)
+ )
+ )
+ (emacs-lisp-mode
+ ((goto mlinks-elisp-goto)
+ (hili mlinks-elisp-hili)
+ (hion t)
+ )
+ )
+ ;; *scractch*
+ (lisp-interaction-mode
+ ((goto mlinks-elisp-goto)
+ (hili mlinks-elisp-hili)
+ (hion t)
+ )
+ )
+ (help-mode
+ ((goto mlinks-elisp-goto)
+ (hili mlinks-elisp-hili)
+ (hion t)
+ )
+ )
+ (Info-mode
+ ((goto mlinks-elisp-goto)
+ (hili mlinks-elisp-hili)
+ (hion t)
+ )
+ )
+ (Custom-mode
+ ((goto mlinks-elisp-custom-goto)
+ (hili mlinks-elisp-hili)
+ (hion t)
+ (fontify mlinks-custom-fontify)
+ )
+ )
+ (text-mode
+ ((goto mlinks-goto-plain-url)
+ (hion t)
+ (fontify mlinks-plain-urls-fontify)
+ )
+ )
+ (nxhtml-mode
+ ((hion t)
+ (fontify mlinks-html-fontify)
+ (goto mlinks-html-style-goto)
+ )
+ )
+ (nxml-mode
+ ((hion t)
+ (fontify mlinks-html-fontify)
+ (goto mlinks-html-style-goto)
+ )
+ )
+ (sgml-mode
+ ((hion t)
+ (fontify mlinks-html-fontify)
+ (goto mlinks-html-style-goto)
+ )
+ )
+ (html-mode
+ ((hion t)
+ (fontify mlinks-html-fontify)
+ (goto mlinks-html-style-goto)
+ )
+ )
+ )
+ "Defines MLinks hyperlinks for major modes.
+"
+ ;; Each element in the list is a list with two elements
+
+ ;; \(MAJOR-MODE SETTINGS)
+
+ ;; where MAJOR-MODE is the major mode for which the settings SETTINGS should be used.
+ ;; SETTINGS is an association list which can have the following element types
+
+ ;; \(hili HILIGHT-FUN) ;; Mandatory
+ ;; \(goto GOTO-FUN) ;; Mandatory
+ ;; \(hion HION-BOOL) ;; Optional
+ ;; \(next NEXT-FUN) ;; Optional
+ ;; \(prev PREV-FUN) ;; Optional
+
+ ;; Where
+ ;; - HILIGHT-FUN is the function to hilight a link when point is
+ ;; inside the link. This is done when Emacs is idle.
+ ;; - GOTO-FUN is the function to follow the link at point.
+ ;; - HION-BOOL is t or nil depending on if hilighting should be on
+ ;; by default.
+ ;; - NEXT-FUN is the function to go to the next link.
+ ;; - PREV-FUN is the function to go to the previous link."
+ ;; ;;:type '(repeat (alist :key-type symbol :value-type (alist :key-type symbol :value symbol)))
+ :type '(alist :key-type major-mode-function
+ :value-type (list
+ (set
+ (const :tag "Enable MLinks in this major mode" hion)
+ (const :tag "Mark All Links" mark)
+ (list :tag "Enable" (const :tag "Hilighting" hili) function)
+ (list :tag "Enable" (const :tag "Follow Link" goto) function)
+ (list :tag "Enable" (const :tag "Goto Next Link" next) function)
+ (list :tag "Enable" (const :tag "Goto Previous Link" prev) function)
+ )))
+ :group 'mlinks)
+
+
+(defun mlinks-get-mode-value (which)
+ (let* ((major major-mode)
+ (mode-rec (assoc major mlinks-mode-functions)))
+ (catch 'mode-rec
+ (while (and major
+ (not mode-rec))
+ (setq major (get major 'derived-mode-parent))
+ (setq mode-rec (assoc major mlinks-mode-functions))
+ (when mode-rec (throw 'mode-rec nil))))
+ (when mode-rec
+ (let* ((mode (car mode-rec))
+ (funs-alist (cadr mode-rec))
+ (funs (assoc which funs-alist)))
+ (cdr funs)))))
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Minor modes
+
+;; (appmenu-dump-keymap mlinks-mode-map)
+(defvar mlinks-mode-map
+ (let ((m (make-sparse-keymap "mlinks")))
+ (define-key m [(control ?c) ?\r ?\r] 'mlinks-goto)
+ (define-key m [(control ?c) ?\r ?w] 'mlinks-goto-other-window)
+ (define-key m [(control ?c) ?\r ?f] 'mlinks-goto-other-frame)
+ (define-key m [(control ?c) ?\r ?n] 'mlinks-next-saved-position)
+ (define-key m [(control ?c) ?\r ?p] 'mlinks-prev-saved-position)
+ (define-key m [(control ?c) ?\r S-tab] 'mlinks-backward-link)
+ (define-key m [(control ?c) ?\r tab] 'mlinks-forward-link)
+ (define-key m [(control ?c) ?\r ?h] 'mlinks-toggle-hilight)
+ (define-key m [(control ?c) ?\r ?c] 'mlinks-copy-link-text)
+ m))
+
+;;;###autoload
+(define-minor-mode mlinks-mode
+ "Recognizes certain parts of a buffer as hyperlinks.
+The hyperlinks are created in different ways for different major
+modes with the help of the functions in the list
+`mlinks-mode-functions'.
+
+The hyperlinks can be hilighted when point is over them. Use
+`mlinks-toggle-hilight' to toggle this feature for the current
+buffer.
+
+All keybindings in this mode are by default done under the prefi§x
+key
+
+ C-c RET
+
+which is supposed to be a kind of mnemonic for link (alluding to
+the RET key commonly used in web browser to follow a link).
+\(Unfortunately this breaks the rules in info node `Key Binding
+Conventions'.) Below are the key bindings defined by this mode:
+
+\\{mlinks-mode-map}
+
+For some major modes `mlinks-backward-link' and
+`mlinks-forward-link' will take you to the previous/next link.
+By default the link moved to will be active, see
+`mlinks-active-links'.
+
+"
+ nil
+ " L"
+ nil
+ :keymap mlinks-mode-map
+ :group 'mlinks
+ (if mlinks-mode
+ (progn
+ (mlinks-add-appmenu)
+ (mlinks-start-point-hilighter)
+ (mlinks-add-font-lock))
+ (mlinks-stop-point-hilighter)
+ (when mlinks-point-hilighter-overlay
+ (when (overlayp mlinks-point-hilighter-overlay)
+ (delete-overlay mlinks-point-hilighter-overlay))
+ (setq mlinks-point-hilighter-overlay nil))
+ (mlinks-remove-font-lock)))
+(put 'mlinks-mode 'permanent-local t)
+
+(defun mlinks-turn-on-in-buffer ()
+ (let ((hion (unless (and (boundp 'mumamo-set-major-running)
+ mumamo-set-major-running)
+ (mlinks-get-mode-value 'hion))))
+ (when hion (mlinks-mode 1))))
+
+;;;###autoload
+(define-globalized-minor-mode mlinks-global-mode mlinks-mode
+ mlinks-turn-on-in-buffer
+ "Turn on `mlink-mode' in all buffer where it is specified.
+This is specified in `mlinks-mode-functions'."
+ :group 'mlinks)
+
+;; The problem with global minor modes:
+(when (and mlinks-global-mode
+ (not (boundp 'define-global-minor-mode-bug)))
+ (mlinks-global-mode 1))
+
+;;(define-toggle mlinks-active-links t
+(define-minor-mode mlinks-active-links
+ "Use quick movement keys on active links if non-nil.
+When moving to an mlink with `mlinks-forward-link' or
+`mlinks-backward-link' the link moved to will be in an active
+state. This is marked with a new color \(the face `isearch').
+When the new color is shown the following keys are active
+
+\\{mlinks-hyperactive-point-hilighter-keymap}
+Any command cancels this state."
+ :global t
+ :init-value t
+ :group 'mlinks)
+
+
+
+(defun mlinks-link-text-prop-range (pos)
+ (let* ((link-here (get-text-property pos 'mlinks-link))
+ (beg (when link-here (previous-single-char-property-change (+ pos 1) 'mlinks-link)))
+ (end (when link-here (next-single-char-property-change (- pos 0) 'mlinks-link))))
+ (when (and beg end)
+ (cons beg end))))
+
+(defun mlinks-link-range (pos)
+ (or (mlinks-link-text-prop-range pos)
+ (let ((funs-- (mlinks-get-mode-value 'hili)))
+ (when funs--
+ (save-match-data
+ (run-hook-with-args-until-success 'funs--))))))
+
+(defun mlinks-link-at-point ()
+ "Get link at point."
+ (mlinks-point-hilighter-1)
+ (when (and mlinks-point-hilighter-overlay
+ (overlay-buffer mlinks-point-hilighter-overlay))
+ (let* ((ovl mlinks-point-hilighter-overlay)
+ (beg (overlay-start ovl))
+ (end (overlay-end ovl)))
+ (buffer-substring-no-properties beg end))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; At point highligher
+
+(defvar mlinks-point-hilighter-timer nil)
+
+(defun mlinks-stop-point-hilighter ()
+ (when (timerp mlinks-point-hilighter-timer)
+ (cancel-timer mlinks-point-hilighter-timer)
+ (setq mlinks-point-hilighter-timer nil)))
+
+(defun mlinks-start-point-hilighter ()
+ (mlinks-stop-point-hilighter)
+ (setq mlinks-point-hilighter-timer
+ (run-with-idle-timer 0.1 t 'mlinks-point-hilighter)))
+
+(defvar mlinks-link-overlay-priority 100)
+
+(defun mlinks-make-point-hilighter-overlay (bounds)
+ (unless mlinks-point-hilighter-overlay
+ (setq mlinks-point-hilighter-overlay
+ (make-overlay (car bounds) (cdr bounds)))
+ (overlay-put mlinks-point-hilighter-overlay 'priority mlinks-link-overlay-priority)
+ (overlay-put mlinks-point-hilighter-overlay 'mouse-face 'highlight)
+ (mlinks-set-normal-point-hilight)
+ ))
+
+(defun mlinks-point-hilighter ()
+ "Mark link at point if any.
+This moves the hilight point overlay to point or deletes it."
+ ;; This runs in a timer, protect it.
+ (condition-case err
+ (let ((inhibit-point-motion-hooks t))
+ (mlinks-point-hilighter-1))
+ (error "mlinks-point-hilighter error: %s" (error-message-string err))))
+
+(defun mlinks-point-hilighter-1 ()
+ (when mlinks-mode
+ (let ((bounds-- (mlinks-link-range (point))))
+ (if bounds--
+ (if mlinks-point-hilighter-overlay
+ (move-overlay mlinks-point-hilighter-overlay (car bounds--) (cdr bounds--))
+ (mlinks-make-point-hilighter-overlay bounds--))
+ (when mlinks-point-hilighter-overlay
+ (delete-overlay mlinks-point-hilighter-overlay))))))
+
+(defvar mlinks-hyperactive-point-hilighter-keymap
+ (let ((m (make-sparse-keymap "mlinks")))
+ (define-key m [S-tab] 'mlinks-backward-link)
+ (define-key m [tab] 'mlinks-forward-link)
+ (define-key m "\t" 'mlinks-forward-link)
+ (define-key m [?\r] 'mlinks-goto)
+ (define-key m [?w] 'mlinks-goto-other-window)
+ (define-key m [?f] 'mlinks-goto-other-frame)
+ (define-key m [mouse-1] 'mlinks-goto)
+ (set-keymap-parent m mlinks-mode-map)
+ m))
+
+(defvar mlinks-point-hilighter-keymap
+ (let ((m (make-sparse-keymap "mlinks")))
+ (define-key m [mouse-1] 'mlinks-goto)
+ (set-keymap-parent m mlinks-mode-map)
+ m))
+
+(defun mlinks-point-hilighter-pre-command ()
+ (condition-case err
+ (unless (let ((map (overlay-get mlinks-point-hilighter-overlay 'keymap)))
+ (where-is-internal this-command
+ (list
+ map)))
+ (mlinks-set-normal-point-hilight)
+ (unless mlinks-point-hilighter-timer
+ (delete-overlay mlinks-point-hilighter-overlay)))
+ (error (message "mlinks-point-hilighter-pre-command: %s" err))))
+(put 'mlinks-point-hilighter-pre-command 'permanent-local t)
+
+(defun mlinks-set-hyperactive-point-hilight ()
+ "Make link hyper active, ie add some special key binding.
+Used after jumping specifically to a link. The idea is that the
+user may want to easily jump between links in this state."
+ (add-hook 'pre-command-hook 'mlinks-point-hilighter-pre-command nil t)
+ (mlinks-point-hilighter)
+ (overlay-put mlinks-point-hilighter-overlay 'face mlinks-hyperactive-link-face)
+ (overlay-put mlinks-point-hilighter-overlay 'keymap mlinks-hyperactive-point-hilighter-keymap))
+
+(defun mlinks-set-normal-point-hilight ()
+ "Make link normally active as if you happened to be on it."
+ (remove-hook 'pre-command-hook 'mlinks-point-hilighter-pre-command t)
+ (mlinks-point-hilighter)
+ (overlay-put mlinks-point-hilighter-overlay 'face mlinks-link-face)
+ (overlay-put mlinks-point-hilighter-overlay 'keymap mlinks-point-hilighter-keymap))
+
+(defun mlinks-set-point-hilight-after-jump-to ()
+ "Set hilight style after jump to link."
+ (if mlinks-active-links
+ (mlinks-set-hyperactive-point-hilight)
+ (mlinks-set-normal-point-hilight)))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Jumping around
+
+(defvar mlinks-places nil)
+(make-variable-buffer-local 'mlinks-placesn)
+(put 'mlinks-places 'permanent-local t)
+
+(defvar mlinks-places-n 0)
+(make-variable-buffer-local 'mlinks-places-n)
+(put 'mlinks-places-n 'permanent-local t)
+
+(defun mlinks-has-links ()
+ (or (mlinks-get-mode-value 'fontify)
+ (when (and (boundp 'mumamo-multi-major-mode) mumamo-multi-major-mode)
+ ;; Fix-me: just assume multi major has it... Need a list of
+ ;; major modes. There is no way to get such a list for the
+ ;; multi major mode (since you can't know what the chunk
+ ;; functions will return. However you can get a list of
+ ;; current chunks major mode.
+ t
+ )))
+
+(defun mlinks-backward-link ()
+ "Go to previous `mlinks-mode' link in buffer."
+ (interactive)
+ (if (not (mlinks-has-links))
+ (message "There is no way to go to previous link for this major mode")
+ (let ((res (mlinks-prev-link)))
+ (if res
+ (progn
+ (goto-char res)
+ (mlinks-set-point-hilight-after-jump-to))
+ (message "No previous link found")))))
+
+(defun mlinks-forward-link ()
+ "Go to next `mlinks-mode' link in buffer."
+ (interactive)
+ (if (not (mlinks-has-links))
+ (message "There is no way to go to next link for this major mode")
+ (let ((res (mlinks-next-link)))
+ (if res
+ (progn
+ (goto-char res)
+ (mlinks-set-point-hilight-after-jump-to))
+ (message "No next link found")))))
+
+
+(defun mlinks-goto ()
+ "Follow `mlinks-mode' link at current point.
+Save the current position so that they can be move to again by
+`mlinks-prev-saved-position' and `mlinks-next-saved-position'.
+
+Return non-nil if link was followed, otherewise nil."
+ (interactive)
+ (mlinks-goto-1 nil))
+
+(defun mlinks-goto-other-window ()
+ "Like `mlinks-goto' but opens in other window.
+Uses `switch-to-buffer-other-window'."
+ (interactive)
+ (mlinks-goto-1 'other-window))
+
+(defun mlinks-goto-other-frame ()
+ "Like `mlinks-goto' but opens in other frame.
+Uses `switch-to-buffer-other-frame'."
+ (interactive)
+ (mlinks-goto-1 'other-frame))
+
+(defun mlinks-goto-1(where)
+ (push-mark)
+ (let* ((funs (mlinks-get-mode-value 'goto))
+ (old (point-marker))
+ (mlinks-temp-buffer-where where)
+ (res (run-hook-with-args-until-success 'funs)))
+ (if (not res)
+ (progn
+ (message "Don't know how to follow this MLink link")
+ nil)
+ (unless (= old (point-marker))
+ (let* ((prev (car mlinks-places)))
+ (when (or (not prev)
+ ;;(not (markerp prev))
+ (not (marker-buffer prev))
+ (/= old prev))
+ (setq mlinks-places (cons old mlinks-places))
+ (setq mlinks-places-n (length mlinks-places))))))))
+
+
+(defun mlinks-prev-saved-position ()
+ "Go to previous position saved by `mlinks-goto'."
+ (interactive)
+ (unless (mlinks-goto-n (1- mlinks-places-n))
+ (message "No previous MLink position")))
+
+(defun mlinks-next-saved-position ()
+ "Go to next position saved by `mlinks-goto'."
+ (interactive)
+ (unless (mlinks-goto-n (1+ mlinks-places-n))
+ (message "No next MLink position")))
+
+(defun mlinks-goto-n (to)
+ (if (not mlinks-places)
+ (message "No saved MLinks positions")
+ (let ((minp 1)
+ (maxp (length mlinks-places)))
+ (if (<= to minp)
+ (progn
+ (setq to minp)
+ (message "Going to first MLinks position"))
+ (if (>= to maxp)
+ (progn
+ (setq to maxp)
+ (message "Going to last MLinks position"))))
+ (setq mlinks-places-n to)
+ (let ((n (- maxp to))
+ (places mlinks-places)
+ place
+ buffer
+ point)
+ (while (> n 0)
+ (setq places (cdr places))
+ (setq n (1- n)))
+ (setq place (car places))
+ (mlinks-switch-to-buffer (marker-buffer place))
+ (goto-char place)))))
+
+(defvar mlinks-temp-buffer-where nil)
+(defun mlinks-switch-to-buffer (buffer)
+ (mlinks-switch-to-buffer-1 buffer mlinks-temp-buffer-where))
+
+(defun mlinks-switch-to-buffer-1(buffer where)
+ (cond
+ ((null where)
+ (switch-to-buffer buffer))
+ ((eq where 'other-window)
+ (switch-to-buffer-other-window buffer))
+ ((eq where 'other-frame)
+ (switch-to-buffer-other-frame buffer))
+ (t
+ (error "Invalid argument, where=%s" where))))
+
+;; FIXME: face, var
+(defun mlinks-custom (var)
+ (customize-option var)
+ )
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; AppMenu support
+
+(defun mlinks-appmenu ()
+ (when mlinks-mode
+ ;; Fix-me: reverse the list
+ (let ((link-val (mlinks-link-at-point))
+ (map (make-sparse-keymap "mlinks"))
+ (num 2))
+ (when (mlinks-get-mode-value 'prev)
+ (define-key map [mlinks-next-link]
+ (list 'menu-item "Next Link" 'mlinks-forward-link)))
+ (when (mlinks-get-mode-value 'next)
+ (define-key map [mlinks-prev-link]
+ (list 'menu-item "Previous Link" 'mlinks-backward-link)))
+ (when link-val
+ (let* ((possible (when (member major-mode '(html-mode nxhtml-mode nxml-mode sqml-mode text-mode))
+ (mlinks-html-possible-href-actions link-val)))
+ (mailto (assoc 'mailto possible))
+ (view-web (assoc 'view-web possible))
+ (view-web-base (assoc 'view-web-base possible))
+ (edit (assoc 'edit possible))
+ (file (nth 1 edit))
+ (anchor (nth 2 edit))
+ (choices)
+ (answer)
+ )
+ (when (> (length map) num)
+ (define-key map [mlinks-href-sep] (list 'menu-item "--")))
+ (setq num (length map))
+ (when view-web
+ (define-key map [mlinks-href-view-web]
+ (list 'menu-item "Browse Link Web Url"
+ `(lambda () (interactive)
+ (browse-url ,link-val)))))
+ (when view-web-base
+ (define-key map [mlinks-href-view-web-based]
+ (list 'menu-item "Browse Link Web Url (base URL found)"
+ `(lambda () (interactive)
+ (browse-url (cdr ,view-web-base))))))
+ (when mailto
+ (define-key map [mlinks-href-mail]
+ (list 'menu-item (concat "&Mail to " (substring link-val 7))
+ `(lambda () (interactive)
+ (mlinks-html-mail-to ,link-val)))))
+ (when edit
+ (when (and (file-exists-p file)
+ (not anchor)
+ (assoc 'upload possible))
+ (let ((abs-file (expand-file-name file)))
+ (define-key map [mlinks-href-upload]
+ (list 'menu-item "Upload Linked File"
+ `(lambda () (interactive)
+ (html-upl-upload-file ,abs-file))))))
+ (when (and (file-exists-p file)
+ (not anchor)
+ (assoc 'edit-gimp possible))
+ (let ((abs-file (expand-file-name file)))
+ (define-key map [mlinks-href-edit-gimp]
+ (list 'menu-item "Edit Linked File with GIMP"
+ `(lambda () (interactive)
+ (gimpedit-edit-file ,abs-file))))))
+ (when (and (file-exists-p file)
+ (assoc 'view-local possible))
+ (let ((url (concat "file:///" (expand-file-name file))))
+ (when anchor
+ (let ((url-anchor (concat url "#" anchor)))
+ (define-key map [mlinks-href-view-file-at]
+ (list 'menu-item (concat "Browse Linked File URL at #" anchor)
+ `(lambda () (interactive)
+ (browse-url ,url-anchor))))))
+ (define-key map [mlinks-href-view-file]
+ (list 'menu-item "&Browse Linked File URL"
+ `(lambda () (interactive)
+ (browse-url ,url))))))
+ (when (> (length map) num)
+ (define-key map [mlinks-href-sep-2] (list 'menu-item "--")))
+ (setq num (length map))
+ (unless (equal file (buffer-file-name))
+ (define-key map [mlinks-href-edit]
+ (list 'menu-item "&Open Linked File"
+ `(lambda () (interactive) (mlinks-goto))))
+ (define-key map [mlinks-href-edit-window]
+ (list 'menu-item "&Open Linked File in Other Window"
+ `(lambda () (interactive) (mlinks-goto-other-window))))
+ (define-key map [mlinks-href-edit-frame]
+ (list 'menu-item "&Open Linked File in New Frame"
+ `(lambda () (interactive) (mlinks-goto-other-frame))))
+ )
+ (when (and (file-exists-p file) anchor)
+ (define-key map [mlinks-href-edit-at]
+ (list 'menu-item (concat "Open Linked File &at #" anchor)
+ `(lambda () (interactive)
+ (mlinks-goto)))))
+ )
+ (when (> (length map) num)
+ (define-key map [mlinks-href-sep-1] (list 'menu-item "--")))
+ (setq num (length map))
+ (when link-val
+ (define-key map [mlinks-href-copy-link]
+ (list 'menu-item "&Copy Link Text"
+ 'mlinks-copy-link-text)))))
+ (when (> (length map) 2)
+ map))))
+
+(defun mlinks-add-appmenu ()
+ "Add entries for MLinks to AppMenu."
+ (when (featurep 'appmenu)
+ (appmenu-add 'mlinks 100 'mlinks-mode "Current MLink" 'mlinks-appmenu)))
+
+(defun mlinks-copy-link-text ()
+ "Copy text of `mlinks-mode' link at point to clipboard."
+ (interactive)
+ (mlinks-point-hilighter)
+ (let ((ovl mlinks-point-hilighter-overlay))
+ (if (and ovl
+ (overlayp ovl)
+ (overlay-buffer ovl)
+ (eq (current-buffer)
+ (overlay-buffer ovl))
+ (<= (overlay-start ovl)
+ (point))
+ (>= (overlay-end ovl)
+ (point)))
+ (let* ((beg (overlay-start ovl))
+ (end (overlay-end ovl))
+ (str (buffer-substring beg end)))
+ (copy-region-as-kill beg end)
+ (message "Copied %d chars to clipboard" (length str)))
+ (message "No link here to copy"))))
+
+
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;; text-mode etc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defvar mlinks-plain-urls-regexp
+ (rx-to-string `(or (submatch (optional "mailto:")
+ (regexp ,(concat
+ ;;"[a-z0-9$%(*-=?[_][^<>\")!;:,{}]*"
+ "[a-z0-9$%(*=?[_-][^<>\")!;:,{}]*"
+ "\@"
+ "\\(?:[a-z0-9\-]+\.\\)+[a-z0-9]\\{2,4\\}")))
+ (submatch (or (regexp "https?://")
+ "www.")
+ (1+ (any ,url-get-url-filename-chars))
+ )
+ )))
+
+(defun mlinks-plain-urls-fontify (bound)
+ (mlinks-fontify bound mlinks-plain-urls-regexp 0))
+
+(defun mlinks-goto-plain-url ()
+ (let* ((range (mlinks-link-range (point)))
+ (link (when range (buffer-substring-no-properties (car range) (cdr range)))))
+ ;;(mlinks-html-href-act-on link)
+ (when (= 0 (string-match mlinks-plain-urls-regexp link))
+ (let ((which (if (match-end 1) 1 2)))
+ (cond
+ ((= 1 which)
+ (mlinks-html-mail-to link)
+ t)
+ ((= 2 which)
+ (browse-url link)
+ t)
+ (t nil))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;; nxhtml-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun mlinks-html-style-goto ()
+ (mlinks-html-style-mode-fun t))
+
+(defvar mlinks-html-link-regexp
+ ;; This value takes care of nxhtml-strval-mode (and is therefore a little bit incorrect ...)
+ ;;"\\(?:^\\|[[:space:]]\\)\\(?:href\\|src\\)[[:space:]]*=[[:space:]]*\"\\([^<«\"]*\\)\""
+ (rx (or "^" space)
+ (or "href" "src")
+ (0+ space)
+ "="
+ (0+ space)
+ (submatch
+ (or
+ (seq "\""
+ (and
+ (0+ (not (any "\""))))
+ "\"")
+ (seq "'"
+ (and
+ (0+ (not (any "\'"))))
+ "'")))))
+
+(defun mlinks-html-style-mode-fun (goto)
+ (let (start
+ end
+ bounds)
+ (save-excursion
+ (forward-char)
+ (when (< 0 (skip-chars-forward "^\"'" (line-end-position)))
+ (forward-char)
+ (save-match-data
+ (when (looking-back
+ mlinks-html-link-regexp
+ (line-beginning-position -1))
+ (let ((which (if (match-beginning 1) 1 2)))
+ (setq start (1+ (match-beginning which)))
+ (setq end (1- (match-end which))))
+ (setq bounds (cons start end))))))
+ (when start
+ (if (not goto)
+ bounds
+ (let ((href-val (buffer-substring-no-properties start end)))
+ (mlinks-html-href-act-on href-val))
+ t))))
+
+(defun mlink-check-file-to-edit (file)
+ (assert (file-name-absolute-p file))
+ (let ((file-dir (file-name-directory file)))
+ (unless (file-directory-p file-dir)
+ (if (file-directory-p (file-name-directory file))
+ (if (yes-or-no-p (format "Directory %s does not exist. Create it? " file-dir))
+ (make-directory file-dir)
+ (setq file nil))
+ (if (yes-or-no-p (format "Directory %s and its parent does not exist. Create them? " file-dir))
+ (make-directory file-dir t)
+ (setq file nil))))
+ file))
+
+(defun mlinks-html-edit-at (file &optional anchor)
+ (let ((abs-file (if (file-name-absolute-p file)
+ file
+ (expand-file-name file))))
+ (if (or (file-directory-p abs-file)
+ (string= abs-file
+ (file-name-as-directory abs-file)))
+ (if (file-directory-p abs-file)
+ (when (y-or-n-p (format "Do you want to edit the directory %s? : " abs-file))
+ (dired abs-file))
+ (message "Can't find directory %s" abs-file))
+ (when (mlink-check-file-to-edit abs-file)
+ (let ((b (find-file-noselect abs-file)))
+ (mlinks-switch-to-buffer b))
+ (when anchor
+ (let ((here (point))
+ (anchor-regexp (concat "\\(?:id\\|name\\)[[:space:]]*=[[:space:]]*\"" anchor "\"")))
+ (goto-char (point-min))
+ (if (search-forward-regexp anchor-regexp nil t)
+ (backward-char 2)
+ (message "Anchor \"%s\" not found" anchor)
+ (goto-char here))))))))
+
+(defun mlinks-html-mail-to (addr)
+ (browse-url addr))
+
+(defun mlinks-html-href-act-on (href-val)
+ (if href-val
+ (let* ((possible (mlinks-html-possible-href-actions href-val))
+ (edit (assoc 'edit possible))
+ (file (nth 1 edit))
+ (anchor (nth 2 edit))
+ )
+ (cond (edit
+ (mlinks-html-edit-at file anchor)
+ t)
+ ((assoc 'mailto possible)
+ (when (y-or-n-p "This is a mail address. Do you want to send a message to this mail address? ")
+ (mlinks-html-mail-to href-val)))
+ ((assoc 'view-web possible)
+ (when (y-or-n-p "Can't edit this URL, it is on the web. View the URL in your web browser? ")
+ (browse-url href-val)))
+ ((assoc 'view-web-base possible)
+ (when (y-or-n-p "Can't edit, based URL is to the web. View resulting URL in your web browser? ")
+ (browse-url (cdr (assoc 'view-web-base possible)))))
+ (t
+ (message "Do not know how to handle this URL"))
+ ))
+ (message "No value for href attribute")))
+
+(defun mlinks-html-possible-href-actions (link)
+ (let ((urlobj (url-generic-parse-url link))
+ (edit nil)
+ (possible nil))
+ (cond ((member (url-type urlobj) '("http" "https"))
+ (add-to-list 'possible (cons 'view-web link)))
+ ((member (url-type urlobj) '("mailto"))
+ (add-to-list 'possible (cons 'mailto link)))
+ ((url-host urlobj)
+ (message "Do not know how to handle this URL"))
+ (t (setq edit t)))
+ (when edit
+ (let ((base-href (mlinks-html-find-base-href)))
+ (when base-href
+ (let ((baseobj (url-generic-parse-url base-href)))
+ (setq edit nil)
+ (cond ((member (url-type baseobj) '("http" "https"))
+ (add-to-list 'possible (cons 'view-web-base (url-expand-file-name link base-href))))
+ ((url-host urlobj)
+ (message "Do not know how to handle this URL"))
+ (t (setq edit t)))))
+ (when edit
+ (let* ((full (split-string (url-filename urlobj) "#"))
+ (file (nth 0 full))
+ (anchor (nth 1 full))
+ )
+ (when (equal file "")
+ (setq file (buffer-file-name)))
+ (when base-href
+ ;; We know at this point it is not a http url
+ (setq file (expand-file-name file base-href)))
+ (let ((ext (downcase (file-name-extension file))))
+ (when (member ext '("htm" "html"))
+ (add-to-list 'possible (cons 'view-local (list file anchor))))
+ (when (and (featurep 'gimpedit)
+ (member ext '("gif" "png" "jpg" "jpeg")))
+ (add-to-list 'possible (cons 'edit-gimp (list file anchor)))))
+ (when (featurep 'html-upl)
+ (add-to-list 'possible (cons 'upload (list file anchor))))
+ (add-to-list 'possible (cons 'edit (list file anchor)))))))
+ possible))
+
+(defun mlinks-html-find-base-href ()
+ "Return base href found in the current file."
+ (let ((base-href))
+ (save-excursion
+ (goto-char (point-min))
+ (while (and (not base-href)
+ (search-forward-regexp "<!--[^!]*-->\\|<base[[:space:]]" nil t))
+ (when (equal " " (char-to-string (char-before)))
+ (backward-char 6)
+ (when (looking-at "<base [^>]*href *= *\"\\(.*?\\)\"")
+ (setq base-href (match-string-no-properties 1))))))
+ base-href))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;; Custom-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun mlinks-elisp-custom-goto ()
+ (mlinks-elisp-mode-fun 'custom))
+
+(defvar mlinks-custom-link-regexp
+ (rx "`"
+ (group
+ (1+ (not (any "'"))))
+ "'"))
+
+(defun mlinks-custom-fontify (bound)
+ (mlinks-fontify bound mlinks-custom-link-regexp 0))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;; emacs-lisp-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun mlinks-elisp-goto ()
+ (mlinks-elisp-mode-fun 'source))
+
+(defun mlinks-elisp-hili ()
+ (mlinks-elisp-mode-fun nil))
+
+(defun mlinks-elisp-mode-fun (goto)
+ (let ((symbol-name (thing-at-point 'symbol)))
+ (when symbol-name
+ (let ((bounds-- (bounds-of-thing-at-point 'symbol))
+ ret--)
+ (if (save-excursion
+ (goto-char (cdr bounds--))
+ (looking-back (concat "(\\(?:require\\|featurep\\)\s+'" symbol-name)
+ (line-beginning-position)))
+ (progn
+ (setq ret-- bounds--)
+ (when goto
+ (mlinks-elisp-mode-require symbol-name)))
+ (when (mlinks-elisp-mode-symbol symbol-name goto)
+ (setq ret-- bounds--)))
+ ret--))))
+
+(defun mlinks-elisp-function (symbol)
+ "Go to an elisp function."
+ (interactive "aElisp function: ")
+ (mlinks-elisp-mode-symbol (symbol-name symbol) 'source))
+
+(defun mlinks-elisp-mode-symbol (symbol-name-- goto--)
+ ;; Fix-me: use uninterned variables (see mail from Miles)
+ ;; Make these names a bit strange because they are boundp at the time of checking:
+ (let ((symbol-- (intern-soft symbol-name--))
+ defs--)
+ (when (and symbol-- (boundp symbol--))
+ (add-to-list 'defs-- 'variable))
+ (when (fboundp symbol--)
+ (add-to-list 'defs-- 'function))
+ (when (facep symbol--)
+ (add-to-list 'defs-- 'face))
+ ;; Avoid some fails hits
+ (when (memq symbol--
+ '(goto t
+ bounds-- funs-- ret--
+ symbol-- defs-- symbol-name-- goto--))
+ (setq defs-- nil))
+ (let (defs-places
+ def)
+ (if (not goto--)
+ (progn
+ defs--)
+ (if (not defs--)
+ (progn
+ (message "Could not find definition of '%s" symbol-name--)
+ nil)
+ (dolist (type (cond
+ ((eq goto-- 'source)
+ '(nil defvar defface))
+ ((eq goto-- 'custom)
+ '(defvar defface))
+ (t
+ (error "Bad goto-- value: %s" goto--))))
+ (condition-case err
+ (add-to-list 'defs-places
+ (cons
+ type
+ (save-excursion
+ (let* ((bp (find-definition-noselect symbol-- type))
+ (b (car bp))
+ (p (cdr bp)))
+ (unless p
+ (with-current-buffer b
+ (save-restriction
+ (widen)
+ (setq bp (find-definition-noselect symbol-- type)))))
+ bp))))
+ (error
+ ;;(lwarn '(mlinks) :error "%s" (error-message-string err))
+ (when t
+ (cond
+ ((eq (car err) 'search-failed))
+ ((and (eq (car err) 'error)
+ (string= (error-message-string err)
+ (format "Don't know where `%s' is defined" symbol--))))
+ (t
+ (message "%s: %s" (car err) (error-message-string err))))))))
+ (if (= 1 (length defs-places))
+ (setq def (car defs-places))
+ (let ((many nil)
+ lnk)
+ (dolist (d defs-places)
+ (if (not lnk)
+ (setq lnk (cdr d))
+ (unless (equal lnk (cdr d))
+ (setq many t))))
+ (if (not many)
+ (setq def (car defs-places))
+ (let* ((alts (mapcar (lambda (elt)
+ (let ((type (car elt))
+ str)
+ (setq str
+ (cond
+ ((not type)
+ "Function")
+ ((eq type 'defvar)
+ "Variable")
+ ((eq type 'defface)
+ "Face")))
+ (cons str elt)))
+ defs-places))
+ (stralts (mapcar (lambda (elt)
+ (car elt))
+ alts))
+ (completion-ignore-case t)
+ (stralt (completing-read "Type: " stralts nil t))
+ (alt (assoc stralt alts)))
+ (setq def (cdr alt))))))
+ (when def
+ (cond
+ ((eq goto-- 'source)
+ ;; Be sure to go to the real sources from CVS:
+ (let* ((buf (car (cdr def)))
+ ;; Avoid going to source
+ ;;(file (find-source-lisp-file (with-current-buffer buf buffer-file-name)) )
+ (file (with-current-buffer buf buffer-file-name))
+ (orig-buf (find-file-noselect file)))
+ (mlinks-switch-to-buffer orig-buf)
+ (let ((p (cdr (cdr def))))
+ ;; Fix-me: Move this test to a more general place.
+ (if (or (< p (point-min))
+ (> p (point-max)))
+ ;; Check for cloned indirect buffers.
+ (progn
+ (setq orig-buf
+ (catch 'view-in-buf
+ (dolist (indirect-buf (buffer-list))
+ ;;(message "base-buffer=%s, orig-buf=%s, eq => %s" (buffer-base-buffer indirect-buf) orig-buf (eq (buffer-base-buffer indirect-buf) orig-buf))
+ (when (eq (buffer-base-buffer indirect-buf) orig-buf)
+ (with-current-buffer indirect-buf
+ ;;(message "indirect-buf=%s" indirect-buf)
+ (unless (or (< p (point-min))
+ (> p (point-max)))
+ ;;(message "switching")
+ ;;(mlinks-switch-to-buffer indirect-buf)
+ (message "mlinks: Switching to indirect buffer because of narrowing")
+ (throw 'view-in-buf indirect-buf)
+ ))
+ ))))
+ (when orig-buf
+ (mlinks-switch-to-buffer orig-buf))
+ ;;(message "cb=%s" (current-buffer))
+ (if (or (< p (point-min))
+ (> p (point-max)))
+ (when (y-or-n-p (format "%s is invisible because of narrowing. Widen? " symbol--))
+ (widen)
+ (goto-char p))
+ (goto-char p)))
+ (goto-char p)))))
+ ((eq goto-- 'custom)
+ (mlinks-custom symbol--))
+ (t
+ (error "Back goto-- value again: %s" goto--)))))))))
+
+(defun mlinks-elisp-mode-require (module)
+ (let ((where mlinks-temp-buffer-where))
+ (cond
+ ((null where)
+ (find-library module))
+ ((eq where 'other-window)
+ (other-window 1)
+ (find-library module))
+ ((eq where 'other-frame)
+ (make-frame-command)
+ (find-library module))
+ (t
+ (error "Invalid argument, where=%s" where)))))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;; Helpers when adopting for modes ;;;;;;;;;;;;;;;;;
+
+;;; Save this, do not delete this comment:
+
+;; (defun mlinks-hit-test ()
+;; "Just a helper function for adding support for new modes."
+;; (let* (
+;; (s0 (if (match-string 0) (match-string 0) ""))
+;; (s1 (if (match-string 1) (match-string 1) ""))
+;; (s2 (if (match-string 2) (match-string 2) ""))
+;; (s3 (if (match-string 3) (match-string 3) ""))
+;; )
+;; (message "match0=%s, match1=%s, match2=%s, match3=%s" s0 s1 s2 s3)))
+
+;; (defun mlinks-handle-reg-fun-list (reg-fun-list)
+;; "Just a helper function."
+;; (let (done
+;; regexp
+;; hitfun
+;; m
+;; p
+;; b
+;; )
+;; (dolist (rh reg-fun-list)
+;; (message "rh=%s" rh);(sit-for 2)
+;; (unless done
+;; (setq regexp (car rh))
+;; (setq hitfun (cadr rh))
+;; (message "regexp=%s, hitfun=%s" regexp hitfun);(sit-for 1)
+;; (when (and (save-match-data
+;; (setq m (re-search-backward regexp (line-beginning-position) t))
+;; (> p (match-beginning 0))))
+;; (setq done t)
+;; (setq b (match-beginning 0))
+;; (setq e (match-end 0))
+;; )
+;; (if (not (and b e
+;; (< b p)
+;; (< p e)))
+;; (message "MLinks Mode did not find any link here")
+;; (goto-char b)
+;; (if (not (looking-at regexp))
+;; (error "Internal error, regexp %s, no match looking-at" regexp)
+;; (let ((last (car mlinks-places))
+;; (m (make-marker)))
+;; (set-marker m (line-beginning-position))
+;; (when (or (not last)
+;; (/= m last))
+;; (setq mlinks-places (cons m mlinks-places))))
+;; (funcall hitfun))
+;; )))))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Font Lock use
+
+(defvar mlinks-link-update-pos-max nil)
+(make-variable-buffer-local 'mlinks-link-update-pos-max)
+(put 'mlinks-link-update-pos-max 'permanent-local t)
+
+(defun mlinks-remove-font-lock ()
+ "Remove info from font-lock."
+ (when (mlinks-want-font-locking)
+ (mlink-font-lock nil)))
+
+(defun mlinks-add-font-lock ()
+ "Add info to font-lock."
+ (when (mlinks-want-font-locking)
+ (mlink-font-lock t)))
+
+(defun mlinks-want-font-locking ()
+ (or (mlinks-get-mode-value 'fontify)
+ (mlinks-get-mode-value 'next-mark)))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Font Lock integration
+
+(defun mlink-font-lock (on)
+ (let* ((add-or-remove (if on 'font-lock-add-keywords 'font-lock-remove-keywords))
+ (fontify-fun (car (mlinks-get-mode-value 'fontify)))
+ (args (list nil `(( ,fontify-fun ( 0 mlinks-font-lock-face t ))))))
+ (when fontify-fun
+ ;; Note: Had a lot of trouble with this which I modelled first
+ ;; after dlink. Using hi-lock as a model made it work with
+ ;; mumamo too.
+ ;;
+ ;; Next arg, HOW, is needed to get it to work with mumamo. This
+ ;; adds it last, like hi-lock.
+ (when on (setq args (append args (list t))))
+ (apply add-or-remove args)
+ (font-lock-mode -1)
+ (font-lock-mode 1))))
+
+(defun mlinks-html-fontify (bound)
+ (mlinks-fontify bound mlinks-html-link-regexp 1))
+
+(defun mlinks-fontify (bound regexp border)
+ (let ((start (point))
+ end-start
+ stop next-stop
+ (more t)
+ old-beg old-end
+ (wn 1)
+ ret)
+ ;; Note: we shouldnot use save-match-data here. Instead
+ ;; set-match-data is called below!
+ (if (not (re-search-forward regexp bound t))
+ (setq end-start bound)
+ (setq ret t)
+ (setq end-start (- (point) 2))
+ (let* ((which (if (match-beginning 1) 1 2))
+ (beg (+ (match-beginning which) border))
+ (end (- (match-end which) border)))
+ (put-text-property beg end 'mlinks-link t)
+ (set-match-data (list (copy-marker end) (copy-marker beg)))))
+ (setq stop start)
+ (setq next-stop -1)
+ (while (and (> 100 (setq wn (1+ wn)))
+ (setq next-stop (next-single-char-property-change stop 'mlinks-link nil end-start))
+ (/= next-stop stop))
+ (setq stop next-stop)
+ (if (get-text-property stop 'mlinks-link)
+ (setq old-beg stop)
+ (when old-beg
+ (remove-list-of-text-properties old-beg stop '(mlinks-link 'mouse-face)))))
+ ret))
+
+(defun mlinks-next-link ()
+ "Find next link, fontify as necessary."
+ (let* ((here (point))
+ (prev-pos (point))
+ (fontified-here (get-text-property (max (point-min) (1- prev-pos)) 'fontified))
+ (fontified-to (next-single-char-property-change prev-pos 'fontified))
+ (pos (next-single-char-property-change prev-pos 'mlinks-link nil
+ (or fontified-to (point-max))))
+ (fontified-all (and fontified-here (not fontified-to)))
+ ready
+ next-fontified-to)
+ (while (not (or ready
+ (and fontified-all
+ (not pos))))
+ (if pos
+ (progn
+ (unless (get-text-property pos 'mlinks-link)
+ ;; Get to next link
+ (setq prev-pos pos)
+ (setq pos (next-single-char-property-change prev-pos 'mlinks-link nil
+ (or fontified-to (point-max)))))
+ (when pos
+ (setq ready (get-text-property pos 'mlinks-link))
+ (setq prev-pos pos)
+ (unless ready (setq pos nil))))
+ (unless (or fontified-all fontified-to)
+ (if (get-text-property prev-pos 'fontified)
+ (setq fontified-all
+ (not (setq fontified-to
+ (next-single-char-property-change prev-pos 'fontified))))
+ (setq fontified-to ( or (previous-single-char-property-change prev-pos 'fontified)
+ 1))))
+ (setq next-fontified-to (min (+ fontified-to 5000)
+ (point-max)))
+ (mumamo-with-buffer-prepared-for-jit-lock
+ (progn
+ (put-text-property fontified-to next-fontified-to 'fontified t)
+ (font-lock-fontify-region fontified-to next-fontified-to)))
+ (setq fontified-to (next-single-char-property-change (1- next-fontified-to)
+ 'fontified))
+ (setq fontified-all (not fontified-to))
+ (setq pos (next-single-char-property-change prev-pos 'mlinks-link nil
+ (or fontified-to (point-max))))))
+ (when ready prev-pos)))
+
+(defun mlinks-prev-link ()
+ "Find previous link, fontify as necessary."
+ (let* ((prev-pos (point))
+ (fontified-from (previous-single-char-property-change prev-pos 'fontified))
+ (fontified-here (get-text-property (max (point-min) (1- prev-pos)) 'fontified))
+ (fontified-all (and fontified-here (not fontified-from)))
+ (pos (when fontified-here
+ (previous-single-char-property-change prev-pos 'mlinks-link nil
+ (or fontified-from 1))))
+ ready
+ next-fontified-from)
+ (while (not (or ready
+ (and fontified-all
+ (not pos))))
+ (assert (numberp prev-pos) t)
+ (if pos
+ (progn
+ (when (and (> (1- pos) (point-min))
+ (get-text-property (1- pos) 'mlinks-link))
+ ;; Get out of current link
+ (setq prev-pos pos)
+ (setq pos (previous-single-char-property-change prev-pos 'mlinks-link nil
+ (or fontified-from 1))))
+ (when pos
+ (setq prev-pos pos)
+ (setq ready (and (get-text-property pos 'fontified)
+ (or (= 1 pos)
+ (not (get-text-property (1- pos) 'mlinks-link)))
+ (get-text-property pos 'mlinks-link)))
+ (unless ready (setq pos nil))))
+ (setq next-fontified-from (max (- fontified-from 5000)
+ (point-min)))
+ (mumamo-with-buffer-prepared-for-jit-lock
+ (progn
+ (put-text-property next-fontified-from fontified-from 'fontified t)
+ (font-lock-fontify-region next-fontified-from fontified-from)))
+ (setq fontified-from (previous-single-char-property-change
+ (1+ next-fontified-from) 'fontified))
+ (setq fontified-all (not fontified-from))
+ (setq pos (previous-single-char-property-change prev-pos 'mlinks-link nil
+ (or fontified-from 1)))))
+ (when ready pos)))
+
+
+;;; This is for the problem reported by some Asian users:
+;;;
+;;; Lisp error: (invalid-read-syntax "] in a list")
+;;;
+;; Local Variables:
+;; coding: utf-8
+;; End:
+
+(provide 'mlinks)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; mlinks.el ends here