When text is copied from an org-mode or markdown-mode buffer and pasted into another Emacs buffer, its visual formatting can sometimes follow it. For instance, text displayed with a particular color, background, or font weight in an Org or Markdown buffer can retain that appearance after being inserted into a Python buffer.
This can be undesirable because the inserted text may then display with the source buffer's face text property instead of relying on the destination buffer's normal font-lock or syntax highlighting.
Understanding the face text property
In Emacs, visual formatting can be represented by the face text property. Some major modes, including org-mode and markdown-mode, make extensive use of text properties, including face, to control how text is displayed. When text containing these properties is yanked (pasted), Emacs can preserve them unless they are explicitly excluded.
Solution 1: Strip faces universally on paste
Emacs provides the yank-excluded-properties variable for specifying text properties that should not be retained when text is yanked (pasted) into a buffer. To prevent face properties from being carried into the destination buffer, add the following to your configuration:
(add-to-list 'yank-excluded-properties 'face)
With this configuration, the face property is removed from the inserted yanked text. The inserted text can therefore be displayed according to the destination buffer's own font-lock and other display rules.
Tradeoff: Once face is included in yank-excluded-properties, yanked text does not retain its face text property. This is generally desirable when the intention is for inserted text to adopt the appearance of its destination buffer, but it may be undesirable for workflows that intentionally use rich text. Examples of workflows and modes where you might want to preserve rich text include enriched-mode, mu4e, or gnus.
For the majority of users, adding 'face to yank-excluded-properties is a worthwhile configuration change. This makes copy and paste more predictable and prevents formatting from leaking into unrelated buffers.
Solution 2: Strip faces per mode on copy
While appending face to the global yank-excluded-properties variable sanitizes pasted text, it applies universally across Emacs. For workflows that require rich text in specific applications like mu4e or gnus, a global configuration is too aggressive.
An alternative way is to strip the visual formatting at the source during the copy operation, rather than at the destination during the paste operation. This can be achieved by applying buffer-local advice to filter-buffer-substring-function:
;; Copy-paste without org-mode or markdown-mode formatting bleeding into other
;; buffers. Alternative to: (push 'face yank-excluded-properties)
;; URL: https://www.jamescherti.com/emacs-fix-org-mode-copy-paste-yank-bleed/
(defun my-strip-face-properties-from-string (string)
"Remove visual face properties from STRING."
(remove-text-properties 0 (length string)
'(face nil font-lock-face nil)
string)
string)
(defun my-enable-plain-text-copy ()
"Strip visual face properties from copied text in the current buffer."
(add-function :filter-return
(local 'filter-buffer-substring-function)
#'my-strip-face-properties-from-string))
(add-hook 'markdown-mode-hook #'my-enable-plain-text-copy)
(add-hook 'markdown-ts-mode-hook #'my-enable-plain-text-copy)
(add-hook 'org-mode-hook #'my-enable-plain-text-copy)
;; Other modes
;; (add-hook 'prog-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'text-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'conf-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'diff-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'help-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'info-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'compilation-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'shell-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'eshell-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'magit-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'dired-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'term-mode-hook #'my-enable-plain-text-copy)
;; (add-hook 'vterm-mode-hook #'my-enable-plain-text-copy)
Instead of filtering the text when it is pasted, this solution cleans the text as it is copied, making sure only plain text enters the Emacs clipboard history (the kill ring).
Thanks. This is something that annoyed me in the past quite a bit. Also good for pointing out workflows where this might break.
Happy to help, Christian! This is one of those quiet Emacs annoyances that are worth fixing.