Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp Jelly completion suggestions #197

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
dbcbb39
Init
janfaracik Nov 30, 2024
fe83f21
Support multiple namespace imports
janfaracik Nov 30, 2024
25dfb8a
Update JellyCompletionContributor.java
janfaracik Nov 30, 2024
ed250f4
Working build
janfaracik Nov 30, 2024
297b2f9
Working build
janfaracik Nov 30, 2024
4370f0f
WB
janfaracik Nov 30, 2024
09929c2
Update icon
janfaracik Nov 30, 2024
10ca5c0
Remove old files
janfaracik Nov 30, 2024
8430cc9
Update JellyCompletionContributor.java
janfaracik Nov 30, 2024
a669a09
Update JellyCompletionContributor.java
janfaracik Nov 30, 2024
8e7b583
Delete stapler.png
janfaracik Nov 30, 2024
ecf90e7
Update JellyCompletionContributor.java
janfaracik Nov 30, 2024
e589f59
Update JellyCompletionContributor.java
janfaracik Nov 30, 2024
95acb67
Update JellyCompletionContributor.java
janfaracik Dec 2, 2024
10cd3fa
Add tests
janfaracik Dec 2, 2024
2c0fde5
Push
janfaracik Dec 2, 2024
13079d2
Update StaplerCustomJellyTagLibraryXmlNSDescriptor.java
janfaracik Dec 2, 2024
bc74174
Merge branch 'lint' into namespace-imports
janfaracik Dec 2, 2024
e7fda3c
Lint
janfaracik Dec 2, 2024
7fae33a
Tests passing
janfaracik Dec 2, 2024
7287f3e
Tidy up
janfaracik Dec 2, 2024
05b3d95
Update StaplerCustomJellyTagLibraryXmlNSDescriptor.java
janfaracik Dec 2, 2024
c9ee9c5
Delete smokeJexlInspection2.jelly
janfaracik Dec 2, 2024
253c79e
Tidy up
janfaracik Dec 2, 2024
89d2225
Merge branch 'master' into namespace-imports
janfaracik Dec 3, 2024
de929d5
Update JellyCompletionContributor.java
janfaracik Dec 3, 2024
8f85902
Update JellyCompletionContributor.java
janfaracik Dec 3, 2024
ea766c2
Update JellyCompletionContributor.java
janfaracik Dec 3, 2024
f3b5aa3
Find project namespaces and offer them
janfaracik Dec 3, 2024
488e1a3
Update NamespaceUtil.java
janfaracik Dec 3, 2024
7ac6a63
Fix bug with duplicate entries + handle jelly libs
janfaracik Dec 3, 2024
d9e8d5e
Merge branch 'master' into namespace-imports
janfaracik Dec 28, 2024
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
51 changes: 51 additions & 0 deletions src/main/java/io/jenkins/stapler/idea/jelly/NamespaceUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.jenkins.stapler.idea.jelly;

import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.xml.XmlTag;
import java.util.HashMap;
import java.util.Map;
import org.kohsuke.stapler.idea.language.JellyFileType;
import org.kohsuke.stapler.idea.psi.JellyFile;

public class NamespaceUtil {

/** Collects all namespaces (and prefixes) from .jelly files in the project */
public static Map<String, String> collectProjectNamespaces(Project project) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check the performance of this and how often its called and if there's any caching

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aye, need to see if there's any performance regressions, and if so how we can avoid them with cache.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems to be called on every key press although I didn't notice any delay... might be worth optimising if it doesn't cause other issues

Map<String, String> namespaces = new HashMap<>();

FileTypeIndex.processFiles(
JellyFileType.INSTANCE,
file -> {
PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
if (psiFile instanceof JellyFile jellyFile) {
XmlTag rootTag = jellyFile.getRootTag();
if (rootTag != null) {
String[] uris = rootTag.knownNamespaces();

for (String uri : uris) {
String prefix = rootTag.getPrefixByNamespace(uri);

if (prefix == null || prefix.isEmpty()) {
continue;
}

// Ignore local prefixes as they're not for global usage
if (prefix.equals("local") || prefix.equals("this")) {
continue;
}

namespaces.put(prefix, uri);
}
}
}
return true;
},
GlobalSearchScope.projectScope(project));

return namespaces;
}
}
285 changes: 184 additions & 101 deletions src/main/java/org/kohsuke/stapler/idea/JellyCompletionContributor.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,26 @@ private XmlElementDescriptor getElementDescriptor(String localName) {
return null;
}

