Skip to content

Commit

Permalink
decouple Twig namespace loading and provide more default namespace wh…
Browse files Browse the repository at this point in the history
…ich work without a compiled container #784 #654
  • Loading branch information
Haehnchen committed Oct 9, 2016
1 parent 96bd875 commit 06a5ac8
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 29 deletions.
3 changes: 3 additions & 0 deletions META-INF/plugin.xml
Expand Up @@ -604,6 +604,9 @@

<TwigNamespaceExtension implementation="fr.adrienbrault.idea.symfony2plugin.templating.path.JsonFileIndexTwigNamespaces"/>
<TwigNamespaceExtension implementation="fr.adrienbrault.idea.symfony2plugin.templating.path.ConfigAddPathTwigNamespaces"/>
<TwigNamespaceExtension implementation="fr.adrienbrault.idea.symfony2plugin.templating.path.ContainerTwigNamespaceExtension"/>
<TwigNamespaceExtension implementation="fr.adrienbrault.idea.symfony2plugin.templating.path.GlobalAppTwigNamespaceExtension"/>
<TwigNamespaceExtension implementation="fr.adrienbrault.idea.symfony2plugin.templating.path.BundleTwigNamespaceExtension"/>

<ServiceCollector implementation="fr.adrienbrault.idea.symfony2plugin.dic.DefaultServiceCollector"/>

