Skip to content
This repository has been archived by the owner on Oct 26, 2023. It is now read-only.

Commit

Permalink
Add context action: Go to Thrift source (#92)
Browse files Browse the repository at this point in the history
* Add context action: Go to Thrift source

Add an action to context menu that allows user to jump to Thrift definition
from any place where it is referenced

* line marker provider allowing to jump to thrift source from a generated file
  • Loading branch information
Tomasz Pasternak authored Aug 28, 2020
1 parent 12e0efe commit c98bff0
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.intellij.plugins.thrift.editor;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.List;

import com.google.common.base.Stopwatch;
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator;
import com.intellij.ide.util.PsiElementListCellRenderer;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.plugins.thrift.ThriftBundle;
import com.intellij.plugins.thrift.index.ThriftDeclarationIndex;
import com.intellij.plugins.thrift.lang.psi.ThriftDeclaration;
import com.intellij.plugins.thrift.lang.psi.ThriftStruct;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope;

import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class GoToThriftDefinition extends AnAction {
static Logger logger = Logger.getInstance(GoToThriftDefinition.class);

@Override
public void actionPerformed(@NotNull AnActionEvent event) {
PsiElement psiElement = event.getData(CommonDataKeys.PSI_ELEMENT);
List<ThriftDeclaration> thriftMatches = getThriftDeclarations(psiElement, event.getProject());
if (thriftMatches.size() > 0) {
PsiElementListCellRenderer<PsiElement> renderer = new PsiElementListCellRenderer<PsiElement>() {
@Override
public String getElementText(PsiElement element) {
if (element instanceof ThriftStruct) {
return ((ThriftStruct) element).getName();
} else {
return element.getText().substring(0, 20);
}
}

@NotNull
public Path commonRoot(@NotNull Path pathA, @NotNull Path pathB) {
Path rootCandidate = pathA;
while (rootCandidate.getParent() != null && rootCandidate.relativize(pathB).startsWith("..")) {
rootCandidate = rootCandidate.getParent();
}
return rootCandidate;
}

@Override
protected @Nullable String getContainerText(PsiElement element, String name) {
Project project = event.getProject();
Path containingFilePath = Paths.get(element.getContainingFile().getVirtualFile().getPath());
if (project != null && project.getBasePath() != null) {
Path projectBasePath = Paths.get(project.getBasePath()).normalize();
return commonRoot(projectBasePath, containingFilePath).relativize(containingFilePath).toString();
} else {
return containingFilePath.toString();
}
}

@Override
protected int getIconFlags() {
return 0;
}
};
@Nullable JBPopup popup = PsiElementListNavigator.navigateOrCreatePopup(
thriftMatches.toArray(new ThriftDeclaration[0]),
ThriftBundle.message("thrift.goto.source"),
ThriftBundle.message("thrift.goto.source"),
renderer,
null
);
if (popup != null) {
popup.showInBestPositionFor(event.getDataContext());
}
}
}

@NotNull
private List<ThriftDeclaration> getThriftDeclarations(PsiElement psiElement, Project project) {
if (psiElement instanceof PsiClass) {
PsiClass psiClassElement = (PsiClass) psiElement;
String name = psiClassElement.getName();
if (name != null) { // todo - handle multiple matches
return ThriftDeclarationIndex.findDeclaration(name, project, GlobalSearchScope.allScope(project));
}
}
return Lists.newArrayList();
}

@Override
public void update(@NotNull AnActionEvent e) {
PsiElement psiElement = e.getData(CommonDataKeys.PSI_ELEMENT);
Presentation p = e.getPresentation();
Stopwatch stopWatch = Stopwatch.createStarted();
boolean enabled = !getThriftDeclarations(psiElement, e.getProject()).isEmpty();
Duration time = stopWatch.elapsed();
if (time.toMillis() > 20) {
logger.warn("GoToThriftDefinition action update took " + time.toMillis() + "ms");
}
p.setEnabledAndVisible(enabled);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package com.intellij.plugins.thrift.editor;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

import com.google.common.base.Stopwatch;
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider;
import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.plugins.thrift.ThriftBundle;
import com.intellij.plugins.thrift.index.ThriftDeclarationIndex;
import com.intellij.plugins.thrift.lang.psi.ThriftDeclaration;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiModifierList;
import com.intellij.psi.search.GlobalSearchScope;

import org.jetbrains.annotations.NotNull;

import icons.ThriftIcons;

public class GoToThriftDefinitionMarkerProvider extends RelatedItemLineMarkerProvider {
static Logger logger = Logger.getInstance(GoToThriftDefinition.class);

@Override
protected void collectNavigationMarkers(@NotNull PsiElement element,
@NotNull Collection<? super RelatedItemLineMarkerInfo<?>> result) {
if (element instanceof PsiClass) {
PsiClass psiClass = (PsiClass) element;
String name = psiClass.getName();
if (name != null) {
Stopwatch stopWatch = Stopwatch.createStarted();
List<ThriftDeclaration> thriftDefinitions =
ThriftDeclarationIndex.findDeclaration(name, psiClass.getProject(),
GlobalSearchScope.allScope(psiClass.getProject()));
Duration time = stopWatch.elapsed();
if (time.toMillis() > 20) {
logger.warn("GoToThriftDefinition action update took " + time.toMillis() + "ms");
}
if (!thriftDefinitions.isEmpty()) {
NavigationGutterIconBuilder<PsiElement> builder =
NavigationGutterIconBuilder.create(ThriftIcons.STRUCT)
.setTargets(thriftDefinitions)
.setTooltipText(ThriftBundle.message("thrift.goto.source"));
Optional<PsiElement> identifier =
Arrays.stream(element.getChildren()).filter(x -> x instanceof PsiIdentifier).findFirst();
// Some languages (Scala) don't put PsiIdentifier here, PsiModifierList is a fallback
Optional<PsiElement> modifiersList =
Arrays.stream(element.getChildren()).filter(x -> x instanceof PsiModifierList).findFirst();
if (identifier.isPresent()) {
result.add(builder.createLineMarkerInfo(identifier.get()));
} else if (modifiersList.isPresent()) {
result.add(builder.createLineMarkerInfo(modifiersList.get()));
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.intellij.plugins.thrift.lang.psi;

import com.intellij.navigation.NavigationItem;
import com.intellij.psi.NavigatablePsiElement;
import com.intellij.psi.PsiNamedElement;

/**
* Created by fkorotkov.
*/
public interface ThriftDeclaration extends ThriftPsiCompositeElement, PsiNamedElement, NavigationItem {
public interface ThriftDeclaration extends ThriftPsiCompositeElement, PsiNamedElement, NavigationItem, NavigatablePsiElement {
ThriftDefinitionName getIdentifier();
}
8 changes: 7 additions & 1 deletion thrift/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,16 @@
<frameworkSupport implementation="com.intellij.plugins.thrift.config.facet.ThriftFacetSupportProvider"/>
<facetType implementation="com.intellij.plugins.thrift.config.facet.ThriftFacetType"/>
<compileServer.plugin classpath="thrift-jps.jar;commons-io-2.4.jar"/>

<codeInsight.lineMarkerProvider language="Scala" implementationClass="com.intellij.plugins.thrift.editor.GoToThriftDefinitionMarkerProvider"/>
<codeInsight.lineMarkerProvider language="JAVA" implementationClass="com.intellij.plugins.thrift.editor.GoToThriftDefinitionMarkerProvider"/>
</extensions>

<actions>
<action class="com.intellij.plugins.thrift.editor.GoToThriftDefinition"
id="com.intellij.plugins.thrift.editor.GoToThriftDefinition"
text="Go to Thrift source">
<add-to-group group-id="RunContextPopupGroup" anchor="first"/>
</action>
</actions>
<project-components>
<component>
Expand Down
1 change: 1 addition & 0 deletions thrift/src/main/resources/ThriftBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,4 @@ thrift.gen.option.validate=Generate PHP validator methods (v0.9.2)
thrift.gen.option.json=Generate JsonSerializable classes (requires PHP >\= 5.4) (v0.9.2)
thrift.gen.option.nsglobal=Set global namespace (v0.9.2)
thrift.gen.option.namespaced=Generate files in idiomatic namespaced directories (v0.9.2)
thrift.goto.source=Go to Thrift source

0 comments on commit c98bff0

Please sign in to comment.