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

Camelcade / Perl5-IDEA / #525521642

22 Jul 2025 01:00PM UTC coverage: 82.312% (+0.008%) from 82.304%
#525521642

push

github

hurricup
#2842 Split Template Toolkit plugin

23 of 42 new or added lines in 5 files covered. (54.76%)

55 existing lines in 10 files now uncovered.

30975 of 37631 relevant lines covered (82.31%)

0.82 hits per line

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

89.52
/mason/htmlmason/backend/src/main/java/com/perl5/lang/htmlmason/HTMLMasonUtil.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.htmlmason;
18

19
import com.intellij.openapi.project.Project;
20
import com.intellij.openapi.util.NlsSafe;
21
import com.intellij.openapi.util.text.StringUtil;
22
import com.intellij.openapi.vfs.VfsUtilCore;
23
import com.intellij.openapi.vfs.VirtualFile;
24
import com.intellij.psi.PsiFile;
25
import com.intellij.psi.PsiManager;
26
import com.intellij.psi.search.GlobalSearchScope;
27
import com.intellij.psi.stubs.StubIndex;
28
import com.intellij.util.Processor;
29
import com.perl5.lang.htmlmason.idea.configuration.HTMLMasonSettings;
30
import com.perl5.lang.htmlmason.parser.psi.*;
31
import com.perl5.lang.htmlmason.parser.psi.impl.HTMLMasonFileImpl;
32
import com.perl5.lang.htmlmason.parser.stubs.HTMLMasonFlagsStubIndex;
33
import com.perl5.lang.perl.idea.project.PerlProjectManager;
34
import com.perl5.lang.perl.psi.utils.PerlSubArgument;
35
import com.perl5.lang.perl.util.PerlSubUtilCore;
36
import org.jetbrains.annotations.NotNull;
37
import org.jetbrains.annotations.Nullable;
38

39
import java.util.*;
40

41
import static com.intellij.openapi.vfs.VfsUtilCore.VFS_SEPARATOR;
42
import static com.intellij.openapi.vfs.VfsUtilCore.VFS_SEPARATOR_CHAR;
43

44

