markdown-mode icon indicating copy to clipboard operation
markdown-mode copied to clipboard

feature request: convert links

Open anarcat opened this issue 8 years ago • 13 comments

i often end up converting links from:

[foo](http://example.com)

to:

[foo][]

 [foo]: http://example.com

The rationale is that writing the former is more natural, but looks more messy, so i start by doing that but often end up converting to the latter because it formats better.

I ended up recording this awful keyboard macro to do this more or less automatically:

C-s         ;; isearch-forward
](          ;; self-insert-command * 2
<left>          ;; left-char
C-SPC           ;; set-mark-command
C-r         ;; isearch-backward
[           ;; self-insert-command
M-w         ;; kill-ring-save
M-C-s           ;; isearch-forward-regexp
C-q         ;; quoted-insert
2*LFD           ;; electric-newline-and-maybe-indent
<left>          ;; left-char
<right>         ;; right-char
C-y         ;; yank
RET         ;; markdown-enter-key
<left>          ;; left-char
:           ;; self-insert-command
SPC         ;; self-insert-command
C-r         ;; isearch-backward
C-y         ;; yank
C-r         ;; isearch-backward
10*<right>      ;; right-char
10*<left>       ;; left-char
<right>         ;; right-char
C-s         ;; isearch-forward
](          ;; self-insert-command * 2
<left>          ;; left-char
<deletechar>        ;; delete-forward-char
[]          ;; self-insert-command * 2
C-SPC           ;; set-mark-command
C-s         ;; isearch-forward
)           ;; self-insert-command
<left>          ;; left-char
C-w         ;; kill-region
<deletechar>        ;; delete-forward-char
C-u C-SPC       ;; set-mark-command
C-u C-SPC       ;; set-mark-command
C-u C-SPC       ;; set-mark-command
C-y         ;; yank
<home>          ;; move-beginning-of-line
SPC         ;; self-insert-command
C-u C-SPC       ;; set-mark-command
C-u C-SPC       ;; set-mark-command
C-u C-SPC       ;; set-mark-command
M-q         ;; fill-paragraph

As a lisp expression:

;; horrible way of converting [foo](link) to [foo][] [foo]: link
(fset 'md-convert-link
   [?\C-s ?\] ?\( left ?\C-  ?\C-r ?\[ ?\M-w ?\C-\M-s ?\C-q ?\C-j ?\C-j left right ?\C-y ?\C-m left ?: ?  ?\C-r ?\C-y ?\C-r right ?\C-s ?\] ?\( left deletechar ?\[ ?\] ?\C-  ?\C-s ?\) left ?\C-w deletechar ?\C-u ?\C-  ?\C-u ?\C-  ?\C-u ?\C-  ?\C-y home ?  ?\C-u ?\C-  ?\C-u ?\C-  ?\C-u ?\C-  ?\M-q])

yes, this is horrible. i started writing this as a function but then got tired and wondered if anyone else worked on this. here's how far i got so far:

(defun anarcat/markdown-convert-link ()
  (interactive)
  (search-forward "](")
  (search-forward ")")
  (kill-region) ;; the link
  ;; need to kill the parens too
  (save-excursion
    (search-forward "\n\n")
    (yank)
    ;; need to save point here to come back
    )
  (search-backwards "]")
  (search-backwards "["]
  (kill-ring-save) ;; the text
  ;; now go back to the point and format the text and all
  )

i don't like it one bit so far... anyone else fixed up something like this?

anarcat avatar Feb 13 '16 04:02 anarcat

This is an existing feature: Place the point anywhere in the inline link and press C-c C-a r (as if you were inserting a reference link) and the link will be converted to a reference style link.

jrblevin avatar Feb 13 '16 05:02 jrblevin

btw, i don't know what markdown you are using, but with pandoc you can just do [foo] instead of [foo][].

vyp avatar Feb 13 '16 05:02 vyp

damn, i was offline and ended up rewriting this thing twice... here's what i ended up with:

(defun anarcat/markdown-convert-link ()
  "convert a [text](link) link format into [text][] ... [text]:
link

allowing search-backwards (say with a prefix) could also be
interesting.

This is discussed in
https://github.com/jrblevin/markdown-mode/issues/94 and should be
eventually merged upstream once it is acceptable elisp.
"
  (interactive)
  ;; find unconverted link
  ;; we have 3 groups in here:
  ;; 1. text (without brakets)
  ;; 2. link (with parens, to replace it with replace-match)
  ;; 3. link (without parens, to extract it to insert it later)
  (re-search-forward "\\[\\([^]]*\\)]\\((\\([^)]*\\))\\)")
  (let ((text (match-string 1))
        (link (match-string 3)))
    ;; remove the link and replace with brakets
    ;; link is the "2" below, and replace only that
    (replace-match "[]" nil nil nil 2)
    ;; go at the end of the paragraph and add stuff there
    (save-excursion
      (search-forward "\n\n")
      ;; insert the link reference
      (insert (format " [%s]: %s\n\n" text link))
      ;; XXX: we insert two newlines to avoid breaking the paragraph
      ;; break, but that makes too many spaces if we insert many links
      ;; like this. not sure how to fix this.
      )
    )
  )

also, C-c C-a r does not convert a link here: it just adds a new one. i am using markdown-mode.el 2.0 from Debian jessie, looking at the current code it seems this is a new feature from 2.1... grml.

oh well, i gues it's time to load a new version from ELPA. :)

@vyp as for the markdown dialect, i'm writing for ikiwiki, using the default markdown processor (which is not pandoc).

anarcat avatar Feb 13 '16 16:02 anarcat

markdown-mode is still at version 2.0 in marmalade, it seems. MELPA has a snapshot from 2016, which works, so I guess i just wasted time re-learning elisp programming. which was actually nice. :)