/** Returns an empty array to disable IntelliJ's autocomplete */
@Override
@NotNull
public XmlElementDescriptor @NotNull [] getRootElementsDescriptors(@Nullable XmlDocument document) {
return new XmlElementDescriptor[0];
}

/**
* Returns all the possible root elements.
* Returns all possible tags for the library.
*
* <p>This appears to be used for code completion. When I returned an empty array, the code completion didn't show
* me anything.
* <p>This is used for generating autocomplete suggestions.
*/
@Override
@NotNull
public XmlElementDescriptor @NotNull [] getRootElementsDescriptors(@Nullable XmlDocument document) {
List<XmlElementDescriptor> r = new ArrayList<>();
public StaplerCustomJellyTagfileXmlElementDescriptor @NotNull [] getTagDescriptors() {
List<StaplerCustomJellyTagfileXmlElementDescriptor> r = new ArrayList<>();
for (PsiFile f : dir.getFiles()) {
if (!isTagFile(f)) continue;
if (f instanceof XmlFile) r.add(new StaplerCustomJellyTagfileXmlElementDescriptor(this, (XmlFile) f));
}
return r.toArray(new XmlElementDescriptor[0]);
return r.toArray(new StaplerCustomJellyTagfileXmlElementDescriptor[0]);
}

private boolean isTagFile(PsiFile file) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/kohsuke/stapler/idea/icons/Icons.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

/** @author Kohsuke Kawaguchi */
public class Icons {
public static final Icon STAPLER = IconLoader.getIcon("/org/kohsuke/stapler/idea/icons/stapler.png", Icons.class);
public static final Icon COMPONENT =
IconLoader.getIcon("/org/kohsuke/stapler/idea/icons/component.svg", Icons.class);
public static final Icon JELLY = IconLoader.getIcon("/org/kohsuke/stapler/idea/icons/jelly.svg", Icons.class);
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package org.kohsuke.stapler.idea;

import com.intellij.testFramework.fixtures.BasePlatformTestCase;

public class JellyCompletionContributorTest extends BasePlatformTestCase {

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

public void testDefaultTagLibrary() {
assertDefaultTagLibrary(
"""
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core">
<l:b<caret>
</j:jelly>
""",
"""
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout">
<l:basic />
</j:jelly>
""");
}

public void testRequiredAttributes() {
assertDefaultTagLibrary(
"""
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core">
<l:req<caret>
</j:jelly>
""",
"""
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout">
<l:required title="" />
</j:jelly>
""");
}

public void testInvokeBody() {
assertDefaultTagLibrary(
"""
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core">
<l:child<caret>
</j:jelly>
""",
"""
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout">
<l:children>
\s
</l:children>
</j:jelly>
""");
}

public void testCustomTagLibrary() {
myFixture.copyDirectoryToProject("testlib", "testlib");
myFixture.configureByText(
"basic.jelly",
"""
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:t="/testlib">
<t:t<caret>
</j:jelly>
""");

myFixture.completeBasic();

myFixture.checkResult(
"""
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:t="/testlib">
<t:test />
</j:jelly>
""");
}

private void assertDefaultTagLibrary(String body, String expected) {
// Simulate the default tag libraries in core
myFixture.copyDirectoryToProject("lib", "lib");
myFixture.configureByText("basic.jelly", body);

myFixture.completeBasic();

myFixture.checkResult(expected);
}
}

This file was deleted.

4 changes: 4 additions & 0 deletions src/test/testData/lib/layout/basic.jelly
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core">
<p>Test</p>
</j:jelly>
4 changes: 4 additions & 0 deletions src/test/testData/lib/layout/children.jelly
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:d="jelly:define">
<d:invokeBody />
</j:jelly>
7 changes: 7 additions & 0 deletions src/test/testData/lib/layout/required.jelly
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
<st:documentation>
<st:attribute name="title" use="required" />
</st:documentation>
<p>Test</p>
</j:jelly>
Empty file.