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

Camelcade / Perl5-IDEA / #525521660

24 Aug 2025 01:28PM UTC coverage: 75.89% (-6.3%) from 82.227%
#525521660

push

github

hurricup
Migrated coverage reporting to https://github.com/nbaztec/coveralls-jacoco-gradle-plugin

See: https://github.com/kt3k/coveralls-gradle-plugin/issues/119

14751 of 22639 branches covered (65.16%)

Branch coverage included in aggregate %.

31091 of 37767 relevant lines covered (82.32%)

0.82 hits per line

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

82.39
/plugin/backend/src/main/java/com/perl5/lang/pod/idea/completion/PodLinkCompletionProvider.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.pod.idea.completion;
18

19
import com.intellij.codeInsight.completion.CompletionParameters;
20
import com.intellij.codeInsight.completion.CompletionProvider;
21
import com.intellij.codeInsight.completion.CompletionResultSet;
22
import com.intellij.codeInsight.lookup.LookupElementBuilder;
23
import com.intellij.openapi.diagnostic.Logger;
24
import com.intellij.openapi.util.text.StringUtil;
25
import com.intellij.psi.PsiElement;
26
import com.intellij.psi.PsiFile;
27
import com.intellij.psi.tree.IElementType;
28
import com.intellij.psi.util.PsiTreeUtil;
29
import com.intellij.psi.util.PsiUtilCore;
30
import com.intellij.usageView.UsageViewUtil;
31
import com.intellij.util.ProcessingContext;
32
import com.perl5.PerlIcons;
33
import com.perl5.lang.perl.idea.completion.providers.processors.PerlCompletionProcessor;
34
import com.perl5.lang.perl.idea.completion.providers.processors.PerlSimpleCompletionProcessor;
35
import com.perl5.lang.perl.idea.completion.util.PerlPackageCompletionUtil;
36
import com.perl5.lang.perl.util.PerlPackageUtil;
37
import com.perl5.lang.pod.filetypes.PodFileType;
38
import com.perl5.lang.pod.parser.psi.PodCompositeElement;
39
import com.perl5.lang.pod.parser.psi.PodFile;
40
import com.perl5.lang.pod.parser.psi.PodStubsAwareRecursiveVisitor;
41
import com.perl5.lang.pod.parser.psi.PodTitledSection;
42
import com.perl5.lang.pod.parser.psi.mixin.PodFormatterL;
43
import com.perl5.lang.pod.parser.psi.mixin.PodFormatterX;
44
import com.perl5.lang.pod.parser.psi.mixin.PodSectionItem;
45
import com.perl5.lang.pod.parser.psi.util.PodFileUtil;
46
import com.perl5.lang.pod.parser.psi.util.PodRenderUtil;
47
import com.perl5.lang.pod.psi.PsiItemSection;
48
import com.perl5.lang.pod.psi.PsiPodFormatIndex;
49
import org.jetbrains.annotations.Contract;
50
import org.jetbrains.annotations.NotNull;
51
import org.jetbrains.annotations.Nullable;
52

53
import java.util.HashSet;
54
import java.util.Set;
55

56
import static com.perl5.lang.pod.parser.PodElementTypesGenerated.*;
57

