14

How do I remove an existing hyperlink without having to wade in and manually delete a bunch of brackets? When I try to org-insert-link and just delete the existing link, I get Lisp error: (error "Empty link").

I want to remove the link and preserve the text (i.e. the description).

9

The following elisp function will take a link around the current point as recognised by org-bracket-link-regexp, so either [[Link][Description]] or [[Link]], and replace it by Description in the first case or Link in the second case.

New version: for org 9.3 or newer (also added save-excursion as in the answer of @Chris)

(defun afs/org-replace-link-by-link-description ()
    "Replace an org link by its description or if empty its address"
  (interactive)
  (if (org-in-regexp org-link-bracket-re 1)
      (save-excursion
        (let ((remove (list (match-beginning 0) (match-end 0)))
              (description
               (if (match-end 2) 
                   (org-match-string-no-properties 2)
                 (org-match-string-no-properties 1))))
          (apply 'delete-region remove)
          (insert description)))))

Old version: before org 9.3

(defun afs/org-replace-link-by-link-description ()
    "Replace an org link by its description or if empty its address"
  (interactive)
  (if (org-in-regexp org-bracket-link-regexp 1)
      (let ((remove (list (match-beginning 0) (match-end 0)))
        (description (if (match-end 3) 
                 (org-match-string-no-properties 3)
                 (org-match-string-no-properties 1))))
    (apply 'delete-region remove)
    (insert description))))

In org version 9.3 org-bracket-link-regexp was obseleted, but aliased to the new org-link-bracket-re. The new regexp is documented in ol.el as

org-link-bracket-re
(rx (seq "[["
         ;; URI part: match group 1.
         (group
          (one-or-more
               (or (not (any "[]\\"))
               (and "\\" (zero-or-more "\\\\") (any "[]"))
               (and (one-or-more "\\") (not (any "[]"))))))
         "]"
         ;; Description (optional): match group 2.
         (opt "[" (group (+? anything)) "]")
         "]"))

so the second group now matches the description of the link if present.

| improve this answer | |
  • 1
    That's a very neat compact solution! I updated my answer to support [[LINK]] format org links too. I learned about match-beginning and match-end from your answer. – Kaushal Modi Apr 16 '15 at 14:09
  • I had to change 3 to 2 in @Andrew Swan answer. I am assuming the current version of regexp search returns only 2 matches: the link and the description. Maybe this change happened through numerous upgrades of org-mode since 2015. I am running Org mode 9.3.7 on GNU Emacs 27.1. With that change, it works perfectly! – Raj Sep 19 at 16:17
  • @Raj Many thanks. Code updated. This changed in version 9.3. – Andrew Swann Sep 20 at 8:23
6

I tried to add this to the answer from @Andrew, but it was too long for a comment...

I really liked his solution, except that it moved the cursor. (Technically I guess it moved the point. Anyway...) Fortunately, it was easy to add save-excursion to avoid that:

(defun afs/org-replace-link-by-link-description ()
  "Replace an org link by its description or if empty its address"
  (interactive)
  (if (org-in-regexp org-bracket-link-regexp 1)
      (save-excursion
        (let ((remove (list (match-beginning 0) (match-end 0)))
              (description (if (match-end 3) 
                               (org-match-string-no-properties 3)
                             (org-match-string-no-properties 1))))
          (apply 'delete-region remove)
          (insert description)))))
| improve this answer | |
5

There is a solution which avoids using custom parsing with regexes and directly uses the built-in org-element API:

(defun org-link-delete-link ()
  "Remove the link part of an org-mode link at point and keep
only the description"
  (interactive)
  (let ((elem (org-element-context)))
    (if (eq (car elem) 'link)
        (let* ((content-begin (org-element-property :contents-begin elem))
               (content-end  (org-element-property :contents-end elem))
               (link-begin (org-element-property :begin elem))
               (link-end (org-element-property :end elem)))
          (if (and content-begin content-end)
              (let ((content (buffer-substring-no-properties content-begin content-end)))
                (delete-region link-begin link-end)
                (insert content)))))))
| improve this answer | |
  • Can you add how to bind your command to a key so it's sufficient for novices? – user12563 Apr 21 '19 at 22:05
  • 1
    This seems to delete white spaces after the link. Any idea how to keep them? – Timm Feb 23 at 11:25
4

Call this command when the point is anywhere after the first [[ brackets of an org-link (or anywhere on/after an hyper-linked org-link).

An org link will be deleted if it is of the format [[LINK][DESCRIPTION]] or [[LINK]] in an org-mode buffer; else nothing will happen.

For safety, the discarded LINK from org-link is saved to the kill-ring in the event a need arises to use that link elsewhere.

(defun my/org-delete-link ()
  "Replace an org link of the format [[LINK][DESCRIPTION]] with DESCRIPTION.
If the link is of the format [[LINK]], delete the whole org link.

In both the cases, save the LINK to the kill-ring.

Execute this command while the point is on or after the hyper-linked org link."
  (interactive)
  (when (derived-mode-p 'org-mode)
    (let ((search-invisible t) start end)
      (save-excursion
        (when (re-search-backward "\\[\\[" nil :noerror)
          (when (re-search-forward "\\[\\[\\(.*?\\)\\(\\]\\[.*?\\)*\\]\\]" nil :noerror)
            (setq start (match-beginning 0))
            (setq end   (match-end 0))
            (kill-new (match-string-no-properties 1)) ; Save the link to kill-ring
            (replace-regexp "\\[\\[.*?\\(\\]\\[\\(.*?\\)\\)*\\]\\]" "\\2" nil start end)))))))
| improve this answer | |
3

The quickest may be to place the cursor before the link, then type C-M-space (mark-sexp), which will mark the entire link. Then delete it by typing a backspace (if you use delete-selection-mode) or C-w.

| improve this answer | |
  • 1
    This seems to delete the entire link, but I want to remove the link and preserve the text (i.e. the description). – incandescentman Apr 16 '15 at 1:15
  • 1
    Sorry, I guess I misunderstood your question. – Harald Hanche-Olsen Apr 16 '15 at 8:01
  • 1
    To the downvoters out there: The question was ambiguous when I answered it. It has been edited since then, removing the ambiguity. I do leave the answer up, though, since it still seems the easiest way to delete the entire link, description and all. – Harald Hanche-Olsen Apr 17 '15 at 7:03
  • This frequently does not select the whole link. Some ]] stay around after deleting the selection. This sometimes works in the same document at the same link and sometimes it doesn't. When I org-toggle-link-display after selection, sometimes the whole link is selected, sometimes only the caption text. Also with the expand-region package, sometimes if have to execute er/expand-region 4 times until text outside the link is selected, sometimes only to times even though I follow exactly the same steps beginning with Emacs restart. – Günter Zöchbauer May 7 at 10:52
  • @GünterZöchbauer That's quite odd, I think, unless of course either the link text or the link itself contains unbalanced delimiters. I can't think of a reasonable explanation. (Which doesn't mean one can't be found.) – Harald Hanche-Olsen May 7 at 19:03
0

This very quick and very dirty macro would be one way, not the best way:

(fset 'my/org-remove-link
   [?\M-a delete delete ?\M-x ?z ?a ?p ?- ?t ?o ?- ?c ?h ?a ?r return ?\[ ?\C-e backspace backspace ?\C-x])
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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