问题
I have written this script that replaces many spaces around the cursor with one space. This however doesn't work when I use it with no spaces around the cursor. It seems to me that Vim doesn't replace on a zero-width match.
function JustOneSpace()
let save_cursor = getpos(".")
let pos = searchpos(' \+', 'bc')
s/\s*\%#\s*/ /e
let save_cursor[2] = pos[1] + 1
call setpos('.', save_cursor)
endfunction
nmap <space> :call JustOneSpace()<cr>
Here are a few examples (pipe | is cursor):
This line
hello | world
becomes
hello |world
But this line
hello wo|rld
doesn't become
hello wo |rld
Update: By changing the function to the following it works for the examples above.
function JustOneSpace()
let save_cursor = getpos(".")
let pos = searchpos(' *', 'bc')
s/\s*\%#\s*/ /e
let save_cursor[2] = pos[1] + 1
call setpos('.', save_cursor)
endfunction
This line
hello |world
becomes
hello w|orld
The problem is that the cursors moves to the next character. It should stay in the same place.
Any pointers and or tips?
回答1:
I think that the only problem with your script is that the position saving doesn't seem correct. You can essentially do what you are trying to do with:
:s/\s*\%#\s*/ /e
which is identical to the (correct) code in your question. You could simply map this with:
:nmap <space> :s/\s*\%#\s*/ /e<CR>
If you want to save the position, it gets a little more complicated. Probably the best bet is to use something like this:
function! JustOneSpace()
" Get the current contents of the current line
let current_line = getline(".")
" Get the current cursor position
let cursor_position = getpos(".")
" Generate a match using the column number of the current cursor position
let matchRE = '\(\s*\)\%' . cursor_position[2] . 'c\s*'
" Find the number of spaces that precede the cursor
let isolate_preceding_spacesRE = '^.\{-}' . matchRE . '.*$'
let preceding_spaces = substitute(current_line, isolate_preceding_spacesRE, '\1', "")
" Modify the line by replacing with one space
let modified_line = substitute(current_line, matchRE, " ", "")
" Modify the cursor position to handle the change in string length
let cursor_position[2] -= len(preceding_spaces) - 1
" Set the line in the window
call setline(".", modified_line)
" Reset the cursor position
call setpos(".", cursor_position)
endfunction
Most of that is comments, but the key thing is that you look at the length of the line before and after the substitution and decide on the new cursor position accordingly. You could do this with your method by comparing len(getline(".")) before and after if you prefer.
Edit
If you want the cursor to end after the space character, modify the line:
let cursor_position[2] -= len(current_line) - len(modified_line)
such that it looks like this:
let cursor_position[2] -= (len(current_line) - len(modified_line)) - 1
Edit (2)
I've changed the script above to consider your comments such that the cursor position is only adjusted by the number of spaces before the cursor position. This is done by creating a second regular expression that extracts the spaces preceding the cursor (and nothing else) from the line and then adjusting the cursor position by the number of spaces.
回答2:
I don't use vim, but if you want to match zero or more spaces, shouldn't you be using ' *' instead of ' \+' ?
EDIT: re the cursor positioning problem: what you're doing now is setting the position at the beginning of the whitespace before you do the substitution, then moving it forward one position so it's after the space. Try setting it at the end of the match instead, like this:
search(' *', 'bce')
That way, any additions or removals will occur before the cursor position. In most editors, the cursor position automatically moves to track such changes. You shouldn't need to do any of that getpos/setpos stuff.
回答3:
This function is based on Al's answer.
function JustOneSpace()
" Get the current contents of the current line
let current_line = getline(".")
" Get the current cursor position
let cursor_position = getpos(".")
" Generate a match using the column number of the current cursor position
let matchre = '\s*\%' . cursor_position[2] . 'c\s*'
let pos = match(current_line, matchre) + 2
" Modify the line by replacing with one space
let modified_line = substitute(current_line, matchre, " ", "")
" Modify the cursor position to handle the change in string length
let cursor_position[2] = pos
" Set the line in the window
call setline(".", modified_line)
" Reset the cursor position
call setpos(".", cursor_position)
endfunction
Instead using the difference between the normal and the modified line, I find the position of the first space that will match the regular expression of the substitution. Then I set the cursor position to that position + 1.
回答4:
This simple one I use does almost the same:
nnoremap <leader>6 d/\S<CR>
Put the cursor till where you want to remove the spaces and it removes all the spaces after the cursor and the next text.
来源:https://stackoverflow.com/questions/1228100/substituting-zero-width-match-in-vim-script