• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Camelcade / Perl5-IDEA / #525521635

20 Jul 2025 03:45PM UTC coverage: 82.266% (-0.09%) from 82.355%
#525521635

push

github

hurricup
Build 252.23892.248

30936 of 37605 relevant lines covered (82.27%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.07
/plugin/backend/src/main/java/com/perl5/lang/perl/idea/completion/util/PerlSubCompletionUtil.java
1
/*
2
 * Copyright 2015-2025 Alexandr Evstigneev
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.perl5.lang.perl.idea.completion.util;
18

19
import com.intellij.codeInsight.lookup.LookupElementBuilder;
20
import com.intellij.openapi.util.text.StringUtil;
21
import com.intellij.psi.PsiElement;
22
import com.intellij.psi.PsiFile;
23
import com.intellij.psi.PsiNamedElement;
24
import com.intellij.psi.PsiReference;
25
import com.intellij.psi.util.PsiTreeUtil;
26
import com.perl5.lang.perl.extensions.packageprocessor.PerlExportDescriptor;
27
import com.perl5.lang.perl.idea.codeInsight.typeInference.value.*;
28
import com.perl5.lang.perl.idea.codeInsight.typeInference.value.serialization.PerlCallValueBackendHelper;
29
import com.perl5.lang.perl.idea.completion.inserthandlers.SubSelectionHandler;
30
import com.perl5.lang.perl.idea.completion.providers.processors.PerlCompletionProcessor;
31
import com.perl5.lang.perl.idea.completion.providers.processors.PerlSimpleCompletionProcessor;
32
import com.perl5.lang.perl.idea.completion.providers.processors.PerlSimpleDelegatingCompletionProcessor;
33
import com.perl5.lang.perl.idea.completion.providers.processors.PerlVariableCompletionProcessor;
34
import com.perl5.lang.perl.psi.*;
35
import com.perl5.lang.perl.psi.impl.PerlImplicitSubDefinition;
36
import com.perl5.lang.perl.psi.references.PerlImplicitDeclarationsService;
37
import com.perl5.lang.perl.util.PerlPackageUtil;
38
import com.perl5.lang.perl.util.PerlPackageUtilCore;
39
import org.jetbrains.annotations.NotNull;
40
import org.jetbrains.annotations.Nullable;
41

42
import java.util.Collections;
43
import java.util.HashSet;
44
import java.util.Set;
45

46

47
public class PerlSubCompletionUtil {
×
48
  public static final SubSelectionHandler SUB_SELECTION_HANDLER = new SubSelectionHandler();
1✔
49

50

51
  public static boolean processSubDefinitionLookupElement(@NotNull PerlSubDefinitionElement subDefinition,
52
                                                          @NotNull PerlCompletionProcessor completionProcessor) {
53
    return processSubDefinitionLookupElement(subDefinition, null, completionProcessor);
1✔
54
  }
55

56
  public static boolean processSubDefinitionLookupElement(@NotNull PerlSubDefinitionElement subDefinition,
57
                                                          @Nullable PerlExportDescriptor exportDescriptor,
58
                                                          @NotNull PerlCompletionProcessor completionProcessor) {
59
    return processSubDefinitionLookupElement(
1✔
60
      exportDescriptor == null ? subDefinition.getSubName() : exportDescriptor.getImportedName(),
1✔
61
      subDefinition, completionProcessor);
62
  }
63

64
  public static boolean processSubDefinitionLookupElement(@NotNull String nameToUse,
65
                                                          @NotNull PerlSubDefinitionElement subDefinition,
66
                                                          @NotNull PerlCompletionProcessor completionProcessor) {
67
    if (!completionProcessor.matches(nameToUse)) {
1✔
68
      return completionProcessor.result();
1✔
69
    }
70
    LookupElementBuilder newElement = LookupElementBuilder
1✔
71
      .create(subDefinition, nameToUse)
1✔
72
      .withIcon(subDefinition.getIcon(0))
1✔
73
      .withStrikeoutness(subDefinition.isDeprecated())
1✔
74
      .withTypeText(subDefinition.getNamespaceName(), true);
1✔
75

76
    String argsString = subDefinition.getSubArgumentsListAsString();
1✔
77

78
    if (!argsString.isEmpty()) {
1✔
79
      newElement = newElement
1✔
80
        .withInsertHandler(SUB_SELECTION_HANDLER)
1✔
81
        .withTailText(argsString);
1✔
82
    }
83

84
    return completionProcessor.process(newElement);
1✔
85
  }
86

87
  public static boolean processImportedEntityLookupElement(@NotNull PsiElement element,
88
                                                           @NotNull PerlExportDescriptor exportDescriptor,
89
                                                           @NotNull PerlCompletionProcessor completionProcessor) {
90
    return switch (element) {
1✔
91
      case PerlSubDefinitionElement subDefinitionElement ->
1✔
92
        processSubDefinitionLookupElement(subDefinitionElement, exportDescriptor, completionProcessor);
1✔
93
      case PerlSubDeclarationElement subDeclarationElement ->
1✔
94
        processSubDeclarationLookupElement(subDeclarationElement, exportDescriptor, completionProcessor);
1✔
95
      case PerlGlobVariableElement globVariableElement ->
×
96
        processGlobLookupElement(globVariableElement, exportDescriptor, completionProcessor);
×
97
      default -> throw new RuntimeException("Don't know how to make lookup element for " + element.getClass());
×
98
    };
99
  }
100

101
  public static boolean processSubDeclarationLookupElement(@NotNull PerlSubDeclarationElement subDeclaration,
102
                                                           @NotNull PerlCompletionProcessor completionProcessor) {
103
    return processSubDeclarationLookupElement(subDeclaration, null, completionProcessor);
1✔
104
  }
105

106
  public static boolean processSubDeclarationLookupElement(@NotNull PerlSubDeclarationElement subDeclaration,
107
                                                           @Nullable PerlExportDescriptor exportDescriptor,
108
                                                           @NotNull PerlCompletionProcessor completionProcessor) {
109

110
    String lookupString = exportDescriptor == null ? subDeclaration.getSubName() : exportDescriptor.getImportedName();
1✔
111
    if (!completionProcessor.matches(lookupString)) {
1✔
112
      return completionProcessor.result();
×
113
    }
114

115
    return completionProcessor.process(LookupElementBuilder
1✔
116
                                         .create(subDeclaration, lookupString)
1✔
117
                                         .withIcon(subDeclaration.getIcon(0))
1✔
118
                                         .withStrikeoutness(subDeclaration.isDeprecated())
1✔
119
                                         .withInsertHandler(SUB_SELECTION_HANDLER)
1✔
120
                                         .withTypeText(subDeclaration.getNamespaceName(), true)
1✔
121
    );
122
  }
123

124
  public static boolean processGlobLookupElement(@NotNull PerlGlobVariableElement globVariable,
125
                                                 @NotNull PerlCompletionProcessor completionProcessor) {
126
    return processGlobLookupElement(globVariable, null, completionProcessor);
1✔
127
  }
128

129
  /**
130
   * Probably duplicate of {@link PerlVariableCompletionUtil#processVariableLookupElement(PerlGlobVariableElement, boolean, PerlVariableCompletionProcessor)}
131
   */
132
  public static boolean processGlobLookupElement(@NotNull PerlGlobVariableElement globVariable,
133
                                                 @Nullable PerlExportDescriptor exportDescriptor,
134
                                                 @NotNull PerlCompletionProcessor completionProcessor) {
135
    String lookupString = exportDescriptor == null ? globVariable.getName() : exportDescriptor.getImportedName();
1✔
136
    if (!completionProcessor.matches(lookupString)) {
1✔
137
      return completionProcessor.result();
×
138
    }
139

140
    return completionProcessor.process(LookupElementBuilder
1✔
141
                                         .create(globVariable, StringUtil.notNullize(lookupString))
1✔
142
                                         .withIcon(globVariable.getIcon(0))
1✔
143
                                         .withInsertHandler(SUB_SELECTION_HANDLER)
1✔
144
                                         .withTypeText(globVariable.getNamespaceName(), true)
1✔
145
    );
146
  }
147

148
  @SuppressWarnings("UnusedReturnValue")
149
  public static boolean processUnresolvedSubsLookups(@NotNull PerlSubElement subDefinition,
150
                                                     @NotNull PerlCompletionProcessor completionProcessor) {
151
    final String packageName = subDefinition.getNamespaceName();
1✔
152
    if (packageName == null) {
1✔
153
      return completionProcessor.result();
×
154
    }
155

156
    final Set<String> namesSet = new HashSet<>();
1✔
157
    PsiFile containingFile = subDefinition.getContainingFile();
1✔
158
    containingFile.accept(new PerlCompletionRecursiveVisitor(completionProcessor) {
1✔
159
      @Override
160
      protected boolean shouldVisitLightElements() {
161
        return true;
1✔
162
      }
163

164
      @Override
165
      public void visitMethod(@NotNull PsiPerlMethod method) {
166
        doVisitMethod(method);
1✔
167
        super.visitMethod(method);
1✔
168
      }
1✔
169

170
      private void doVisitMethod(@NotNull PsiPerlMethod method) {
171
        PerlCallValue methodValue = PerlCallValue.from(method);
1✔
172
        if (methodValue == null) {
1✔
173
          return;
×
174
        }
175

176
        PerlValue namespaceValue = methodValue.getNamespaceNameValue();
1✔
177

178
        if (!namespaceValue.canRepresentNamespace(packageName)) {
1✔
179
          return;
×
180
        }
181
        PerlSubNameElement subNameElement = method.getSubNameElement();
1✔
182

183
        if (subNameElement == null || !subNameElement.isValid()) {
1✔
184
          return;
×
185
        }
186

187
        String subName = subNameElement.getName();
1✔
188

189
        if (StringUtil.isEmpty(subName) || namesSet.contains(subName) || !completionProcessor.matches(subName)) {
1✔
190
          return;
×
191
        }
192
        PsiReference[] references = subNameElement.getReferences();
1✔
193
        if (references.length == 0) {
1✔
194
          return;
×
195
        }
196

197
        for (PsiReference reference : references) {
1✔
198
          if (reference.resolve() != null) {
1✔
199
            return;
1✔
200
          }
201
        }
202
        // unresolved
203
        namesSet.add(subName);
1✔
204
        completionProcessor.process(LookupElementBuilder.create(subName));
1✔
205
      }
1✔
206
    });
207
    return completionProcessor.result();
1✔
208
  }
209

210
  public static void processWithNotOverriddenSubs(@NotNull PerlSubElement subDefinition,
211
                                                  @NotNull PerlCompletionProcessor completionProcessor) {
212
    PerlPackageUtil.processNotOverridedMethods(
1✔
213
      PsiTreeUtil.getParentOfType(subDefinition, PerlNamespaceDefinitionElement.class),
1✔
214
      subDefinitionBase -> {
215
        String lookupString = subDefinitionBase.getSubName();
1✔
216
        if (completionProcessor.matches(lookupString)) {
1✔
217
          return completionProcessor.process(LookupElementBuilder.create(subDefinitionBase, lookupString));
1✔
218
        }
219
        return completionProcessor.result();
×
220
      }
221
    );
222
  }
1✔
223

224
  public static boolean processSubsCompletionsForCallValue(@NotNull PerlSimpleCompletionProcessor completionProcessor,
225
                                                           @NotNull PerlCallValue perlValue,
226
                                                           boolean isStatic) {
227
    return PerlCallValueBackendHelper.get(perlValue).processTargetNamespaceElements(perlValue,
1✔
228
      completionProcessor.getLeafElement(), new PerlNamespaceItemProcessor<>() {
1✔
229
        @Override
230
        public boolean processItem(@NotNull PsiNamedElement element) {
231
          return switch (element) {
1✔
232
            case PerlImplicitSubDefinition implicitSubDefinition
1✔
233
              when implicitSubDefinition.isAnonymous() -> //noinspection DuplicateBranchesInSwitch
1✔
234
              completionProcessor.result();
×
235
            case PerlSubDefinitionElement subDefinitionElement
1✔
236
              when !subDefinitionElement.isAnonymous() &&
1✔
237
                   (isStatic && subDefinitionElement.isStatic() || subDefinitionElement.isMethod()) ->
1✔
238
              processSubDefinitionLookupElement(subDefinitionElement, completionProcessor);
1✔
239
            case PerlSubDeclarationElement subDeclarationElement
1✔
240
              when (isStatic && subDeclarationElement.isStatic() || subDeclarationElement.isMethod()) ->
1✔
241
              processSubDeclarationLookupElement(subDeclarationElement, completionProcessor);
1✔
242
            case PerlGlobVariableElement perlGlobVariableElement
1✔
243
              when perlGlobVariableElement.isLeftSideOfAssignment() && StringUtil.isNotEmpty(element.getName()) ->
1✔
244
              processGlobLookupElement(perlGlobVariableElement, completionProcessor);
1✔
245
            default -> completionProcessor.result();
1✔
246
          };
247
        }
248

249
        @Override
250
        public boolean processImportedItem(@NotNull PsiNamedElement element,
251
                                           @NotNull PerlExportDescriptor exportDescriptor) {
252
          return processImportedEntityLookupElement(element, exportDescriptor, completionProcessor);
1✔
253
        }
254

255
        @Override
256
        public boolean processOrphanDescriptor(@NotNull PerlExportDescriptor exportDescriptor) {
257
          if (exportDescriptor.isSub()) {
1✔
258
            return exportDescriptor.processLookupElement(completionProcessor);
1✔
259
          }
260
          return completionProcessor.result();
1✔
261
        }
262
      });
263
  }
264

265
  public static boolean processBuiltInSubsLookupElements(PerlSimpleCompletionProcessor completionProcessor) {
266
    PerlCompletionProcessor builtInCompletionProcessor = new PerlSimpleDelegatingCompletionProcessor(completionProcessor) {
1✔
267
      @Override
268
      public void addElement(@NotNull LookupElementBuilder lookupElement) {
269
        super.addElement(lookupElement.withBoldness(true));
1✔
270
      }
1✔
271
    };
272

273
    return PerlImplicitDeclarationsService.getInstance(completionProcessor.getProject()).processSubs(
1✔
274
      sub -> sub.isBuiltIn() ? processSubDefinitionLookupElement(sub, builtInCompletionProcessor)
1✔
275
                             : builtInCompletionProcessor.result());
1✔
276
  }
277

278
  /**
279
   * Processes all subs applicable at current context. Declarations, imports, built-ins.
280
   */
281
  @SuppressWarnings("UnusedReturnValue")
282
  public static boolean processContextSubsLookupElements(@NotNull PerlSimpleCompletionProcessor completionProcessor) {
283
    if (!PerlSubCompletionUtil.processBuiltInSubsLookupElements(completionProcessor)) {
1✔
284
      return false;
×
285
    }
286
    PerlCallStaticValue callValue = new PerlCallStaticValue(
1✔
287
      PerlPackageUtilCore.getContextType(completionProcessor.getLeafElement()), PerlScalarValue.create("dummy"), Collections.emptyList(),
1✔
288
      false);
289
    return processSubsCompletionsForCallValue(completionProcessor, callValue, true);
1✔
290
  }
291
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc