@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.analysis.api.analyzeCopy
1616import org.jetbrains.kotlin.analysis.api.projectStructure.KaDanglingFileResolutionMode
1717import org.jetbrains.kotlin.analysis.api.renderer.types.KaTypeRenderer
1818import org.jetbrains.kotlin.analysis.api.renderer.types.impl.KaTypeRendererForSource
19+ import org.jetbrains.kotlin.analysis.api.scopes.KaScope
1920import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol
2021import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind
2122import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol
@@ -31,13 +32,13 @@ import org.jetbrains.kotlin.analysis.api.symbols.KaTypeAliasSymbol
3132import org.jetbrains.kotlin.analysis.api.symbols.KaTypeParameterSymbol
3233import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol
3334import org.jetbrains.kotlin.analysis.api.symbols.name
35+ import org.jetbrains.kotlin.analysis.api.symbols.receiverType
3436import org.jetbrains.kotlin.analysis.api.types.KaClassType
3537import org.jetbrains.kotlin.analysis.api.types.KaType
3638import org.jetbrains.kotlin.com.intellij.psi.PsiElement
3739import org.jetbrains.kotlin.name.Name
3840import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
3941import org.jetbrains.kotlin.psi.KtElement
40- import org.jetbrains.kotlin.psi.KtFile
4142import org.jetbrains.kotlin.psi.KtQualifiedExpression
4243import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression
4344import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
@@ -92,21 +93,48 @@ fun CompilationEnvironment.complete(params: CompletionParams): CompletionResult
9293 resolutionMode = KaDanglingFileResolutionMode .PREFER_SELF ,
9394 ) {
9495 val completionContext = determineCompletionContext(elementAtOffset)
96+
97+ // Find the nearest KtElement parent for scope resolution
98+ val ktElement = elementAtOffset.getParentOfType<KtElement >(strict = false )
99+ val scopeContext = ktElement?.let { element -> completionKtFile.scopeContext(element) }
100+ val compositeScope = scopeContext?.compositeScope()
95101 val items = mutableListOf<CompletionItem >()
96102
97- when (completionContext) {
98- CompletionContext .Scope -> collectScopeCompletions(
99- element = elementAtOffset,
100- file = completionKtFile,
101- partial = partial,
102- to = items
103+ if (ktElement == null ) {
104+ logger.error(
105+ " Cannot find parent of element {} with partial {}" ,
106+ elementAtOffset,
107+ partial
103108 )
104109
105- CompletionContext .Member -> collectMemberCompletions(
106- element = elementAtOffset,
107- partial = partial,
108- to = items
110+ return @analyzeCopy CompletionResult .EMPTY
111+ }
112+
113+ if (compositeScope == null ) {
114+ logger.error(
115+ " Unable to get CompositeScope for element {} with partial {}" ,
116+ compositeScope,
117+ partial
109118 )
119+ return @analyzeCopy CompletionResult .EMPTY
120+ }
121+
122+ when (completionContext) {
123+ CompletionContext .Scope ->
124+ collectScopeCompletions(
125+ ktElement = ktElement,
126+ scope = compositeScope,
127+ partial = partial,
128+ to = items
129+ )
130+
131+ CompletionContext .Member ->
132+ collectMemberCompletions(
133+ scope = compositeScope,
134+ element = elementAtOffset,
135+ partial = partial,
136+ to = items
137+ )
110138 }
111139
112140 CompletionResult (items)
@@ -122,6 +150,7 @@ fun CompilationEnvironment.complete(params: CompletionParams): CompletionResult
122150}
123151
124152private fun KaSession.collectMemberCompletions (
153+ scope : KaScope ,
125154 element : PsiElement ,
126155 partial : String ,
127156 to : MutableList <CompletionItem >
@@ -140,12 +169,22 @@ private fun KaSession.collectMemberCompletions(
140169 return
141170 }
142171
172+ logger.info(
173+ " Complete members of {}: {} [{}] matching '{}'" ,
174+ receiver,
175+ receiverType,
176+ receiver.text,
177+ partial
178+ )
179+
143180 collectMembersFromType(receiverType, partial, to)
144181
145182 if (qualifiedExpr is KtSafeQualifiedExpression ) {
146183 val nonNullType = receiverType.withNullability(isMarkedNullable = false )
147184 collectMembersFromType(nonNullType, partial, to)
148185 }
186+
187+ collectExtensionFunctions(scope, partial, receiverType, to)
149188}
150189
151190@OptIn(KaExperimentalApi ::class )
@@ -156,8 +195,15 @@ private fun KaSession.collectMembersFromType(
156195) {
157196 val typeScope = receiverType.scope
158197 if (typeScope != null ) {
159- to + = toCompletionItems(typeScope.getCallableSignatures { name -> matchesPrefix(name, partial) }.map { it.symbol }, partial)
160- to + = toCompletionItems(typeScope.getClassifierSymbols { name -> matchesPrefix(name, partial) }, partial)
198+ val callables =
199+ typeScope.getCallableSignatures { name -> matchesPrefix(name, partial) }
200+ .map { it.symbol }
201+
202+ val classifiers =
203+ typeScope.getClassifierSymbols { name -> matchesPrefix(name, partial) }
204+
205+ to + = toCompletionItems(callables, partial)
206+ to + = toCompletionItems(classifiers, partial)
161207 return
162208 }
163209
@@ -166,22 +212,37 @@ private fun KaSession.collectMembersFromType(
166212 val classSymbol = classType.symbol as ? KaClassSymbol ? : return
167213 val memberScope = classSymbol.memberScope
168214
169- to + = toCompletionItems(memberScope.callables { name -> matchesPrefix(name, partial) }, partial)
170- to + = toCompletionItems(memberScope.classifiers { name -> matchesPrefix(name, partial) }, partial)
215+ val callables = memberScope.callables { name -> matchesPrefix(name, partial) }
216+ val classifiers = memberScope.classifiers { name -> matchesPrefix(name, partial) }
217+
218+ to + = toCompletionItems(callables, partial)
219+ to + = toCompletionItems(classifiers, partial)
171220}
172221
173- private fun KaSession.collectScopeCompletions (
174- element : PsiElement ,
175- file : KtFile ,
222+ private fun KaSession.collectExtensionFunctions (
223+ scope : KaScope ,
176224 partial : String ,
225+ receiverType : KaType ,
177226 to : MutableList <CompletionItem >
178227) {
179- // Find the nearest KtElement parent for scope resolution
180- val ktElement = element.getParentOfType<KtElement >(strict = false )
181- if (ktElement == null ) {
182- logger.error(" Cannot find parent of element {} with partial {}" , element, partial)
183- return
184- }
228+ val extensionSymbols =
229+ scope.callables { name -> matchesPrefix(name, partial) }
230+ .filter { symbol ->
231+ if (! symbol.isExtension) return @filter false
232+
233+ val extReceiverType = symbol.receiverType ? : return @filter false
234+ receiverType.isSubtypeOf(extReceiverType)
235+ }
236+
237+ to + = toCompletionItems(extensionSymbols, partial)
238+ }
239+
240+ private fun KaSession.collectScopeCompletions (
241+ ktElement : KtElement ,
242+ scope : KaScope ,
243+ partial : String ,
244+ to : MutableList <CompletionItem >,
245+ ) {
185246
186247 logger.info(
187248 " Complete scope members of {}: [{}] matching '{}'" ,
@@ -190,21 +251,30 @@ private fun KaSession.collectScopeCompletions(
190251 partial
191252 )
192253
193- val scopeContext = file.scopeContext(ktElement)
194- val compositeScope = scopeContext.compositeScope()
195-
196- to + = toCompletionItems(compositeScope.callables { name -> matchesPrefix(name, partial) }, partial)
197- to + = toCompletionItems(compositeScope.classifiers { name -> matchesPrefix(name, partial) }, partial)
254+ to + = toCompletionItems(
255+ scope.callables { name -> matchesPrefix(name, partial) },
256+ partial
257+ )
258+ to + = toCompletionItems(
259+ scope.classifiers { name -> matchesPrefix(name, partial) },
260+ partial
261+ )
198262}
199263
200264@JvmName(" callablesToCompletionItems" )
201- private fun KaSession.toCompletionItems (callables : Sequence <KaCallableSymbol >, partial : String ): Sequence <CompletionItem > =
265+ private fun KaSession.toCompletionItems (
266+ callables : Sequence <KaCallableSymbol >,
267+ partial : String
268+ ): Sequence <CompletionItem > =
202269 callables.mapNotNull {
203270 callableSymbolToCompletionItem(it, partial)
204271 }
205272
206273@JvmName(" classifiersToCompletionItems" )
207- private fun KaSession.toCompletionItems (classifiers : Sequence <KaClassifierSymbol >, partial : String ): Sequence <CompletionItem > =
274+ private fun KaSession.toCompletionItems (
275+ classifiers : Sequence <KaClassifierSymbol >,
276+ partial : String
277+ ): Sequence <CompletionItem > =
208278 classifiers.mapNotNull {
209279 classifierSymbolToCompletionItem(it, partial)
210280 }
@@ -284,7 +354,11 @@ private fun KaSession.classifierSymbolToCompletionItem(
284354 val item = createSymbolCompletionItem(symbol, partial) ? : return null
285355 item.detail = when (symbol) {
286356 is KaClassSymbol -> symbol.classId?.asFqNameString() ? : " "
287- is KaTypeAliasSymbol -> renderName(symbol.expandedType, KaTypeRendererForSource .WITH_QUALIFIED_NAMES )
357+ is KaTypeAliasSymbol -> renderName(
358+ symbol.expandedType,
359+ KaTypeRendererForSource .WITH_QUALIFIED_NAMES
360+ )
361+
288362 is KaTypeParameterSymbol -> item.ideLabel
289363 }
290364 return item
@@ -347,6 +421,12 @@ private fun partialIdentifier(prefix: String): String {
347421}
348422
349423private fun matchesPrefix (name : Name , partial : String ): Boolean {
424+ logger.info(
425+ " '{}' matches '{}': {}" ,
426+ name,
427+ partial,
428+ name.asString().startsWith(partial, ignoreCase = true )
429+ )
350430 if (partial.isEmpty()) return true
351431 return name.asString().startsWith(partial, ignoreCase = true )
352432}
0 commit comments