ziulfer
ziulfer

Reputation: 1369

Change title case (the first letters of all words capitalized) in Vim

There are a lot of solutions around to change the case of the entire word, but what I want is change the case in a way like this:

From "What is the meaning of life? A new proposal" I want: "What is The Meaning of Life? A new Proposal" if that's too hard this: "What Is The Meaning of Life? A New Proposal" would be enough.

Upvotes: 5

Views: 1584

Answers (2)

Logan Frederick
Logan Frederick

Reputation: 31

There are a couple ways to capitalize every word.

:s/\w\+/\u\0/g appears in the :substitute examples. This is viewable after the documentation for :help s/\\\\

A less efficient but more intuitive approach is to use gUl with the cursor on the first letter of the first word. Then repeat w. for all remaining words.

Upvotes: 3

Ingo Karkat
Ingo Karkat

Reputation: 172590

This is commonly called title case; there is a visual mode mapping solution from the Vim Tips Wiki

function! TwiddleCase(str)
    if a:str ==# toupper(a:str)
        let result = tolower(a:str)
    elseif a:str ==# tolower(a:str)
        let result = substitute(a:str,'\(\<\w\+\>\)', '\u\1', 'g')
    else
        let result = toupper(a:str)
    endif
    return result
endfunction
vnoremap ~ y:call setreg('', TwiddleCase(@"), getregtype(''))<CR>gv""Pgvl

Alternative

If you want to implement a more robust solution yourself, my TextTransform plugin can help with setting up x{motion}, xx and {Visual}x mappings, so you just need to write the actual transformation function.

Edit: Ah well, couldn't stop myself, here is an implementation that is able to handle exceptions:

if ! exists('g:TitleCase_ExceptionPattern')
    " Source:
    "   http://grammar.yourdictionary.com/capitalization/rules-for-capitalization-in-titles.html
    let g:TitleCase_ExceptionPattern = '^\%(amid\|a[nst]\?\|and\|are\|but\|by\|down\|for\|from\|i[ns]\|into\|like\|near\|new\|nor\|old\|o[fnr]\|off\|onto\|over\|past\|per\|plus\|than\|the\|to\|up\|upon\|via\|with\)$'
endif
function! TitleCase( text )
    return substitute(a:text, '\(^\<\w\+\>\)\|\<\w\+\>', '\=s:TitleCase(submatch(0), ! empty(submatch(1)))', 'g')
endfunction
function! s:TitleCase( word, isException )
    if ! a:isException && a:word =~# g:TitleCase_ExceptionPattern
        return tolower(a:word)
    endif

    return substitute(a:word, '\w', '\u&', '')
endfunction
call TextTransform#MakeMappings('', '<Leader>s~', 'TitleCase')

And here's a variant that cycles through Title Case with exceptions → Title Case all (without exceptions) → lowercase:

function! subs#TitleCase#Do( text )
    let l:wordExpr =  '\(^\<\w\+\>\|\<\w\+\>$\)\|\<\w\+\>'
    let l:text = substitute(a:text, l:wordExpr, '\=s:TitleCase(submatch(0), ! empty(submatch(1)))', 'g')
    if l:text ==# a:text
        let l:text = substitute(a:text, l:wordExpr, '\=s:TitleCase(submatch(0), 1)', 'g')
        if l:text ==# a:text
            let l:text = substitute(a:text, l:wordExpr, '\L&', 'g')
        endif
    endif
    return l:text
endfunction

Upvotes: 4

Related Questions