Skip to content

Commit

Permalink
support Twig lexer changes in PhpStorm 2017.3.2 #1123 #1125
Browse files Browse the repository at this point in the history
  • Loading branch information
Haehnchen committed Dec 23, 2017
1 parent f5b4780 commit 6de39f8
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 60 deletions.
5 changes: 2 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ before_script:
- chmod +x travis.sh

env:
- PHPSTORM_ENV=2017.2
- PHPSTORM_ENV=2017.2.4
#- PHPSTORM_ENV=2017.2 # unsupported api
#- PHPSTORM_ENV=2017.2.4 # unsupported api
- PHPSTORM_ENV=2017.3.2
#- PHPSTORM_ENV=eap # disabled never used

matrix:
allow_failures:
- env: PHPSTORM_ENV=eap
- env: PHPSTORM_ENV=2017.3.2
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
public class BlockGotoCompletionRegistrar implements GotoCompletionRegistrar {
public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
// {{ block('foo_block') }}
registrar.register(TwigPattern.getPrintBlockFunctionPattern("block"), psiElement -> {
registrar.register(TwigPattern.getPrintBlockOrTagFunctionPattern("block"), psiElement -> {
if (!Symfony2ProjectComponent.isEnabled(psiElement)) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public void collectSlowLineMarkers(@NotNull List<PsiElement> psiElements, @NotNu
if(overwrites != null) {
results.add(overwrites);
}
} else if (TwigPattern.getBlockTagPattern().accepts(psiElement) || TwigPattern.getPrintBlockFunctionPattern("block").accepts(psiElement)) {
} else if (TwigPattern.getBlockTagPattern().accepts(psiElement) || TwigPattern.getPrintBlockOrTagFunctionPattern("block").accepts(psiElement)) {
// blocks: {% block 'foobar' %}, {{ block('foobar') }}

VirtualFile virtualFile = psiElement.getContainingFile().getVirtualFile();
Expand Down
40 changes: 13 additions & 27 deletions src/fr/adrienbrault/idea/symfony2plugin/templating/TwigPattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,31 +93,6 @@ public static ElementPattern<PsiElement> getTranslationTokenTagFromPattern() {
);
}

/**
* Check for {{ include('|') }}
*
* @param functionName twig function name
*/
public static ElementPattern<PsiElement> getPrintBlockFunctionPattern(String... functionName) {
//noinspection unchecked
return PlatformPatterns
.psiElement(TwigTokenTypes.STRING_TEXT)
.withParent(
PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK)
)
.afterLeafSkipping(
PlatformPatterns.or(
PlatformPatterns.psiElement(TwigTokenTypes.LBRACE),
PlatformPatterns.psiElement(PsiWhiteSpace.class),
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE),
PlatformPatterns.psiElement(TwigTokenTypes.SINGLE_QUOTE),
PlatformPatterns.psiElement(TwigTokenTypes.DOUBLE_QUOTE)
),
PlatformPatterns.psiElement(TwigTokenTypes.IDENTIFIER).withText(PlatformPatterns.string().oneOf(functionName))
)
.withLanguage(TwigLanguage.INSTANCE);
}

/**
* {% include ['', ~ '', ''] %}
*/
Expand Down Expand Up @@ -184,12 +159,18 @@ public static ElementPattern<PsiElement> getPrintBlockOrTagFunctionPattern(Strin
.psiElement(TwigTokenTypes.STRING_TEXT)
.withParent(
PlatformPatterns.or(

// old and inconsistently implementations of FUNCTION_CALL:
// eg {% if asset('') %} does not provide a FUNCTION_CALL whereas a print block does
PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK),
PlatformPatterns.psiElement(TwigElementTypes.TAG),
PlatformPatterns.psiElement(TwigElementTypes.IF_TAG),
PlatformPatterns.psiElement(TwigElementTypes.SET_TAG),
PlatformPatterns.psiElement(TwigElementTypes.ELSE_TAG),
PlatformPatterns.psiElement(TwigElementTypes.ELSEIF_TAG)
PlatformPatterns.psiElement(TwigElementTypes.ELSEIF_TAG),

// PhpStorm 2017.3.2: {{ asset('') }}
PlatformPatterns.psiElement(TwigElementTypes.FUNCTION_CALL)
)
)
.afterLeafSkipping(
Expand Down Expand Up @@ -587,7 +568,12 @@ public static ElementPattern<PsiElement> getEmbedPattern() {
}

public static ElementPattern<PsiElement> getPrintBlockFunctionPattern() {
return PlatformPatterns.psiElement().withParent(PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK)).withLanguage(TwigLanguage.INSTANCE);
return PlatformPatterns.psiElement().withParent(PlatformPatterns.or(
PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK),

// PhpStorm 2017.3.2
PlatformPatterns.psiElement(TwigElementTypes.FUNCTION_CALL)
)).withLanguage(TwigLanguage.INSTANCE);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public TwigTemplateCompletionContributor() {

// all file template "include" pattern
extend(CompletionType.BASIC, PlatformPatterns.or(
TwigPattern.getPrintBlockFunctionPattern("include", "source"),
TwigPattern.getPrintBlockOrTagFunctionPattern("include", "source"),
TwigPattern.getIncludeTagArrayPattern(),
TwigPattern.getTagTernaryPattern(TwigElementTypes.INCLUDE_TAG)
), new TemplateCompletionProvider());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset,
targets.addAll(getRouteParameterGoTo(psiElement));
}

if(TwigPattern.getTemplateFileReferenceTagPattern().accepts(psiElement) || TwigPattern.getPrintBlockFunctionPattern("include", "source").accepts(psiElement)) {
if(TwigPattern.getTemplateFileReferenceTagPattern().accepts(psiElement) || TwigPattern.getPrintBlockOrTagFunctionPattern("include", "source").accepts(psiElement)) {
// support: {% include() %}, {{ include() }}
targets.addAll(getTwigFiles(psiElement, offset));
} else if (PlatformPatterns.psiElement(TwigTokenTypes.STRING_TEXT).withText(PlatformPatterns.string().endsWith(".twig")).accepts(psiElement)) {
Expand Down Expand Up @@ -140,7 +140,9 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset,
.psiElement(TwigTokenTypes.IDENTIFIER)
.withParent(PlatformPatterns.or(
PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK),
PlatformPatterns.psiElement(TwigElementTypes.SET_TAG)
PlatformPatterns.psiElement(TwigElementTypes.SET_TAG),

PlatformPatterns.psiElement(TwigElementTypes.FUNCTION_CALL)
)).withLanguage(TwigLanguage.INSTANCE).accepts(psiElement)) {

targets.addAll(this.getFunctions(psiElement));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder, bool
return new PsiElementVisitor() {
@Override
public void visitElement(PsiElement element) {
if((TwigPattern.getTemplateFileReferenceTagPattern().accepts(element) || TwigPattern.getPrintBlockFunctionPattern("include", "source").accepts(element)) && TwigUtil.isValidStringWithoutInterpolatedOrConcat(element)) {
if((TwigPattern.getTemplateFileReferenceTagPattern().accepts(element) || TwigPattern.getPrintBlockOrTagFunctionPattern("include", "source").accepts(element)) && TwigUtil.isValidStringWithoutInterpolatedOrConcat(element)) {
invoke(element, holder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,17 @@ public static Map<String, Collection<PsiElement>> getTemplateAnnotationFilesWith
public static String getTransDefaultDomainOnScope(@NotNull PsiElement position) {
// {% embed 'foo.html.twig' with { foo: '<caret>'|trans } %}
PsiElement parent = position.getParent();
if(parent != null && parent.getNode().getElementType() == TwigElementTypes.EMBED_TAG) {
PsiElement firstParent = PsiTreeUtil.findFirstParent(position, true, psiElement -> {
IElementType elementType = psiElement.getNode().getElementType();
return elementType != TwigElementTypes.EMBED_TAG && elementType != TwigElementTypes.EMBED_STATEMENT;
});
if(parent != null && parent.getNode().getElementType() == TwigElementTypes.LITERAL) {
PsiElement parent2 = parent.getParent();
if(parent2 != null && parent2.getNode().getElementType() == TwigElementTypes.EMBED_TAG) {
PsiElement firstParent = PsiTreeUtil.findFirstParent(parent, true, psiElement -> {
IElementType elementType = psiElement.getNode().getElementType();
return elementType != TwigElementTypes.EMBED_TAG && elementType != TwigElementTypes.EMBED_STATEMENT;
});

if(firstParent != null) {
position = firstParent;
if(firstParent != null) {
position = firstParent;
}
}
}

Expand Down Expand Up @@ -1556,23 +1559,27 @@ public static Collection<String> getIncludeTagStrings(@NotNull TwigTagWithFileRe

/**
* Visit string values of given array start brace
* ["foobar"]
*
* ["foobar", "foobar"]
* {"foobar", "foobar"}
*/
public static void visitStringInArray(@NotNull PsiElement arrayStartBrace, @NotNull Consumer<Pair<String, PsiElement>> pair) {
private static void visitStringInArray(@NotNull PsiElement arrayStartBrace, @NotNull Consumer<Pair<String, PsiElement>> pair) {
// match: "([,)''(,])"
Collection<PsiElement> questString = PsiElementUtils.getNextSiblingOfTypes(arrayStartBrace, PlatformPatterns.psiElement(TwigTokenTypes.STRING_TEXT)
.afterLeafSkipping(
TwigPattern.STRING_WRAP_PATTERN,
PlatformPatterns.or(
PlatformPatterns.psiElement(TwigTokenTypes.COMMA),
PlatformPatterns.psiElement(TwigTokenTypes.LBRACE_SQ)
)
PlatformPatterns.psiElement(TwigTokenTypes.LBRACE_SQ),
PlatformPatterns.psiElement(TwigTokenTypes.LBRACE_CURL)
)
)
.beforeLeafSkipping(
TwigPattern.STRING_WRAP_PATTERN,
PlatformPatterns.or(
PlatformPatterns.psiElement(TwigTokenTypes.COMMA),
PlatformPatterns.psiElement(TwigTokenTypes.RBRACE_SQ)
PlatformPatterns.psiElement(TwigTokenTypes.RBRACE_SQ),
PlatformPatterns.psiElement(TwigTokenTypes.RBRACE_CURL)
)
)
);
Expand Down Expand Up @@ -1620,7 +1627,7 @@ public void visitElement(PsiElement element) {
psiElement.acceptChildren(new PsiRecursiveElementVisitor() {
@Override
public void visitElement(PsiElement element) {
if(target[0] == null && TwigPattern.getPrintBlockFunctionPattern("block").accepts(element)) {
if(target[0] == null && TwigPattern.getPrintBlockOrTagFunctionPattern("block").accepts(element)) {
target[0] = element;
}
super.visitElement(element);
Expand Down Expand Up @@ -2213,7 +2220,12 @@ public static void visitTemplateIncludes(@NotNull TwigFile twigFile, @NotNull Co
visitTemplateIncludes(
twigFile,
consumer,
TemplateInclude.TYPE.EMBED, TemplateInclude.TYPE.INCLUDE, TemplateInclude.TYPE.INCLUDE_FUNCTION, TemplateInclude.TYPE.FROM, TemplateInclude.TYPE.IMPORT, TemplateInclude.TYPE.FORM_THEME
TemplateInclude.TYPE.EMBED,
TemplateInclude.TYPE.INCLUDE,
TemplateInclude.TYPE.INCLUDE_FUNCTION,
TemplateInclude.TYPE.FROM,
TemplateInclude.TYPE.IMPORT,
TemplateInclude.TYPE.FORM_THEME
);
}

Expand Down Expand Up @@ -2262,7 +2274,7 @@ private static void visitTemplateIncludes(@NotNull TwigFile twigFile, @NotNull C
// {{ include() }}
// {{ source() }}
if(myTypes.contains(TemplateInclude.TYPE.INCLUDE_FUNCTION)) {
PsiElement includeTag = PsiElementUtils.getChildrenOfType(psiElement, TwigPattern.getPrintBlockFunctionPattern("include", "source"));
PsiElement includeTag = PsiElementUtils.getChildrenOfType(psiElement, TwigPattern.getPrintBlockOrTagFunctionPattern("include", "source"));
if(includeTag != null) {
String templateName = includeTag.getText();
if(StringUtils.isNotBlank(templateName)) {
Expand Down Expand Up @@ -2299,17 +2311,21 @@ private static void visitTemplateIncludes(@NotNull TwigFile twigFile, @NotNull C
}
}

// {% form_theme form.child 'form/fields_child.html.twig' %}
// {% form_theme form.child with ['form/fields_child.html.twig'] %}
PsiElement withElement = PsiElementUtils.getNextSiblingOfType(tagElement, PlatformPatterns.psiElement().withElementType(TwigTokenTypes.IDENTIFIER).withText("with"));
if(withElement != null) {
PsiElement arrayStart = PsiElementUtils.getNextSiblingAndSkip(tagElement, TwigTokenTypes.LBRACE_SQ,
// find LITERAL "[", "{"
PsiElement arrayStart = PsiElementUtils.getNextSiblingAndSkip(tagElement, TwigElementTypes.LITERAL,
TwigTokenTypes.IDENTIFIER, TwigTokenTypes.SINGLE_QUOTE, TwigTokenTypes.DOUBLE_QUOTE, TwigTokenTypes.DOT
);

if(arrayStart != null) {
visitStringInArray(arrayStart, pair ->
consumer.consume(new TemplateInclude(psiElement, pair.getFirst(), TemplateInclude.TYPE.FORM_THEME))
);
PsiElement firstChild = arrayStart.getFirstChild();
if(firstChild != null) {
visitStringInArray(firstChild, pair ->
consumer.consume(new TemplateInclude(psiElement, pair.getFirst(), TemplateInclude.TYPE.FORM_THEME))
);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public void visitElement(PsiElement element) {

if(element instanceof TwigCompositeElement) {
// {{ include('template.html') }}
PsiElement includeTag = PsiElementUtils.getChildrenOfType(element, TwigPattern.getPrintBlockFunctionPattern("include"));
PsiElement includeTag = PsiElementUtils.getChildrenOfType(element, TwigPattern.getPrintBlockOrTagFunctionPattern("include"));
if(includeTag != null) {
collectContextVars(TwigTokenTypes.IDENTIFIER, element, includeTag);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ public void testThatConstantProvidesCompletionForClassAndDefine() {
}

public void testCompletionForRoutingParameter() {
assertCompletionContains(TwigFileType.INSTANCE, "{{ path('xml_route', {'<caret>']) }}", "slug");
assertNavigationMatch(TwigFileType.INSTANCE, "{{ path('xml_route', {'sl<caret>ug']) }}", PlatformPatterns.psiElement());
assertCompletionContains(TwigFileType.INSTANCE, "{{ path('xml_route', {'<caret>'}) }}", "slug");
assertNavigationMatch(TwigFileType.INSTANCE, "{{ path('xml_route', {'sl<caret>ug'}) }}", PlatformPatterns.psiElement());
}

public void testInsertHandlerForTwigFunctionWithStringParameter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,14 +274,16 @@ public void testVisitTemplateIncludes() {
PsiFile fileFromText = PsiFileFactory.getInstance(getProject()).createFileFromText(TwigLanguage.INSTANCE,
"{% form_theme form ':Foobar:fields.html.twig' %}" +
"{% form_theme form.foobar \":Foobar:fields_foobar.html.twig\" %}" +
"{% form_theme form.foobar with [\":Foobar:fields_foobar_1.html.twig\"] %}"
"{% form_theme form.foobar with [\":Foobar:fields_foobar_1.html.twig\"] %}" +
"{% form_theme form.foobar with {\":Foobar:fields_foobar_2.html.twig\", \":Foobar:fields_foobar_3.html.twig\", \":Foobar:fields_foobar_4.html.twig\"} %}"
);

TwigUtil.visitTemplateIncludes((TwigFile) fileFromText, templateInclude ->
includes.add(templateInclude.getTemplateName())
);

assertContainsElements(includes, ":Foobar:fields.html.twig", ":Foobar:fields_foobar.html.twig", ":Foobar:fields_foobar_1.html.twig");
assertContainsElements(includes, ":Foobar:fields_foobar_2.html.twig", ":Foobar:fields_foobar_3.html.twig", ":Foobar:fields_foobar_4.html.twig");
}

/**
Expand Down

0 comments on commit 6de39f8

Please sign in to comment.