From f653ffb98cf58191bacc0b8439cc569df2f2a119 Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Wed, 22 Apr 2015 21:32:40 +0300 Subject: [PATCH 1/4] Skipping json injection to HTML for Django templates (for PY-15074) --- .../src/META-INF/XmlPlugin.xml | 7 +- .../html/HtmlScriptInjectionBlocker.java | 35 ++++++++++ .../HtmlScriptInjectionBlockerExtension.java | 69 +++++++++++++++++++ .../html/HtmlScriptLanguageInjector.java | 18 +++-- .../psi/impl/source/html/InjectionInfo.java | 50 ++++++++++++++ 5 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlocker.java create mode 100644 xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlockerExtension.java create mode 100644 xml/impl/src/com/intellij/psi/impl/source/html/InjectionInfo.java diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 87eed975a9bc..785c0b26160e 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -21,6 +21,11 @@ + + + + + - + diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlocker.java b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlocker.java new file mode 100644 index 000000000000..b9c4b095d103 --- /dev/null +++ b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlocker.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.html; + +import com.intellij.lang.Language; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.annotations.NotNull; + +/** + * Interface to be implemented by extension point that wants to prevent some language from injection into script HTML tag + * + * @author Ilya.Kazakevich + */ +public interface HtmlScriptInjectionBlocker { + /** + * @param scriptTag <script> tag + * @param language language that should be injected according to
type
attribute + * @return true if language should not be injected + */ + boolean isDenyLanguageInjection(@NotNull XmlTag scriptTag, @NotNull Language language); +} diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlockerExtension.java b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlockerExtension.java new file mode 100644 index 000000000000..f7385dfcbdaa --- /dev/null +++ b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlockerExtension.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.html; + +import com.intellij.lang.Language; +import com.intellij.lang.LanguageExtension; +import com.intellij.psi.xml.XmlTag; +import com.intellij.xml.util.HtmlUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * Language extension to block script injection into HTML tag <script> when this language exists on file's + * {@link com.intellij.psi.FileViewProvider}. + * + * @author Ilya.Kazakevich + */ +public final class HtmlScriptInjectionBlockerExtension extends LanguageExtension { + private static final HtmlScriptInjectionBlockerExtension INSTANCE = new HtmlScriptInjectionBlockerExtension(); + + private HtmlScriptInjectionBlockerExtension() { + super("com.intellij.html.htmlScriptInjectionBlocker"); + } + + + /** + * Finds language to be injected into script tag and checks if is blocked by some extension point + * + * @param xmlTag tag that may be script tag. + * @return null if tag is not script tag at all, info otherwise + */ + @Nullable + public static InjectionInfo getInjectionInfo(@NotNull XmlTag xmlTag) { + if (!HtmlUtil.isScriptTag(xmlTag)) { + return null; + } + String mimeType = xmlTag.getAttributeValue("type"); + Collection languages = Language.findInstancesByMimeType(mimeType); + Language language = !languages.isEmpty() ? languages.iterator().next() : Language.ANY; + + + Collection allFileLanguages = xmlTag.getContainingFile().getViewProvider().getLanguages(); + for (Language fileLanguage : allFileLanguages) { + for (final HtmlScriptInjectionBlocker blocker : INSTANCE.allForLanguage(fileLanguage)) { + if (blocker.isDenyLanguageInjection(xmlTag, language)) { + // Language exists, but denied by EP + return new InjectionInfo(true, language); + } + } + } + // Language exists and not denied + return new InjectionInfo(false, language); + } +} diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptLanguageInjector.java b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptLanguageInjector.java index d874118395b1..c2edd96ba211 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptLanguageInjector.java +++ b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptLanguageInjector.java @@ -17,7 +17,6 @@ package com.intellij.psi.impl.source.html; import com.intellij.lang.Language; import com.intellij.lang.LanguageUtil; -import com.intellij.lang.StdLanguages; import com.intellij.lang.injection.MultiHostInjector; import com.intellij.lang.injection.MultiHostRegistrar; import com.intellij.openapi.util.TextRange; @@ -28,23 +27,30 @@ import com.intellij.psi.xml.XmlText; import com.intellij.xml.util.HtmlUtil; import org.jetbrains.annotations.NotNull; -import java.util.Collection; import java.util.Collections; import java.util.List; public class HtmlScriptLanguageInjector implements MultiHostInjector { + + + @Override public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement host) { if (!host.isValid() || !(host instanceof XmlText) || !HtmlUtil.isHtmlTagContainingFile(host)) { return; } XmlTag scriptTag = ((XmlText)host).getParentTag(); - if (scriptTag == null || !HtmlUtil.isScriptTag(scriptTag)) { + + if (scriptTag == null) { return; } - String mimeType = scriptTag.getAttributeValue("type"); - Collection languages = Language.findInstancesByMimeType(mimeType); - Language language = !languages.isEmpty() ? languages.iterator().next() : Language.ANY; + + final InjectionInfo injectAndProtection = HtmlScriptInjectionBlockerExtension.getInjectionInfo(scriptTag); + if (injectAndProtection == null || injectAndProtection.isDeniedByExtensionPoint()) { + return; // Not even a script tag, or language injection denied by EP + } + final Language language = injectAndProtection.getLanguage(); + if (LanguageUtil.isInjectableLanguage(language)) { registrar .startInjecting(language) diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/InjectionInfo.java b/xml/impl/src/com/intellij/psi/impl/source/html/InjectionInfo.java new file mode 100644 index 000000000000..7759113af321 --- /dev/null +++ b/xml/impl/src/com/intellij/psi/impl/source/html/InjectionInfo.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.html; + +import com.intellij.lang.Language; +import org.jetbrains.annotations.NotNull; + +/** + * Information about language injection + * + * @author Ilya.Kazakevich + */ +public class InjectionInfo { + private final boolean myIsDeniedByExtensionPoint; + @NotNull + private final Language myLanguage; + + InjectionInfo(boolean deniedByExtensionPoint, @NotNull Language language) { + myIsDeniedByExtensionPoint = deniedByExtensionPoint; + myLanguage = language; + } + + /** + * @return true if injection is denied by {@link HtmlScriptInjectionBlocker} + */ + public boolean isDeniedByExtensionPoint() { + return myIsDeniedByExtensionPoint; + } + + /** + * @return language to inject + */ + @NotNull + public Language getLanguage() { + return myLanguage; + } +} From 208d8bca136804e6ad7d321a0d02fcd1128d3f60 Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Wed, 22 Apr 2015 22:28:31 +0300 Subject: [PATCH 2/4] Tests added for "Skipping json injection to HTML for Django templates (for PY-15074)" --- .../jetbrains/python/fixtures/PyTestCase.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java index a44ed7a65d8a..61da6f912088 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java @@ -30,16 +30,20 @@ import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.impl.FilePropertyPusher; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.DirectoryProjectConfigurator; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.testFramework.LightProjectDescriptor; @@ -55,6 +59,7 @@ import com.intellij.usageView.UsageInfo; import com.intellij.usages.Usage; import com.intellij.usages.rules.PsiElementUsage; import com.intellij.util.CommonProcessors.CollectProcessor; +import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PythonHelpersLocator; import com.jetbrains.python.PythonTestUtil; import com.jetbrains.python.psi.LanguageLevel; @@ -94,6 +99,24 @@ public abstract class PyTestCase extends UsefulTestCase { return null; } + /** + * Reformats currently configured file. + */ + protected final void reformatFile() { + WriteCommandAction.runWriteCommandAction(null, new Runnable() { + @Override + public void run() { + doPerformFormatting(); + } + }); + } + + private void doPerformFormatting() throws IncorrectOperationException { + final PsiFile file = myFixture.getFile(); + final TextRange myTextRange = file.getTextRange(); + CodeStyleManager.getInstance(myFixture.getProject()).reformatText(file, myTextRange.getStartOffset(), myTextRange.getEndOffset()); + } + @Override protected void setUp() throws Exception { super.setUp(); From 374b1174de98bdae162902fc275d81efd485378c Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Wed, 22 Apr 2015 22:52:49 +0300 Subject: [PATCH 3/4] EP removed mistakenly reverted (for PY-15074)" --- platform/platform-resources/src/META-INF/XmlPlugin.xml | 2 +- python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 785c0b26160e..dbd7e8ffd2cd 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -458,7 +458,7 @@ - + diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java index 61da6f912088..0e6c77e4f6f2 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java @@ -35,7 +35,6 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.impl.FilePropertyPusher; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; From 69a732354ff3032e59f1e8384878f5186dd98773 Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Thu, 23 Apr 2015 17:53:16 +0300 Subject: [PATCH 4/4] Fixed for PY-15074) according to Dmitry's review --- .../html/HtmlScriptInjectionBlocker.java | 3 +- .../HtmlScriptInjectionBlockerExtension.java | 27 ++++------ .../html/HtmlScriptLanguageInjector.java | 24 +++++++-- .../psi/impl/source/html/InjectionInfo.java | 50 ------------------- 4 files changed, 31 insertions(+), 73 deletions(-) delete mode 100644 xml/impl/src/com/intellij/psi/impl/source/html/InjectionInfo.java diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlocker.java b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlocker.java index b9c4b095d103..dab20cf50e1e 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlocker.java +++ b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlocker.java @@ -16,7 +16,6 @@ package com.intellij.psi.impl.source.html; import com.intellij.lang.Language; -import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.psi.xml.XmlTag; import org.jetbrains.annotations.NotNull; @@ -31,5 +30,5 @@ public interface HtmlScriptInjectionBlocker { * @param language language that should be injected according to
type
attribute * @return true if language should not be injected */ - boolean isDenyLanguageInjection(@NotNull XmlTag scriptTag, @NotNull Language language); + boolean isLanguageInjectionDenied(@NotNull XmlTag scriptTag, @NotNull Language language); } diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlockerExtension.java b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlockerExtension.java index f7385dfcbdaa..1a99a5845489 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlockerExtension.java +++ b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptInjectionBlockerExtension.java @@ -18,9 +18,7 @@ package com.intellij.psi.impl.source.html; import com.intellij.lang.Language; import com.intellij.lang.LanguageExtension; import com.intellij.psi.xml.XmlTag; -import com.intellij.xml.util.HtmlUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -39,31 +37,26 @@ public final class HtmlScriptInjectionBlockerExtension extends LanguageExtension /** - * Finds language to be injected into script tag and checks if is blocked by some extension point + * Checks if language injection to this tag is explicitly denied by one or more @link HtmlScriptInjectionBlocker extension points} * - * @param xmlTag tag that may be script tag. - * @return null if tag is not script tag at all, info otherwise + * @param xmlTag <script> tag + * @param language lang user wants to inject (probably obtained via {@link HtmlScriptLanguageInjector} + * @return true if injection denied by extension point */ - @Nullable - public static InjectionInfo getInjectionInfo(@NotNull XmlTag xmlTag) { - if (!HtmlUtil.isScriptTag(xmlTag)) { - return null; - } - String mimeType = xmlTag.getAttributeValue("type"); - Collection languages = Language.findInstancesByMimeType(mimeType); - Language language = !languages.isEmpty() ? languages.iterator().next() : Language.ANY; + public static boolean isInjectionBlocked(@NotNull XmlTag xmlTag, @NotNull Language language) { Collection allFileLanguages = xmlTag.getContainingFile().getViewProvider().getLanguages(); + for (Language fileLanguage : allFileLanguages) { - for (final HtmlScriptInjectionBlocker blocker : INSTANCE.allForLanguage(fileLanguage)) { - if (blocker.isDenyLanguageInjection(xmlTag, language)) { + for (HtmlScriptInjectionBlocker blocker : INSTANCE.allForLanguage(fileLanguage)) { + if (blocker.isLanguageInjectionDenied(xmlTag, language)) { // Language exists, but denied by EP - return new InjectionInfo(true, language); + return true; } } } // Language exists and not denied - return new InjectionInfo(false, language); + return false; } } diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptLanguageInjector.java b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptLanguageInjector.java index c2edd96ba211..701046da40c3 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptLanguageInjector.java +++ b/xml/impl/src/com/intellij/psi/impl/source/html/HtmlScriptLanguageInjector.java @@ -26,13 +26,30 @@ import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlText; import com.intellij.xml.util.HtmlUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.Collections; import java.util.List; public class HtmlScriptLanguageInjector implements MultiHostInjector { + /** + * Finds language to be injected into <script> tag + * + * @param xmlTag <script> tag + * @return language to inject or null if no language found or not a script tag at all + */ + @Nullable + public static Language getScriptLanguageToInject(@NotNull XmlTag xmlTag) { + if (!HtmlUtil.isScriptTag(xmlTag)) { + return null; + } + String mimeType = xmlTag.getAttributeValue("type"); + Collection languages = Language.findInstancesByMimeType(mimeType); + return !languages.isEmpty() ? languages.iterator().next() : Language.ANY; + } @Override public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement host) { @@ -44,12 +61,11 @@ public class HtmlScriptLanguageInjector implements MultiHostInjector { if (scriptTag == null) { return; } + final Language language = getScriptLanguageToInject(scriptTag); - final InjectionInfo injectAndProtection = HtmlScriptInjectionBlockerExtension.getInjectionInfo(scriptTag); - if (injectAndProtection == null || injectAndProtection.isDeniedByExtensionPoint()) { - return; // Not even a script tag, or language injection denied by EP + if (language == null || HtmlScriptInjectionBlockerExtension.isInjectionBlocked(scriptTag, language)) { + return; } - final Language language = injectAndProtection.getLanguage(); if (LanguageUtil.isInjectableLanguage(language)) { registrar diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/InjectionInfo.java b/xml/impl/src/com/intellij/psi/impl/source/html/InjectionInfo.java deleted file mode 100644 index 7759113af321..000000000000 --- a/xml/impl/src/com/intellij/psi/impl/source/html/InjectionInfo.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.impl.source.html; - -import com.intellij.lang.Language; -import org.jetbrains.annotations.NotNull; - -/** - * Information about language injection - * - * @author Ilya.Kazakevich - */ -public class InjectionInfo { - private final boolean myIsDeniedByExtensionPoint; - @NotNull - private final Language myLanguage; - - InjectionInfo(boolean deniedByExtensionPoint, @NotNull Language language) { - myIsDeniedByExtensionPoint = deniedByExtensionPoint; - myLanguage = language; - } - - /** - * @return true if injection is denied by {@link HtmlScriptInjectionBlocker} - */ - public boolean isDeniedByExtensionPoint() { - return myIsDeniedByExtensionPoint; - } - - /** - * @return language to inject - */ - @NotNull - public Language getLanguage() { - return myLanguage; - } -}