Vim Tips Wiki
No edit summary
No edit summary
Line 6: Line 6:
 
*<tt>autocmd InsertEnter</tt> – trigger capitalization routine when user enters input mode
 
*<tt>autocmd InsertEnter</tt> – trigger capitalization routine when user enters input mode
 
 
The basic idea is fairly simple, but some logical manipulations are required to handle the beginnings and the endings of lines because of how exiting from input mode works (cursor sometimes moves back one character). Some further complexity arises from ensuring the input loop is triggered multiple times.
+
The basic idea is fairly simple, but some logical manipulations are required to handle the beginnings and the endings of lines because of how exiting from input mode works (cursor sometimes moves back one character). Some further complexity arises from ensuring the input loop isn't triggered multiple times.
   
 
==Script==
 
==Script==

Revision as of 01:41, 4 March 2012

Autocapitalizing the start of every sentence, a common function in cell phones, is unexpectedly nontrivial. Approaches such as mapping ". a" to ". A" in insert mode, using abbreviate, do not work, produce unexpected delay, or require extra keypresses. The strategy here is to interrupt insert mode on certain key presses, such as ., ?, !, etc and to catch subsequent keypresses with getchar().

The key Vim commands here are:

  • getchar – wait for a single keypress, return the numeric key code
  • startinsert – start insert mode after the interrupt as to not disrupt flow
  • autocmd InsertEnter – trigger capitalization routine when user enters input mode

The basic idea is fairly simple, but some logical manipulations are required to handle the beginnings and the endings of lines because of how exiting from input mode works (cursor sometimes moves back one character). Some further complexity arises from ensuring the input loop isn't triggered multiple times.

Script

The following script can be placed in your vimrc.

let g:SentBreakStr=[".","?","!"]
let g:SentBreak=[char2nr("."),char2nr("?"),char2nr("!")]
let g:NewLine=[13]
let g:WhiteSpace=[char2nr(" "),char2nr("	"),13]
let g:SBandWS=g:SentBreak + g:WhiteSpace

let g:Waiting=0
let g:DontTriggerAutocmd=0 
function! WaitForKey(capNext) "Capitalize next key
	if g:Waiting
		return
	endif
	let g:Waiting=1
	let capNext=a:capNext
	let isNL=(col(".")==1) 
	echo "-- AUTOCAP --"
	let input=getchar()
	while index(g:SBandWS,input)>=0 
		if isNL
			exe "normal! i" . nr2char(input)
		else 
			exe "normal! a" . nr2char(input)
		endif
		redraw
	    echo "-- AUTOCAP --"
		let capNext=(index(g:WhiteSpace,input)>=0)
		let isNL=(index(g:NewLine,input)>=0)
		let input=getchar()
	endwhile
	if capNext && input>=97 && input<=122  "a=97 z=122
		let input=input-32    "A=65 Z=90
	endif
	if isNL
		exe "normal! i" . nr2char(input)
	else
		exe "normal! a" . nr2char(input)
	endif
	normal! l
	if input != 27  "ESC=27
		let g:DontTriggerAutocmd=1
		if (col("$")-1)==col(".")
			startinsert!
		else
			startinsert
		endif
	endif
	let g:Waiting=0
endfu

function! InsertEnterCapitalize()
	let line=getline(".")
	if col(".")==1.    "Always capitalize the start of every line
		call WaitForKey(1)
		return
	endif
	let LineToCur=strpart(line,0,col(".")) "Capitalize if insertion after, eg, ?<space>
	let trimmed=substitute(LineToCur,'\s\+$','','') "Trim trailing whitespaces
	if index(g:SentBreakStr,trimmed[len(trimmed)-1])>=0
		call WaitForKey(len(LineToCur)-len(trimmed))
	endif
endfu

inoremap <silent> . .<Esc>:call WaitForKey(0)<CR>
inoremap <silent> ? ?<Esc>:call WaitForKey(0)<CR>
inoremap <silent> ! !<Esc>:call WaitForKey(0)<CR>
inoremap <silent> <CR> <CR><Esc>:call WaitForKey(1)<CR>
autocmd InsertEnter * redraw | if !g:DontTriggerAutocmd | call InsertEnterCapitalize() | endif
autocmd InsertLeave * let g:DontTriggerAutocmd=0 

Comments

Amazing. I have not read the code, but I guess it would always be active? Wouldn't you want a way to turn it on/off? JohnBeckett 01:00, March 4, 2012 (UTC)

Yeah, you're right, it's always active, and a future version should probably include an easy way to unmap and remove the autocommands, although that might increase the complexity even more. I use my vim on my cell phone a lot, and I'm not really sure how common that is and how useful people may find this, but it might be interesting to some as a way to use getchar() to create a "new mode" or a more interactive vim. -- q335r49