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

Camelcade / Perl5-IDEA / #525521868

27 Jun 2026 07:39AM UTC coverage: 76.112% (-0.1%) from 76.22%
#525521868

Pull #3225

github

web-flow
Merge 5e3b8275b into 724363189
Pull Request #3225: Hurricup/gradle bump

14745 of 22542 branches covered (65.41%)

Branch coverage included in aggregate %.

31061 of 37640 relevant lines covered (82.52%)

0.83 hits per line

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

85.47
/plugin/intelliLang/src/main/java/com/perl5/lang/perl/intellilang/PerlStringLanguageInjector.java
1
/*
2
 * Copyright 2015-2024 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.intellilang;
18

19
import com.intellij.codeInsight.completion.CompletionUtil;
20
import com.intellij.lang.Language;
21
import com.intellij.lang.injection.MultiHostRegistrar;
22
import com.intellij.openapi.util.RecursionManager;
23
import com.intellij.openapi.util.TextRange;
24
import com.intellij.openapi.util.registry.Registry;
25
import com.intellij.psi.ElementManipulators;
26
import com.intellij.psi.PsiElement;
27
import com.intellij.psi.PsiLanguageInjectionHost;
28
import com.intellij.psi.PsiReference;
29
import com.intellij.psi.search.LocalSearchScope;
30
import com.intellij.psi.search.searches.ReferencesSearch;
31
import com.intellij.psi.util.CachedValueProvider;
32
import com.intellij.psi.util.CachedValuesManager;
33
import com.intellij.psi.util.PsiUtilCore;
34
import com.intellij.util.Query;
35
import com.perl5.lang.perl.PerlLanguage;
36
import com.perl5.lang.perl.idea.intellilang.PerlInjectionMarkersService;
37
import com.perl5.lang.perl.psi.*;
38
import com.perl5.lang.perl.psi.mixins.PerlStringMixin;
39
import com.perl5.lang.perl.psi.utils.PerlAnnotations;
40
import org.jetbrains.annotations.Contract;
41
import org.jetbrains.annotations.NotNull;
42
import org.jetbrains.annotations.Nullable;
43

44
import java.util.Collections;
45
import java.util.List;
46
import java.util.Objects;
47

48
import static com.perl5.lang.perl.parser.PerlElementTypesGenerated.OPERATOR_ASSIGN;
49

50

51
public class PerlStringLanguageInjector extends PerlLiteralLanguageInjector {
1✔
52
  @Override
53
  public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement host) {
54
    if (!(host instanceof PerlStringMixin stringMixin) || !stringMixin.isValidHost() || PerlAnnotations.isInjectionSuppressed(host)) {
1!
55
      return;
1✔
56
    }
57

58
    // before element
59
    PerlAnnotationInject injectAnnotation = PerlAnnotations.getAnyAnnotationByClass(stringMixin, PerlAnnotationInject.class);
1✔
60
    if (injectAnnotation != null) {
1✔
61
      injectByAnnotation(stringMixin, registrar, injectAnnotation);
1✔
62
    }
63

64
    // program context
65
    if (Registry.is("perl5.eval.auto.injection", true)) {
1✔
66
      PsiElement context = getPerlInjectionContext(stringMixin);
1✔
67
      if (context != null) {
1✔
68
        injectLanguage(stringMixin, registrar, PerlLanguage.INSTANCE);
1✔
69
      }
70
    }
71
  }
1✔
72

73
  @Contract("null->null;!null->!null")
74
  public static @Nullable PsiElement getInjectionContextOrSelf(@Nullable PsiElement context) {
75
    if (context instanceof PsiLanguageInjectionHost injectionHost) {
1✔
76
      return Objects.requireNonNullElse(getPerlInjectionContext(injectionHost), context);
1✔
77
    }
78
    return context;
1✔
79
  }
80

81
  @Override
82
  public @NotNull List<? extends Class<? extends PsiElement>> elementsToInjectIn() {
83
    return Collections.singletonList(PerlStringMixin.class);
1!
84
  }
85

86
  public static @Nullable PsiElement getPerlInjectionContext(@NotNull PsiLanguageInjectionHost host) {
87
    return CachedValuesManager.getCachedValue(
1✔
88
      host, () -> {
89
        var result = RecursionManager.doPreventingRecursion(host, false, () -> computeInjectionContext(host));
1✔
90
        return CachedValueProvider.Result.create(result, host.getContainingFile());
1✔
91
      });
92
  }
93

94
  private static @Nullable PsiElement computeInjectionContext(@NotNull PsiLanguageInjectionHost host) {
95
    if (host instanceof PsiPerlStringXq) {
1✔
96
      return null;
1✔
97
    }
98
    PsiElement parent = host.getParent();
1✔
99
    if (parent instanceof PsiPerlEvalExpr) {
1✔
100
      return parent;
1✔
101
    }
102

103
    if (!(parent instanceof PsiPerlAssignExpr assignExpr)) {
1✔
104
      return null;
1✔
105
    }
106

107
    if (PsiUtilCore.getElementType(assignExpr.getRightOperatorElement(host)) != OPERATOR_ASSIGN) {
1✔
108
      return null;
1✔
109
    }
110

111
    PsiElement variable = assignExpr.getLeftPartOfAssignment(host);
1✔
112
    if (variable instanceof PerlVariableDeclarationExpr variableDeclarationExpr) {
1✔
113
      List<PsiPerlVariableDeclarationElement> variables = variableDeclarationExpr.getVariableDeclarationElementList();
1✔
114
      if (variables.size() != 1) {
1!
115
        return null;
×
116
      }
117
      variable = variables.getFirst();
1✔
118
    }
1✔
119
    else if (variable instanceof PsiPerlScalarVariable scalarVariable) {
1✔
120
      PerlVariableDeclarationElement variableDeclarationElement = scalarVariable.getLexicalDeclaration();
1✔
121
      if (variableDeclarationElement == null) {
1!
122
        return null;
×
123
      }
124
      variable = variableDeclarationElement;
1✔
125
    }
1✔
126
    else {
127
      return null;
1✔
128
    }
129
    PsiElement realVariable = CompletionUtil.getOriginalOrSelf(variable);
1✔
130
    Query<PsiReference> references =
1✔
131
      ReferencesSearch.search(realVariable, new LocalSearchScope(host.getContainingFile().getOriginalFile()));
1✔
132
    for (PsiReference reference : references) {
1✔
133
      PsiElement referenceElement = reference.getElement();
1✔
134
      PsiElement variableUsage = referenceElement.getParent();
1✔
135
      PsiElement usageContext = variableUsage.getParent();
1✔
136
      if (usageContext instanceof PsiPerlEvalExpr) {
1✔
137
        return usageContext;
1✔
138
      }
139
    }
1✔
140
    return null;
1✔
141
  }
142

143
  protected void injectByAnnotation(@NotNull PerlStringMixin host,
144
                                    @NotNull MultiHostRegistrar registrar,
145
                                    PerlAnnotationInject injectAnnotation) {
146
    String languageMarker = injectAnnotation.getLanguageMarker();
1✔
147
    if (languageMarker == null) {
1!
148
      return;
×
149
    }
150
    injectLanguage(host, registrar, PerlInjectionMarkersService.getInstance(host.getProject()).getLanguageByMarker(languageMarker));
1✔
151
  }
1✔
152

153
  private void injectLanguage(@NotNull PerlStringMixin perlString,
154
                              @NotNull MultiHostRegistrar registrar,
155
                              @Nullable Language targetLanguage) {
156
    if (targetLanguage == null) {
1!
157
      return;
×
158
    }
159
    TextRange contentRange = ElementManipulators.getValueTextRange(perlString);
1✔
160
    if (contentRange.isEmpty()) {
1!
161
      return;
×
162
    }
163
    PsiElement openQuoteElement = perlString.getOpenQuoteElement();
1✔
164
    if (openQuoteElement == null) {
1!
165
      return;
×
166
    }
167
    PsiElement closeQuoteElement = perlString.getCloseQuoteElement();
1✔
168
    PsiElement firstContentElement = openQuoteElement.getNextSibling();
1✔
169
    if (firstContentElement == closeQuoteElement) {
1!
170
      return;
×
171
    }
172
    injectLanguageIntoPsiRange(firstContentElement, closeQuoteElement, registrar, targetLanguage);
1✔
173
  }
1✔
174
}
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