1

I'm trying to build a simple proc that copies the text on which the point is set and returns the point to where it was, but can't make it work properly.
Before you say "thing-at-point" - I cannot install it (work network).

My proc looks like this:

(defun string-at-point ()
 "copy string around point"
 (interactive "")
 (point-to-register ?y)
 (re-search-backward "\_<" nil t 1)
 (setq mark-active t)
 (re-search-forward "\_>" nil t 1)
 (call-interactively 'kill-ring-save)
 (register-to-point ?y)
)

Thing is, it only copies parts of the text.
Sample text:
this is some /very/long/name-of/my.file/that-might_contain_letters/andnumbers0-9/anywhere in it.
I expect my proc, when placed inside the long string (say - point is at 0), to copy the string and leave the point back at 0. instead, it copies 0-9/anywhere in it. (meaning - until the end of the line).

Also tried using "\\_>" and "\\_<" in the re-searches, but got even weirder results...

What am I doing wrong?

2
  • You can use (save-excursion ...) instead of saving/restoring your point position. See for instance here. Mar 4, 2021 at 9:49
  • @FirminMartin thanks, that's a nice command I wasn't aware of, but the return to point is probably the only thing working properly in this function I wrote, so it doesn't advance me too much... Mar 4, 2021 at 9:58

2 Answers 2

2

You can try the following command. It has certainly lots of corner cases as it uses a simple whitespace syntax. If your intention is to copy arbitrary text, it's easier to select the region C-SPC (set-mark-command), then copy it M-w (kill-ring-save).

(defun my-string-at-point ()
  "Save the space-delimited string at point to the kill ring."
  (interactive)
  (save-excursion
    (let ((beg (progn (skip-syntax-backward "^ " (line-beginning-position))
                      (point)))
          (end (progn (skip-syntax-forward "^ " (line-end-position))
                      (point))))
      (copy-region-as-kill beg end))))
2
  • well, that seems to do a good job. checking it across syntaxes I'm using. I tried a similar approach before (search for space), but then the space was part of my marked area. How come it isn't the case here? Mar 4, 2021 at 11:45
  • 1
    @user2141046 Because skip-syntax-forward, skip-syntax-backward, skip-chars-forward, and skip-chars-backward all leave point at the first character that doesn't match the thing they're looking for. By contrast, re-search-forward, re-search-backward, search-forward, and search-backward all leave point after the thing they're looking for. So in the latter case you need to make up for that.
    – Basil
    Mar 4, 2021 at 13:12
2

Before you say "thing-at-point" - I cannot install it (work network).

thing-at-point has been built into Emacs since 1993.

thing-at-point is an autoloaded compiled Lisp function in
‘thingatpt.el’.

(thing-at-point THING &optional NO-PROPERTIES)

  Probably introduced at or before Emacs version 20.

Return the THING at point.
THING should be a symbol specifying a type of syntactic entity.
Possibilities include ‘symbol’, ‘list’, ‘sexp’, ‘defun’,
‘filename’, ‘url’, ‘email’, ‘uuid’, ‘word’, ‘sentence’, ‘whitespace’,
‘line’, ‘number’, and ‘page’.

When the optional argument NO-PROPERTIES is non-nil,
strip text properties from the return value.

See the file ‘thingatpt.el’ for documentation on how to define
a symbol as a valid THING.

For example:

(thing-at-point 'word)
(thing-at-point 'symbol)

(re-search-backward "\_<" nil t 1)

This is invalid regexp syntax - it should be either "\\<" for "beginning of word", or "\\_<" for "beginning of symbol". Similarly with the matching "end of X" regexp. See (info "(elisp) Syntax of Regexps") and its subnodes.

this is some /very/long/name-of/my.file/that-might_contain_letters/andnumbers0-9/anywhere in it.

Hm, that's not an easy construct. The problem is that word/symbol boundaries depend on the current buffer's syntax table, so they will work differently depending on which buffer you're in.

This means you need to specify exactly what constitutes your desired boundaries, or in which mode you will be using this command.

For instance, the following should work with your example in a Lisp buffer:

(defun my-save-symbol-at-point ()
  "Make symbol at point the latest kill in the kill ring."
  (interactive)
  (let ((symbol (thing-at-point 'symbol)))
    (when symbol (kill-new symbol))))

But it won't work the same in a Text buffer. Ideally the following should work more reliably regardless of mode:

(defun my-save-filename-at-point ()
  "Make file name at point the latest kill in the kill ring."
  (interactive)
  (let ((file (thing-at-point 'filename)))
    (when file (kill-new file))))

You could even complete different types of thing at point that you may be interested in:

(defun my-save-thing-at-point (&optional thing)
  "Make THING at point the latest kill in the kill ring.

See `thing-at-point' for possible values of THING.
It defaults to `filename'.

With a prefix argument, select a type of THING with completion."
  (interactive
   (when current-prefix-arg
     ;; Add whatever you like here.
     (let ((things '("filename" "symbol" "sexp" "url" "word")))
       (list (intern (completing-read "Type of thing (default filename): "
                                      things nil t nil nil things))))))
  (kill-new (or (thing-at-point (or thing 'filename))
                (user-error "No such thing at point"))))

If all you want is to detect text boundaries that consist of spaces, then see Firmin Martin's answer.

3
  • Your answer is very thorough and I must look deeper into it in order to see if it works or not, but for the meanwhile - in the coding languages I use, Firmin's answer seem to be doing a fine job. I'll delve deep into your answer later on... BTW, I tried using "\_>", it didn't work properly... Mar 4, 2021 at 11:58
  • @user2141046 That should be "\\_>", not "\_>". And like I said, whether symbol start and symbol end is appropriate for your needs depends on the current mode's syntax table.
    – Basil
    Mar 4, 2021 at 13:07
  • I wrote with double backslashes - the syntax convertor changed it to a single backslash :-/. in any case, Firmin's solution was enough for me because some of my strings may contain all sorts of parenthesis, so between spaces is good enough... Mar 8, 2021 at 10:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.