3
3
4
4
namespace LanguageServer ;
5
5
6
+ use PhpParser \ErrorHandler \Collecting ;
6
7
use PhpParser \Node ;
7
8
use LanguageServer \Index \ReadableIndex ;
8
9
use LanguageServer \Protocol \{
10
+ Range ,
9
11
Position ,
10
12
SignatureHelp ,
11
13
SignatureInformation ,
@@ -24,6 +26,16 @@ class SignatureHelpProvider
24
26
*/
25
27
private $ index ;
26
28
29
+ /**
30
+ * @var Parser
31
+ */
32
+ private $ parser ;
33
+
34
+ /**
35
+ * @var Parser
36
+ */
37
+ private $ parserErrorHandler ;
38
+
27
39
/**
28
40
* @param DefinitionResolver $definitionResolver
29
41
* @param ReadableIndex $index
@@ -32,6 +44,8 @@ public function __construct(DefinitionResolver $definitionResolver, ReadableInde
32
44
{
33
45
$ this ->definitionResolver = $ definitionResolver ;
34
46
$ this ->index = $ index ;
47
+ $ this ->parser = new Parser ;
48
+ $ this ->parserErrorHandler = new Collecting ;
35
49
}
36
50
37
51
/**
@@ -41,76 +55,52 @@ public function __construct(DefinitionResolver $definitionResolver, ReadableInde
41
55
* @param Position $pos The cursor position
42
56
* @return SignatureHelp
43
57
*/
44
- public function provideSignature (PhpDocument $ doc , Position $ pos ): SignatureHelp
58
+ public function provideSignature (PhpDocument $ doc , Position $ pos ) : SignatureHelp
45
59
{
46
60
$ help = new SignatureHelp ;
47
61
$ help ->signatures = [];
48
62
49
- $ newPos = clone $ pos ;
50
- $ line = explode ("\n" , $ doc ->getContent ())[$ newPos ->line ];
51
- do {
52
- $ newPos ->character --;
53
- } while ($ newPos ->character > 0 && $ line [$ newPos ->character ] !== "( " );
54
-
55
- if (!$ newPos ->character ) {
56
- return $ help ;
63
+ $ handle = fopen ($ doc ->getUri (), 'r ' );
64
+ $ lines = [];
65
+ for ($ i = 0 ; $ i < $ pos ->line ; $ i ++) {
66
+ $ lines [] = strlen (fgets ($ handle ));
57
67
}
58
- $ line = substr ($ line , 0 , $ newPos ->character );
68
+ $ filePos = ftell ($ handle ) + $ pos ->character ;
69
+ $ line = substr (fgets ($ handle ), 0 , $ pos ->character );
70
+ fseek ($ handle , 0 );
59
71
60
- $ newPos ->character --;
61
-
62
- $ node = $ doc ->getNodeAtPosition ($ newPos );
72
+ do {
73
+ $ node = $ doc ->getNodeAtPosition ($ pos );
74
+ $ pos ->character --;
75
+ if ($ pos ->character < 0 ) {
76
+ $ pos ->line --;
77
+ if ($ pos ->line < 0 ) {
78
+ break ;
79
+ }
80
+ $ pos ->character = $ lines [$ pos ->line ];
81
+ }
82
+ } while ($ node === null );
63
83
64
- if ($ node instanceof Node \Expr \Error) {
65
- $ node = $ node ->getAttribute ('parentNode ' );
84
+ if ($ node === null ) {
85
+ fclose ($ handle );
86
+ return $ help ;
66
87
}
67
-
68
- if ($ node instanceof Node \Expr \Error) {
88
+ $ i = 0 ;
89
+ while (!(
90
+ $ node instanceof Node \Expr \PropertyFetch ||
91
+ $ node instanceof Node \Expr \MethodCall ||
92
+ $ node instanceof Node \Expr \FuncCall ||
93
+ $ node instanceof Node \Expr \ClassConstFetch ||
94
+ $ node instanceof Node \Expr \StaticCall
95
+ ) && ++$ i < 5 && $ node !== null ) {
69
96
$ node = $ node ->getAttribute ('parentNode ' );
70
97
}
71
- if ($ node instanceof Node \Expr \FuncCall) {
72
- if ($ def = $ this ->definitionResolver ->resolveReferenceNodeToDefinition ($ node )) {
73
- $ signature = new SignatureInformation ;
74
- $ signature ->label = str_replace ('() ' , '' , $ def ->fqn ) . '( ' .implode (', ' , $ def ->parameters ).') ' ;
75
- $ signature ->documentation = $ def ->documentation ;
76
- $ signature ->parameters = [];
77
- foreach ($ def ->parameters as $ param ) {
78
- $ p = new ParameterInformation ;
79
- $ p ->label = $ param ;
80
- $ signature ->parameters [] = $ p ;
81
- }
82
- $ help ->signatures [] = $ signature ;
83
- }
84
- } else if ($ node instanceof Node \Name \FullyQualified || $ node === null ) {
85
- if (preg_match ('([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$) ' , $ line , $ method )) {
86
- $ fqn = $ method [0 ] . '() ' ;
87
- if ($ def = $ this ->index ->getDefinition ($ fqn )) {
88
- $ signature = new SignatureInformation ;
89
- $ signature ->label = $ method [0 ] . '( ' .implode (', ' , $ def ->parameters ).') ' ;
90
- $ signature ->documentation = $ def ->documentation ;
91
- $ signature ->parameters = [];
92
- foreach ($ def ->parameters as $ param ) {
93
- $ p = new ParameterInformation ;
94
- $ p ->label = $ param ;
95
- $ signature ->parameters [] = $ p ;
96
- }
97
- $ help ->signatures [] = $ signature ;
98
- }
99
- }
100
- } else if ($ node instanceof Node \Expr \MethodCall) {
101
- if ($ def = $ this ->definitionResolver ->resolveReferenceNodeToDefinition ($ node )) {
102
- $ signature = new SignatureInformation ;
103
- $ signature ->label = str_replace ('() ' , '' , explode ('-> ' , $ def ->fqn )[1 ]) . '( ' .implode (', ' , $ def ->parameters ).') ' ;
104
- $ signature ->documentation = $ def ->documentation ;
105
- $ signature ->parameters = [];
106
- foreach ($ def ->parameters as $ param ) {
107
- $ p = new ParameterInformation ;
108
- $ p ->label = $ param ;
109
- $ signature ->parameters [] = $ p ;
110
- }
111
- $ help ->signatures [] = $ signature ;
112
- }
113
- } else if ($ node instanceof Node \Expr \PropertyFetch) {
98
+ $ params = '' ;
99
+ if ($ node instanceof Node \Expr \PropertyFetch) {
100
+ fseek ($ handle , $ node ->name ->getAttribute ('startFilePos ' ));
101
+ $ method = fread ($ handle , ($ node ->name ->getAttribute ('endFilePos ' ) + 1 ) - $ node ->name ->getAttribute ('startFilePos ' ));
102
+ fseek ($ handle , $ node ->name ->getAttribute ('endFilePos ' ) + 1 );
103
+ $ params = fread ($ handle , ($ filePos - 1 ) - $ node ->name ->getAttribute ('endFilePos ' ));
114
104
if ($ def = $ this ->definitionResolver ->resolveReferenceNodeToDefinition ($ node ->var )) {
115
105
$ fqn = $ def ->fqn ;
116
106
if (!$ fqn ) {
@@ -121,55 +111,65 @@ public function provideSignature(PhpDocument $doc, Position $pos): SignatureHelp
121
111
$ fqn = $ fqns [0 ];
122
112
}
123
113
}
124
- $ method = trim (substr ($ line , strrpos ($ line , "> " ) + 1 ));
125
- if ($ method && $ fqn ) {
114
+ if ($ fqn ) {
126
115
$ fqn = $ fqn . '-> ' . $ method . '() ' ;
127
- if ($ def = $ this ->index ->getDefinition ($ fqn )) {
128
- $ signature = new SignatureInformation ;
129
- $ signature ->label = str_replace ('() ' , '' , explode ('-> ' , $ def ->fqn )[1 ]) . '( ' .implode (', ' , $ def ->parameters ).') ' ;
130
- $ signature ->documentation = $ def ->documentation ;
131
- $ signature ->parameters = [];
132
- foreach ($ def ->parameters as $ param ) {
133
- $ p = new ParameterInformation ;
134
- $ p ->label = $ param ;
135
- $ signature ->parameters [] = $ p ;
136
- }
137
- $ help ->signatures [] = $ signature ;
138
- }
116
+ $ def = $ this ->index ->getDefinition ($ fqn );
139
117
}
140
118
}
119
+ } else if ($ node instanceof Node \Expr \MethodCall) {
120
+ fseek ($ handle , $ node ->getAttribute ('startFilePos ' ));
121
+ $ params = explode ('( ' , fread ($ handle , $ filePos - $ node ->getAttribute ('startFilePos ' )), 2 )[1 ];
122
+ $ def = $ this ->definitionResolver ->resolveReferenceNodeToDefinition ($ node );
123
+ } else if ($ node instanceof Node \Expr \FuncCall) {
124
+ fseek ($ handle , $ node ->getAttribute ('startFilePos ' ));
125
+ $ params = explode ('( ' , fread ($ handle , $ filePos - $ node ->getAttribute ('startFilePos ' )), 2 )[1 ];
126
+ $ fqn = $ this ->definitionResolver ->resolveReferenceNodeToFqn ($ node ->name );
127
+ $ def = $ this ->index ->getDefinition ($ fqn );
141
128
} else if ($ node instanceof Node \Expr \StaticCall) {
142
- if ($ def = $ this ->definitionResolver ->resolveReferenceNodeToDefinition ($ node )) {
143
- $ signature = new SignatureInformation ;
144
- $ signature ->label = str_replace ('() ' , '' , explode (':: ' , $ def ->fqn )[1 ]) . '( ' .implode (', ' , $ def ->parameters ).') ' ;
145
- $ signature ->documentation = $ def ->documentation ;
146
- $ signature ->parameters = [];
147
- foreach ($ def ->parameters as $ param ) {
148
- $ p = new ParameterInformation ;
149
- $ p ->label = $ param ;
150
- $ signature ->parameters [] = $ p ;
151
- }
152
- $ help ->signatures [] = $ signature ;
153
- }
129
+ fseek ($ handle , $ node ->getAttribute ('startFilePos ' ));
130
+ $ params = explode ('( ' , fread ($ handle , $ filePos - $ node ->getAttribute ('startFilePos ' )), 2 )[1 ];
131
+ $ def = $ this ->definitionResolver ->resolveReferenceNodeToDefinition ($ node );
154
132
} else if ($ node instanceof Node \Expr \ClassConstFetch) {
155
- if ($ def = $ this ->definitionResolver ->resolveReferenceNodeToDefinition ($ node ->class )) {
156
- $ method = trim (substr ($ line , strrpos ($ line , ": " ) + 1 ));
157
- if ($ method ) {
158
- $ fqn = $ def ->fqn . ':: ' . $ method . '() ' ;
159
- if ($ def = $ this ->index ->getDefinition ($ fqn )) {
160
- $ signature = new SignatureInformation ;
161
- $ signature ->label = str_replace ('() ' , '' , explode (':: ' , $ def ->fqn )[1 ]) . '( ' .implode (', ' , $ def ->parameters ).') ' ;
162
- $ signature ->documentation = $ def ->documentation ;
163
- $ signature ->parameters = [];
164
- foreach ($ def ->parameters as $ param ) {
165
- $ p = new ParameterInformation ;
166
- $ p ->label = $ param ;
167
- $ signature ->parameters [] = $ p ;
168
- }
169
- $ help ->signatures [] = $ signature ;
170
- }
133
+ fseek ($ handle , $ node ->name ->getAttribute ('endFilePos ' ) + 2 );
134
+ $ params = fread ($ handle , ($ filePos - 1 ) - $ node ->name ->getAttribute ('endFilePos ' ));
135
+ fseek ($ handle , $ node ->name ->getAttribute ('startFilePos ' ));
136
+ $ method = fread ($ handle , ($ node ->name ->getAttribute ('endFilePos ' ) + 1 ) - $ node ->name ->getAttribute ('startFilePos ' ));
137
+ $ method = explode (':: ' , str_replace ('() ' , '' , $ method ), 2 );
138
+ $ method = $ method [1 ] ?? $ method [0 ];
139
+ $ fqn = $ this ->definitionResolver ->resolveReferenceNodeToFqn ($ node ->class );
140
+ $ def = $ this ->index ->getDefinition ($ fqn .':: ' .$ method .'() ' );
141
+ } else {
142
+ if (!preg_match ('(([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\((.*)$) ' , $ line , $ method )) {
143
+ fclose ($ handle );
144
+ return $ help ;
145
+ }
146
+ $ def = $ this ->index ->getDefinition ($ method [1 ] . '() ' );
147
+ $ params = $ method [2 ];
148
+ }
149
+ fclose ($ handle );
150
+
151
+ if ($ def ) {
152
+ $ method = preg_split ('(::|->) ' , str_replace ('() ' , '' , $ def ->fqn ), 2 );
153
+ $ method = $ method [1 ] ?? $ method [0 ];
154
+ $ signature = new SignatureInformation ;
155
+ $ signature ->label = $ method . '( ' .implode (', ' , $ def ->parameters ).') ' ;
156
+ $ signature ->documentation = $ def ->documentation ;
157
+ $ signature ->parameters = [];
158
+ foreach ($ def ->parameters as $ param ) {
159
+ $ p = new ParameterInformation ;
160
+ $ p ->label = $ param ;
161
+ $ signature ->parameters [] = $ p ;
162
+ }
163
+ $ help ->activeSignature = 0 ;
164
+ $ help ->activeParameter = 0 ;
165
+ if (strlen (trim ($ params ))) {
166
+ try {
167
+ $ params = $ this ->parser ->parse ('<?php $a = [ ' . $ params . ']; ' , $ this ->parserErrorHandler )[0 ]->expr ->items ;
168
+ $ help ->activeParameter = count ($ params );
169
+ } catch (\Exception $ e ) {
171
170
}
172
171
}
172
+ $ help ->signatures [] = $ signature ;
173
173
}
174
174
175
175
return $ help ;
0 commit comments