0

I want to create a function that I can call to replace a specific string pattern with md5sum of current buffer.

I have no experience writing VimL before and can't figure out the way to do it.

This is what I roughly have in my mind:

" Calculate text hash and replace the old one
function! HashBuffer()
  :redir @a
  " Will this work? Is this a buffer contents or previously save file?
  :!md5sum %
  :redir END

  " replace with @a (How?)
  %s/%%%HASH.*%%%/%%%HASH-XXXXXXXXX%%%/
endfunction

Usage example:

Let say I have a file, I am working on

# %%%HASH-XXXXXXXXX%%%

the content of file ....
....
..

I want the hash to be calculate when I manually :call HashBuffer()

# %%%HASH-1d4171a91c31af2c59acb89a042836e1%%%

the content of file ....
....
..

It's obviously not working but I don't have any idea what to do next.

  • 3
    When you save the file, calculate the md5sum and insert it into the file, you have to save the file again. This changes the file, so you need to calculate the m5sum and insert it into the file. This is an endless loop. – Ralf May 20 at 10:05
  • Oh, never think of that. OK, I change my goal to just only when I manually call the function. – wizzup May 20 at 10:06
1

You could try the following:

"" calculate text hash and replace the old one
function! HashBuffer()
  let md5=trim(system('md5sum ' . expand('%') . ' | cut "-d " -f1'))
  %s/%%%\<HASH.*%%%/\='%%%' . 'HASH-' . md5 . '%%%'/
endfunction

A few notes:

  • expand('%') returns the current file name.
  • | cut "-d " -f1 only returns the md5sum, but cuts of the file name
  • trim(...) is used to trim the trailing newline (trim() was introduced in Vim 8.0.1630 or so, if you have an older versions I could provide a replacement)

The interesting part is the regex. This is written more complicated than needed, so if you ever run the function on its source code, the source code will not be destroyed. Lets simplify it first:

%s/%%%\<HASH.*%%%/\='%%%' . 'HASH-' . md5 . '%%%'/
%s/%%%HASH.*%%%/\='%%%HASH-' . md5 . '%%%'/

The \< was removed, it just matches the "start of a word". See :h /\<.

The interesting part is the replacement String:

\='%%%HASH-' . md5 . '%%%'

The leading \= is called "sub-replace-special". When the replacement string starts with this, the rest is interpreted as a VimScript expression. Here we just concatenate the leading string with the MD5 and the trailer.

  • Work like a charm! – wizzup May 20 at 11:21
1

EDIT After our discussion in chat here is the final function you need:

function! HashBuffer()
    let hashedContent = system("md5sum " . expand('%')) 
    execute '%s/HASH.*/HASH-' . split(hashedContent, ' ')[0]
endfunction

First we use :h system() to get the result of the external command md5sum <filename> in a variable and we use :h expand() to get the actual filename.

In my system md5sum returns a result of the form {hash} {filename}, as you don't want the filename we use :h split() to get only the hash.

Finally we use :h execute() to build the susbtitution command you want to use with the content of the variable.


I think you only need one command:

:%!md5sum

Which basically filters the content of the full buffer (%) through the external command (!) md5sum.

In my setup the file

bar
buzz
fizz

Becomes:

e70ad0b62c1f427ae49ce73b9c09155c  -

And if you want to make that a function, the following should work:

function! HashBuffer()
    %!md5sum
endfunction

You probably want to read

  • I'm not sure I understand what you mean: In a vim without configuration using :%!{cmd} replaces the content of the current buffer with the output of the command. No new buffer is created and you don't need to do any substitution. – statox May 20 at 9:46
  • Let us continue this discussion in chat. – wizzup May 20 at 9:57

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.