45
public final class HTMLMasonUtil {
46
  private HTMLMasonUtil() {
47
  }
48

49
  public static @Nullable VirtualFile getComponentRoot(@NotNull Project project, @Nullable VirtualFile file) {
50
    return MasonCoreUtil.getComponentRoot(HTMLMasonSettings.getInstance(project), file);
1✔
51
  }
52

53
  public static List<PerlSubArgument> getArgumentsList(HTMLMasonParametrizedEntity entity) {
54
    List<PerlSubArgument> result = new ArrayList<>();
1✔
55

56
    for (HTMLMasonCompositeElement argsBlock : entity.getArgsBlocks()) {
1✔
57
      result.addAll(((HTMLMasonArgsBlock)argsBlock).getArgumentsList());
1✔
58
    }
1✔
59

60
    return result;
1✔
61
  }
62

63
  public static String getArgumentsListAsString(HTMLMasonParametrizedEntity entity) {
64
    return PerlSubUtilCore.getArgumentsListAsString(getArgumentsList(entity));
1✔
65
  }
66

67
  /**
68
   * @return absolute path relative to the components root
69
   */
70
  public static @NlsSafe @Nullable String getAbsoluteComponentPath(@NotNull HTMLMasonFileImpl masonFile) {
71

72
    VirtualFile componentFile = getComponentVirtualFile(masonFile);
1✔
73
    VirtualFile componentRoot = getComponentRoot(masonFile);
1✔
74

75
    if (componentFile != null && componentRoot != null) {
1✔
76
      return VFS_SEPARATOR + VfsUtilCore.getRelativePath(componentFile, componentRoot);
1✔
77
    }
UNCOV
78
    return null;
×
79
  }
80

81
  public static @Nullable HTMLMasonFileImpl getParentComponent(@NotNull HTMLMasonFileImpl masonFile) {
82
    String parentComponentPath = getParentComponentPath(masonFile);
1✔
83
    HTMLMasonSettings settings = HTMLMasonSettings.getInstance(masonFile.getProject());
1✔
84
    VirtualFile parentFile = null;
1✔
85

86
    if (parentComponentPath == null) {
1✔
87
      VirtualFile containingFile = getComponentVirtualFile(masonFile);
1✔
88
      if (containingFile != null) {
1✔
89
        VirtualFile startDir = containingFile.getParent();
1✔
90
        if (StringUtil.equals(containingFile.getName(), settings.autoHandlerName)) {
1✔
91
          startDir = startDir.getParent();
1✔
92
        }
93

94
        VirtualFile componentRoot = getComponentRoot(masonFile.getProject(), startDir);
1✔
95
        if (componentRoot != null) {
1✔
96
          while (VfsUtilCore.isAncestor(componentRoot, startDir, false)) {
1✔
97
            if ((parentFile = startDir.findFileByRelativePath(settings.autoHandlerName)) != null) {
1✔
98
              break;
1✔
99
            }
100
            startDir = startDir.getParent();
1✔
101
          }
102
        }
103
      }
104
    }
1✔
105
    else if (!StringUtil.equals(parentComponentPath, HTMLMasonFlagsStatement.UNDEF_RESULT)) {
1✔
106
      if (StringUtil.startsWith(parentComponentPath, "/")) {
1✔
107
        parentComponentPath = parentComponentPath.substring(1);
1✔
108
        for (VirtualFile root : PerlProjectManager.getInstance(settings.getProject()).getModulesRootsOfType(settings.getSourceRootType())) {
1✔
109
          if ((parentFile = root.findFileByRelativePath(parentComponentPath)) != null) {
1✔
110
            break;
1✔
111
          }
112
        }
1✔
113
      }
114
      else {
115
        VirtualFile containingVirtualFile = getComponentVirtualFile(masonFile);
1✔
116
        if (containingVirtualFile != null) {
1✔
117
          VirtualFile containingDir = containingVirtualFile.getParent();
1✔
118
          if (containingDir != null) {
1✔
119
            parentFile = containingDir.findFileByRelativePath(parentComponentPath);
1✔
120
          }
121
        }
122
      }
123
    }
124

125
    if (parentFile != null) {
1✔
126
      PsiFile file = PsiManager.getInstance(masonFile.getProject()).findFile(parentFile);
1✔
127
      if (file instanceof HTMLMasonFileImpl htmlMasonFile) {
1✔
128
        return htmlMasonFile;
1✔
129
      }
130
    }
131

132
    return null;
1✔
133
  }
134

135
  public static @Nullable String getParentComponentPath(@NotNull HTMLMasonFileImpl masonFile) {
136
    HTMLMasonFlagsStatement statement = masonFile.getFlagsStatement();
1✔
137
    return statement == null ? null : statement.getParentComponentPath();
1✔
138
  }
139

140
  public static @NotNull List<HTMLMasonFileImpl> getChildComponents(@NotNull HTMLMasonFileImpl masonFile) {
141
    VirtualFile containingFile = getComponentVirtualFile(masonFile);
1✔
142

143
    if (containingFile == null) {
1✔
UNCOV
144
      return Collections.emptyList();
×
145
    }
146

147
    VirtualFile componentRoot = getComponentRoot(masonFile);
1✔
148

149
    if (componentRoot == null) {
1✔
UNCOV
150
      return Collections.emptyList();
×
151
    }
152

153
    final List<HTMLMasonFileImpl> result = new ArrayList<>();
1✔
154
    final String relativePath = VFS_SEPARATOR + VfsUtilCore.getRelativePath(containingFile, componentRoot);
1✔
155
    final Project project = masonFile.getProject();
1✔
156
    final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
1✔
157
    final HTMLMasonFileImpl currentFile = masonFile;
1✔
158
    HTMLMasonSettings settings = HTMLMasonSettings.getInstance(project);
1✔
159

160
    // indexed children
161
    for (String parentPath : StubIndex.getInstance().getAllKeys(HTMLMasonFlagsStubIndex.KEY, project)) {
1✔
162
      boolean isEquals = StringUtil.equals(relativePath, parentPath);
1✔
163
      boolean isRelative = parentPath.isEmpty() || parentPath.charAt(0) != VFS_SEPARATOR_CHAR;
1✔
164

165
      for (HTMLMasonFlagsStatement statement : StubIndex.getElements(
1✔
166
        HTMLMasonFlagsStubIndex.KEY, parentPath, project, scope, HTMLMasonFlagsStatement.class)) {
167
        PsiFile file = statement.getContainingFile();
1✔
168
        if (file instanceof HTMLMasonFileImpl htmlMasonFile &&
1✔
169
            (isEquals || isRelative && currentFile.equals(getParentComponent(htmlMasonFile)))) {
1✔
170
          result.add(htmlMasonFile);
1✔
171
        }
172
      }
1✔
173
    }
1✔
174

175
    if (StringUtil.equals(containingFile.getName(), settings.autoHandlerName)) {
1✔
176
      collectAutoHandledFiles(masonFile, PsiManager.getInstance(project), containingFile.getParent(), result, settings.autoHandlerName,
1✔
177
                              null);
178
    }
179

180
    return result;
1✔
181
  }
182

183
  public static void collectAutoHandledFiles(@NotNull HTMLMasonFileImpl masonFile,
184
                                             @NotNull PsiManager manager,
185
                                             @Nullable VirtualFile dir,
186
                                             @NotNull List<? super HTMLMasonFileImpl> result,
187
                                             @NotNull String autoHandlerName,
188
                                             @Nullable Set<? super VirtualFile> recursionMap) {
189
    if (dir == null) {
1✔
UNCOV
190
      return;
×
191
    }
192
    if (recursionMap == null) {
1✔
193
      recursionMap = new HashSet<>();
1✔
194
    }
195
    else {
196
      VirtualFile autoHandlerVirtualFile = dir.findChild(autoHandlerName);
1✔
197
      if (autoHandlerVirtualFile != null) {
1✔
198
        PsiFile autoHandlerPsiFile = manager.findFile(autoHandlerVirtualFile);
1✔
199
        if (autoHandlerPsiFile instanceof HTMLMasonFileImpl htmlMasonFile && masonFile.equals(getParentComponent(htmlMasonFile))) {
1✔
200
          result.add(htmlMasonFile);
1✔
201
        }
202
        return;
1✔
203
      }
204
    }
205

206
    recursionMap.add(dir);
1✔
207

208
    for (VirtualFile file : dir.getChildren()) {
1✔
209
      if (file.isDirectory() && !recursionMap.contains(file)) {
1✔
210
        collectAutoHandledFiles(masonFile, manager, file, result, autoHandlerName, recursionMap);
1✔
211
      }
212
      else if (!StringUtil.equals(file.getName(), autoHandlerName)) {
1✔
213
        PsiFile psiFile = manager.findFile(file);
1✔
214
        if (psiFile instanceof HTMLMasonFileImpl htmlMasonFile && masonFile.equals(getParentComponent(htmlMasonFile))) {
1✔
215
          result.add(htmlMasonFile);
1✔
216
        }
217
      }
218
    }
219
  }
1✔
220

221
  /**
222
   * @return absolute containing dir path relative to the components root
223
   */
224
  public static @NlsSafe @Nullable String getAbsoluteComponentContainerPath(@NotNull HTMLMasonFileImpl masonFile) {
UNCOV
225
    VirtualFile componentFile = getComponentVirtualFile(masonFile);
×
UNCOV
226
    VirtualFile componentRoot = getComponentRoot(masonFile);
×
227

UNCOV
228
    if (componentFile != null && componentRoot != null) {
×
UNCOV
229
      return VFS_SEPARATOR + VfsUtilCore.getRelativePath(componentFile.getParent(), componentRoot);
×
230
    }
UNCOV
231
    return null;
×
232
  }
233

234
  public static @Nullable VirtualFile getComponentRoot(@NotNull HTMLMasonFileImpl masonFile) {
235
    return getComponentRoot(masonFile.getProject(), getComponentVirtualFile(masonFile));
1✔
236
  }
237

238
  public static VirtualFile getComponentVirtualFile(@NotNull HTMLMasonFileImpl masonFile) {
239
    return MasonCoreUtil.getContainingVirtualFile(masonFile);
1✔
240
  }
241

242
  /**
243
   * Recursively looking for method in child components
244
   *
245
   * @param name method name
246
   * @return list of child components
247
   */
248
  public static @NotNull List<HTMLMasonMethodDefinition> findMethodDefinitionByNameInChildComponents(@NotNull HTMLMasonFileImpl masonFile,
249
                                                                                                     String name) {
250
    List<HTMLMasonMethodDefinition> result = new ArrayList<>();
1✔
251
    Set<HTMLMasonFileImpl> recursionSet = new HashSet<>();
1✔
252

253
    collectMethodDefinitionByNameInChildComponents(masonFile, name, result, recursionSet);
1✔
254

255
    return result;
1✔
256
  }
257

258
  public static void collectMethodDefinitionByNameInChildComponents(@NotNull HTMLMasonFileImpl masonFile,
259
                                                                    String name,
260
                                                                    List<HTMLMasonMethodDefinition> result,
261
                                                                    Set<HTMLMasonFileImpl> recursionSet) {
262
    for (HTMLMasonFileImpl childComponent : getChildComponents(masonFile)) {
1✔
263
      if (!recursionSet.contains(childComponent)) {
1✔
264
        recursionSet.add(childComponent);
1✔
265
        HTMLMasonMethodDefinition methodDefinition = childComponent.getMethodDefinitionByName(name);
1✔
266
        if (methodDefinition != null) {
1✔
267
          result.add(methodDefinition);
1✔
268
        }
269
        else {
UNCOV
270
          collectMethodDefinitionByNameInChildComponents(masonFile, name, result, recursionSet);
×
271
        }
272
      }
273
    }
1✔
274
  }
1✔
275

276
  /**
277
   * Recursively looking for method in parent components
278
   *
279
   * @param name method name
280
   * @return method definition or null
281
   */
282
  public static @Nullable HTMLMasonMethodDefinition findMethodDefinitionByNameInParents(@NotNull HTMLMasonFileImpl masonFile, String name) {
283
    HTMLMasonFileImpl parentComponent = getParentComponent(masonFile);
1✔
284
    return parentComponent == null ? null : findMethodDefinitionByNameInThisOrParents(parentComponent, name);
1✔
285
  }
286

287
  public static boolean processMethodDefinitionsInThisOrParents(@NotNull HTMLMasonFileImpl masonFile,
288
                                                                Processor<HTMLMasonMethodDefinition> processor,
289
                                                                Set<HTMLMasonFileImpl> recursionSet) {
290
    if (recursionSet.contains(masonFile)) {
1✔
UNCOV
291
      return false;
×
292
    }
293
    recursionSet.add(masonFile);
1✔
294

295
    if (!masonFile.processMethodDefinitions(processor)) {
1✔
296
      return false;
1✔
297
    }
298

UNCOV
299
    HTMLMasonFileImpl parentComponent = getParentComponent(masonFile);
×
300

UNCOV
301
    return parentComponent != null && processMethodDefinitionsInThisOrParents(parentComponent, processor, recursionSet);
×
302
  }
303

304
  @SuppressWarnings("UnusedReturnValue")
305
  public static boolean processMethodDefinitionsInThisOrParents(@NotNull HTMLMasonFileImpl masonFile,
306
                                                                Processor<HTMLMasonMethodDefinition> processor) {
307
    return processMethodDefinitionsInThisOrParents(masonFile, processor, new HashSet<HTMLMasonFileImpl>());
1✔
308
  }
309

310
  /**
311
   * Recursively looking for method in current or parent components
312
   *
313
   * @param name method name
314
   * @return method definition or null
315
   */
316
  public static @Nullable HTMLMasonMethodDefinition findMethodDefinitionByNameInThisOrParents(@NotNull HTMLMasonFileImpl masonFile,
317
                                                                                              String name) {
318
    HTMLMasonFileImpl.HTMLMasonMethodDefinitionSeeker seeker = new HTMLMasonFileImpl.HTMLMasonMethodDefinitionSeeker(name);
1✔
319
    processMethodDefinitionsInThisOrParents(masonFile, seeker);
1✔
320
    return seeker.getResult();
1✔
321
  }
322
}
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