1" To use this file, add this line to your ~/.vimrc:, w/o the dquote
2" source /path/to/kde/sources/kdesdk/scripts/kde-devel-vim.vim
3"
4" For CreateChangeLogEntry() : If you don't want to re-enter your
5" Name/Email in each vim session then make sure to have the viminfo
6" option enabled in your ~/.vimrc, with the '!' flag, enabling persistent
7" storage of global variables. Something along the line of
8" set   viminfo=%,!,'50,\"100,:100,n~/.viminfo
9" should do the trick.
10"
11" To make use of the ,ll and ,lg shortcuts you need to have the files
12" GPLHEADER and LGPLHEADER in your home directory. Their content will be
13" copied as license header then.
14
15" Don't include these in filename completions
16set suffixes+=.lo,.o,.moc,.la,.closure,.loT
17
18" Search for headers here
19set path=.,/usr/include,/usr/local/include,
20if $QTDIR != ''
21    let &path = &path . $QTDIR . '/include/,'
22    let &path = &path . $QTDIR . '/include/Qt/,'
23    let &path = &path . $QTDIR . '/include/QtCore/,'
24    let &path = &path . $QTDIR . '/include/Qt3Support/,'
25    let &path = &path . $QTDIR . '/include/QtAssistant/,'
26    let &path = &path . $QTDIR . '/include/QtDBus/,'
27    let &path = &path . $QTDIR . '/include/QtDesigner/,'
28    let &path = &path . $QTDIR . '/include/QtGui/,'
29    let &path = &path . $QTDIR . '/include/QtNetwork/,'
30    let &path = &path . $QTDIR . '/include/QtOpenGL/,'
31    let &path = &path . $QTDIR . '/include/QtSql/,'
32    let &path = &path . $QTDIR . '/include/QtSvg/,'
33    let &path = &path . $QTDIR . '/include/QtTest/,'
34    let &path = &path . $QTDIR . '/include/QtUiTools/,'
35    let &path = &path . $QTDIR . '/include/QtXml/,'
36endif
37if $KDEDIR != ''
38    let &path = &path . $KDEDIR . '/include/,'
39endif
40if $KDEDIRS != ''
41    let &path = &path . substitute( $KDEDIRS, '\(:\|$\)', '/include,', 'g' )
42endif
43set path+=,
44
45" Use makeobj to build
46set mp=makeobj
47
48" If TagList is Loaded then get a funny statusline
49" Only works if kde-devel-vim.vim is loaded after taglist.
50" Droping this script in ~/.vim/plugin works fine
51if exists('loaded_taglist')
52    let Tlist_Process_File_Always=1
53    set statusline=%<%f:[\ %{Tlist_Get_Tag_Prototype_By_Line()}\ ]\ %h%m%r%=%-14.(%l,%c%V%)\ %P
54endif
55
56" Insert tab character in whitespace-only lines, complete otherwise
57inoremap <Tab> <C-R>=SmartTab()<CR>
58
59if !exists("DisableSmartParens")
60" Insert a space after ( or [ and before ] or ) unless preceded by a matching
61" paren/bracket or space or inside a string or comment. Comments are only
62" recognized as such if they start on the current line :-(
63inoremap ( <C-R>=SmartParens( '(' )<CR>
64inoremap [ <C-R>=SmartParens( '[' )<CR>
65inoremap ] <C-R>=SmartParens( ']', '[' )<CR>
66inoremap ) <C-R>=SmartParens( ')', '(' )<CR>
67endif
68
69" Insert an #include statement for the current/last symbol
70inoremap <F5> <C-O>:call AddHeader()<CR>
71
72" Insert a forward declaration for the current/last symbol
73inoremap <S-F5> <C-O>:call AddForward()<CR>
74
75" Switch between header and implementation files on ,h
76nmap <silent> ,h :call SwitchHeaderImpl()<CR>
77nmap <silent> ,p :call SwitchPrivateHeaderImpl()<CR>
78
79" Comment selected lines on ,c in visual mode
80vmap ,c :s,^,//X ,<CR>:noh<CR>
81" Uncomment selected lines on ,u in visual mode
82vmap ,u :s,^//X ,,<CR>
83
84" Insert an include guard based on the file name on ,i
85nmap ,i :call IncludeGuard()<CR>
86
87" Insert license headers at the top of the file
88nmap ,lg :call LicenseHeader( "GPL" )<CR>
89nmap ,ll :call LicenseHeader( "LGPL" )<CR>
90nmap ,lm :call LicenseHeader( "MIT" )<CR>
91
92" Expand #i to #include <.h> or #include ".h". The latter is chosen
93" if the character typed after #i is a dquote
94" If the character is > #include <> is inserted (standard C++ headers w/o .h)
95iab #i <C-R>=SmartInclude()<CR>
96
97" Insert a stripped down CVS diff
98iab DIFF <Esc>:call RunDiff()<CR>
99
100" mark 'misplaced' tab characters
101set listchars=tab:�\ ,trail:�
102set list
103
104set incsearch
105
106function! SetCodingStyle()
107    if &syntax == 'cmake'
108        call SmartParensOff()
109        set sw=3
110        set ts=3
111        set et
112        set tw=0
113        return
114    endif
115    if ( &syntax !~ '^\(c\|cpp\|java\)$' )
116        return
117    endif
118    "the path for the file
119    let pathfn = expand( '%:p:h' )
120    if pathfn =~ 'nmm'
121        call SmartParensOff()
122        inoremap ( <C-R>=SpaceBetweenKeywordAndParens()<CR>
123        let g:need_brace_on_next_line = '\<\(class\|namespace\|struct\)\>'
124        let g:need_brace_on_same_line = '\<\(if\|else\|while\|switch\|do\|enum\|for\|try\|catch\)\>'
125        set sw=4
126        set ts=4
127        set noet
128        set tw=100
129    elseif pathfn =~ 'xine-lib'
130        call SmartParensOff()
131        let g:need_brace_on_next_line = '\<\(class\|namespace\|struct\)\>'
132        let g:need_brace_on_same_line = '\<\(if\|else\|while\|switch\|do\|foreach\|forever\|enum\|for\|try\|catch\)\>'
133        set sw=2
134        set sts=2
135        set ts=8
136        set noet
137        "set tw=100
138    elseif pathfn =~ 'kdemultimedia\/juk'
139        call SmartParensOff()
140        let g:need_brace_on_next_line = '\<\(class\|namespace\|struct\|if\|else\|while\|switch\|do\|foreach\|forever\|enum\|for\|try\|catch\)\>'
141        let g:need_brace_on_same_line = ''
142        set sw=4
143        set sts=4
144        set et
145        "set tw=100
146    elseif pathfn =~ 'kdenetwork\/kopete'
147        call SmartParensOff()
148        let g:need_brace_on_next_line = '\<\(class\|namespace\|struct\|if\|else\|while\|switch\|do\|foreach\|forever\|enum\|for\|try\|catch\)\>'
149        let g:need_brace_on_same_line = ''
150        set sw=4
151        set sts=4
152        set noet
153        "set tw=100
154    else   " kdelibs/kf5/qt coding style
155        call SmartParensOff()
156        inoremap ( <C-R>=SpaceBetweenKeywordAndParens()<CR>
157        let g:need_brace_on_next_line = '\<\(class\|namespace\|struct\)\>'
158        let g:need_brace_on_same_line = '\<\(if\|else\|while\|switch\|do\|foreach\|forever\|enum\|for\|try\|catch\)\>'
159        set sw=4
160        set sts=4
161        set et
162        "set tw=100
163    endif
164    if ( !exists("g:noautobrace") )
165        call EnableSmartLineBreak()
166    endif
167endfunction
168
169function! DisableSmartLineBreak()
170    iunmap <CR>
171    iuna else
172endfunction
173function! EnableSmartLineBreak()
174    if exists("*pumvisible")
175        inoremap <CR> <C-R>=pumvisible() ? "\<lt>CR>" : "\<lt>ESC>:call SmartLineBreak()\<lt>CR>a\<lt>CR>"<CR>
176    else
177        inoremap <CR> <ESC>:call SmartLineBreak()<CR>a<CR>
178    endif
179    iab else <C-R>=SmartElse()<CR>
180endfunction
181
182function! SmartElse()
183    "let next = nr2char( getchar( 0 ) )
184    let prefix = ''
185    if strlen(g:need_brace_on_same_line) > 0 && 'else' =~ g:need_brace_on_same_line
186        if getline('.') =~ '^\s*$'
187            if getline(line('.') - 1) =~ '}$'
188                let prefix = prefix . "\<ESC>kmMjdd`MA "
189            elseif getline(line('.') - 1) =~ '}\s*$'
190                let prefix = prefix . "\<ESC>kmMjdd`MA"
191            endif
192        endif
193    endif
194    return prefix . "else\<Right>"
195endfunction
196
197" automatic indenting is required for SmartLineBreak to work correctly
198filetype indent on
199
200function! CreateMatchLine()
201    let linenum = line( '.' )
202    let current_line = getline( linenum )
203    " don't do magic if the cursor isn't at the end of the line or if it's
204    " inside a // comment
205    if col( '.' ) != strlen( current_line ) || match( current_line, '//' ) >= 0
206        return ''
207    endif
208    " remove whitespace at the end
209    if match( current_line, '\s\+$' ) >= 0
210        :execute ':s/\s*$//'
211        " the following is needed if return '' is called
212        :execute "normal $"
213    endif
214    let current_line = getline( linenum )
215    " remove all /* */ comments
216    let current_line = substitute( current_line, '/\*.\{-}\*/', '', 'g' )
217    " remove all strings
218    let current_line = substitute( current_line, "'[^']*'", '', 'g' )
219    let current_line = substitute( current_line, '"\(\\"\|[^"]\)*"', '', 'g' )
220    " remove all ( )
221    while current_line =~ '(.*)'
222        let current_line = substitute( current_line, '([^()]*)', '', 'g' )
223    endwhile
224    " prepend earlier lines until we find a ; or {
225    while linenum > 1 && current_line !~ ';' && current_line !~ '{.\+$'
226        let linenum = linenum - 1
227        let prev_line = getline(linenum)
228        if synIDattr(synID(linenum, 1, 1), "name") == 'cComment' "inside a /* */ comment at the beginning of the line
229            if stridx(prev_line, '*/') == -1
230                " next line please
231                let prev_line = ''
232            else
233                " remove everything before */
234                let prev_line = substitute(prev_line, '^.*\*/', '*/', '')
235            endif
236        endif
237        " remove // comment
238        let prev_line = substitute(prev_line, '//.*$', '', '' )
239        " concatenate the lines with a space in between
240        let current_line = prev_line.' '.current_line
241        " remove all /* */ comments
242        let current_line = substitute( current_line, '/\*.\{-}\*/', '', 'g' )
243        " remove all strings
244        let current_line = substitute( current_line, "'[^']*'", '', 'g' )
245        let current_line = substitute( current_line, '"\(\\"\|[^"]\)*"', '', 'g' )
246        " remove all ( )
247        while current_line =~ '(.*)'
248            let current_line = substitute( current_line, '([^()]*)', '', 'g' )
249        endwhile
250    endwhile
251    " remove everything until the last ;
252    let current_line = substitute( current_line, '^.*;', '', '' )
253    " remove everything until the last { which is not at the end of the line
254    let current_line = substitute( current_line, '^.*{\(.\+\)$', '\1', '' )
255    " remove all [ ]
256    while current_line =~ '\[.*\]'
257        let current_line = substitute( current_line, '\[[^\[\]]*\]', '', 'g' )
258    endwhile
259    " if <CR> was pressed inside ( ), [ ] or /* */ don't add braces
260    if current_line =~ '[(\[]' || current_line =~ '/\*'
261        return ''
262    endif
263    return current_line
264endfunction
265
266function! AddClosingBrace(current_line)
267    if a:current_line =~ '\<enum\|class\|struct\>'
268        :execute "normal o};\<ESC>k"
269    elseif a:current_line =~ '\<namespace\>'
270        let namespace = substitute( a:current_line, '^.*namespace\s\+', '', '' )
271        let namespace = substitute( namespace, '\s.*$', '', '' )
272        :execute "normal o} // namespace " . namespace . "\<ESC>k"
273    else
274        :execute "normal o}\<ESC>k"
275    endif
276endfunction
277
278function! SmartLineBreak()
279    if synIDattr(synID(line("."), col("."), 1), "name") == 'cComment' "inside a /* */ comment at the point where the line break occurs
280        return
281    endif
282    let match_line = CreateMatchLine()
283    if match_line == ''
284        return
285    endif
286
287    let match_position1 = -1
288    let match_position2 = -1
289    if strlen(g:need_brace_on_same_line) > 0
290        let match_position1 = match(match_line, g:need_brace_on_same_line)
291        if match_position1 > 0
292            while strpart(match_line, match_position1 - 1, 1) == '#'
293                let old_position = match_position1
294                let match_position1 = match(match_line, g:need_brace_on_same_line, match_position1 + 1)
295                if match_position1 == -1
296                    if strpart(match_line, old_position, 2) == 'if'
297                        :execute "normal o#endif\<ESC>k$"
298                    endif
299                    return
300                endif
301            endwhile
302        endif
303    endif
304    if strlen(g:need_brace_on_next_line) > 0 && match_position1 == -1
305        let match_position2 = match(match_line, g:need_brace_on_next_line)
306        if match_position2 > 0
307            while strpart(match_line, match_position2 - 1, 1) == '#'
308                let old_position = match_position2
309                let match_position2 = match(match_line, g:need_brace_on_same_line, match_position2 + 1)
310                if match_position2 == -1
311                    if strpart(match_line, old_position, 2) == 'if'
312                        :execute "normal o#endif\<ESC>k$"
313                    endif
314                    return
315                endif
316            endwhile
317        endif
318    endif
319
320    if match_position1 > -1
321        if match_line =~ '}\s*else\>'
322            " make sure else is on the same line as the closing brace
323            if getline('.') =~ '^\s*else'
324                if getline(line('.') - 1) =~ '}$'
325                    :execute "normal kA \<ESC>J"
326                elseif getline(line('.') - 1) =~ '}\s*$'
327                    :execute "normal kJ"
328                endif
329            endif
330        endif
331        while getline('.') =~ '^\s*{$'
332            " opening brace is on its own line: move it up
333            :execute "normal kJ"
334        endwhile
335        if match_line =~ '{$'
336            if getline('.') =~ '[^ ]{$'
337                :execute ':s/{$/ {/'
338            endif
339        else
340            :execute ':s/$/ {/'
341        endif
342        call AddClosingBrace(match_line)
343    elseif getline('.') =~ '^\s*{$'
344        call AddClosingBrace('')
345    elseif match_position2 > -1
346        if match_line =~ '{$'
347            :execute ':s/\s*{$//'
348        endif
349        :execute "normal o{"
350        call AddClosingBrace(match_line)
351    endif
352    :execute "normal $"
353endfunction
354
355function! SmartParensOn()
356    inoremap ( <C-R>=SmartParens( '(' )<CR>
357    inoremap [ <C-R>=SmartParens( '[' )<CR>
358    inoremap ] <C-R>=SmartParens( ']', '[' )<CR>
359    inoremap ) <C-R>=SmartParens( ')', '(' )<CR>
360endfunction
361
362function! SmartParensOff()
363    if strlen(mapcheck('[','i')) > 0
364        iunmap (
365        iunmap [
366        iunmap ]
367        iunmap )
368    endif
369endfunction
370
371function! SmartTab()
372    let col = col('.') - 1
373    if !col || getline('.')[col-1] !~ '\k'
374        return "\<Tab>"
375    else
376        return "\<C-P>"
377    endif
378endfunction
379
380function! SmartParens( char, ... )
381    if ! ( &syntax =~ '^\(c\|cpp\|java\)$' )
382        return a:char
383    endif
384    let s = strpart( getline( '.' ), 0, col( '.' ) - 1 )
385    if s =~ '//'
386        return a:char
387    endif
388    let s = substitute( s, '/\*\([^*]\|\*\@!/\)*\*/', '', 'g' )
389    let s = substitute( s, "'[^']*'", '', 'g' )
390    let s = substitute( s, '"\(\\"\|[^"]\)*"', '', 'g' )
391    if s =~ "\\([\"']\\|/\\*\\)"
392        return a:char
393    endif
394    if a:0 > 0
395        if strpart( getline( '.' ), col( '.' ) - 3, 2 ) == a:1 . ' '
396            return "\<BS>" . a:char
397        endif
398        if strpart( getline( '.' ), col( '.' ) - 2, 1 ) == ' '
399            return a:char
400        endif
401        return ' ' . a:char
402    endif
403    if !exists("g:DisableSpaceBeforeParen")
404        if a:char == '('
405            if strpart( getline( '.' ), col( '.' ) - 3, 2 ) == 'if' ||
406              \strpart( getline( '.' ), col( '.' ) - 4, 3 ) == 'for' ||
407              \strpart( getline( '.' ), col( '.' ) - 6, 5 ) == 'while' ||
408              \strpart( getline( '.' ), col( '.' ) - 7, 6 ) == 'switch'
409                return ' ( '
410            endif
411        endif
412    endif
413    return a:char . ' '
414endfunction
415
416function! SpaceBetweenKeywordAndParens()
417    if ! ( &syntax =~ '^\(c\|cpp\|java\)$' )
418        return '('
419    endif
420    let s = strpart( getline( '.' ), 0, col( '.' ) - 1 )
421    if s =~ '//'
422        " text inside a comment
423        return '('
424    endif
425    let s = substitute( s, '/\*\([^*]\|\*\@!/\)*\*/', '', 'g' )
426    let s = substitute( s, "'[^']*'", '', 'g' )
427    let s = substitute( s, '"\(\\"\|[^"]\)*"', '', 'g' )
428    if s =~ "\\([\"']\\|/\\*\\)"
429        " text inside a string
430        return '('
431    endif
432    if a:0 > 0
433        if strpart( getline( '.' ), col( '.' ) - 3, 2 ) == a:1 . ' '
434            return "\<BS>" . a:char
435        endif
436        if strpart( getline( '.' ), col( '.' ) - 2, 1 ) == ' '
437            return a:char
438        endif
439        return ' ' . a:char
440    endif
441    if strpart( getline( '.' ), col( '.' ) - 3, 2 ) == 'if' ||
442        \strpart( getline( '.' ), col( '.' ) - 4, 3 ) == 'for' ||
443        \strpart( getline( '.' ), col( '.' ) - 6, 5 ) == 'while' ||
444        \strpart( getline( '.' ), col( '.' ) - 7, 6 ) == 'switch' ||
445        \strpart( getline( '.' ), col( '.' ) - 8, 7 ) == 'foreach' ||
446        \strpart( getline( '.' ), col( '.' ) - 8, 7 ) == 'forever'
447        return ' ('
448    endif
449    return '('
450endfunction
451
452function! SwitchHeaderImpl()
453    let privateheaders = '_p\.\([hH]\|hpp\|hxx\)$'
454    let headers = '\.\([hH]\|hpp\|hxx\)$'
455    let impl = '\.\([cC]\|cpp\|cc\|cxx\)$'
456    let fn = expand( '%' )
457    if fn =~ privateheaders
458        let list = glob( substitute( fn, privateheaders, '.*', '' ) )
459    elseif fn =~ headers
460        let list = glob( substitute( fn, headers, '.*', '' ) )
461    elseif fn =~ impl
462        let list = glob( substitute( fn, impl, '.*', '' ) )
463    endif
464    while strlen( list ) > 0
465        let file = substitute( list, "\n.*", '', '' )
466        let list = substitute( list, "[^\n]*", '', '' )
467        let list = substitute( list, "^\n", '', '' )
468        if ( ( fn =~ headers || fn =~ privateheaders ) && file =~ impl ) || ( fn =~ impl && file =~ headers )
469            call AskToSave()
470            execute( "edit " . file )
471            return
472        endif
473    endwhile
474    if ( fn =~ headers )
475        call AskToSave()
476        if exists( "$implextension" )
477            let file = substitute( fn, headers, '.' . $implextension, '' )
478        else
479            let file = substitute( fn, headers, '.cpp', '' )
480        endif
481        " check for modified state of current buffer and if modified ask:
482        " save, discard, cancel
483        execute( 'edit '.file )
484        call append( 0, "#include \"".fn."\"" )
485        call append( 2, "// vim: sw=4 sts=4 et tw=100" )
486        execute( "set sw=4" )
487        execute( "set sts=4" )
488        execute( "set et" )
489        "execute( "set tw=100" )
490    elseif fn =~ impl
491        call AskToSave()
492        let file = substitute( fn, impl, '.h', '' )
493        execute( "edit ".file )
494    endif
495endfunction
496
497function! SwitchPrivateHeaderImpl()
498    let privateheaders = '_p\.\([hH]\|hpp\|hxx\)$'
499    let headers = '\.\([hH]\|hpp\|hxx\)$'
500    let impl = '\.\([cC]\|cpp\|cc\|cxx\)$'
501    let fn = expand( '%' )
502    if fn =~ privateheaders
503        let list = glob( substitute( fn, privateheaders, '.*', '' ) )
504    elseif fn =~ headers
505        let list = glob( substitute( fn, headers, '_p.*', '' ) )
506    elseif fn =~ impl
507        let list = glob( substitute( fn, impl, '_p.*', '' ) )
508    endif
509    while strlen( list ) > 0
510        let file = substitute( list, "\n.*", '', '' )
511        let list = substitute( list, "[^\n]*", '', '' )
512        let list = substitute( list, "^\n", '', '' )
513        if ( fn =~ privateheaders && file =~ impl ) || ( fn =~ impl && file =~ privateheaders ) || ( fn =~ headers && file =~ privateheaders )
514            call AskToSave()
515            execute( "edit " . file )
516            return
517        endif
518    endwhile
519    if ( fn =~ privateheaders )
520        call AskToSave()
521        if exists( "$implextension" )
522            let file = substitute( fn, privateheaders, '.' . $implextension, '' )
523        else
524            let file = substitute( fn, privateheaders, '.cpp', '' )
525        endif
526        " check for modified state of current buffer and if modified ask:
527        " save, discard, cancel
528        execute( 'edit '.file )
529        call append( 0, "#include \"".fn."\"" )
530        call append( 2, "// vim: sw=4 ts=4 noet" )
531        execute( "set sw=4" )
532        execute( "set ts=4" )
533    elseif fn =~ impl
534        let file = substitute( fn, impl, '_p.h', '' )
535        call CreatePrivateHeader( file )
536    elseif fn =~ headers
537        let file = substitute( fn, headers, '_p.h', '' )
538        call CreatePrivateHeader( file )
539    endif
540endfunction
541
542function! AskToSave()
543    if &modified
544        let yesorno = input("Save changes before switching file? [Y/n]")
545        if yesorno == 'y' || yesorno == '' || yesorno == 'Y'
546            :execute 'w'
547            return 1
548        else
549            return 0
550        endif
551    endif
552    return 1
553endfunction
554
555function! CreatePrivateHeader( privateHeader )
556    let privateheaders = '_p\.\([hH]\|hpp\|hxx\)$'
557    let headers = '\.\([hH]\|hpp\|hxx\)$'
558    let impl = '\.\([cC]\|cpp\|cc\|cxx\)$'
559    let fn = expand( '%' )
560    if fn =~ headers
561        let className = ClassNameFromHeader()
562    elseif fn =~ impl
563        let className = ClassNameFromImpl()
564    endif
565
566    if AskToSave() && fn =~ headers
567        :normal gg
568        " check whether a Q_DECLARE_PRIVATE is needed
569        let dp = search( '\(^\|\s\+\)Q_DECLARE_PRIVATE\s*(\s*'.className.'\s*)' )
570        if dp == 0 "nothing found
571            call search( '^\s*class\s\+\([A-Za-z0-9]\+_EXPORT\s\+\)\?[A-Za-z_]\+\s*\(:\s*[,\t A-Za-z_]\+\)\?\s*\n\?\s*{' )
572            call search( '{' )
573            let @c = className
574            if getline(line('.')+1) =~ 'Q_OBJECT'
575                :normal joQ_DECLARE_PRIVATE(c)
576            else
577                :normal oQ_DECLARE_PRIVATE(c)
578            endif
579            :execute 'w'
580        endif
581    endif
582    execute( "edit ".a:privateHeader )
583    let privateClassName = className . 'Private'
584    let header = substitute( a:privateHeader, privateheaders, '.h', '' )
585
586    call IncludeGuard()
587    " FIXME: find out what license to use
588    call LicenseHeader( "LGPL" )
589    let @h = header
590    let @p = privateClassName
591    let @c = className
592    :normal Gkko
593#include "h"
594
595class p
596Q_DECLARE_PUBLIC(c)
597protected:
598c *q_ptr;
599endfunction
600
601function! ClassNameFromHeader()
602    :normal gg
603    call search( '^\s*class\s\+\([A-Za-z0-9]\+_EXPORT\s\+\)\?[A-Za-z_]\+\s*\(:\s*[,\t A-Za-z_]\+\)\?\s*\n\?\s*{' )
604    "\zs and \ze mark start and end of the matching
605    return matchstr( getline('.'), '\s\+\zs\w\+\ze\s*\(:\|{\|$\)' )
606endfunction
607
608function! ClassNameFromImpl()
609    :normal gg
610    call search( '\s*\([A-Za-z_]\+\)::\1\s*(' )
611    :normal "cye
612    return @c
613endfunction
614
615function! IncludeGuard()
616    let guard = toupper( substitute( expand( '%' ), '[\./]', '_', 'g' ) )
617    call append( '^', '#define ' . guard )
618    +
619    call append( '^', '#ifndef ' . guard )
620    call append( '$', '#endif // ' . guard )
621    +
622endfunction
623
624function! LicenseHeader( license )
625    let filename = $HOME . "/" . a:license . "HEADER"
626    execute ":0r " . filename
627"   call append( 0, system( "cat " . filename ) )
628endfunction
629
630function! SmartInclude()
631    let next = nr2char( getchar( 0 ) )
632    if next == '"'
633        return "#include \".h\"\<Left>\<Left>\<Left>"
634    endif
635    if next == '>'
636        return "#include <>\<Left>"
637    endif
638    return "#include <.h>\<Left>\<Left>\<Left>"
639endfunction
640
641function! MapIdentHeader( ident )
642    let header = tolower(substitute(a:ident, '::', '/', 'g')).'.h'
643    if a:ident =~ 'Private$'
644        let header = substitute(header, 'private', '_p', '')
645    endif
646    " always prefer the headers in the same directory
647    let check = header
648    let slash = 1
649    while slash != -1
650        if filereadable( check )
651            return '"' . check . '"'
652        endif
653        let slash = match( check, '/' )
654        let check = strpart( check, slash + 1 )
655    endwhile
656    let check = tolower(substitute(a:ident, '::', '/', 'g')).'_p.h'
657    let slash = 1
658    while slash != -1
659        if filereadable(check)
660            return '"' . check . '"'
661        endif
662        let slash = match(check, '/')
663        let check = strpart(check, slash + 1)
664    endwhile
665
666    " Qt stuff
667    if a:ident =~ '^Q[A-Z]'
668        " let's try to find the module
669        let module = ''
670        if $QTDIR != ''
671            if filereadable($QTDIR.'/include/QtCore/'.a:ident)
672                let module = 'QtCore/'
673            elseif filereadable($QTDIR.'/include/QtGui/'.a:ident)
674                let module = 'QtGui/'
675            elseif filereadable($QTDIR.'/include/Qt3Support/'.a:ident)
676                let module = 'Qt3Support/'
677            elseif filereadable($QTDIR.'/include/QtAssistant/'.a:ident)
678                let module = 'QtAssistant/'
679            elseif filereadable($QTDIR.'/include/QtDBus/'.a:ident)
680                let module = 'QtDBus/'
681            elseif filereadable($QTDIR.'/include/QtDesigner/'.a:ident)
682                let module = 'QtDesigner/'
683            elseif filereadable($QTDIR.'/include/QtNetwork/'.a:ident)
684                let module = 'QtNetwork/'
685            elseif filereadable($QTDIR.'/include/QtOpenGL/'.a:ident)
686                let module = 'QtOpenGL/'
687            elseif filereadable($QTDIR.'/include/QtSql/'.a:ident)
688                let module = 'QtSql/'
689            elseif filereadable($QTDIR.'/include/QtSvg/'.a:ident)
690                let module = 'QtSvg/'
691            elseif filereadable($QTDIR.'/include/QtTest/'.a:ident)
692                let module = 'QtTest/'
693            elseif filereadable($QTDIR.'/include/QtUiTools/'.a:ident)
694                let module = 'QtUiTools/'
695            elseif filereadable($QTDIR.'/include/QtXml/'.a:ident)
696                let module = 'QtXml/'
697            endif
698        endif
699        return '<'.module.a:ident.'>'
700    elseif a:ident == 'qDebug' ||
701          \a:ident == 'qWarning' ||
702          \a:ident == 'qCritical' ||
703          \a:ident == 'qFatal'
704        return '<QtCore/QtDebug>'
705    elseif a:ident == 'Q_EXPORT_PLUGIN2'
706        return '<QtCore/QtPlugin>'
707    elseif a:ident =~ 'Q_DECLARE_INTERFACE'
708        return '<QtCore/QObject>'
709    elseif a:ident =~ '^QT_VERSION' ||
710          \a:ident =~ '^Q_\(W\|O\)S_' ||
711          \a:ident =~ '^Q_CC_' ||
712          \a:ident =~ '^Q_.*STRUCTOR_FUNCTION$' ||
713          \a:ident =~ '^qu\?int' ||
714          \a:ident =~ '^Q_.*_RESOURCE$' ||
715          \a:ident == 'qreal' ||
716          \a:ident == 'qAbs' ||
717          \a:ident == 'qRound' ||
718          \a:ident == 'qRound64' ||
719          \a:ident == 'qMin' ||
720          \a:ident == 'qMax' ||
721          \a:ident == 'qBound' ||
722          \a:ident == 'qVersion' ||
723          \a:ident == 'qSharedBuild' ||
724          \a:ident == 'Q_UNUSED' ||
725          \a:ident == 'Q_ASSERT' ||
726          \a:ident == 'qInstallMsgHandler' ||
727          \a:ident == 'Q_GLOBAL_STATIC' ||
728          \a:ident == 'Q_GLOBAL_STATIC_WITH_ARGS' ||
729          \a:ident == 'qFuzzyCompare' ||
730          \a:ident == 'qIsNull' ||
731          \a:ident == 'qSwap' ||
732          \a:ident =~ 'Q_DECLARE_\(FLAGS\|OPERATORS_FOR_FLAGS\|PRIVATE\|PUBLIC\)' ||
733          \a:ident == 'Q_D' ||
734          \a:ident == 'Q_Q' ||
735          \a:ident == 'Q_DISABLE_COPY' ||
736          \a:ident == 'qsrand' ||
737          \a:ident == 'qrand'
738        return '<QtCore/QtGlobal>'
739
740    " Phonon stuff
741    elseif a:ident =~ '^Phonon::[A-Z]'
742        if a:ident =~ '^Phonon::\(NoDisc\|Cd\|Dvd\|Vcd\|.\+MetaData\|.*State\|.*Category\|.\+Error\)'
743            return '<Phonon/Global>'
744        endif
745        return '<'.substitute(a:ident, '::', '/', 'g').'>'
746    endif
747
748    " KDE stuff
749    let kdeincdir = substitute(system('kde4-config --prefix'), '[\n\r]*', '', 'g').'/include/KDE/'
750    let classname = substitute(a:ident, '^.*:', '', '')
751    let pathfn = expand('%:p:h')
752    if filereadable(kdeincdir.classname) && !pathfn =~ 'kdelibs'
753        return '<'.classname.'>'
754    elseif filereadable(kdeincdir.'Phonon/'.classname)
755        return '<Phonon/'.classname.'>'
756    elseif filereadable(kdeincdir.'Solid/'.classname)
757        return '<Solid/'.classname.'>'
758    elseif filereadable(kdeincdir.'KIO/'.classname)
759        return '<KIO/'.classname.'>'
760    elseif filereadable(kdeincdir.'KParts/'.classname)
761        return '<KParts/'.classname.'>'
762    elseif a:ident == 'K_GLOBAL_STATIC'
763        return '<KGlobal>'
764    elseif a:ident == 'K_EXPORT_PLUGIN'
765        return '<KPluginLoader>'
766    elseif a:ident =~ 'K_PLUGIN_FACTORY'
767        return '<KPluginFactory>'
768    elseif a:ident == 'K\(Double\|Int\)\(NumInput\|SpinBox\)'
769        return '<knuminput.h>'
770    elseif a:ident == 'KSharedConfig'
771        return '<kconfig.h>'
772    elseif a:ident == 'KConfigGroup'
773        return '<kconfiggroup.h>'
774    elseif a:ident == 'KListViewItem'
775        return '<klistview.h>'
776    elseif a:ident =~ 'kd\(Debug\|Warning\|Error\|Fatal\|Backtrace\)'
777        return '<kdebug.h>'
778    elseif a:ident == 'kapp'
779        return '<kapplication.h>'
780    elseif a:ident == 'i18n' ||
781          \a:ident == 'I18N_NOOP'
782        return '<klocale.h>'
783    elseif a:ident == 'locate' ||
784          \a:ident == 'locateLocal'
785        return '<kstandarddirs.h>'
786    elseif a:ident =~ '\(Small\|Desktop\|Bar\|MainBar\|User\)Icon\(Set\)\?' ||
787          \a:ident == 'IconSize'
788        return '<kiconloader.h>'
789
790    " Standard Library stuff
791    elseif a:ident =~ '\(std::\)\?\(cout\|cerr\|endl\)'
792        return '<iostream>'
793    elseif a:ident =~ '\(std::\)\?is\(alnum\|alpha\|ascii\|blank\|graph\|lower\|print\|punct\|space\|upper\|xdigit\)'
794        return '<cctype>'
795    elseif a:ident == 'printf'
796        return '<cstdio>'
797    endif
798
799    let check = header
800    while 1
801        if filereadable( check )
802            return '"' . check . '"'
803        endif
804        let slash = match( check, '/' )
805        if slash == -1
806            return '<' . header . '>'
807        endif
808        let check = strpart( check, slash + 1 )
809    endwhile
810endfunction
811
812" This is a rather dirty hack, but seems to work somehow :-) (malte)
813function! AddHeader()
814    let s = getline( '.' )
815    let i = col( '.' ) - 1
816    while i > 0 && strpart( s, i, 1 ) !~ '[A-Za-z0-9_:]'
817        let i = i - 1
818    endwhile
819    while i > 0 && strpart( s, i, 1 ) =~ '[A-Za-z0-9_:]'
820        let i = i - 1
821    endwhile
822    let start = match( s, '[A-Za-z0-9_]\+\(::[A-Z][A-Za-z0-9_]*\)*', i )
823    let end = matchend( s, '[A-Za-z0-9_]\+\(::[A-Z][A-Za-z0-9_]*\)*', i )
824"    if end > col( '.' )
825"        let end = matchend( s, '[A-Za-z0-9_]\+', i )
826"    endif
827    let ident = strpart( s, start, end - start )
828    let header = MapIdentHeader(ident)
829    let include = '#include '.header
830
831    let line = 1
832    let incomment = 0
833    let appendpos = 0
834    let codestart = 0
835    let similarpos = 0
836    let similarity = 0
837    while line <= line( '$' )
838        let s = getline( line )
839        if incomment == 1
840            let end = matchend( s, '\*/' )
841            if end == -1
842                let line = line + 1
843                continue
844            else
845                let s = strpart( s, end )
846                let incomment = 0
847            endif
848        endif
849        let s = substitute( s, '//.*', '', '' )
850        let s = substitute( s, '/\*\([^*]\|\*\@!/\)*\*/', '', 'g' )
851        if s =~ '/\*'
852            let incomment = 1
853        elseif s =~ '^' . include
854            break
855        elseif s =~ '^#include' && s !~ '\.moc"'
856            let appendpos = line
857            if s =~ '^#include '.header[0:similarity+1]
858                let similarpos = line
859                let similarity = similarity + 1
860                while s =~ '^#include '.header[0:similarity+1]
861                    let similarity = similarity + 1
862                endwhile
863                if s[9:strlen(s)-2] > header[0:strlen(header)-2]
864                    let similarpos = similarpos - 1
865                    let similarity = 100 "this include belongs one line higher (assuming the order of includes already is alphabetically)
866                endif
867            endif
868        elseif codestart == 0 && s !~ '^$'
869            let codestart = line
870        endif
871        let line = line + 1
872    endwhile
873    if similarpos > 0
874        let appendpos = similarpos
875    endif
876    if line == line( '$' ) + 1
877        if appendpos == 0
878            call append( codestart - 1, include )
879            call append( codestart, '' )
880        else
881            call append( appendpos, include )
882        endif
883    endif
884endfunction
885
886function! AddForward()
887    let s = getline( '.' )
888    let i = col( '.' ) - 1
889    while i > 0 && strpart( s, i, 1 ) !~ '[A-Za-z0-9_:]'
890        let i = i - 1
891    endwhile
892    while i > 0 && strpart( s, i, 1 ) =~ '[A-Za-z0-9_:]'
893        let i = i - 1
894    endwhile
895    let start = match( s, '[A-Za-z0-9_]\+\(::[A-Za-z0-9_]\+\)*', i )
896    let end = matchend( s, '[A-Za-z0-9_]\+\(::[A-Za-z0-9_]\+\)*', i )
897    if end > col( '.' )
898        let end = matchend( s, '[A-Za-z0-9_]\+', i )
899    endif
900    let ident = strpart( s, start, end - start )
901    let forward = 'class ' . ident . ';'
902
903    let line = 1
904    let incomment = 0
905    let appendpos = 0
906    let codestart = 0
907    while line <= line( '$' )
908        let s = getline( line )
909        if incomment == 1
910            let end = matchend( s, '\*/' )
911            if end == -1
912                let line = line + 1
913                continue
914            else
915                let s = strpart( s, end )
916                let incomment = 0
917            endif
918        endif
919        let s = substitute( s, '//.*', '', '' )
920        let s = substitute( s, '/\*\([^*]\|\*\@!/\)*\*/', '', 'g' )
921        if s =~ '/\*'
922            let incomment = 1
923        elseif s =~ '^' . forward
924            break
925        elseif s =~ '^\s*class [A-za-z0-9_]\+;' || (s =~ '^#include' && s !~ '\.moc"')
926            let appendpos = line
927        elseif codestart == 0 && s !~ '^$'
928            let codestart = line
929        endif
930        let line = line + 1
931    endwhile
932    if line == line( '$' ) + 1
933        if appendpos == 0
934            call append( codestart - 1, forward )
935            call append( codestart, '' )
936        else
937            call append( appendpos, forward )
938        endif
939    endif
940endfunction
941
942function! RunDiff()
943    echo 'Diffing....'
944    read! cvs diff -bB -I \\\#include | egrep -v '(^Index:|^=+$|^RCS file:|^retrieving revision|^diff -u|^[+-]{3})'
945endfunction
946
947function! CreateChangeLogEntry()
948    let currentBuffer = expand( "%" )
949
950    if exists( "g:EMAIL" )
951        let mail = g:EMAIL
952    elseif exists( "$EMAIL" )
953        let mail = $EMAIL
954    else
955        let mail = inputdialog( "Enter Name/Email for Changelog entry: " )
956    if mail == ""
957        echo "Aborted ChangeLog edit..."
958        return
959    endif
960    let g:EMAIL = mail
961    endif
962
963    if bufname( "ChangeLog" ) != "" && bufwinnr( bufname( "ChangeLog" ) ) != -1
964    execute bufwinnr( bufname( "ChangeLog" ) ) . " wincmd w"
965    else
966        execute "split ChangeLog"
967    endif
968
969    let lastEntry = getline( nextnonblank( 1 ) )
970    let newEntry = strftime("%Y-%m-%d") . "  " . mail
971
972    if lastEntry != newEntry
973        call append( 0, "" )
974        call append( 0, "" )
975        call append( 0, newEntry )
976    endif
977
978    " like emacs, prepend the current buffer name to the entry. but unlike
979    " emacs I have no idea how to figure out the current function name :(
980    " (Simon)
981    if currentBuffer != ""
982        let newLine = "\t* " . currentBuffer . ": "
983    else
984        let newLine = "\t* "
985    endif
986
987    call append( 2, newLine )
988
989    execute "normal 3G$"
990endfunction
991
992function! AddQtSyntax()
993    if expand( "<amatch>" ) == "cpp"
994        syn keyword qtKeywords     signals slots emit Q_SLOTS Q_SIGNALS
995        syn keyword qtMacros       Q_OBJECT Q_WIDGET Q_PROPERTY Q_ENUMS Q_OVERRIDE Q_CLASSINFO Q_SETS SIGNAL SLOT Q_DECLARE_PUBLIC Q_DECLARE_PRIVATE Q_D Q_Q Q_DISABLE_COPY Q_DECLARE_METATYPE Q_PRIVATE_SLOT Q_FLAGS Q_INTERFACES Q_DECLARE_INTERFACE Q_EXPORT_PLUGIN2 Q_GADGET Q_SCRIPTABLE Q_INVOKABLE METHOD Q_ARG Q_RETURN_ARG Q_GLOBAL_STATIC Q_GLOBAL_STATIC_WITH_ARGS
996        syn keyword qtCast         qt_cast qobject_cast qvariant_cast qstyleoption_cast qgraphicsitem_cast
997        syn keyword qtTypedef      uchar uint ushort ulong Q_INT8 Q_UINT8 Q_INT16 Q_UINT16 Q_INT32 Q_UINT32 Q_LONG Q_ULONG Q_INT64 Q_UINT64 Q_LLONG Q_ULLONG pchar puchar pcchar qint8 quint8 qint16 quint16 qint32 quint32 qint64 quint64 qlonglong qulonglong qreal
998        syn keyword kdeMacros      ASYNC PHONON_ABSTRACTBASE PHONON_OBJECT PHONON_HEIR PHONON_ABSTRACTBASE_IMPL PHONON_OBJECT_IMPL PHONON_HEIR_IMPL PHONON_PRIVATECLASS PHONON_PRIVATEABSTRACTCLASS K_DECLARE_PRIVATE K_D K_EXPORT_PLUGIN K_PLUGIN_FACTORY K_PLUGIN_FACTORY_DEFINITION K_PLUGIN_FACTORY_DECLARATION K_GLOBAL_STATIC K_GLOBAL_STATIC_WITH_ARGS
999        syn keyword cRepeat        foreach
1000        syn keyword cRepeat        forever
1001
1002        hi def link qtKeywords          Statement
1003        hi def link qtMacros            Type
1004        hi def link qtCast              Statement
1005        hi def link qtTypedef           Type
1006        hi def link kdeMacros           Type
1007    endif
1008endfunction
1009
1010function! UpdateMocFiles()
1011    if &syntax == "cpp"
1012        let i = 1
1013        while i < 80
1014            let s = getline( i )
1015            if s =~ '^#include ".*\.moc"'
1016                let s = substitute( s, '.*"\(.*\)\.moc"', '\1.h', '' )
1017                if stridx( &complete, s ) == -1
1018                    let &complete = &complete . ',k' . s
1019                endif
1020                break
1021            endif
1022            let i = i + 1
1023        endwhile
1024    endif
1025endfunction
1026
1027autocmd Syntax * call AddQtSyntax()
1028autocmd CursorHold * call UpdateMocFiles()
1029autocmd BufNewFile,BufRead * call SetCodingStyle()
1030
1031" vim: sw=4 sts=4 et
1032