Skip to content

Commit

Permalink
Extract some helper methods into a separate class
Browse files Browse the repository at this point in the history
  • Loading branch information
JojOatXGME committed May 17, 2024
1 parent 7a13192 commit f1dd5ab
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
package org.nixos.idea.lang.references;

import com.intellij.find.usages.api.PsiUsage;
import com.intellij.find.usages.api.SearchTarget;
import com.intellij.find.usages.api.Usage;
import com.intellij.find.usages.api.UsageSearchParameters;
import com.intellij.model.psi.PsiSymbolDeclaration;
import com.intellij.model.search.SearchService;
import com.intellij.navigation.SymbolNavigationService;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
import com.intellij.platform.backend.navigation.NavigationTarget;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTreeUtilKt;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
import org.intellij.lang.annotations.Language;
Expand All @@ -28,8 +16,6 @@
import org.nixos.idea.file.NixFileType;
import org.nixos.idea.lang.builtins.NixBuiltin;
import org.nixos.idea.lang.references.symbol.NixSymbol;
import org.nixos.idea.lang.references.symbol.NixUserSymbol;
import org.nixos.idea.psi.NixDeclarationHost;
import org.nixos.idea.settings.NixSymbolSettings;

import java.util.ArrayList;
Expand All @@ -39,8 +25,6 @@
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

