1db8429d98157cfea02ccbcc3391f4c58de1c0c3
[stack/conf/vim.git] / addons / PDV_-_phpDocumentor_for_Vim / archive / php-doc.vim
1 " PDV (phpDocumentor for Vim)
2 " ===========================
3 "
4 " Version: 1.0.1
5
6 " Copyright 2005 by Tobias Schlitt <toby@php.net>
7 " Inspired by phpDoc script for Vim by Vidyut Luther (http://www.phpcult.com/).
8 "
9 " Provided under the GPL (http://www.gnu.org/copyleft/gpl.html).
10 "
11 " This script provides functions to generate phpDocumentor conform
12 " documentation blocks for your PHP code. The script currently
13 " documents:
14
15 " - Classes
16 " - Methods/Functions
17 " - Attributes
18 "
19 " All of those supporting all PHP 4 and 5 syntax elements. 
20 "
21 " Beside that it allows you to define default values for phpDocumentor tags 
22 " like @version (I use $id$ here), @author, @license and so on. 
23 "
24 " For function/method parameters and attributes, the script tries to guess the 
25 " type as good as possible from PHP5 type hints or default values (array, bool, 
26 " int, string...).
27 "
28 " You can use this script by mapping the function PhpDoc() to any
29 " key combination. Hit this on the line where the element to document
30 " resides and the doc block will be created directly above that line.
31
32 " Installation
33 " ============
34
35 " For example include into your .vimrc:
36
37 " source ~/.vim/php-doc.vim
38 " imap <C-o> \e:set paste<CR>:exe PhpDoc()<CR>:set nopaste<CR>i
39 "
40 " This includes the script and maps the combination <ctrl>+o (only in
41 " insert mode) to the doc function. 
42 "
43 " Changelog
44 " =========
45 "
46 " Version 1.0.0
47 " -------------
48 "
49 "  * Created the initial version of this script while playing around with VIM
50 "  scripting the first time and trying to fix Vidyut's solution, which
51 "  resulted in a complete rewrite.
52 "
53 " Version 1.0.1
54 " -------------
55 "  * Fixed issues when using tabs instead of spaces.
56 "  * Fixed some parsing bugs when using a different coding style.
57 "  * Fixed bug with call-by-reference parameters. 
58 "  * ATTENTION: This version already has code for the next version 1.1.0,
59 "  which is propably not working!
60 "
61 " Version 1.1.0 (preview)
62 " -------------
63 "  * Added foldmarker generation.
64
65
66 if has ("user_commands")
67
68 " {{{ Globals
69
70 " After phpDoc standard
71 let g:pdv_cfg_CommentHead = "/**"
72 let g:pdv_cfg_Comment1 = " * "
73 let g:pdv_cfg_Commentn = " * "
74 let g:pdv_cfg_CommentTail = " */"
75 let g:pdv_cfg_CommentSingle = "//"
76
77 " Default values
78 let g:pdv_cfg_Type = "mixed"
79 let g:pdv_cfg_Package = ""
80 let g:pdv_cfg_Version = "$id$"
81 let g:pdv_cfg_Author = "Tobias Schlitt <toby@php.net>"
82 let g:pdv_cfg_Copyright = "1997-2005 The PHP Group"
83 let g:pdv_cfg_License = "PHP Version 3.0 {@link http://www.php.net/license/3_0.txt}"
84
85 let g:pdv_cfg_ReturnVal = "void"
86
87 " Wether to create @uses tags for implementation of interfaces and inheritance
88 let g:pdv_cfg_Uses = 1
89
90 " Options
91 " :set paste before documenting (1|0)? Recommended.
92 let g:pdv_cfg_paste = 1
93
94 " Wether for PHP5 code PHP4 tags should be set, like @access,... (1|0)?
95 let g:pdv_cfg_php4always = 1
96  
97 " Wether to guess scopes after PEAR coding standards:
98 " $_foo/_bar() == <private|protected> (1|0)?
99 let g:pdv_cfg_php4guess = 1
100
101 " If you selected 1 for the last value, this scope identifier will be used for
102 " the identifiers having an _ in the first place.
103 let g:pdv_cfg_php4guessval = "protected"
104
105 "
106 " Regular expressions 
107
108
109 let g:pdv_re_comment = ' *\*/ *'
110
111 " (private|protected|public)
112 let g:pdv_re_scope = '\(private\|protected\|public\)'
113 " (static)
114 let g:pdv_re_static = '\(static\)'
115 " (abstract)
116 let g:pdv_re_abstract = '\(abstract\)'
117 " (final)
118 let g:pdv_re_final = '\(final\)'
119
120 " [:space:]*(private|protected|public|static|abstract)*[:space:]+[:identifier:]+\([:params:]\)
121 let g:pdv_re_func = '^\s*\([a-zA-Z ]*\)function\s\+\([^ (]\+\)\s*(\s*\(.*\)\s*)\s*[{;]\?$'
122 " [:typehint:]*[:space:]*$[:identifier]\([:space:]*=[:space:]*[:value:]\)?
123 let g:pdv_re_param = ' *\([^ &]*\) *&\?\$\([A-Za-z_][A-Za-z0-9_]*\) *=\? *\(.*\)\?$'
124
125 " [:space:]*(private|protected|public\)[:space:]*$[:identifier:]+\([:space:]*=[:space:]*[:value:]+\)*;
126 let g:pdv_re_attribute = '^\s*\(\(private\|public\|protected\|var\|static\)\+\)\s*\$\([^ ;=]\+\)[ =]*\(.*\);\?$'
127
128 " [:spacce:]*(abstract|final|)[:space:]*(class|interface)+[:space:]+\(extends ([:identifier:])\)?[:space:]*\(implements ([:identifier:][, ]*)+\)?
129 let g:pdv_re_class = '^\s*\([a-zA-Z]*\)\s*\(interface\|class\)\s*\([^ ]\+\)\s*\(extends\)\?\s*\([a-zA-Z0-9]*\)\?\s*\(implements*\)\? *\([a-zA-Z0-9_ ,]*\)\?.*$'
130
131 let g:pdv_re_array  = "^array *(.*"
132 let g:pdv_re_float  = '^[0-9.]\+'
133 let g:pdv_re_int    = '^[0-9]\+$'
134 let g:pdv_re_string = "['\"].*"
135 let g:pdv_re_bool = "\(true\|false\)"
136
137 let g:pdv_re_indent = '^\s*'
138
139 " Shortcuts for editing the text:
140 let g:pdv_cfg_BOL = "norm! o"
141 let g:pdv_cfg_EOL = "\e"
142
143 " }}}  
144
145  " {{{ PhpDocSingle()
146  " Document a single line of code ( does not check if doc block already exists )
147
148 func! PhpDocSingle()
149     let l:endline = line(".") + 1
150     call PhpDoc()
151     exe "norm! " . l:endline . "G$"
152 endfunc
153
154 " }}}
155  " {{{ PhpDocRange()
156  " Documents a whole range of code lines ( does not add defualt doc block to
157  " unknown types of lines ). Skips elements where a docblock is already
158  " present.
159 func! PhpDocRange() range
160     let l:line = a:firstline
161     let l:endLine = a:lastline
162         let l:elementName = ""
163     while l:line <= l:endLine
164         " TODO: Replace regex check for existing doc with check more lines
165         " above...
166         if (getline(l:line) =~ g:pdv_re_func || getline(l:line) =~ g:pdv_re_attribute || getline(l:line) =~ g:pdv_re_class) && getline(l:line - 1) !~ g:pdv_re_comment
167                         let l:docLines = 0
168                         " Ensure we are on the correct line to run PhpDoc()
169             exe "norm! " . l:line . "G$"
170                         " No matter what, this returns the element name
171             let l:elementName = PhpDoc()
172             let l:endLine = l:endLine + (line(".") - l:line) + 1
173             let l:line = line(".") + 1 
174         endif
175         let l:line = l:line + 1
176     endwhile
177 endfunc
178
179  " }}}
180 " {{{ PhpDocFold()
181
182 " func! PhpDocFold(name)
183 "       let l:startline = line(".")
184 "       let l:currentLine = l:startLine
185 "       let l:commentHead = escape(g:pdv_cfg_CommentHead, "*.");
186 "     let l:txtBOL = g:pdv_cfg_BOL . matchstr(l:name, '^\s*')
187 "       " Search above for comment start
188 "       while (l:currentLine > 1)
189 "               if (matchstr(l:commentHead, getline(l:currentLine)))
190 "                       break;
191 "               endif
192 "               let l:currentLine = l:currentLine + 1
193 "       endwhile
194 "       " Goto 1 line above and open a newline
195 "     exe "norm! " . (l:currentLine - 1) . "Go\<ESC>"
196 "       " Write the fold comment
197 "     exe l:txtBOL . g:pdv_cfg_CommentSingle . " {"."{{ " . a:name . g:pdv_cfg_EOL
198 "       " Add another newline below that
199 "       exe "norm! o\<ESC>"
200 "       " Search for our comment line
201 "       let l:currentLine = line(".")
202 "       while (l:currentLine <= line("$"))
203 "               " HERE!!!!
204 "       endwhile
205 "       
206
207 " endfunc
208
209
210 " }}}
211
212 " {{{ PhpDoc()
213
214 func! PhpDoc()
215     " Needed for my .vimrc: Switch off all other enhancements while generating docs
216     let l:paste = &g:paste
217     let &g:paste = g:pdv_cfg_paste == 1 ? 1 : &g:paste
218     
219     let l:line = getline(".")
220     let l:result = ""
221
222     if l:line =~ g:pdv_re_func
223         let l:result = PhpDocFunc()
224
225     elseif l:line =~ g:pdv_re_attribute
226         let l:result = PhpDocVar()
227
228     elseif l:line =~ g:pdv_re_class
229         let l:result = PhpDocClass()
230
231     else
232         let l:result = PhpDocDefault()
233
234     endif
235
236 "       if g:pdv_cfg_folds == 1
237 "               PhpDocFolds(l:result)
238 "       endif
239
240     let &g:paste = l:paste
241
242     return l:result
243 endfunc
244
245 " }}}
246 " {{{  PhpDocFunc()  
247
248 func! PhpDocFunc()
249         " Line for the comment to begin
250         let commentline = line (".") - 1
251
252         let l:name = substitute (getline ("."), '^\(.*\)\/\/.*$', '\1', "")
253
254     "exe g:pdv_cfg_BOL . "DEBUG:" . name. g:pdv_cfg_EOL
255
256         " First some things to make it more easy for us:
257         " tab -> space && space+ -> space
258         " let l:name = substitute (l:name, '\t', ' ', "")
259         " Orphan. We're now using \s everywhere...
260
261         " Now we have to split DECL in three parts:
262         " \[(skopemodifier\)]\(funcname\)\(parameters\)
263     let l:indent = matchstr(l:name, g:pdv_re_indent)
264         
265         let l:modifier = substitute (l:name, g:pdv_re_func, '\1', "g")
266         let l:funcname = substitute (l:name, g:pdv_re_func, '\2', "g")
267         let l:parameters = substitute (l:name, g:pdv_re_func, '\3', "g") . ","
268     let l:scope = PhpDocScope(l:modifier, l:funcname)
269     let l:static = g:pdv_cfg_php4always == 1 ? matchstr(l:modifier, g:pdv_re_static) : ""
270         let l:abstract = g:pdv_cfg_php4always == 1 ? matchstr(l:modifier, g:pdv_re_abstract) : ""
271         let l:final = g:pdv_cfg_php4always == 1 ? matchstr(l:modifier, g:pdv_re_final) : ""
272     
273     exe "norm! " . commentline . "G$"
274     
275     " Local indent
276     let l:txtBOL = g:pdv_cfg_BOL . l:indent
277         
278     exe l:txtBOL . g:pdv_cfg_CommentHead . g:pdv_cfg_EOL
279         exe l:txtBOL . g:pdv_cfg_Comment1 . funcname . " " . g:pdv_cfg_EOL
280     exe l:txtBOL . g:pdv_cfg_Commentn . g:pdv_cfg_EOL
281
282         while (l:parameters != ",") && (l:parameters != "")
283                 " Save 1st parameter
284                 let _p = substitute (l:parameters, '\([^,]*\) *, *\(.*\)', '\1', "")
285                 " Remove this one from list
286                 let l:parameters = substitute (l:parameters, '\([^,]*\) *, *\(.*\)', '\2', "")
287                 " PHP5 type hint?
288                 let l:paramtype = substitute (_p, g:pdv_re_param, '\1', "")
289                 " Parameter name
290                 let l:paramname = substitute (_p, g:pdv_re_param, '\2', "")
291                 " Parameter default
292                 let l:paramdefault = substitute (_p, g:pdv_re_param, '\3', "")
293
294         if l:paramtype == ""
295             let l:paramtype = PhpDocType(l:paramdefault)
296         endif
297         
298         if l:paramtype != ""
299             let l:paramtype = " " . l:paramtype
300         endif
301                 exe l:txtBOL . g:pdv_cfg_Commentn . "@param" . l:paramtype . " $" . l:paramname . " " . g:pdv_cfg_EOL
302         endwhile
303
304         if l:static != ""
305         exe l:txtBOL . g:pdv_cfg_Commentn . "@static" . g:pdv_cfg_EOL
306     endif
307         if l:abstract != ""
308         exe l:txtBOL . g:pdv_cfg_Commentn . "@abstract" . g:pdv_cfg_EOL
309     endif
310         if l:final != ""
311         exe l:txtBOL . g:pdv_cfg_Commentn . "@final" . g:pdv_cfg_EOL
312     endif
313     if l:scope != ""
314         exe l:txtBOL . g:pdv_cfg_Commentn . "@access " . l:scope . g:pdv_cfg_EOL
315     endif
316         exe l:txtBOL . g:pdv_cfg_Commentn . "@return " . g:pdv_cfg_ReturnVal . g:pdv_cfg_EOL
317
318         " Close the comment block.
319         exe l:txtBOL . g:pdv_cfg_CommentTail . g:pdv_cfg_EOL
320     return l:modifier ." ". l:funcname
321 endfunc
322
323 " }}}  
324  " {{{  PhpDocVar() 
325
326 func! PhpDocVar()
327         " Line for the comment to begin
328         let commentline = line (".") - 1
329
330         let l:name = substitute (getline ("."), '^\(.*\)\/\/.*$', '\1', "")
331
332         " Now we have to split DECL in three parts:
333         " \[(skopemodifier\)]\(funcname\)\(parameters\)
334         " let l:name = substitute (l:name, '\t', ' ', "")
335         " Orphan. We're now using \s everywhere...
336
337     let l:indent = matchstr(l:name, g:pdv_re_indent)
338
339         let l:modifier = substitute (l:name, g:pdv_re_attribute, '\1', "g")
340         let l:varname = substitute (l:name, g:pdv_re_attribute, '\3', "g")
341         let l:default = substitute (l:name, g:pdv_re_attribute, '\4', "g")
342     let l:scope = PhpDocScope(l:modifier, l:varname)
343
344     let l:static = g:pdv_cfg_php4always == 1 ? matchstr(l:modifier, g:pdv_re_static) : ""
345
346     let l:type = PhpDocType(l:default)
347     
348     exe "norm! " . commentline . "G$"
349     
350     " Local indent
351     let l:txtBOL = g:pdv_cfg_BOL . l:indent
352         
353     exe l:txtBOL . g:pdv_cfg_CommentHead . g:pdv_cfg_EOL
354         exe l:txtBOL . g:pdv_cfg_Comment1 . l:varname . " " . g:pdv_cfg_EOL
355     exe l:txtBOL . g:pdv_cfg_Commentn . g:pdv_cfg_EOL
356     if l:static != ""
357         exe l:txtBOL . g:pdv_cfg_Commentn . "@static" . g:pdv_cfg_EOL
358     endif
359     exe l:txtBOL . g:pdv_cfg_Commentn . "@var " . l:type . g:pdv_cfg_EOL
360     if l:scope != ""
361         exe l:txtBOL . g:pdv_cfg_Commentn . "@access " . l:scope . g:pdv_cfg_EOL
362     endif
363         
364     " Close the comment block.
365         exe l:txtBOL . g:pdv_cfg_CommentTail . g:pdv_cfg_EOL
366         return l:modifier ." ". l:varname
367 endfunc
368
369 " }}}
370 "  {{{  PhpDocClass()
371
372 func! PhpDocClass()
373         " Line for the comment to begin
374         let commentline = line (".") - 1
375
376         let l:name = substitute (getline ("."), '^\(.*\)\/\/.*$', '\1', "")
377
378     "exe g:pdv_cfg_BOL . "DEBUG:" . name. g:pdv_cfg_EOL
379
380         " First some things to make it more easy for us:
381         " tab -> space && space+ -> space
382         " let l:name = substitute (l:name, '\t', ' ', "")
383         " Orphan. We're now using \s everywhere...
384
385         " Now we have to split DECL in three parts:
386         " \[(skopemodifier\)]\(classname\)\(parameters\)
387     let l:indent = matchstr(l:name, g:pdv_re_indent)
388         
389         let l:modifier = substitute (l:name, g:pdv_re_class, '\1', "g")
390         let l:classname = substitute (l:name, g:pdv_re_class, '\3', "g")
391         let l:extends = g:pdv_cfg_Uses == 1 ? substitute (l:name, g:pdv_re_class, '\5', "g") : ""
392         let l:interfaces = g:pdv_cfg_Uses == 1 ? substitute (l:name, g:pdv_re_class, '\7', "g") . "," : ""
393
394         let l:abstract = g:pdv_cfg_php4always == 1 ? matchstr(l:modifier, g:pdv_re_abstract) : ""
395         let l:final = g:pdv_cfg_php4always == 1 ?  matchstr(l:modifier, g:pdv_re_final) : ""
396     
397     exe "norm! " . commentline . "G$"
398     
399     " Local indent
400     let l:txtBOL = g:pdv_cfg_BOL . l:indent
401         
402     exe l:txtBOL . g:pdv_cfg_CommentHead . g:pdv_cfg_EOL
403         exe l:txtBOL . g:pdv_cfg_Comment1 . l:classname . " " . g:pdv_cfg_EOL
404     exe l:txtBOL . g:pdv_cfg_Commentn . g:pdv_cfg_EOL
405     if l:extends != "" && l:extends != "implements"
406         exe l:txtBOL . g:pdv_cfg_Commentn . "@uses " . l:extends . g:pdv_cfg_EOL
407     endif
408
409         while (l:interfaces != ",") && (l:interfaces != "")
410                 " Save 1st parameter
411                 let interface = substitute (l:interfaces, '\([^, ]*\) *, *\(.*\)', '\1', "")
412                 " Remove this one from list
413                 let l:interfaces = substitute (l:interfaces, '\([^, ]*\) *, *\(.*\)', '\2', "")
414                 exe l:txtBOL . g:pdv_cfg_Commentn . "@uses " . l:interface . g:pdv_cfg_EOL
415         endwhile
416
417         if l:abstract != ""
418         exe l:txtBOL . g:pdv_cfg_Commentn . "@abstract" . g:pdv_cfg_EOL
419     endif
420         if l:final != ""
421         exe l:txtBOL . g:pdv_cfg_Commentn . "@final" . g:pdv_cfg_EOL
422     endif
423         exe l:txtBOL . g:pdv_cfg_Commentn . "@package " . g:pdv_cfg_Package . g:pdv_cfg_EOL
424         exe l:txtBOL . g:pdv_cfg_Commentn . "@version " . g:pdv_cfg_Version . g:pdv_cfg_EOL
425         exe l:txtBOL . g:pdv_cfg_Commentn . "@copyright " . g:pdv_cfg_Copyright . g:pdv_cfg_EOL
426         exe l:txtBOL . g:pdv_cfg_Commentn . "@author " . g:pdv_cfg_Author g:pdv_cfg_EOL
427         exe l:txtBOL . g:pdv_cfg_Commentn . "@license " . g:pdv_cfg_License . g:pdv_cfg_EOL
428
429         " Close the comment block.
430         exe l:txtBOL . g:pdv_cfg_CommentTail . g:pdv_cfg_EOL
431         return l:modifier ." ". l:classname
432 endfunc
433
434 " }}} 
435 " {{{ PhpDocScope() 
436
437 func! PhpDocScope(modifiers, identifier)
438 " exe g:pdv_cfg_BOL . DEBUG: . a:modifiers . g:pdv_cfg_EOL
439     let l:scope  = ""
440     if  matchstr (a:modifiers, g:pdv_re_scope) != "" 
441         if g:pdv_cfg_php4always == 1
442             let l:scope = matchstr (a:modifiers, g:pdv_re_scope)
443         else
444             let l:scope = "x"
445         endif
446     endif
447     if l:scope =~ "^\s*$" && g:pdv_cfg_php4guess
448         if a:identifier[0] == "_"
449             let l:scope = g:pdv_cfg_php4guessval
450         else
451             let l:scope = "public"
452         endif
453     endif
454     return l:scope != "x" ? l:scope : ""
455 endfunc
456
457 " }}}
458 " {{{ PhpDocType()
459
460 func! PhpDocType(typeString)
461     let l:type = ""
462     if a:typeString =~ g:pdv_re_array 
463         let l:type = "array"
464     endif
465     if a:typeString =~ g:pdv_re_float 
466         let l:type = "float"
467     endif
468     if a:typeString =~ g:pdv_re_int 
469         let l:type = "int"
470     endif
471     if a:typeString =~ g:pdv_re_string
472         let l:type = "string"
473     endif
474     if a:typeString =~ g:pdv_re_bool
475         let l:type = "bool"
476     endif
477         if l:type == ""
478                 let l:type = g:pdv_cfg_Type
479         endif
480     return l:type
481 endfunc
482     
483 "  }}} 
484 " {{{  PhpDocDefault()
485
486 func! PhpDocDefault()
487         " Line for the comment to begin
488         let commentline = line (".") - 1
489     
490     let l:indent = matchstr(getline("."), '^\ *')
491     
492     exe "norm! " . commentline . "G$"
493     
494     " Local indent
495     let l:txtBOL = g:pdv_cfg_BOL . indent
496
497     exe l:txtBOL . g:pdv_cfg_CommentHead . g:pdv_cfg_EOL
498     exe l:txtBOL . g:pdv_cfg_Commentn . " " . g:pdv_cfg_EOL
499         
500     " Close the comment block.
501         exe l:txtBOL . g:pdv_cfg_CommentTail . g:pdv_cfg_EOL
502 endfunc
503
504 " }}}
505
506 endif " user_commands