Vim Tips Wiki
(→‎Comments: correct line numbers)
(→‎Comments: Reply and thanks to Fritzophrenic's fix.)
Line 159: Line 159:
 
</source>
 
</source>
 
:::--[[User:Fritzophrenic|Fritzophrenic]] 18:17, November 16, 2011 (UTC)
 
:::--[[User:Fritzophrenic|Fritzophrenic]] 18:17, November 16, 2011 (UTC)
  +
::::Thanks [[User:Fritzophrenic|Fritzophrenic]] for looking into this so quickly! Yes, your updated script works for me (for all three fileformats). It seems that the switch from the :substitute to :normal did it.
  +
::::Regarding the :undojoin error: I think this is caused by a user undo operation immediately before the :write command. There's little one can do about it; how about suppressing the error via <code>:silent! undojoin</code>, so that we still get the benefit of :undojoin in most cases, and never cause an error?! -- [[User:Inkarkat|Inkarkat]] 12:49, November 17, 2011 (UTC)

Revision as of 12:49, 17 November 2011

Tip 1369 Printable Monobook Previous Next

created October 24, 2006 · complexity basic · author Yakov Lerner · version 6


This script causes Vim to 'preserve' a missing end-of-line at the end of a text file when you save it, if it was missing when the file was read. If the file was read with EOL at the end, it will be saved with one. If it was read without one, it will be saved without one. Some (arguably broken) Windows apps like this missing EOL, so this might be useful for Windows.

This works for all three line ending styles which Vim recognizes: DOS (Windows), Unix, and traditional Mac.

The script relies on Vim's recognition of a missing line ending, saved in the 'eol' option value. Even though 'eol' is set properly, it only has any effects when 'binary' is also set. So, the core of the script simply sets 'binary' temporarily. Unfortunately, when 'binary' is set, the file is always written with Unix-format line endings. So the rest of the script manually adds the correct line endings for the current format, and removes them again after writing.

:undojoin is used to keep these temporary line ending transformations transparent to the user.

" Preserve noeol (missing trailing eol) when saving file. In order
" to do this we need to temporarily 'set binary' for the duration of
" file writing, and for DOS line endings, add the CRs manually.
" For Mac line endings, also must join everything to one line since it doesn't
" use a LF character anywhere and 'binary' writes everything as if it were Unix.

" This works because 'eol' is set properly no matter what file format is used,
" even if it is only used when 'binary' is set.

augroup automatic_noeol
au!

au BufWritePre  * call TempSetBinaryForNoeol()
au BufWritePost * call TempRestoreBinaryForNoeol()

fun! TempSetBinaryForNoeol()
  let s:save_binary = &binary
  if ! &eol && ! &binary
    setlocal binary
    if &ff == "dos" || &ff == "mac"
      undojoin | silent 1,$-1s#$#\=nr2char(13)
    endif
    if &ff == "mac"
      let s:save_eol = &eol
      undojoin | %join!
      " mac format does not use a \n anywhere, so don't add one when writing in
      " binary (uses unix format always)
      setlocal noeol
    endif
  endif
endfun

fun! TempRestoreBinaryForNoeol()
  if ! &eol && ! s:save_binary
    if &ff == "dos"
      undojoin | silent 1,$-1s/\r$/
    elseif &ff == "mac"
      undojoin | %s/\r/\r/g
      let &l:eol = s:save_eol
    endif
    setlocal nobinary
  endif
endfun

augroup END

Comments

not work in vim 7.2

Same for me, using Vim 7.3.0 64-bit on Windows 7, with a file with fileformat=dos. TempRestoreBinaryForNoeol complains in line 3 with E486: Pattern not found: \r$, and the file is scattered with empty lines. It seems that the temporary insertion of CR characters actually inserted newline characters, and I could not make Vim do otherwise. This may be due to the fact that 'binary' is meant to be set during buffer loading (e.g. with :e ++bin), not set temporarily. I could only preserve the missing EOL through an external command, as outlined here. -- Inkarkat 14:35, November 16, 2011 (UTC)
Strange. I know I tested this, and I'm pretty sure it worked before. But I just reproduced the problem in Vim 7.3.338 on XP 64-bit. Without this script, I created a file as follows:
a
b
c
d
e
f
I then saved it as abc.txt, deleted the buffer with :bd, and reloaded with :e ++bin abc.txt. I removed the final ^M character, :set noeol, saved, and quit.
Then, I loaded the file with the script from this tip as the ONLY thing in the .vimrc: gvim -N --noplugin -u test.vim -i NONE abc.txt
The file loads properly in DOS format, with a [noeol] message.
Inserting a new line, "f2", just below "f", then saving and quitting, then reloading the file, does not show any error messages. However, the file now loads in Unix format, with empty lines between each line inserted previously. I.e.
a

b

c

d

e

f

f2

g
Again, I'm almost certain I tested this and found it working flawlessly. So either something broke or I didn't test very well on Windows, which doesn't make much sense, since I mostly use Vim in Windows.
--Fritzophrenic 16:57, November 16, 2011 (UTC)
This version appears to work, at least with DOS format. I was suspicious of the way we were adding the newline characters in line 21 of the new version, so I replaced it with a normal command as shown. I also added 'e' flags to the substitute commands to avoid those errors. However, I kept getting E790: undojoin is not allowed after undo on line 4 of TempRestoreBinaryForNoeol with the :undojoin commands in there. I don't like removing them, they really ought to be there, but there's got to be something wrong with the way they're being used.
" Preserve noeol (missing trailing eol) when saving file. In order
" to do this we need to temporarily 'set binary' for the duration of
" file writing, and for DOS line endings, add the CRs manually.
" For Mac line endings, also must join everything to one line since it doesn't
" use a LF character anywhere and 'binary' writes everything as if it were Unix.

" This works because 'eol' is set properly no matter what file format is used,
" even if it is only used when 'binary' is set.

augroup automatic_noeol
au!

au BufWritePre  * call TempSetBinaryForNoeol()
au BufWritePost * call TempRestoreBinaryForNoeol()

fun! TempSetBinaryForNoeol()
  let s:save_binary = &binary
  if ! &eol && ! &binary
    setlocal binary
    if &ff == "dos" || &ff == "mac"
      "undojoin | silent 1,$-1s#$#\=nr2char(13)
      if line('$') > 1
        undojoin | exec "silent 1,$-1normal! A\<C-V>\<C-M>"
      endif
    endif
    if &ff == "mac"
      undojoin | %join!
      " mac format does not use a \n anywhere, so we don't add one when writing
      " in binary (which uses unix format always). However, inside the outer
      " "if" statement, we already know that 'noeol' is set, so no special logic
      " is needed.
    endif
  endif
endfun

fun! TempRestoreBinaryForNoeol()
  if ! &eol && ! s:save_binary
    if &ff == "dos"
      if line('$') > 1
        "undojoin | silent 1,$-1s/\r$//e
        silent 1,$-1s/\r$//e
      endif
    elseif &ff == "mac"
      "undojoin | %s/\r/\r/ge
      %s/\r/\r/ge
    endif
    setlocal nobinary
  endif
endfun

augroup END
--Fritzophrenic 18:17, November 16, 2011 (UTC)
Thanks Fritzophrenic for looking into this so quickly! Yes, your updated script works for me (for all three fileformats). It seems that the switch from the :substitute to :normal did it.
Regarding the :undojoin error: I think this is caused by a user undo operation immediately before the :write command. There's little one can do about it; how about suppressing the error via :silent! undojoin, so that we still get the benefit of :undojoin in most cases, and never cause an error?! -- Inkarkat 12:49, November 17, 2011 (UTC)