From 1b76276ea831ee5cb657e0261ff5fe472626118f Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Fri, 20 Jul 2018 20:31:13 +1200 Subject: Overhaul for new version Refactor completely, mostly for clarity of code rather than speed. Also accept a range and deal with vertical whitespace trimming intelligently based on its value. --- plugin/strip_trailing_whitespace.vim | 152 +++++++++++++++++++++-------------- 1 file changed, 90 insertions(+), 62 deletions(-) (limited to 'plugin') diff --git a/plugin/strip_trailing_whitespace.vim b/plugin/strip_trailing_whitespace.vim index de08fae..75438d9 100644 --- a/plugin/strip_trailing_whitespace.vim +++ b/plugin/strip_trailing_whitespace.vim @@ -1,6 +1,7 @@ " -" strip_trailing_whitespace.vim: User-defined key mapping to strip trailing -" whitespace in the whole document. +" strip_trailing_whitespace.vim: User command to strip both horizontal and +" vertical whitespace in a buffer, with optional range, reporting both +" accurately and restoring the cursor afterwards. " " Author: Tom Ryder " License: Same as Vim itself @@ -8,88 +9,115 @@ if exists('g:loaded_strip_trailing_whitespace') || &compatible finish endif -if v:version < 600 +if !has('user_commands') || v:version < 600 finish endif let g:loaded_strip_trailing_whitespace = 1 -" Define function for stripping whitespace -function! s:StripTrailingWhitespace() +" Wrapper function to strip both horizontal and vertical trailing whitespace, +" return the cursor to its previous position, and report changes +function s:Strip(start, end) abort - " Line number of last line that had non-whitespace characters on it - let l:cutoff = 1 + " Save cursor position + let l:line = line('.') + let l:col = col('.') - " Tracking lines trimmed between non-whitespace lines and then totalling - let l:trimmed_buffer = 0 - let l:trimmed = 0 + " Strip horizontal space + let l:horizontal = s:StripHorizontal(a:start, a:end) + let l:msg = l:horizontal.' trimmed' + let l:changed = l:horizontal > 0 - " Line number of the file's last line - let l:last = line('$') + " If we're going to the end, strip vertical space + if a:end == line('$') + let l:vertical = s:StripVertical() + let l:msg = l:msg.', '.l:vertical.' deleted' + let l:changed = l:changed || l:vertical > 0 + endif - " Iterate over the lines - let l:li = 1 - while l:li <= l:last + " Return the cursor + call s:Cursor(l:line, l:col) - " Get the line text - let l:line = getline(l:li) + " Report what changed + echomsg l:msg - " If the current line contains trailing whitespace, substitute it out - if l:line =~# '\s\+$' - call setline(l:li, substitute(l:line, '\s\+$', '', '')) - let l:trimmed_buffer = l:trimmed_buffer + 1 - endif + " Return whether anything changed + return l:changed + +endfunction - " If this line has any non-whitespace characters on it, update our cutoff - " point using its index, and push the trimmed lines we've counted since - " the last non-whitespace line onto the trimmed total - if l:line =~# '\S' || l:last == 1 - let l:cutoff = l:li - let l:trimmed = l:trimmed + l:trimmed_buffer - let l:trimmed_buffer = 0 +" Strip horizontal trailing whitespace, return the number of lines changed +function s:StripHorizontal(start, end) abort + + " Start a count of lines trimmed + let l:count = 0 + + " Iterate through buffer + let l:num = 1 + while l:num <= line('$') + + " If the line has trailing whitespace, strip it off and bump the count + let l:line = getline(l:num) + if l:line =~# '\s\+$' + call setline(l:num, substitute(l:line, '\s*$', '', '')) + let l:count = l:count + 1 endif - " Increment the line counter for the next iteration - let l:li = l:li + 1 + " Bump for next iteration + let l:num = l:num + 1 endwhile - " If the last non-whitespace line was before the last line proper, we can - " delete all lines after it - let l:deleted = 0 - if l:cutoff < l:last - - " Get the current line and column so we can return to it - " (Yes I know about winsaveview() and winrestview(); I want this to work - " even on very old versions of Vim if possible) - let l:cursor_line = line('.') - let l:cursor_col = col('.') - - " Delete the rest of the lines, which will move the cursor - silent execute l:cutoff + 1 . ',$ delete _' - - " Return the cursor to the saved position (Vim 6.0 fallback) - if exists('*cursor') - call cursor(l:cursor_line, l:cursor_col) - else - execute 'normal! ' - \ . l:cursor_line . 'G' - \ . l:cursor_col . '|' + " Return the number of lines trimmed + return l:count + +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 l:eof = 1 + + " Iterate through buffer + let l:num = 1 + while l:num <= line('$') + + " If the line has any non-whitespace characters in it, update our pointer + " to the end of the file text + let l:line = getline(l:num) + if l:line =~# '\S' + let l:eof = l:num endif - " Record the number of lines deleted - let l:deleted = l:last - l:cutoff + " Bump for next iteration + let l:num = l: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 l:count = line('$') - l:eof + if l:count + let l:range = (l:eof + 1).',$' + silent execute l:range.'delete' endif - " Print what we did - echomsg l:trimmed . ' trimmed, ' . l:deleted . ' deleted' + " Return the number of lines deleted + return l:count - " Return the number of affected lines - return l:trimmed + l:deleted +endfunction +" Position the cursor; use cursor() if we have it, :normal if not (Vim 6.0) +function s:Cursor(line, col) abort + if exists('*cursor') + return cursor(a:line, a:col) + else + execute 'normal! '.a:line.'G'.a:col.'|' + return 1 + endif endfunction -" Create mapping proxy to the function just defined -nnoremap - \ (StripTrailingWhitespace) - \ :call StripTrailingWhitespace() +" User command for the above +command! -range=% StripTrailingWhitespace + \ call Strip(, ) -- cgit v1.2.3