qadzek / link.vim

(Neo)Vim plugin to keep long URLs out of your way
MIT License
21 stars 1 forks source link

gitcommit adaption #5

Closed Konfekt closed 5 months ago

Konfekt commented 6 months ago

For what it's worth (maybe put it in a wiki?), here's an adaption to gitcommit messages, moving the links into a link section before any other comments on writing the buffer:

autocmd BufWrite <buffer>
            \ TagLinks |
            \ call mdlink#ConvertAllLinks() |
            \ call PutLinksBeforeComments()

command! -buffer -range=% -bar TagLinks silent keeppatterns <line1>,<line2>substitute#\v%(\[[^\]]+\]\(\s*)@<!\zs<https?://(%([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})#[\1](\0)#giep

" needs to be :sourced only once per session.
silent! function PutLinksBeforeComments()
    mark `
    normal! gg
    let hash_start = search('^#', 'nW')
    if hash_start == 0 || getline(hash_start) ==? '## Links'
        return
    endif

    let links_start = search('^## Links$', 'nW')
    if links_start == 0
        return
    endif

    let move_start = links_start
    call append(line('$'), '')
    let move_end = line('$')

    keepjumps execute move_start . ',' . move_end . 'move ' . (hash_start - 1)
    normal! g``

    let l:last_non_blank_line = prevnonblank(line('$'))
    if l:last_non_blank_line < line('$')
        execute (l:last_non_blank_line + 1) . ',$delete'
    endif
endfunction
qadzek commented 6 months ago

Thanks for sharing this snippet.

I had some trouble getting it to work:

E216: No such group or event: vimrcFileTypeGitcommit BufWrite <buffer> TagLinks | call mdlink#ConvertAllLinks() | call PutLinksBeforeComments()

I am not very familiar with autocmds, but changing it this way made it work on my system (I am using Neovim):

augroup git_links
  autocmd!
  autocmd FileType gitcommit autocmd BufWrite *
            \ TagLinks |
            \ call mdlink#ConvertAllLinks() |
            \ call PutLinksBeforeComments()
augroup END

Could you explain the purpose of TagLinks?

Integrating this into the main plugin seems difficult, but these extensions are certainly welcome. The Wiki should now be open for everyone. I could also mention extensions like this one and the one you shared in #1 in README.md.

Konfekt commented 6 months ago

I am sorry, the autocmd group vimrcFileTypeGitcommit is a personal leftover, there would be a preceding buffer-local augroup definition like

augroup vimrcFileTypeGitcommit
  autocmd! * <buffer>
augroup END

The idea of Taglinks is to convert all https into markdown links, so that your plug-in can treat them.

One motiviation of using your plug-in is that sometimes one lazily pastes long https into a message, but these better be put at the end, and some reference inline.

Konfekt commented 6 months ago

By the way, the wiki does not yet show up here

qadzek commented 6 months ago

The Wiki should now be up and running and editable by everyone. I have created blank pages for email and git commit extensions. Feel free to add your scripts, as you are familiar with them.

I don't really use Vim for email, so I am unable to test the snippet you shared in #1.

This extension works nicely though.

I like the idea of TagLinks. It appears to add an unnecessary dot, e.g. [vim.](https://vim.org).

It should probably be noted that users have to add this script to gitcommit.vim, instead of vimrc, to avoid that autocmd BufWrite <buffer> would operate on all filetypes.

Konfekt commented 6 months ago

Oops, you are right, I fixed the regex above. The command now reads

command! -buffer -range=% -bar TagLinks silent keeppatterns <line1>,<line2>substitute#\v%(\[[^\]]+\]\(\s*)@<!\zs<https?://(%([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})#[\1](\0)#giep
Konfekt commented 6 months ago

I think it does not hurt to provide sensible stubs to other common text ftplugins. Maybe Taglinks (corrected) can be added generically? I think this preprocessing is what https://github.com/qadzek/vim-md-link/issues/1 has in mind, and also what sprang to mine when I heard of your plug-in

Konfekt commented 6 months ago

The Wiki should now be up and running and editable by everyone. I have created blank pages for email and git commit extensions. Feel free to add your scripts, as you are familiar with them.

I updated the wiki accordingly, including a speculative declaration of b:md_link_heading

qadzek commented 6 months ago

I think it does not hurt to provide sensible stubs to other common text ftplugins. Maybe Taglinks (corrected) can be added generically?

This is certainly a good idea, to make it easier for users to create new extensions.

Currently, TagLinks converts

GitHub is a website https://github.com

to

GitHub is a website [github.com](https://github.com)

That is already a nice addition. However, running this plugin leaves us with:

GitHub is a website [github.com][0]

## Links

[0]: https://github.com

While, correct me if I am wrong, what we really want is the following, because that [github.com] is a bit ugly?

GitHub is a website [0]

## Links

[0]: https://github.com

So, in my opinion, the plugin should first be able to convert [github.com](https://github.com) into [0] instead of [github.com][0].

Konfekt commented 6 months ago

Yes, at the moment it works as described in the README

# Notes

[Vim](https://www.vim.org) and [Neovim](https://neovim.io) are text editors.

will be turned into

# Notes

[Vim][0] and [Neovim][1] are text editors.

## Links

[0]: https://www.vim.org
[1]: https://neovim.io

But true, in text messages, like mail, gitcommits, ..., more common is a simple bracketed number.

So the taglinks preprocesses the links for md-link.

You propose a command to postprocess them, removing the tag? I have not so strong opinions on it; the domain name can make the text more readable

qadzek commented 6 months ago

So the taglinks preprocesses the links for md-link.

You propose a command to postprocess them, removing the tag? I have not so strong opinions on it; the domain name can make the text more readable

When the feature request about email was made in #1, I was thinking about adding a new syntax, but that would be quite hard to implement.

Your idea seems more straightforward:

1) Preprocess: :TagLinks 2) Main conversion: :MdLinkConvertAll 3) Postprocess: optional, no command yet

Konfekt commented 6 months ago

Take a look at the latest wiki modifications; it was a bit of work to make it work

qadzek commented 6 months ago

Take a look at the latest wiki modifications; it was a bit of work to make it work

I would add a bit more explanation about what your snippets are doing exactly and maybe include a before/after comparison like in README.md, so it will be easier to understand for new users.

Maybe this plugin should include something like let b:md_link_insert_heading_before_first = '#', so PutLinksBeforeComments() wouldn't be needed.

In both snippets, there is a hardcoded heading value in let links_start = search('^## Links$', 'nW').

Konfekt commented 6 months ago

In both snippets, there is a hardcoded heading value in let links_start = search('^## Links$', 'nW').

Thank you very much, I missed updating the function

Konfekt commented 6 months ago

Maybe this plugin should include something like let b:md_link_insert_heading_before_first = '#', so PutLinksBeforeComments() wouldn't be needed.

Yes, an end marker would be great

qadzek commented 6 months ago

It would nice if your range function takes as optional parameters beginning and end markers, say.

I am not sure if this is what you mean, but I changed the plugin in a way that the reference section is no longer necessarily at the end of the buffer. This should simplify creating extensions a lot.

The code is still experimental (no documentation), so I created a dev branch. Would you be willing to give it a try?

You would use it like this:

" mail.vim
if !empty(findfile('ftplugin/markdown/vim-md-link.vim', &runtimepath))
  runtime ftplugin/markdown/vim-md-link.vim
endif

if exists(':MdLinkConvertAll') != 2 | finish | endif

let b:md_link_heading = 'Links:'
let b:md_link_heading_before = '^--'

and

" gitcommit.vim
if !empty(findfile('ftplugin/markdown/vim-md-link.vim', &runtimepath))
  runtime ftplugin/markdown/vim-md-link.vim
endif

if exists(':MdLinkConvertAll') != 2 | finish | endif

let b:md_link_heading = 'Links:'
let b:md_link_heading_before = '^# Please enter'

Note that for now, only inline Markdown links are supported [foo](https://bar.com). I will work on preprocess and postprocess commands though. There is now also an IRC channel: #vim-md-link on Libera.

Konfekt commented 6 months ago

Yes, seems to work:

[github.com][1]

[github.com][0]

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor 
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis      
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.   

[github.com][2]

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in  
culpa qui officia deserunt mollit anim id est laborum.                          

Links:

[0]: https://github.com/qadzek/vim-md-link/issues/5
[1]: https://github.com/qadzek/vim-md-link/issues/5
[2]: https://github.com/qadzek/vim-md-link/issues/5

-- 
Signature

using

let b:md_link_heading = 'Links:'
let b:md_link_heading_before = '^-- $'
let s:body_range = '1/^\s*$/,/' . b:md_link_heading_before.'\|^\V'.escape(b:md_link_heading,'\').'\m' . '$/-1'

command! -buffer -range=% -bar TagLinks silent keepjumps keeppatterns <line1>,<line2>substitute#\v%(\[[^\]]+\]\(\s*)@<!\zs<https?://(%([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})\S+#[\1](\0)#giep
autocmd BufWrite <buffer>
            \ exe s:body_range . 'TagLinks'|
            \ exe s:body_range . 'MdLinkConvertRange'
Konfekt commented 6 months ago

For gitcommit less so:


[github.com][0]

# Immetti il messaggio di commit per le modifiche. Le righe che iniziano
# con '#' saranno ignorate e un messaggio vuoto interromperà il commit.
#
# Sul branch master
# Il tuo branch è aggiornato rispetto a 'origin/master'.
#
# Modifiche di cui verrà eseguito il commit:
#   modificato:             .vim/ftplugin/gitcommit_md-link.vim
#
# ------------------------ >8 ------------------------

Links:

[0]: https://github.com/qadzek/vim-md-link/issues/5

using

let b:md_link_heading = 'Links:'
let b:md_link_heading_before = '^#'
let s:body_range = '1,/' . b:md_link_heading_before.'\|\V\^'.escape(b:md_link_heading,'\') . '\$/-1'

command! -buffer -range=% -bar TagLinks silent keepjumps keeppatterns <line1>,<line2>substitute#\v%(\[[^\]]+\]\(\s*)@<!\zs<https?://(%([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})\S+#[\1](\0)#giep
command! -buffer -range=% LinkConvertRange <line1>,<line2>TagLinks | <line1>,<line2>MdLinkConvertRange
autocmd BufWrite <buffer> exe s:body_range..'LinkConvertRange'
qadzek commented 6 months ago

I should have given a bit more explanation. let b:md_link_heading_before searches from the bottom of the buffer until the first match for the pattern is found.

So I would give let b:md_link_heading_before = '^# Immetti a try, because that's unique; there are multiple occurrences of '^#'.

Konfekt commented 6 months ago

Oh, that's good to know. Yes, that works.

qadzek commented 6 months ago

Thanks for testing the new features.

I have just added three new commands:

The first two are mostly helper commands, you probably want to use the last one.

Plaintext URLs like foo https://bar.com are now supported. This should simplify your extensions by a lot.

Konfekt commented 6 months ago

That's great. Would you mind passing a range to MdLinkProcessConvert? For example, to spare links in the mail header. Also in Git commit only up to the first comment. So the s:body_range still has its use.

Also, how about allowing concatenating commands by adding -bar to their definition ?

qadzek commented 6 months ago

Also, how about allowing concatenating commands by adding -bar to their definition ?

Should this be added to every command? Thanks for the suggestion, I wasn't really aware of -bar.

That's great. Would you mind passing a range to MdLinkProcessConvert? For example, to spare links in the mail header. Also in Git commit only up to the first comment. So the s:body_range still has its use.

I could make both mdlink#ProcessUrls() and mdlink#ProcessConvertRange() range functions, but the latter calls the former and I am not sure how to pass a range from one function to another?

Konfekt commented 6 months ago

Should this be added to every command? Thanks for the suggestion, I wasn't really aware of -bar.

Yes, indeed

I am not sure how to pass a range from one function to another?

Does

function! FirstFunction(...) range
  " Capture the range into variables
  let l:startline = a:firstline
  let l:endline = a:lastline

  " Do something with the range, if needed
  " ...

  " Call the second function with the range
  execute l:startline.','.l:endline.'call SecondFunction()'
endfunction

function! SecondFunction() range
  " This function now has access to the range
  " Do something with the range
  for i in range(a:firstline, a:lastline)
    " Process each line
    " ...
  endfor
endfunction

help?

qadzek commented 6 months ago

Thank you. execute l:startline.','.l:endline.'call SecondFunction()' was crucial to getting this right.

You can now use any of these commands. For convenience, I created an All version for every range function.

MdLinkPreProcessUrls
MdLinkPostProcessUrls
MdLinkProcessConvert

MdLinkPreProcessUrlsAll
MdLinkPostProcessUrlsAll
MdLinkProcessConvertAll

Let me know if it works as expected for gitcommit, mail or other non markdown filetypes.

Konfekt commented 6 months ago

Yes, it works flawlessly, thank you very much. I updated the wiki accordingly.

Maybe in the future, comments could be skipped, say by an exclusion regex ^>.

For example, in a mail, one rarely wants to reference all the URLs in the cited text. One can restrict the range to the first citation, but maybe the text continues afterward:


[0]

John Doe said:
>
> [1]

Links:

[0]: https://github.com/qadzek/vim-md-link/tree/dev
[1]: https://github.com/qadzek/vim-md-link/tree/main
qadzek commented 6 months ago

I updated the wiki accordingly.

Thanks. I am not sure if let s:body_range still needed, in both extensions? And for ft=gitcommit, is PutLinksBeforeComments() still required? let b:md_link_heading_before should take care of that part.

Maybe in the future, comments could be skipped, say by an exclusion regex ^>.

I will try to add that, but I will focus first on writing tests and documentation for the recently added features.

Konfekt commented 6 months ago

I am not sure if let s:body_range still needed, in both extensions?

Yes, for the mails only the links in the body need conversion, not in the subject header say. For commits, not those in comments.

And for ft=gitcommit, is PutLinksBeforeComments() still required? let b:md_link_heading_before should take care of that part.

As you explained above, b:md_link_heading_before looks from below, but the links are placed before the comments

qadzek commented 6 months ago

Yes, for the mails only the links in the body need conversion, not in the subject header say. For commits, not those in comments.

Oh, I see. I didn't realize there could be links in the subject header or in the comments.

Maybe in the future, comments could be skipped, say by an exclusion regex ^>.

Thank you for the suggestion. This feature has now been implemented. See :help b:md_link_skip_line for the details.

I have also performed several refactorings, to remove code duplication, and have added tests and documentation for the new features.

If you don't notice any bugs, I will merge it into main.

Konfekt commented 6 months ago

Great! I'll use this starting from now.

b:md_link_skip_line

You could suggest, or even default to, &commentstring

Konfekt commented 6 months ago

I am trying

let b:md_link_skip_line = '\V\^'..substitute(escape(&commentstring,'\'),'%s','\\.\\*', 'g')..'\$'

Seems a sensible default

Konfekt commented 6 months ago

I adapted the Wiki accordingly; the setup lines are becoming fewer

qadzek commented 6 months ago
let b:md_link_skip_line = '\V\^'..substitute(escape(&commentstring,'\'),'%s','\\.\\*', 'g')..'\$'

Seems a sensible default

Thanks for the suggestion. This has now been implemented: 1764dbc7d7f9842719574340c6642b5994797722

the setup lines are becoming fewer

That was the goal :smile: Now it should be pretty easy to extend this plugin to other filetypes.

Konfekt commented 5 months ago

This has now been implemented: https://github.com/qadzek/vim-md-link/commit/1764dbc7d7f9842719574340c6642b5994797722

While this is a good default, it is not perfect, as the commentstring uses spacing after the comment sign in, say shell or mail (and likely many other) files, and thus does not recognize, say ## or >>. Since most likely no comment signs depend on white space, maybe just strip the space before %s to make it more robust.

qadzek commented 5 months ago

There are tests for JavaScript comments // foo and Markdown comments <!-- bar --> and your regex seems to be able to handle both.

Konfekt commented 5 months ago

Yes, that will work. But /// will not work for Javascript, though still a comment. Well, it's a finesse, but I doesn't cost much calling substitute(b:md_link_skip_line, '\s', '', g)

qadzek commented 5 months ago

I wasn't entirely happy with the pre- and post-processing. It was working alright for converting links, but it wasn't compatible with :MdLinkPeek, :MdLinkJump, :MdLinkOpen and :MdLinkDelete. So I have removed the pre- and post-processing commands in the dev branch and added native support for plaintext links. Note that this is a breaking change. Would you be willing to give it a try?

Konfekt commented 5 months ago

Surely, but what do I have to try, and what to change? I am mainly using mdlink as documented in the wiki

Konfekt commented 5 months ago

Maybe you could indicate how the wiki needs to be updated?

qadzek commented 5 months ago

Surely, but what do I have to try, and what to change? I am mainly using mdlink as documented in the wiki

Just your normal workflow, e.g. converting links in a gitcommit buffer. You can give :MdLinkPeek, :MdLinkJump, :MdLinkOpen and :MdLinkDelete a try, these should now work in non-Markdown documents too.

Maybe you could indicate how the wiki needs to be updated?

MdLinkProcessConvert has been removed, so it should be replaced in your snippets, both in the if exists and in the autocmd. You are converting a range (s:body_range), so you should be able to replace it by MdLinkConvertRange. And MdLinkDeleteUnneededRefs has been renamed to MdLinkDelete.

Konfekt commented 5 months ago

MdLinkProcessConvert has been removed, so it should be replaced in your snippets, both in the if exists and in the autocmd. You are converting a range (s:body_range), so you should be able to replace it by MdLinkConvertRange.

Okay, so MdLinkProcessConvert is now MdLinkConvertRange? Feel free to adapt the Wiki

qadzek commented 5 months ago

I have updated the Wiki. I also had to change your aliases a bit. I can't really test those because I don't use aliases.

Okay, so MdLinkProcessConvert is now MdLinkConvertRange?

Before, only Markdown inline links [foo](http://bar.com) were supported. To handle plaintext links like foo http://bar.com, in e.g. gitcommit buffers, these plaintext links had to be pre-processed to Markdown links, then converted and afterwards needed post-processing.

Now, plaintext links are natively supported. This means all commands and functions related to pre- and post-processing have been removed. This is a breaking change.

MdLinkConvertRange exists to convert the links contained in a range of lines. To convert only a link on the current line, you might prefer MdLinkConvertSingle. Or, to convert the entire buffer, MdLinkConvertAll.

Konfekt commented 5 months ago

Okay, thank you. This is on the dev branch that I will switch to using your adaptions of the wiki.

qadzek commented 5 months ago

I will merge it into main if you don't notice anything weird. All features should now be available for both Markdown buffers and for other filetypes.

I will try to implement #6 next.

Thanks for testing the plugin.

qadzek commented 5 months ago

:MdLinkDelete has been replaced by :MdLinkReformat (another breaking change).

This new command is pretty powerful. It's especially useful for long documents, containing lots of links. It can renumber, merge and delete links. :help :MdLinkReformat contains some examples.

I have updated the Wiki accordingly.

qadzek commented 5 months ago

There is another major update, once again including some breaking changes (hopefully for the last time). It's available on the main branch.

I wanted to make the plugin more user-friendly, for newcomers to the plugin and for people not so experienced with Vim.

I wasn't entirely happy with the way the key bindings were set and the way to activate the plugin for additional filetypes, so I have made these changes:

I renamed the plugin as well, to less emphasize the Markdown part: it's now link.vim. And for filetypes other than Markdown, Links: is now the default heading, instead of ## Links. The Wiki has been updated.

Konfekt commented 5 months ago

Thank you for the heads-up. All the changes seem sensible. I hope to have caught all affected places in my local config

Konfekt commented 5 months ago

Regarding the autocmd, it looks fine, but somehow fires later than defining it in a ftplugin file. Therefore the previous check for the existence of :Link command became for the default file types

if !exists('g:loaded_link') | finish | endif
if exists('g:link_enabled_filetypes') && index(g:link_enabled_filetypes, &l:filetype) == -1 | finish | endif

respectively for the others

if !exists('g:link_enabled_filetypes') || index(g:link_enabled_filetypes, &l:filetype) == -1 | finish | endif
qadzek commented 5 months ago

I am not really sure how to implement your two snippets.

Regarding the autocmd, it looks fine, but somehow fires later than defining it in a ftplugin file.

I did notice that e.g. when opening foo.py and executing :set ft=markdown, the plugin isn't working, while I expected the autocmd would fire. Not sure if that's related.

if !exists('g:link_enabled_filetypes') || index(g:link_enabled_filetypes, &l:filetype) == -1 | finish | endif

g:link_enabled_filetypes not existing is allowed. Currently, if the user doesn't set g:link_enabled_filetypes, the plugin will be activated for the default filetypes: let s:default_enabled_filetypes = [ 'markdown', 'vimwiki', 'mail', 'text' ].

Konfekt commented 5 months ago

g:link_enabled_filetypes not existing is allowed. Currently, if the user doesn't set g:link_enabled_filetypes, the plugin will be activated for the default filetypes: let s:default_enabled_filetypes = [ 'markdown', 'vimwiki', 'mail', 'text' ].

Exactly, that's why for these filetypes additional checks are needed, as implemented above.

did notice that e.g. when opening foo.py and executing :set ft=markdown, the plugin isn't working, while I expected the autocmd would fire. Not sure if that's related.

That must be a separate issue; for me on Ubuntu 22.04 with 9.1.369 it fires when switching, say from TomL files.

I am not really sure how to implement your two snippets.

These are maybe useful for the snippets in the wiki, but now seem overkill

qadzek commented 5 months ago

That must be a separate issue; for me on Ubuntu 22.04 with 9.1.369 it fires when switching, say from TomL files.

It seems to fire on my system now too. Strange.

These are maybe useful for the snippets in the wiki, but now seem overkill

Ok. Feel free to make a PR if you feel otherwise.

I will close this issue, as it's getting long and the original issue has been solved. Don't hesitate to open a new one if anything comes up. Thanks again for your suggestions.