diff --git a/build.gradle b/build.gradle index e398c4a..011302a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ import org.jetbrains.grammarkit.tasks.* plugins { - id 'org.jetbrains.intellij' version "0.6.5" - id "org.jetbrains.grammarkit" version "2020.3" + id 'org.jetbrains.intellij' version "0.7.3" + id "org.jetbrains.grammarkit" version "2021.1" } sourceSets { @@ -19,7 +19,7 @@ properties.load(project.rootProject.file("local.properties").newDataInputStream( sourceCompatibility = 1.8 targetCompatibility = 1.8 -version '1.1.2' +version '1.1.3' group 'com.jantvrdik.intellij.latte' jar { diff --git a/gradle.properties b/gradle.properties index 2ce11a9..29fc6ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -ideaVersion = 2020.3 -phpPluginVersion = 203.5981.155 +ideaVersion = 2021.1 +phpPluginVersion = 211.6693.111 #toolboxPluginVersion = 0.4.6 #annotationPluginVersion = 5.3 \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/annotator/LatteAnnotator.java b/src/main/java/com/jantvrdik/intellij/latte/annotator/LatteAnnotator.java index 0788d08..e5d2238 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/annotator/LatteAnnotator.java +++ b/src/main/java/com/jantvrdik/intellij/latte/annotator/LatteAnnotator.java @@ -11,12 +11,8 @@ import com.jantvrdik.intellij.latte.intentions.*; import com.jantvrdik.intellij.latte.psi.*; import com.jantvrdik.intellij.latte.settings.LatteTagSettings; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; -import com.jetbrains.php.lang.psi.elements.PhpClass; import org.jetbrains.annotations.NotNull; -import java.util.Collection; - /** * Annotator is mostly used to check semantic rules which can not be easily checked during parsing. */ @@ -59,7 +55,7 @@ private void checkNetteAttr(@NotNull LatteNetteAttr element, @NotNull Annotation .range(attrName) .withFix(new AddCustomPairMacro(tagName)); if (!prefixed) { - builder.withFix(new AddCustomAttrOnlyMacro(tagName)); + builder = builder.withFix(new AddCustomAttrOnlyMacro(tagName)); } builder.create(); diff --git a/src/main/java/com/jantvrdik/intellij/latte/completion/LatteCompletionContributor.java b/src/main/java/com/jantvrdik/intellij/latte/completion/LatteCompletionContributor.java index 39dd493..cc344f5 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/completion/LatteCompletionContributor.java +++ b/src/main/java/com/jantvrdik/intellij/latte/completion/LatteCompletionContributor.java @@ -33,7 +33,7 @@ public LatteCompletionContributor() { extend(CompletionType.BASIC, PlatformPatterns.psiElement(LatteTypes.T_MACRO_NAME).withLanguage(LatteLanguage.INSTANCE), new CompletionProvider() { @Override - protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) { + protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { PsiElement parent = parameters.getPosition().getParent(); Project project = parameters.getOriginalFile().getProject(); attachClassicMacrosCompletion(project, result, parent instanceof LatteMacroCloseTag); @@ -42,7 +42,7 @@ protected void addCompletions(@NotNull CompletionParameters parameters, Processi extend(CompletionType.BASIC, PlatformPatterns.psiElement(LatteTypes.T_HTML_TAG_NATTR_NAME).withLanguage(LatteLanguage.INSTANCE), new CompletionProvider() { @Override - protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) { + protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { Project project = parameters.getOriginalFile().getProject(); Map customMacros = LatteConfiguration.getInstance(project).getTags(); result.addAllElements(getAttrMacroCompletions(customMacros)); @@ -51,7 +51,7 @@ protected void addCompletions(@NotNull CompletionParameters parameters, Processi extend(CompletionType.BASIC, PlatformPatterns.psiElement().withLanguage(LatteLanguage.INSTANCE), new CompletionProvider() { @Override - protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) { + protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { PsiElement element = parameters.getPosition().getParent(); if (!LatteUtil.matchParentMacroName(element, "contentType")) { return; @@ -62,7 +62,7 @@ protected void addCompletions(@NotNull CompletionParameters parameters, Processi extend(CompletionType.BASIC, PlatformPatterns.psiElement(LatteTypes.T_MACRO_FILTERS).withLanguage(LatteLanguage.INSTANCE), new CompletionProvider() { @Override - protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) { + protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { PsiElement element = parameters.getPosition().getParent(); if (!(element instanceof LatteMacroModifier)) { return; @@ -143,7 +143,12 @@ private LookupElementBuilder createBuilderForMacro(LatteTagSettings tag, boolean LookupElementBuilder builder = LookupElementBuilder.create(name); builder = builder.withInsertHandler(MacroInsertHandler.getInstance()); if (!isEndTag) { - String appendText = tag.getType() == LatteTagSettings.Type.PAIR ? (" … {/" + tag.getMacroName() + "}") : ""; + String appendText = ""; + if (tag.getType() == LatteTagSettings.Type.PAIR) { + appendText = " … {/" + tag.getMacroName() + "}"; + } else if (tag.getType() == LatteTagSettings.Type.AUTO_EMPTY) { + appendText = " … ?{/" + tag.getMacroName() + "}"; + } String arguments = tag.getArgumentsInfo(); if (arguments.length() > 0) { builder = builder.withTailText(" " + arguments + "}" + appendText); @@ -173,12 +178,8 @@ private LookupElementBuilder createBuilderForTag(String name) { private List getAttrMacroCompletions(Map macros) { List lookupElements = new ArrayList<>(macros.size()); for (LatteTagSettings macro : macros.values()) { - if (macro.getType() != LatteTagSettings.Type.UNPAIRED) { - lookupElements.add(createBuilderForTag("n:" + macro.getMacroName())); - if (macro.getType() == LatteTagSettings.Type.PAIR || macro.getType() == LatteTagSettings.Type.AUTO_EMPTY) { - lookupElements.add(createBuilderForTag("n:tag-" + macro.getMacroName())); - lookupElements.add(createBuilderForTag("n:inner-" + macro.getMacroName())); - } + for (String tagName : macro.getAllowedNetteAttributes()) { + lookupElements.add(createBuilderForTag(tagName)); } } return lookupElements; diff --git a/src/main/java/com/jantvrdik/intellij/latte/completion/handlers/PhpClassInsertHandler.java b/src/main/java/com/jantvrdik/intellij/latte/completion/handlers/PhpClassInsertHandler.java index 87a6456..d5b5f7d 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/completion/handlers/PhpClassInsertHandler.java +++ b/src/main/java/com/jantvrdik/intellij/latte/completion/handlers/PhpClassInsertHandler.java @@ -2,13 +2,7 @@ import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.openapi.editor.CaretModel; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.jantvrdik.intellij.latte.psi.LattePhpClassUsage; -import com.jantvrdik.intellij.latte.psi.LatteTypes; import com.jetbrains.php.completion.insert.PhpReferenceInsertHandler; import com.jetbrains.php.lang.psi.elements.PhpClass; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/jantvrdik/intellij/latte/completion/handlers/PhpNamespaceInsertHandler.java b/src/main/java/com/jantvrdik/intellij/latte/completion/handlers/PhpNamespaceInsertHandler.java new file mode 100644 index 0000000..bc0ec05 --- /dev/null +++ b/src/main/java/com/jantvrdik/intellij/latte/completion/handlers/PhpNamespaceInsertHandler.java @@ -0,0 +1,48 @@ +package com.jantvrdik.intellij.latte.completion.handlers; + +import com.intellij.codeInsight.completion.InsertionContext; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.editor.EditorModificationUtil; +import com.intellij.psi.PsiDocumentManager; +import com.jetbrains.php.completion.insert.PhpReferenceInsertHandler; +import com.jetbrains.php.lang.psi.elements.PhpNamespace; +import org.jetbrains.annotations.NotNull; + +public class PhpNamespaceInsertHandler extends PhpReferenceInsertHandler { + + private static final PhpNamespaceInsertHandler instance = new PhpNamespaceInsertHandler(); + + public PhpNamespaceInsertHandler() { + super(); + } + + public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement lookupElement) { + final Object object = lookupElement.getObject(); + String fqn = object instanceof PhpNamespace ? ((PhpNamespace) object).getParentNamespaceName() : ""; + if (fqn.isEmpty()) { + return; + } + + int startOffset = context.getEditor().getCaretModel().getOffset(); + String fileText = context.getEditor().getDocument().getText(); + String current = fileText.substring(0, startOffset); + int lastSpace = current.lastIndexOf(" "); + current = current.substring(lastSpace + 1); + + if (fqn.startsWith("\\")) { + fqn = fqn.substring(1); + } + + context.getDocument().insertString(context.getStartOffset(), fqn); + + if (!current.endsWith("\\")) { + EditorModificationUtil.insertStringAtCaret(context.getEditor(), "\\"); + } + + PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getDocument()); + } + + public static PhpNamespaceInsertHandler getInstance() { + return instance; + } +} \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/BaseLatteCompletionProvider.java b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/BaseLatteCompletionProvider.java index 2329805..5ea9182 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/BaseLatteCompletionProvider.java +++ b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/BaseLatteCompletionProvider.java @@ -2,6 +2,10 @@ import com.intellij.codeInsight.completion.CompletionParameters; import com.intellij.codeInsight.completion.CompletionProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.jantvrdik.intellij.latte.psi.LattePhpClassUsage; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.completion.PhpLookupElement; import com.jetbrains.php.lang.psi.elements.PhpNamedElement; import org.jetbrains.annotations.NotNull; @@ -11,18 +15,42 @@ import java.util.Set; abstract class BaseLatteCompletionProvider extends CompletionProvider { - PhpLookupElement getPhpLookupElement(@NotNull PhpNamedElement phpNamedElement, @Nullable String searchedWord) { - PhpLookupElement lookupItem = new PhpLookupElement(phpNamedElement) { + protected PhpLookupElement getPhpLookupElement(@NotNull PhpNamedElement phpNamedElement, @Nullable String searchedWord) { + return getPhpLookupElement(phpNamedElement, searchedWord, null); + } + + protected PhpLookupElement getPhpLookupElement(@NotNull PhpNamedElement phpNamedElement, @Nullable String searchedWord, @Nullable String tailText) { + PhpLookupElement element = new PhpLookupElement(phpNamedElement) { @Override public Set getAllLookupStrings() { Set original = super.getAllLookupStrings(); - Set strings = new HashSet(original.size() + 1); + Set strings = new HashSet<>(original.size() + 1); strings.addAll(original); - strings.add(searchedWord == null ? this.getNamedElement().getFQN() : searchedWord); + if (searchedWord != null || this.getNamedElement() != null) { + strings.add(searchedWord == null ? this.getNamedElement().getFQN() : searchedWord); + } return strings; } }; - return lookupItem; + if (tailText != null) { + element.tailText = tailText; + } + return element; + } + + protected String getNamespaceName(PsiElement element) { + LattePhpClassUsage classUsage = PsiTreeUtil.getParentOfType(element, LattePhpClassUsage.class); + String namespaceName = ""; + if (classUsage != null) { + String className = classUsage.getClassName().substring(1); + if (className.length() == 1) { + return ""; + } + className = classUsage.getClassName(); + int index = className.lastIndexOf("\\"); + namespaceName = className.substring(0, index); + } + return LattePhpUtil.normalizeClassName(namespaceName); } } \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpClassCompletionProvider.java b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpClassCompletionProvider.java index deb7f67..b540122 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpClassCompletionProvider.java +++ b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpClassCompletionProvider.java @@ -7,7 +7,7 @@ import com.intellij.util.ProcessingContext; import com.jantvrdik.intellij.latte.completion.handlers.PhpClassInsertHandler; import com.jantvrdik.intellij.latte.psi.LattePhpContent; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.completion.PhpLookupElement; import com.jetbrains.php.lang.psi.elements.PhpNamedElement; import org.jetbrains.annotations.NotNull; @@ -34,15 +34,14 @@ protected void addCompletions( return; } - PrefixMatcher prefixMatcher = results.getPrefixMatcher(); - String prefix = prefixMatcher.getPrefix(); - if (prefix.contains("\\")) { - int index = prefix.lastIndexOf("\\"); - prefixMatcher = prefixMatcher.cloneWithPrefix(prefix.substring(index + 1)); + String prefix = results.getPrefixMatcher().getPrefix(); + String namespaceName = getNamespaceName(curr); + if (namespaceName.length() > 0) { + namespaceName = namespaceName + "\\" + prefix; } Project project = params.getPosition().getProject(); - Collection classNames = LattePhpUtil.getAllExistingClassNames(project, prefixMatcher); + Collection classNames = LattePhpUtil.getAllExistingClassNames(project, results.getPrefixMatcher().cloneWithPrefix(namespaceName)); Collection variants = LattePhpUtil.getAllClassNamesAndInterfaces(project, classNames); // Add variants diff --git a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpCompletionProvider.java b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpCompletionProvider.java index 870a10a..6c93245 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpCompletionProvider.java +++ b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpCompletionProvider.java @@ -6,9 +6,10 @@ import com.intellij.psi.PsiElement; import com.intellij.util.ProcessingContext; import com.jantvrdik.intellij.latte.completion.handlers.PhpVariableInsertHandler; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.*; import com.jantvrdik.intellij.latte.psi.elements.BaseLattePhpElement; -import com.jantvrdik.intellij.latte.utils.LattePhpType; +import com.jantvrdik.intellij.latte.utils.LatteTagsUtil; import com.jantvrdik.intellij.latte.utils.LatteTypesUtil; import com.jantvrdik.intellij.latte.utils.LatteUtil; import com.jetbrains.php.completion.PhpLookupElement; @@ -19,6 +20,7 @@ import com.jetbrains.php.lang.psi.elements.PhpClass; import com.jetbrains.php.lang.psi.elements.PhpModifier; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -43,7 +45,8 @@ protected void addCompletions( ProcessingContext context, @NotNull CompletionResultSet result ) { - PsiElement element = parameters.getPosition().getParent(); + PsiElement current = parameters.getPosition(); + PsiElement element = current.getParent(); if ( element instanceof LattePhpStaticVariable || element instanceof LattePhpConstant @@ -56,9 +59,17 @@ protected void addCompletions( } else if (!(element instanceof LatteMacroModifier) && !(element instanceof LattePhpString)) { classCompletionProvider.addCompletions(parameters, context, result); namespaceCompletionProvider.addCompletions(parameters, context, result); + if (isInClassDefinition(element)) { + return; + } - if (LatteUtil.matchParentMacroName(element, "varType") || LatteUtil.matchParentMacroName(element, "var")) { + boolean parentType = LatteUtil.matchParentMacroName(element, LatteTagsUtil.Type.VAR_TYPE.getTagName()); + boolean parentTemplateType = LatteUtil.matchParentMacroName(element, LatteTagsUtil.Type.TEMPLATE_TYPE.getTagName()); + if (parentType || parentTemplateType || LatteUtil.matchParentMacroName(element, LatteTagsUtil.Type.VAR.getTagName())) { attachVarTypes(result); + if (parentType || parentTemplateType || isInTypeDefinition(current)) { + return; + } } variableCompletionProvider.addCompletions(parameters, context, result); @@ -79,10 +90,10 @@ private void attachPhpCompletions( @NotNull BaseLattePhpElement psiElement, boolean isStatic ) { - LattePhpType type = psiElement.getPhpType(); + NettePhpType type = psiElement.getPhpType(); Collection phpClasses = type.getPhpClasses(psiElement.getProject()); - if (phpClasses == null || phpClasses.size() == 0) { + if (phpClasses.size() == 0) { if (psiElement instanceof LattePhpMethod && (psiElement.getPhpStatementPart() == null || psiElement.getPhpStatementPart().getPrevPhpStatementPart() == null)) { functionCompletionProvider.addCompletions(parameters, context, result); } @@ -137,6 +148,16 @@ private void attachPhpCompletions( } } + private boolean isInClassDefinition(@Nullable PsiElement element) { + return element != null && element.getNode().getElementType() == LatteTypes.PHP_CLASS_USAGE; + } + + private boolean isInTypeDefinition(@Nullable PsiElement element) { + return element != null + && (element.getPrevSibling() == null || (LatteTypesUtil.phpTypeTokens.contains(element.getPrevSibling().getNode().getElementType()))) + && element.getNode().getElementType() != LatteTypes.T_MACRO_ARGS_VAR; + } + private boolean canShowCompletionElement(boolean isStatic, @NotNull PhpModifier modifier) { return (isStatic && modifier.isStatic()) || (!isStatic && !modifier.isStatic()); } diff --git a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpFunctionCompletionProvider.java b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpFunctionCompletionProvider.java index e761e6a..ad5131b 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpFunctionCompletionProvider.java +++ b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpFunctionCompletionProvider.java @@ -12,7 +12,7 @@ import com.jantvrdik.intellij.latte.config.LatteConfiguration; import com.jantvrdik.intellij.latte.psi.LattePhpContent; import com.jantvrdik.intellij.latte.settings.LatteFunctionSettings; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.PhpIcons; import com.jetbrains.php.completion.PhpLookupElement; import com.jetbrains.php.completion.insert.PhpFunctionInsertHandler; diff --git a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpNamespaceCompletionProvider.java b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpNamespaceCompletionProvider.java index 39d5370..3995a6e 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpNamespaceCompletionProvider.java +++ b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LattePhpNamespaceCompletionProvider.java @@ -1,19 +1,21 @@ package com.jantvrdik.intellij.latte.completion.providers; import com.intellij.codeInsight.completion.CompletionParameters; -import com.intellij.codeInsight.completion.CompletionProvider; import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; +import com.jantvrdik.intellij.latte.completion.handlers.PhpNamespaceInsertHandler; import com.jantvrdik.intellij.latte.psi.LattePhpContent; -import com.jetbrains.php.PhpIndex; -import com.jetbrains.php.completion.PhpCompletionUtil; -import com.jetbrains.php.completion.insert.PhpNamespaceInsertHandler; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; +import com.jetbrains.php.completion.PhpLookupElement; +import com.jetbrains.php.lang.psi.elements.PhpNamespace; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Collection; -public class LattePhpNamespaceCompletionProvider extends CompletionProvider { +public class LattePhpNamespaceCompletionProvider extends BaseLatteCompletionProvider { public LattePhpNamespaceCompletionProvider() { } @@ -25,14 +27,28 @@ protected void addCompletions(@NotNull CompletionParameters params, ProcessingCo return; } - PhpIndex phpIndex = PhpIndex.getInstance(curr.getProject()); - String prefix = result.getPrefixMatcher().getPrefix(); - String namespace = ""; - if (prefix.contains("\\")) { - int index = prefix.lastIndexOf("\\"); - namespace = prefix.substring(0, index) + "\\"; - prefix = prefix.substring(index + 1); + String namespaceName = getNamespaceName(curr); + Collection namespaceNames = LattePhpUtil.getAllExistingNamespacesByName(curr.getProject(), namespaceName); + Collection namespaces = LattePhpUtil.getAlNamespaces(curr.getProject(), namespaceNames); + for (PhpNamespace namespace : namespaces) { + PhpLookupElement lookupItem = getPhpLookupElement(namespace, null, getTypeText(namespace.getParentNamespaceName())); + lookupItem.handler = PhpNamespaceInsertHandler.getInstance(); + result.addElement(lookupItem); } - PhpCompletionUtil.addSubNamespaces(namespace, result.withPrefixMatcher(prefix), phpIndex, PhpNamespaceInsertHandler.getInstance()); } + + @Nullable + private String getTypeText(String parentNamespace) { + if (parentNamespace.length() > 1) { + if (parentNamespace.startsWith("\\")) { + parentNamespace = parentNamespace.substring(1); + } + if (parentNamespace.endsWith("\\") && parentNamespace.length() > 1) { + parentNamespace = parentNamespace.substring(0, parentNamespace.length() - 1); + } + return " [" + parentNamespace + "]"; + } + return null; + } + } diff --git a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LatteVariableCompletionProvider.java b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LatteVariableCompletionProvider.java index d5c7520..7a8331c 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LatteVariableCompletionProvider.java +++ b/src/main/java/com/jantvrdik/intellij/latte/completion/providers/LatteVariableCompletionProvider.java @@ -5,17 +5,17 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.util.ProcessingContext; import com.jantvrdik.intellij.latte.completion.handlers.PhpVariableInsertHandler; import com.jantvrdik.intellij.latte.config.LatteConfiguration; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.settings.LatteVariableSettings; import com.jantvrdik.intellij.latte.psi.LatteFile; import com.jantvrdik.intellij.latte.psi.LattePhpVariable; -import com.jantvrdik.intellij.latte.utils.LattePhpType; import com.jantvrdik.intellij.latte.utils.LatteUtil; -import com.jantvrdik.intellij.latte.utils.PsiPositionedElement; +import com.jantvrdik.intellij.latte.utils.PsiCachedElement; import com.jetbrains.php.PhpIcons; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.PhpClass; @@ -32,7 +32,7 @@ public LatteVariableCompletionProvider() { @Override protected void addCompletions( @NotNull CompletionParameters parameters, - ProcessingContext context, + @NotNull ProcessingContext context, @NotNull CompletionResultSet result ) { PsiElement element = parameters.getPosition().getParent(); @@ -40,7 +40,7 @@ protected void addCompletions( return; } - List elements = attachPhpVariableCompletions(element, parameters.getOriginalFile().getVirtualFile()); + List elements = attachPhpVariableCompletions(element); result.addAllElements(elements); if (parameters.getOriginalFile() instanceof LatteFile) { @@ -49,47 +49,41 @@ protected void addCompletions( } private void attachTemplateTypeCompletions(@NotNull CompletionResultSet result, @NotNull Project project, @NotNull LatteFile file) { - LattePhpType type = LatteUtil.findFirstLatteTemplateType(file); + NettePhpType type = LatteUtil.findFirstLatteTemplateType(file); if (type == null) { return; } Collection phpClasses = type.getPhpClasses(project); - if (phpClasses != null) { - for (PhpClass phpClass : phpClasses) { - for (Field field : phpClass.getFields()) { - if (!field.isConstant() && field.getModifier().isPublic()) { - LookupElementBuilder builder = LookupElementBuilder.create(field, "$" + field.getName()); - builder = builder.withInsertHandler(PhpVariableInsertHandler.getInstance()); - builder = builder.withTypeText(LattePhpType.create(field.getType()).toString()); - builder = builder.withIcon(PhpIcons.VARIABLE); - if (field.isDeprecated() || field.isInternal()) { - builder = builder.withStrikeoutness(true); - } - result.addElement(builder); + for (PhpClass phpClass : phpClasses) { + for (Field field : phpClass.getFields()) { + if (!field.isConstant() && field.getModifier().isPublic()) { + LookupElementBuilder builder = LookupElementBuilder.create(field, "$" + field.getName()); + builder = builder.withInsertHandler(PhpVariableInsertHandler.getInstance()); + builder = builder.withTypeText(NettePhpType.create(field.getType()).toString()); + builder = builder.withIcon(PhpIcons.VARIABLE); + if (field.isDeprecated() || field.isInternal()) { + builder = builder.withStrikeoutness(true); } + result.addElement(builder); } } } } - private List attachPhpVariableCompletions(@NotNull PsiElement psiElement, @NotNull VirtualFile virtualFile) { + private List attachPhpVariableCompletions(@NotNull PsiElement psiElement) { List lookupElements = new ArrayList<>(); List foundVariables = new ArrayList<>(); - for (PsiPositionedElement element : LatteUtil.findVariablesDefinitionsInFileBeforeElement(psiElement, virtualFile)) { - if (!(element.getElement() instanceof LattePhpVariable)) { - continue; - } - - String variableName = ((LattePhpVariable) element.getElement()).getVariableName(); + for (PsiCachedElement element : LattePhpVariableUtil.getVariablesDefinitionsBeforeElement(psiElement)) { + String variableName = element.getElement().getVariableName(); if (foundVariables.stream().anyMatch(variableName::equals)) { continue; } LookupElementBuilder builder = LookupElementBuilder.create(element.getElement(), "$" + variableName); builder = builder.withInsertHandler(PhpVariableInsertHandler.getInstance()); - builder = builder.withTypeText(((LattePhpVariable) element.getElement()).getPhpType().toString()); + builder = builder.withTypeText(element.getElement().getPhpType().toString()); builder = builder.withIcon(PhpIcons.VARIABLE); builder = builder.withBoldness(true); lookupElements.add(builder); @@ -98,7 +92,6 @@ private List attachPhpVariableCompletions(@NotNull PsiElement psi } Collection defaultVariables = LatteConfiguration.getInstance(psiElement.getProject()).getVariables(); - for (LatteVariableSettings variable : defaultVariables) { String variableName = variable.getVarName(); if (foundVariables.stream().anyMatch(variableName::equals)) { diff --git a/src/main/java/com/jantvrdik/intellij/latte/config/LatteConfiguration.java b/src/main/java/com/jantvrdik/intellij/latte/config/LatteConfiguration.java index f4ab043..5fee118 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/config/LatteConfiguration.java +++ b/src/main/java/com/jantvrdik/intellij/latte/config/LatteConfiguration.java @@ -2,9 +2,9 @@ import com.intellij.openapi.project.Project; import com.intellij.ui.JBColor; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; import com.jantvrdik.intellij.latte.settings.*; import com.jantvrdik.intellij.latte.settings.xml.LatteXmlFileData; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,9 +23,9 @@ public enum Vendor { LATTE("Latte", JBColor.ORANGE), CUSTOM("Custom", JBColor.GRAY); - private String name; + private final String name; - private JBColor color; + private final JBColor color; Vendor(String name, JBColor color) { this.name = name; @@ -41,9 +41,7 @@ public JBColor getColor() { } } - private boolean checkDumbMode; - - private static Map instances = new HashMap<>(); + private static final Map instances = new HashMap<>(); @NotNull private final Project project; @@ -107,7 +105,7 @@ public LatteFunctionSettings getFunction(String name) { */ @Nullable public LatteVariableSettings getVariable(String name) { - name = LattePhpUtil.normalizePhpVariable(name); + name = LattePhpVariableUtil.normalizePhpVariable(name); for (LatteVariableSettings variable : getVariables()) { if (variable.getVarName().equals(name)) { return variable; diff --git a/src/main/java/com/jantvrdik/intellij/latte/indexes/LatteIndexUtil.java b/src/main/java/com/jantvrdik/intellij/latte/indexes/LatteIndexUtil.java index d1cf76a..0738653 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/indexes/LatteIndexUtil.java +++ b/src/main/java/com/jantvrdik/intellij/latte/indexes/LatteIndexUtil.java @@ -3,8 +3,9 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.search.GlobalSearchScope; import com.jantvrdik.intellij.latte.indexes.extensions.*; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; import com.jantvrdik.intellij.latte.psi.*; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -28,7 +29,7 @@ public static Collection findPropertiesByName(@NotNull Project public static Collection findStaticVariablesByName(@NotNull Project project, String name) { return LattePhpStaticVariableIndex.getInstance().get( - LattePhpUtil.normalizePhpVariable(name), + LattePhpVariableUtil.normalizePhpVariable(name), project, GlobalSearchScope.allScope(project) ); @@ -44,7 +45,7 @@ public static Collection findNamespacesByFqn(@NotNul public static Collection findVariablesByName(@NotNull Project project, String name) { return LattePhpVariableIndex.getInstance().get( - LattePhpUtil.normalizePhpVariable(name), + LattePhpVariableUtil.normalizePhpVariable(name), project, GlobalSearchScope.allScope(project) ); diff --git a/src/main/java/com/jantvrdik/intellij/latte/indexes/extensions/LattePhpVariableIndex.java b/src/main/java/com/jantvrdik/intellij/latte/indexes/extensions/LattePhpVariableIndex.java index 67e4dc9..63bbac6 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/indexes/extensions/LattePhpVariableIndex.java +++ b/src/main/java/com/jantvrdik/intellij/latte/indexes/extensions/LattePhpVariableIndex.java @@ -21,7 +21,7 @@ public static LattePhpVariableIndex getInstance() { @Override public int getVersion() { - return super.getVersion() + 1; + return super.getVersion() + 2; } @Override diff --git a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/LattePhpTypeStub.java b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/LattePhpTypeStub.java index 04cc24d..6ba441e 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/LattePhpTypeStub.java +++ b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/LattePhpTypeStub.java @@ -3,7 +3,7 @@ import com.intellij.lang.Language; import com.intellij.psi.PsiElement; import com.intellij.psi.stubs.*; -import com.jantvrdik.intellij.latte.utils.LattePhpType; +import com.jantvrdik.intellij.latte.php.NettePhpType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -17,7 +17,7 @@ public LattePhpTypeStub(@NotNull String debugName, @Nullable Language language) super(debugName, language); } - protected static void writePhpType(StubOutputStream dataStream, @NotNull LattePhpType type) throws IOException { + protected static void writePhpType(StubOutputStream dataStream, @NotNull NettePhpType type) throws IOException { Collection types = type.getTypes(); dataStream.writeVarInt(types.size()); for (String s : types) { @@ -26,7 +26,7 @@ protected static void writePhpType(StubOutputStream dataStream, @NotNull LattePh } @NotNull - protected static LattePhpType readPhpType(StubInputStream dataStream) throws IOException { + protected static NettePhpType readPhpType(StubInputStream dataStream) throws IOException { int i = dataStream.readVarInt(); List types = new ArrayList<>(); @@ -36,6 +36,6 @@ protected static LattePhpType readPhpType(StubInputStream dataStream) throws IOE types.add(s); } } - return LattePhpType.create(String.join("|", types)); + return NettePhpType.create(String.join("|", types)); } } diff --git a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpClassStubType.java b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpClassStubType.java index fa8306c..3355b46 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpClassStubType.java +++ b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpClassStubType.java @@ -17,7 +17,7 @@ import com.jantvrdik.intellij.latte.parser.LatteElementTypes; import com.jantvrdik.intellij.latte.psi.LattePhpClassReference; import com.jantvrdik.intellij.latte.psi.impl.LattePhpClassReferenceImpl; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import org.jetbrains.annotations.NotNull; import java.io.IOException; diff --git a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpMethodStubType.java b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpMethodStubType.java index 74f9898..b79cbd2 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpMethodStubType.java +++ b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpMethodStubType.java @@ -5,7 +5,6 @@ import com.intellij.lang.LighterASTTokenNode; import com.intellij.psi.impl.source.tree.LightTreeUtil; import com.intellij.psi.stubs.*; -import com.intellij.psi.tree.TokenSet; import com.intellij.util.CharTable; import com.jantvrdik.intellij.latte.indexes.extensions.LattePhpMethodIndex; import com.jantvrdik.intellij.latte.indexes.stubs.LattePhpMethodStub; @@ -85,6 +84,9 @@ public LattePhpMethodStub createStub(@NotNull LighterAST tree, @NotNull LighterA @Override public LattePhpMethodStub createStub(@NotNull LighterAST tree, @NotNull LighterASTNode node, @NotNull StubElement parentStub) { LighterASTNode keyNode = LightTreeUtil.firstChildOfType(tree, node, LatteTypesUtil.methodTokens); + if (keyNode != null && keyNode.getTokenType() == LatteTypes.PHP_VARIABLE) { + keyNode = LightTreeUtil.firstChildOfType(tree, keyNode, LatteTypes.T_MACRO_ARGS_VAR); + } String key = intern(tree.getCharTable(), keyNode); return new LattePhpMethodStubImpl(parentStub, key); } diff --git a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpNamespaceStubType.java b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpNamespaceStubType.java index a714ae1..46aa589 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpNamespaceStubType.java +++ b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpNamespaceStubType.java @@ -18,7 +18,7 @@ import com.jantvrdik.intellij.latte.psi.LattePhpNamespaceReference; import com.jantvrdik.intellij.latte.psi.LatteTypes; import com.jantvrdik.intellij.latte.psi.impl.LattePhpNamespaceReferenceImpl; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import org.jetbrains.annotations.NotNull; import java.io.IOException; diff --git a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpStaticVariableStubType.java b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpStaticVariableStubType.java index 9234b3d..298e75e 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpStaticVariableStubType.java +++ b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpStaticVariableStubType.java @@ -15,10 +15,10 @@ import com.jantvrdik.intellij.latte.indexes.stubs.LattePhpTypeStub; import com.jantvrdik.intellij.latte.indexes.stubs.impl.LattePhpStaticVariableStubImpl; import com.jantvrdik.intellij.latte.parser.LatteElementTypes; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; import com.jantvrdik.intellij.latte.psi.LattePhpStaticVariable; import com.jantvrdik.intellij.latte.psi.LatteTypes; import com.jantvrdik.intellij.latte.psi.impl.LattePhpStaticVariableImpl; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -67,7 +67,7 @@ public void indexStub(@NotNull final LattePhpStaticVariableStub stub, @NotNull f public LattePhpStaticVariableStub createStub(@NotNull LighterAST tree, @NotNull LighterASTNode node, @NotNull StubElement parentStub) { LighterASTNode keyNode = LightTreeUtil.firstChildOfType(tree, node, TokenSet.create(LatteTypes.T_MACRO_ARGS_VAR)); String key = intern(tree.getCharTable(), keyNode); - return new LattePhpStaticVariableStubImpl(parentStub, LattePhpUtil.normalizePhpVariable(key)); + return new LattePhpStaticVariableStubImpl(parentStub, LattePhpVariableUtil.normalizePhpVariable(key)); } public static String intern(@NotNull CharTable table, LighterASTNode node) { diff --git a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpVariableStubType.java b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpVariableStubType.java index 0cecf77..55ea5d6 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpVariableStubType.java +++ b/src/main/java/com/jantvrdik/intellij/latte/indexes/stubs/types/LattePhpVariableStubType.java @@ -1,5 +1,6 @@ package com.jantvrdik.intellij.latte.indexes.stubs.types; +import com.intellij.lang.ASTNode; import com.intellij.lang.LighterAST; import com.intellij.lang.LighterASTNode; import com.intellij.lang.LighterASTTokenNode; @@ -15,10 +16,10 @@ import com.jantvrdik.intellij.latte.indexes.stubs.LattePhpTypeStub; import com.jantvrdik.intellij.latte.indexes.stubs.impl.LattePhpVariableStubImpl; import com.jantvrdik.intellij.latte.parser.LatteElementTypes; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; import com.jantvrdik.intellij.latte.psi.LattePhpVariable; import com.jantvrdik.intellij.latte.psi.LatteTypes; import com.jantvrdik.intellij.latte.psi.impl.LattePhpVariableImpl; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -50,6 +51,11 @@ public void serialize(@NotNull final LattePhpVariableStub stub, @NotNull final S dataStream.writeName(stub.getVariableName()); } + @Override + public boolean shouldCreateStub(@NotNull LighterAST tree, @NotNull LighterASTNode node, @NotNull StubElement parentStub) { + return LightTreeUtil.firstChildOfType(tree, node, TokenSet.create(LatteTypes.T_MACRO_ARGS_VAR)) != null; + } + @Override @NotNull public LattePhpVariableStub deserialize(@NotNull final StubInputStream dataStream, final StubElement parentStub) throws IOException { @@ -67,7 +73,7 @@ public void indexStub(@NotNull final LattePhpVariableStub stub, @NotNull final I public LattePhpVariableStub createStub(@NotNull LighterAST tree, @NotNull LighterASTNode node, @NotNull StubElement parentStub) { LighterASTNode keyNode = LightTreeUtil.firstChildOfType(tree, node, TokenSet.create(LatteTypes.T_MACRO_ARGS_VAR)); String key = intern(tree.getCharTable(), keyNode); - return new LattePhpVariableStubImpl(parentStub, LattePhpUtil.normalizePhpVariable(key)); + return new LattePhpVariableStubImpl(parentStub, LattePhpVariableUtil.normalizePhpVariable(key)); } public static String intern(@NotNull CharTable table, LighterASTNode node) { diff --git a/src/main/java/com/jantvrdik/intellij/latte/inspections/BaseLocalInspectionTool.java b/src/main/java/com/jantvrdik/intellij/latte/inspections/BaseLocalInspectionTool.java index 1d42413..ba2f356 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/inspections/BaseLocalInspectionTool.java +++ b/src/main/java/com/jantvrdik/intellij/latte/inspections/BaseLocalInspectionTool.java @@ -2,12 +2,25 @@ import com.intellij.codeInspection.*; import com.intellij.psi.PsiElement; +import com.jantvrdik.intellij.latte.inspections.utils.LatteInspectionInfo; import org.jetbrains.annotations.NotNull; import java.util.List; abstract class BaseLocalInspectionTool extends LocalInspectionTool { + protected void addInspections( + @NotNull final InspectionManager manager, + @NotNull final List problems, + @NotNull final List inspectionInfo, + final boolean isOnTheFly + ) { + for (LatteInspectionInfo info : inspectionInfo) { + ProblemDescriptor problem = manager.createProblemDescriptor(info.getElement(), info.getDescription(), true, info.getType(), isOnTheFly, info.getFixes()); + problems.add(problem); + } + } + protected void addError( @NotNull final InspectionManager manager, List problems, diff --git a/src/main/java/com/jantvrdik/intellij/latte/inspections/ClassUsagesInspection.java b/src/main/java/com/jantvrdik/intellij/latte/inspections/ClassUsagesInspection.java index 19ed31c..f422bdf 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/inspections/ClassUsagesInspection.java +++ b/src/main/java/com/jantvrdik/intellij/latte/inspections/ClassUsagesInspection.java @@ -6,7 +6,7 @@ import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.jantvrdik.intellij.latte.psi.LatteFile; import com.jantvrdik.intellij.latte.psi.LattePhpClassReference; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.lang.psi.elements.PhpClass; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/jantvrdik/intellij/latte/inspections/ConstantUsagesInspection.java b/src/main/java/com/jantvrdik/intellij/latte/inspections/ConstantUsagesInspection.java index bf24bc8..e75c4a6 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/inspections/ConstantUsagesInspection.java +++ b/src/main/java/com/jantvrdik/intellij/latte/inspections/ConstantUsagesInspection.java @@ -5,9 +5,9 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LatteFile; import com.jantvrdik.intellij.latte.psi.LattePhpConstant; -import com.jantvrdik.intellij.latte.utils.LattePhpType; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.PhpClass; import com.jetbrains.php.lang.psi.elements.PhpModifier; @@ -38,10 +38,10 @@ public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final Inspe @Override public void visitElement(PsiElement element) { if (element instanceof LattePhpConstant) { - LattePhpType phpType = ((LattePhpConstant) element).getPhpType(); + NettePhpType phpType = ((LattePhpConstant) element).getPhpType(); Collection phpClasses = phpType.getPhpClasses(element.getProject()); - if (phpClasses == null) { + if (phpClasses.size() == 0) { return; } diff --git a/src/main/java/com/jantvrdik/intellij/latte/inspections/LatteIterableTypeInspection.java b/src/main/java/com/jantvrdik/intellij/latte/inspections/LatteIterableTypeInspection.java index 8a8e454..bbb4be2 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/inspections/LatteIterableTypeInspection.java +++ b/src/main/java/com/jantvrdik/intellij/latte/inspections/LatteIterableTypeInspection.java @@ -5,12 +5,12 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LatteFile; import com.jantvrdik.intellij.latte.psi.LattePhpArrayUsage; import com.jantvrdik.intellij.latte.psi.LattePhpForeach; import com.jantvrdik.intellij.latte.psi.elements.BaseLattePhpElement; -import com.jantvrdik.intellij.latte.utils.LattePhpType; -import com.jantvrdik.intellij.latte.utils.LattePhpVariableUtil; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,7 +46,7 @@ public void visitElement(PsiElement element) { } } else if (element instanceof LattePhpForeach) { - LattePhpType type = ((LattePhpForeach) element).getPhpExpression().getPhpType(); + NettePhpType type = ((LattePhpForeach) element).getPhpExpression().getPhpType(); if (!type.isMixed() && !type.isIterable(element.getProject())) { addProblem( manager, diff --git a/src/main/java/com/jantvrdik/intellij/latte/inspections/MethodUsagesInspection.java b/src/main/java/com/jantvrdik/intellij/latte/inspections/MethodUsagesInspection.java index 31f2ce7..36870a2 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/inspections/MethodUsagesInspection.java +++ b/src/main/java/com/jantvrdik/intellij/latte/inspections/MethodUsagesInspection.java @@ -4,17 +4,16 @@ import com.intellij.codeInspection.InspectionManager; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.jantvrdik.intellij.latte.config.LatteConfiguration; import com.jantvrdik.intellij.latte.intentions.AddCustomLatteFunction; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LatteFile; import com.jantvrdik.intellij.latte.psi.LattePhpMethod; import com.jantvrdik.intellij.latte.settings.LatteFunctionSettings; -import com.jantvrdik.intellij.latte.utils.LattePhpType; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.lang.psi.elements.Function; import com.jetbrains.php.lang.psi.elements.Method; import com.jetbrains.php.lang.psi.elements.PhpClass; @@ -100,37 +99,35 @@ private void processMethod( @NotNull final InspectionManager manager, final boolean isOnTheFly ) { - LattePhpType phpType = element.getPhpType(); + NettePhpType phpType = element.getPhpType(); boolean isFound = false; Collection phpClasses = phpType.getPhpClasses(element.getProject()); String methodName = element.getMethodName(); - if (phpClasses != null) { - for (PhpClass phpClass : phpClasses) { - for (Method method : phpClass.getMethods()) { - if (method.getName().equals(methodName)) { - if (method.getModifier().isPrivate()) { - addProblem(manager, problems, getElementToLook(element), "Used private method '" + methodName + "'", isOnTheFly); - } else if (method.getModifier().isProtected()) { - addProblem(manager, problems, getElementToLook(element), "Used protected method '" + methodName + "'", isOnTheFly); - } else if (method.isDeprecated()) { - addDeprecated(manager, problems, getElementToLook(element), "Used method '" + methodName + "' is marked as deprecated", isOnTheFly); - } else if (method.isInternal()) { - addDeprecated(manager, problems, getElementToLook(element), "Used method '" + methodName + "' is marked as internal", isOnTheFly); - } - - String description; - boolean isStatic = element.isStatic(); - if (isStatic && !method.getModifier().isStatic()) { - description = "Method '" + methodName + "' is not static but called statically"; - addProblem(manager, problems, getElementToLook(element), description, isOnTheFly); - - } else if (!isStatic && method.getModifier().isStatic()) { - description = "Method '" + methodName + "' is static but called non statically"; - addProblem(manager, problems, getElementToLook(element), description, isOnTheFly); - } - isFound = true; + for (PhpClass phpClass : phpClasses) { + for (Method method : phpClass.getMethods()) { + if (method.getName().equals(methodName)) { + if (method.getModifier().isPrivate()) { + addProblem(manager, problems, getElementToLook(element), "Used private method '" + methodName + "'", isOnTheFly); + } else if (method.getModifier().isProtected()) { + addProblem(manager, problems, getElementToLook(element), "Used protected method '" + methodName + "'", isOnTheFly); + } else if (method.isDeprecated()) { + addDeprecated(manager, problems, getElementToLook(element), "Used method '" + methodName + "' is marked as deprecated", isOnTheFly); + } else if (method.isInternal()) { + addDeprecated(manager, problems, getElementToLook(element), "Used method '" + methodName + "' is marked as internal", isOnTheFly); } + + String description; + boolean isStatic = element.isStatic(); + if (isStatic && !method.getModifier().isStatic()) { + description = "Method '" + methodName + "' is not static but called statically"; + addProblem(manager, problems, getElementToLook(element), description, isOnTheFly); + + } else if (!isStatic && method.getModifier().isStatic()) { + description = "Method '" + methodName + "' is static but called non statically"; + addProblem(manager, problems, getElementToLook(element), description, isOnTheFly); + } + isFound = true; } } } diff --git a/src/main/java/com/jantvrdik/intellij/latte/inspections/PropertyUsagesInspection.java b/src/main/java/com/jantvrdik/intellij/latte/inspections/PropertyUsagesInspection.java index 973effd..84de143 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/inspections/PropertyUsagesInspection.java +++ b/src/main/java/com/jantvrdik/intellij/latte/inspections/PropertyUsagesInspection.java @@ -5,10 +5,10 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LatteFile; import com.jantvrdik.intellij.latte.psi.LattePhpProperty; -import com.jantvrdik.intellij.latte.utils.LattePhpType; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.PhpClass; import com.jetbrains.php.lang.psi.elements.PhpModifier; @@ -37,12 +37,12 @@ public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final Inspe final List problems = new ArrayList<>(); file.acceptChildren(new PsiRecursiveElementWalkingVisitor() { @Override - public void visitElement(PsiElement element) { + public void visitElement(@NotNull PsiElement element) { if (element instanceof LattePhpProperty) { - LattePhpType phpType = ((LattePhpProperty) element).getPhpType(); + NettePhpType phpType = ((LattePhpProperty) element).getPhpType(); Collection phpClasses = phpType.getPhpClasses(element.getProject()); - if (phpClasses == null) { + if (phpClasses.size() == 0) { return; } @@ -50,7 +50,7 @@ public void visitElement(PsiElement element) { String variableName = ((LattePhpProperty) element).getPropertyName(); for (PhpClass phpClass : phpClasses) { for (Field field : phpClass.getFields()) { - if (!field.isConstant() && field.getName().equals(LattePhpUtil.normalizePhpVariable(variableName))) { + if (!field.isConstant() && field.getName().equals(LattePhpVariableUtil.normalizePhpVariable(variableName))) { PhpModifier modifier = field.getModifier(); if (modifier.isPrivate()) { addProblem(manager, problems, element, "Used private property '" + variableName + "'", isOnTheFly); diff --git a/src/main/java/com/jantvrdik/intellij/latte/inspections/StaticPropertyUsagesInspection.java b/src/main/java/com/jantvrdik/intellij/latte/inspections/StaticPropertyUsagesInspection.java index 67993e9..28bc7dd 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/inspections/StaticPropertyUsagesInspection.java +++ b/src/main/java/com/jantvrdik/intellij/latte/inspections/StaticPropertyUsagesInspection.java @@ -5,9 +5,9 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LatteFile; import com.jantvrdik.intellij.latte.psi.LattePhpStaticVariable; -import com.jantvrdik.intellij.latte.utils.LattePhpType; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.PhpClass; import com.jetbrains.php.lang.psi.elements.PhpModifier; @@ -38,10 +38,10 @@ public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final Inspe @Override public void visitElement(PsiElement element) { if (element instanceof LattePhpStaticVariable) { - LattePhpType phpType = ((LattePhpStaticVariable) element).getPhpType(); + NettePhpType phpType = ((LattePhpStaticVariable) element).getPhpType(); Collection phpClasses = phpType.getPhpClasses(element.getProject()); - if (phpClasses == null) { + if (phpClasses.size() == 0) { return; } diff --git a/src/main/java/com/jantvrdik/intellij/latte/inspections/VariablesInspection.java b/src/main/java/com/jantvrdik/intellij/latte/inspections/VariablesInspection.java index 6151687..b8147de 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/inspections/VariablesInspection.java +++ b/src/main/java/com/jantvrdik/intellij/latte/inspections/VariablesInspection.java @@ -2,25 +2,26 @@ import com.intellij.codeInsight.intention.IntentionManager; import com.intellij.codeInspection.*; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.jantvrdik.intellij.latte.config.LatteConfiguration; -import com.jantvrdik.intellij.latte.settings.LatteVariableSettings; +import com.jantvrdik.intellij.latte.inspections.utils.LatteInspectionInfo; import com.jantvrdik.intellij.latte.intentions.AddCustomNotNullVariable; import com.jantvrdik.intellij.latte.intentions.AddCustomNullableVariable; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; import com.jantvrdik.intellij.latte.psi.LatteFile; import com.jantvrdik.intellij.latte.psi.LattePhpVariable; -import com.jantvrdik.intellij.latte.utils.LattePhpVariableUtil; -import com.jantvrdik.intellij.latte.utils.LatteUtil; -import com.jantvrdik.intellij.latte.utils.PsiPositionedElement; +import com.jantvrdik.intellij.latte.settings.LatteVariableSettings; +import com.jantvrdik.intellij.latte.utils.LattePhpVariableDefinition; +import com.jantvrdik.intellij.latte.utils.PsiCachedElement; import com.jetbrains.php.lang.psi.elements.Field; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; public class VariablesInspection extends BaseLocalInspectionTool { @@ -31,122 +32,151 @@ public String getShortName() { return "LatteVariablesProblems"; } + @Nullable @Override public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final InspectionManager manager, final boolean isOnTheFly) { if (!(file instanceof LatteFile)) { return null; } - final List problems = new ArrayList<>(); + addInspections(manager, problems, checkFile(file), isOnTheFly); + return problems.toArray(new ProblemDescriptor[0]); + } + + + + @NotNull + List checkFile(@NotNull final PsiFile file) { + final List problems = new ArrayList<>(); + Map all = LattePhpVariableUtil.getAllVariablesInFile(file); file.acceptChildren(new PsiRecursiveElementWalkingVisitor() { @Override - public void visitElement(PsiElement element) { - if (element instanceof LattePhpVariable) { - String variableName = ((LattePhpVariable) element).getVariableName(); - VirtualFile file = element.getContainingFile().getVirtualFile(); - List all = LatteUtil.findVariablesInFile(element.getProject(), file, variableName); - List afterElement = LatteUtil.findVariablesInFileAfterElement(element, file, variableName); - List definitions = all.stream() - .filter(variableElement -> variableElement.getElement() instanceof LattePhpVariable && ((LattePhpVariable) variableElement.getElement()).isDefinition()) - .collect(Collectors.toList()); - - int offset = LatteUtil.getStartOffsetInFile(element); - List beforeElement = definitions.stream() - .filter(variableElement -> variableElement.getPosition() <= offset) - .collect(Collectors.toList()); - int varDefinitions = (int) definitions.stream() - .filter(variableElement -> variableElement.getElement() instanceof LattePhpVariable && !((LattePhpVariable) variableElement.getElement()).isVarTypeDefinition()) - .count(); - int cyclesDefinitions = (int) definitions.stream() - .filter( - variableElement -> variableElement.getElement() instanceof LattePhpVariable - && !((LattePhpVariable) variableElement.getElement()).isVarTypeDefinition() - && ( - ((LattePhpVariable) variableElement.getElement()).isDefinitionInFor() - || ((LattePhpVariable) variableElement.getElement()).isDefinitionInForeach() - ) - ).count(); - int normalVarDefinitions = varDefinitions - cyclesDefinitions; - - ProblemHighlightType type = null; - String description = null; - boolean isUndefined = false; - if (((LattePhpVariable) element).isDefinition()) { - List usages = afterElement.stream() - .filter( - variableElement -> variableElement.getElement() instanceof LattePhpVariable - && !((LattePhpVariable) variableElement.getElement()).isDefinition() - ) + public void visitElement(@NotNull PsiElement psiElement) { + if (psiElement instanceof LattePhpVariable) { + PsiCachedElement element = all.get(psiElement); + if (element == null) { + super.visitElement(psiElement); + return; + } + + if (element.isDefinition()) { + List sameName = all.values().stream() + .filter((current) -> current.getVariableName() != null && current.getVariableName().equals(element.getVariableName())) .collect(Collectors.toList()); + checkVariableDefinition(sameName, element, problems); + } else { + checkVariableUsages(element, problems); + } - if (varDefinitions > 0 && !((LattePhpVariable) element).isVarTypeDefinition()) { - LatteVariableSettings defaultVariable = LatteConfiguration.getInstance(element.getProject()).getVariable(variableName); - if (defaultVariable != null) { - ProblemDescriptor descriptor = manager.createProblemDescriptor( - element, - "Rewrite default variable '" + variableName + "' defined as template parameters", - true, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - isOnTheFly - ); - problems.add(descriptor); - } - } + } else { + super.visitElement(psiElement); + } + } + }); + return problems; + } + + private void checkVariableDefinition( + @NotNull final List sameName, + @NotNull final PsiCachedElement element, + @NotNull final List problems + ) { + List definitions = sameName.stream() + .filter(current -> current.isDefinition() && !current.isVarTypeDefinition()) + .collect(Collectors.toList()); + List usages = sameName.stream() + .filter((current) -> !current.isDefinition() && current.getPosition() >= element.getPosition()) + .collect(Collectors.toList()); + + LattePhpVariable variable = element.getElement(); + String variableName = element.getVariableName(); + if (!element.isVarTypeDefinition()) { + LatteVariableSettings defaultVariable = LatteConfiguration.getInstance(element.getProject()).getVariable(variableName); + if (defaultVariable != null) { + problems.add( + LatteInspectionInfo.error(variable, "Rewrite default variable '" + variableName + "' defined as template parameters") + ); + } + + for (PsiCachedElement varDefinition : definitions) { + if (!varDefinition.matchElement(element) && varDefinition.getVariableContext() == element.getVariableContext()) { + problems.add( + LatteInspectionInfo.warning(variable, "Multiple definitions for variable '" + variableName + "'") + ); + break; + } + } + } - if (normalVarDefinitions > 1) { - type = ProblemHighlightType.GENERIC_ERROR_OR_WARNING; - description = "Multiple definitions for variable '" + variableName + "'"; + boolean isUsed = false; + if (usages.size() > 0) { + for (PsiCachedElement usage : usages) { + if (usage.getVariableDefinitions().contains(variable)) { + isUsed = true; + break; + } + } + } - } else if (normalVarDefinitions == 1 && cyclesDefinitions > 0) { - type = ProblemHighlightType.GENERIC_ERROR_OR_WARNING; - description = "Multiple definitions for variable '" + variableName + "'. Defined in for/foreach and normally."; + if (!isUsed) { + problems.add(LatteInspectionInfo.unused(variable, "Unused variable '" + variableName + "'")); + } + } - } else if (usages.size() == 0) { - type = ProblemHighlightType.LIKE_UNUSED_SYMBOL; - description = "Unused variable '" + variableName + "'"; - } + private void checkVariableUsages( + @NotNull final PsiCachedElement element, + @NotNull final List problems + ) { + LattePhpVariable variable = element.getElement(); + List variableDefinitions = LattePhpVariableUtil.getVariableDefinition(variable); + + boolean isDefined = false; + boolean isProbablyUndefined = false; + for (LattePhpVariableDefinition variableDefinition : variableDefinitions) { + if (!variableDefinition.isProbablyUndefined()) { + isDefined = true; + } else { + isProbablyUndefined = true; + } + } - } else if (beforeElement.size() == 0) { - LatteVariableSettings defaultVariable = LatteConfiguration.getInstance(element.getProject()).getVariable(variableName); - if (defaultVariable == null) { - List fields = LattePhpVariableUtil.findPhpFiledListFromTemplateTypeTag(element, variableName); - if (fields.size() == 0) { - type = ProblemHighlightType.GENERIC_ERROR_OR_WARNING; - description = "Undefined variable '" + variableName + "'"; - isUndefined = true; - - } else { - for (Field field : fields) { - if (field.isDeprecated()) { - addDeprecated(manager, problems, element, "Variable '" + variableName + "' is deprecated", isOnTheFly); - } - if (field.isInternal()) { - addDeprecated(manager, problems, element, "Variable '" + variableName + "' is internal", isOnTheFly); - } - } - } + String variableName = element.getVariableName(); + if (!isDefined) { + LatteVariableSettings defaultVariable = LatteConfiguration.getInstance(element.getProject()).getVariable(variableName); + if (defaultVariable != null) { + isDefined = true; + isProbablyUndefined = false; + + } else { + List fields = LattePhpVariableUtil.findPhpFiledListFromTemplateTypeTag(variable, variableName); + if (fields.size() > 0) { + isDefined = true; + isProbablyUndefined = false; + for (Field field : fields) { + if (field.isDeprecated()) { + problems.add(LatteInspectionInfo.deprecated(variable, "Variable '" + variableName + "' is deprecated")); } - } - - if (type != null) { - ProblemDescriptor problem; - if (isUndefined) { - LocalQuickFix notNullFix = IntentionManager.getInstance().convertToFix(new AddCustomNullableVariable(variableName)); - LocalQuickFix notNotNullFix = IntentionManager.getInstance().convertToFix(new AddCustomNotNullVariable(variableName)); - problem = manager.createProblemDescriptor(element, description, true, type, isOnTheFly, notNotNullFix, notNullFix); - } else { - problem = manager.createProblemDescriptor(element, description, true, type, isOnTheFly); + if (field.isInternal()) { + problems.add(LatteInspectionInfo.deprecated(variable, "Variable '" + variableName + "' is internal")); } - problems.add(problem); } - - } else { - super.visitElement(element); } } - }); + } - return problems.toArray(new ProblemDescriptor[0]); + if (!isDefined && isProbablyUndefined) { + problems.add(LatteInspectionInfo.weakWarning(variable, "Variable '" + variableName + "' is probably undefined")); + + } else if (!isDefined) { + LatteInspectionInfo info = LatteInspectionInfo.error(variable, "Undefined variable '" + variableName + "'"); + IntentionManager intentionManager = IntentionManager.getInstance(); + if (intentionManager != null) { + info.addFix(intentionManager.convertToFix(new AddCustomNullableVariable(variableName))); + info.addFix(intentionManager.convertToFix(new AddCustomNotNullVariable(variableName))); + } + problems.add(info); + } } + } diff --git a/src/main/java/com/jantvrdik/intellij/latte/inspections/utils/LatteInspectionInfo.java b/src/main/java/com/jantvrdik/intellij/latte/inspections/utils/LatteInspectionInfo.java new file mode 100644 index 0000000..d64382b --- /dev/null +++ b/src/main/java/com/jantvrdik/intellij/latte/inspections/utils/LatteInspectionInfo.java @@ -0,0 +1,77 @@ +package com.jantvrdik.intellij.latte.inspections.utils; + +import com.intellij.codeInspection.*; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public final class LatteInspectionInfo { + + @NotNull + private final PsiElement element; + + @NotNull + private final String description; + + @NotNull + private final ProblemHighlightType type; + + @NotNull + private final List fixes = new ArrayList<>(); + + private LatteInspectionInfo( + @NotNull final PsiElement element, + @NotNull final String description, + @NotNull final ProblemHighlightType type + ) { + this.element = element; + this.description = description; + this.type = type; + } + + public static LatteInspectionInfo error(@NotNull final PsiElement element, @NotNull final String description) { + return new LatteInspectionInfo(element, description, ProblemHighlightType.GENERIC_ERROR_OR_WARNING); + } + + public static LatteInspectionInfo unused(@NotNull final PsiElement element, @NotNull final String description) { + return new LatteInspectionInfo(element, description, ProblemHighlightType.LIKE_UNUSED_SYMBOL); + } + + public static LatteInspectionInfo warning(@NotNull final PsiElement element, @NotNull final String description) { + return new LatteInspectionInfo(element, description, ProblemHighlightType.WARNING); + } + + public static LatteInspectionInfo deprecated(@NotNull final PsiElement element, @NotNull final String description) { + return new LatteInspectionInfo(element, description, ProblemHighlightType.LIKE_DEPRECATED); + } + + public static LatteInspectionInfo weakWarning(@NotNull final PsiElement element, @NotNull final String description) { + return new LatteInspectionInfo(element, description, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL); + } + + public void addFix(@NotNull LocalQuickFix quickFix) { + fixes.add(quickFix); + } + + @NotNull + public String getDescription() { + return description; + } + + @NotNull + public PsiElement getElement() { + return element; + } + + @NotNull + public ProblemHighlightType getType() { + return type; + } + + @NotNull + public LocalQuickFix[] getFixes() { + return fixes.toArray(new LocalQuickFix[0]); + } +} diff --git a/src/main/java/com/jantvrdik/intellij/latte/lexer/LatteHighlightingLexer.java b/src/main/java/com/jantvrdik/intellij/latte/lexer/LatteHighlightingLexer.java index 3c8871f..668c955 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/lexer/LatteHighlightingLexer.java +++ b/src/main/java/com/jantvrdik/intellij/latte/lexer/LatteHighlightingLexer.java @@ -2,7 +2,6 @@ import com.intellij.lexer.Lexer; import com.intellij.lexer.LookAheadLexer; -import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import static com.jantvrdik.intellij.latte.psi.LatteTypes.*; diff --git a/src/main/java/com/jantvrdik/intellij/latte/lexer/LatteLookAheadLexer.java b/src/main/java/com/jantvrdik/intellij/latte/lexer/LatteLookAheadLexer.java index fc09590..0bf207f 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/lexer/LatteLookAheadLexer.java +++ b/src/main/java/com/jantvrdik/intellij/latte/lexer/LatteLookAheadLexer.java @@ -6,9 +6,13 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.jantvrdik.intellij.latte.psi.LatteTypes; +import com.jantvrdik.intellij.latte.utils.LatteTagsUtil; +import com.jantvrdik.intellij.latte.utils.LatteTypesUtil; +import org.jetbrains.annotations.NotNull; -import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Lexer used for syntax highlighting @@ -18,11 +22,14 @@ public class LatteLookAheadLexer extends LookAheadLexer { private static final TokenSet WHITESPACES = TokenSet.create(TokenType.WHITE_SPACE, LatteTypes.T_WHITESPACE); private static final TokenSet TAG_TAGS = TokenSet.create(LatteTypes.T_HTML_TAG_ATTR_EQUAL_SIGN, LatteTypes.T_HTML_TAG_ATTR_DQ); - private static final List LINK_TAGS = Arrays.asList("link", "plink", "n:href"); - private boolean lastLink = false; - private boolean replaceAsLink = false; - private Lexer lexer; + private static final String IDENTIFIER_LINKS = "links"; + private static final String IDENTIFIER_TYPES = "types"; + + private final Map lastValid = new HashMap<>(); + private final Map replaceAs = new HashMap<>(); + + final private Lexer lexer; public LatteLookAheadLexer(Lexer baseLexer) { super(baseLexer, 1); @@ -32,21 +39,42 @@ public LatteLookAheadLexer(Lexer baseLexer) { @Override protected void addToken(int endOffset, IElementType type) { boolean wasLinkDestination = false; - if ((type == LatteTypes.T_PHP_IDENTIFIER || type == LatteTypes.T_PHP_KEYWORD || (type == LatteTypes.T_MACRO_ARGS && isCharacterAtCurrentPosition(lexer, '#', ':'))) && replaceAsLink) { + if ((type == LatteTypes.T_PHP_IDENTIFIER || type == LatteTypes.T_PHP_KEYWORD || (type == LatteTypes.T_MACRO_ARGS && isCharacterAtCurrentPosition(lexer, '#', ':'))) && needReplaceAsMacro(IDENTIFIER_LINKS)) { type = LatteTypes.T_LINK_DESTINATION; wasLinkDestination = true; } + boolean wasTypeDefinition = false; + boolean isMacroSeparator = type == LatteTypes.T_PHP_MACRO_SEPARATOR; + boolean isMacroFilters = type == LatteTypes.T_MACRO_FILTERS; + if ((LatteTypesUtil.phpTypeTokens.contains(type) || isMacroSeparator || isMacroFilters) && needReplaceAsMacro(IDENTIFIER_TYPES)) { + if (isMacroSeparator) { + type = LatteTypes.T_PHP_OR_INCLUSIVE; + } else if (isMacroFilters) { + type = LatteTypes.T_PHP_IDENTIFIER; + } + wasTypeDefinition = true; + } + super.addToken(endOffset, type); if (!TAG_TAGS.contains(type)) { - boolean current = (type == LatteTypes.T_MACRO_NAME || type == LatteTypes.T_HTML_TAG_NATTR_NAME) && isLinkMacro(lexer); - replaceAsLink = (wasLinkDestination && !WHITESPACES.contains(type)) - || (!wasLinkDestination && lastLink && WHITESPACES.contains(type)) - || current; - lastLink = current; + checkMacroType("links", type, LatteTagsUtil.LINK_TAGS_LIST, wasLinkDestination); + checkMacroType("types", type, LatteTagsUtil.TYPE_TAGS_LIST, wasTypeDefinition); } } + private boolean needReplaceAsMacro(@NotNull String identifier) { + return replaceAs.getOrDefault(identifier, false); + } + + private void checkMacroType(@NotNull String identifier, IElementType type, @NotNull List types, boolean currentValid) { + boolean current = (type == LatteTypes.T_MACRO_NAME || type == LatteTypes.T_HTML_TAG_NATTR_NAME) && isMacroTypeMacro(lexer, types); + replaceAs.put(identifier, (currentValid && !WHITESPACES.contains(type)) + || (!currentValid && lastValid.containsKey(identifier) && lastValid.getOrDefault(identifier, false) && WHITESPACES.contains(type)) + || current); + lastValid.put(identifier, current); + } + public static boolean isCharacterAtCurrentPosition(Lexer baseLexer, char ...characters) { char current = baseLexer.getBufferSequence().charAt(baseLexer.getCurrentPosition().getOffset()); for (char character : characters) { @@ -57,11 +85,11 @@ public static boolean isCharacterAtCurrentPosition(Lexer baseLexer, char ...char return false; } - public static boolean isLinkMacro(Lexer baseLexer) { + private static boolean isMacroTypeMacro(Lexer baseLexer, @NotNull List types) { CharSequence tagName = baseLexer.getBufferSequence().subSequence(baseLexer.getTokenStart(), baseLexer.getTokenEnd()); if (tagName.length() == 0) { return false; } - return LINK_TAGS.contains(tagName.toString()); + return types.contains(tagName.toString()); } } diff --git a/src/main/java/com/jantvrdik/intellij/latte/parser/LatteParser.bnf b/src/main/java/com/jantvrdik/intellij/latte/parser/LatteParser.bnf index fbca79b..d239d27 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/parser/LatteParser.bnf +++ b/src/main/java/com/jantvrdik/intellij/latte/parser/LatteParser.bnf @@ -36,6 +36,7 @@ macroClassic ::= macroTag macroTag?{ pairMacro ::= macroOpenTag structureToken* macroCloseTag? { //incomplete pair macro is handled in a annotator extends=macroClassic + methods=[getMacroOpenTag] } emptyMacro ::= emptyMacroTag { @@ -82,7 +83,7 @@ private classicMacroContent ::= macroName macroContent? macroContent ::= macroArgs? (T_MACRO_MODIFIERS macroArgs)* { - methods=[getFirstPhpContent] + methods=[getFirstPhpContent getMacroNameElement] } private @@ -114,14 +115,19 @@ netteAttrValue ::= T_HTML_TAG_ATTR_SQ macroContent? T_HTML_TAG_ATTR_SQ outerHtml ::= htmlTag | T_TEXT | T_HTML_CLOSE_TAG_OPEN | T_HTML_OPEN_TAG_OPEN | T_HTML_OPEN_TAG_CLOSE | T_HTML_TAG_CLOSE; private -htmlTag ::= <> (htmlOpenTag htmlTagContainer htmlCloseTag) | <> htmlEmptyTag +htmlTag ::= <> htmlPairTag | <> htmlEmptyTag -htmlTagContainer ::= (htmlTag | T_TEXT | macro | (pairMacro | unpairedMacro))* +htmlPairTag ::= htmlOpenTag htmlTagContainer htmlCloseTag + +htmlTagContainer ::= (htmlTag | T_TEXT | macro | (pairMacro | unpairedMacro))* { + methods=[getHtmlOpenTag] + } htmlTagContent ::= (netteAttr | macro | T_TEXT | (pairMacro | unpairedMacro))* htmlOpenTag ::= T_HTML_OPEN_TAG_OPEN htmlTagContent T_HTML_TAG_CLOSE { pin=1 + methods=[getHtmlTagName] } htmlCloseTag ::= T_HTML_CLOSE_TAG_OPEN htmlTagContent T_HTML_TAG_CLOSE { @@ -147,7 +153,7 @@ phpVariable ::= T_MACRO_ARGS_VAR phpArrayUsage* { getName setName getNameIdentifier getTextElement getVariableName isDefinition getPhpArrayLevel isDefinitionInFor isDefinitionInForeach getReturnType getPhpType isCaptureDefinition isBlockDefineVarDefinition isVarTypeDefinition isVarDefinition - isPhpArrayVarDefinition getPhpStatementPart + isPhpArrayVarDefinition getPhpStatementPart getVariableContext getVariableDefinition ] } @@ -320,7 +326,7 @@ phpPrivateArgument ::= phpString | phpArrayOfVariables | phpTypedArguments | | macroModifier | T_PHP_IDENTIFIER | T_PHP_MACRO_SEPARATOR | T_PHP_OR_INCLUSIVE | T_PHP_DEFINITION_OPERATOR phpExpression ::= (expression | T_MACRO_ARGS)* { - methods=[getPhpType] + methods=[getPhpType getPhpArrayLevel] implements="com.jantvrdik.intellij.latte.psi.elements.LattePhpExpressionElement" } diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpUtil.java b/src/main/java/com/jantvrdik/intellij/latte/php/LattePhpUtil.java similarity index 85% rename from src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpUtil.java rename to src/main/java/com/jantvrdik/intellij/latte/php/LattePhpUtil.java index 4e76e08..782bfe2 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpUtil.java +++ b/src/main/java/com/jantvrdik/intellij/latte/php/LattePhpUtil.java @@ -1,4 +1,4 @@ -package com.jantvrdik.intellij.latte.utils; +package com.jantvrdik.intellij.latte.php; import com.intellij.codeInsight.completion.PrefixMatcher; import com.intellij.openapi.project.Project; @@ -6,6 +6,7 @@ import com.intellij.psi.ResolveResult; import com.jantvrdik.intellij.latte.psi.*; import com.jantvrdik.intellij.latte.psi.elements.BaseLattePhpElement; +import com.jantvrdik.intellij.latte.utils.LatteUtil; import com.jetbrains.php.PhpIndex; import com.jetbrains.php.lang.psi.elements.*; import com.jetbrains.php.lang.psi.resolve.types.PhpType; @@ -20,18 +21,18 @@ public class LattePhpUtil { public static Collection getAllClassNamesAndInterfaces(Project project, Collection classNames) { - Collection variants = new THashSet(); + Collection variants = new THashSet<>(); PhpIndex phpIndex = getPhpIndex(project); for (String name : classNames) { - variants.addAll(filterClasses(phpIndex.getClassesByName(name), null)); - variants.addAll(filterClasses(phpIndex.getInterfacesByName(name), null)); + variants.addAll(filterClasses(phpIndex.getClassesByFQN(name), null)); + variants.addAll(filterClasses(phpIndex.getInterfacesByFQN(name), null)); } return variants; } public static Collection getAllFunctions(Project project, Collection functionNames) { - Collection variants = new THashSet(); + Collection variants = new THashSet<>(); PhpIndex phpIndex = getPhpIndex(project); for (String name : functionNames) { @@ -40,14 +41,6 @@ public static Collection getAllFunctions(Project project, Collection getFieldsForPhpElement(@NotNull BaseLattePhpElement psiElement) { - List out = new ArrayList(); - LattePhpType phpType = psiElement.getPhpType(); + List out = new ArrayList<>(); + NettePhpType phpType = psiElement.getPhpType(); String name = psiElement.getPhpElementName(); boolean isConstant = psiElement instanceof LattePhpConstant; if (psiElement instanceof LattePhpVariable) { @@ -125,7 +118,7 @@ public static List getFieldsForPhpElement(@NotNull BaseLattePhpElement ps } Collection phpClasses = phpType.getPhpClasses(psiElement.getProject()); - if (phpClasses == null || phpClasses.size() == 0) { + if (phpClasses.size() == 0) { return out; } @@ -144,9 +137,9 @@ public static List getFieldsForPhpElement(@NotNull BaseLattePhpElement ps } public static List getMethodsForPhpElement(@NotNull LattePhpMethod psiElement) { - List out = new ArrayList(); + List out = new ArrayList<>(); Collection phpClasses = psiElement.getPhpType().getPhpClasses(psiElement.getProject()); - if (phpClasses != null && phpClasses.size() > 0) { + if (phpClasses.size() > 0) { String methodName = psiElement.getMethodName(); for (PhpClass phpClass : phpClasses) { for (Method currentMethod : phpClass.getMethods()) { @@ -171,13 +164,27 @@ public static Collection getNamespacesByName(Project project, Stri return getPhpIndex(project).getNamespacesByName(className); } + public static Collection getAlNamespaces(Project project, Collection namespaceNames) { + Collection variants = new THashSet<>(); + PhpIndex phpIndex = getPhpIndex(project); + + for (String name : namespaceNames) { + variants.addAll(phpIndex.getNamespacesByName(name)); + } + return variants; + } + + public static Collection getAllExistingNamespacesByName(Project project, String className) { + return getPhpIndex(project).getAllChildNamespacesFqns(className); + } + public static Collection getAllExistingFunctionNames(Project project, PrefixMatcher prefixMatcher) { return getPhpIndex(project).getAllFunctionNames(prefixMatcher); } public static Collection getAllExistingClassNames(Project project, PrefixMatcher prefixMatcher) { - return getPhpIndex(project).getAllClassNames(prefixMatcher); + return getPhpIndex(project).getAllClassFqns(prefixMatcher); } private static PhpIndex getPhpIndex(Project project) { @@ -189,7 +196,7 @@ private static Collection filterClasses(Collection classes, return classes; } namespace = "\\" + namespace + "\\"; - Collection result = new ArrayList(); + Collection result = new ArrayList<>(); for (PhpClass cls : classes) { String classNs = cls.getNamespaceName(); if (classNs.equals(namespace) || classNs.startsWith(namespace)) { diff --git a/src/main/java/com/jantvrdik/intellij/latte/php/LattePhpVariableUtil.java b/src/main/java/com/jantvrdik/intellij/latte/php/LattePhpVariableUtil.java new file mode 100644 index 0000000..bd3c83b --- /dev/null +++ b/src/main/java/com/jantvrdik/intellij/latte/php/LattePhpVariableUtil.java @@ -0,0 +1,462 @@ +package com.jantvrdik.intellij.latte.php; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiRecursiveElementWalkingVisitor; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.jantvrdik.intellij.latte.config.LatteConfiguration; +import com.jantvrdik.intellij.latte.psi.*; +import com.jantvrdik.intellij.latte.psi.elements.LattePhpStatementPartElement; +import com.jantvrdik.intellij.latte.psi.elements.LattePhpTypedPartElement; +import com.jantvrdik.intellij.latte.settings.LatteVariableSettings; +import com.jantvrdik.intellij.latte.utils.*; + +import static com.jantvrdik.intellij.latte.utils.LatteTagsUtil.*; + +import com.jetbrains.php.lang.psi.elements.Field; +import com.jetbrains.php.lang.psi.elements.PhpClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +import static com.jantvrdik.intellij.latte.psi.LatteTypes.*; + +public class LattePhpVariableUtil { + public static boolean isVariableDefinition(LattePhpVariable element) { + if (isVarTypeDefinition(element) || isCaptureDefinition(element) || isBlockDefineVarDefinition(element)) { + return true; + } + + if (isVarDefinition(element) || isPhpArrayVarDefinition(element)) { + if (isNextDefinitionOperator(element)) { + return true; + } + } + + PsiElement parent = element.getParent(); + if (parent == null) { + return false; + } + + if (parent.getNode().getElementType() == PHP_ARRAY_OF_VARIABLES) { + if (isNextDefinitionOperator(parent)) { + return true; + } + } + + if (isDefinitionInForeach(element)) { + return true; + } + + return isDefinitionInFor(element); + } + + public static boolean isVarTypeDefinition(@NotNull LattePhpVariable element) { + return LatteUtil.matchParentMacroName(element, Type.VAR_TYPE.getTagName()); + } + + public static boolean isCaptureDefinition(@NotNull LattePhpVariable element) { + return LatteUtil.matchParentMacroName(element, Type.CAPTURE.getTagName()); + } + + public static boolean isBlockDefineVarDefinition(@NotNull LattePhpVariable element) { + return LatteUtil.matchParentMacroName(element, Type.DEFINE.getTagName()); + } + + public static boolean isVarDefinition(@NotNull LattePhpVariable element) { + return LatteUtil.matchParentMacroName(element, Type.VAR.getTagName()) || LatteUtil.matchParentMacroName(element, Type.DEFAULT.getTagName()); + } + + public static boolean isPhpArrayVarDefinition(@NotNull LattePhpVariable element) { + return (LatteUtil.matchParentMacroName(element, Type.PHP.getTagName()) || LatteUtil.matchParentMacroName(element, Type.DO.getTagName())) + && element.getParent() instanceof LattePhpArrayOfVariables; + } + + public static boolean isDefinitionInForeach(@NotNull PsiElement element) { + PsiElement parent = element.getParent(); + if (parent != null && parent.getNode().getElementType() == PHP_FOREACH) { + PsiElement prevElement = PsiTreeUtil.skipWhitespacesBackward(element); + IElementType type = prevElement != null ? prevElement.getNode().getElementType() : null; + return type == T_PHP_AS || type == T_PHP_DOUBLE_ARROW; + + } else if (parent != null && parent.getNode().getElementType() == PHP_ARRAY_OF_VARIABLES) { + return isDefinitionInForeach(parent); + } + return false; + } + + public static boolean isDefinitionInFor(@NotNull LattePhpVariable element) { + return LatteUtil.matchParentMacroName(element, Type.FOR.getTagName()) && isNextDefinitionOperator(element); + } + + @NotNull + public static List getVariableOtherDefinitions(@NotNull LattePhpVariable element) { + PsiElement context = element.getVariableContext(); + if (context == null) { + return new ArrayList<>(); + } + int offset = LatteUtil.getStartOffsetInFile(element); + return findVariables(element, context, offset, false); + } + + @NotNull + public static List getVariablesDefinitionsBeforeElement(PsiElement psiElement) { + LatteFile file = PsiTreeUtil.getParentOfType(psiElement, LatteFile.class); + if (file == null) { + return Collections.emptyList(); + } + List out = new ArrayList<>(); + int offset = LatteUtil.getStartOffsetInFile(psiElement); + findLattePhpVariables(out, file, null, true, false, offset, null); + return out; + } + + @NotNull + public static List getVariableDefinition(@NotNull LattePhpVariable element) { + PsiElement context = element.getVariableContext(); + if (context == null || element.isDefinition()) { + return new ArrayList<>(); + } + int offset = LatteUtil.getStartOffsetInFile(element); + return findVariables(element, context, offset, true); + } + + @NotNull + public static Map getAllVariablesInFile(PsiElement psiElement) { + LatteFile file = psiElement instanceof LatteFile + ? (LatteFile) psiElement + : PsiTreeUtil.getParentOfType(psiElement, LatteFile.class); + if (file == null) { + return Collections.emptyMap(); + } + Map out = new HashMap<>(); + psiElement.acceptChildren(new PsiRecursiveElementWalkingVisitor() { + @Override + public void visitElement(@NotNull PsiElement element) { + if (element instanceof LattePhpVariable) { + out.put(element, new PsiCachedElement(LatteUtil.getStartOffsetInFile(element), (LattePhpVariable) element)); + } else { + super.visitElement(element); + } + } + }); + return out; + } + + @NotNull + private static List getParentDefinition( + @NotNull LattePhpVariable element, + @NotNull PsiElement context, + int offset + ) { + List contextDefinitions = new ArrayList<>(); + findLattePhpVariables( + contextDefinitions, + context, + element.getVariableName(), + true, + false, + offset, + context + ); + if (contextDefinitions.size() > 0) { + List out = new ArrayList<>(); + for (PsiCachedElement contextDefinition : contextDefinitions) { + if (contextDefinition.getElement() != element) { + out.add(new LattePhpVariableDefinition(false, contextDefinition.getElement())); + } + } + return out; + } else if (context instanceof LatteFile) { + return new ArrayList<>(); + } + + PsiElement parentContext = getCurrentContext(context.getParent()); + if (parentContext == null) { + return new ArrayList<>(); + } + return getParentDefinition(element, parentContext, offset); + } + + @Nullable + public static PsiElement getCurrentContext(@NotNull PsiElement element) { + if (element instanceof LattePhpVariable && (!((LattePhpVariable) element).isDefinition() && !LatteUtil.matchParentMacroName(element, Type.FOR.getTagName()))) { + PsiElement mainOpenTag = PsiTreeUtil.getParentOfType(element, LatteMacroOpenTag.class, LatteHtmlOpenTag.class); + if (mainOpenTag != null) { + element = mainOpenTag.getParent(); + } + } + + PsiElement context = PsiTreeUtil.getParentOfType(element, LattePairMacro.class, LatteHtmlPairTag.class, LatteFile.class); + if (context instanceof LattePairMacro) { + LatteMacroTag openTag = ((LattePairMacro) context).getMacroOpenTag(); + if (openTag == null || !LatteTagsUtil.isContextTag(openTag.getMacroName())) { + return getCurrentContext(context); + } + return context; + + } else if (context instanceof LatteHtmlPairTag) { + LatteHtmlOpenTag openTag = ((LatteHtmlPairTag) context).getHtmlOpenTag(); + if (openTag.getHtmlTagContent() == null) { + return getCurrentContext(context); + } + for (LatteNetteAttr netteAttr : openTag.getHtmlTagContent().getNetteAttrList()) { + if (LatteTagsUtil.isContextNetteAttribute(netteAttr.getAttrName().getText())) { + return context; + } + } + return getCurrentContext(context); + } + return context instanceof LatteFile ? context : PsiTreeUtil.getParentOfType(element, LatteFile.class); + } + + public static boolean isSameOrParentContext(@Nullable PsiElement check, @Nullable PsiElement probablySameOrParent) { + if (check == probablySameOrParent) { + return true; + } else if (probablySameOrParent == null) { + return false; + } + PsiElement parentContext = getCurrentContext(probablySameOrParent.getParent()); + return isSameOrParentContext(check, parentContext); + } + + @NotNull + public static NettePhpType detectVariableType(@NotNull LattePhpVariable element) { + String variableName = element.getVariableName(); + List definitions = getVariableDefinition(element); + + LattePhpStatementPartElement mainStatementPart = element.getPhpStatementPart(); + + for (LattePhpVariableDefinition positionedElement : definitions) { + LattePhpVariable current = positionedElement.getElement(); + int startDepth = 0; + if (!(current.getParent() instanceof LattePhpArrayOfVariables)) { + NettePhpType prevPhpType = findPrevPhpType(current); + if (prevPhpType != null) { + return prevPhpType; + } + } else { + startDepth = 1; + } + + if (current.isDefinitionInForeach()) { + PsiElement nextElement = PsiTreeUtil.skipWhitespacesForward(current); + IElementType type = nextElement != null ? nextElement.getNode().getElementType() : null; + if (type != T_PHP_DOUBLE_ARROW) { + LattePhpForeach phpForeach = PsiTreeUtil.getParentOfType(current, LattePhpForeach.class); + if (phpForeach != null && phpForeach.getPhpExpression().getPhpStatementList().size() > 0) { + return phpForeach.getPhpExpression().getPhpType().withDepth(startDepth + 1); + } + } + } + + LattePhpStatementPartElement statementPart = current.getPhpStatementPart(); + LattePhpContent phpContent = PsiTreeUtil.getParentOfType(current, LattePhpContent.class); + if (phpContent != null && statementPart != mainStatementPart) { + return detectVariableType(phpContent, startDepth); + } + return NettePhpType.MIXED; + } + + NettePhpType templateType = detectVariableTypeFromTemplateType(element, variableName); + if (templateType != null) { + return templateType; + } + + LatteVariableSettings defaultVariable = LatteConfiguration.getInstance(element.getProject()).getVariable(variableName); + if (defaultVariable != null) { + return defaultVariable.toPhpType(); + } + + return NettePhpType.MIXED; + } + + @NotNull + public static List findPhpFiledListFromTemplateTypeTag( + @NotNull PsiElement element, + @NotNull String variableName + ) { + if (!(element.getContainingFile() instanceof LatteFile)) { + return Collections.emptyList(); + } + + NettePhpType templateType = LatteUtil.findFirstLatteTemplateType(element.getContainingFile()); + if (templateType == null) { + return Collections.emptyList(); + } + + Collection classes = templateType.getPhpClasses(element.getProject()); + List out = new ArrayList<>(); + for (PhpClass phpClass : classes) { + for (Field field : phpClass.getFields()) { + if (!field.isConstant() && field.getModifier().isPublic() && variableName.equals(field.getName())) { + out.add(field); + } + } + } + return out; + } + + @Nullable + public static NettePhpType detectVariableTypeFromTemplateType( + @NotNull final PsiElement element, + @NotNull final String variableName + ) { + List fields = findPhpFiledListFromTemplateTypeTag(element, variableName); + if (fields.size() == 0) { + return null; + } + Field field = fields.get(0); + return NettePhpType.create(field.getName(), field.getType().toString(), LattePhpUtil.isNullable(field.getType())); + } + + @NotNull + private static NettePhpType detectVariableType(@NotNull final LattePhpContent phpContent, final int startDepth) { + final PsiElement[] varDefinition = {null}; + final boolean[] varDefinitionOperator = {false}; + List otherParts = new ArrayList<>(); + phpContent.acceptChildren(new PsiElementVisitor() { + @Override + public void visitElement(@NotNull PsiElement element) { + if ( + element instanceof LattePhpStatement && ((LattePhpStatement) element).isPhpVariableOnly() + || element instanceof LattePhpArrayOfVariables + ) { + varDefinition[0] = element; + + } else if (varDefinition[0] != null && element.getNode().getElementType() == LatteTypes.T_PHP_DEFINITION_OPERATOR) { + varDefinitionOperator[0] = true; + + } else if ( + !(element instanceof LatteMacroModifier) + && !LatteTypesUtil.whitespaceTokens.contains(element.getNode().getElementType()) + && varDefinitionOperator[0] + ) { + otherParts.add(element); + } + } + }); + + if (otherParts.size() == 1 && otherParts.get(0) instanceof LattePhpStatement) { + return ((LattePhpStatement) otherParts.get(0)).getPhpType().withDepth(startDepth); + } + + if (startDepth > 0) { + return NettePhpType.MIXED; + + } else if ( + otherParts.stream().anyMatch(element -> element instanceof LattePhpString + || element.getNode().getElementType() == T_PHP_CONCATENATION) + ) { + return NettePhpType.STRING; + + } else if (otherParts.stream().anyMatch(element -> element.getNode().getElementType() == T_MACRO_ARGS_NUMBER)) { + return NettePhpType.INT; + + } else if (otherParts.stream().anyMatch(element -> element instanceof LattePhpArray || element instanceof LattePhpArrayOfVariables)) { + return NettePhpType.ARRAY; + } + + return NettePhpType.MIXED; + } + + public static boolean isNextDefinitionOperator(@NotNull PsiElement element) { + PsiElement found = null; + if (element instanceof LattePhpVariable && ((LattePhpVariable) element).getPhpStatementPart() != null) { + LattePhpStatementPartElement statementPart = ((LattePhpVariable) element).getPhpStatementPart(); + found = statementPart != null ? statementPart.getPhpStatement() : null; + } else if (element.getParent() instanceof LattePhpArrayOfVariables) { + found = element.getParent(); + } + + if (found == null) { + LattePhpTypedArguments typedArgs = PsiTreeUtil.getParentOfType(element, LattePhpTypedArguments.class); + found = typedArgs != null ? typedArgs : element; + } + + PsiElement nextElement = PsiTreeUtil.skipWhitespacesForward(found); + return nextElement != null && nextElement.getNode().getElementType() == LatteTypes.T_PHP_DEFINITION_OPERATOR; + } + + @NotNull + private static List findVariables( + @NotNull LattePhpVariable element, + @NotNull PsiElement context, + int offset, + boolean onlyDefinitions + ) { + List definitions = getParentDefinition(element, context, offset); + if (definitions.size() == 0) { + List contextDefinitions = new ArrayList<>(); + findLattePhpVariables(contextDefinitions, context, element.getVariableName(), onlyDefinitions, !onlyDefinitions, offset, null); + for (PsiCachedElement contextDefinition : contextDefinitions) { + if (contextDefinition.getElement() != element) { + definitions.add(new LattePhpVariableDefinition(true, contextDefinition.getElement())); + } + } + } + if (definitions.size() == 0 && !(context instanceof LatteFile)) { + return findVariables(element, context.getParent(), offset, onlyDefinitions); + } + return definitions; + } + + private static void findLattePhpVariables( + List properties, + PsiElement psiElement, + @Nullable String variableName, + boolean onlyDefinitions, + boolean onlyUsages, + int offset, + @Nullable PsiElement context + ) { + psiElement.acceptChildren(new PsiRecursiveElementWalkingVisitor() { + @Override + public void visitElement(@NotNull PsiElement element) { + if (element instanceof LattePhpVariable) { + if (onlyDefinitions) { + if (!((LattePhpVariable) element).isDefinition() || (offset > 0 && LatteUtil.getStartOffsetInFile(element) >= offset)) { + return; + } + } else if (onlyUsages) { + if (((LattePhpVariable) element).isDefinition() || (offset > 0 && LatteUtil.getStartOffsetInFile(element) < offset)) { + return; + } + } + if (variableName != null && !variableName.equals(((LattePhpVariable) element).getVariableName())) { + return; + } + if (context != null && ((LattePhpVariable) element).getVariableContext() != context) { + return; + } + properties.add(new PsiCachedElement(LatteUtil.getStartOffsetInFile(element), (LattePhpVariable) element)); + } else { + super.visitElement(element); + } + } + }); + } + + @Nullable + private static NettePhpType findPrevPhpType(LattePhpVariable element) { + LattePhpTypedPartElement typedElement = PsiTreeUtil.getParentOfType(element, LattePhpTypedPartElement.class); + if (typedElement != null) { + return typedElement.getPhpType(); + } + LattePhpStatementPartElement statementPart = element.getPhpStatementPart(); + if (statementPart == null) { + return null; + } + PsiElement phpTypeElement = PsiTreeUtil.skipWhitespacesBackward(statementPart.getPhpStatement()); + return phpTypeElement instanceof LattePhpTypeElement + ? ((LattePhpTypeElement) phpTypeElement).getPhpType() + : (phpTypeElement instanceof LattePhpStatement ? ((LattePhpStatement) phpTypeElement).getPhpType() : null); + } + + public static String normalizePhpVariable(String name) { + return name == null ? null : (name.startsWith("$") ? name.substring(1) : name); + } + +} \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpType.java b/src/main/java/com/jantvrdik/intellij/latte/php/NettePhpType.java similarity index 54% rename from src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpType.java rename to src/main/java/com/jantvrdik/intellij/latte/php/NettePhpType.java index a274909..d15a955 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpType.java +++ b/src/main/java/com/jantvrdik/intellij/latte/php/NettePhpType.java @@ -1,4 +1,4 @@ -package com.jantvrdik.intellij.latte.utils; +package com.jantvrdik.intellij.latte.php; import com.intellij.openapi.project.Project; import com.jetbrains.php.lang.psi.elements.PhpClass; @@ -9,34 +9,44 @@ import java.util.*; import java.util.stream.Collectors; -public class LattePhpType { - private static final Map instances = new HashMap<>(); - - final public static LattePhpType MIXED = new LattePhpType("mixed"); - final public static LattePhpType STRING = new LattePhpType("string"); - final public static LattePhpType INT = new LattePhpType("int"); - final public static LattePhpType BOOL = new LattePhpType("bool"); - final public static LattePhpType OBJECT = new LattePhpType("object"); - final public static LattePhpType FLOAT = new LattePhpType("float"); - final public static LattePhpType ARRAY = new LattePhpType("array"); - final public static LattePhpType NULL = new LattePhpType("null"); - final public static LattePhpType CALLABLE = new LattePhpType("callable"); - final public static LattePhpType ITERABLE = new LattePhpType("iterable"); - - final private static Map nativeTypes = new HashMap() {{ - put("string", new LattePhpType[]{STRING, new LattePhpType("mixed|null"), new LattePhpType("mixed[]")}); - put("int", new LattePhpType[]{INT, new LattePhpType("int|null"), new LattePhpType("int[]")}); - put("bool", new LattePhpType[]{BOOL, new LattePhpType("bool|null"), new LattePhpType("bool[]")}); - put("object", new LattePhpType[]{OBJECT, new LattePhpType("object|null"), new LattePhpType("object[]")}); - put("float", new LattePhpType[]{FLOAT, new LattePhpType("float|null"), new LattePhpType("float[]")}); - put("array", new LattePhpType[]{ARRAY, new LattePhpType("array|null"), new LattePhpType("array[]")}); - put("mixed", new LattePhpType[]{MIXED, new LattePhpType("mixed|null"), new LattePhpType("mixed[]")}); - put("null", new LattePhpType[]{NULL, new LattePhpType("null"), new LattePhpType("null[]")}); - put("callable", new LattePhpType[]{CALLABLE, new LattePhpType("callable|null"), new LattePhpType("callable[]")}); - put("iterable", new LattePhpType[]{ITERABLE, new LattePhpType("iterable|null"), new LattePhpType("iterable[]")}); +public class NettePhpType { + private static final Map instances = new HashMap<>(); + + final private static String[] nativeClassConstants = new String[]{"class"}; + + final private static String[] nativeTypeHints = new String[]{"string", "int", "bool", "object", "float", "array", "mixed", "null", "callable", "iterable"}; + + final private static String[] iterableTypes = new String[]{"\\Iterator", "\\Generator"}; + + final private static String[] nativeIterableTypeHints = new String[]{"array", "iterable"}; + + final private static String[] magicMethods = new String[]{"__construct", "__callstatic", "__call", "__get", "__isset", "__clone", "__set", "__unset"}; + + final public static NettePhpType MIXED = new NettePhpType("mixed"); + final public static NettePhpType STRING = new NettePhpType("string"); + final public static NettePhpType INT = new NettePhpType("int"); + final public static NettePhpType BOOL = new NettePhpType("bool"); + final public static NettePhpType OBJECT = new NettePhpType("object"); + final public static NettePhpType FLOAT = new NettePhpType("float"); + final public static NettePhpType ARRAY = new NettePhpType("array"); + final public static NettePhpType NULL = new NettePhpType("null"); + final public static NettePhpType CALLABLE = new NettePhpType("callable"); + final public static NettePhpType ITERABLE = new NettePhpType("iterable"); + + final private static Map nativeTypes = new HashMap() {{ + put("string", new NettePhpType[]{STRING, new NettePhpType("mixed|null"), new NettePhpType("mixed[]")}); + put("int", new NettePhpType[]{INT, new NettePhpType("int|null"), new NettePhpType("int[]")}); + put("bool", new NettePhpType[]{BOOL, new NettePhpType("bool|null"), new NettePhpType("bool[]")}); + put("object", new NettePhpType[]{OBJECT, new NettePhpType("object|null"), new NettePhpType("object[]")}); + put("float", new NettePhpType[]{FLOAT, new NettePhpType("float|null"), new NettePhpType("float[]")}); + put("array", new NettePhpType[]{ARRAY, new NettePhpType("array|null"), new NettePhpType("array[]")}); + put("mixed", new NettePhpType[]{MIXED, new NettePhpType("mixed|null"), new NettePhpType("mixed[]")}); + put("null", new NettePhpType[]{NULL, new NettePhpType("null"), new NettePhpType("null[]")}); + put("callable", new NettePhpType[]{CALLABLE, new NettePhpType("callable|null"), new NettePhpType("callable[]")}); + put("iterable", new NettePhpType[]{ITERABLE, new NettePhpType("iterable|null"), new NettePhpType("iterable[]")}); }}; - private final String name; + private final @Nullable String name; private final List types = new ArrayList<>(); private final Map> wholeTypes = new HashMap<>(); private final List nullable = new ArrayList<>(); @@ -44,76 +54,76 @@ public class LattePhpType { private final List iterable = new ArrayList<>(); private final List natives = new ArrayList<>(); private final Map> classes = new HashMap<>(); - private final Map forDepth = new HashMap<>(); + private final Map forDepth = new HashMap<>(); @NotNull - public static LattePhpType create(String type, boolean nullable) { + public static NettePhpType create(final @Nullable String type, boolean nullable) { return create(null, type, nullable); } @NotNull - public static LattePhpType create(String name, String type) { + public static NettePhpType create(final @Nullable String name, final @Nullable String type) { return create(name, type, false); } @NotNull - public static LattePhpType create(String type) { + public static NettePhpType create(final @Nullable String type) { return create(null, type, false); } @NotNull - public static LattePhpType create(PhpType phpType) { + public static NettePhpType create(final @NotNull PhpType phpType) { return create(null, String.join("|", phpType.getTypes()), LattePhpUtil.isNullable(phpType)); } @NotNull - public static LattePhpType create(List phpTypes) { + public static NettePhpType create(final @NotNull List phpTypes) { List typesStrings = new ArrayList<>(); for (PhpType type : phpTypes) { typesStrings.add(type.toString()); } - Set temp = new LinkedHashSet( + Set temp = new LinkedHashSet<>( Arrays.asList(String.join("|", typesStrings).split("\\|")) ); return create(null, String.join("|", temp)); } @NotNull - public static LattePhpType create(String name, String type, boolean nullable) { + public static NettePhpType create(final @Nullable String name, final @Nullable String type, final boolean nullable) { if (type == null || type.trim().length() == 0) { - return LattePhpType.MIXED; + return NettePhpType.MIXED; } String trimmed = type.trim().toLowerCase(); - if (LatteTypesUtil.isNativeTypeHint(trimmed)) { - return nativeTypes.get(LatteTypesUtil.normalizeTypeHint(trimmed))[0]; + if (isNativeTypeHint(trimmed)) { + return nativeTypes.get(normalizeTypeHint(trimmed))[0]; } else if (trimmed.endsWith("[]")) { String typeHint = trimmed.substring(0, trimmed.length() - 2); - if (LatteTypesUtil.isNativeTypeHint(typeHint)) { - return nativeTypes.get(LatteTypesUtil.normalizeTypeHint(typeHint))[2]; + if (isNativeTypeHint(typeHint)) { + return nativeTypes.get(normalizeTypeHint(typeHint))[2]; } } if (trimmed.endsWith("|null") || trimmed.startsWith("null|")) { String typeHint = trimmed.startsWith("null|") ? trimmed.substring(6) : trimmed.substring(0, trimmed.length() - 5); - if (LatteTypesUtil.isNativeTypeHint(typeHint)) { - return nativeTypes.get(LatteTypesUtil.normalizeTypeHint(typeHint))[1]; + if (isNativeTypeHint(typeHint)) { + return nativeTypes.get(normalizeTypeHint(typeHint))[1]; } } if (!instances.containsKey(type)) { - instances.put(type, new LattePhpType(name, type, nullable)); + instances.put(type, new NettePhpType(name, type, nullable)); } return instances.get(type); } - private LattePhpType(@NotNull String type) { + private NettePhpType(@NotNull String type) { this(null, type, false); } - private LattePhpType(String name, @NotNull String typeString, boolean nullable) { + private NettePhpType(final @Nullable String name, final @NotNull String typeString, final boolean nullable) { String[] parts = typeString.split("\\|"); for (String part : parts) { part = part.trim(); @@ -121,7 +131,7 @@ private LattePhpType(String name, @NotNull String typeString, boolean nullable) continue; } - TypePart typePart = new TypePart(part); + NettePhpType.TypePart typePart = new NettePhpType.TypePart(part); if (typePart.isClass()) { if (!this.classes.containsKey(typePart.depth)) { this.classes.put(typePart.depth, new ArrayList<>()); @@ -142,20 +152,24 @@ private LattePhpType(String name, @NotNull String typeString, boolean nullable) } if (typePart.isIterable()) { - this.iterable.add(typePart.depth - 1); + for (int i = typePart.depth - 1; i >= 0; i--) { + this.iterable.add(i); + } } - types.add(typePart.getPart()); wholeTypes.putIfAbsent(typePart.depth, new ArrayList<>()); wholeTypes.get(typePart.depth).add(typePart.getWholePart()); + + types.add(typePart.getPart()); } - this.name = name == null ? null : LattePhpUtil.normalizePhpVariable(name); + this.name = name == null ? null : LattePhpVariableUtil.normalizePhpVariable(name); if (nullable && !this.nullable.contains(0)) { this.nullable.add(0); } } + @Nullable public String getName() { return name; } @@ -164,7 +178,7 @@ public boolean containsClasses() { return this.classes.containsKey(0); } - public boolean hasUndefinedClass(@NotNull Project project) { + public boolean hasUndefinedClass(final @NotNull Project project) { if (!containsClasses()) { return false; } @@ -177,7 +191,7 @@ public boolean hasUndefinedClass(@NotNull Project project) { return false; } - public boolean hasClass(String className) { + public boolean hasClass(final @NotNull String className) { if (!containsClasses()) { return false; } @@ -185,7 +199,7 @@ public boolean hasClass(String className) { return classes.containsKey(0) && classes.get(0).contains(normalizedName); } - public boolean hasClass(Collection phpClasses) { + public boolean hasClass(final @NotNull Collection phpClasses) { if (!containsClasses()) { return false; } @@ -209,7 +223,7 @@ public boolean isNative() { return isNative(0); } - public boolean isNative(int depth) { + public boolean isNative(final int depth) { return natives.contains(depth); } @@ -217,33 +231,43 @@ public boolean isMixed() { return isMixed(0); } - public boolean isMixed(int depth) { + public boolean isMixed(final int depth) { return mixed.contains(depth); } - public boolean isIterable(Project project) { + public boolean isIterable(final @NotNull Project project) { return isIterable(project, 0); } - public boolean isIterable(Project project, int depth) { + public boolean isIterable(final @NotNull Project project, final int depth) { if (iterable.contains(depth)) { return true; } Collection classes = getPhpClasses(project, depth); for (PhpClass phpClass : classes) { - if (LattePhpUtil.isReferenceFor(LatteTypesUtil.getIterableInterfaces(), phpClass)) { + if (LattePhpUtil.isReferenceFor(getIterableTypes(), phpClass)) { return true; } } return false; } - public Collection getPhpClasses(Project project) { + boolean isIterable(final int depth) { + return iterable.contains(depth); + } + + boolean isIterable() { + return isIterable(0); + } + + @NotNull + public Collection getPhpClasses(final @NotNull Project project) { return getPhpClasses(project, 0); } - public Collection getPhpClasses(Project project, int depth) { + @NotNull + public Collection getPhpClasses(final @NotNull Project project, final int depth) { List output = new ArrayList<>(); for (String wholeType : findClasses(depth)) { output.addAll(LattePhpUtil.getClassesByFQN(project, wholeType)); @@ -251,37 +275,41 @@ public Collection getPhpClasses(Project project, int depth) { return output; } + @NotNull String[] findClasses() { return findClasses(0); } - String[] findClasses(int depth) { + @NotNull + String[] findClasses(final int depth) { if (!this.classes.containsKey(depth)) { return new String[0]; } return this.classes.get(depth).toArray(new String[0]); } - public LattePhpType withDepth(int depth) { + @NotNull + public NettePhpType withDepth(final int depth) { if (depth == 0) { return this; } - LattePhpType found = forDepth.get(depth); + NettePhpType found = forDepth.get(depth); if (found != null) { return found; } List depthTypes = getTypesForDepth(depth); if (depthTypes.size() > 0) { - found = new LattePhpType(String.join("|", depthTypes)); + found = new NettePhpType(String.join("|", depthTypes)); } - found = found == null ? LattePhpType.MIXED : found; + found = found == null ? NettePhpType.MIXED : found; forDepth.put(depth, found); return found; } - private List getTypesForDepth(int depth) { + @NotNull + private List getTypesForDepth(final int depth) { if (depth == 0) { return types; } @@ -297,7 +325,7 @@ private List getTypesForDepth(int depth) { return depthTypes; } - private void getTypesForDepth(List depthTypes, int depth, int subDepth) { + private void getTypesForDepth(final @NotNull List depthTypes, final int depth, final int subDepth) { List currentTypes = wholeTypes.get(depth + subDepth); if (currentTypes == null) { return; @@ -310,10 +338,12 @@ private void getTypesForDepth(List depthTypes, int depth, int subDepth) } @Override + @NotNull public String toString() { return String.join("|", types); } + @NotNull public List getTypes() { return Collections.unmodifiableList(types); } @@ -326,7 +356,8 @@ private static class TypePart { private boolean isNull = false; private boolean isMixed = false; private boolean isClass = false; - private @Nullable TypePart arrayOf = null; + private @Nullable + NettePhpType.TypePart arrayOf = null; TypePart(@NotNull String part) { if (part.endsWith("[]")) { @@ -339,14 +370,14 @@ private static class TypePart { } loadArrayOf(type, depth); - } else if (LatteTypesUtil.isNativeTypeHint(part)) { + } else if (NettePhpType.isNativeTypeHint(part)) { part = part.toLowerCase(); - if (LatteTypesUtil.isIterable(part)) { + if (NettePhpType.isIterable(part)) { loadArrayOf("mixed", depth); - } else if (LatteTypesUtil.isNull(part)) { + } else if (NettePhpType.isNull(part)) { this.isNull = true; - } else if (LatteTypesUtil.isMixed(part)) { + } else if (NettePhpType.isMixed(part)) { this.isMixed = true; } this.isNative = true; @@ -358,9 +389,9 @@ private static class TypePart { this.part = part; } - private void loadArrayOf(@NotNull String type, int depth) { + private void loadArrayOf(final @NotNull String type, final int depth) { this.depth = depth; - this.arrayOf = new TypePart(type); + this.arrayOf = new NettePhpType.TypePart(type); } boolean isIterable() { @@ -383,15 +414,58 @@ boolean isNullable() { return isNull || (arrayOf != null && arrayOf.isNullable()); } + @NotNull String getPart() { return arrayOf == null || isNative ? part : (arrayOf.getPart() + String.join("", Collections.nCopies(depth, "[]"))); } + @NotNull String getWholePart() { return arrayOf == null || isNative ? part : arrayOf.getWholePart(); } } + @NotNull + public static String[] getNativeClassConstants() { + return nativeClassConstants; + } + + @NotNull + public static String[] getNativeTypeHints() { + return nativeTypeHints; + } + + @NotNull + public static String[] getIterableTypes() { + return iterableTypes; + } + + public static boolean isNativeTypeHint(final @NotNull String value) { + return Arrays.asList(nativeTypeHints).contains(normalizeTypeHint(value)); + } + + public static boolean isIterable(final @NotNull String value) { + return Arrays.asList(nativeIterableTypeHints).contains(normalizeTypeHint(value)); + } + + public static boolean isNull(final @NotNull String value) { + return "null".equals(normalizeTypeHint(value)); + } + + public static boolean isMixed(final @NotNull String value) { + return "mixed".equals(normalizeTypeHint(value)); + } + + public static boolean isMagicMethod(final @NotNull String value) { + return Arrays.asList(magicMethods).contains(value.toLowerCase()); + } + + @NotNull + public static String normalizeTypeHint(@NotNull String value) { + value = value.toLowerCase(); + return value.startsWith("\\") ? value.substring(1) : value; + } + } \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/psi/elements/BaseLattePhpElement.java b/src/main/java/com/jantvrdik/intellij/latte/psi/elements/BaseLattePhpElement.java index f1b827c..7de70e1 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/psi/elements/BaseLattePhpElement.java +++ b/src/main/java/com/jantvrdik/intellij/latte/psi/elements/BaseLattePhpElement.java @@ -2,8 +2,8 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNameIdentifierOwner; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LattePhpArrayUsage; -import com.jantvrdik.intellij.latte.utils.LattePhpType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,9 +14,9 @@ public interface BaseLattePhpElement extends PsiNameIdentifierOwner { @NotNull List getPhpArrayUsageList(); - public abstract LattePhpType getPhpType(); + public abstract NettePhpType getPhpType(); - public abstract LattePhpType getReturnType(); + public abstract NettePhpType getReturnType(); public abstract String getPhpElementName(); diff --git a/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpExpressionElement.java b/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpExpressionElement.java index c778aa6..8ef0344 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpExpressionElement.java +++ b/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpExpressionElement.java @@ -1,8 +1,8 @@ package com.jantvrdik.intellij.latte.psi.elements; import com.intellij.psi.PsiElement; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LattePhpStatement; -import com.jantvrdik.intellij.latte.utils.LattePhpType; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -10,7 +10,7 @@ public interface LattePhpExpressionElement extends PsiElement { @NotNull - LattePhpType getPhpType(); + NettePhpType getPhpType(); @NotNull List getPhpStatementList(); diff --git a/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpStatementPartElement.java b/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpStatementPartElement.java index 4ca1889..c89c177 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpStatementPartElement.java +++ b/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpStatementPartElement.java @@ -1,14 +1,14 @@ package com.jantvrdik.intellij.latte.psi.elements; import com.intellij.psi.PsiElement; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LattePhpStatement; -import com.jantvrdik.intellij.latte.utils.LattePhpType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public interface LattePhpStatementPartElement extends PsiElement { - public abstract LattePhpType getPhpType(); + public abstract NettePhpType getPhpType(); @NotNull public LattePhpStatement getPhpStatement(); diff --git a/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpTypedPartElement.java b/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpTypedPartElement.java index e8549d4..b88251d 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpTypedPartElement.java +++ b/src/main/java/com/jantvrdik/intellij/latte/psi/elements/LattePhpTypedPartElement.java @@ -1,9 +1,9 @@ package com.jantvrdik.intellij.latte.psi.elements; import com.intellij.psi.PsiElement; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LattePhpTypeElement; import com.jantvrdik.intellij.latte.psi.LattePhpVariable; -import com.jantvrdik.intellij.latte.utils.LattePhpType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -16,6 +16,6 @@ public interface LattePhpTypedPartElement extends PsiElement { LattePhpVariable getPhpVariable(); @NotNull - LattePhpType getPhpType(); + NettePhpType getPhpType(); } \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/psi/impl/LattePsiImplUtil.java b/src/main/java/com/jantvrdik/intellij/latte/psi/impl/LattePsiImplUtil.java index 9ba8dc9..173a5b0 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/psi/impl/LattePsiImplUtil.java +++ b/src/main/java/com/jantvrdik/intellij/latte/psi/impl/LattePsiImplUtil.java @@ -1,7 +1,6 @@ package com.jantvrdik.intellij.latte.psi.impl; import com.intellij.lang.ASTNode; -import com.intellij.notification.NotificationType; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiRecursiveElementVisitor; import com.intellij.psi.tree.IElementType; @@ -9,6 +8,9 @@ import com.intellij.psi.util.PsiTreeUtil; import com.jantvrdik.intellij.latte.config.LatteConfiguration; import com.jantvrdik.intellij.latte.indexes.stubs.*; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.elements.BaseLattePhpElement; import com.jantvrdik.intellij.latte.psi.elements.LattePhpExpressionElement; import com.jantvrdik.intellij.latte.psi.elements.LattePhpStatementPartElement; @@ -16,7 +18,6 @@ import com.jantvrdik.intellij.latte.settings.LatteFunctionSettings; import com.jantvrdik.intellij.latte.psi.*; import com.jantvrdik.intellij.latte.utils.*; -import com.jantvrdik.intellij.latte.utils.LattePhpType; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.Method; import com.jetbrains.php.lang.psi.elements.PhpClass; @@ -24,10 +25,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; import static com.jantvrdik.intellij.latte.psi.LatteTypes.*; @@ -82,9 +80,18 @@ private static boolean matchPsiElement(ASTNode element, @NotNull String text) { return phpContents.stream().findFirst().isPresent() ? phpContents.stream().findFirst().get() : null; } + public static @Nullable PsiElement getMacroNameElement(@NotNull LatteMacroContent macroContent) { + return PsiTreeUtil.skipWhitespacesBackward(macroContent); + } + public static String getVariableName(@NotNull LattePhpVariable element) { PsiElement found = getTextElement(element); - return found != null ? LattePhpUtil.normalizePhpVariable(found.getText()) : null; + return found != null ? LattePhpVariableUtil.normalizePhpVariable(found.getText()) : null; + } + + @NotNull + public static List getVariableDefinition(@NotNull LattePhpVariable element) { + return LattePhpVariableUtil.getVariableDefinition(element); } public static String getVariableName(@NotNull LattePhpStaticVariable element) { @@ -94,7 +101,7 @@ public static String getVariableName(@NotNull LattePhpStaticVariable element) { } PsiElement found = getTextElement(element); - return found != null ? LattePhpUtil.normalizePhpVariable(found.getText()) : null; + return found != null ? LattePhpVariableUtil.normalizePhpVariable(found.getText()) : null; } public static @Nullable PsiElement getTextElement(@NotNull LattePhpStaticVariable element) { @@ -174,7 +181,7 @@ public static String getClassName(@NotNull LattePhpClassUsage classUsage) { StringBuilder out = new StringBuilder(); classUsage.getParent().acceptChildren(new PsiRecursiveElementVisitor() { @Override - public void visitElement(PsiElement element) { + public void visitElement(@NotNull PsiElement element) { IElementType type = element.getNode().getElementType(); if (TokenSet.create(T_PHP_NAMESPACE_REFERENCE, T_PHP_NAMESPACE_RESOLUTION, T_PHP_IDENTIFIER).contains(type)) { out.append(element.getText()); @@ -201,11 +208,11 @@ public static boolean isVariableModifier(@NotNull LatteMacroModifier element) { return variableModifier != null; } - public static @NotNull LattePhpType getPhpType(@NotNull LattePhpVariable element) { + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpVariable element) { return LattePhpVariableUtil.detectVariableType(element); } - public static @NotNull LattePhpType getPhpType(@NotNull LattePhpTypeElement element) { + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpTypeElement element) { List out = new ArrayList<>(); for (LattePhpTypePart part : element.getPhpTypePartList()) { String item; @@ -220,17 +227,17 @@ public static boolean isVariableModifier(@NotNull LatteMacroModifier element) { } out.add(item); } - return LattePhpType.create(String.join("|", out)); + return NettePhpType.create(String.join("|", out)); } - public static @NotNull LattePhpType getPhpType(@NotNull BaseLattePhpElement element) { + public static @NotNull NettePhpType getPhpType(@NotNull BaseLattePhpElement element) { LattePhpStatementPartElement part = element.getPhpStatementPart(); if (part == null) { - return LattePhpType.MIXED; + return NettePhpType.MIXED; } part = part.getPrevPhpStatementPart(); if (part == null || part.getPhpElement() == null) { - return LattePhpType.MIXED; + return NettePhpType.MIXED; } return part.getPhpType().withDepth(part.getPhpElement().getPhpArrayLevel()); } @@ -267,22 +274,35 @@ public static int getPhpArrayLevel(@NotNull BaseLattePhpElement element) { return (LattePhpStatement) element.getParent(); } - public static @NotNull LattePhpType getPhpType(@NotNull LattePhpStatement statement) { + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpStatement statement) { BaseLattePhpElement last = statement.getLastPhpElement(); - return last != null ? last.getReturnType() : LattePhpType.MIXED; + return last != null ? last.getReturnType() : NettePhpType.MIXED; } - public static @NotNull LattePhpType getPhpType(@NotNull LattePhpTypedPartElement statement) { + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpTypedPartElement statement) { LattePhpTypeElement typeElement = statement.getPhpTypeElement(); - return typeElement == null ? LattePhpType.MIXED : typeElement.getPhpType(); + return typeElement == null ? NettePhpType.MIXED : typeElement.getPhpType(); } - public static @NotNull LattePhpType getPhpType(@NotNull LattePhpExpressionElement expression) { + public static int getPhpArrayLevel(@NotNull LattePhpExpressionElement expression) { + List statements = expression.getPhpStatementList(); + if (statements.size() > 0) { + BaseLattePhpElement phpElement = statements.get(statements.size() - 1).getLastPhpElement(); + return phpElement != null ? phpElement.getPhpArrayLevel() : 0; + } + return 0; + } + + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpExpression expression) { + return getPhpType((LattePhpExpressionElement) expression).withDepth(expression.getPhpArrayLevel()); + } + + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpExpressionElement expression) { List statements = expression.getPhpStatementList(); if (statements.size() > 0) { return statements.get(0).getPhpType(); } - return LattePhpType.MIXED; + return NettePhpType.MIXED; } public static @Nullable BaseLattePhpElement getLastPhpElement(@NotNull LattePhpStatement statement) { @@ -325,9 +345,9 @@ public static boolean isPhpClassReferenceOnly(@NotNull LattePhpStatement stateme return statement.getPhpStatementFirstPart().getPhpClassReference() != null && statement.getPhpStatementPartList().size() == 0; } - public static @NotNull LattePhpType getPhpType(@NotNull LattePhpStatementPartElement statement) { + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpStatementPartElement statement) { BaseLattePhpElement phpElement = statement.getPhpElement(); - return phpElement == null ? LattePhpType.MIXED : phpElement.getReturnType(); + return phpElement == null ? NettePhpType.MIXED : phpElement.getReturnType(); } public static boolean isStatic(@NotNull PsiElement element) { @@ -340,30 +360,30 @@ public static boolean isFunction(@NotNull PsiElement element) { return prev == null || (prev.getNode().getElementType() != T_PHP_DOUBLE_COLON && prev.getNode().getElementType() != T_PHP_OBJECT_OPERATOR); } - public static @NotNull LattePhpType getReturnType(@NotNull LattePhpVariable element) { + public static @NotNull NettePhpType getReturnType(@NotNull LattePhpVariable element) { return element.getPhpType(); } @NotNull - public static LattePhpType getReturnType(@NotNull LattePhpClassReference element) { + public static NettePhpType getReturnType(@NotNull LattePhpClassReference element) { return element.getPhpClassUsage().getPhpType(); } - public static @NotNull LattePhpType getReturnType(@NotNull LattePhpClassUsage element) { + public static @NotNull NettePhpType getReturnType(@NotNull LattePhpClassUsage element) { return element.getPhpType(); } - public static @NotNull LattePhpType getReturnType(@NotNull LattePhpNamespaceReference element) { + public static @NotNull NettePhpType getReturnType(@NotNull LattePhpNamespaceReference element) { return element.getPhpType(); } - public static @NotNull LattePhpType getReturnType(@NotNull LattePhpMethod element) { - LattePhpType type = element.getPhpType(); + public static @NotNull NettePhpType getReturnType(@NotNull LattePhpMethod element) { + NettePhpType type = element.getPhpType(); Collection phpClasses = type.getPhpClasses(element.getProject()); String name = element.getMethodName(); if (phpClasses.size() == 0) { LatteFunctionSettings customFunction = LatteConfiguration.getInstance(element.getProject()).getFunction(name); - return customFunction == null ? LattePhpType.MIXED : LattePhpType.create(customFunction.getFunctionReturnType()); + return customFunction == null ? NettePhpType.MIXED : NettePhpType.create(customFunction.getFunctionReturnType()); } List types = new ArrayList<>(); @@ -374,24 +394,24 @@ public static LattePhpType getReturnType(@NotNull LattePhpClassReference element } } } - return types.size() > 0 ? LattePhpType.create(types).withDepth(element.getPhpArrayLevel()) : LattePhpType.MIXED; + return types.size() > 0 ? NettePhpType.create(types).withDepth(element.getPhpArrayLevel()) : NettePhpType.MIXED; } - public static @NotNull LattePhpType getReturnType(@NotNull BaseLattePhpElement element) { + public static @NotNull NettePhpType getReturnType(@NotNull BaseLattePhpElement element) { Collection phpClasses = element.getPhpType().getPhpClasses(element.getProject()); if (phpClasses.size() == 0) { - return LattePhpType.MIXED; + return NettePhpType.MIXED; } List types = new ArrayList<>(); for (PhpClass phpClass : phpClasses) { for (Field field : phpClass.getFields()) { - if (field.getName().equals(LattePhpUtil.normalizePhpVariable(element.getPhpElementName()))) { + if (field.getName().equals(LattePhpVariableUtil.normalizePhpVariable(element.getPhpElementName()))) { types.add(field.getType()); } } } - return types.size() > 0 ? LattePhpType.create(types).withDepth(element.getPhpArrayLevel()) : LattePhpType.MIXED; + return types.size() > 0 ? NettePhpType.create(types).withDepth(element.getPhpArrayLevel()) : NettePhpType.MIXED; } public static @NotNull String getNamespaceName(LattePhpNamespaceReference namespaceReference) { @@ -413,16 +433,41 @@ public static LattePhpType getReturnType(@NotNull LattePhpClassReference element return LattePhpUtil.normalizeClassName(out.toString()); } - public static @NotNull LattePhpType getPhpType(@NotNull LattePhpClassUsage element) { - return LattePhpType.create(element.getClassName(), false); + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpClassUsage element) { + return NettePhpType.create(element.getClassName(), false); } - public static @NotNull LattePhpType getPhpType(@NotNull LattePhpClassReference element) { + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpClassReference element) { return element.getPhpClassUsage().getPhpType(); } - public static @NotNull LattePhpType getPhpType(@NotNull LattePhpNamespaceReference element) { - return LattePhpType.create(element.getNamespaceName(), false); + public static @NotNull NettePhpType getPhpType(@NotNull LattePhpNamespaceReference element) { + return NettePhpType.create(element.getNamespaceName(), false); + } + + @Nullable + public static LatteMacroTag getMacroOpenTag(@NotNull LattePairMacro element) { + return element.getMacroTagList().stream().findFirst().orElse(null); + } + + @Nullable + public static PsiElement getVariableContext(@NotNull LattePhpVariable element) { + return LattePhpVariableUtil.getCurrentContext(element); + } + + @Nullable + public static LatteHtmlOpenTag getHtmlOpenTag(@NotNull LatteHtmlTagContainer element) { + PsiElement prev = element.getPrevSibling(); + if (prev instanceof LatteHtmlOpenTag) { + return (LatteHtmlOpenTag) prev; + } + return element.getHtmlOpenTag(); + } + + @NotNull + public static String getHtmlTagName(@NotNull LatteHtmlOpenTag element) { + PsiElement child = findFirstChildWithType(element, T_HTML_OPEN_TAG_OPEN); + return child != null ? child.getText() : "?"; } public static boolean isTemplateType(@NotNull LattePhpClassUsage element) { @@ -430,73 +475,35 @@ public static boolean isTemplateType(@NotNull LattePhpClassUsage element) { } public static boolean isVarTypeDefinition(@NotNull LattePhpVariable element) { - return LatteUtil.matchParentMacroName(element, "varType"); + return LattePhpVariableUtil.isVarTypeDefinition(element); } public static boolean isCaptureDefinition(@NotNull LattePhpVariable element) { - return LatteUtil.matchParentMacroName(element, "capture"); + return LattePhpVariableUtil.isCaptureDefinition(element); } public static boolean isBlockDefineVarDefinition(@NotNull LattePhpVariable element) { - return LatteUtil.matchParentMacroName(element, "define"); + return LattePhpVariableUtil.isBlockDefineVarDefinition(element); } public static boolean isVarDefinition(@NotNull LattePhpVariable element) { - return LatteUtil.matchParentMacroName(element, "var") || LatteUtil.matchParentMacroName(element, "default"); + return LattePhpVariableUtil.isVarDefinition(element); } public static boolean isPhpArrayVarDefinition(@NotNull LattePhpVariable element) { - return LatteUtil.matchParentMacroName(element, "php") && element.getParent() instanceof LattePhpArrayOfVariables; + return LattePhpVariableUtil.isPhpArrayVarDefinition(element); } public static boolean isDefinitionInForeach(@NotNull PsiElement element) { - PsiElement parent = element.getParent(); - if (parent != null && parent.getNode().getElementType() == PHP_FOREACH) { - PsiElement prevElement = PsiTreeUtil.skipWhitespacesBackward(element); - IElementType type = prevElement != null ? prevElement.getNode().getElementType() : null; - return type == T_PHP_AS || type == T_PHP_DOUBLE_ARROW; - - } else if (parent != null && parent.getNode().getElementType() == PHP_ARRAY_OF_VARIABLES) { - return isDefinitionInForeach(parent); - } - return false; + return LattePhpVariableUtil.isDefinitionInForeach(element); } public static boolean isDefinitionInFor(@NotNull LattePhpVariable element) { - return LatteUtil.matchParentMacroName(element, "for") && LattePhpVariableUtil.isNextDefinitionOperator(element); + return LattePhpVariableUtil.isDefinitionInFor(element); } public static boolean isDefinition(@NotNull LattePhpVariable element) { - if ( - isVarTypeDefinition(element) - || LatteUtil.matchParentMacroName(element, "capture") - || isBlockDefineVarDefinition(element) - ) { - return true; - } - - if (isVarDefinition(element) || isPhpArrayVarDefinition(element)) { - if (LattePhpVariableUtil.isNextDefinitionOperator(element)) { - return true; - } - } - - PsiElement parent = element.getParent(); - if (parent == null) { - return false; - } - - if (parent.getNode().getElementType() == PHP_ARRAY_OF_VARIABLES) { - if (LattePhpVariableUtil.isNextDefinitionOperator(parent)) { - return true; - } - } - - if (isDefinitionInForeach(element)) { - return true; - } - - return isDefinitionInFor(element); + return LattePhpVariableUtil.isVariableDefinition(element); } public static String getName(LattePhpVariable element) { @@ -605,7 +612,7 @@ public static PsiElement getNameIdentifier(LattePhpVariable element) { public static String getName(LattePhpStaticVariable element) { PsiElement found = findFirstChildWithType(element, T_MACRO_ARGS_VAR); - return found != null ? LattePhpUtil.normalizePhpVariable(found.getText()) : null; + return found != null ? LattePhpVariableUtil.normalizePhpVariable(found.getText()) : null; } public static PsiElement getNameIdentifier(LattePhpStaticVariable element) { diff --git a/src/main/java/com/jantvrdik/intellij/latte/psi/impl/elements/LattePhpVariableElementImpl.java b/src/main/java/com/jantvrdik/intellij/latte/psi/impl/elements/LattePhpVariableElementImpl.java index 2b446bc..7159030 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/psi/impl/elements/LattePhpVariableElementImpl.java +++ b/src/main/java/com/jantvrdik/intellij/latte/psi/impl/elements/LattePhpVariableElementImpl.java @@ -1,6 +1,5 @@ package com.jantvrdik.intellij.latte.psi.impl.elements; -import com.intellij.extapi.psi.StubBasedPsiElementBase; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiReference; import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; diff --git a/src/main/java/com/jantvrdik/intellij/latte/reference/LatteReferenceContributor.java b/src/main/java/com/jantvrdik/intellij/latte/reference/LatteReferenceContributor.java index 6fa6dab..fd3324b 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/reference/LatteReferenceContributor.java +++ b/src/main/java/com/jantvrdik/intellij/latte/reference/LatteReferenceContributor.java @@ -26,6 +26,25 @@ public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNu return PsiReference.EMPTY_ARRAY; } + PsiElement value = ((LattePhpVariable) element).getTextElement(); + if (value != null && value.getTextLength() > 0) { + PsiReference reference = new LattePhpVariableReference((LattePhpVariable) element, new TextRange(0, value.getTextLength())); + return new PsiReference[]{reference}; + } + + return PsiReference.EMPTY_ARRAY; + } + }); + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(LatteTypes.PHP_VARIABLE), + new PsiReferenceProvider() { + @NotNull + @Override + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { + if (!(element instanceof LattePhpVariable) || !((LattePhpVariable) element).isDefinition()) { + return PsiReference.EMPTY_ARRAY; + } + PsiElement value = ((LattePhpVariable) element).getTextElement(); if (value != null && value.getTextLength() > 0) { return new PsiReference[]{ @@ -51,7 +70,7 @@ public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNu if (methodName != null && methodName.length() > 0) { int addition = element.getFirstChild().getNode().getElementType() == LatteTypes.T_PHP_NAMESPACE_RESOLUTION ? 1 : 0; return new PsiReference[]{ - new LattePhpMethodReference((LattePhpMethod) element, new TextRange(0 + addition, methodName.length() + addition)) + new LattePhpMethodReference((LattePhpMethod) element, new TextRange(addition, methodName.length() + addition)) }; } return PsiReference.EMPTY_ARRAY; diff --git a/src/main/java/com/jantvrdik/intellij/latte/reference/LatteReferenceSearch.java b/src/main/java/com/jantvrdik/intellij/latte/reference/LatteReferenceSearch.java index 9000e8d..cff0a5a 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/reference/LatteReferenceSearch.java +++ b/src/main/java/com/jantvrdik/intellij/latte/reference/LatteReferenceSearch.java @@ -8,14 +8,14 @@ import com.intellij.psi.search.*; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Processor; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.LattePhpClassUsage; import com.jantvrdik.intellij.latte.psi.LattePhpStaticVariable; import com.jantvrdik.intellij.latte.psi.LattePhpVariable; import com.jantvrdik.intellij.latte.reference.references.LattePhpClassReference; import com.jantvrdik.intellij.latte.reference.references.LattePhpStaticVariableReference; import com.jantvrdik.intellij.latte.reference.references.LattePhpVariableReference; -import com.jantvrdik.intellij.latte.utils.LattePhpType; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jantvrdik.intellij.latte.utils.LatteUtil; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.PhpClass; @@ -39,6 +39,11 @@ private void processClass(@NotNull PhpClass phpClass, @NotNull SearchScope searc ApplicationManager.getApplication().runReadAction(() -> { String fieldName = phpClass.getFQN(); + String searchString = fieldName.startsWith("\\") ? fieldName.substring(1) : fieldName; + if (searchString.length() == 0) { + return; + } + PsiSearchHelper.getInstance(phpClass.getProject()) .processElementsWithWord(new TextOccurenceProcessor() { @Override @@ -47,11 +52,10 @@ public boolean execute(@NotNull PsiElement psiElement, int i) { if (currentClass instanceof LattePhpClassUsage) { String value = ((LattePhpClassUsage) currentClass).getClassName(); processor.process(new LattePhpClassReference((LattePhpClassUsage) currentClass, new TextRange(0, value.length()))); - } return true; } - }, searchScope, fieldName.startsWith("\\") ? fieldName.substring(1) : fieldName, UsageSearchContext.IN_CODE, true); + }, searchScope, searchString, UsageSearchContext.IN_CODE, true); }); } @@ -72,16 +76,12 @@ public boolean execute(PsiElement psiElement, int i) { processor.process(new LattePhpStaticVariableReference((LattePhpStaticVariable) currentMethod, new TextRange(0, value.length() + 1))); } else if (currentMethod instanceof LattePhpVariable && field.getContainingClass() != null) { - LattePhpType type = LatteUtil.findFirstLatteTemplateType(currentMethod.getContainingFile()); + NettePhpType type = LatteUtil.findFirstLatteTemplateType(currentMethod.getContainingFile()); if (type == null) { return true; } Collection classes = type.getPhpClasses(psiElement.getProject()); - if (classes == null) { - return true; - } - for (PhpClass phpClass : classes) { if (LattePhpUtil.isReferenceFor(field.getContainingClass(), phpClass)) { String value = ((LattePhpVariable) currentMethod).getVariableName(); diff --git a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LatteNamespaceReference.java b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LatteNamespaceReference.java index debad34..64da314 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LatteNamespaceReference.java +++ b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LatteNamespaceReference.java @@ -4,7 +4,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.jantvrdik.intellij.latte.psi.LattePhpNamespaceReference; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.lang.psi.elements.PhpNamespace; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpClassReference.java b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpClassReference.java index 60a89b1..b8df841 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpClassReference.java +++ b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpClassReference.java @@ -6,7 +6,7 @@ import com.jantvrdik.intellij.latte.indexes.LatteIndexUtil; import com.jantvrdik.intellij.latte.indexes.extensions.LattePhpClassIndex; import com.jantvrdik.intellij.latte.psi.LattePhpClassUsage; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.lang.psi.elements.PhpClass; import com.jetbrains.php.lang.psi.elements.PhpNamespace; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpConstantReference.java b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpConstantReference.java index 7b3b165..3573165 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpConstantReference.java +++ b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpConstantReference.java @@ -5,7 +5,7 @@ import com.jantvrdik.intellij.latte.indexes.LatteIndexUtil; import com.jantvrdik.intellij.latte.psi.LattePhpConstant; import com.jantvrdik.intellij.latte.psi.elements.BaseLattePhpElement; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.PhpClass; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpMethodReference.java b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpMethodReference.java index 3d2c689..c45df92 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpMethodReference.java +++ b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpMethodReference.java @@ -8,7 +8,7 @@ import com.jantvrdik.intellij.latte.config.LatteFileConfiguration; import com.jantvrdik.intellij.latte.indexes.LatteIndexUtil; import com.jantvrdik.intellij.latte.psi.LattePhpMethod; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.lang.psi.elements.Function; import com.jetbrains.php.lang.psi.elements.Method; import com.jetbrains.php.lang.psi.elements.PhpClass; diff --git a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpPropertyReference.java b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpPropertyReference.java index 0c11c56..cb7dab2 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpPropertyReference.java +++ b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpPropertyReference.java @@ -5,7 +5,7 @@ import com.jantvrdik.intellij.latte.indexes.LatteIndexUtil; import com.jantvrdik.intellij.latte.psi.LattePhpProperty; import com.jantvrdik.intellij.latte.psi.elements.BaseLattePhpElement; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.PhpClass; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpStaticVariableReference.java b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpStaticVariableReference.java index 8bfc5de..06d301f 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpStaticVariableReference.java +++ b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpStaticVariableReference.java @@ -3,9 +3,10 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.jantvrdik.intellij.latte.indexes.LatteIndexUtil; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; import com.jantvrdik.intellij.latte.psi.LattePhpStaticVariable; import com.jantvrdik.intellij.latte.psi.elements.BaseLattePhpElement; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.php.LattePhpUtil; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.PhpClass; import org.jetbrains.annotations.NotNull; @@ -16,8 +17,8 @@ import java.util.List; public class LattePhpStaticVariableReference extends PsiReferenceBase implements PsiPolyVariantReference { - private String variableName; - private Collection phpClasses; + private final String variableName; + private final Collection phpClasses; public LattePhpStaticVariableReference(@NotNull LattePhpStaticVariable element, TextRange textRange) { super(element, textRange); @@ -67,7 +68,7 @@ public Object[] getVariants() { @NotNull @Override public String getCanonicalText() { - return LattePhpUtil.normalizePhpVariable(getElement().getText()); + return LattePhpVariableUtil.normalizePhpVariable(getElement().getText()); } @Override diff --git a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpVariableReference.java b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpVariableReference.java index b36b7b3..5455a03 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpVariableReference.java +++ b/src/main/java/com/jantvrdik/intellij/latte/reference/references/LattePhpVariableReference.java @@ -6,12 +6,12 @@ import com.intellij.psi.*; import com.intellij.psi.xml.XmlAttributeValue; import com.jantvrdik.intellij.latte.config.LatteFileConfiguration; +import com.jantvrdik.intellij.latte.php.LattePhpVariableUtil; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.*; import com.jantvrdik.intellij.latte.psi.elements.BaseLattePhpElement; -import com.jantvrdik.intellij.latte.utils.LattePhpType; -import com.jantvrdik.intellij.latte.utils.LattePhpUtil; +import com.jantvrdik.intellij.latte.utils.LattePhpVariableDefinition; import com.jantvrdik.intellij.latte.utils.LatteUtil; -import com.jantvrdik.intellij.latte.utils.PsiPositionedElement; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.PhpClass; import org.jetbrains.annotations.*; @@ -20,21 +20,20 @@ public class LattePhpVariableReference extends PsiReferenceBase implements PsiPolyVariantReference { - private String variableName; - private boolean definition; + private final String variableName; public LattePhpVariableReference(@NotNull LattePhpVariable element, TextRange textRange) { super(element, textRange); variableName = element.getVariableName(); - definition = element.isDefinition(); } @NotNull @Override public ResolveResult[] multiResolve(boolean incompleteCode) { - if (getElement().getContainingFile().getVirtualFile() == null) { + if (!(getElement() instanceof LattePhpVariable || getElement().getContainingFile().getVirtualFile() == null)) { return new ResolveResult[0]; } + LattePhpVariable variable = (LattePhpVariable) getElement(); Project project = getElement().getProject(); List results = new ArrayList<>(); @@ -42,7 +41,7 @@ public ResolveResult[] multiResolve(boolean incompleteCode) { results.add(new PsiElementResolveResult(attributeValue)); } - LattePhpType fields = LatteUtil.findFirstLatteTemplateType(getElement().getContainingFile()); + NettePhpType fields = LatteUtil.findFirstLatteTemplateType(getElement().getContainingFile()); String name = ((BaseLattePhpElement) getElement()).getPhpElementName(); if (fields != null) { for (PhpClass phpClass : fields.getPhpClasses(project)) { @@ -54,26 +53,9 @@ public ResolveResult[] multiResolve(boolean incompleteCode) { } } - //todo: complete resolving for variables - //final List variables = LatteUtil.findVariablesInFileBeforeElement(getElement(), getElement().getContainingFile().getVirtualFile(), variableName); - final List variables = LatteUtil.findVariablesInFile(getElement().getProject(), getElement().getContainingFile().getVirtualFile(), variableName); - /*if (!(getElement() instanceof LattePhpVariable)) { - return new ResolveResult[0]; - } - - final List variables; - if (((LattePhpVariable) getElement()).isVarTypeDefinition()) { - variables = LatteUtil.findVariablesInFileAfterElement(getElement(), getElement().getContainingFile().getVirtualFile(), variableName); - } else { - variables = LatteUtil.findVariablesInFileBeforeElement(getElement(), getElement().getContainingFile().getVirtualFile(), variableName); - }*/ - - for (PsiPositionedElement variable : variables) { - PsiElement var = variable.getElement(); - if (!(var instanceof LattePhpVariable) || !((LattePhpVariable) var).isDefinition()) { - continue; - } - results.add(new PsiElementResolveResult(variable.getElement())); + final List variables = LattePhpVariableUtil.getVariableDefinition(variable); + for (LattePhpVariableDefinition variableDefinition : variables) { + results.add(new PsiElementResolveResult(variableDefinition.getElement())); } return results.toArray(new ResolveResult[0]); } @@ -99,7 +81,7 @@ public Object[] getVariants() { @NotNull @Override public String getCanonicalText() { - return LattePhpUtil.normalizePhpVariable(getElement().getText()); + return LattePhpVariableUtil.normalizePhpVariable(getElement().getText()); } @Override @@ -112,9 +94,8 @@ public PsiElement handleElementRename(@NotNull String newName) { @Override public boolean isReferenceTo(@NotNull PsiElement element) { - if (element instanceof LattePhpVariable) { - return (!definition || !((LattePhpVariable) element).isDefinition()) - && ((LattePhpVariable) element).getVariableName().equals(variableName); + if (element instanceof LattePhpVariable && ((LattePhpVariable) element).isDefinition()) { + return ((LattePhpVariable) element).getVariableName().equals(variableName); } PsiElement currentElement = element; diff --git a/src/main/java/com/jantvrdik/intellij/latte/settings/LatteTagSettings.java b/src/main/java/com/jantvrdik/intellij/latte/settings/LatteTagSettings.java index 09ff9ec..8a8f303 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/settings/LatteTagSettings.java +++ b/src/main/java/com/jantvrdik/intellij/latte/settings/LatteTagSettings.java @@ -3,6 +3,7 @@ import com.intellij.util.xmlb.annotations.Attribute; import com.jantvrdik.intellij.latte.config.LatteConfiguration; import org.apache.commons.lang.builder.HashCodeBuilder; +import org.jetbrains.annotations.NotNull; import java.io.Serializable; import java.util.*; @@ -147,6 +148,27 @@ public String getArgumentsInfo() { return ""; } + public List getAllowedNetteAttributes() { + return createNetteAttributes( + getMacroName(), + getType() != LatteTagSettings.Type.UNPAIRED, + getType() == LatteTagSettings.Type.PAIR || getMacroName().equals("block") + ); + } + + public static List createNetteAttributes(String tagName, boolean allowed, boolean pair) { + if (!allowed) { + return Collections.emptyList(); + } + List out = new ArrayList<>(); + out.add("n:" + tagName); + if (pair) { + out.add("n:tag-" + tagName); + out.add("n:inner-" + tagName); + } + return out; + } + public boolean hasParameters() { return (arguments != null && arguments.length() > 0) || argumentSettings.size() > 0; } diff --git a/src/main/java/com/jantvrdik/intellij/latte/settings/LatteVariableSettings.java b/src/main/java/com/jantvrdik/intellij/latte/settings/LatteVariableSettings.java index fe01790..7a1c526 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/settings/LatteVariableSettings.java +++ b/src/main/java/com/jantvrdik/intellij/latte/settings/LatteVariableSettings.java @@ -2,7 +2,7 @@ import com.intellij.util.xmlb.annotations.Attribute; import com.jantvrdik.intellij.latte.config.LatteConfiguration; -import com.jantvrdik.intellij.latte.utils.LattePhpType; +import com.jantvrdik.intellij.latte.php.NettePhpType; import org.apache.commons.lang.builder.HashCodeBuilder; import java.io.Serializable; @@ -35,8 +35,8 @@ public void setVarType(String varType) { this.varType = varType; } - public LattePhpType toPhpType() { - return LattePhpType.create(varName, varType); + public NettePhpType toPhpType() { + return NettePhpType.create(varName, varType); } @Attribute("VarName") diff --git a/src/main/java/com/jantvrdik/intellij/latte/syntaxHighlighter/LatteColorSettingsPage.java b/src/main/java/com/jantvrdik/intellij/latte/syntaxHighlighter/LatteColorSettingsPage.java index 07ae19c..6028ae5 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/syntaxHighlighter/LatteColorSettingsPage.java +++ b/src/main/java/com/jantvrdik/intellij/latte/syntaxHighlighter/LatteColorSettingsPage.java @@ -14,25 +14,25 @@ public class LatteColorSettingsPage implements ColorSettingsPage { private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[] { - new AttributesDescriptor("Tag Name", LatteSyntaxHighlighter.MACRO_NAME), - new AttributesDescriptor("Tag Filters", LatteSyntaxHighlighter.MACRO_MODIFIERS), - new AttributesDescriptor("Tag Delimiters", LatteSyntaxHighlighter.MACRO_DELIMITERS), - new AttributesDescriptor("Tag Comment", LatteSyntaxHighlighter.MACRO_COMMENT), + new AttributesDescriptor("Tag name", LatteSyntaxHighlighter.MACRO_NAME), + new AttributesDescriptor("Tag filters", LatteSyntaxHighlighter.MACRO_MODIFIERS), + new AttributesDescriptor("Tag delimiters", LatteSyntaxHighlighter.MACRO_DELIMITERS), + new AttributesDescriptor("Tag comment", LatteSyntaxHighlighter.MACRO_COMMENT), new AttributesDescriptor("Block name", LatteSyntaxHighlighter.MACRO_BLOCK_NAME), new AttributesDescriptor("Destination link", LatteSyntaxHighlighter.MACRO_LINK_DESTINATION), new AttributesDescriptor("Attribute n:tag Name", LatteSyntaxHighlighter.HTML_NATTR_NAME), new AttributesDescriptor("Attribute n:tag Value", LatteSyntaxHighlighter.HTML_NATTR_VALUE), - new AttributesDescriptor("PHP Variable", LatteSyntaxHighlighter.MACRO_ARGS_VAR), - new AttributesDescriptor("PHP String", LatteSyntaxHighlighter.MACRO_ARGS_STRING), - new AttributesDescriptor("PHP Number", LatteSyntaxHighlighter.MACRO_ARGS_NUMBER), - new AttributesDescriptor("PHP Class", LatteSyntaxHighlighter.PHP_CLASS_NAME), - new AttributesDescriptor("PHP Method, Function", LatteSyntaxHighlighter.PHP_METHOD), - new AttributesDescriptor("PHP Keyword", LatteSyntaxHighlighter.PHP_KEYWORD), - new AttributesDescriptor("PHP Property, Constant", LatteSyntaxHighlighter.PHP_IDENTIFIER), - new AttributesDescriptor("PHP Cast", LatteSyntaxHighlighter.PHP_CAST), - new AttributesDescriptor("PHP Type", LatteSyntaxHighlighter.PHP_TYPE), - new AttributesDescriptor("PHP Null", LatteSyntaxHighlighter.PHP_NULL), - new AttributesDescriptor("Content Type", LatteSyntaxHighlighter.PHP_CONTENT_TYPE), + new AttributesDescriptor("PHP variable", LatteSyntaxHighlighter.MACRO_ARGS_VAR), + new AttributesDescriptor("PHP string", LatteSyntaxHighlighter.MACRO_ARGS_STRING), + new AttributesDescriptor("PHP number", LatteSyntaxHighlighter.MACRO_ARGS_NUMBER), + new AttributesDescriptor("PHP class", LatteSyntaxHighlighter.PHP_CLASS_NAME), + new AttributesDescriptor("PHP method, function", LatteSyntaxHighlighter.PHP_METHOD), + new AttributesDescriptor("PHP keyword", LatteSyntaxHighlighter.PHP_KEYWORD), + new AttributesDescriptor("PHP property, constant", LatteSyntaxHighlighter.PHP_IDENTIFIER), + new AttributesDescriptor("PHP cast", LatteSyntaxHighlighter.PHP_CAST), + new AttributesDescriptor("PHP type", LatteSyntaxHighlighter.PHP_TYPE), + new AttributesDescriptor("PHP null", LatteSyntaxHighlighter.PHP_NULL), + new AttributesDescriptor("Content type", LatteSyntaxHighlighter.PHP_CONTENT_TYPE), new AttributesDescriptor("Assignment Operator (=, +=, ...)", LatteSyntaxHighlighter.PHP_ASSIGNMENT_OPERATOR), new AttributesDescriptor("Logic Operator (&&, ||)", LatteSyntaxHighlighter.PHP_LOGIC_OPERATOR), new AttributesDescriptor("Other PHP Operators", LatteSyntaxHighlighter.PHP_OPERATOR), diff --git a/src/main/java/com/jantvrdik/intellij/latte/ui/PhpTypeColumn.java b/src/main/java/com/jantvrdik/intellij/latte/ui/PhpTypeColumn.java index 362b968..979886d 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/ui/PhpTypeColumn.java +++ b/src/main/java/com/jantvrdik/intellij/latte/ui/PhpTypeColumn.java @@ -5,7 +5,7 @@ import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.ui.ColumnInfo; -import com.jantvrdik.intellij.latte.utils.LattePhpType; +import com.jantvrdik.intellij.latte.php.NettePhpType; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -31,7 +31,7 @@ protected void customizeCellRenderer(JTable table, Object value, return; } - LattePhpType type = LattePhpType.create((String) value); + NettePhpType type = NettePhpType.create((String) value); if (type.hasUndefinedClass(project)) { append((String) value, new SimpleTextAttributes(Font.PLAIN, JBColor.RED)); } else { diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpVariableDefinition.java b/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpVariableDefinition.java new file mode 100644 index 0000000..93985f9 --- /dev/null +++ b/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpVariableDefinition.java @@ -0,0 +1,23 @@ +package com.jantvrdik.intellij.latte.utils; + +import com.jantvrdik.intellij.latte.psi.LattePhpVariable; +import org.jetbrains.annotations.NotNull; + +public class LattePhpVariableDefinition { + + private final boolean probablyUndefined; + private final LattePhpVariable element; + + public LattePhpVariableDefinition(boolean probablyUndefined, @NotNull LattePhpVariable element) { + this.probablyUndefined = probablyUndefined; + this.element = element; + } + + public boolean isProbablyUndefined() { + return probablyUndefined; + } + + public LattePhpVariable getElement() { + return element; + } +} \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpVariableUtil.java b/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpVariableUtil.java deleted file mode 100644 index 4764ed5..0000000 --- a/src/main/java/com/jantvrdik/intellij/latte/utils/LattePhpVariableUtil.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.jantvrdik.intellij.latte.utils; - -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.PsiTreeUtil; -import com.jantvrdik.intellij.latte.config.LatteConfiguration; -import com.jantvrdik.intellij.latte.psi.*; -import com.jantvrdik.intellij.latte.psi.elements.LattePhpStatementPartElement; -import com.jantvrdik.intellij.latte.psi.elements.LattePhpTypedPartElement; -import com.jantvrdik.intellij.latte.settings.LatteVariableSettings; -import com.jetbrains.php.lang.psi.elements.Field; -import com.jetbrains.php.lang.psi.elements.PhpClass; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import static com.jantvrdik.intellij.latte.psi.LatteTypes.*; - -public class LattePhpVariableUtil { - public static LattePhpType detectVariableType(@NotNull LattePhpVariable element) { - String variableName = element.getVariableName(); - List all = LatteUtil.findVariablesInFileBeforeElement( - element, - element.getContainingFile().getOriginalFile().getVirtualFile(), - element.getVariableName() - ); - List definitions = all.stream().filter( - psiPositionedElement -> psiPositionedElement.getElement() instanceof LattePhpVariable - && ((LattePhpVariable) psiPositionedElement.getElement()).isDefinition() - ).collect(Collectors.toList()); - - LattePhpStatementPartElement mainStatementPart = element.getPhpStatementPart(); - - for (PsiPositionedElement positionedElement : definitions) { - if (!(positionedElement.getElement() instanceof LattePhpVariable)) { - continue; - } - - PsiElement current = positionedElement.getElement(); - if ( - ((LattePhpVariable) current).isVarTypeDefinition() - || ((LattePhpVariable) current).isVarDefinition() - || ((LattePhpVariable) current).isPhpArrayVarDefinition() - || ((LattePhpVariable) current).isCaptureDefinition() - || ((LattePhpVariable) current).isBlockDefineVarDefinition() - || ((LattePhpVariable) current).isDefinitionInForeach() - ) { - int startDepth = 0; - if (!(current.getParent() instanceof LattePhpArrayOfVariables)) { - LattePhpType prevPhpType = findPrevPhpType((LattePhpVariable) current); - if (prevPhpType != null) { - return prevPhpType; - } - } else { - startDepth = 1; - } - - if (((LattePhpVariable) current).isDefinitionInForeach()) { - PsiElement nextElement = PsiTreeUtil.skipWhitespacesForward(current); - IElementType type = nextElement != null ? nextElement.getNode().getElementType() : null; - if (type != T_PHP_DOUBLE_ARROW) { - LattePhpForeach phpForeach = PsiTreeUtil.getParentOfType(current, LattePhpForeach.class); - if (phpForeach != null && phpForeach.getPhpExpression().getPhpStatementList().size() > 0) { - return phpForeach.getPhpExpression().getPhpType().withDepth(startDepth + 1); - } - } - } - - LattePhpStatementPartElement statementPart = ((LattePhpVariable) current).getPhpStatementPart(); - LattePhpContent phpContent = PsiTreeUtil.getParentOfType(current, LattePhpContent.class); - if (phpContent != null && statementPart != mainStatementPart) { - return detectVariableType(phpContent, startDepth); - } - return LattePhpType.MIXED; - } - } - - LattePhpType templateType = detectVariableTypeFromTemplateType(element, variableName); - if (templateType != null) { - return templateType; - } - - LatteVariableSettings defaultVariable = LatteConfiguration.getInstance(element.getProject()).getVariable(variableName); - if (defaultVariable != null) { - return defaultVariable.toPhpType(); - } - - return LattePhpType.MIXED; - } - - public static List findPhpFiledListFromTemplateTypeTag(@NotNull PsiElement element, @NotNull String variableName) { - if (!(element.getContainingFile() instanceof LatteFile)) { - return Collections.emptyList(); - } - - LattePhpType templateType = LatteUtil.findFirstLatteTemplateType(element.getContainingFile()); - if (templateType == null) { - return Collections.emptyList(); - } - - Collection classes = templateType.getPhpClasses(element.getProject()); - if (classes == null) { - return Collections.emptyList(); - } - - List out = new ArrayList<>(); - for (PhpClass phpClass : classes) { - for (Field field : phpClass.getFields()) { - if (!field.isConstant() && field.getModifier().isPublic() && variableName.equals(field.getName())) { - out.add(field); - } - } - } - return out; - } - - @Nullable - public static LattePhpType detectVariableTypeFromTemplateType(@NotNull PsiElement element, @NotNull String variableName) { - List fields = findPhpFiledListFromTemplateTypeTag(element, variableName); - if (fields.size() == 0) { - return null; - } - Field field = fields.get(0); - return LattePhpType.create(field.getName(), field.getType().toString(), LattePhpUtil.isNullable(field.getType())); - } - - private static LattePhpType detectVariableType(@NotNull LattePhpContent phpContent, int startDepth) { - final PsiElement[] varDefinition = {null}; - final boolean[] varDefinitionOperator = {false}; - List otherParts = new ArrayList<>(); - phpContent.acceptChildren(new PsiElementVisitor() { - @Override - public void visitElement(@NotNull PsiElement element) { - if ( - element instanceof LattePhpStatement && ((LattePhpStatement) element).isPhpVariableOnly() - || element instanceof LattePhpArrayOfVariables - ) { - varDefinition[0] = element; - - } else if (varDefinition[0] != null && element.getNode().getElementType() == LatteTypes.T_PHP_DEFINITION_OPERATOR) { - varDefinitionOperator[0] = true; - - } else if ( - !(element instanceof LatteMacroModifier) - && !LatteTypesUtil.whitespaceTokens.contains(element.getNode().getElementType()) - && varDefinitionOperator[0] - ) { - otherParts.add(element); - } - } - }); - - if (otherParts.size() == 1 && otherParts.get(0) instanceof LattePhpStatement) { - return ((LattePhpStatement) otherParts.get(0)).getPhpType().withDepth(startDepth); - } - - if (startDepth > 0) { - return LattePhpType.MIXED; - - } else if ( - otherParts.stream().anyMatch(element -> element instanceof LattePhpString - || element.getNode().getElementType() == T_PHP_CONCATENATION) - ) { - return LattePhpType.STRING; - - } else if (otherParts.stream().anyMatch(element -> element.getNode().getElementType() == T_MACRO_ARGS_NUMBER)) { - return LattePhpType.INT; - - } else if (otherParts.stream().anyMatch(element -> element instanceof LattePhpArray || element instanceof LattePhpArrayOfVariables)) { - return LattePhpType.ARRAY; - } - - return LattePhpType.MIXED; - } - - public static boolean isNextDefinitionOperator(@NotNull PsiElement element) { - PsiElement found = null; - if (element instanceof LattePhpVariable && ((LattePhpVariable) element).getPhpStatementPart() != null) { - found = ((LattePhpVariable) element).getPhpStatementPart().getPhpStatement(); - } else if (element.getParent() instanceof LattePhpArrayOfVariables) { - found = element.getParent(); - } - - if (found == null) { - LattePhpTypedArguments typedArgs = PsiTreeUtil.getParentOfType(element, LattePhpTypedArguments.class); - found = typedArgs != null ? typedArgs : element; - } - - PsiElement nextElement = PsiTreeUtil.skipWhitespacesForward(found); - return nextElement != null && nextElement.getNode().getElementType() == LatteTypes.T_PHP_DEFINITION_OPERATOR; - } - - @Nullable - private static LattePhpType findPrevPhpType(LattePhpVariable element) { - LattePhpTypedPartElement typedElement = PsiTreeUtil.getParentOfType(element, LattePhpTypedPartElement.class); - if (typedElement != null) { - return typedElement.getPhpType(); - } - LattePhpStatementPartElement statementPart = element.getPhpStatementPart(); - if (statementPart == null) { - return null; - } - PsiElement phpTypeElement = PsiTreeUtil.skipWhitespacesBackward(statementPart.getPhpStatement()); - return phpTypeElement instanceof LattePhpTypeElement - ? ((LattePhpTypeElement) phpTypeElement).getPhpType() - : (phpTypeElement instanceof LattePhpStatement ? ((LattePhpStatement) phpTypeElement).getPhpType() : null); - } - -} \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/LatteReparseFilesUtil.java b/src/main/java/com/jantvrdik/intellij/latte/utils/LatteReparseFilesUtil.java index 2321b71..4cc3cb1 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/utils/LatteReparseFilesUtil.java +++ b/src/main/java/com/jantvrdik/intellij/latte/utils/LatteReparseFilesUtil.java @@ -28,7 +28,7 @@ public static void notifyRemovedFiles(List projects) { "File latte-intellij.xml was removed. You can refresh configurations.", NotificationType.INFORMATION, null, - new NotificationAction("Refresh Configuration") { + new NotificationAction("Refresh configuration") { @Override public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification notification) { tryPerform(projects.toArray(new Project[0]), notification); @@ -82,7 +82,7 @@ public static Notification notifyReparseFiles(Project project) { NotificationType.WARNING, project, true, - new NotificationAction("Reparse Latte Files") { + new NotificationAction("Reparse latte files") { @Override public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification current) { tryPerform(new Project[]{project}, current); @@ -98,7 +98,7 @@ public static Notification notifyDefaultReparse(Project project) { NotificationType.WARNING, project, true, - new NotificationAction("Reparse Latte Files") { + new NotificationAction("Reparse latte files") { @Override public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification current) { tryPerformReadLock(new Project[]{project}, current); @@ -151,7 +151,7 @@ private static void showWaring(Project[] projects) { "Latte files can not be reparsed during indexing. Wait after all processes around indexing will be done.", NotificationType.ERROR, projects[0], - new NotificationAction("Refresh Configuration") { + new NotificationAction("Refresh configuration") { @Override public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification notification) { tryPerform(projects, notification); diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/LatteTagsUtil.java b/src/main/java/com/jantvrdik/intellij/latte/utils/LatteTagsUtil.java new file mode 100644 index 0000000..93de14a --- /dev/null +++ b/src/main/java/com/jantvrdik/intellij/latte/utils/LatteTagsUtil.java @@ -0,0 +1,90 @@ +package com.jantvrdik.intellij.latte.utils; + +import com.jantvrdik.intellij.latte.settings.LatteTagSettings; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LatteTagsUtil { + + public enum Type { + + BLOCK("block"), + CAPTURE("capture"), + DEFAULT("default"), + DEFINE("define"), + DO("do"), + FOREACH("foreach"), + ELSE("else"), + ELSEIF("elseif"), + ELSEIFSET("elseifset"), + FOR("for"), + IF("if"), + IFCHANGED("ifchanged"), + IFSET("ifset"), + LINK("link"), + N_HREF("n:href"), + PHP("php"), + PLINK("plink"), + SNIPPET("snippet"), + SNIPPETAREA("snippetArea"), + TEMPLATE_TYPE("templateType"), + VAR("var"), + VAR_TYPE("varType"), + WHILE("while"); + + final private String tagName; + + Type(@NotNull String tagName) { + this.tagName = tagName; + } + + public String getTagName() { + return tagName; + } + + } + + public static final List LINK_TAGS_LIST = Arrays.asList( + Type.LINK.getTagName(), + Type.PLINK.getTagName(), + Type.N_HREF.getTagName() + ); + public static final List CONTEXT_TAGS_LIST = Arrays.asList( + Type.FOREACH.getTagName(), + Type.FOR.getTagName(), + Type.BLOCK.getTagName(), + Type.DEFINE.getTagName(), + Type.SNIPPET.getTagName(), + Type.SNIPPETAREA.getTagName(), + Type.IF.getTagName(), + Type.IFCHANGED.getTagName(), + Type.ELSE.getTagName(), + Type.ELSEIF.getTagName(), + Type.ELSEIFSET.getTagName(), + Type.WHILE.getTagName(), + Type.IFSET.getTagName() + ); + public static final List TYPE_TAGS_LIST = Arrays.asList(Type.VAR_TYPE.getTagName(), Type.TEMPLATE_TYPE.getTagName(), Type.VAR.getTagName()); + + private static List CONTEXT_NETTE_ATTRIBUTES_LIST = null; + + public static boolean isContextTag(@NotNull String tagName) { + return CONTEXT_TAGS_LIST.contains(tagName); + } + + public static boolean isContextNetteAttribute(@NotNull String tagName) { + if (CONTEXT_NETTE_ATTRIBUTES_LIST == null) { + CONTEXT_NETTE_ATTRIBUTES_LIST = new ArrayList<>(); + for (String currentTagName : CONTEXT_TAGS_LIST) { + CONTEXT_NETTE_ATTRIBUTES_LIST.addAll( + LatteTagSettings.createNetteAttributes(currentTagName, true, true) + ); + } + } + return CONTEXT_NETTE_ATTRIBUTES_LIST.contains(tagName); + } + +} \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/LatteTypesUtil.java b/src/main/java/com/jantvrdik/intellij/latte/utils/LatteTypesUtil.java index ed7e096..dab1e63 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/utils/LatteTypesUtil.java +++ b/src/main/java/com/jantvrdik/intellij/latte/utils/LatteTypesUtil.java @@ -21,7 +21,19 @@ public class LatteTypesUtil { final public static TokenSet whitespaceTokens = TokenSet.create(LatteTypes.T_WHITESPACE, TokenType.WHITE_SPACE); - final public static TokenSet methodTokens = TokenSet.create(LatteTypes.T_PHP_IDENTIFIER, LatteTypes.T_PHP_NAMESPACE_REFERENCE); + final public static TokenSet methodTokens = TokenSet.create(LatteTypes.T_PHP_IDENTIFIER, LatteTypes.T_PHP_NAMESPACE_REFERENCE, LatteTypes.PHP_VARIABLE); + + final public static TokenSet phpTypeTokens = TokenSet.create( + LatteTypes.T_PHP_TYPE, + LatteTypes.T_PHP_NULL, + LatteTypes.T_PHP_MIXED, + LatteTypes.T_PHP_NAMESPACE_RESOLUTION, + LatteTypes.T_PHP_NAMESPACE_REFERENCE, + LatteTypes.T_PHP_IDENTIFIER, + LatteTypes.T_PHP_OR_INCLUSIVE, + LatteTypes.T_PHP_LEFT_BRACKET, + LatteTypes.T_PHP_RIGHT_BRACKET + ); public static String[] getNativeClassConstants() { return nativeClassConstants; diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/LatteUtil.java b/src/main/java/com/jantvrdik/intellij/latte/utils/LatteUtil.java index 467a280..d4904f8 100644 --- a/src/main/java/com/jantvrdik/intellij/latte/utils/LatteUtil.java +++ b/src/main/java/com/jantvrdik/intellij/latte/utils/LatteUtil.java @@ -2,89 +2,25 @@ import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiElement; +import com.jantvrdik.intellij.latte.php.NettePhpType; import com.jantvrdik.intellij.latte.psi.*; -import com.jantvrdik.intellij.latte.psi.elements.BaseLattePhpElement; -import com.jetbrains.php.lang.psi.elements.PhpClass; import org.jetbrains.annotations.NotNull; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.intellij.psi.search.*; import com.intellij.psi.util.PsiTreeUtil; -import com.jantvrdik.intellij.latte.LatteFileType; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.stream.Collectors; public class LatteUtil { - public static List findVariablesDefinitionsInFileBeforeElement(@NotNull PsiElement element, @NotNull VirtualFile virtualFile) { - return findVariablesDefinitionsInFileBeforeElement(element, virtualFile, null); - } - - public static List findVariablesDefinitionsInFileBeforeElement(@NotNull PsiElement element, @NotNull VirtualFile virtualFile, @Nullable String key) { - List variables = findVariablesInFileBeforeElement(element, virtualFile, key); - - return variables.stream() - .filter(variableElement -> variableElement.getElement() instanceof LattePhpVariable && ((LattePhpVariable) variableElement.getElement()).isDefinition()) - .collect(Collectors.toList()); - } - - public static List findVariablesInFileBeforeElement(@NotNull PsiElement element, @NotNull VirtualFile virtualFile, @Nullable String key) { - List variables = findVariablesInFile(element.getProject(), virtualFile, key); - - int offset = getStartOffsetInFile(element); - return variables.stream() - .filter(variableElement -> variableElement.getPosition() <= offset) - .collect(Collectors.toList()); - } - - public static List findVariablesInFileAfterElement(@NotNull PsiElement element, @NotNull VirtualFile virtualFile, @Nullable String key) { - List variables = findVariablesInFile(element.getProject(), virtualFile, key); - - int offset = getStartOffsetInFile(element); - return variables.stream() - .filter(variableElement -> variableElement.getPosition() >= offset) - .collect(Collectors.toList()); - } - - public static List findVariablesInFile(@NotNull Project project, @NotNull VirtualFile file, @Nullable String key) { - List result = null; - PsiFile simpleFile = PsiManager.getInstance(project).findFile(file); - if (simpleFile != null) { - List properties = new ArrayList<>(); - for (PsiElement element : simpleFile.getChildren()) { - findLattePhpVariables(properties, element); - } - - for (PsiPositionedElement variable : properties) { - if (!(variable.getElement() instanceof LattePhpVariable)) { - continue; - } - - String varName = ((LattePhpVariable) variable.getElement()).getVariableName(); - if (key == null || key.equals(varName)) { - if (result == null) { - result = new ArrayList<>(); - } - result.add(variable); - } - } - } - return result != null ? result : Collections.emptyList(); - } - public static boolean matchParentMacroName(@NotNull PsiElement element, @NotNull String name) { - LatteMacroClassic macroClassic = PsiTreeUtil.getParentOfType(element, LatteMacroClassic.class); - if (macroClassic != null) { - return macroClassic.getOpenTag().getMacroName().equals(name); - } - LatteNetteAttr netteAttr = PsiTreeUtil.getParentOfType(element, LatteNetteAttr.class); - if (netteAttr == null) { + PsiElement foundElement = PsiTreeUtil.getParentOfType(element, LatteMacroClassic.class, LatteNetteAttr.class); + if (foundElement instanceof LatteMacroClassic) { + return ((LatteMacroClassic) foundElement).getOpenTag().getMacroName().equals(name); + } else if (foundElement == null) { return false; } - String attributeName = netteAttr.getAttrName().getText(); + String attributeName = ((LatteNetteAttr) foundElement).getAttrName().getText(); return attributeName.equals("n:" + name) || attributeName.equals("n:tag-" + name) || attributeName.equals("n:inner-" + name); } @@ -118,70 +54,8 @@ public static boolean isStringAtCaret(@NotNull Editor editor, @NotNull String st return fileText.length() >= startOffset + string.length() && fileText.substring(startOffset, startOffset + string.length()).equals(string); } - private static Collection findElementsInAllFiles(Project project, String key, Class className, @Nullable Collection phpClass) { - List result = new ArrayList<>(); - Collection virtualFiles = - FileTypeIndex.getFiles(LatteFileType.INSTANCE, GlobalSearchScope.allScope(project)); - for (VirtualFile virtualFile : virtualFiles) { - LatteFile simpleFile = (LatteFile) PsiManager.getInstance(project).findFile(virtualFile); - if (simpleFile != null) { - List elements = new ArrayList<>(); - for (PsiElement element : simpleFile.getChildren()) { - findFileItem(elements, element, className); - } - - attachResults(result, key, elements, phpClass); - } - } - return result; - } - - private static void attachResults(@NotNull List result, String key, List elements, @Nullable Collection phpClasses) - { - for (PsiElement element : elements) { - if (!(element instanceof BaseLattePhpElement)) { - continue; - } - - if ((phpClasses != null && phpClasses.size() > 0 && !((BaseLattePhpElement) element).getPhpType().hasClass(phpClasses))) { - continue; - } - - String varName = ((BaseLattePhpElement) element).getPhpElementName(); - if (key.equals(varName)) { - result.add((T) element); - } - } - } - - private static void findLattePhpVariables(List properties, PsiElement psiElement) { - psiElement.acceptChildren(new PsiRecursiveElementWalkingVisitor() { - @Override - public void visitElement(PsiElement element) { - if (element instanceof LattePhpVariable) { - properties.add(new PsiPositionedElement(getStartOffsetInFile(element), element)); - } else { - super.visitElement(element); - } - } - }); - } - - private static void findFileItem(List properties, PsiElement psiElement, Class className) { - psiElement.acceptChildren(new PsiRecursiveElementWalkingVisitor() { - @Override - public void visitElement(PsiElement element) { - if (className.isInstance(element)) { - properties.add(element); - } else { - super.visitElement(element); - } - } - }); - } - @Nullable - public static LattePhpType findFirstLatteTemplateType(PsiElement element) { + public static NettePhpType findFirstLatteTemplateType(PsiElement element) { List out = new ArrayList<>(); findLatteTemplateType(out, element); return out.isEmpty() ? null : out.get(0).getPhpType(); @@ -190,7 +64,7 @@ public static LattePhpType findFirstLatteTemplateType(PsiElement element) { public static void findLatteTemplateType(List classes, PsiElement psiElement) { psiElement.acceptChildren(new PsiRecursiveElementWalkingVisitor() { @Override - public void visitElement(PsiElement element) { + public void visitElement(@NotNull PsiElement element) { if (element instanceof LattePhpClassUsage && ((LattePhpClassUsage) element).isTemplateType()) { classes.add((LattePhpClassUsage) element); } else { @@ -203,7 +77,7 @@ public void visitElement(PsiElement element) { public static void findLatteMacroTemplateType(List classes, LatteFile file) { file.acceptChildren(new PsiRecursiveElementWalkingVisitor() { @Override - public void visitElement(PsiElement element) { + public void visitElement(@NotNull PsiElement element) { if (element instanceof LatteMacroTag && ((LatteMacroTag) element).matchMacroName("templateType")) { classes.add((LatteMacroTag) element); } else { diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/PsiCachedElement.java b/src/main/java/com/jantvrdik/intellij/latte/utils/PsiCachedElement.java new file mode 100644 index 0000000..b0f8db6 --- /dev/null +++ b/src/main/java/com/jantvrdik/intellij/latte/utils/PsiCachedElement.java @@ -0,0 +1,96 @@ +package com.jantvrdik.intellij.latte.utils; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.jantvrdik.intellij.latte.psi.LattePhpVariable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class PsiCachedElement { + + private final int position; + @NotNull + private final LattePhpVariable element; + private PsiElement context; + private boolean contextInitialized = false; + private boolean definition = false; + private List definitions = null; + private boolean definitionInitialized = false; + private boolean varTypeDefinition = false; + private boolean varTypeDefinitionInitialized = false; + private String variableName; + + public PsiCachedElement(int position, @NotNull LattePhpVariable element) { + this.position = position; + this.element = element; + } + + public int getPosition() { + return position; + } + + @NotNull + public LattePhpVariable getElement() { + return element; + } + + public boolean matchElement(PsiCachedElement cachedElement) { + return element == cachedElement.element; + } + + public String getVariableName() { + if (variableName == null) { + variableName = element.getVariableName(); + } + return variableName; + } + + @Nullable + public PsiElement getVariableContext() { + if (!contextInitialized) { + context = element.getVariableContext(); + contextInitialized = true; + } + return context; + } + + @NotNull + public List getVariableDefinitions() { + if (isDefinition()) { + return Collections.emptyList(); + } + + if (definitions == null) { + definitions = new ArrayList<>(); + for (LattePhpVariableDefinition variableDefinition : element.getVariableDefinition()) { + definitions.add(variableDefinition.getElement()); + } + } + return definitions; + } + + public boolean isDefinition() { + if (!definitionInitialized) { + definition = element.isDefinition(); + definitionInitialized = true; + } + return definition; + } + + public boolean isVarTypeDefinition() { + if (!varTypeDefinitionInitialized) { + varTypeDefinition = element.isVarTypeDefinition(); + varTypeDefinitionInitialized = true; + } + return varTypeDefinition; + } + + @NotNull + public Project getProject() { + return element.getProject(); + } +} \ No newline at end of file diff --git a/src/main/java/com/jantvrdik/intellij/latte/utils/PsiPositionedElement.java b/src/main/java/com/jantvrdik/intellij/latte/utils/PsiPositionedElement.java deleted file mode 100644 index 80cbae8..0000000 --- a/src/main/java/com/jantvrdik/intellij/latte/utils/PsiPositionedElement.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jantvrdik.intellij.latte.utils; - -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.NotNull; - -public class PsiPositionedElement { - - private final int position; - private final PsiElement element; - - public PsiPositionedElement(int position, @NotNull PsiElement element) { - this.position = position; - this.element = element; - } - - public int getPosition() { - return position; - } - - public PsiElement getElement() { - return element; - } -} \ No newline at end of file diff --git a/src/main/resources/META-INF/change-notes.html b/src/main/resources/META-INF/change-notes.html index 181c5d1..aff773f 100644 --- a/src/main/resources/META-INF/change-notes.html +++ b/src/main/resources/META-INF/change-notes.html @@ -1,4 +1,27 @@ + +

1.1.3

+
    +
  • just released
  • +
+ +

1.1.3-RC1

+
    +
  • added missing tags and filters for latest Latte version
  • +
  • better completion for tags with optional closing tag (issue #110)
  • +
  • fixed for 2021.1 (issue #163, #164)
  • +
  • fixed methods with variable in name (issue #153, #166)
  • +
  • fixed and improved variables inspections, implemented variable context (issue #104)
  • +
  • fixed detection for variable types in varType tag (issue #107)
  • +
  • fixed problem with multiple variable definitions (issue #124)
  • +
  • added more tests for psi helpers and variable inspections
  • +
+ +

1.1.2

+
    +
  • just released
  • +
+

1.1.2-RC1

  • fixed bug with bad ASTNode creating (issue #135)
  • diff --git a/src/main/resources/xmlSources/Latte.dtd b/src/main/resources/xmlSources/Latte.dtd index ab65978..0cf3a95 100644 --- a/src/main/resources/xmlSources/Latte.dtd +++ b/src/main/resources/xmlSources/Latte.dtd @@ -8,7 +8,7 @@ - + diff --git a/src/main/resources/xmlSources/Latte.xml b/src/main/resources/xmlSources/Latte.xml index 29502ee..0bfceaf 100644 --- a/src/main/resources/xmlSources/Latte.xml +++ b/src/main/resources/xmlSources/Latte.xml @@ -12,7 +12,7 @@ - + @@ -182,6 +182,11 @@ + + + + + @@ -197,6 +202,7 @@ + @@ -209,6 +215,7 @@ + @@ -220,25 +227,25 @@ - - - + + + - - - - - - - + + + + + + + - + @@ -247,18 +254,37 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/com/jantvrdik/intellij/latte/BasePsiParsingTestCase.java b/src/test/java/com/jantvrdik/intellij/latte/BasePsiParsingTestCase.java new file mode 100644 index 0000000..c29b2a6 --- /dev/null +++ b/src/test/java/com/jantvrdik/intellij/latte/BasePsiParsingTestCase.java @@ -0,0 +1,66 @@ +package com.jantvrdik.intellij.latte; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiRecursiveElementWalkingVisitor; +import com.intellij.testFramework.ParsingTestCase; +import com.intellij.testFramework.TestDataFile; +import com.jantvrdik.intellij.latte.config.LatteConfiguration; +import com.jantvrdik.intellij.latte.parser.LatteParserDefinition; +import com.jantvrdik.intellij.latte.psi.LattePhpVariable; +import com.jantvrdik.intellij.latte.settings.LatteTagSettings; +import com.jantvrdik.intellij.latte.settings.xml.LatteXmlFileData; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +abstract public class BasePsiParsingTestCase extends ParsingTestCase { + + protected BasePsiParsingTestCase() { + super("", "latte", new LatteParserDefinition()); + } + + protected String loadFile(@NotNull @NonNls @TestDataFile String name) throws IOException { + return FileUtil.loadFile(new File(myFullDataPath, name), CharsetToolkit.UTF8, true); + } + + protected PsiFile parseFile(@NotNull String fileName) throws IOException { + return parseFile(fileName, loadFile(fileName)); + } + + protected List collectVariables(PsiElement parent) { + List variables = new ArrayList<>(); + parent.acceptChildren(new PsiRecursiveElementWalkingVisitor() { + @Override + public void visitElement(@NotNull PsiElement element) { + if (element instanceof LattePhpVariable) { + variables.add((LattePhpVariable) element); + } else { + super.visitElement(element); + } + } + }); + return variables; + } + + protected Collection getXmlFileData() { + Collection out = new HashSet<>(); + LatteXmlFileData xmlFileData = new LatteXmlFileData(new LatteXmlFileData.VendorResult(LatteConfiguration.Vendor.LATTE, "Latte")); + out.add(xmlFileData); + xmlFileData.addTag(new LatteTagSettings("block", LatteTagSettings.Type.PAIR)); + xmlFileData.addTag(new LatteTagSettings("define", LatteTagSettings.Type.PAIR)); + xmlFileData.addTag(new LatteTagSettings("foreach", LatteTagSettings.Type.PAIR)); + xmlFileData.addTag(new LatteTagSettings("if", LatteTagSettings.Type.PAIR)); + xmlFileData.addTag(new LatteTagSettings("var", LatteTagSettings.Type.UNPAIRED)); + return out; + } + +} diff --git a/src/test/java/com/jantvrdik/intellij/latte/inspections/VariablesInspectionTest.java b/src/test/java/com/jantvrdik/intellij/latte/inspections/VariablesInspectionTest.java new file mode 100644 index 0000000..ce397b9 --- /dev/null +++ b/src/test/java/com/jantvrdik/intellij/latte/inspections/VariablesInspectionTest.java @@ -0,0 +1,117 @@ +package com.jantvrdik.intellij.latte.inspections; + +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.HeavyPlatformTestCase; +import com.jantvrdik.intellij.latte.BasePsiParsingTestCase; +import com.jantvrdik.intellij.latte.config.LatteConfiguration; +import com.jantvrdik.intellij.latte.inspections.utils.LatteInspectionInfo; +import com.jantvrdik.intellij.latte.settings.LatteSettings; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +public class VariablesInspectionTest extends BasePsiParsingTestCase { + + public VariablesInspectionTest() { + super(); + HeavyPlatformTestCase.doAutodetectPlatformPrefix(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + // initialize configuration with test configuration + LatteConfiguration.getInstance(getProject(), getXmlFileData()); + + getProject().registerService(LatteSettings.class); + } + + @Override + protected String getTestDataPath() { + URL url = getClass().getClassLoader().getResource("data/inspections/variables"); + assert url != null; + return url.getFile(); + } + + @Test + public void testUndefinedVariable() throws IOException { + List problems = getProblems("UndefinedVariable.latte"); + + Assert.assertNotNull(problems); + Assert.assertSame(1, problems.size()); + + Assert.assertEquals("Undefined variable 'foo'", problems.get(0).getDescription()); + Assert.assertEquals(ProblemHighlightType.GENERIC_ERROR_OR_WARNING, problems.get(0).getType()); + } + + @Test + public void testProbablyUndefinedVariable() throws IOException { + List problems = getProblems("ProbablyUndefinedVariable.latte"); + + Assert.assertNotNull(problems); + Assert.assertSame(1, problems.size()); + + Assert.assertEquals("Variable 'bar' is probably undefined", problems.get(0).getDescription()); + Assert.assertEquals(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, problems.get(0).getType()); + } + + @Test + public void testMultipleDefinitions() throws IOException { + List problems = getProblems("MultipleDefinitions.latte"); + + Assert.assertNotNull(problems); + Assert.assertSame(2, problems.size()); + + Assert.assertEquals("Multiple definitions for variable 'foo'", problems.get(0).getDescription()); + Assert.assertEquals(ProblemHighlightType.WARNING, problems.get(0).getType()); + + Assert.assertEquals("Multiple definitions for variable 'foo'", problems.get(1).getDescription()); + Assert.assertEquals(ProblemHighlightType.WARNING, problems.get(1).getType()); + } + + @Test + public void testDefinitionsInAnotherContext() throws IOException { + List problems = getProblems("DefinitionsInAnotherContext.latte"); + + Assert.assertNotNull(problems); + Assert.assertSame(1, problems.size()); + + Assert.assertEquals("Unused variable 'foo'", problems.get(0).getDescription()); + Assert.assertEquals(ProblemHighlightType.LIKE_UNUSED_SYMBOL, problems.get(0).getType()); + } + + @Test + public void testVariableInNFor() throws IOException { + List problems = getProblems("VariableInNFor.latte"); + + Assert.assertNotNull(problems); + Assert.assertSame(0, problems.size()); + } + + @Test + public void testVariableInBlock() throws IOException { + List problems = getProblems("VariableInBlock.latte"); + + Assert.assertNotNull(problems); + Assert.assertSame(2, problems.size()); + Assert.assertNotEquals(problems.get(0).getElement(), problems.get(1).getElement()); + + Assert.assertEquals("Unused variable 'foo'", problems.get(0).getDescription()); + Assert.assertEquals(ProblemHighlightType.LIKE_UNUSED_SYMBOL, problems.get(0).getType()); + + Assert.assertEquals("Unused variable 'foo'", problems.get(1).getDescription()); + Assert.assertEquals(ProblemHighlightType.LIKE_UNUSED_SYMBOL, problems.get(1).getType()); + } + + private List getProblems(@NotNull String templateName) throws IOException { + PsiFile file = parseFile(templateName); + + return (new VariablesInspection()).checkFile(file); + } + +} diff --git a/src/test/java/com/jantvrdik/intellij/latte/parser/ParserTest.java b/src/test/java/com/jantvrdik/intellij/latte/parser/ParserTest.java index c10c6dd..02b7a25 100644 --- a/src/test/java/com/jantvrdik/intellij/latte/parser/ParserTest.java +++ b/src/test/java/com/jantvrdik/intellij/latte/parser/ParserTest.java @@ -1,33 +1,17 @@ package com.jantvrdik.intellij.latte.parser; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.CharsetToolkit; -import com.intellij.psi.PsiElement; import com.intellij.testFramework.HeavyPlatformTestCase; -import com.intellij.testFramework.ParsingTestCase; -import com.intellij.testFramework.TestDataFile; -import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; +import com.jantvrdik.intellij.latte.BasePsiParsingTestCase; import com.jantvrdik.intellij.latte.config.LatteConfiguration; import com.jantvrdik.intellij.latte.settings.LatteSettings; -import com.jantvrdik.intellij.latte.settings.LatteTagSettings; -import com.jantvrdik.intellij.latte.settings.xml.LatteXmlFileData; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; import org.junit.Test; -import java.io.File; -import java.io.IOException; import java.net.URL; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -public class ParserTest extends ParsingTestCase { +public class ParserTest extends BasePsiParsingTestCase { public ParserTest() { - super("", "latte", new LatteParserDefinition()); + super(); HeavyPlatformTestCase.doAutodetectPlatformPrefix(); } @@ -47,10 +31,6 @@ protected String getTestDataPath() { return url.getFile(); } - protected String loadFile(@NonNls @TestDataFile String name) throws IOException { - return FileUtil.loadFile(new File(myFullDataPath, name), CharsetToolkit.UTF8, true); - } - @Test public void testVariable() { doTest(true, true); @@ -66,13 +46,4 @@ public void testUnknownInIf() { doTest(true, true); } - private Collection getXmlFileData() { - Collection out = new HashSet<>(); - LatteXmlFileData xmlFileData = new LatteXmlFileData(new LatteXmlFileData.VendorResult(LatteConfiguration.Vendor.LATTE, "Latte")); - out.add(xmlFileData); - xmlFileData.addTag(new LatteTagSettings("block", LatteTagSettings.Type.PAIR)); - xmlFileData.addTag(new LatteTagSettings("if", LatteTagSettings.Type.PAIR)); - return out; - } - } diff --git a/src/test/java/com/jantvrdik/intellij/latte/utils/LattePhpTypeTest.java b/src/test/java/com/jantvrdik/intellij/latte/php/NettePhpTypeTest.java similarity index 69% rename from src/test/java/com/jantvrdik/intellij/latte/utils/LattePhpTypeTest.java rename to src/test/java/com/jantvrdik/intellij/latte/php/NettePhpTypeTest.java index 9bfcc7b..4fc6076 100644 --- a/src/test/java/com/jantvrdik/intellij/latte/utils/LattePhpTypeTest.java +++ b/src/test/java/com/jantvrdik/intellij/latte/php/NettePhpTypeTest.java @@ -1,13 +1,12 @@ -package com.jantvrdik.intellij.latte.utils; +package com.jantvrdik.intellij.latte.php; import org.junit.Test; import static org.junit.Assert.*; -public class LattePhpTypeTest { +public class NettePhpTypeTest { @Test - @SuppressWarnings("unchecked") - public void testReadableString() throws Exception { + public void testReadableString() { assertLattePhpType("string", "string"); assertLattePhpType("int", "Int"); assertLattePhpType("callable", "callable"); @@ -22,8 +21,7 @@ public void testReadableString() throws Exception { } @Test - @SuppressWarnings("unchecked") - public void testClassNames() throws Exception { + public void testClassNames() { assertLattePhpTypeClasses(new String[]{}, "string"); assertLattePhpTypeClasses(new String[]{}, "Int"); assertLattePhpTypeClasses(new String[]{}, "callable"); @@ -38,8 +36,7 @@ public void testClassNames() throws Exception { } @Test - @SuppressWarnings("unchecked") - public void testClassNamesForDepth() throws Exception { + public void testClassNamesForDepth() { assertLattePhpTypeClasses(1, new String[]{}, "Iterable|NULL"); assertLattePhpTypeClasses(1, new String[]{}, "Iterable[]|null"); assertLattePhpTypeClasses(1, new String[]{}, "\\Foo\\Bar\\TestClass"); @@ -50,8 +47,7 @@ public void testClassNamesForDepth() throws Exception { } @Test - @SuppressWarnings("unchecked") - public void testIsNullable() throws Exception { + public void testIsNullable() { assertIsNullable(false, "string"); assertIsNullable(false, "Int"); assertIsNullable(false, "callable"); @@ -71,8 +67,7 @@ public void testIsNullable() throws Exception { } @Test - @SuppressWarnings("unchecked") - public void testIsNative() throws Exception { + public void testIsNative() { assertIsNative(true, "string"); assertIsNative(true, "Int"); assertIsNative(true, "callable"); @@ -90,8 +85,7 @@ public void testIsNative() throws Exception { } @Test - @SuppressWarnings("unchecked") - public void testIsMixed() throws Exception { + public void testIsMixed() { assertIsMixed(true, "mixed"); assertIsMixed(false, "mixed[]"); assertIsMixed(false, "Int"); @@ -107,8 +101,23 @@ public void testIsMixed() throws Exception { } @Test - @SuppressWarnings("unchecked") - public void testHasClass() throws Exception { + public void testIsIterable() { + assertIsIterable(false, "mixed"); + assertIsIterable(true, "mixed[]"); + assertIsIterable(false, "Int"); + assertIsIterable(false, "callable"); + assertIsIterable(true, "Iterable[]|null"); + assertIsIterable(false, "\\Foo\\Bar\\TestClass"); + assertIsIterable(false, "Foo\\Bar\\TestClass|\\Bar\\TestClass"); + assertIsIterable(true, "Foo\\Bar\\TestClass[][]|String|null"); + + assertIsIterable(1, false, "Iterable[]"); + assertIsIterable(2, true, "mixed[][][]"); + assertIsIterable(5, false, "string[][]"); + } + + @Test + public void testHasClass() { assertHasClass("\\Foo\\Bar", "string", false); assertHasClass("Foo\\Bar", "Int", false); assertHasClass("Int", "Int", false); @@ -123,16 +132,37 @@ public void testHasClass() throws Exception { assertHasClass("\\Unknown", "Unknown|String|NULL", true); } + @Test + public void testWithDepth() { + NettePhpType phpType = NettePhpType.create("Foo\\TestClass[][]|String|null"); + assertEquals("\\Foo\\TestClass[][]|string|null", phpType.toString()); + assertTrue(phpType.isIterable()); + assertTrue("Must be nullable", phpType.isNullable()); + + NettePhpType firstDepth = phpType.withDepth(1); + assertEquals("\\Foo\\TestClass[]", firstDepth.toString()); + assertTrue(firstDepth.isIterable()); + assertFalse(firstDepth.isNullable()); + assertFalse(firstDepth.containsClasses()); + + NettePhpType secondDepth = firstDepth.withDepth(1); + assertEquals(secondDepth.toString(), phpType.withDepth(2).toString()); + assertEquals("\\Foo\\TestClass", secondDepth.toString()); + assertFalse(secondDepth.isIterable()); + assertFalse(secondDepth.isNullable()); + assertTrue(secondDepth.containsClasses()); + } + public static void assertLattePhpType(String expected, String type) { - assertEquals(expected, LattePhpType.create(type).toString()); + assertEquals(expected, NettePhpType.create(type).toString()); } public static void assertLattePhpTypeClasses(String[] expected, String type) { - assertArrayEquals(expected, LattePhpType.create(type).findClasses()); + assertArrayEquals(expected, NettePhpType.create(type).findClasses()); } public static void assertLattePhpTypeClasses(int depth, String[] expected, String type) { - assertArrayEquals(expected, LattePhpType.create(type).findClasses(depth)); + assertArrayEquals(expected, NettePhpType.create(type).findClasses(depth)); } public static void assertIsNullable(boolean nullable, String type) { @@ -141,9 +171,9 @@ public static void assertIsNullable(boolean nullable, String type) { public static void assertIsNullable(int depth, boolean nullable, String type) { if (nullable) { - assertTrue(LattePhpType.create(type).isNullable(depth)); + assertTrue(NettePhpType.create(type).isNullable(depth)); } else { - assertFalse(LattePhpType.create(type).isNullable(depth)); + assertFalse(NettePhpType.create(type).isNullable(depth)); } } @@ -153,9 +183,9 @@ public static void assertIsNative(boolean isNative, String type) { public static void assertIsNative(int depth, boolean isNative, String type) { if (isNative) { - assertTrue(LattePhpType.create(type).isNative(depth)); + assertTrue(NettePhpType.create(type).isNative(depth)); } else { - assertFalse(LattePhpType.create(type).isNative(depth)); + assertFalse(NettePhpType.create(type).isNative(depth)); } } @@ -165,17 +195,29 @@ public static void assertIsMixed(boolean isMixed, String type) { public static void assertIsMixed(int depth, boolean isMixed, String type) { if (isMixed) { - assertTrue(LattePhpType.create(type).isMixed(depth)); + assertTrue(NettePhpType.create(type).isMixed(depth)); + } else { + assertFalse(NettePhpType.create(type).isMixed(depth)); + } + } + + public static void assertIsIterable(boolean isIterable, String type) { + assertIsIterable(0, isIterable, type); + } + + public static void assertIsIterable(int depth, boolean isIterable, String type) { + if (isIterable) { + assertTrue(NettePhpType.create(type).isIterable(depth)); } else { - assertFalse(LattePhpType.create(type).isMixed(depth)); + assertFalse(NettePhpType.create(type).isIterable(depth)); } } public static void assertHasClass(String expected, String type, boolean has) { if (has) { - assertTrue(LattePhpType.create(type).hasClass(expected)); + assertTrue(NettePhpType.create(type).hasClass(expected)); } else { - assertFalse(LattePhpType.create(type).hasClass(expected)); + assertFalse(NettePhpType.create(type).hasClass(expected)); } } } diff --git a/src/test/java/com/jantvrdik/intellij/latte/php/VariableContextTest.java b/src/test/java/com/jantvrdik/intellij/latte/php/VariableContextTest.java new file mode 100644 index 0000000..7cd878f --- /dev/null +++ b/src/test/java/com/jantvrdik/intellij/latte/php/VariableContextTest.java @@ -0,0 +1,178 @@ +package com.jantvrdik.intellij.latte.php; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.testFramework.HeavyPlatformTestCase; +import com.jantvrdik.intellij.latte.BasePsiParsingTestCase; +import com.jantvrdik.intellij.latte.config.LatteConfiguration; +import com.jantvrdik.intellij.latte.psi.LattePairMacro; +import com.jantvrdik.intellij.latte.psi.LattePhpVariable; +import com.jantvrdik.intellij.latte.psi.impl.LatteHtmlPairTagImpl; +import com.jantvrdik.intellij.latte.settings.LatteSettings; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +public class VariableContextTest extends BasePsiParsingTestCase { + + public VariableContextTest() { + super(); + HeavyPlatformTestCase.doAutodetectPlatformPrefix(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + // initialize configuration with test configuration + LatteConfiguration.getInstance(getProject(), getXmlFileData()); + + getProject().registerService(LatteSettings.class); + } + + @Override + protected String getTestDataPath() { + URL url = getClass().getClassLoader().getResource("data/php/context"); + assert url != null; + return url.getFile(); + } + + @Test + public void testVariable() throws IOException { + String name = "Variable.latte"; + PsiFile file = parseFile(name, loadFile(name)); + LattePhpVariable definition = PsiTreeUtil.findChildOfAnyType(file, LattePhpVariable.class); + Assert.assertNotNull(definition); + + Assert.assertSame(file, LattePhpVariableUtil.getCurrentContext(definition)); + } + + @Test + public void testInBlock() throws IOException { + String name = "InBlock.latte"; + PsiFile file = parseFile(name, loadFile(name)); + LattePhpVariable definition = PsiTreeUtil.findChildOfAnyType(file, LattePhpVariable.class); + Assert.assertNotNull(definition); + + Assert.assertSame(file.getFirstChild(), LattePhpVariableUtil.getCurrentContext(definition)); + } + + @Test + public void testNetteAttribute() throws IOException { + String name = "NetteAttribute.latte"; + PsiFile file = parseFile(name, loadFile(name)); + LattePhpVariable definition = PsiTreeUtil.findChildOfAnyType(file, LattePhpVariable.class); + Assert.assertNotNull(definition); + + Assert.assertSame(file, LattePhpVariableUtil.getCurrentContext(definition)); + } + + @Test + public void testHtmlTag() throws IOException { + String name = "HtmlTag.latte"; + PsiFile file = parseFile(name, loadFile(name)); + LattePhpVariable usage = PsiTreeUtil.findChildOfAnyType(file, LattePhpVariable.class); + Assert.assertNotNull(usage); + + Assert.assertSame(file, LattePhpVariableUtil.getCurrentContext(usage)); + } + + @Test + public void testNetteAttributeForeach() throws IOException { + String name = "NetteAttributeForeach.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(2, variables.size()); + + LattePhpVariable usage = variables.get(0); + LattePhpVariable definition = variables.get(1); + + PsiElement context1 = LattePhpVariableUtil.getCurrentContext(usage); + Assert.assertSame(file, context1); + + PsiElement context2 = LattePhpVariableUtil.getCurrentContext(definition); + LatteHtmlPairTagImpl block = PsiTreeUtil.findChildOfAnyType(file, LatteHtmlPairTagImpl.class); + Assert.assertNotNull(block); + Assert.assertSame(block, context2); + } + + @Test + public void testNetteAttrComplexForeach() throws IOException { + String name = "NetteAttrComplexForeach.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(3, variables.size()); + + LattePhpVariable usage = variables.get(0); + LattePhpVariable definition = variables.get(1); + LattePhpVariable usage1 = variables.get(2); + + PsiElement context1 = LattePhpVariableUtil.getCurrentContext(usage); + Assert.assertSame(file, context1); + + PsiElement context2 = LattePhpVariableUtil.getCurrentContext(definition); + PsiElement context3 = LattePhpVariableUtil.getCurrentContext(usage1); + + LatteHtmlPairTagImpl block = PsiTreeUtil.findChildOfAnyType(file, LatteHtmlPairTagImpl.class); + Assert.assertNotNull(block); + Assert.assertSame(block, context2); + Assert.assertSame(block, context3); + } + + @Test + public void testVariableInNFor() throws IOException { + String name = "VariableInNFor.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(4, variables.size()); + + LattePhpVariable definition = variables.get(0); + LattePhpVariable usage1 = variables.get(1); + LattePhpVariable usage2 = variables.get(2); + LattePhpVariable usage3 = variables.get(3); + + PsiElement context1 = LattePhpVariableUtil.getCurrentContext(definition); + PsiElement context2 = LattePhpVariableUtil.getCurrentContext(usage1); + PsiElement context3 = LattePhpVariableUtil.getCurrentContext(usage2); + PsiElement context4 = LattePhpVariableUtil.getCurrentContext(usage3); + Assert.assertSame(context1, context2); + Assert.assertSame(context3, context4); + Assert.assertNotSame(file, context1); + } + + @Test + public void testVariablesInDefine() throws IOException { + String name = "VariablesInDefine.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(6, variables.size()); + + LattePhpVariable definition1 = variables.get(0); + LattePhpVariable definition2 = variables.get(1); + LattePhpVariable definition3 = variables.get(2); + LattePhpVariable usage1 = variables.get(3); + LattePhpVariable usage2 = variables.get(4); + LattePhpVariable usage3 = variables.get(5); + + LattePairMacro parentMacro = PsiTreeUtil.getParentOfType(definition1, LattePairMacro.class); + Assert.assertNotNull(parentMacro); + + PsiElement context1 = LattePhpVariableUtil.getCurrentContext(definition1); + PsiElement context2 = LattePhpVariableUtil.getCurrentContext(definition2); + PsiElement context3 = LattePhpVariableUtil.getCurrentContext(definition3); + PsiElement context4 = LattePhpVariableUtil.getCurrentContext(usage1); + PsiElement context5 = LattePhpVariableUtil.getCurrentContext(usage2); + PsiElement context6 = LattePhpVariableUtil.getCurrentContext(usage3); + + Assert.assertSame(parentMacro, context1); + Assert.assertSame(parentMacro, context2); + Assert.assertSame(parentMacro, context3); + Assert.assertSame(parentMacro, context4); + Assert.assertSame(parentMacro, context5); + Assert.assertSame(parentMacro, context6); + } + +} diff --git a/src/test/java/com/jantvrdik/intellij/latte/php/VariableDefinitionBeforeElementTest.java b/src/test/java/com/jantvrdik/intellij/latte/php/VariableDefinitionBeforeElementTest.java new file mode 100644 index 0000000..abd8c1c --- /dev/null +++ b/src/test/java/com/jantvrdik/intellij/latte/php/VariableDefinitionBeforeElementTest.java @@ -0,0 +1,72 @@ +package com.jantvrdik.intellij.latte.php; + +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.HeavyPlatformTestCase; +import com.jantvrdik.intellij.latte.BasePsiParsingTestCase; +import com.jantvrdik.intellij.latte.config.LatteConfiguration; +import com.jantvrdik.intellij.latte.psi.LattePhpVariable; +import com.jantvrdik.intellij.latte.settings.LatteSettings; +import com.jantvrdik.intellij.latte.utils.PsiCachedElement; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +public class VariableDefinitionBeforeElementTest extends BasePsiParsingTestCase { + + public VariableDefinitionBeforeElementTest() { + super(); + HeavyPlatformTestCase.doAutodetectPlatformPrefix(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + // initialize configuration with test configuration + LatteConfiguration.getInstance(getProject(), getXmlFileData()); + + getProject().registerService(LatteSettings.class); + } + + @Override + protected String getTestDataPath() { + URL url = getClass().getClassLoader().getResource("data/php/definitionBefore"); + assert url != null; + return url.getFile(); + } + + @Test + public void testSimpleDefinition() throws IOException { + String name = "SimpleDefinition.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(2, variables.size()); + + LattePhpVariable definition = variables.get(0); + LattePhpVariable usage = variables.get(1); + + List definitions = LattePhpVariableUtil.getVariablesDefinitionsBeforeElement(usage); + Assert.assertSame(1, definitions.size()); + Assert.assertSame(definition, definitions.get(0).getElement()); + } + + @Test + public void testDefinitionInBlock() throws IOException { + String name = "DefinitionInBlock.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(3, variables.size()); + + LattePhpVariable definition1 = variables.get(0); + LattePhpVariable definition2 = variables.get(1); + LattePhpVariable usage = variables.get(2); + + List definitions = LattePhpVariableUtil.getVariablesDefinitionsBeforeElement(usage); + Assert.assertSame(2, definitions.size()); + Assert.assertSame(definition1, definitions.get(0).getElement()); + Assert.assertSame(definition2, definitions.get(1).getElement()); + } + +} diff --git a/src/test/java/com/jantvrdik/intellij/latte/php/VariableDefinitionTest.java b/src/test/java/com/jantvrdik/intellij/latte/php/VariableDefinitionTest.java new file mode 100644 index 0000000..88137d1 --- /dev/null +++ b/src/test/java/com/jantvrdik/intellij/latte/php/VariableDefinitionTest.java @@ -0,0 +1,200 @@ +package com.jantvrdik.intellij.latte.php; + +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.HeavyPlatformTestCase; +import com.jantvrdik.intellij.latte.BasePsiParsingTestCase; +import com.jantvrdik.intellij.latte.config.LatteConfiguration; +import com.jantvrdik.intellij.latte.psi.LattePhpVariable; +import com.jantvrdik.intellij.latte.settings.LatteSettings; +import com.jantvrdik.intellij.latte.utils.LattePhpVariableDefinition; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +public class VariableDefinitionTest extends BasePsiParsingTestCase { + + public VariableDefinitionTest() { + super(); + HeavyPlatformTestCase.doAutodetectPlatformPrefix(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + // initialize configuration with test configuration + LatteConfiguration.getInstance(getProject(), getXmlFileData()); + + getProject().registerService(LatteSettings.class); + } + + @Override + protected String getTestDataPath() { + URL url = getClass().getClassLoader().getResource("data/php/definition"); + assert url != null; + return url.getFile(); + } + + @Test + public void testVariable() throws IOException { + String name = "Variable.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(3, variables.size()); + + LattePhpVariable definition = variables.get(0); + LattePhpVariable usage1 = variables.get(1); + LattePhpVariable usage2 = variables.get(2); + + List definition1 = LattePhpVariableUtil.getVariableDefinition(definition); + Assert.assertSame(0, definition1.size()); + + List definition2 = LattePhpVariableUtil.getVariableDefinition(usage1); + Assert.assertSame(1, definition2.size()); + Assert.assertFalse(definition2.get(0).isProbablyUndefined()); + Assert.assertSame(definition, definition2.get(0).getElement()); + + List definition3 = LattePhpVariableUtil.getVariableDefinition(usage2); + Assert.assertSame(1, definition3.size()); + Assert.assertFalse(definition3.get(0).isProbablyUndefined()); + Assert.assertSame(definition, definition3.get(0).getElement()); + } + + @Test + public void testVariableInside() throws IOException { + String name = "VariableInside.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(2, variables.size()); + + LattePhpVariable definition = variables.get(0); + LattePhpVariable usage = variables.get(1); + + List definitions1 = LattePhpVariableUtil.getVariableDefinition(definition); + Assert.assertSame(0, definitions1.size()); + + List definitions2 = LattePhpVariableUtil.getVariableDefinition(usage); + Assert.assertSame(1, definitions2.size()); + Assert.assertSame(definition, definitions2.get(0).getElement()); + Assert.assertFalse(definitions2.get(0).isProbablyUndefined()); + } + + @Test + public void testBlockWithNAttr() throws IOException { + String name = "BlockWithNAttr.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(6, variables.size()); + + LattePhpVariable definition1 = variables.get(0); + LattePhpVariable usage1a = variables.get(1); + LattePhpVariable usage1b = variables.get(2); + LattePhpVariable definition2 = variables.get(3); + LattePhpVariable usage2a = variables.get(4); + LattePhpVariable usage2b = variables.get(5); + + List definitions1 = LattePhpVariableUtil.getVariableDefinition(definition1); + Assert.assertSame(0, definitions1.size()); + + List definitions2 = LattePhpVariableUtil.getVariableDefinition(usage1a); + Assert.assertSame(1, definitions2.size()); + Assert.assertSame(definition1, definitions2.get(0).getElement()); + Assert.assertFalse(definitions2.get(0).isProbablyUndefined()); + + List definitions3 = LattePhpVariableUtil.getVariableDefinition(usage1b); + Assert.assertSame(1, definitions3.size()); + Assert.assertSame(definition1, definitions3.get(0).getElement()); + Assert.assertTrue(definitions3.get(0).isProbablyUndefined()); + + List definitions4 = LattePhpVariableUtil.getVariableDefinition(definition2); + Assert.assertSame(0, definitions4.size()); + + List definitions5 = LattePhpVariableUtil.getVariableDefinition(usage2a); + Assert.assertSame(1, definitions5.size()); + Assert.assertSame(definition2, definitions5.get(0).getElement()); + Assert.assertFalse(definitions5.get(0).isProbablyUndefined()); + + List definitions6 = LattePhpVariableUtil.getVariableDefinition(usage2b); + Assert.assertSame(1, definitions6.size()); + Assert.assertSame(definition2, definitions6.get(0).getElement()); + Assert.assertTrue(definitions6.get(0).isProbablyUndefined()); + } + + @Test + public void testBlockInIf() throws IOException { + String name = "BlockInIf.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(2, variables.size()); + + LattePhpVariable definition = variables.get(0); + LattePhpVariable usage = variables.get(1); + + List definitions1 = LattePhpVariableUtil.getVariableDefinition(definition); + Assert.assertSame(0, definitions1.size()); + + List definitions2 = LattePhpVariableUtil.getVariableDefinition(usage); + Assert.assertSame(1, definitions2.size()); + Assert.assertSame(definition, definitions2.get(0).getElement()); + Assert.assertFalse(definitions2.get(0).isProbablyUndefined()); + } + + @Test + public void testOtherVariables() throws IOException { + String name = "OtherVariables.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(3, variables.size()); + + LattePhpVariable definition1 = variables.get(0); + LattePhpVariable definition2 = variables.get(1); + LattePhpVariable usage = variables.get(2); + + List definitions1 = LattePhpVariableUtil.getVariableDefinition(definition1); + Assert.assertSame(0, definitions1.size()); + + List otherDefinitions = LattePhpVariableUtil.getVariableOtherDefinitions(definition2); + Assert.assertSame(1, otherDefinitions.size()); + Assert.assertSame(definition1, otherDefinitions.get(0).getElement()); + Assert.assertFalse(otherDefinitions.get(0).isProbablyUndefined()); + + List definitions2 = LattePhpVariableUtil.getVariableDefinition(usage); + Assert.assertSame(2, definitions2.size()); + Assert.assertSame(definition1, definitions2.get(0).getElement()); + Assert.assertSame(definition2, definitions2.get(1).getElement()); + Assert.assertFalse(definitions2.get(0).isProbablyUndefined()); + Assert.assertFalse(definitions2.get(1).isProbablyUndefined()); + } + + @Test + public void testVariableInNFor() throws IOException { + String name = "VariableInNFor.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(4, variables.size()); + + LattePhpVariable definition = variables.get(0); + LattePhpVariable usage1 = variables.get(1); + LattePhpVariable usage2 = variables.get(2); + LattePhpVariable usage3 = variables.get(3); + + List definitions = LattePhpVariableUtil.getVariableDefinition(definition); + Assert.assertSame(0, definitions.size()); + + List definitions1 = LattePhpVariableUtil.getVariableDefinition(usage1); + Assert.assertSame(1, definitions1.size()); + Assert.assertSame(definition, definitions1.get(0).getElement()); + Assert.assertFalse(definitions1.get(0).isProbablyUndefined()); + + List definitions2 = LattePhpVariableUtil.getVariableDefinition(usage2); + Assert.assertSame(1, definitions2.size()); + Assert.assertFalse(definitions2.get(0).isProbablyUndefined()); + + List definitions3 = LattePhpVariableUtil.getVariableDefinition(usage3); + Assert.assertSame(1, definitions3.size()); + Assert.assertFalse(definitions3.get(0).isProbablyUndefined()); + } + +} diff --git a/src/test/java/com/jantvrdik/intellij/latte/php/VariableIsDefinitionTest.java b/src/test/java/com/jantvrdik/intellij/latte/php/VariableIsDefinitionTest.java new file mode 100644 index 0000000..11fb9a7 --- /dev/null +++ b/src/test/java/com/jantvrdik/intellij/latte/php/VariableIsDefinitionTest.java @@ -0,0 +1,96 @@ +package com.jantvrdik.intellij.latte.php; + +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.HeavyPlatformTestCase; +import com.jantvrdik.intellij.latte.BasePsiParsingTestCase; +import com.jantvrdik.intellij.latte.config.LatteConfiguration; +import com.jantvrdik.intellij.latte.psi.LattePhpVariable; +import com.jantvrdik.intellij.latte.settings.LatteSettings; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +public class VariableIsDefinitionTest extends BasePsiParsingTestCase { + + public VariableIsDefinitionTest() { + super(); + HeavyPlatformTestCase.doAutodetectPlatformPrefix(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + // initialize configuration with test configuration + LatteConfiguration.getInstance(getProject(), getXmlFileData()); + + getProject().registerService(LatteSettings.class); + } + + @Override + protected String getTestDataPath() { + URL url = getClass().getClassLoader().getResource("data/php/isDefinition"); + assert url != null; + return url.getFile(); + } + + @Test + public void testVarTypeDefinition() throws IOException { + String name = "VarTypeDefinition.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(3, variables.size()); + + LattePhpVariable varTypeDefinition = variables.get(0); + LattePhpVariable varDefinition = variables.get(1); + LattePhpVariable usage = variables.get(2); + + Assert.assertTrue(LattePhpVariableUtil.isVariableDefinition(varTypeDefinition)); + Assert.assertTrue(LattePhpVariableUtil.isVarTypeDefinition(varTypeDefinition)); + Assert.assertTrue(LattePhpVariableUtil.isVariableDefinition(varDefinition)); + Assert.assertFalse(LattePhpVariableUtil.isVariableDefinition(usage)); + } + + @Test + public void testDefinitionInNForeach() throws IOException { + String name = "DefinitionInNForeach.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(3, variables.size()); + + LattePhpVariable definition1 = variables.get(0); + LattePhpVariable usage = variables.get(1); + LattePhpVariable definition2 = variables.get(2); + + Assert.assertTrue(LattePhpVariableUtil.isVariableDefinition(definition1)); + Assert.assertFalse(LattePhpVariableUtil.isVariableDefinition(usage)); + Assert.assertTrue(LattePhpVariableUtil.isVariableDefinition(definition2)); + } + + @Test + public void testDefinitionInNForeachWithBefore() throws IOException { + String name = "DefinitionInNForeachWithBefore.latte"; + PsiFile file = parseFile(name, loadFile(name)); + List variables = collectVariables(file); + Assert.assertSame(6, variables.size()); + + LattePhpVariable definition1 = variables.get(0); + LattePhpVariable usage1 = variables.get(1); + LattePhpVariable definition2 = variables.get(2); + + LattePhpVariable definition3 = variables.get(3); + LattePhpVariable usage2 = variables.get(4); + LattePhpVariable definition4 = variables.get(5); + + Assert.assertTrue(LattePhpVariableUtil.isVariableDefinition(definition1)); + Assert.assertFalse(LattePhpVariableUtil.isVariableDefinition(usage1)); + Assert.assertTrue(LattePhpVariableUtil.isVariableDefinition(definition2)); + + Assert.assertTrue(LattePhpVariableUtil.isVariableDefinition(definition3)); + Assert.assertFalse(LattePhpVariableUtil.isVariableDefinition(usage2)); + Assert.assertTrue(LattePhpVariableUtil.isVariableDefinition(definition4)); + } + +} diff --git a/src/test/resources/data/inspections/variables/DefinitionsInAnotherContext.latte b/src/test/resources/data/inspections/variables/DefinitionsInAnotherContext.latte new file mode 100644 index 0000000..10f0dcf --- /dev/null +++ b/src/test/resources/data/inspections/variables/DefinitionsInAnotherContext.latte @@ -0,0 +1,5 @@ +{var $foo = []} +{if true} + {var $foo = []} + {$foo} +{/if} diff --git a/src/test/resources/data/inspections/variables/MultipleDefinitions.latte b/src/test/resources/data/inspections/variables/MultipleDefinitions.latte new file mode 100644 index 0000000..e8a5ff0 --- /dev/null +++ b/src/test/resources/data/inspections/variables/MultipleDefinitions.latte @@ -0,0 +1,3 @@ +{var $foo = []} +{var $foo = []} +{$foo} \ No newline at end of file diff --git a/src/test/resources/data/inspections/variables/ProbablyUndefinedVariable.latte b/src/test/resources/data/inspections/variables/ProbablyUndefinedVariable.latte new file mode 100644 index 0000000..7b1bec0 --- /dev/null +++ b/src/test/resources/data/inspections/variables/ProbablyUndefinedVariable.latte @@ -0,0 +1,5 @@ +{var $foo = []} +{foreach $foo as $bar} + {= 123} +{/foreach} +{$bar} \ No newline at end of file diff --git a/src/test/resources/data/inspections/variables/UndefinedVariable.latte b/src/test/resources/data/inspections/variables/UndefinedVariable.latte new file mode 100644 index 0000000..b9490d3 --- /dev/null +++ b/src/test/resources/data/inspections/variables/UndefinedVariable.latte @@ -0,0 +1 @@ +{$foo} \ No newline at end of file diff --git a/src/test/resources/data/inspections/variables/VariableInBlock.latte b/src/test/resources/data/inspections/variables/VariableInBlock.latte new file mode 100644 index 0000000..86a4860 --- /dev/null +++ b/src/test/resources/data/inspections/variables/VariableInBlock.latte @@ -0,0 +1,7 @@ +{var $foos = []} +
    + +{define bar} + {var $foos = []} +
    +{/define} \ No newline at end of file diff --git a/src/test/resources/data/inspections/variables/VariableInNFor.latte b/src/test/resources/data/inspections/variables/VariableInNFor.latte new file mode 100644 index 0000000..eb36c85 --- /dev/null +++ b/src/test/resources/data/inspections/variables/VariableInNFor.latte @@ -0,0 +1,3 @@ +
  • + foo +
  • \ No newline at end of file diff --git a/src/test/resources/data/parser/NBlock.txt b/src/test/resources/data/parser/NBlock.txt index 10c618d..1aa4c66 100644 --- a/src/test/resources/data/parser/NBlock.txt +++ b/src/test/resources/data/parser/NBlock.txt @@ -1,28 +1,29 @@ Latte file LatteOuterHtmlImpl(OUTER_HTML) - LatteHtmlOpenTagImpl(HTML_OPEN_TAG) - PsiElement(LatteTokenType.T_HTML_OPEN_TAG_OPEN)('<') - LatteHtmlTagContentImpl(HTML_TAG_CONTENT) - PsiElement(LatteTokenType.T_TEXT)('div ') - LatteNetteAttrImpl(NETTE_ATTR) - PsiElement(LatteTokenType.T_HTML_TAG_NATTR_NAME)('n:block') - PsiElement(LatteTokenType.T_HTML_TAG_ATTR_EQUAL_SIGN)('=') - LatteNetteAttrValueImpl(NETTE_ATTR_VALUE) - PsiElement(LatteTokenType.T_HTML_TAG_ATTR_DQ)('"') - LatteMacroContentImpl(MACRO_CONTENT) - LattePhpContentImpl(PHP_CONTENT) - LattePhpStatementImpl(PHP_STATEMENT) - LattePhpStatementFirstPartImpl(PHP_STATEMENT_FIRST_PART) - LattePhpVariableImpl(PHP_VARIABLE)('$test') - PsiElement(LatteTokenType.T_MACRO_ARGS_VAR)('$test') - PsiElement(LatteTokenType.T_HTML_TAG_ATTR_DQ)('"') - PsiElement(LatteTokenType.T_HTML_TAG_CLOSE)('>') - LatteHtmlTagContainerImpl(HTML_TAG_CONTAINER) - - LatteHtmlCloseTagImpl(HTML_CLOSE_TAG) - PsiElement(LatteTokenType.T_HTML_CLOSE_TAG_OPEN)('') + LatteHtmlPairTagImpl(HTML_PAIR_TAG) + LatteHtmlOpenTagImpl(HTML_OPEN_TAG) + PsiElement(LatteTokenType.T_HTML_OPEN_TAG_OPEN)('<') + LatteHtmlTagContentImpl(HTML_TAG_CONTENT) + PsiElement(LatteTokenType.T_TEXT)('div ') + LatteNetteAttrImpl(NETTE_ATTR) + PsiElement(LatteTokenType.T_HTML_TAG_NATTR_NAME)('n:block') + PsiElement(LatteTokenType.T_HTML_TAG_ATTR_EQUAL_SIGN)('=') + LatteNetteAttrValueImpl(NETTE_ATTR_VALUE) + PsiElement(LatteTokenType.T_HTML_TAG_ATTR_DQ)('"') + LatteMacroContentImpl(MACRO_CONTENT) + LattePhpContentImpl(PHP_CONTENT) + LattePhpStatementImpl(PHP_STATEMENT) + LattePhpStatementFirstPartImpl(PHP_STATEMENT_FIRST_PART) + LattePhpVariableImpl(PHP_VARIABLE)('$test') + PsiElement(LatteTokenType.T_MACRO_ARGS_VAR)('$test') + PsiElement(LatteTokenType.T_HTML_TAG_ATTR_DQ)('"') + PsiElement(LatteTokenType.T_HTML_TAG_CLOSE)('>') + LatteHtmlTagContainerImpl(HTML_TAG_CONTAINER) + + LatteHtmlCloseTagImpl(HTML_CLOSE_TAG) + PsiElement(LatteTokenType.T_HTML_CLOSE_TAG_OPEN)('') LatteOuterHtmlImpl(OUTER_HTML) PsiElement(LatteTokenType.T_TEXT)('\n') \ No newline at end of file diff --git a/src/test/resources/data/parser/Variable.txt b/src/test/resources/data/parser/Variable.txt index 22fa531..68630ce 100644 --- a/src/test/resources/data/parser/Variable.txt +++ b/src/test/resources/data/parser/Variable.txt @@ -1,5 +1,5 @@ Latte file - LattePairMacroImpl(PAIR_MACRO) + LatteUnpairedMacroImpl(UNPAIRED_MACRO) LatteMacroOpenTagImpl(MACRO_OPEN_TAG) PsiElement(LatteTokenType.T_MACRO_OPEN_TAG_OPEN)('{') LatteMacroContentImpl(MACRO_CONTENT) diff --git a/src/test/resources/data/php/context/HtmlTag.latte b/src/test/resources/data/php/context/HtmlTag.latte new file mode 100644 index 0000000..8f75493 --- /dev/null +++ b/src/test/resources/data/php/context/HtmlTag.latte @@ -0,0 +1 @@ +
    {$name}
    \ No newline at end of file diff --git a/src/test/resources/data/php/context/InBlock.latte b/src/test/resources/data/php/context/InBlock.latte new file mode 100644 index 0000000..80691bc --- /dev/null +++ b/src/test/resources/data/php/context/InBlock.latte @@ -0,0 +1,3 @@ +{block foo} + {var string $foo = 123} +{/block} \ No newline at end of file diff --git a/src/test/resources/data/php/context/NetteAttrComplexForeach.latte b/src/test/resources/data/php/context/NetteAttrComplexForeach.latte new file mode 100644 index 0000000..abb18ac --- /dev/null +++ b/src/test/resources/data/php/context/NetteAttrComplexForeach.latte @@ -0,0 +1 @@ +
    {$value}
    \ No newline at end of file diff --git a/src/test/resources/data/php/context/NetteAttribute.latte b/src/test/resources/data/php/context/NetteAttribute.latte new file mode 100644 index 0000000..6556baa --- /dev/null +++ b/src/test/resources/data/php/context/NetteAttribute.latte @@ -0,0 +1 @@ +
    \ No newline at end of file diff --git a/src/test/resources/data/php/context/NetteAttributeForeach.latte b/src/test/resources/data/php/context/NetteAttributeForeach.latte new file mode 100644 index 0000000..6c70dad --- /dev/null +++ b/src/test/resources/data/php/context/NetteAttributeForeach.latte @@ -0,0 +1 @@ +
    \ No newline at end of file diff --git a/src/test/resources/data/php/context/Variable.latte b/src/test/resources/data/php/context/Variable.latte new file mode 100644 index 0000000..32ced66 --- /dev/null +++ b/src/test/resources/data/php/context/Variable.latte @@ -0,0 +1 @@ +{var string $foo = 123} diff --git a/src/test/resources/data/php/context/VariableInNFor.latte b/src/test/resources/data/php/context/VariableInNFor.latte new file mode 100644 index 0000000..eb36c85 --- /dev/null +++ b/src/test/resources/data/php/context/VariableInNFor.latte @@ -0,0 +1,3 @@ +
  • + foo +
  • \ No newline at end of file diff --git a/src/test/resources/data/php/context/VariablesInDefine.latte b/src/test/resources/data/php/context/VariablesInDefine.latte new file mode 100644 index 0000000..1854071 --- /dev/null +++ b/src/test/resources/data/php/context/VariablesInDefine.latte @@ -0,0 +1,3 @@ +{define input, $name, $value, $type = 'text'} + +{/define} \ No newline at end of file diff --git a/src/test/resources/data/php/definition/BlockInIf.latte b/src/test/resources/data/php/definition/BlockInIf.latte new file mode 100644 index 0000000..51e0720 --- /dev/null +++ b/src/test/resources/data/php/definition/BlockInIf.latte @@ -0,0 +1,5 @@ +{varType float $activityLog} + +{if true} + {$activityLog} +{/if} \ No newline at end of file diff --git a/src/test/resources/data/php/definition/BlockWithNAttr.latte b/src/test/resources/data/php/definition/BlockWithNAttr.latte new file mode 100644 index 0000000..100e778 --- /dev/null +++ b/src/test/resources/data/php/definition/BlockWithNAttr.latte @@ -0,0 +1,10 @@ + +{block} + {var $val = array()} + {$val} +{/block} + +
    + {$value} +
    +{$value} \ No newline at end of file diff --git a/src/test/resources/data/php/definition/OtherVariables.latte b/src/test/resources/data/php/definition/OtherVariables.latte new file mode 100644 index 0000000..e6fc8de --- /dev/null +++ b/src/test/resources/data/php/definition/OtherVariables.latte @@ -0,0 +1,6 @@ +{varType float $activityLog} +{var $activityLog = 123} + +{if true} + {$activityLog} +{/if} \ No newline at end of file diff --git a/src/test/resources/data/php/definition/Variable.latte b/src/test/resources/data/php/definition/Variable.latte new file mode 100644 index 0000000..75c1cd2 --- /dev/null +++ b/src/test/resources/data/php/definition/Variable.latte @@ -0,0 +1,2 @@ +{varType App\Users\UserAdmin[] $admins} +
    {$admins}
    \ No newline at end of file diff --git a/src/test/resources/data/php/definition/VariableInNFor.latte b/src/test/resources/data/php/definition/VariableInNFor.latte new file mode 100644 index 0000000..eb36c85 --- /dev/null +++ b/src/test/resources/data/php/definition/VariableInNFor.latte @@ -0,0 +1,3 @@ +
  • + foo +
  • \ No newline at end of file diff --git a/src/test/resources/data/php/definition/VariableInside.latte b/src/test/resources/data/php/definition/VariableInside.latte new file mode 100644 index 0000000..776320b --- /dev/null +++ b/src/test/resources/data/php/definition/VariableInside.latte @@ -0,0 +1,5 @@ + +{block} + {var $val = array()} + {$val} +{/block} \ No newline at end of file diff --git a/src/test/resources/data/php/definitionBefore/DefinitionInBlock.latte b/src/test/resources/data/php/definitionBefore/DefinitionInBlock.latte new file mode 100644 index 0000000..e6fc8de --- /dev/null +++ b/src/test/resources/data/php/definitionBefore/DefinitionInBlock.latte @@ -0,0 +1,6 @@ +{varType float $activityLog} +{var $activityLog = 123} + +{if true} + {$activityLog} +{/if} \ No newline at end of file diff --git a/src/test/resources/data/php/definitionBefore/SimpleDefinition.latte b/src/test/resources/data/php/definitionBefore/SimpleDefinition.latte new file mode 100644 index 0000000..bc534e0 --- /dev/null +++ b/src/test/resources/data/php/definitionBefore/SimpleDefinition.latte @@ -0,0 +1,2 @@ +{var $foo = 123} +{$foo} \ No newline at end of file diff --git a/src/test/resources/data/php/isDefinition/DefinitionInNForeach.latte b/src/test/resources/data/php/isDefinition/DefinitionInNForeach.latte new file mode 100644 index 0000000..74fc846 --- /dev/null +++ b/src/test/resources/data/php/isDefinition/DefinitionInNForeach.latte @@ -0,0 +1,2 @@ +{var $foos = []} +
    \ No newline at end of file diff --git a/src/test/resources/data/php/isDefinition/DefinitionInNForeachWithBefore.latte b/src/test/resources/data/php/isDefinition/DefinitionInNForeachWithBefore.latte new file mode 100644 index 0000000..86a4860 --- /dev/null +++ b/src/test/resources/data/php/isDefinition/DefinitionInNForeachWithBefore.latte @@ -0,0 +1,7 @@ +{var $foos = []} +
    + +{define bar} + {var $foos = []} +
    +{/define} \ No newline at end of file diff --git a/src/test/resources/data/php/isDefinition/VarTypeDefinition.latte b/src/test/resources/data/php/isDefinition/VarTypeDefinition.latte new file mode 100644 index 0000000..b961442 --- /dev/null +++ b/src/test/resources/data/php/isDefinition/VarTypeDefinition.latte @@ -0,0 +1,3 @@ +{varType string $foo} +{var $foo = 'bar'} +{$foo} \ No newline at end of file