Chat log: Ragmacs test

Elisp introspection tools for gptel


Prompt: Write an emacs command that yanks text from the kill-ring after stripping its text properties. Call it yank-no-properties.

Response:

(function_source :function “yank-no-properties”)
(:name "function_source" :args (:function "yank-no-properties"))

nil

To create the Emacs command yank-no-properties that yanks text from the kill-ring but strips its text properties, the command should grab the first item from the kill-ring, remove text properties using substring-no-properties, and insert it at point.

Here’s the exact Elisp code:

(defun yank-no-properties ()
  "Yank from the kill-ring, but strip all text properties."
  (interactive)
  (if kill-ring
      (insert (substring-no-properties (current-kill 0)))
    (message "kill-ring is empty")))

Prompt: Is there a way to do this that respects the other interactive behaviors of yank? Ex: Pressing M-y should cycle through the kill-ring etc.

Response: Yes. The command yank manages the interactive behavior like cycling with M-y internally by setting this-command and using yank-pop. You can reuse its machinery but override the insertion to strip properties.

You can advise or replace yank behavior, or define a wrapper around yank that uses current-kill stripped of properties, then call yank-pop normally afterward.

A typical approach is to define an around-advice or a function that sets kill-ring entries with properties removed temporarily.

Would you like me to give you a code example that integrates with the usual yank=/=yank-pop interactive behavior, including M-y cycling?


Prompt: Check if there are any built-ins in the Emacs API that you can use to do this, then write the code.

Response:

(function_completions :function_prefix “yank”)
(:name "function_completions" :args (:function_prefix "yank"))

yank-pop
message-fill-yanked-message
yank-in-context
gnus-summary-yank-process-mark
eat-yank
mail-yank-region
,*table--cell-yank-clipboard-selection
eww-bookmark-yank
org-yank-image-read-filename
insert-for-yank
isearch-yank-pop
calc-yank
ibuffer-yank-filter-group
remove-yank-excluded-properties
eat-mouse-yank-primary
org--image-yank-media-handler
mouse-yank-secondary
org-yank
isearch-yank-string
org-bibtex-yank
org-mouse-yank-link
x-clipboard-yank
eat--t-term-bracketed-yank--cmacro
comint--mark-yanked-as-output
calc-trail-yank
bibtex-yank-pop
isearch-yank-word
isearch-yank-kill
consult-yank-pop
elfeed-search-yank
clipboard-yank
yank-rectangle
isearch-yank-from-kill-ring
mail-yank-clear-headers
diff-yank-function
isearch-yank-char
avy-action-yank
rectangle--insert-for-yank
org-setup-yank-dnd-handlers
mouse-yank-at-click
menu-bar-update-yank-menu
message--yank-original-internal
senator-yank-tag
elfeed-show-yank
yank-media-handler
isearch-yank-internal
yank-media--find-matching-media
ibuffer-yank
eat-term-send-string-as-yank
isearch-yank-until-char
yank-in-context--transform
message-yank-buffer
org-yank-image-autogen-filename
gnus-group-yank-group
isearch-yank-char-in-minibuffer
isearch-yank-line
rotate-yank-pointer
yank-media-types
message--yank-media-image-handler
bookmark-yank-word
isearch-yank-symbol-or-char
,*table--cell-clipboard-yank
yank-from-kill-ring
bibtex-yank
isearch-yank-x-selection
eat-yank-from-kill-ring
calc-yank-internal
message-yank-original
mail-yank-original
consult-yank-replace
yank-handle-category-property
eat--t-term-bracketed-yank
calc-yank-mouse-primary
isearch-yank-word-or-char
eat--t-enable-bracketed-yank
eat-mouse-yank-secondary
org-yank-folding-would-swallow-text
isearch-yank-pop-only
avy-action-yank-whole-line
,*table--cell-yank
yank-media--get-selection
eat--t-disable-bracketed-yank
isearch--yank-char-or-syntax
yank-handle-font-lock-face-property
yank-media--utf-16-p
org-yank-generic
mouse-yank-primary
avy-action-yank-line
insert-for-yank-1
yank-media
eshell--mark-yanked-as-output
menu-bar-select-yank
consult-yank-from-kill-ring
mail-fill-yanked-message
mouse-yank-from-menu
yank
insert-buffer-substring-as-yank
org--copied-files-yank-media-handler
yank-media-types--format
(function_completions :function_prefix “pop”)
(:name "function_completions" :args (:function_prefix "pop"))

