Skip to content
This repository was archived by the owner on Mar 12, 2022. It is now read-only.

Commit 626cf37

Browse files
committed
provide references for assets #170
1 parent 06e77c2 commit 626cf37

File tree

4 files changed

+221
-1
lines changed

4 files changed

+221
-1
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package de.espend.idea.laravel.asset;
2+
3+
import com.intellij.codeInsight.lookup.LookupElement;
4+
import com.intellij.patterns.PlatformPatterns;
5+
import com.intellij.psi.PsiElement;
6+
import com.jetbrains.php.lang.PhpLanguage;
7+
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
8+
import de.espend.idea.laravel.LaravelProjectComponent;
9+
import de.espend.idea.laravel.util.PsiElementUtils;
10+
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProvider;
11+
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar;
12+
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrarParameter;
13+
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
14+
import org.apache.commons.lang.StringUtils;
15+
import org.jetbrains.annotations.NotNull;
16+
17+
import java.util.ArrayList;
18+
import java.util.Collection;
19+
import java.util.Collections;
20+
21+
/**
22+
* public/foobar => {{ asset('foobar.css') }}
23+
*
24+
* @author Daniel Espendiller <[email protected]>
25+
*/
26+
public class AssetGotoCompletionRegistrar implements GotoCompletionRegistrar {
27+
private static final MethodMatcher.CallToSignature[] ASSETS = {
28+
new MethodMatcher.CallToSignature("\\Illuminate\\Routing\\UrlGenerator", "asset"),
29+
new MethodMatcher.CallToSignature("\\Illuminate\\Routing\\UrlGenerator", "secureAsset"),
30+
};
31+
32+
@Override
33+
public void register(GotoCompletionRegistrarParameter registrar) {
34+
/*
35+
* view('caret');
36+
* Factory::make('caret');
37+
*/
38+
registrar.register(PlatformPatterns.psiElement().withParent(StringLiteralExpression.class).withLanguage(PhpLanguage.INSTANCE), psiElement -> {
39+
if(psiElement == null || !LaravelProjectComponent.isEnabled(psiElement)) {
40+
return null;
41+
}
42+
43+
PsiElement stringLiteral = psiElement.getParent();
44+
if(!(stringLiteral instanceof StringLiteralExpression)) {
45+
return null;
46+
}
47+
48+
if(!(
49+
PsiElementUtils.isFunctionReference(stringLiteral, "asset", 0)
50+
|| PsiElementUtils.isFunctionReference(stringLiteral, "secure_asset", 0)
51+
|| PsiElementUtils.isFunctionReference(stringLiteral, "secureAsset", 0)
52+
) && MethodMatcher.getMatchedSignatureWithDepth(stringLiteral, ASSETS) == null) {
53+
return null;
54+
}
55+
56+
return new AssetGotoCompletionProvider(stringLiteral);
57+
});
58+
}
59+
60+
/**
61+
* public/foobar => {{ asset('foobar.css') }}
62+
*/
63+
private static class AssetGotoCompletionProvider extends GotoCompletionProvider {
64+
AssetGotoCompletionProvider(PsiElement stringLiteral) {
65+
super(stringLiteral);
66+
}
67+
68+
@NotNull
69+
@Override
70+
public Collection<LookupElement> getLookupElements() {
71+
return AssetUtil.getLookupElements(getProject());
72+
}
73+
74+
@NotNull
75+
@Override
76+
public Collection<PsiElement> getPsiTargets(StringLiteralExpression element) {
77+
String contents = element.getContents();
78+
if(StringUtils.isBlank(contents)) {
79+
return Collections.emptyList();
80+
}
81+
82+
return new ArrayList<>(
83+
PsiElementUtils.convertVirtualFilesToPsiFiles(getProject(), AssetUtil.resolveAsset(getProject(), contents))
84+
);
85+
}
86+
}
87+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package de.espend.idea.laravel.asset;
2+
3+
import com.intellij.codeInsight.lookup.LookupElement;
4+
import com.intellij.codeInsight.lookup.LookupElementBuilder;
5+
import com.intellij.openapi.project.Project;
6+
import com.intellij.openapi.vfs.VfsUtil;
7+
import com.intellij.openapi.vfs.VirtualFile;
8+
import com.intellij.openapi.vfs.VirtualFileVisitor;
9+
import com.intellij.psi.PsiFile;
10+
import com.intellij.psi.PsiManager;
11+
import org.jetbrains.annotations.NotNull;
12+
13+
import java.util.ArrayList;
14+
import java.util.Collection;
15+
import java.util.Objects;
16+
import java.util.stream.Collectors;
17+
import java.util.stream.Stream;
18+
19+
/**
20+
* @author Daniel Espendiller <[email protected]>
21+
*/
22+
public class AssetUtil {
23+
private static final String[] FOLDERS = new String[] {
24+
"public"
25+
};
26+
27+
public static Collection<LookupElement> getLookupElements(@NotNull Project project) {
28+
Collection<LookupElement> lookupElements = new ArrayList<>();
29+
30+
PsiManager psiManager = PsiManager.getInstance(project);
31+
32+
for (String folder : FOLDERS) {
33+
VirtualFile assetDir = VfsUtil.findRelativeFile(project.getBaseDir(), folder);
34+
if(assetDir == null) {
35+
continue;
36+
}
37+
38+
VfsUtil.visitChildrenRecursively(assetDir, new VirtualFileVisitor() {
39+
@Override
40+
public boolean visitFile(@NotNull VirtualFile virtualFile) {
41+
if(virtualFile.isDirectory()) {
42+
return true;
43+
}
44+
45+
String filename = VfsUtil.getRelativePath(virtualFile, assetDir, '/');
46+
if(filename == null || filename.startsWith(".")) {
47+
return true;
48+
}
49+
50+
PsiFile psiFile = psiManager.findFile(virtualFile);
51+
if(psiFile != null) {
52+
LookupElementBuilder lookupElementBuilder = LookupElementBuilder.create(filename);
53+
lookupElementBuilder = lookupElementBuilder.withIcon(psiFile.getIcon(0));
54+
55+
lookupElements.add(lookupElementBuilder);
56+
}
57+
58+
return true;
59+
}
60+
});
61+
}
62+
63+
return lookupElements;
64+
}
65+
66+
/**
67+
* Provide targets for given assets. Mainly inside "/public" folder of root
68+
*/
69+
@NotNull
70+
public static Collection<VirtualFile> resolveAsset(@NotNull Project project, @NotNull String asset) {
71+
// normalize path
72+
String path = asset
73+
.replace("\\", "/")
74+
.replaceAll("/+", "/");
75+
76+
return Stream.of(FOLDERS)
77+
.map(folder -> VfsUtil.findRelativeFile(project.getBaseDir(), folder))
78+
.filter(Objects::nonNull)
79+
.map(relativeFile -> VfsUtil.findRelativeFile(relativeFile, path.split("/")))
80+
.filter(Objects::nonNull)
81+
.collect(Collectors.toSet());
82+
}
83+
}

src/fr/adrienbrault/idea/symfony2plugin/codeInsight/utils/GotoCompletionUtil.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package fr.adrienbrault.idea.symfony2plugin.codeInsight.utils;
22

33
import com.intellij.psi.PsiElement;
4+
import de.espend.idea.laravel.asset.AssetGotoCompletionRegistrar;
45
import de.espend.idea.laravel.blade.BladeDirectiveReferences;
56
import de.espend.idea.laravel.config.AppConfigReferences;
67
import de.espend.idea.laravel.config.ProviderGotoCompletion;
@@ -27,7 +28,8 @@ public class GotoCompletionUtil {
2728
new BladeDirectiveReferences(),
2829
new TranslationReferences(),
2930
new RoutingGotoCompletionRegistrar(),
30-
new DicCompletionRegistrar()
31+
new DicCompletionRegistrar(),
32+
new AssetGotoCompletionRegistrar(),
3133
};
3234

3335
public static Collection<GotoCompletionContributor> getContributors(final PsiElement psiElement) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package de.espend.idea.laravel.tests.asset;
2+
3+
import de.espend.idea.laravel.asset.AssetUtil;
4+
import de.espend.idea.laravel.tests.LaravelTempCodeInsightFixtureTestCase;
5+
6+
/**
7+
* @author Daniel Espendiller <[email protected]>
8+
*
9+
* @see de.espend.idea.laravel.asset.AssetUtil
10+
*/
11+
public class AssetUtilTempTest extends LaravelTempCodeInsightFixtureTestCase {
12+
/**
13+
* @see AssetUtil#getLookupElements
14+
*/
15+
public void testGetLookupElements() {
16+
createFiles(".htaccess", "public/foo/bar.css", "public/bar.js");
17+
18+
assertEquals(2, AssetUtil.getLookupElements(getProject()).size());
19+
20+
assertNotNull(AssetUtil.getLookupElements(getProject())
21+
.stream()
22+
.filter(lookupElement -> "foo/bar.css".equals(lookupElement.getLookupString()))
23+
.findFirst()
24+
.orElseGet(null)
25+
);
26+
}
27+
28+
/**
29+
* @see AssetUtil#resolveAsset
30+
*/
31+
public void testResolveAsset() {
32+
createFiles("public/foo/bar.css", "public/bar.js");
33+
34+
assertNotNull(AssetUtil.resolveAsset(getProject(), "foo\\bar.css")
35+
.stream()
36+
.filter(virtualFile -> "bar.css".equals(virtualFile.getName()))
37+
.findFirst()
38+
.orElseGet(null)
39+
);
40+
41+
assertNotNull(AssetUtil.resolveAsset(getProject(), "bar.js")
42+
.stream()
43+
.filter(virtualFile -> "bar.js".equals(virtualFile.getName()))
44+
.findFirst()
45+
.orElseGet(null)
46+
);
47+
}
48+
}

0 commit comments

Comments
 (0)