Vim Tips Wiki
(→‎Comments: Reply and thanks to Fritzophrenic's fix.)
(Vim 7.4.785 adds this feature natively with an option)
Tag: sourceedit
 
(6 intermediate revisions by 3 users not shown)
Line 3: Line 3:
 
|previous=1366
 
|previous=1366
 
|next=1370
 
|next=1370
|created=October 24, 2006
+
|created=2006
 
|complexity=basic
 
|complexity=basic
 
|author=Yakov Lerner
 
|author=Yakov Lerner
Line 11: Line 11:
 
|category2=
 
|category2=
 
}}
 
}}
  +
{{Deprecated|[https://github.com/vim/vim/releases/tag/v7.4.785 Vim 7.4.785] adds the 'fixeol' option that can be disabled to automatically preserve any missing EOL at the end of the file. This script becomes uneccessary for Vim 7.4.785 and later.}}
{{dodgy|Problems have been noted in this script for recent Vim versions; see [[#Comments]] for a partial solution.}}
 
 
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 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.
   
Line 20: Line 20:
 
{{help|prefix=no|:undojoin}} is used to keep these temporary line ending transformations transparent to the user.
 
{{help|prefix=no|:undojoin}} is used to keep these temporary line ending transformations transparent to the user.
   
 
<source lang='vim'>
<pre>
 
 
" Preserve noeol (missing trailing eol) when saving file. In order
 
" Preserve noeol (missing trailing eol) when saving file. In order
 
" to do this we need to temporarily 'set binary' for the duration of
 
" to do this we need to temporarily 'set binary' for the duration of
Line 31: Line 31:
   
 
augroup automatic_noeol
 
augroup automatic_noeol
au!
+
au!
 
au BufWritePre * call <SID>TempSetBinaryForNoeol()
 
 
au BufWritePost * call <SID>TempRestoreBinaryForNoeol()
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
 
augroup END
</pre>
 
 
==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. <code>TempRestoreBinaryForNoeol</code> 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 <code>:e ++bin</code>), not set temporarily. I could only preserve the missing EOL through an external command, as outlined [http://stackoverflow.com/questions/1050640/vim-disable-automatic-newline-at-end-of-file here]. -- [[User:Inkarkat|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:
 
<pre>
 
a
 
b
 
c
 
d
 
e
 
f
 
</pre>
 
::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: <tt>gvim -N --noplugin -u test.vim -i NONE abc.txt</tt>
 
::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.
 
<pre>
 
a
 
 
b
 
 
c
 
 
d
 
 
e
 
 
f
 
 
f2
 
 
g
 
</pre>
 
::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.
 
::--[[User:Fritzophrenic|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 <tt><span style='color: white; background-color: red; font-weight: bold;'>E790: undojoin is not allowed after undo</span></tt> 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.
 
<source lang='vim'>
 
" 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()
+
function! s:TempSetBinaryForNoeol()
 
let s:save_binary = &binary
 
let s:save_binary = &binary
 
if ! &eol && ! &binary
 
if ! &eol && ! &binary
  +
let s:save_view = winsaveview()
 
setlocal binary
 
setlocal binary
 
if &ff == "dos" || &ff == "mac"
 
if &ff == "dos" || &ff == "mac"
"undojoin | silent 1,$-1s#$#\=nr2char(13)
 
 
if line('$') > 1
 
if line('$') > 1
 
undojoin | exec "silent 1,$-1normal! A\<C-V>\<C-M>"
 
undojoin | exec "silent 1,$-1normal! A\<C-V>\<C-M>"
Line 135: Line 50:
 
" mac format does not use a \n anywhere, so we don't add one when writing
 
" 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
 
" in binary (which uses unix format always). However, inside the outer
" "if" statement, we already know that 'noeol' is set, so no special logic
+
" if statement, we already know that 'noeol' is set, so no special logic
 
" is needed.
 
" is needed.
 
endif
 
endif
 
endif
 
endif
  +
endfunction
endfun
 
   
fun! TempRestoreBinaryForNoeol()
+
function! s:TempRestoreBinaryForNoeol()
 
if ! &eol && ! s:save_binary
 
if ! &eol && ! s:save_binary
 
if &ff == "dos"
 
if &ff == "dos"
 
if line('$') > 1
 
if line('$') > 1
"undojoin | silent 1,$-1s/\r$//e
+
" Sometimes undojoin gives errors here, even when it shouldn't.
  +
" Suppress them for now...if you can figure out and fix them instead,
silent 1,$-1s/\r$//e
 
  +
" please update http://vim.wikia.com/wiki/VimTip1369
 
silent! undojoin | silent 1,$-1s/\r$//e
 
endif
 
endif
 
elseif &ff == "mac"
 
elseif &ff == "mac"
"undojoin | %s/\r/\r/ge
+
" Sometimes undojoin gives errors here, even when it shouldn't.
  +
" Suppress them for now...if you can figure out and fix them instead,
%s/\r/\r/ge
 
  +
" please update http://vim.wikia.com/wiki/VimTip1369
 
silent! undojoin | silent %s/\r/\r/ge
 
endif
 
endif
 
setlocal nobinary
 
setlocal nobinary
  +
call winrestview(s:save_view)
 
endif
 
endif
  +
endfunction
endfun
 
 
augroup END
 
 
</source>
 
</source>
  +
:::--[[User:Fritzophrenic|Fritzophrenic]] 18:17, November 16, 2011 (UTC)
 
  +
==Related scripts==
::::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)
 
  +
* The {{script|id=4550|text=PreserveNoEOL plugin}} uses the above pure Vimscript implementation to automatically preserve no EOL on all or certain opened files, but it also comes with alternative strategies that use postprocessing in Python or Perl to remove the trailing EOL character after the save, which is probably more robust.
  +
 
==Comments==
 
: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)
  +
::You're welcome! I really don't know why that was needed. I took your suggestion for suppressing errors and updated the tip with the new script. The odd thing was, I wasn't doing any undo operation prior to the write. The sequence was:
  +
:#Open the file without an eol
  +
:#Modify a line in the file (by using <C-A> to increment a number on the line)
  +
:#Save the file, get an error
  +
::So, I've added code comments in case anybody figures it out down the road.
 
::--[[User:Fritzophrenic|Fritzophrenic]] 18:10, November 17, 2011 (UTC)

Latest revision as of 16:04, 2 September 2015

Tip 1369 Printable Monobook Previous Next

created 2006 · complexity basic · author Yakov Lerner · version 6


This tip is deprecated for the following reasons:

Vim 7.4.785 adds the 'fixeol' option that can be disabled to automatically preserve any missing EOL at the end of the file. This script becomes uneccessary for Vim 7.4.785 and later.

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 <SID>TempSetBinaryForNoeol()
  au BufWritePost * call <SID>TempRestoreBinaryForNoeol()
augroup END

function! s:TempSetBinaryForNoeol()
  let s:save_binary = &binary
  if ! &eol && ! &binary
    let s:save_view = winsaveview()
    setlocal binary
    if &ff == "dos" || &ff == "mac"
      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
endfunction

function! s:TempRestoreBinaryForNoeol()
  if ! &eol && ! s:save_binary
    if &ff == "dos"
      if line('$') > 1
        " Sometimes undojoin gives errors here, even when it shouldn't.
        " Suppress them for now...if you can figure out and fix them instead,
        " please update http://vim.wikia.com/wiki/VimTip1369
        silent! undojoin | silent 1,$-1s/\r$//e
      endif
    elseif &ff == "mac"
      " Sometimes undojoin gives errors here, even when it shouldn't.
      " Suppress them for now...if you can figure out and fix them instead,
      " please update http://vim.wikia.com/wiki/VimTip1369
      silent! undojoin | silent %s/\r/\r/ge
    endif
    setlocal nobinary
    call winrestview(s:save_view)
  endif
endfunction

Related scripts[]

  • The PreserveNoEOL plugin uses the above pure Vimscript implementation to automatically preserve no EOL on all or certain opened files, but it also comes with alternative strategies that use postprocessing in Python or Perl to remove the trailing EOL character after the save, which is probably more robust.

Comments[]

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)
You're welcome! I really don't know why that was needed. I took your suggestion for suppressing errors and updated the tip with the new script. The odd thing was, I wasn't doing any undo operation prior to the write. The sequence was:
  1. Open the file without an eol
  2. Modify a line in the file (by using <C-A> to increment a number on the line)
  3. Save the file, get an error
So, I've added code comments in case anybody figures it out down the road.
--Fritzophrenic 18:10, November 17, 2011 (UTC)