Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* Navigation from the Jelly tags to their definitions
* Integrate `st:documentation` tag into IntelliJ documentation support
* Report custom tag attributes that are marked deprecated in `st:documentation`.
* Navigation from Jelly attribute `field` in `entry` tags to their Java definition
* Error checks and autocompletion on attributes and elements of taglibs
* `style` attribute and `script` tag contents should be recognized as CSS and JavaScript correspondingly.
* [Jenkins Symbols](https://weekly.ci.jenkins.io/design-library/symbols/) suggestions are available when using `<l:icon src="..." />`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.jenkins.stapler.idea.jelly;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceProvider;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;

public class JellyJavaReferenceProvider extends PsiReferenceProvider {

@Override
public PsiReference @NotNull [] getReferencesByElement(
@NotNull PsiElement element, @NotNull ProcessingContext context) {

if (!(element instanceof XmlAttributeValue attributeValue)) {
return PsiReference.EMPTY_ARRAY;
}
if (attributeValue.getParent() instanceof final XmlAttribute attribute) {
if (attribute.getParent().getLocalName().equals("entry")
&& attribute.getLocalName().equals("field")) {
return new PsiReference[] {new JellyToJavaClassReference(attributeValue)};
}
}

return PsiReference.EMPTY_ARRAY;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.jenkins.stapler.idea.jelly;

import com.intellij.patterns.XmlPatterns;
import com.intellij.psi.PsiReferenceContributor;
import com.intellij.psi.PsiReferenceRegistrar;

public class JellyReferenceContributor extends PsiReferenceContributor {

@Override
public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue(), new JellyJavaReferenceProvider());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.jenkins.stapler.idea.jelly;

import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReferenceBase;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.psi.xml.XmlAttributeValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class JellyToJavaClassReference extends PsiReferenceBase<XmlAttributeValue> {

public JellyToJavaClassReference(@NotNull XmlAttributeValue element) {
super(element, true);
}

@Override
public @Nullable PsiElement resolve() {

final var containingFile = getElement().getContainingFile();
final var containingDirectory = containingFile.getContainingDirectory();

String className = containingDirectory.getName();
String methodName = getElement().getValue();
Project project = getElement().getProject();

PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project);
GlobalSearchScope scope = GlobalSearchScope.projectScope(project);

String[] getterNames = PropertyUtilBase.suggestGetterNames(methodName);

PsiClass[] psiClass = cache.getClassesByName(className, scope);
for (var aClass : psiClass) {
for (var getterName : getterNames) {
for (PsiMethod method : aClass.findMethodsByName(getterName, true)) {
return method;
}
}
}
return null;
}

@Override
public boolean isReferenceTo(@NotNull PsiElement element) {
return super.isReferenceTo(element);
}

@Override
public Object @NotNull [] getVariants() {
return EMPTY_ARRAY;
}
}
4 changes: 4 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
<!-- <idea-version since-build="110.00"/>-->

<extensions defaultExtensionNs="com.intellij">

<psi.referenceContributor language="Jelly" implementation="io.jenkins.stapler.idea.jelly.JellyReferenceContributor"/>


<!--
REFERENCE:
to find out how to register a given extension point,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.jenkins.stapler.idea.jelly;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;

public class JellyReferenceContributorTest extends BasePlatformTestCase {

@Override
protected String getTestDataPath() {
return "src/test/testData";
}

public void testReferenceAtCaret() {
myFixture.configureByText(
"Foo.java",
"""
public class Foo {
public String getBar() {
return "";
}
}
""");

myFixture.configureByFiles("Foo/config.jelly");

PsiReference reference = myFixture.getReferenceAtCaretPosition();
assertNotNull(reference);

PsiElement resolved = reference.resolve();
assertNotNull(resolved);
if (resolved instanceof PsiMethod method) {
assertEquals("getBar", method.getName());
} else {
fail("not a method");
}
}
}
6 changes: 6 additions & 0 deletions src/test/testData/Foo/config.jelly
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry field="b<caret>ar">
<f:textbox/>
</f:entry>
</j:jelly>