diff --git a/gradle.properties b/gradle.properties index 05a20aba..7483f6b0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,7 +20,7 @@ platformVersion = 2024.2.4 # Example: platformPlugins = com.jetbrains.php:203.4449.22, org.intellij.scala:2023.3.27@EAP platformPlugins = # Example: platformBundledPlugins = com.intellij.java -platformBundledPlugins = +platformBundledPlugins = com.intellij.java # Gradle Releases -> https://github.com/gradle/gradle/releases gradleVersion = 8.11 diff --git a/src/main/java/se/isselab/HAnS/featureLocation/FeatureLocationManager.java b/src/main/java/se/isselab/HAnS/featureLocation/FeatureLocationManager.java index d7f84af3..e94a4edd 100644 --- a/src/main/java/se/isselab/HAnS/featureLocation/FeatureLocationManager.java +++ b/src/main/java/se/isselab/HAnS/featureLocation/FeatureLocationManager.java @@ -18,7 +18,10 @@ import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.project.Project; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Query; import org.jetbrains.annotations.NotNull; @@ -97,6 +100,35 @@ public static FeatureFileMapping getFeatureFileMapping(Project project, FeatureM return featureFileMapping; } // &end[FeatureFileMapping] + public static FeatureFileMapping getFeatureFileMappingFile(Project project, FeatureModelFeature feature) { + FeatureFileMapping featureFileMapping = new FeatureFileMapping(feature); + FileType fileTypeFeatureFile = FileTypeManager.getInstance().getFileTypeByExtension("feature-to-file"); + System.out.println("Querying"); + GlobalSearchScope globalSearchScope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(project), fileTypeFeatureFile); + Query featureReference = ReferencesSearch.search(feature, globalSearchScope, true); + + for (PsiReference reference : ReadAction.compute(() -> featureReference)) { + System.out.println("Querying loop"); + //get comment sibling of the feature comment + + PsiElement element = reference.getElement(); + var originatingFilePath = ReadAction.compute(()->element.getContainingFile().getVirtualFile().getPath()); + //determine file type and process content + var fileType = ReadAction.compute(element::getContainingFile); + if (fileType instanceof CodeAnnotationFile) { + processCodeFile(project, featureFileMapping, element, originatingFilePath); + } else if (fileType instanceof FileAnnotationFile) { + processFeatureToFile(project, featureFileMapping, element, originatingFilePath); + } else if (fileType instanceof FolderAnnotationFile) { + PsiDirectory dir = ReadAction.compute(fileType::getContainingDirectory); + if (dir == null) + continue; + processFeatureToFolder(project, featureFileMapping, dir, originatingFilePath); + } + } + featureFileMapping.buildFromQueue(); + return featureFileMapping; + } private static void processCodeFile(Project project, FeatureFileMapping featureFileMapping, PsiElement element, String originatingFilePath) { var commentElement = ReadAction.compute(() -> PsiTreeUtil.getContextOfType(element, PsiComment.class)); diff --git a/src/main/java/se/isselab/HAnS/referencing/FeatureReference.java b/src/main/java/se/isselab/HAnS/referencing/FeatureReference.java index 6fbe1bbf..35c738b7 100644 --- a/src/main/java/se/isselab/HAnS/referencing/FeatureReference.java +++ b/src/main/java/se/isselab/HAnS/referencing/FeatureReference.java @@ -17,33 +17,44 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementResolveResult; -import com.intellij.psi.PsiReferenceBase; -import com.intellij.psi.ResolveResult; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import se.isselab.HAnS.AnnotationIcons; import se.isselab.HAnS.codeAnnotation.psi.CodeAnnotationLpq; import se.isselab.HAnS.codeAnnotation.psi.impl.CodeAnnotationPsiImplUtil; +import se.isselab.HAnS.featureLocation.FeatureFileMapping; +import se.isselab.HAnS.featureLocation.FeatureLocation; +import se.isselab.HAnS.featureLocation.FeatureLocationManager; import se.isselab.HAnS.featureModel.FeatureModelUtil; import se.isselab.HAnS.featureModel.psi.FeatureModelFeature; import se.isselab.HAnS.fileAnnotation.psi.FileAnnotationLpq; import se.isselab.HAnS.fileAnnotation.psi.impl.FileAnnotationPsiImplUtil; import se.isselab.HAnS.folderAnnotation.psi.FolderAnnotationLpq; import se.isselab.HAnS.folderAnnotation.psi.impl.FolderAnnotationPsiImplUtil; +import se.isselab.HAnS.pluginExtensions.ProjectMetricsService; +import se.isselab.HAnS.pluginExtensions.backgroundTasks.featureFileMappingTasks.FeatureFileMappingCallback; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; -public class FeatureReference extends PsiReferenceBase { +public class FeatureReference extends PsiPolyVariantReferenceBase { private final String lpq; + private PsiElement element; public FeatureReference(@NotNull PsiElement element, TextRange textRange) { super(element, textRange); + this.element = element; lpq = element.getText().substring(textRange.getStartOffset(), textRange.getEndOffset()); } @@ -71,17 +82,6 @@ else if (myElement instanceof CodeAnnotationLpq) { return myElement; } - @Nullable - @Override - public PsiElement resolve() { - Project project = myElement.getProject(); - final List features = FeatureModelUtil.findLPQ(project, lpq); - List results = new ArrayList<>(); - for (FeatureModelFeature feature : features) { - results.add(new PsiElementResolveResult(feature)); - } - return results.size() == 1 ? results.get(0).getElement() : null; - } @Override public Object @NotNull [] getVariants() { @@ -99,4 +99,114 @@ public PsiElement resolve() { return variants.toArray(); } -} \ No newline at end of file + private boolean isEndTag(PsiElement psiElement) { + if (psiElement.getText().startsWith("&end")) return true; + if (psiElement.getParent() != null) { + return isEndTag(psiElement.getParent()); + } + return false; + } + + @Override + public ResolveResult @NotNull [] multiResolve(boolean b) { + Project project = element.getProject(); + + List results = new ArrayList<>(); + + if(element instanceof FileAnnotationLpq) { + //System.out.println("FileLpq"); + ProjectMetricsService projectMetricsService = new ProjectMetricsService(project); + final List features = FeatureModelUtil.findLPQ(project, lpq); + if (features.isEmpty()) { + System.out.println("no feature found"); + return results.toArray(new ResolveResult[0]); + } + FeatureModelFeature feature = features.get(0); + System.out.println("Querying"); + FeatureFileMapping featureFileMapping = FeatureLocationManager.getFeatureFileMappingFile(project, feature); + ArrayList featureLocations = featureFileMapping.getFeatureLocations(); + System.out.println("Amount of locations: " + featureLocations.size()); + for (FeatureLocation featureLocation : featureLocations) { + System.out.println(featureLocation.toString()); + } + //CompletableFuture future = new CompletableFuture<>(); + /*projectMetricsService.getFeatureFileMappingBackground(feature, new FeatureFileMappingCallback() { + @Override + public void onComplete(FeatureFileMapping featureFileMapping) { + future.complete(featureFileMapping); + } + }); + try { + /* Waiting for this takes to long, but we also do not know how to get it once and save + * it for later, as the FeatureReference object is newly constructed everytime + FeatureFileMapping featureFileMapping = future.get(); + + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + }*/ + } else if(element instanceof CodeAnnotationLpq) { + if (isEndTag(element)) return ResolveResult.EMPTY_ARRAY; + final List features = FeatureModelUtil.findLPQ(project, lpq); + + for (FeatureModelFeature feature : features) { + + PsiElement commentElement = ReadAction.compute(() -> PsiTreeUtil.getContextOfType(element, PsiComment.class)); + + if(commentElement == null) continue; + + PsiFile file = commentElement.getContainingFile(); + String[] lines = file.getText().split("\n"); + + int beginLineNumber = getLine(project, commentElement); + int endLineNumber = 1; + + for(String line : lines) { + if(endLineNumber > beginLineNumber && line.contains("&end[" + feature.getName() + "]")){ + break; + } + endLineNumber++; + } + + + results.add(new PsiElementResolveResult(feature)); + } + } + return results.toArray(new ResolveResult[0]); + } + public ArrayList getAllFileNamesForFeature(String[] lines, String featureName) { + String[] nonEmptyLines = Arrays.stream(lines).filter((String line) -> !line.trim().isBlank()).toArray(String[]::new); + ArrayList fileNames = new ArrayList<>(); + for (int i = 0; i + 1 < nonEmptyLines.length; i+=2) { + String[] features = nonEmptyLines[i + 1].split(","); + boolean featureNameFound = false; + for (String feature : features) { + if (feature.trim().equals(featureName)){ + featureNameFound = true; + break; + } + } + if (!featureNameFound) continue; + fileNames.addAll(Arrays.stream(nonEmptyLines[i].split(",")).map(String::trim).toList()); + } + + return fileNames; + } + + private int getLine(Project project, PsiElement elem) { + + PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project); + PsiFile openedFile = ReadAction.compute(elem::getContainingFile); + + //iterate over each psiElement and check for PsiComment-Feature-Annotations + if (openedFile == null) + return -1; + Document document = psiDocumentManager.getDocument(openedFile); + if (document == null) + return -1; + + return document.getLineNumber(elem.getTextRange().getStartOffset()); + } +} + diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index f755552b..602c85e5 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -23,6 +23,7 @@ limitations under the License. com.intellij.modules.platform + com.intellij.modules.java