1

In GVIM, I'd like to save the current file with either \r\n or \n line endings. Depending on the opened file, the appropriate line endings should be chosen automatically. I had a look at https://unix.stackexchange.com/questions/79827/make-vim-act-as-notepad-on-mixed-line-endings but it doesn't quite do what I want.

My preferred behavior would be:

  • If the file in the current buffer contains NO carriage returns (\r), save the whole file with line feed (\n) line endings
  • If the file in the current buffer contains ANY carriage returns, save the whole file with \r\n line endings

However, I can't get quite this behavior. My current .vimrc looks like this:

" Some settings in my .vimrc that may be important
set encoding=utf-8
set list
set ffs=unix
source $VIMRUNTIME/mswin.vim
behave mswin


" My current status
function! CorrectLineEndings()
    " Carriage return
    let @g = "^M"
    " Testing for carriage return - doesn't work
    if match(getreg('<reg>'),@g) == @g
        " convert to \r\n line endings -> HOW?
    else
        " convert to \n line endings
        :%s/\r//g<CR>
    endif
endfunction
autocmd BufWriteCmd *.* call CorrectLineEndings()

There are two problems with this code - the testing for existing \r endings doesn't work and I'm not sure how to replace all line endings with \r\n. Any help would be greatly appreciated! Thanks in advance!

4
  • 1
    I might be wrong, but I'm pretty sure Vim does this mostly automatically (I rarely have to worry about it, and having edited some ff=dos files recently I noticed the correct line endings were used)
    – D. Ben Knoble
    Commented Sep 13, 2022 at 13:22
  • For me the problem is, when I open a file with Windows line endings. Then, when I press Enter in insert mode, it will insert only \n. So at the end, I'd need to insert \r manually in every line...
    – Lvn
    Commented Sep 13, 2022 at 13:24
  • +1 to D. Ben Knoble's comment. I'd also add that changing ffs default value is likely to mess the automagic behaviour of Vim.. Otherwise, I'd have listened FileRead event to set ff accordingly -- in case the default behaviour didn't work. Commented Sep 13, 2022 at 13:51
  • Thanks for your very quick replies! Indeed I can confirm that without setting ffs everything works as expected. However, I explicitly set this so that vim indicates to me if line endings are \r\n or just \n. This is important for me since I work in Linux and Windows in parallel. How would you make the decision in FileRead what line endings there are? With a match as indicated above or another way?
    – Lvn
    Commented Sep 13, 2022 at 13:57

1 Answer 1

1

Convert on reading

Try this:

set fileformats=unix,dos

augroup correct_endings
  autocmd!
  autocmd BufRead * call CorrectEndings()
augroup END

function! CorrectEndings()
  if &fileformat != 'dos' && search('\r$', 'cnw')
    set fileformat=dos
    %s/\r$
  endif
endfunction

This uses Vim's built in detection for all normal files, and then performs a search to check for files with mixed line endings, saving them as dos and then removing the excess carriage returns.

Convert on save

I include the current file's 'fileformat' in my 'statusline' so I can easily check what line endings the current file has without seeing ^Ms in the buffer. However, you've indicated in the comments that you specifically want to edit Windows-format files with 'fileformat' set to unix, so the carriage returns are visible in the file.

You can achieve the above like the below:

set ffs=unix

augroup correct_endings
  autocmd!
  autocmd BufWritePre * call CorrectEndings()
augroup END

function! CorrectEndings()
  if search('\r$', 'cnw')
    execute "g/\\v(\\r)@<!$/norm!A\<C-V>\<C-M>"
  endif
endfunction

This means that you always edit in ff=unix mode, but converts the line endings with a :global command if any CRNL endings are detected on save.

The global command looks a little hairy, but it's actually not that complicated:

g/\\v(\\r)@<!$/norm!A\<C-V>\<C-M>
g/            /                    # For every line where
  \\v                              # (using very magic)
             $                     # the end of the line
          @<!                      # is not preceded by
     (\\r)                         # a carriage return
               norm!               # Run the normal mode command
                    A              # to append at the end of the line
                     \<C-V>\<C-M>  # A literal carriage return character
2
  • @Lvn If you like the sound of the convert on read solution, but you really want dos line endings to be indicated inline in the buffer, then one way you can do this is to set 'list' and something like 'listchars=eol:M' but only when &ff=='dos'. I just tried this, and with appropriate highlighting to distinguish the list characters from the buffer contents, it works pretty well.
    – Rich
    Commented Sep 13, 2022 at 14:50
  • 1
    Wow, thank you very much for your effort! The second solution works like a charm - this is exactly what I wanted to do. I prefer this kind of line ending visualization since this looks like Notepad++ (which I worked extensively with in the past). Thanks also for the detailed explanation! It is very much appreciated.
    – Lvn
    Commented Sep 13, 2022 at 15:14

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.