thanks!

anarcat avatar Feb 13 '16 17:02 anarcat

ah - one thing my function does a little better is that it puts the link at the end of the paragraph instead of at the end of file, which I like because it makes the plain text more readable: you don't have to jump to the end of file to follow links by hand.

anarcat avatar Feb 13 '16 17:02 anarcat

You can customize the location for inserting reference definitions by setting markdown-reference-location.

jrblevin avatar Feb 13 '16 18:02 jrblevin

cool, that's what i was looking for, thanks!

anarcat avatar Feb 13 '16 20:02 anarcat

This is an existing feature: Place the point anywhere in the inline link and press C-c C-a r (as if you were inserting a reference link) and the link will be converted to a reference style link.

Is it me or did this feature disappear?

If I have a document that only has [foo](link) and I move the point to "link" then type C-c C-a r, it just prompts me for the same link info again but doesn't convert the link...

Ideas?

anarcat avatar Sep 07 '17 13:09 anarcat

I combined the three old functions (for inline, reference, plain URL links) into one with the new C-c C-l command. The old commands are now aliases for it. If you remove the URL (kill it for using later), then give a reference label it will convert for you. If the ref isn't defined, one will be added and you'll be prompted for the URL (which you can then yank).

The idea is that it uses whatever information you give it to determine the link type. There are examples of other possible conversions in the documentation.

jrblevin avatar Sep 07 '17 14:09 jrblevin

oh i see. sorry for not catching up on the docs and all the noise. ;) short too, i like it. i'll try to train my muscle memory back in polace - thanks!

anarcat avatar Sep 07 '17 14:09 anarcat

It's no problem. My sense at the time was that C-c C-l would be more intuitive, but I'm considering bringing back dedicated conversion commands, as they do avoid shuffling URLs/text around.

jrblevin avatar Sep 07 '17 14:09 jrblevin

If you remove the URL (kill it for using later), then give a reference label it will convert for you. If the ref isn't defined, one will be added and you'll be prompted for the URL (which you can then yank).

I understand this ticket is getting really old by now, but I keep on thinking about it every time I switch links between inline and ref. That process actually takes a long time. The actual keystrokes are:

  • C-c
  • C-l
  • alt-backspace (orc-s-left c-w)
  • RET
  • RET
  • c-y
  • RET

phew! i often end up doing a macro instead, but it's not very reliable: if a ref already exist, it kind of explodes midway...

it would still be nice to have a wrapper around this, possibly by setting a prefix argument?

similar: https://github.com/jrblevin/markdown-mode/issues/565

anarcat avatar Nov 26 '20 23:11 anarcat

i reworked my code a bit and ended up with something like this:

(defun anarcat/markdown-convert-link ()
  "Convert markdown inline links to references.

This convers a [text](link) link format into [text][] ... [text]:
linké

Allowing search-backwards (say with a prefix) could also be
interesting.

This is discussed in:

https://github.com/jrblevin/markdown-mode/issues/94

Should eventually merged upstream once it is acceptable elisp."
  (interactive)
  ;; find unconverted link
  ;; we have 3 groups in here:
  ;; 1. text (without brakets)
  ;; 2. link (with parens, to replace it with replace-match)
  ;; 3. link (without parens, to extract it to insert it later)
  (if (re-search-forward "\\[\\([^]]*\\)]\\((\\([^)]*\\))\\)" nil t)
      (let ((text (match-string 1))
            (link (match-string 3)))
        ;; remove the link and replace with brakets
        ;; link is the "2" below, and replace only that
        (replace-match "[]" nil nil nil 2)
        ;; go at the end of the paragraph and add stuff there
        (save-excursion
          (search-forward "\n\n" nil 1)
          ;; insert the link reference
          (insert (format " [%s]: %s\n\n" text link))
          ;; XXX: we insert two newlines to avoid breaking the paragraph
          ;; break, but that makes too many spaces if we insert many links
          ;; like this. not sure how to fix this.
          ))
    (message "no inline link found until end of buffer")))

would you be open a merge request to add that to markdown-mode?

anarcat avatar Feb 13 '24 17:02 anarcat