yank-pop
calc-pop-above
transient--pop-keymap
pop-tag-mark
pop3-get-message-count
org-roam-populate
kmacro-pop-ring1
internal-pop-keymap
,*table--present-cell-popup-menu
magit-pop-revision-stack
dired-rsync--pop-to-rsync-failed-buf
popper--update-popups
popper-display-control-p
cua-pop-to-last-change
display-buffer--maybe-pop-up-window
menu-or-popup-active-p
isearch-yank-pop
popper-tab-line--ensure
popper--fit-window-height
diff-hl-show-hunk-inline-popup
popper-echo--activate-keymap
eat--line-history-isearch-pop-state
ebnf-pop-style
0x0-popup
semantic-popup-menu
reftex-pop-to-bibtex-entry
my/pop-global-mark-display-buffer
comint-history-isearch-pop-state
special-display-popup-frame
org-mouse-popup-global-menu
popper--find-buried-popups
edebug-pop-to-buffer
popper--set-reference-vars
isearch--state-pop-fun--cmacro
popper--modified-mode-line
popper-group-by-directory
bool-vector-count-population
org-pop-to-buffer-same-window
backtrace--pop-to-buffer-pos
bibtex-pop
popper-toggle-type
my/eshell-font-lock-and-pop
bibtex-yank-pop
ad-pop-advised-function
tq-queue-pop
x-popup-menu
consult-yank-pop
popper-group-by-project
pop-mark
display-buffer--maybe-pop-up-frame-or-window
ibuffer-pop-filter
Info-isearch-pop-state
kmacro-pop-ring
isearch--state-pop-fun
popper-select-popup-at-bottom
Electric-pop-up-window
which-key--hide-popup-ignore-command
eat--line-populate-input-ring
corfu--popup-show
vc-git-stash-pop-at-point
cvs-pop-to-buffer-same-frame
json-pop--inliner
popper-group-by-perspective
xref-quit-and-pop-marker-stack
popper--restore-mode-lines
edebug-pop-to-backtrace
corfu--candidates-popup
mail-source-check-pop
popper-lower-to-popup
popper-tab-line--format
message-pop-to-buffer
pop-to-mark-command
my/popper-select-below
semantic-stickyfunc-popup-menu
popper-toggle-latest
which-key--popup-showing-p
calc-pop
js--ensure-cache--pop-if-ended
json-pop
popper--find-popups
popper--open-all
popper-kill-latest-popup
yaml--pop-state
pop3-movemail
calc-pop-push-record-list
c-populate-abbrev-table
cua-paste-pop
which-key--show-popup
corfu-popupinfo-toggle
my/popper-switch-to-popup
calc-pop-push-list
calc-pop-push
popper-close-latest
bookmark-menu-popup-paned-menu
popper-echo-mode
popper-echo
x-popup-dialog
calc-pop-stack
notmuch-mua-pop-to-buffer
xref-pop-marker-stack
popper-mode
notmuch-tree-outline--pop-at-end
bibtex-pop-next
calc-pop-push-record
popper-popup-p
eshell/popd
corfu--popup-support-p
pop-to-buffer-same-window
diff-hl-inline-popup-hide
dictionary-popup-matching-words
sp--undo-pop-to-last-insertion-node
pop-to-buffer
which-key--popup-max-dimensions
sql-toggle-pop-to-buffer-after-send-region
vc-git-stash-pop
transient--stack-pop
multi-isearch-pop-state
popper-open-latest
magit-stash-pop
indent-rigidly--pop-undo
corfu--popup-hide
magit-submodule-unpopulate
popper-cycle
ibuffer-mode-groups-popup
package-menu--populate-new-package-list
ibuffer-mouse-popup-menu
dired-dnd-popup-notice
c-populate-syntax-table
python-util-popn
minibuffer-history-isearch-pop-state
smerge-popup-context-menu
popper-tab-line-mode
pop-global-mark
ibuffer-pop-filter-group
popper-message-shorten
xref-pop-to-location
display-buffer-pop-up-window
popper--suppress-popups
shell-process-popd
popper-raise-popup
display-buffer-pop-up-frame
calc-vector-pop-variance
calc-vector-pop-sdev
popper-echo--dispatch-kill
dired-filter-pop
popper-cycle-backwards
org-babel-pop-to-session
avl-tree-stack-pop
info-pop-to-buffer
embark-popper-toggle
tablist-pop-filter
org-babel-pop-to-session-maybe
display-popup-menus-p
which-key--hide-popup
window--pop-up-frames
popper-toggle
inspector-pop
which-key--get-popup-map
popper-group-by-projectile
gnus-summary-pop-article
isearch-pop-state
isearch-yank-pop-only
popper-echo--dispatch-raise
bibtex-pop-previous
dired-filter-pop-all
popper-echo--dispatch-toggle
sp--populate-keymap
gnus-summary-pop-limit
pdf-misc-popup-context-menu
popper-echo--popup-info
dired-mark-pop-up
semantic-highlight-func-popup-menu
popper--bury-all
popup-menu-normalize-position
repeat-help--which-key-popup
display-buffer--maybe-pop-up-frame
mail-source-fetch-pop
avl-tree--stack-repopulate
avy-pop-mark
dictionary-mouse-popup-matching-words
corfu-popupinfo-mode
calc-kbd-pop
tablist-filter-pop
aw--pop-window
TeX-pop-to-buffer
mail-source-touch-pop
popper--delete-popup
which-key--hide-popup-on-frame-size-change
popper--suppress-p
pdf-annot-pop-to-attachment
magit-submodule-populate
popup-menu
diff-hl-inline-popup-show
popper-display-popup-at-bottom
calc-vector-pop-covariance
(function_source :function “yank”)
(:name "function_source" :args (:function "yank"))

