Skip to content

Commit d44a8f2

Browse files
author
dudu
committed
Add php7 return type hint support
Fixes: #101
1 parent ce60790 commit d44a8f2

File tree

5 files changed

+150
-17
lines changed

5 files changed

+150
-17
lines changed

autoload/phpcomplete.vim

+64-17
Original file line numberDiff line numberDiff line change
@@ -1680,23 +1680,28 @@ function! phpcomplete#GetCallChainReturnType(classname_candidate, class_candidat
16801680
for classstructure in classcontents
16811681
let docblock_target_pattern = 'function\s\+&\?'.method.'\>\|\(public\|private\|protected\|var\).\+\$'.method.'\>\|@property.\+\$'.method.'\>'
16821682
let doc_str = phpcomplete#GetDocBlock(split(classstructure.content, '\n'), docblock_target_pattern)
1683-
if doc_str != ''
1683+
let return_type_hint = phpcomplete#GetFunctionReturnTypeHint(split(classstructure.content, '\n'), 'function\s\+&\?'.method.'\>')
1684+
if doc_str != '' || return_type_hint != ''
16841685
break
16851686
endif
16861687
endfor
1687-
if doc_str != ''
1688+
if doc_str != '' || return_type_hint != ''
16881689
let docblock = phpcomplete#ParseDocBlock(doc_str)
1689-
if has_key(docblock.return, 'type') || has_key(docblock.var, 'type') || len(docblock.properties) > 0
1690-
let type = has_key(docblock.return, 'type') ? docblock.return.type : has_key(docblock.var, 'type') ? docblock.var.type : ''
1691-
1692-
if type == ''
1693-
for property in docblock.properties
1694-
if property.description =~? method
1695-
let type = property.type
1696-
break
1697-
endif
1698-
endfor
1699-
endif
1690+
if has_key(docblock.return, 'type') || has_key(docblock.var, 'type') || len(docblock.properties) > 0 || return_type_hint != ''
1691+
if return_type_hint == ''
1692+
let type = has_key(docblock.return, 'type') ? docblock.return.type : has_key(docblock.var, 'type') ? docblock.var.type : ''
1693+
1694+
if type == ''
1695+
for property in docblock.properties
1696+
if property.description =~? method
1697+
let type = property.type
1698+
break
1699+
endif
1700+
endfor
1701+
endif
1702+
else
1703+
let type = return_type_hint
1704+
end
17001705

17011706
" there's a namespace in the type, threat the type as FQCN
17021707
if type =~ '\\'
@@ -1874,9 +1879,11 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor
18741879
elseif function_file != '' && filereadable(function_file)
18751880
let file_lines = readfile(function_file)
18761881
let docblock_str = phpcomplete#GetDocBlock(file_lines, 'function\s*&\?\<'.function_name.'\>')
1882+
let return_type_hint = phpcomplete#GetFunctionReturnTypeHint(file_lines, 'function\s*&\?'.function_name.'\>')
18771883
let docblock = phpcomplete#ParseDocBlock(docblock_str)
1878-
if has_key(docblock.return, 'type')
1879-
let classname_candidate = docblock.return.type
1884+
let type = has_key(docblock.return, 'type') ? docblock.return.type : return_type_hint
1885+
if type != ''
1886+
let classname_candidate = type
18801887
let [class_candidate_namespace, function_imports] = phpcomplete#GetCurrentNameSpace(file_lines)
18811888
" try to expand the classname of the returned type with the context got from the function's source file
18821889

@@ -2108,9 +2115,11 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor
21082115
elseif function_file != '' && filereadable(function_file)
21092116
let file_lines = readfile(function_file)
21102117
let docblock_str = phpcomplete#GetDocBlock(file_lines, 'function\s*&\?\<'.function_name.'\>')
2118+
let return_type_hint = phpcomplete#GetFunctionReturnTypeHint(file_lines, 'function\s*&\?'.function_name.'\>')
21112119
let docblock = phpcomplete#ParseDocBlock(docblock_str)
2112-
if has_key(docblock.return, 'type')
2113-
let classname_candidate = docblock.return.type
2120+
let type = has_key(docblock.return, 'type') ? docblock.return.type : return_type_hint
2121+
if type != ''
2122+
let classname_candidate = type
21142123
let [class_candidate_namespace, function_imports] = phpcomplete#GetCurrentNameSpace(file_lines)
21152124
" try to expand the classname of the returned type with the context got from the function's source file
21162125
let [classname_candidate, class_candidate_namespace] = phpcomplete#ExpandClassName(classname_candidate, class_candidate_namespace, function_imports)
@@ -2703,6 +2712,44 @@ function! phpcomplete#ParseDocBlock(docblock) " {{{
27032712
endfunction
27042713
" }}}
27052714

