;;; ffap-href.el --- find href URL/link anywhere in the tag ;; Copyright 2007, 2009, 2010, 2011, 2014, 2015 Kevin Ryde ;; Author: Kevin Ryde ;; Version: 5 ;; Keywords: files, ffap, html ;; URL: http://user42.tuxfamily.org/ffap-href/index.html ;; EmacsWiki: FindFileAtPoint ;; ffap-href.el 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 3, or (at your option) any later ;; version. ;; ;; ffap-href.el is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General ;; Public License for more details. ;; ;; You can get a copy of the GNU General Public License online at ;; . ;;; Commentary: ;; This spot of code lets M-x ffap recognise a html link, like ;; ;; ;; ;; with point anywhere in the tag, not just on filename part (which ffap ;; handles already). ;;; Emacsen: ;; Designed for Emacs 21 up, works in XEmacs 21 and Emacs 20. ;;; Install: ;; Put ffap-href.el in one of your `load-path' directories and the following ;; in your .emacs ;; ;; (autoload 'ffap-href-enable "ffap-href" nil t) ;; ;; To enable it when ffap loads add the following ;; ;; (eval-after-load "ffap" '(ffap-href-enable)) ;; ;; The function is interactive so `M-x ffap-href-enable' can be used if only ;; wanted sometimes, etc. ;; ;; There's an autoload cookie for `ffap-href-enable' if you know how to use ;; `update-file-autoloads' and friends, then just `eval-after-load' or ;; `M-x'. ;;; History: ;; ;; Version 1 - the first version ;; Version 2 - emacs21 fix ffap-url-at-point when unloaded ;; - xemacs21,emacs20 no error for unavailable unicode chars ;; Version 3 - oops, don't enable by default, only from ffap-href-enable ;; Version 4 - prefer `dotimes' over `position' ;; Version 5 - no autoload of eval-after-load ;;; Code: ;; for `ad-find-advice' macro when running uncompiled ;; (don't unload 'advice before our -unload-function) (require 'advice) (eval-when-compile (unless (fboundp 'dotimes) (require 'cl))) ;; emacs20 `dotimes' macro (defvar sgml-char-names) ;; in sgml-mode.el ;;----------------------------------------------------------------------------- (defun ffap-href-ucs-string (num) "An internal part of ffap-href.el. Return a single-char string for unicode character number NUM. For Emacs 21 up this is simply `(string (decode-char 'ucs num))'. For XEmacs 21 latin-1 characters are are always converted but higher characters require `utf-16-be' charset, for example from mule-ucs. If `utf-16-be' is not available then the return for the higher characters is nil." (cond ((eval-when-compile (fboundp 'decode-char)) ;; emacs21 up (string (decode-char 'ucs num))) ((<= num 255) ;; xemacs21 and emacs20 latin1 (decode-coding-string (string num) 'iso-8859-1)) ((memq 'utf-16-be (coding-system-list)) ;; xemacs21 with mule-ucs, emacs20 maybe with mule-ucs ;; not sure this is right on the surrogates D800 etc, but they ;; shouldn't occur (decode-coding-string (string (ash num -8) (logand num 255)) 'utf-16-be)))) (eval-when-compile (put 'ffap-href-ucs-string 'side-effect-free t)) (defun ffap-href-char-name-to-num (str) "An internal part of ffap-href.el. Return latin-1 character number for HTML entity name STR. STR is like \"Auml\" and is sought in the `sgml-char-names' vector. If it doesn't exist in that vector then return nil." (let (ret) (dotimes (i (length sgml-char-names)) (if (equal str (aref sgml-char-names i)) (setq ret i))) ret)) (eval-when-compile (put 'ffap-href-char-name-to-num 'side-effect-free t)) (defun ffap-href-unentitize (str) "This is an internal part of ffap-href.el. Return STR with any &foo; html entities turned into plain characters. Named entities are sought in `iso-sgml2iso' (Emacs 21 up) or `sgml-char-names'. Numbered entities ÷ are latin-1, or unicode if available (Emacs 21, or XEmacs 21 with mule-ucs)." (if (string-match "&" str) (progn (eval-and-compile ;; quieten the byte compiler (require 'iso-cvt)) (eval-and-compile ;; quieten the byte compiler (require 'sgml-mode)) (with-temp-buffer (insert str) (if (fboundp 'iso-sgml2iso) ;; new in emacs21, not in xemacs21 or emacs20 (iso-sgml2iso (point-min) (point-max))) (goto-char (point-min)) (while (re-search-forward "&\\(#\\([0-9]+\\)\\|[^;]*\\);" nil t) (let* ((num (or (and (match-beginning 2) ;; numbered (string-to-number (match-string 2))) (ffap-href-char-name-to-num (match-string 1))))) ;; named (when num ;; recognised &foo;, or a number &1234; (let* ((beg (match-beginning 0)) (end (match-end 0)) (repl (ffap-href-ucs-string num))) (when repl ;; unicode charset available (delete-region beg end) (insert repl)))))) (buffer-string))) str)) (defun ffap-href-at-point (&optional type) "An internal part of ffap-href.el. Return an " to have that highlighted in the ;; `ffap-string-at-point-region', but that ">" is only optional so ;; something unterminated still works. ;; (and (save-restriction (narrow-to-region (save-excursion (forward-line -5) (point)) (save-excursion (forward-line 5) (point))) (let ((case-fold-search t)) (thing-at-point-looking-at "\ <\\(a\\|img\\)\ \\s-\ \\([^<>]+\\s-\\)?\ \\(href\\|src\\)=\\(\"\\([^\"<>]*\\)\"\\|'\\([^'<>]*\\)'\\)\ \\(?:[^<>]*>\\)?"))) (let* ((beg (or (match-beginning 5) ;; href="" (match-beginning 6))) ;; href='' (end (or (match-end 5) (match-end 6))) (str (buffer-substring beg end))) (and (or (not type) (eq type (let ((case-fold-search t)) (if (string-match "\\`[a-z]+:/" str) 'url 'file)))) (progn (setq ffap-string-at-point-region (list beg end)) (setq ffap-string-at-point (ffap-href-unentitize str))))))) (defadvice ffap-url-at-point (around ffap-href disable) "Recognise \" anywhere in the tag. `ffap' already works with point on the url proper, but not elsewhere like on the \"