58
public class PodLinkCompletionProvider extends CompletionProvider<CompletionParameters> {
1✔
59
  private static final Logger LOG = Logger.getInstance(PodLinkCompletionProvider.class);
1✔
60

61
  @Override
62
  protected void addCompletions(@NotNull CompletionParameters parameters,
63
                                @NotNull ProcessingContext context,
64
                                @NotNull CompletionResultSet result) {
65
    PsiElement element = parameters.getPosition();
1✔
66
    PsiElement linkPart = element.getParent();
1✔
67
    PodFormatterL linkElement = PsiTreeUtil.getParentOfType(element, PodFormatterL.class);
1✔
68
    if (linkElement == null) {
1!
69
      return;
×
70
    }
71
    IElementType parentType = PsiUtilCore.getElementType(linkPart);
1✔
72
    PerlSimpleCompletionProcessor completionProcessor = new PerlSimpleCompletionProcessor(parameters, result, linkElement);
1✔
73

74
    if (parentType == LINK_TEXT && element.getPrevSibling() == null) {
1!
75
      processFilesCompletions(completionProcessor);
×
76
    }
77
    if (parentType == LINK_NAME) {
1✔
78
      processFilesCompletions(completionProcessor);
1✔
79
    }
80
    if (parentType == LINK_SECTION) {
1✔
81
      addSectionsCompletions(PodFileUtil.getTargetFile(linkElement), completionProcessor);
1✔
82
    }
83
    completionProcessor.logStatus(getClass());
1✔
84
  }
1✔
85

86
  @SuppressWarnings("UnusedReturnValue")
87
  protected boolean processFilesCompletions(@NotNull PerlCompletionProcessor completionProcessor) {
88
    PodFormatterL linkElement = (PodFormatterL)completionProcessor.getLeafElement();
1✔
89
    final Set<String> foundPods = new HashSet<>();
1✔
90
    PerlPackageUtil.processIncFilesForPsiElement(linkElement, (file, classRoot) -> {
1✔
91
      String className = PodFileUtil.getPackageNameFromVirtualFile(file, classRoot);
1✔
92
      if (StringUtil.isNotEmpty(className)) {
1!
93
        boolean isBuiltIn = false;
1✔
94
        if (StringUtil.startsWith(className, "pod::")) {
1✔
95
          isBuiltIn = true;
1✔
96
          className = className.substring(5);
1✔
97
        }
98
        if (completionProcessor.matches(className) && foundPods.add(className)) {
1!
99
          if (!completionProcessor
1✔
100
            .process(LookupElementBuilder.create(file, className).withIcon(PerlIcons.POD_FILE).withBoldness(isBuiltIn))) {
1!
101
            return false;
×
102
          }
103
        }
104
      }
105
      return completionProcessor.result();
1✔
106
    }, PodFileType.INSTANCE);
107

108
    return PerlPackageUtil.processPackageFilesForPsiElement(linkElement, (packageName, file) -> {
1✔
109
      if (StringUtil.isNotEmpty(packageName) && completionProcessor.matches(packageName) && foundPods.add(packageName)) {
1!
110
        return PerlPackageCompletionUtil.processPackageLookupElement(file, packageName, null, completionProcessor, false);
1✔
111
      }
112
      return completionProcessor.result();
1✔
113
    });
114
  }
115

116
  protected static void addSectionsCompletions(@Nullable PsiFile targetFile,
117
                                               @NotNull PerlCompletionProcessor completionProcessor) {
118
    if (targetFile == null) {
1!
119
      return;
×
120
    }
121
    Set<String> distinctString = new HashSet<>();
1✔
122

123
    targetFile.accept(new PodStubsAwareRecursiveVisitor() {
1✔
124
      @Override
125
      public void visitElement(@NotNull PsiElement element) {
126
        if (completionProcessor.result()) {
1✔
127
          super.visitElement(element);
1✔
128
        }
129
      }
1✔
130

131
      @Override
132
      public void visitTargetableSection(PodTitledSection o) {
133
        String title = cleanItemText(o.getTitleText());
1✔
134
        if (completionProcessor.matches(title) && distinctString.add(title)) {
1✔
135
          completionProcessor.process(LookupElementBuilder.create(o, PodRenderUtil.escapeTitle(title))
1✔
136
                                        .withLookupString(title)
1✔
137
                                        .withPresentableText(title)
1✔
138
                                        .withIcon(PerlIcons.POD_FILE)
1✔
139
                                        .withTypeText(UsageViewUtil.getType(o)));
1✔
140
        }
141
        super.visitTargetableSection(o);
1✔
142
      }
1✔
143

144
      @Override
145
      public void visitItemSection(@NotNull PsiItemSection o) {
146
        if (((PodSectionItem)o).isTargetable()) {
1✔
147
          super.visitItemSection(o);
1✔
148
        }
149
        else {
150
          visitElement(o);
1✔
151
        }
152
      }
1✔
153

154
      @Contract("null->null")
155
      private @Nullable String cleanItemText(@Nullable String itemText) {
156
        if (itemText == null) {
1!
157
          return null;
×
158
        }
159
        String trimmed = StringUtil.trimLeading(itemText.trim(), '*');
1✔
160
        if (isNumber(trimmed)) {
1✔
161
          return null;
1✔
162
        }
163
        return PodRenderUtil.trimItemText(trimmed);
1✔
164
      }
165

166
      private static boolean isNumber(@NotNull String text){
167
        if( text.isEmpty()){
1!
168
          return false;
×
169
        }
170

171
        for( int i = 0; i < text.length(); i++){
1✔
172
          if( !Character.isDigit(text.charAt(i))){
1✔
173
            return false;
1✔
174
          }
175
        }
176

177
        return true;
1✔
178
      }
179

180
      @Override
181
      public void visitPodFormatIndex(@NotNull PsiPodFormatIndex o) {
182
        assert o instanceof PodFormatterX;
1!
183
        if (!((PodFormatterX)o).isMeaningful()) {
1✔
184
          return;
1✔
185
        }
186
        String indexTitle = ((PodFormatterX)o).getTitleText();
1✔
187
        if (StringUtil.isEmpty(indexTitle) || !distinctString.add(indexTitle)) {
1!
188
          return;
1✔
189
        }
190

191
        String escapedIndexTitle = PodRenderUtil.escapeTitle(indexTitle);
1✔
192
        if (!completionProcessor.matches(indexTitle) && !completionProcessor.matches(escapedIndexTitle)) {
1!
193
          return;
1✔
194
        }
195

196
        PsiElement indexTarget = ((PodFormatterX)o).getIndexTarget();
1✔
197
        String targetPresentableText;
198
        if (indexTarget instanceof PodFile podFile) {
1!
199
          targetPresentableText = cleanItemText(podFile.getPodLinkText());
×
200
        }
201
        else if (indexTarget instanceof PodCompositeElement podCompositeElement) {
1!
202
          targetPresentableText = cleanItemText(podCompositeElement.getPresentableText());
1✔
203
        }
204
        else {
205
          LOG.warn("Unhandled index target: " + indexTarget);
×
206
          return;
×
207
        }
208
        String tailText = null;
1✔
209
        if (targetPresentableText != null) {
1!
210
          tailText = "(" + targetPresentableText + ")";
1✔
211
        }
212
        completionProcessor.process(
1✔
213
          LookupElementBuilder.create(o, escapedIndexTitle)
1✔
214
            .withLookupString(indexTitle)
1✔
215
            .withPresentableText(indexTitle)
1✔
216
            .withTailText(tailText)
1✔
217
            .withTypeText(UsageViewUtil.getType(o))
1✔
218
            .withIcon(PerlIcons.POD_FILE)
1✔
219
        );
220
      }
1✔
221
    });
222
  }
1✔
223
}
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