thingatpt.el 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. ;;; thingatpt.el --- get the `thing' at point
  2. ;; Copyright (C) 1991-1998, 2000-2012 Free Software Foundation, Inc.
  3. ;; Author: Mike Williams <mikew@gopher.dosli.govt.nz>
  4. ;; Maintainer: FSF
  5. ;; Keywords: extensions, matching, mouse
  6. ;; Created: Thu Mar 28 13:48:23 1991
  7. ;; This file is part of GNU Emacs.
  8. ;; GNU Emacs is free software: you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation, either version 3 of the License, or
  11. ;; (at your option) any later version.
  12. ;; GNU Emacs is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;; This file provides routines for getting the "thing" at the location of
  20. ;; point, whatever that "thing" happens to be. The "thing" is defined by
  21. ;; its beginning and end positions in the buffer.
  22. ;;
  23. ;; The function bounds-of-thing-at-point finds the beginning and end
  24. ;; positions by moving first forward to the end of the "thing", and then
  25. ;; backwards to the beginning. By default, it uses the corresponding
  26. ;; forward-"thing" operator (eg. forward-word, forward-line).
  27. ;;
  28. ;; Special cases are allowed for using properties associated with the named
  29. ;; "thing":
  30. ;;
  31. ;; forward-op Function to call to skip forward over a "thing" (or
  32. ;; with a negative argument, backward).
  33. ;;
  34. ;; beginning-op Function to call to skip to the beginning of a "thing".
  35. ;; end-op Function to call to skip to the end of a "thing".
  36. ;;
  37. ;; Reliance on existing operators means that many `things' can be accessed
  38. ;; without further code: eg.
  39. ;; (thing-at-point 'line)
  40. ;; (thing-at-point 'page)
  41. ;;; Code:
  42. (provide 'thingatpt)
  43. ;; Basic movement
  44. ;;;###autoload
  45. (defun forward-thing (thing &optional n)
  46. "Move forward to the end of the Nth next THING.
  47. THING should be a symbol specifying a type of syntactic entity.
  48. Possibilities include `symbol', `list', `sexp', `defun',
  49. `filename', `url', `email', `word', `sentence', `whitespace',
  50. `line', and `page'."
  51. (let ((forward-op (or (get thing 'forward-op)
  52. (intern-soft (format "forward-%s" thing)))))
  53. (if (functionp forward-op)
  54. (funcall forward-op (or n 1))
  55. (error "Can't determine how to move over a %s" thing))))
  56. ;; General routines
  57. ;;;###autoload
  58. (defun bounds-of-thing-at-point (thing)
  59. "Determine the start and end buffer locations for the THING at point.
  60. THING should be a symbol specifying a type of syntactic entity.
  61. Possibilities include `symbol', `list', `sexp', `defun',
  62. `filename', `url', `email', `word', `sentence', `whitespace',
  63. `line', and `page'.
  64. See the file `thingatpt.el' for documentation on how to define a
  65. valid THING.
  66. Return a cons cell (START . END) giving the start and end
  67. positions of the thing found."
  68. (if (get thing 'bounds-of-thing-at-point)
  69. (funcall (get thing 'bounds-of-thing-at-point))
  70. (let ((orig (point)))
  71. (condition-case nil
  72. (save-excursion
  73. ;; Try moving forward, then back.
  74. (funcall ;; First move to end.
  75. (or (get thing 'end-op)
  76. (lambda () (forward-thing thing 1))))
  77. (funcall ;; Then move to beg.
  78. (or (get thing 'beginning-op)
  79. (lambda () (forward-thing thing -1))))
  80. (let ((beg (point)))
  81. (if (<= beg orig)
  82. ;; If that brings us all the way back to ORIG,
  83. ;; it worked. But END may not be the real end.
  84. ;; So find the real end that corresponds to BEG.
  85. ;; FIXME: in which cases can `real-end' differ from `end'?
  86. (let ((real-end
  87. (progn
  88. (funcall
  89. (or (get thing 'end-op)
  90. (lambda () (forward-thing thing 1))))
  91. (point))))
  92. (when (and (<= orig real-end) (< beg real-end))
  93. (cons beg real-end)))
  94. (goto-char orig)
  95. ;; Try a second time, moving backward first and then forward,
  96. ;; so that we can find a thing that ends at ORIG.
  97. (funcall ;; First, move to beg.
  98. (or (get thing 'beginning-op)
  99. (lambda () (forward-thing thing -1))))
  100. (funcall ;; Then move to end.
  101. (or (get thing 'end-op)
  102. (lambda () (forward-thing thing 1))))
  103. (let ((end (point))
  104. (real-beg
  105. (progn
  106. (funcall
  107. (or (get thing 'beginning-op)
  108. (lambda () (forward-thing thing -1))))
  109. (point))))
  110. (if (and (<= real-beg orig) (<= orig end) (< real-beg end))
  111. (cons real-beg end))))))
  112. (error nil)))))
  113. ;;;###autoload
  114. (defun thing-at-point (thing)
  115. "Return the THING at point.
  116. THING should be a symbol specifying a type of syntactic entity.
  117. Possibilities include `symbol', `list', `sexp', `defun',
  118. `filename', `url', `email', `word', `sentence', `whitespace',
  119. `line', and `page'.
  120. See the file `thingatpt.el' for documentation on how to define
  121. a symbol as a valid THING."
  122. (if (get thing 'thing-at-point)
  123. (funcall (get thing 'thing-at-point))
  124. (let ((bounds (bounds-of-thing-at-point thing)))
  125. (if bounds
  126. (buffer-substring (car bounds) (cdr bounds))))))
  127. ;; Go to beginning/end
  128. (defun beginning-of-thing (thing)
  129. "Move point to the beginning of THING.
  130. The bounds of THING are determined by `bounds-of-thing-at-point'."
  131. (let ((bounds (bounds-of-thing-at-point thing)))
  132. (or bounds (error "No %s here" thing))
  133. (goto-char (car bounds))))
  134. (defun end-of-thing (thing)
  135. "Move point to the end of THING.
  136. The bounds of THING are determined by `bounds-of-thing-at-point'."
  137. (let ((bounds (bounds-of-thing-at-point thing)))
  138. (or bounds (error "No %s here" thing))
  139. (goto-char (cdr bounds))))
  140. ;; Special cases
  141. ;; Lines
  142. ;; bolp will be false when you click on the last line in the buffer
  143. ;; and it has no final newline.
  144. (put 'line 'beginning-op
  145. (lambda () (if (bolp) (forward-line -1) (beginning-of-line))))
  146. ;; Sexps
  147. (defun in-string-p ()
  148. "Return non-nil if point is in a string.
  149. \[This is an internal function.]"
  150. (let ((orig (point)))
  151. (save-excursion
  152. (beginning-of-defun)
  153. (nth 3 (parse-partial-sexp (point) orig)))))
  154. (defun end-of-sexp ()
  155. "Move point to the end of the current sexp.
  156. \[This is an internal function.]"
  157. (let ((char-syntax (char-syntax (char-after))))
  158. (if (or (eq char-syntax ?\))
  159. (and (eq char-syntax ?\") (in-string-p)))
  160. (forward-char 1)
  161. (forward-sexp 1))))
  162. (put 'sexp 'end-op 'end-of-sexp)
  163. (defun beginning-of-sexp ()
  164. "Move point to the beginning of the current sexp.
  165. \[This is an internal function.]"
  166. (let ((char-syntax (char-syntax (char-before))))
  167. (if (or (eq char-syntax ?\()
  168. (and (eq char-syntax ?\") (in-string-p)))
  169. (forward-char -1)
  170. (forward-sexp -1))))
  171. (put 'sexp 'beginning-op 'beginning-of-sexp)
  172. ;; Lists
  173. (put 'list 'bounds-of-thing-at-point 'thing-at-point-bounds-of-list-at-point)
  174. (defun thing-at-point-bounds-of-list-at-point ()
  175. "Return the bounds of the list at point.
  176. \[Internal function used by `bounds-of-thing-at-point'.]"
  177. (save-excursion
  178. (let ((opoint (point))
  179. (beg (condition-case nil
  180. (progn (up-list -1)
  181. (point))
  182. (error nil))))
  183. (condition-case nil
  184. (if beg
  185. (progn (forward-sexp)
  186. (cons beg (point)))
  187. ;; Are we are at the beginning of a top-level sexp?
  188. (forward-sexp)
  189. (let ((end (point)))
  190. (backward-sexp)
  191. (if (>= opoint (point))
  192. (cons opoint end))))
  193. (error nil)))))
  194. ;; Defuns
  195. (put 'defun 'beginning-op 'beginning-of-defun)
  196. (put 'defun 'end-op 'end-of-defun)
  197. (put 'defun 'forward-op 'end-of-defun)
  198. ;; Filenames and URLs www.com/foo%32bar
  199. (defvar thing-at-point-file-name-chars "-~/[:alnum:]_.${}#%,:"
  200. "Characters allowable in filenames.")
  201. (put 'filename 'end-op
  202. (lambda ()
  203. (re-search-forward (concat "\\=[" thing-at-point-file-name-chars "]*")
  204. nil t)))
  205. (put 'filename 'beginning-op
  206. (lambda ()
  207. (if (re-search-backward (concat "[^" thing-at-point-file-name-chars "]")
  208. nil t)
  209. (forward-char)
  210. (goto-char (point-min)))))
  211. (defvar thing-at-point-url-path-regexp
  212. "[^]\t\n \"'<>[^`{}]*[^]\t\n \"'<>[^`{}.,;]+"
  213. "A regular expression probably matching the host and filename or e-mail part of a URL.")
  214. (defvar thing-at-point-short-url-regexp
  215. (concat "[-A-Za-z0-9]+\\.[-A-Za-z0-9.]+" thing-at-point-url-path-regexp)
  216. "A regular expression probably matching a URL without an access scheme.
  217. Hostname matching is stricter in this case than for
  218. ``thing-at-point-url-regexp''.")
  219. (defvar thing-at-point-uri-schemes
  220. ;; Officials from http://www.iana.org/assignments/uri-schemes.html
  221. '("ftp://" "http://" "gopher://" "mailto:" "news:" "nntp:"
  222. "telnet://" "wais://" "file:/" "prospero:" "z39.50s:" "z39.50r:"
  223. "cid:" "mid:" "vemmi:" "service:" "imap:" "nfs:" "acap:" "rtsp:"
  224. "tip:" "pop:" "data:" "dav:" "opaquelocktoken:" "sip:" "tel:" "fax:"
  225. "modem:" "ldap:" "https://" "soap.beep:" "soap.beeps:" "urn:" "go:"
  226. "afs:" "tn3270:" "mailserver:"
  227. "crid:" "dict:" "dns:" "dtn:" "h323:" "im:" "info:" "ipp:"
  228. "iris.beep:" "mtqp:" "mupdate:" "pres:" "sips:" "snmp:" "tag:"
  229. "tftp:" "xmlrpc.beep:" "xmlrpc.beeps:" "xmpp:"
  230. ;; Compatibility
  231. "snews:" "irc:" "mms://" "mmsh://")
  232. "Uniform Resource Identifier (URI) Schemes.")
  233. (defvar thing-at-point-url-regexp
  234. (concat "\\<\\(" (mapconcat 'identity thing-at-point-uri-schemes "\\|") "\\)"
  235. thing-at-point-url-path-regexp)
  236. "A regular expression probably matching a complete URL.")
  237. (defvar thing-at-point-markedup-url-regexp
  238. "<URL:[^>]+>"
  239. "A regular expression matching a URL marked up per RFC1738.
  240. This may contain whitespace (including newlines) .")
  241. (put 'url 'bounds-of-thing-at-point 'thing-at-point-bounds-of-url-at-point)
  242. (defun thing-at-point-bounds-of-url-at-point ()
  243. (let ((strip (thing-at-point-looking-at
  244. thing-at-point-markedup-url-regexp))) ;; (url "") short
  245. (if (or strip
  246. (thing-at-point-looking-at thing-at-point-url-regexp)
  247. ;; Access scheme omitted?
  248. ;; (setq short (thing-at-point-looking-at
  249. ;; thing-at-point-short-url-regexp))
  250. )
  251. (let ((beginning (match-beginning 0))
  252. (end (match-end 0)))
  253. (when strip
  254. (setq beginning (+ beginning 5))
  255. (setq end (- end 1)))
  256. (cons beginning end)))))
  257. (put 'url 'thing-at-point 'thing-at-point-url-at-point)
  258. (defun thing-at-point-url-at-point ()
  259. "Return the URL around or before point.
  260. Search backwards for the start of a URL ending at or after point. If
  261. no URL found, return nil. The access scheme will be prepended if
  262. absent: \"mailto:\" if the string contains \"@\", \"ftp://\" if it
  263. starts with \"ftp\" and not \"ftp:/\", or \"http://\" by default."
  264. (let ((url "") short strip)
  265. (if (or (setq strip (thing-at-point-looking-at
  266. thing-at-point-markedup-url-regexp))
  267. (thing-at-point-looking-at thing-at-point-url-regexp)
  268. ;; Access scheme omitted?
  269. (setq short (thing-at-point-looking-at
  270. thing-at-point-short-url-regexp)))
  271. (progn
  272. (setq url (buffer-substring-no-properties (match-beginning 0)
  273. (match-end 0)))
  274. (and strip (setq url (substring url 5 -1))) ; Drop "<URL:" & ">"
  275. ;; strip whitespace
  276. (while (string-match "[ \t\n\r]+" url)
  277. (setq url (replace-match "" t t url)))
  278. (and short (setq url (concat (cond ((string-match "^[a-zA-Z]+:" url)
  279. ;; already has a URL scheme.
  280. "")
  281. ((string-match "@" url)
  282. "mailto:")
  283. ;; e.g. ftp.swiss... or ftp-swiss...
  284. ((string-match "^ftp" url)
  285. "ftp://")
  286. (t "http://"))
  287. url)))
  288. (if (string-equal "" url)
  289. nil
  290. url)))))
  291. ;; The normal thingatpt mechanism doesn't work for complex regexps.
  292. ;; This should work for almost any regexp wherever we are in the
  293. ;; match. To do a perfect job for any arbitrary regexp would mean
  294. ;; testing every position before point. Regexp searches won't find
  295. ;; matches that straddle the start position so we search forwards once
  296. ;; and then back repeatedly and then back up a char at a time.
  297. (defun thing-at-point-looking-at (regexp)
  298. "Return non-nil if point is in or just after a match for REGEXP.
  299. Set the match data from the earliest such match ending at or after
  300. point."
  301. (save-excursion
  302. (let ((old-point (point)) match)
  303. (and (looking-at regexp)
  304. (>= (match-end 0) old-point)
  305. (setq match (point)))
  306. ;; Search back repeatedly from end of next match.
  307. ;; This may fail if next match ends before this match does.
  308. (re-search-forward regexp nil 'limit)
  309. (while (and (re-search-backward regexp nil t)
  310. (or (> (match-beginning 0) old-point)
  311. (and (looking-at regexp) ; Extend match-end past search start
  312. (>= (match-end 0) old-point)
  313. (setq match (point))))))
  314. (if (not match) nil
  315. (goto-char match)
  316. ;; Back up a char at a time in case search skipped
  317. ;; intermediate match straddling search start pos.
  318. (while (and (not (bobp))
  319. (progn (backward-char 1) (looking-at regexp))
  320. (>= (match-end 0) old-point)
  321. (setq match (point))))
  322. (goto-char match)
  323. (looking-at regexp)))))
  324. (put 'url 'end-op
  325. (lambda ()
  326. (let ((bounds (thing-at-point-bounds-of-url-at-point)))
  327. (if bounds
  328. (goto-char (cdr bounds))
  329. (error "No URL here")))))
  330. (put 'url 'beginning-op
  331. (lambda ()
  332. (let ((bounds (thing-at-point-bounds-of-url-at-point)))
  333. (if bounds
  334. (goto-char (car bounds))
  335. (error "No URL here")))))
  336. ;; Email addresses
  337. (defvar thing-at-point-email-regexp
  338. "<?[-+_.~a-zA-Z][-+_.~:a-zA-Z0-9]*@[-.a-zA-Z0-9]+>?"
  339. "A regular expression probably matching an email address.
  340. This does not match the real name portion, only the address, optionally
  341. with angle brackets.")
  342. ;; Haven't set 'forward-op on 'email nor defined 'forward-email' because
  343. ;; not sure they're actually needed, and URL seems to skip them too.
  344. ;; Note that (end-of-thing 'email) and (beginning-of-thing 'email)
  345. ;; work automagically, though.
  346. (put 'email 'bounds-of-thing-at-point
  347. (lambda ()
  348. (let ((thing (thing-at-point-looking-at thing-at-point-email-regexp)))
  349. (if thing
  350. (let ((beginning (match-beginning 0))
  351. (end (match-end 0)))
  352. (cons beginning end))))))
  353. (put 'email 'thing-at-point
  354. (lambda ()
  355. (let ((boundary-pair (bounds-of-thing-at-point 'email)))
  356. (if boundary-pair
  357. (buffer-substring-no-properties
  358. (car boundary-pair) (cdr boundary-pair))))))
  359. ;; Whitespace
  360. (defun forward-whitespace (arg)
  361. "Move point to the end of the next sequence of whitespace chars.
  362. Each such sequence may be a single newline, or a sequence of
  363. consecutive space and/or tab characters.
  364. With prefix argument ARG, do it ARG times if positive, or move
  365. backwards ARG times if negative."
  366. (interactive "p")
  367. (if (natnump arg)
  368. (re-search-forward "[ \t]+\\|\n" nil 'move arg)
  369. (while (< arg 0)
  370. (if (re-search-backward "[ \t]+\\|\n" nil 'move)
  371. (or (eq (char-after (match-beginning 0)) ?\n)
  372. (skip-chars-backward " \t")))
  373. (setq arg (1+ arg)))))
  374. ;; Buffer
  375. (put 'buffer 'end-op (lambda () (goto-char (point-max))))
  376. (put 'buffer 'beginning-op (lambda () (goto-char (point-min))))
  377. ;; Symbols
  378. (defun forward-symbol (arg)
  379. "Move point to the next position that is the end of a symbol.
  380. A symbol is any sequence of characters that are in either the
  381. word constituent or symbol constituent syntax class.
  382. With prefix argument ARG, do it ARG times if positive, or move
  383. backwards ARG times if negative."
  384. (interactive "p")
  385. (if (natnump arg)
  386. (re-search-forward "\\(\\sw\\|\\s_\\)+" nil 'move arg)
  387. (while (< arg 0)
  388. (if (re-search-backward "\\(\\sw\\|\\s_\\)+" nil 'move)
  389. (skip-syntax-backward "w_"))
  390. (setq arg (1+ arg)))))
  391. ;; Syntax blocks
  392. (defun forward-same-syntax (&optional arg)
  393. "Move point past all characters with the same syntax class.
  394. With prefix argument ARG, do it ARG times if positive, or move
  395. backwards ARG times if negative."
  396. (interactive "p")
  397. (while (< arg 0)
  398. (skip-syntax-backward
  399. (char-to-string (char-syntax (char-before))))
  400. (setq arg (1+ arg)))
  401. (while (> arg 0)
  402. (skip-syntax-forward (char-to-string (char-syntax (char-after))))
  403. (setq arg (1- arg))))
  404. ;; Aliases
  405. (defun word-at-point ()
  406. "Return the word at point. See `thing-at-point'."
  407. (thing-at-point 'word))
  408. (defun sentence-at-point ()
  409. "Return the sentence at point. See `thing-at-point'."
  410. (thing-at-point 'sentence))
  411. (defun read-from-whole-string (str)
  412. "Read a Lisp expression from STR.
  413. Signal an error if the entire string was not used."
  414. (let* ((read-data (read-from-string str))
  415. (more-left
  416. (condition-case nil
  417. ;; The call to `ignore' suppresses a compiler warning.
  418. (progn (ignore (read-from-string (substring str (cdr read-data))))
  419. t)
  420. (end-of-file nil))))
  421. (if more-left
  422. (error "Can't read whole string")
  423. (car read-data))))
  424. (defun form-at-point (&optional thing pred)
  425. (let ((sexp (condition-case nil
  426. (read-from-whole-string (thing-at-point (or thing 'sexp)))
  427. (error nil))))
  428. (if (or (not pred) (funcall pred sexp)) sexp)))
  429. ;;;###autoload
  430. (defun sexp-at-point ()
  431. "Return the sexp at point, or nil if none is found."
  432. (form-at-point 'sexp))
  433. ;;;###autoload
  434. (defun symbol-at-point ()
  435. "Return the symbol at point, or nil if none is found."
  436. (let ((thing (thing-at-point 'symbol)))
  437. (if thing (intern thing))))
  438. ;;;###autoload
  439. (defun number-at-point ()
  440. "Return the number at point, or nil if none is found."
  441. (form-at-point 'sexp 'numberp))
  442. ;;;###autoload
  443. (defun list-at-point ()
  444. "Return the Lisp list at point, or nil if none is found."
  445. (form-at-point 'list 'listp))
  446. ;;; thingatpt.el ends here