aboutsummaryrefslogblamecommitdiff
path: root/autoload/strip_trailing_whitespace.vim
blob: 17dec7a33984e6582f7ec7ddfa35161ac102b913 (plain) (tree)












































                                                                                             

                                                                             
                                                      

                        
                       















                                                                           
                       













                                                                          
                                             









                                                                          

                                                            













                                                                        
                                 
































                                                                             
" 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.
"
" <https://jkorpela.fi/chars/spaces.html>
" archived: <http://web.archive.org/web/20230318105634/https://jkorpela.fi/chars/spaces.html>
"
" * 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