Elisp: Problems of thing-at-point

By Xah Lee. Date: . Last updated: .

This page discuss some problems of the function thing-at-point. 〔see Elisp: thing-at-point

thing-at-point Behavior Dependents on Syntax Table

(thing-at-point 'word) result depends on the Elisp: Syntax Table.

this means, result is unpredictable. sometimes it includes - or _ or not. it may also include ' .

this means, you have to be careful which major mode the buffer is in.

This problem also applies for “things” of 'sentence, 'paragraph and others.

(defun my-thing-symbol ()
  "print current symbol."
  (interactive)
  (message "symbol is %s" (thing-at-point 'symbol)))

test text:

aa_bb-cc

Put your cursor between “b”.

return a cons pair problem

as of Emacs 29 (Released 2023-07)

It returns a Elisp: Cons Cell . This make it hard to use Elisp: Destructure Binding (seq-setq, seq-let)

;; if bounds-of-thing-at-point return list, you can do
(let (xbeg xend)
  (seq-setq (xbeg xend) (bounds-of-thing-at-point 'symbol)))

;; but bounds-of-thing-at-point return a cons, you need to turn it into a list, and need a intermediate variable
(let (xbeg xend)
  (let (xbds (bounds-of-thing-at-point 'symbol))
    (seq-setq (xbeg xend) (list (car xbds) (cdr xbds)))))

;; or
(let (xbeg xend)
  (let (xbds (bounds-of-thing-at-point 'symbol))
    (setq xbeg (car xbds)
          xend (cdr xbds))))

;; actual example
(defun my-test ()
  "print begin end of current symbol"
  (interactive)
  (let (xbeg xend)
    (let (xbds (bounds-of-thing-at-point 'symbol))
      (seq-setq (xbeg xend) (list (car xbds) (cdr xbds)))
      (message "begin at %s , end at %s" xbeg xend))))

;; worse. it won't work with seq-let
(defun my-test-seqlet ()
  "print begin end of current symbol"
  (interactive)
  (seq-let (xbeg xend)
    (let (xbds (bounds-of-thing-at-point 'symbol)) (list (car xbds) (cdr xbds)))
    ;; warning, this does not work
    (message "begin at %s , end at %s" xbeg xend)))

Inconsistent Behavior for line

This means you have to write extra code to check the newline char.

better is never include newline.

(defun my-thing-line ()
  "print current line, and print whether it contain eol char.
2024-12-14"
  (interactive)
  (let ((xline (thing-at-point 'line)))
    (message "current line is [%s]" xline)
    (if (string-match "\n" xline)
        (message "line contain newline char")
      (message "NOOOO!"))))

thing-at-point 'filename problem

2024-12-14 as of Emacs 29 (Released 2023-07)

if the file name contains non-ASCII characters, it returns the wrong name.

test file name:

some→thing.txt

Non-ASCII file names happens for example when you download some Wikipedia image, it has en-dash in file name or é.

(defun my-thing-filename ()
  "print `thing-at-point' filename"
  (interactive)
  (message "filename is %s" (thing-at-point 'filename)))

;; test file name
;; xxx→yyy.txt

;; returns xxx
;; or
;; yyy.txt

thing-at-point 'url problem (fixed as of emacs 24.4.1)

What thing-at-point returns is not necessarily the exact text under cursor. When the URL you want to grab does not start with “http”, it adds it.

2015-05-22 This is fixed as of Emacs 24.4 (Released 2014-10) .

For example, if the text under cursor is

example_org/emacs/elisp.html

it'll return

http://example_org/emacs/elisp.html

This is very annoying.

Sometimes i just want to grab a sequence of chars that may be file path or URL, in a HTML file text such as href="my_cat.html" or href="http://example/my_cat.html". You do not know which in advance, but after you got the thing you can test it by checking for “http” or other things. But if you use thing-at-point with 'filename or 'url, it does things to the string that you didn't expect.

URL contains parenthesis

(thing-at-point 'url) gets confused if the URL contains parenthesis. e.g. http://en.wikipedia.org/wiki/Oz_(programming_language). (this is fixed in Emacs 23.2 (Released 2010-05) .)

(defun my-thing-url ()
  "print `thing-at-point' url"
  (interactive)
  (message "[%s]" (thing-at-point 'url)))

Get Text Selection or Unit at Current Cursor Position

Starting with emacs 23, text selection is highlighted by default. (this means: Transient Mark Mode is on by default.) There's a new user interface idiom. When there is a text selection, the command will act on the text selection. Otherwise, the command acts on the current word, line, paragraph, buffer, etc, whichever is appropriate for the command. This is great because users don't have to think about whether to call the “-region” version of the command. [see Emacs 23 (Released 2009-07)]

When you write a command to do this, the code typically looks like this:

;; get current selection or word
(let (bds p1 p2 inputStr resultStr)

  ;; get boundary
  (if (use-region-p)
      (setq bds (cons (region-beginning) (region-end) ))
      (setq bds (bounds-of-thing-at-point 'word)) )
  (setq p1 (car bds) )
  (setq p2 (cdr bds) )

  ;; grab the string
  (setq inputStr (buffer-substring-no-properties p1 p2)  )

  ;; do something with inputStr here

  (delete-region p1 p2 ) ; delete the region
  (insert resultStr) ; insert new string
 )

It takes about 6 lines to get the boundary and the string. If you are grabbing line, then you need few more lines to check EOL.

Alternative Solution: xah-get-thing.el

Because i need to grab the text so often, i got tired of repeatedly writing these 10 or so lines. I wrote a function that does this. See: Emacs: xah-get-thing.el 📦.

Emacs Lisp, Get Thing at Point