(defun yank (&optional arg)
  "Reinsert (\"paste\") the last stretch of killed text.
More precisely, reinsert the most recent kill, which is the stretch of
text most recently killed OR yanked, as returned by `current-kill' (which
see).  Put point at the end, and set mark at the beginning without
activating it. With just \\[universal-argument] as argument, put point
at beginning, and mark at end.
With argument N, reinsert the Nth most recent kill.

This command honors the `yank-handled-properties' and
`yank-excluded-properties' variables, and the `yank-handler' text
property, as described below.

Properties listed in `yank-handled-properties' are processed,
then those listed in `yank-excluded-properties' are discarded.

STRING will be run through `yank-transform-functions'.
`yank-in-context' is a command that uses this mechanism to
provide a `yank' alternative that conveniently preserves
string/comment syntax.

If STRING has a non-nil `yank-handler' property anywhere, the
normal insert behavior is altered, and instead, for each contiguous
segment of STRING that has a given value of the `yank-handler'
property, that value is used as follows:

The value of a `yank-handler' property must be a list of one to four
elements, of the form (FUNCTION PARAM NOEXCLUDE UNDO).
FUNCTION, if non-nil, should be a function of one argument (the
 object to insert); FUNCTION is called instead of `insert'.
PARAM, if present and non-nil, is passed to FUNCTION (to be handled
 in whatever way is appropriate; e.g. if FUNCTION is `yank-rectangle',
 PARAM may be a list of strings to insert as a rectangle).  If PARAM
 is nil, then the current segment of STRING is used.