2715+
function! phpcomplete#GetFunctionReturnTypeHint(sccontent, search)
2716+
let i = 0
2717+
let l = 0
2718+
let function_line_start = -1
2719+
let function_line_end = -1
2720+
let sccontent_len = len(a:sccontent)
2721+
let return_type = ''
2722+
2723+
while (i < sccontent_len)
2724+
let line = a:sccontent[i]
2725+
" search for a function declaration
2726+
if line =~? a:search
2727+
let l = i
2728+
let function_line_start = i
2729+
" now search for the first { where the function body starts
2730+
while l < sccontent_len
2731+
let line = a:sccontent[l]
2732+
if line =~? '\V{'
2733+
let function_line_end = l
2734+
break
2735+
endif
2736+
let l += 1
2737+
endwhile
2738+
break
2739+
endif
2740+
let i += 1
2741+
endwhile
2742+
2743+
" now grab the lines that holds the function declaration line
2744+
if function_line_start != -1 && function_line_end != -1
2745+
let function_line = join(a:sccontent[function_line_start :function_line_end], " ")
2746+
let class_name_pattern = '[a-zA-Z_\x7f-\xff\\][a-zA-Z_0-9\x7f-\xff\\]*'
2747+
let return_type = matchstr(function_line, '\c\s*:\s*\zs'.class_name_pattern.'\ze\s*{')
2748+
endif
2749+
return return_type
2750+
2751+
endfunction
2752+
27062753
function! phpcomplete#GetTypeFromDocBlockParam(docblock_type) " {{{
27072754
if a:docblock_type !~ '|'
27082755
return a:docblock_type

tests/GetClassName_test.vim

+23
Original file line numberDiff line numberDiff line change
@@ -897,4 +897,27 @@ fun! TestCase_resolves_inside_a_function_body()
897897
silent! bw! %
898898
endf
899899

900+
fun! TestCase_resolves_return_type_hints()
901+
call SetUp()
902+
903+
let path = expand('%:p:h')."/"."fixtures/GetClassName/return_typehinted_functions.php"
904+
below 1new
905+
exe ":silent! edit ".path
906+
exe 'let b:phpbegin = [0, 0]'
907+
908+
exe ':24'
909+
let classname = phpcomplete#GetClassName(24, '$f->', '', {})
910+
call VUAssertEquals('FooReturnBars', classname)
911+
912+
exe ':26'
913+
let classname = phpcomplete#GetClassName(26, '$f->returnBar()->', '', {})
914+
call VUAssertEquals('Bar', classname)
915+
916+
exe ':28'
917+
let classname = phpcomplete#GetClassName(28, '$f->returnBar2()->', '', {})
918+
call VUAssertEquals('Bar2', classname)
919+
920+
silent! bw! %
921+
endf
922+
900923
" vim: foldmethod=marker:expandtab:ts=4:sts=4
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
fun! SetUp()
2+
" disable builtin information
3+
let g:php_builtin_functions = {}
4+
" disable tag files
5+
exe ':set tags='
6+
let g:fixture_file_content = readfile(expand('%:p:h').'/'.'fixtures/GetFunctionReturnTypeHint/functions.php')[2:]
7+
endf
8+
9+
fun! TestCase_return_current_file_path_when_function_declaration_is_found_in_the_file()
10+
call SetUp()
11+
12+
let ret = phpcomplete#GetFunctionReturnTypeHint(g:fixture_file_content, 'function\s*\<returnFoo\>')
13+
call VUAssertEquals('Foo', ret)
14+
15+
let ret = phpcomplete#GetFunctionReturnTypeHint(g:fixture_file_content, 'function\s*\<returnBar\>')
16+
call VUAssertEquals('Bar', ret)
17+
18+
let ret = phpcomplete#GetFunctionReturnTypeHint(g:fixture_file_content, 'function\s*\<returnBar2\>')
19+
call VUAssertEquals('Bar2', ret)
20+
endf
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
class Bar {
4+
public function findMe() { }
5+
}
6+
7+
class Bar2 {
8+
public function findMe() { }
9+
}
10+
11+
class FooReturnBars {
12+
public function returnBar($a, $b = 'foo') : Bar {
13+
}
14+
15+
public function returnBar2($a, $b = 'foo') : Bar2
16+
{
17+
}
18+
}
19+
20+
function returnFoo() : FooReturnBars {
21+
}
22+
23+
$f = returnFoo();
24+
$f->
25+
;
26+
$f->returnBar()->
27+
;
28+
$f->returnBar2()->
29+
;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
class Foo
4+
public function returnBar($a, $b = 'foo') : Bar {
5+
}
6+
7+
public function returnBar2($a, $b = 'foo') : Bar2
8+
{
9+
}
10+
}
11+
12+
function returnFoo() : Foo {
13+
14+
}

0 commit comments

Comments
 (0)