diff options
| author | Richard M. Stallman | 1997-07-04 19:59:49 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1997-07-04 19:59:49 +0000 |
| commit | d9cc804bf8f440fa73f49abe7977518554126601 (patch) | |
| tree | f36cddd67c874dfec9f1c4d5cbec67379a9ece82 | |
| parent | 766e15c6b5dd54cc46aec2453eb00b4d0cc9e962 (diff) | |
| download | emacs-d9cc804bf8f440fa73f49abe7977518554126601.tar.gz emacs-d9cc804bf8f440fa73f49abe7977518554126601.zip | |
(thing-at-point): Use `thing-at-point' property, if any.
(bounds-of-thing-at-point): Use `bounds-of-thing-at-point' property.
(thing-at-point-bounds-of-url-at-point): New function.
(thing-at-point-looking-at): New function, adapted from old
browse-url-looking-at.
(thing-at-point-url-at-point): New function, adapted from
browse-url-url-at-point.
(thing-at-point-url-chars): Variable deleted.
(thing-at-point-url-path-regexp, thing-at-point-short-url-regexp,
thing-at-point-url-regexp, thing-at-point-markedup-url-regexp):
New variables.
(url): `beginning-op' property function changed to use
`thing-at-point-bounds-of-url-at-point'. `end-op' property no
longer set -- functionality no longer supported for the more
sophisticated treatment of URLs so `forward-thing' no longer works
in this case.
| -rw-r--r-- | lisp/thingatpt.el | 211 |
1 files changed, 155 insertions, 56 deletions
diff --git a/lisp/thingatpt.el b/lisp/thingatpt.el index 436f2ff3589..4ea50c5dd23 100644 --- a/lisp/thingatpt.el +++ b/lisp/thingatpt.el | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | ;;; thingatpt.el --- Get the `thing' at point | 1 | ;;; thingatpt.el --- Get the `thing' at point |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 1991,92,93,94,95,1996 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 1991,92,93,94,95,96,1997 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Author: Mike Williams <mikew@gopher.dosli.govt.nz> | 5 | ;; Author: Mike Williams <mikew@gopher.dosli.govt.nz> |
| 6 | ;; Keywords: extensions, matching, mouse | 6 | ;; Keywords: extensions, matching, mouse |
| @@ -72,54 +72,56 @@ a symbol as a valid THING. | |||
| 72 | 72 | ||
| 73 | The value is a cons cell (START . END) giving the start and end positions | 73 | The value is a cons cell (START . END) giving the start and end positions |
| 74 | of the textual entity that was found." | 74 | of the textual entity that was found." |
| 75 | (let ((orig (point))) | 75 | (if (get thing 'bounds-of-thing-at-point) |
| 76 | (condition-case nil | 76 | (funcall (get thing 'bounds-of-thing-at-point)) |
| 77 | (save-excursion | 77 | (let ((orig (point))) |
| 78 | ;; Try moving forward, then back. | 78 | (condition-case nil |
| 79 | (let ((end (progn | 79 | (save-excursion |
| 80 | (funcall | 80 | ;; Try moving forward, then back. |
| 81 | (or (get thing 'end-op) | 81 | (let ((end (progn |
| 82 | (function (lambda () (forward-thing thing 1))))) | ||
| 83 | (point))) | ||
| 84 | (beg (progn | ||
| 85 | (funcall | ||
| 86 | (or (get thing 'beginning-op) | ||
| 87 | (function (lambda () (forward-thing thing -1))))) | ||
| 88 | (point)))) | ||
| 89 | (if (not (and beg (> beg orig))) | ||
| 90 | ;; If that brings us all the way back to ORIG, | ||
| 91 | ;; it worked. But END may not be the real end. | ||
| 92 | ;; So find the real end that corresponds to BEG. | ||
| 93 | (let ((real-end | ||
| 94 | (progn | ||
| 95 | (funcall | 82 | (funcall |
| 96 | (or (get thing 'end-op) | 83 | (or (get thing 'end-op) |
| 97 | (function (lambda () (forward-thing thing 1))))) | 84 | (function (lambda () (forward-thing thing 1))))) |
| 85 | (point))) | ||
| 86 | (beg (progn | ||
| 87 | (funcall | ||
| 88 | (or (get thing 'beginning-op) | ||
| 89 | (function (lambda () (forward-thing thing -1))))) | ||
| 98 | (point)))) | 90 | (point)))) |
| 99 | (if (and beg real-end (<= beg orig) (<= orig real-end)) | 91 | (if (not (and beg (> beg orig))) |
| 100 | (cons beg real-end))) | 92 | ;; If that brings us all the way back to ORIG, |
| 101 | (goto-char orig) | 93 | ;; it worked. But END may not be the real end. |
| 102 | ;; Try a second time, moving backward first and then forward, | 94 | ;; So find the real end that corresponds to BEG. |
| 103 | ;; so that we can find a thing that ends at ORIG. | 95 | (let ((real-end |
| 104 | (let ((beg (progn | 96 | (progn |
| 105 | (funcall | ||
| 106 | (or (get thing 'beginning-op) | ||
| 107 | (function (lambda () (forward-thing thing -1))))) | ||
| 108 | (point))) | ||
| 109 | (end (progn | ||
| 110 | (funcall | 97 | (funcall |
| 111 | (or (get thing 'end-op) | 98 | (or (get thing 'end-op) |
| 112 | (function (lambda () (forward-thing thing 1))))) | 99 | (function (lambda () (forward-thing thing 1))))) |
| 113 | (point))) | 100 | (point)))) |
| 114 | (real-beg | 101 | (if (and beg real-end (<= beg orig) (<= orig real-end)) |
| 115 | (progn | 102 | (cons beg real-end))) |
| 116 | (funcall | 103 | (goto-char orig) |
| 117 | (or (get thing 'beginning-op) | 104 | ;; Try a second time, moving backward first and then forward, |
| 118 | (function (lambda () (forward-thing thing -1))))) | 105 | ;; so that we can find a thing that ends at ORIG. |
| 119 | (point)))) | 106 | (let ((beg (progn |
| 120 | (if (and real-beg end (<= real-beg orig) (<= orig end)) | 107 | (funcall |
| 121 | (cons real-beg end)))))) | 108 | (or (get thing 'beginning-op) |
| 122 | (error nil)))) | 109 | (function (lambda () (forward-thing thing -1))))) |
| 110 | (point))) | ||
| 111 | (end (progn | ||
| 112 | (funcall | ||
| 113 | (or (get thing 'end-op) | ||
| 114 | (function (lambda () (forward-thing thing 1))))) | ||
| 115 | (point))) | ||
| 116 | (real-beg | ||
| 117 | (progn | ||
| 118 | (funcall | ||
| 119 | (or (get thing 'beginning-op) | ||
| 120 | (function (lambda () (forward-thing thing -1))))) | ||
| 121 | (point)))) | ||
| 122 | (if (and real-beg end (<= real-beg orig) (<= orig end)) | ||
| 123 | (cons real-beg end)))))) | ||
| 124 | (error nil))))) | ||
| 123 | 125 | ||
| 124 | ;;;###autoload | 126 | ;;;###autoload |
| 125 | (defun thing-at-point (thing) | 127 | (defun thing-at-point (thing) |
| @@ -130,9 +132,11 @@ Possibilities include `symbol', `list', `sexp', `defun', `filename', `url', | |||
| 130 | 132 | ||
| 131 | See the file `thingatpt.el' for documentation on how to define | 133 | See the file `thingatpt.el' for documentation on how to define |
| 132 | a symbol as a valid THING." | 134 | a symbol as a valid THING." |
| 133 | (let ((bounds (bounds-of-thing-at-point thing))) | 135 | (if (get thing 'thing-at-point) |
| 134 | (if bounds | 136 | (funcall (get thing 'thing-at-point)) |
| 135 | (buffer-substring (car bounds) (cdr bounds))))) | 137 | (let ((bounds (bounds-of-thing-at-point thing))) |
| 138 | (if bounds | ||
| 139 | (buffer-substring (car bounds) (cdr bounds)))))) | ||
| 136 | 140 | ||
| 137 | ;; Go to beginning/end | 141 | ;; Go to beginning/end |
| 138 | 142 | ||
| @@ -197,19 +201,114 @@ a symbol as a valid THING." | |||
| 197 | (put 'filename 'beginning-op | 201 | (put 'filename 'beginning-op |
| 198 | '(lambda () (skip-chars-backward thing-at-point-file-name-chars))) | 202 | '(lambda () (skip-chars-backward thing-at-point-file-name-chars))) |
| 199 | 203 | ||
| 200 | (defvar thing-at-point-url-chars "~/A-Za-z0-9---_@$%&=.," | 204 | (defvar thing-at-point-url-path-regexp |
| 201 | "Characters allowable in a URL.") | 205 | "[^]\t\n \"'()<>[^`{}]*[^]\t\n \"'()<>[^`{}.,;]+" |
| 202 | 206 | "A regular expression probably matching the host, path or e-mail part of a URL.") | |
| 203 | (put 'url 'end-op | 207 | |
| 204 | '(lambda () (skip-chars-forward (concat ":" thing-at-point-url-chars)) | 208 | (defvar thing-at-point-short-url-regexp |
| 205 | (skip-chars-backward ".,:"))) | 209 | (concat "[-A-Za-z0-9.]+" thing-at-point-url-path-regexp) |
| 210 | "A regular expression probably matching a URL without an access scheme. | ||
| 211 | Hostname matching is stricter in this case than for | ||
| 212 | ``thing-at-point-url-regexp''.") | ||
| 213 | |||
| 214 | (defvar thing-at-point-url-regexp | ||
| 215 | (concat | ||
| 216 | "\\(https?://\\|ftp://\\|gopher://\\|telnet://\\|wais://\\|file:/\\|s?news:\\|mailto:\\)" | ||
| 217 | thing-at-point-url-path-regexp) | ||
| 218 | "A regular expression probably matching a complete URL.") | ||
| 219 | |||
| 220 | (defvar thing-at-point-markedup-url-regexp | ||
| 221 | "<URL:[^>]+>" | ||
| 222 | "A regular expression matching a URL marked up per RFC1738. | ||
| 223 | This may contain whitespace (including newlines) .") | ||
| 224 | |||
| 225 | (put 'url 'bounds-of-thing-at-point 'thing-at-point-bounds-of-url-at-point) | ||
| 226 | (defun thing-at-point-bounds-of-url-at-point () | ||
| 227 | (let ((url "") short strip) | ||
| 228 | (if (or (setq strip (thing-at-point-looking-at | ||
| 229 | thing-at-point-markedup-url-regexp)) | ||
| 230 | (thing-at-point-looking-at thing-at-point-url-regexp) | ||
| 231 | ;; Access scheme omitted? | ||
| 232 | (setq short (thing-at-point-looking-at | ||
| 233 | thing-at-point-short-url-regexp))) | ||
| 234 | (let ((beginning (match-beginning 0)) | ||
| 235 | (end (match-end 0))) | ||
| 236 | (cond (strip | ||
| 237 | (setq beginning (+ beginning 5)) | ||
| 238 | (setq end (- end 1)))) | ||
| 239 | (cons beginning end))))) | ||
| 240 | |||
| 241 | (put 'url 'thing-at-point 'thing-at-point-url-at-point) | ||
| 242 | (defun thing-at-point-url-at-point () | ||
| 243 | "Return the URL around or before point. | ||
| 244 | Search backwards for the start of a URL ending at or after | ||
| 245 | point. If no URL found, return nil. The access scheme, `http://' | ||
| 246 | will be prepended if absent." | ||
| 247 | (let ((url "") short strip) | ||
| 248 | (if (or (setq strip (thing-at-point-looking-at | ||
| 249 | thing-at-point-markedup-url-regexp)) | ||
| 250 | (thing-at-point-looking-at thing-at-point-url-regexp) | ||
| 251 | ;; Access scheme omitted? | ||
| 252 | (setq short (thing-at-point-looking-at | ||
| 253 | thing-at-point-short-url-regexp))) | ||
| 254 | (progn | ||
| 255 | (setq url (buffer-substring-no-properties (match-beginning 0) | ||
| 256 | (match-end 0))) | ||
| 257 | (and strip (setq url (substring url 5 -1))) ; Drop "<URL:" & ">" | ||
| 258 | ;; strip whitespace | ||
| 259 | (while (string-match "\\s +\\|\n+" url) | ||
| 260 | (setq url (replace-match "" t t url))) | ||
| 261 | (and short (setq url (concat (if (string-match "@" url) | ||
| 262 | "mailto:" "http://") url))) | ||
| 263 | (if (string-equal "" url) | ||
| 264 | nil | ||
| 265 | url))))) | ||
| 266 | |||
| 267 | ;; The normal thingatpt mechanism doesn't work for complex regexps. | ||
| 268 | ;; This should work for almost any regexp wherever we are in the | ||
| 269 | ;; match. To do a perfect job for any arbitrary regexp would mean | ||
| 270 | ;; testing every position before point. Regexp searches won't find | ||
| 271 | ;; matches that straddle the start position so we search forwards once | ||
| 272 | ;; and then back repeatedly and then back up a char at a time. | ||
| 273 | |||
| 274 | (defun thing-at-point-looking-at (regexp) | ||
| 275 | "Return non-nil if point is in or just after a match for REGEXP. | ||
| 276 | Set the match data from the earliest such match ending at or after | ||
| 277 | point." | ||
| 278 | (save-excursion | ||
| 279 | (let ((old-point (point)) match) | ||
| 280 | (and (looking-at regexp) | ||
| 281 | (>= (match-end 0) old-point) | ||
| 282 | (setq match (point))) | ||
| 283 | ;; Search back repeatedly from end of next match. | ||
| 284 | ;; This may fail if next match ends before this match does. | ||
| 285 | (re-search-forward regexp nil 'limit) | ||
| 286 | (while (and (re-search-backward regexp nil t) | ||
| 287 | (or (> (match-beginning 0) old-point) | ||
| 288 | (and (looking-at regexp) ; Extend match-end past search start | ||
| 289 | (>= (match-end 0) old-point) | ||
| 290 | (setq match (point)))))) | ||
| 291 | (if (not match) nil | ||
| 292 | (goto-char match) | ||
| 293 | ;; Back up a char at a time in case search skipped | ||
| 294 | ;; intermediate match straddling search start pos. | ||
| 295 | (while (and (not (bobp)) | ||
| 296 | (progn (backward-char 1) (looking-at regexp)) | ||
| 297 | (>= (match-end 0) old-point) | ||
| 298 | (setq match (point)))) | ||
| 299 | (goto-char match) | ||
| 300 | (looking-at regexp))))) | ||
| 301 | |||
| 302 | ;; Can't do it sensibly? | ||
| 303 | ;(put 'url 'end-op | ||
| 304 | ; '(lambda () (skip-chars-forward (concat ":" thing-at-point-url-chars)) | ||
| 305 | ; (skip-chars-backward ".,:"))) | ||
| 206 | (put 'url 'beginning-op | 306 | (put 'url 'beginning-op |
| 207 | '(lambda () | 307 | '(lambda () |
| 208 | (skip-chars-backward thing-at-point-url-chars) | 308 | (let ((bounds (thing-at-point-bounds-of-url-at-point))) |
| 209 | (or (= (preceding-char) ?:) | 309 | (if bounds |
| 210 | (error "No URL here")) | 310 | (goto-char (car bounds)) |
| 211 | (forward-char -1) | 311 | (error "No URL here"))))) |
| 212 | (skip-chars-backward "a-zA-Z"))) | ||
| 213 | 312 | ||
| 214 | ;; Whitespace | 313 | ;; Whitespace |
| 215 | 314 | ||