" Set the pattern for trailing horizontal whitespace to match and remove. The " `[:space:]` character class suffices for almost everything in practice, but " at the time of writing it still only includes ASCII characters. I'm writing " this because I had a document with lines with a trailing NO-BREAK SPACE " (U+00A0) which `[:space:]` doesn't catch, so we'll round out the collection " by adding all the Unicode space characters, since that's easy to do. " " " archived: " " * U+0020: SPACE " * U+00A0: NO-BREAK SPACE " * U+1680: OGHAM SPACE MARK " * U+180E: MONGOLIAN VOWEL SEPARATOR " * U+2000: EN QUAD " * U+2001: EM QUAD " * U+2002: EN SPACE " * U+2003: EM SPACE " * U+2004: THREE-PER-EM SPACE " * U+2005: FOUR-PER-EM SPACE " * U+2006: SIX-PER-EM SPACE " * U+2007: FIGURE SPACE " * U+2008: PUNCTUATION SPACE " * U+2009: THIN SPACE " * U+200A: HAIR SPACE " * U+200B: ZERO WIDTH SPACE " * U+202F: NARROW NO-BREAK SPACE " * U+205F: MEDIUM MATHEMATICAL SPACE " * U+3000: IDEOGRAPHIC SPACE " * U+FEFF: ZERO WIDTH NO-BREAK SPACE " let s:pattern \ = '[' \ . '[:space:]' \ . '\u0020' \ . '\u00A0' \ . '\u1680' \ . '\u180E' \ . '\u2000-\u200B' \ . '\u202F' \ . '\u205F' \ . '\u3000' \ . '\uFEFF' \ . ']\+$' " Wrapper function to strip both horizontal and vertical trailing whitespace, " return the cursor to its previous position, and report changes function! strip_trailing_whitespace#(start, end) abort " Save cursor position let pos = getpos('.') " Whether we made changes let changed = 0 " If we're going to the end, strip vertical space; we do this first so we " don't end up reporting having trimmed lines that we deleted if a:end == line('$') let vertical = s:StripVertical() let changed = changed || vertical > 0 endif " Strip horizontal space let horizontal = s:StripHorizontal(a:start, a:end) let changed = changed || horizontal > 0 " Return the cursor call setpos('.', pos) " Report what changed let msg = horizontal.' trimmed' if exists('vertical') let msg = msg.', '.vertical.' deleted' endif echomsg msg " Return whether anything changed return changed endfunction " Strip horizontal trailing whitespace, return the number of lines changed function! s:StripHorizontal(start, end) abort " Start a count of lines trimmed let stripped = 0 " Iterate through buffer let num = a:start while num <= line('$') && num <= a:end " If the line has trailing whitespace, strip it off and bump the count let line = getline(num) if line =~# s:pattern call setline(num, substitute(line, s:pattern, '', '')) let stripped = stripped + 1 endif " Bump for next iteration let num = num + 1 endwhile " Return the number of lines trimmed return stripped endfunction " Strip trailing vertical whitespace, return the number of lines changed function! s:StripVertical() abort " Store the number of the last line we found with non-whitespace characters " on it; start at 1 because even if it's empty it's never trailing let eof = 1 " Iterate through buffer let num = 1 while num <= line('$') " If the line has any non-whitespace characters in it, update our pointer " to the end of the file text let line = getline(num) if line =~# '\S' let eof = num endif " Bump for next iteration let num = num + 1 endwhile " Get the number of lines to delete; if there are any, build a range and " remove them with :delete, suppressing its normal output (we'll do it) let stripped = line('$') - eof if stripped let range = (eof + 1).',$' silent execute range.'delete' endif " Return the number of lines deleted return stripped endfunction