What I'm trying to do is convert between Textile and Markdown links, so it should be able to do something like :s/".*"(.*)/[.*](.*)/, which is to say, keep the stuff that is matched by the .*

Is this possible in nvim/is there a plugin for this, or should I just resort to using a external program for this?

Additionally, does such a feature have a name?

1 Answer 1


Yes and no. Depends a lot on the complexity of the documents. Textile has a lot of various formats for links and Markdown has it's share.

Keeping pattern

When you do a substitute you can capture parts of the matched text in groups:

  • Start: \(
  • End: \)

The groups are numbered from one, zero being the whole text. One reference them by backslash + number, e.g. \1, \2. (Note that different dialects uses different syntax, JavaScript for example uses $1, $2)

  • Having the text foo bar baz and using foo \(bar\) \(baz\)

    • Group 0: foo bar baz
    • Group 1: bar
    • Group 2: baz

These groups can further be used in the replacement portion of substitute. From above:

  • s/foo \(bar\) \(baz\)/foo \2 \1

    • Result: foo baz bar

So if you have for example "Some Title":/some/path and do:

|    |    |     |
|    |    |     +------ Group 2: match a-z and /
|    |    +------------ Match ":
|    +----------------- Group 1: match anything but "
+---------------------- Match "

You can use the captured groups in the replacement part, for example:

%s/"\([^"]*\)":\([a-z/]*\)/GROUP0: \0\rGROUP1: \1\rGROUP2: \2

Result is:

GROUP0: "Some Title":/some/path
GROUP1: Some Title
GROUP2: /some/path

See :h \( and the rest of the pattern.txt help-file. Also note the groups can be nested. One can use \%( for non-numbered groups etc.

You can also use the group AKA back-reference / sub-expression in the search/match portion.
For example \(abra\)cad\1 would match abracadabra.

It is usually a good idea to use the confirm flag. E.g.: %s/foo/bar/c - the c at the end makes Vim ask before each + one can skip, do the rest etc. g for global is also often useful if one need to replace same thing more then once per line.

If you are new to RegExp it is a good idea to play around with one of the many online tools:

  • various other tools, guides etc. Only be aware that there are different versions, syntax etc.

Vim has it's own + a lot of extra niceties - but the basic concept is OK to train on using such tools.


Substitution patterns can quickly become very complex. For fun; Say one have:

"link text":/example
"link text(with title)":https://example.com/
"(classname)link text(title tooltip)":mailto:[email protected]

One could do:

s/"\%(\%((\)\([^)]*\)\%()\)\)\?\([^("]\+\)\%(\%((\)\([^)]*\)\%()\)\)\?":\([a-z:/@.%#?+&-]\+\)/\0:\r Class: \1\r Text : \2\r Title: \3\r URL : \4\r

To get:

  Text : Wikipedia
  URL  : https://en.wikipedia.org/

"link text":/example:
  Text : link text
  URL  : /example

"link text(with title)":https://example.com/:
  Text : link text
  Title: with title
  URL  : https://example.com/

"(classname)link text(title tooltip)":mailto:[email protected]:
  Class: classname
  Text : link text
  Title: title tooltip
  URL  : mailto:[email protected]

But that far from cover all + does not have a way to check for faults etc. Regexp is very very powerful but not the best tool for all tasks.

If for anything one usually have to work very methodically and know the mediums at task very good.

Break it up

Once it becomes a tad complex it is best to write a function where one can break it up and make it more readable etc. Use comments and descriptive names. As a simple example from the above (Here converting to HTML anchors):

(Extra quotes at end of comments as SE does not recognize Vim's one line comments.)

fun! s:MyLinkConverter()
        " Groups: 1 "
        let match_class_or_title =
                \   '\%('
                \ .        '\%((\)'
                \ .        '\([^)]*\)'
                \ .        '\%()\)'
                \ . '\)\?'
        " Groups: 1 "
        let match_text = '\([^("]\+\)'
        " Groups: 1 "
        let match_href = '\([A-Za-z:/@.%#?+&-]\+\)'

        " Groups:          "
        "       1: class   "
        "       2: text    "
        "       3: title   "
        "       4: href    "
        let pat_search =
                \ '"'
                \ . match_class_or_title
                \ . match_text
                \ . match_class_or_title
                \ . '":'
                \ . match_href

        let pat_replace =
                \ '<a href="\4" title="\3" class="\1">\2<\/a>'

        exe "'<,'>s/" . pat_search . "/" . pat_replace . "/gc"

command! -range MyLinkConverter call s:MyLinkConverter()

(SE also strip and mangles tabs and completely disrespect anyone using tabs in code, so I had to reformat it with spaces.) - (So sorry for the spaces over tabs)


Have not converted this kind of documents. A quick search hints to https://github.com/jgm/pandoc for one tool. There is also an extra tool: https://github.com/jldec/tomd - but status, usage etc. of either of these is not my domain.


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.