Expand Down
51 changes: 30 additions & 21 deletions src/fr/adrienbrault/idea/symfony2plugin/TwigHelper.java
Expand Up @@ -18,7 +18,6 @@
import com.intellij.psi.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileBasedIndexImpl;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.twig.TwigFile;
import com.jetbrains.twig.TwigFileType;
import com.jetbrains.twig.TwigLanguage;
Expand All @@ -34,7 +33,10 @@
import fr.adrienbrault.idea.symfony2plugin.templating.assets.TwigNamedAssetsServiceParser;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TemplateFileMap;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigBlock;
import fr.adrienbrault.idea.symfony2plugin.templating.path.*;
import fr.adrienbrault.idea.symfony2plugin.templating.path.TwigNamespaceSetting;
import fr.adrienbrault.idea.symfony2plugin.templating.path.TwigPath;
import fr.adrienbrault.idea.symfony2plugin.templating.path.TwigPathContentIterator;
import fr.adrienbrault.idea.symfony2plugin.templating.path.TwigPathIndex;
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigTypeResolveUtil;
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
Expand Down Expand Up @@ -402,38 +404,23 @@ public static List<TwigPath> getTwigNamespaces(@NotNull Project project) {
@NotNull
public static List<TwigPath> getTwigNamespaces(@NotNull Project project, boolean includeSettings) {
List<TwigPath> twigPaths = new ArrayList<>();
PhpIndex phpIndex = PhpIndex.getInstance(project);

TwigPathServiceParser twigPathServiceParser = ServiceXmlParserFactory.getInstance(project, TwigPathServiceParser.class);
twigPaths.addAll(twigPathServiceParser.getTwigPathIndex().getTwigPaths());

String appDirectoryName = Settings.getInstance(project).directoryToApp + "/Resources/views";
VirtualFile globalDirectory = VfsUtil.findRelativeFile(project.getBaseDir(), appDirectoryName.split("/"));
if(globalDirectory != null) {
twigPaths.add(new TwigPath(globalDirectory.getPath(), TwigPathIndex.MAIN, TwigPathIndex.NamespaceType.BUNDLE));
}

Collection<SymfonyBundle> symfonyBundles = new SymfonyBundleUtil(phpIndex).getBundles();
for (SymfonyBundle bundle : symfonyBundles) {
PsiDirectory views = bundle.getSubDirectory("Resources", "views");
if(views != null) {
twigPaths.add(new TwigPath(views.getVirtualFile().getPath(), bundle.getName(), TwigPathIndex.NamespaceType.BUNDLE));
}
}

// load extension
TwigNamespaceExtensionParameter parameter = new TwigNamespaceExtensionParameter(project);
for (TwigNamespaceExtension namespaceExtension : EXTENSIONS.getExtensions()) {
twigPaths.addAll(namespaceExtension.getNamespaces(parameter));
}

// disable namespace explicitly disabled by user
for(TwigPath twigPath: twigPaths) {
TwigNamespaceSetting twigNamespaceSetting = findManagedTwigNamespace(project, twigPath);
if(twigNamespaceSetting != null) {
twigPath.setEnabled(false);
}
}

twigPaths = getUniqueTwigTemplatesList(twigPaths);

if(!includeSettings) {
return twigPaths;
}
Expand All @@ -443,14 +430,36 @@ public static List<TwigPath> getTwigNamespaces(@NotNull Project project, boolean
for(TwigNamespaceSetting twigNamespaceSetting: twigNamespaceSettings) {
if(twigNamespaceSetting.isCustom()) {
twigPaths.add(new TwigPath(twigNamespaceSetting.getPath(), twigNamespaceSetting.getNamespace(), twigNamespaceSetting.getNamespaceType(), true).setEnabled(twigNamespaceSetting.isEnabled()));

}
}
}

return twigPaths;
}

/**
* Build a unique path + namespace + type list
* normalize also windows linux path
*/
@NotNull
public static List<TwigPath> getUniqueTwigTemplatesList(@NotNull Collection<TwigPath> origin) {
List<TwigPath> twigPaths = new ArrayList<>();

Set<String> hashes = new HashSet<>();
for (TwigPath twigPath : origin) {
// normalize hash; for same path element
// TODO: move to path object itself
String hash = twigPath.getNamespaceType() + twigPath.getNamespace() + twigPath.getPath().replace("\\", "/");
if(hashes.contains(hash)) {
continue;
}

twigPaths.add(twigPath);
hashes.add(hash);
}

return twigPaths;
}

@Nullable
public static String getTwigMethodString(@Nullable PsiElement transPsiElement) {
Expand Down
@@ -0,0 +1,47 @@
package fr.adrienbrault.idea.symfony2plugin.templating.path;

import com.intellij.psi.PsiDirectory;
import com.jetbrains.php.PhpIndex;
import fr.adrienbrault.idea.symfony2plugin.extension.TwigNamespaceExtension;
import fr.adrienbrault.idea.symfony2plugin.extension.TwigNamespaceExtensionParameter;
import fr.adrienbrault.idea.symfony2plugin.util.SymfonyBundleUtil;
import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;

/**
* FooBundle/Resources/views/foo.html.twig => FooBundle:foo.html.twig
* FooBundle/Resources/views/foo.html.twig => @Foo/foo.html.twig
*
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class BundleTwigNamespaceExtension implements TwigNamespaceExtension {
@NotNull
@Override
public Collection<TwigPath> getNamespaces(@NotNull TwigNamespaceExtensionParameter parameter) {
Collection<TwigPath> twigPaths = new ArrayList<>();

Collection<SymfonyBundle> symfonyBundles = new SymfonyBundleUtil(PhpIndex.getInstance(parameter.getProject())).getBundles();
for (SymfonyBundle bundle : symfonyBundles) {
PsiDirectory views = bundle.getSubDirectory("Resources", "views");
if(views == null) {
continue;
}

// strip starting backslash to force relative path
String path = StringUtils.stripStart(views.getVirtualFile().getPath(), "/");

String bundleName = bundle.getName();

twigPaths.add(new TwigPath(path, bundleName, TwigPathIndex.NamespaceType.BUNDLE));
if(bundleName.endsWith("Bundle")) {
twigPaths.add(new TwigPath(path, bundleName.substring(0, bundleName.length() - 6), TwigPathIndex.NamespaceType.ADD_PATH));
}
}

return twigPaths;
}
}
@@ -0,0 +1,31 @@
package fr.adrienbrault.idea.symfony2plugin.templating.path;

import fr.adrienbrault.idea.symfony2plugin.extension.TwigNamespaceExtension;
import fr.adrienbrault.idea.symfony2plugin.extension.TwigNamespaceExtensionParameter;
import fr.adrienbrault.idea.symfony2plugin.util.service.ServiceXmlParserFactory;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;

/**
* Collects path on compiled container: appDevDebugProjectContainer.xml
*
* <call method="addPath">
* <argument>... ymfony\Bundle\FrameworkBundle/Resources/views</argument>
* <argument>Framework</argument>
* </call>
*
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class ContainerTwigNamespaceExtension implements TwigNamespaceExtension {
@NotNull
@Override
public Collection<TwigPath> getNamespaces(@NotNull TwigNamespaceExtensionParameter parameter) {
TwigPathServiceParser twigPathServiceParser = ServiceXmlParserFactory.getInstance(parameter.getProject(), TwigPathServiceParser.class);

return new ArrayList<>(
twigPathServiceParser.getTwigPathIndex().getTwigPaths()
);
}
}
@@ -0,0 +1,39 @@
package fr.adrienbrault.idea.symfony2plugin.templating.path;

import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import fr.adrienbrault.idea.symfony2plugin.Settings;
import fr.adrienbrault.idea.symfony2plugin.extension.TwigNamespaceExtension;
import fr.adrienbrault.idea.symfony2plugin.extension.TwigNamespaceExtensionParameter;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

/**
* app/Resources/views/foo.html.twig => :foo.html.twig
* app/Resources/views/foo.html.twig => foo.html.twig
*
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class GlobalAppTwigNamespaceExtension implements TwigNamespaceExtension {
@NotNull
@Override
public Collection<TwigPath> getNamespaces(@NotNull TwigNamespaceExtensionParameter parameter) {
String appDirectoryName = Settings.getInstance(parameter.getProject()).directoryToApp + "/Resources/views";
VirtualFile baseDir = parameter.getProject().getBaseDir();

VirtualFile globalDirectory = VfsUtil.findRelativeFile(baseDir, appDirectoryName.split("/"));
if(globalDirectory == null) {
return Collections.emptyList();
}

String path = globalDirectory.getPath();

return Arrays.asList(
new TwigPath(path, TwigPathIndex.MAIN, TwigPathIndex.NamespaceType.BUNDLE),
new TwigPath(path, TwigPathIndex.MAIN, TwigPathIndex.NamespaceType.ADD_PATH)
);
}
}
Expand Up @@ -3,34 +3,40 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.text.Collator;

public class TwigPath implements Comparable<TwigPath> {

@NotNull
private String path;

@NotNull
private String namespace = TwigPathIndex.MAIN;

@NotNull
private TwigPathIndex.NamespaceType namespaceType = TwigPathIndex.NamespaceType.ADD_PATH;

private boolean enabled = true;
private boolean customPath = false;

@NotNull
public TwigPathIndex.NamespaceType getNamespaceType() {
return namespaceType;
}

private TwigPathIndex.NamespaceType namespaceType = TwigPathIndex.NamespaceType.ADD_PATH;

public TwigPath(String path) {
public TwigPath(@NotNull String path) {
this.path = path;
}

public TwigPath(String path, String namespace) {
public TwigPath(@NotNull String path, @NotNull String namespace) {
this.path = path;
this.namespace = namespace;
}

public TwigPath(String path, String namespace, TwigPathIndex.NamespaceType namespaceType, boolean customPath) {
public TwigPath(@NotNull String path, @NotNull String namespace, @NotNull TwigPathIndex.NamespaceType namespaceType, boolean customPath) {
this(path, namespace, namespaceType);
this.customPath = customPath;
}
Expand All @@ -47,11 +53,12 @@ public TwigPath clone() {
return twigPath;
}

public TwigPath(String path, String namespace, TwigPathIndex.NamespaceType namespaceType) {
public TwigPath(@NotNull String path, @NotNull String namespace, @NotNull TwigPathIndex.NamespaceType namespaceType) {
this(path, namespace);
this.namespaceType = namespaceType;
}

@NotNull
public String getNamespace() {
return namespace;
}
Expand Down Expand Up @@ -85,6 +92,7 @@ public VirtualFile getDirectory(Project project) {
return VfsUtil.findRelativeFile(relativePath, project.getBaseDir());
}

@NotNull
public String getPath() {
return path;
}
Expand All @@ -111,7 +119,7 @@ private VirtualFile getDirectory() {
}

@Override
public int compareTo(TwigPath twigPath) {
public int compareTo(@NotNull TwigPath twigPath) {
Collator collator = Collator.getInstance();
collator.setStrength(Collator.SECONDARY);
return collator.compare(this.getNamespace(), twigPath.getNamespace());
Expand Down
Expand Up @@ -15,6 +15,8 @@
import com.jetbrains.twig.elements.TwigTagWithFileReference;
import fr.adrienbrault.idea.symfony2plugin.TwigHelper;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigBlock;
import fr.adrienbrault.idea.symfony2plugin.templating.path.TwigPath;
import fr.adrienbrault.idea.symfony2plugin.templating.path.TwigPathIndex;
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlPsiElementFactory;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -199,6 +201,31 @@ public void testGetBlockTagPattern() {
}
}

/**
* @see TwigHelper#getUniqueTwigTemplatesList
*/
public void testGetUniqueTwigTemplatesList() {
assertSize(1, TwigHelper.getUniqueTwigTemplatesList(Arrays.asList(
new TwigPath("path/", "path"),
new TwigPath("path\\", "path")
)));

assertSize(1, TwigHelper.getUniqueTwigTemplatesList(Arrays.asList(
new TwigPath("path", "path"),
new TwigPath("path", "path")
)));

assertSize(2, TwigHelper.getUniqueTwigTemplatesList(Arrays.asList(
new TwigPath("path/a", "path"),
new TwigPath("foobar", "path")
)));

assertSize(2, TwigHelper.getUniqueTwigTemplatesList(Arrays.asList(
new TwigPath("path", "path", TwigPathIndex.NamespaceType.BUNDLE),
new TwigPath("path", "path")
)));
}

private void assertEqual(Collection<String> c, String... values) {
if(!StringUtils.join(c, ",").equals(StringUtils.join(Arrays.asList(values), ","))) {
fail(String.format("Fail that '%s' is equal '%s'", StringUtils.join(c, ","), StringUtils.join(Arrays.asList(values), ",")));
Expand Down
@@ -0,0 +1,43 @@
package fr.adrienbrault.idea.symfony2plugin.tests.templating.path;

import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.VfsTestUtil;
import com.intellij.util.containers.ContainerUtil;
import fr.adrienbrault.idea.symfony2plugin.extension.TwigNamespaceExtensionParameter;
import fr.adrienbrault.idea.symfony2plugin.templating.path.BundleTwigNamespaceExtension;
import fr.adrienbrault.idea.symfony2plugin.templating.path.TwigPath;
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;

import java.io.File;
import java.util.Collection;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
* @see fr.adrienbrault.idea.symfony2plugin.templating.path.BundleTwigNamespaceExtension
*/
public class BundleTwigNamespaceExtensionTest extends SymfonyLightCodeInsightFixtureTestCase {
@Override
public void setUp() throws Exception {
super.setUp();

VirtualFile virtualFile = myFixture.copyFileToProject("classes.php");
VfsTestUtil.createDir(virtualFile.getParent(), "Resources/views");
}

public String getTestDataPath() {
return new File(this.getClass().getResource("fixtures").getFile()).getAbsolutePath();
}

public void testThatBundleNamespacesAreAdded() {
Collection<TwigPath> namespaces = new BundleTwigNamespaceExtension()
.getNamespaces(new TwigNamespaceExtensionParameter(getProject()));

assertNotNull(ContainerUtil.find(namespaces, twigPath ->
"FooBundle".equals(twigPath.getNamespace()) && "src/Resources/views".equals(twigPath.getPath()))
);

assertNotNull(ContainerUtil.find(namespaces, twigPath ->
"Foo".equals(twigPath.getNamespace()) && "src/Resources/views".equals(twigPath.getPath()))
);
}
}

0 comments on commit 06a5ac8

Please sign in to comment.