@WithIdeaPlatform.OnEdt
Expand All @@ -51,9 +35,11 @@ final class SymbolNavigationTest {
private static final @NotNull Markers.TagName TAG_DECL = Markers.tagName("decl");

private final @NotNull CodeInsightTestFixture myFixture;
private final @NotNull SymbolTestHelper mySymbolHelper;

SymbolNavigationTest(@NotNull CodeInsightTestFixture fixture) {
myFixture = fixture;
mySymbolHelper = new SymbolTestHelper(fixture);
}

@BeforeEach
Expand Down Expand Up @@ -258,32 +244,17 @@ record Input(@NotNull PsiFile file,
return new Input(file, markers.unmarkedText(), markers, symbolName);
}

@SuppressWarnings({"UnstableApiUsage", "OverrideOnly"})
@SuppressWarnings({"UnstableApiUsage"})
private void resolveSymbolFromDeclaration(@NotNull Input input, @NotNull Markers.Marker declarationMarker) {
NixSymbol expectedSymbol = getSymbol(input);

List<PsiSymbolDeclaration> allDeclarations = new ArrayList<>();
PsiTreeUtilKt.elementsAtOffsetUp(input.file, declarationMarker.start())
.forEachRemaining(element -> allDeclarations.addAll(element.getFirst().getOwnDeclarations()));
List<PsiSymbolDeclaration> filteredDeclarations = allDeclarations.stream()
.filter(declaration -> declaration.getAbsoluteRange().contains(declarationMarker.start()))
.toList();
if (filteredDeclarations.isEmpty()) {
fail("No declaration found");
} else if (filteredDeclarations.size() > 1) {
fail("Multiple declarations found: " + filteredDeclarations);
}

PsiSymbolDeclaration declaration = filteredDeclarations.get(0);
NixSymbolDeclaration declaration = mySymbolHelper.findDeclaration(NixSymbolDeclaration.class, input.file, declarationMarker.start());
assertEquals(declarationMarker.range(), declaration.getAbsoluteRange());
assertEquals(expectedSymbol, declaration.getSymbol());
}

@SuppressWarnings("UnstableApiUsage")
private void resolveSymbolFromReference(@NotNull Input input, @NotNull Markers.Marker referenceMarker) {
NixSymbol expectedSymbol = getSymbol(input);
myFixture.getEditor().getCaretModel().moveToOffset(referenceMarker.start());
NixScopeReference reference = assertInstanceOf(NixScopeReference.class, ReadAction.compute(myFixture::findSingleReferenceAtCaret));
NixScopeReference reference = mySymbolHelper.findReference(NixScopeReference.class, input.file, referenceMarker.start());

assertEquals(referenceMarker.range(), reference.getElement().getTextRange().cutOut(reference.getRangeInElement()));

Expand All @@ -292,44 +263,21 @@ private void resolveSymbolFromReference(@NotNull Input input, @NotNull Markers.M
assertEquals(input.symbolName, symbols.iterator().next().getName());
}

@SuppressWarnings("UnstableApiUsage")
private void findDeclarations(@NotNull Input input) {
NixSymbol symbol = getSymbol(input);
Collection<? extends NavigationTarget> navigationTargets = SymbolNavigationService.getInstance()
.getNavigationTargets(myFixture.getProject(), symbol);
Collection<NixNavigationTarget> navigationTargets = mySymbolHelper.findNavigationTargets(NixNavigationTarget.class, symbol);
assertEquals(
input.markers.markers(TAG_DECL),
Markers.create(input.code, navigationTargets.stream().map(target -> {
NixNavigationTarget nixTarget = assertInstanceOf(NixNavigationTarget.class, target);
return Markers.marker(TAG_DECL, nixTarget.getRangeInFile());
}), TAG_DECL)
Markers.create(input.code, navigationTargets.stream().map(
target -> Markers.marker(TAG_DECL, target.getRangeInFile())
), TAG_DECL)
);
}

@SuppressWarnings("UnstableApiUsage")
private void findUsages(@NotNull Input input) {
NixSymbol symbol = getSymbol(input);
Collection<Usage> usages = SearchService.getInstance().searchParameters(new UsageSearchParameters() {
@Override
public @NotNull SearchTarget getTarget() {
return symbol;
}

@Override
public @NotNull SearchScope getSearchScope() {
return new LocalSearchScope(input.file);
}

@Override
public @NotNull Project getProject() {
return myFixture.getProject();
}

@Override
public boolean areValid() {
return true;
}
}).findAll();
Collection<Usage> usages = mySymbolHelper.findUsages(symbol, input.file);

assertEquals(
input.markers.markers(TAG_DECL, TAG_REF),
Expand All @@ -342,17 +290,11 @@ public boolean areValid() {
}

private @NotNull NixSymbol getSymbol(@NotNull Input input) {
List<NixDeclarationHost> hosts = input.markers.ranges(TAG_DECL).stream()
.map(textRange -> PsiTreeUtil.findElementOfClassAtOffset(input.file, textRange.getStartOffset(), NixDeclarationHost.class, false))
.distinct().toList();
if (hosts.isEmpty()) {
return input.markers.ranges(TAG_DECL).stream().findFirst().map(declaration -> {
return mySymbolHelper.findSymbol(input.file, input.symbolName, declaration.getStartOffset());
}).orElseGet(() -> {
NixBuiltin builtin = NixBuiltin.resolveGlobal(input.symbolName);
return NixSymbol.builtin(Objects.requireNonNull(builtin, "Builtin not found: " + input.symbolName));
} else if (hosts.size() == 1) {
NixUserSymbol symbol = hosts.get(0).getSymbol(List.of(input.symbolName));
return Objects.requireNonNull(symbol, "Symbol not found: " + input.symbolName);
} else {
throw new IllegalStateException("Declarations are spread over multiple declaration hosts: " + hosts);
}
});
}
}
146 changes: 146 additions & 0 deletions src/test/java/org/nixos/idea/lang/references/SymbolTestHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package org.nixos.idea.lang.references;

import com.intellij.find.usages.api.SearchTarget;
import com.intellij.find.usages.api.Usage;
import com.intellij.find.usages.api.UsageSearchParameters;
import com.intellij.model.Symbol;
import com.intellij.model.psi.PsiSymbolDeclaration;
import com.intellij.model.psi.PsiSymbolReference;
import com.intellij.model.search.SearchService;
import com.intellij.navigation.SymbolNavigationService;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
import com.intellij.platform.backend.navigation.NavigationTarget;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTreeUtilKt;
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
import org.jetbrains.annotations.NotNull;
import org.nixos.idea.lang.references.symbol.NixSymbol;
import org.nixos.idea.lang.references.symbol.NixUserSymbol;
import org.nixos.idea.psi.NixDeclarationHost;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.fail;

public final class SymbolTestHelper {

private final @NotNull CodeInsightTestFixture myFixture;

public SymbolTestHelper(@NotNull CodeInsightTestFixture fixture) {
myFixture = fixture;
}

//region findSymbol

public @NotNull NixSymbol findSymbol(@NotNull PsiFile file, @NotNull String name, int offset) {
return findSymbol(file, List.of(name), offset);
}

public @NotNull NixSymbol findSymbol(@NotNull PsiFile file, @NotNull List<String> path, int offset) {
NixDeclarationHost host = PsiTreeUtil.findElementOfClassAtOffset(file, offset, NixDeclarationHost.class, false);
if (host == null) {
throw new IllegalStateException("No NixDeclarationHost found on given location");
}
NixUserSymbol symbol = host.getSymbol(path);
return Objects.requireNonNull(symbol, "Symbol not found: " + String.join(".", path));
}

//endregion
//region findDeclarations

@SuppressWarnings("UnstableApiUsage")
public <T extends PsiSymbolDeclaration> @NotNull T findDeclaration(@NotNull Class<T> type, @NotNull PsiFile file, int offset) {
Collection<PsiSymbolDeclaration> declarations = findDeclarations(file, offset);
List<T> typedDeclarations = declarations.stream()
.filter(type::isInstance).map(type::cast)
.toList();
if (declarations.isEmpty()) {
return fail("No declaration found");
} else if (typedDeclarations.isEmpty()) {
return fail(String.format("No declaration of type %s found. Found: %s", type.getSimpleName(), declarations));
} else if (typedDeclarations.size() > 1) {
return fail("Multiple declarations found: " + declarations);
} else {
return typedDeclarations.get(0);
}
}

@SuppressWarnings("UnstableApiUsage")
public @NotNull Collection<PsiSymbolDeclaration> findDeclarations(@NotNull PsiFile file, int offset) {
return findDeclarations(PsiSymbolDeclaration.class, file, offset);
}

@SuppressWarnings({"UnstableApiUsage", "OverrideOnly"})
public <T extends PsiSymbolDeclaration> @NotNull Collection<T> findDeclarations(@NotNull Class<T> type, @NotNull PsiFile file, int offset) {
List<PsiSymbolDeclaration> allDeclarations = new ArrayList<>();
PsiTreeUtilKt.elementsAtOffsetUp(file, offset)
.forEachRemaining(element -> allDeclarations.addAll(element.getFirst().getOwnDeclarations()));
return allDeclarations.stream()
.filter(declaration -> declaration.getAbsoluteRange().contains(offset))
.filter(type::isInstance).map(type::cast)
.toList();
}

//endregion
//region findReferences

@SuppressWarnings("UnstableApiUsage")
public <T extends PsiSymbolReference> @NotNull T findReference(@NotNull Class<T> type, @NotNull PsiFile file, int offset) {
myFixture.openFileInEditor(file.getVirtualFile());
myFixture.getEditor().getCaretModel().moveToOffset(offset);
return assertInstanceOf(type, ReadAction.compute(myFixture::findSingleReferenceAtCaret));
}

//endregion
//region findNavigationTargets

@SuppressWarnings("UnstableApiUsage")
public <T extends NavigationTarget> @NotNull Collection<T> findNavigationTargets(@NotNull Class<T> type, @NotNull Symbol symbol) {
return SymbolNavigationService.getInstance().getNavigationTargets(myFixture.getProject(), symbol).stream()
.filter(type::isInstance).map(type::cast)
.toList();
}

//endregion
//region findUsages

@SuppressWarnings("UnstableApiUsage")
public @NotNull Collection<Usage> findUsages(@NotNull SearchTarget target, @NotNull PsiFile file) {
return findUsages(target, new LocalSearchScope(file));
}

@SuppressWarnings("UnstableApiUsage")
public @NotNull Collection<Usage> findUsages(@NotNull SearchTarget target, @NotNull SearchScope scope) {
return SearchService.getInstance().searchParameters(new UsageSearchParameters() {
@Override
public @NotNull SearchTarget getTarget() {
return target;
}

@Override
public @NotNull SearchScope getSearchScope() {
return scope;
}

@Override
public @NotNull Project getProject() {
return myFixture.getProject();
}

@Override
public boolean areValid() {
return true;
}
}).findAll();
}

//endregion
}

0 comments on commit f1dd5ab

Please sign in to comment.