JohnBeckett (talk | contribs) (Fix double-brace ending tip template + clean) |
JohnBeckett (talk | contribs) (Change to TipImported template + clean + integrate most comments to tip) |
||
Line 1: | Line 1: | ||
{{review}} |
{{review}} |
||
+ | {{TipImported |
||
− | {{Tip |
||
|id=1061 |
|id=1061 |
||
+ | |previous=1059 |
||
− | |title=SmartHome & SmartEnd over wrapped lines |
||
+ | |next=1063 |
||
|created=November 27, 2005 |
|created=November 27, 2005 |
||
|complexity=intermediate |
|complexity=intermediate |
||
Line 8: | Line 9: | ||
|version=6.0 |
|version=6.0 |
||
|rating=11/4 |
|rating=11/4 |
||
− | |text= |
||
}} |
}} |
||
− | |||
The Smart Home function allows the cursor to toggle between the start position and the first non-whitespace character position of the line. This is useful for skipping past indentation, whether it be tabs or spaces. Likewise, the Smart End function toggles past trailing whitespaces at the end of a line. |
The Smart Home function allows the cursor to toggle between the start position and the first non-whitespace character position of the line. This is useful for skipping past indentation, whether it be tabs or spaces. Likewise, the Smart End function toggles past trailing whitespaces at the end of a line. |
||
Line 17: | Line 16: | ||
The reason I am bringing this up again is because previous implementations of the Smart Home functionality have not worked well over lines that are wrapped. The implementation below works pleasingly well even when lines are wrapped, stepping through the wrapped lines by <Home> or <End> one by one. It also solves the "<Home> for one character on a line" insert mode problem, and is Replace mode friendly. |
The reason I am bringing this up again is because previous implementations of the Smart Home functionality have not worked well over lines that are wrapped. The implementation below works pleasingly well even when lines are wrapped, stepping through the wrapped lines by <Home> or <End> one by one. It also solves the "<Home> for one character on a line" insert mode problem, and is Replace mode friendly. |
||
− | Of course, cursor positioning can be achieved via vanilla Vim way: using 0, ^, g0, g$, g_, etc |
+ | Of course, cursor positioning can be achieved via vanilla Vim way: using <tt>0</tt>, <tt>^</tt>, <tt>g0</tt>, <tt>g$</tt>, <tt>g_</tt>, etc, but it's easier and faster when two keys take care of all of the functions. Especially over wrapped lines. |
<pre> |
<pre> |
||
"place in vimrc |
"place in vimrc |
||
− | nmap <silent><Home> :call SmartHome()<CR> |
+ | nmap <silent><Home> :call SmartHome("n")<CR> |
− | nmap <silent><End> :call SmartEnd()<CR> |
+ | nmap <silent><End> :call SmartEnd("n")<CR> |
− | imap <silent><Home> <C-r>=SmartHome()<CR> |
||
− | imap <silent><End> <C-r>=SmartEnd()<CR> |
||
− | vmap <silent><Home> <Esc>:call SmartHome()<CR>msgv`s |
||
− | vmap <silent><End> <Esc>:call SmartEnd()<CR>msgv`s |
||
− | |||
− | "smart home function |
||
− | function SmartHome() |
||
− | let curcol = col(".") |
||
− | "gravitate towards beginning for wrapped lines |
||
− | if curcol > indent(".") + 2 |
||
− | call cursor(0, col(".") - 1) |
||
− | endif |
||
− | if curcol == 1 || curcol > indent(".") + 1 |
||
− | if &wrap |
||
− | normal g^ |
||
− | else |
||
− | normal ^ |
||
− | endif |
||
− | else |
||
− | if &wrap |
||
− | normal g0 |
||
− | else |
||
− | normal 0 |
||
− | endif |
||
− | endif |
||
− | return "" |
||
− | endfunction |
||
− | |||
− | "smart end function |
||
− | function SmartEnd() |
||
− | let editmode = (mode() == "i" || mode() == "R") |
||
− | let curcol = col(".") |
||
− | let lastcol = editmode ? col("$") : col("$") - 1 |
||
− | " |
||
− | "gravitate towards ending for wrapped lines |
||
− | if curcol < lastcol - 1 |
||
− | call cursor(0, col(".") + 1) |
||
− | endif |
||
− | if curcol < lastcol |
||
− | if &wrap |
||
− | normal g$ |
||
− | else |
||
− | normal $ |
||
− | endif |
||
− | else |
||
− | normal g_ |
||
− | endif |
||
− | " |
||
− | "correct edit mode cursor position, put after current character |
||
− | if editmode |
||
− | call cursor(0, col(".") + 1) |
||
− | endif |
||
− | return "" |
||
− | endfunction |
||
− | </pre> |
||
− | |||
⚫ | |||
− | This is the most useful tip for a runaway from MS notepad/Visual Studio. But sometimes it confused me in the insert-select mode. When I selected something with shift and arrow keys then usually pressed <home> or <end> to move quickier, I got the "msgv`s" inserted in my text. |
||
− | So I spent a little time to fix the wrong action. My decision: |
||
− | |||
− | <pre> |
||
− | " substitute proposed visual mappings for following: |
||
− | vmap <silent><Home> <Esc>:call RestoreSelection(SmartHome())<CR> |
||
− | vmap <silent><End> <Esc>:call RestoreSelection(SmartEnd())<CR> |
||
− | " |
||
− | " Check if it's needed to restore visual selection |
||
− | function RestoreSelection(foo) |
||
− | if (mode() != 's' && mode() != 'S') |
||
− | normal msgv`s |
||
− | endif |
||
− | endfunction |
||
− | </pre> |
||
− | |||
− | The composition of the two functions is used only to execute them consequently in the only normal session. I don't know if it's the best way, but it works well. |
||
− | |||
− | ---- |
||
− | You know what? :) I found that bug soon after I posted this tip and fixed it, but I forgot to post the fix. The cause of the bug is due to Vim's confusion and inability to properly keep track of its modes. |
||
− | |||
− | The Insert-Select mode SmartHome/End is one instant where Vim is using almost all of its modes at one go! i.e. Insert, Select, Normal (within the function called as an expression register) |
||
− | |||
− | The fix is simple. Prefix <Esc> to "msgv`s" to make sure Vim is in Normal mode by that time. You would, however, lose your Insert-Select mode, after a SmartHome/End, to the Select mode. |
||
− | |||
− | vmap <silent><Home> <Esc>:call SmartHome()<CR><Esc>msgv`s |
||
− | vmap <silent><End> <Esc>:call SmartEnd()<CR><Esc>msgv`s |
||
− | |||
− | ---- |
||
− | I was a little bugged that the Insert-Visual/Select mode would be lost ;) |
||
− | Apparently, mode() does not work well here, and I decided to scrap it. |
||
− | You're right, Anatoli - "normal msgv`s" is the way to go. |
||
− | |||
− | '''Please disregard the tip above for the one below''' |
||
− | |||
− | <pre> |
||
− | "fixes the bug in Insert-Visual/Select mode |
||
− | nmap <silent><Home> :cal SmartHome("n")<CR> |
||
− | nmap <silent><End> :cal SmartEnd("n")<CR> |
||
imap <silent><Home> <C-r>=SmartHome("i")<CR> |
imap <silent><Home> <C-r>=SmartHome("i")<CR> |
||
imap <silent><End> <C-r>=SmartEnd("i")<CR> |
imap <silent><End> <C-r>=SmartEnd("i")<CR> |
||
− | vmap <silent><Home> <Esc>: |
+ | vmap <silent><Home> <Esc>:call SmartHome("v")<CR> |
− | vmap <silent><End> <Esc>: |
+ | vmap <silent><End> <Esc>:call SmartEnd("v")<CR> |
+ | |||
− | " |
||
− | "smart home function |
||
function SmartHome(mode) |
function SmartHome(mode) |
||
let curcol = col(".") |
let curcol = col(".") |
||
Line 144: | Line 46: | ||
endif |
endif |
||
endif |
endif |
||
− | " |
||
if a:mode == "v" |
if a:mode == "v" |
||
normal msgv`s |
normal msgv`s |
||
endif |
endif |
||
− | " |
||
return "" |
return "" |
||
endfunction |
endfunction |
||
− | "smart end function |
||
function SmartEnd(mode) |
function SmartEnd(mode) |
||
let curcol = col(".") |
let curcol = col(".") |
||
let lastcol = a:mode == "i" ? col("$") : col("$") - 1 |
let lastcol = a:mode == "i" ? col("$") : col("$") - 1 |
||
− | " |
||
"gravitate towards ending for wrapped lines |
"gravitate towards ending for wrapped lines |
||
if curcol < lastcol - 1 |
if curcol < lastcol - 1 |
||
call cursor(0, curcol + 1) |
call cursor(0, curcol + 1) |
||
endif |
endif |
||
− | " |
||
if curcol < lastcol |
if curcol < lastcol |
||
if &wrap |
if &wrap |
||
Line 171: | Line 68: | ||
normal g_ |
normal g_ |
||
endif |
endif |
||
− | " |
||
"correct edit mode cursor position, put after current character |
"correct edit mode cursor position, put after current character |
||
if a:mode == "i" |
if a:mode == "i" |
||
call cursor(0, col(".") + 1) |
call cursor(0, col(".") + 1) |
||
endif |
endif |
||
− | " |
||
if a:mode == "v" |
if a:mode == "v" |
||
normal msgv`s |
normal msgv`s |
||
endif |
endif |
||
− | " |
||
return "" |
return "" |
||
endfunction |
endfunction |
||
</pre> |
</pre> |
||
⚫ | |||
− | ---- |
||
This tip and [[VimTip315]] and gave me some great ideas for my own <Home> and <End> keys. |
This tip and [[VimTip315]] and gave me some great ideas for my own <Home> and <End> keys. |
||
− | I also studied some great ideas from URL mentioned in [[VimTip315]] |
+ | I also studied some great ideas from a URL mentioned in [[VimTip315]]: http://hermitte.free.fr/vim/ressources/vimfiles/plugin/homeLikeVC++_vim.html |
− | Try putting these in your |
+ | Try putting these in your vimrc. |
<pre> |
<pre> |
||
Line 218: | Line 112: | ||
endif |
endif |
||
endfun |
endfun |
||
+ | |||
− | " |
||
" map <End> to move to end of line |
" map <End> to move to end of line |
||
" if at end of line, move to end of window |
" if at end of line, move to end of window |
Revision as of 08:00, 20 December 2007
created November 27, 2005 · complexity intermediate · author Gerald Lai · version 6.0
The Smart Home function allows the cursor to toggle between the start position and the first non-whitespace character position of the line. This is useful for skipping past indentation, whether it be tabs or spaces. Likewise, the Smart End function toggles past trailing whitespaces at the end of a line.
This topic has been discussed before, see Smart home.
The reason I am bringing this up again is because previous implementations of the Smart Home functionality have not worked well over lines that are wrapped. The implementation below works pleasingly well even when lines are wrapped, stepping through the wrapped lines by <Home> or <End> one by one. It also solves the "<Home> for one character on a line" insert mode problem, and is Replace mode friendly.
Of course, cursor positioning can be achieved via vanilla Vim way: using 0, ^, g0, g$, g_, etc, but it's easier and faster when two keys take care of all of the functions. Especially over wrapped lines.
"place in vimrc nmap <silent><Home> :call SmartHome("n")<CR> nmap <silent><End> :call SmartEnd("n")<CR> imap <silent><Home> <C-r>=SmartHome("i")<CR> imap <silent><End> <C-r>=SmartEnd("i")<CR> vmap <silent><Home> <Esc>:call SmartHome("v")<CR> vmap <silent><End> <Esc>:call SmartEnd("v")<CR> function SmartHome(mode) let curcol = col(".") "gravitate towards beginning for wrapped lines if curcol > indent(".") + 2 call cursor(0, curcol - 1) endif if curcol == 1 || curcol > indent(".") + 1 if &wrap normal g^ else normal ^ endif else if &wrap normal g0 else normal 0 endif endif if a:mode == "v" normal msgv`s endif return "" endfunction function SmartEnd(mode) let curcol = col(".") let lastcol = a:mode == "i" ? col("$") : col("$") - 1 "gravitate towards ending for wrapped lines if curcol < lastcol - 1 call cursor(0, curcol + 1) endif if curcol < lastcol if &wrap normal g$ else normal $ endif else normal g_ endif "correct edit mode cursor position, put after current character if a:mode == "i" call cursor(0, col(".") + 1) endif if a:mode == "v" normal msgv`s endif return "" endfunction
Comments
This tip and VimTip315 and gave me some great ideas for my own <Home> and <End> keys.
I also studied some great ideas from a URL mentioned in VimTip315: http://hermitte.free.fr/vim/ressources/vimfiles/plugin/homeLikeVC++_vim.html
Try putting these in your vimrc.
" map <Home> to move to first word in line " if at first word, move to beginning of line " if at beginning of line, move to beginning of window " if at beginning of window, move to beginning of file nnoremap <silent> <Home> :call SmartHome("n")<CR> inoremap <silent> <Home> <C-O>:call SmartHome("i")<CR> vnoremap <silent> <Home> <Esc>:call SmartHome("v")<CR> function! SmartHome(mode) if col('.') == 1 if line('.') == 1 return elseif winline() == &scrolloff+1 normal! m`gg else normal! m`H0 endif elseif strpart(getline('.'), -1, col('.')) =~ '^\s\+$' normal! 0 else normal! ^ endif if a:mode == "v" normal! msgv`s endif endfun " map <End> to move to end of line " if at end of line, move to end of window " if at end of window, move to end of file nnoremap <silent> <End> :call SmartEnd("n")<CR> inoremap <silent> <End> <C-O>:call SmartEnd("i")<CR> vnoremap <silent> <End> <Esc>:call SmartEnd("v")<CR> function! SmartEnd(mode) if col('.') < col('$')-1 normal! $ elseif winline() < winheight(0) - &scrolloff normal! m`L$ else normal! m`G$ endif if a:mode == "v" normal! msgv`s endif endfun