If NOEXCLUDE is present and non-nil, the normal removal of
 `yank-excluded-properties' is not performed; instead FUNCTION is
 responsible for the removal.  This may be necessary if FUNCTION
 adjusts point before or after inserting the object.
UNDO, if present and non-nil, should be a function to be called
 by `yank-pop' to undo the insertion of the current PARAM.  It is
 given two arguments, the start and end of the region.  FUNCTION
 may set `yank-undo-function' to override UNDO.

See also the command `yank-pop' (\\[yank-pop])."
  (interactive "*P")
  (setq yank-window-start (window-start))
  ;; If we don't get all the way thru, make last-command indicate that
  ;; for the following command.
  (setq this-command t)
  (push-mark)
  (insert-for-yank (current-kill (cond
				  ((listp arg) 0)
				  ((eq arg '-) -2)
				  (t (1- arg)))))
  (if (consp arg)
      ;; This is like exchange-point-and-mark, but doesn't activate the mark.
      ;; It is cleaner to avoid activation, even though the command
      ;; loop would deactivate the mark because we inserted text.
      (goto-char (prog1 (mark t)
		   (set-marker (mark-marker) (point) (current-buffer)))))
  ;; If we do get all the way thru, make this-command indicate that.
  (if (eq this-command t)
      (setq this-command 'yank))
  nil)
(function_source :function “yank-pop”)
(:name "function_source" :args (:function "yank-pop"))

(defun yank-pop (&optional arg)
  "Replace just-yanked stretch of killed text with a different stretch.
The main use of this command is immediately after a `yank' or a
`yank-pop'.  At such a time, the region contains a stretch of
reinserted (\"pasted\") previously-killed text.  `yank-pop' deletes
that text and inserts in its place a different stretch of killed text
by traversing the value of the `kill-ring' variable and selecting
another kill from there.

With no argument, the previous kill is inserted.
With argument N, insert the Nth previous kill.
If N is negative, it means to use a more recent kill.

The sequence of kills wraps around, so if you keep invoking this command
time after time, and pass the oldest kill, you get the newest one.

You can also invoke this command after a command other than `yank'
or `yank-pop'.  This is the same as invoking `yank-from-kill-ring',
including the effect of the prefix argument; see there for the details.

This command honors the `yank-handled-properties' and
`yank-excluded-properties' variables, and the `yank-handler' text
property, in the way that `yank' does."
  (interactive "p")
  (if (not (eq last-command 'yank))
      (yank-from-kill-ring (read-from-kill-ring "Yank from kill-ring: ")
                           current-prefix-arg)
    (setq this-command 'yank)
    (unless arg (setq arg 1))
    (let ((inhibit-read-only t)
          (before (< (point) (mark t))))
      (if before
          (funcall (or yank-undo-function 'delete-region) (point) (mark t))
        (funcall (or yank-undo-function 'delete-region) (mark t) (point)))
      (setq yank-undo-function nil)
      (set-marker (mark-marker) (point) (current-buffer))
      (insert-for-yank (current-kill arg))
      ;; Set the window start back where it was in the yank command,
      ;; if possible.
      (set-window-start (selected-window) yank-window-start t)
      (if before
          ;; This is like exchange-point-and-mark, but doesn't activate the mark.
          ;; It is cleaner to avoid activation, even though the command
          ;; loop would deactivate the mark because we inserted text.
          (goto-char (prog1 (mark t)
                       (set-marker (mark-marker) (point) (current-buffer))))))
    nil))
(function_source :function “remove-yank-excluded-properties”)
(:name "function_source" :args (:function "remove-yank-excluded-properties"))

(defun remove-yank-excluded-properties (start end)
  "Process text properties between START and END, inserted for a `yank'.
Perform the handling specified by `yank-handled-properties', then
remove properties specified by `yank-excluded-properties'."
  (let ((inhibit-read-only t))
    (dolist (handler yank-handled-properties)
      (let ((prop (car handler))
            (fun  (cdr handler))
            (run-start start))
        (while (< run-start end)
          (let ((value (get-text-property run-start prop))
                (run-end (next-single-property-change
                          run-start prop nil end)))
            (funcall fun value run-start run-end)
            (setq run-start run-end)))))
    (if (eq yank-excluded-properties t)
        (set-text-properties start end nil)
      (remove-list-of-text-properties start end yank-excluded-properties))))

You can use the built-in variable yank-excluded-properties to control which text properties are stripped when yanking. If you set (setq yank-excluded-properties t), then all properties will be stripped for all yanks, including M-y (yank-pop) cycling.

To provide this behavior only for a specific command, you can temporarily rebind yank-excluded-properties while calling yank interactively. This keeps the interactive behavior of yank, including support for M-y.

Here is the correct implementation:

(defun yank-no-properties (arg)
  "Yank like `yank', but forcibly strips all text properties.
With prefix ARG, behave like `yank' with that argument."
  (interactive "*P")
  (let ((yank-excluded-properties t))
    (call-interactively 'yank)))

This will fully integrate with the standard yank/yank-pop machinery and work seamlessly with M-y.


Prompt: Thanks.

Author: Karthik Chikmagalur

Created: 2025-08-01 Fri 16:25

Validate