diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java index f9e9fe78247a..7844515f4492 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java @@ -18,6 +18,8 @@ package org.jetbrains.plugins.javaFX.fxml; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.openapi.application.PluginPathManager; import com.intellij.openapi.module.Module; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.testFramework.LightProjectDescriptor; @@ -33,6 +35,8 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) { PsiTestUtil.addLibrary(module, model, "javafx", PluginPathManager.getPluginHomePath("javaFX") + "/testData", "jfxrt.jar"); PsiTestUtil.addLibrary(module, model, "groovy", PluginPathManager.getPluginHomePath("groovy") + "/testdata/mockGroovyLib1.8", "groovy-1.8.0-beta-2.jar"); + final Sdk projectJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk(); + PsiTestUtil.addLibrary(module, model, "rt", projectJdk.getHomePath() + "/lib", "rt.jar"); super.configureModule(module, model, contentEntry); } }; @@ -55,6 +59,14 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { doTest("Create Field 'btn'", ".java"); } + public void testRegisterPageLanguage() throws Exception { + myFixture.configureByFile(getTestName(true) + ".fxml"); + final IntentionAction intention = myFixture.findSingleIntention("Specify page language"); + assertNotNull(intention); + myFixture.launchAction(intention); + myFixture.checkResultByFile(getTestName(true) + ".fxml", getTestName(true) + "_after.fxml", true); + } + public void testWrapWithDefine() throws Exception { final IntentionAction intention = myFixture.getAvailableIntention("Wrap \"lb\" with fx:define", getTestName(true) + ".fxml"); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/ScriptLanguageInjector.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/ScriptLanguageInjector.java index e97c5ea64b11..814d0307b5d3 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/ScriptLanguageInjector.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/ScriptLanguageInjector.java @@ -1,10 +1,10 @@ - package org.jetbrains.plugins.javaFX.fxml; import com.intellij.lang.Language; import com.intellij.lang.injection.MultiHostInjector; import com.intellij.lang.injection.MultiHostRegistrar; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.patterns.XmlElementPattern; import com.intellij.patterns.XmlPatterns; import com.intellij.psi.PsiElement; @@ -23,12 +23,17 @@ public class ScriptLanguageInjector implements MultiHostInjector { public void getLanguagesToInject(@NotNull final MultiHostRegistrar registrar, @NotNull final PsiElement host) { if (SCRIPT_PATTERN.accepts(host)) { - final Language language = Language.findLanguageByID("JavaScript"); - if (language != null && JavaFxPsiUtil.parseInjectedLanguages((XmlFile)host.getContainingFile()).contains("javascript")) { - registrar.startInjecting(language) - .addPlace(null, null, (PsiLanguageInjectionHost) host, - TextRange.from(0, host.getTextLength() - 1)) - .doneInjecting(); + final List registeredLanguages = JavaFxPsiUtil.parseInjectedLanguages((XmlFile)host.getContainingFile()); + for (Language language : Language.getRegisteredLanguages()) { + for (String registeredLanguage : registeredLanguages) { + if (StringUtil.equalsIgnoreCase(language.getID(), registeredLanguage)) { + registrar.startInjecting(language) + .addPlace(null, null, (PsiLanguageInjectionHost) host, + TextRange.from(0, host.getTextLength() - 1)) + .doneInjecting(); + break; + } + } } } } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/intentions/JavaFxInjectPageLanguageIntention.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/intentions/JavaFxInjectPageLanguageIntention.java new file mode 100644 index 000000000000..9f734203c722 --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/intentions/JavaFxInjectPageLanguageIntention.java @@ -0,0 +1,147 @@ +/* + * Copyright 2000-2013 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 org.jetbrains.plugins.javaFX.fxml.codeInsight.intentions; + +import com.intellij.codeInsight.CodeInsightUtilBase; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.OrderEnumerator; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.PsiParserFacade; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlDocument; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlProcessingInstruction; +import com.intellij.psi.xml.XmlProlog; +import com.intellij.ui.components.JBList; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.lang.UrlClassLoader; +import org.jetbrains.annotations.NotNull; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptEngineManager; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; + +/** +* User: anna +* Date: 4/8/13 +*/ +public class JavaFxInjectPageLanguageIntention extends PsiElementBaseIntentionAction { + public static final Logger LOG = Logger.getInstance("#" + JavaFxInjectPageLanguageIntention.class.getName()); + + private static Set getAvailableLanguages(Project project) { + final List engineFactories = new ScriptEngineManager(composeUserClassLoader(project)).getEngineFactories(); + + if (engineFactories != null) { + final Set availableNames = new TreeSet(); + for (ScriptEngineFactory factory : engineFactories) { + final String engineName = (String)factory.getParameter(ScriptEngine.NAME); + availableNames.add(engineName); + } + return availableNames; + } + + return null; + } + + private static UrlClassLoader composeUserClassLoader(Project project) { + final List urls = new ArrayList(); + final List list = OrderEnumerator.orderEntries(project).recursively().runtimeOnly().getPathsList().getPathList(); + for (String path : list) { + try { + urls.add(new File(FileUtil.toSystemIndependentName(path)).toURI().toURL()); + } + catch (MalformedURLException e1) { + LOG.info(e1); + } + } + + return new UrlClassLoader(urls, null); + } + + @Override + public void invoke(@NotNull final Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + if (!CodeInsightUtilBase.preparePsiElementsForWrite(element)) return; + final XmlFile containingFile = (XmlFile)element.getContainingFile(); + + final Set availableLanguages = getAvailableLanguages(project); + if (availableLanguages.size() == 1) { + registerPageLanguage(project, containingFile, availableLanguages.iterator().next()); + } else { + final JBList list = new JBList(availableLanguages); + JBPopupFactory.getInstance().createListPopupBuilder(list) + .setItemChoosenCallback(new Runnable() { + @Override + public void run() { + registerPageLanguage(project, containingFile, (String)list.getSelectedValue()); + } + }).createPopup().showInBestPositionFor(editor); + } + } + + private void registerPageLanguage(final Project project, final XmlFile containingFile, final String languageName) { + new WriteCommandAction.Simple(project, getFamilyName()) { + @Override + protected void run() { + final PsiFileFactory factory = PsiFileFactory.getInstance(project); + final XmlFile dummyFile = (XmlFile)factory.createFileFromText("_Dummy_.fxml", StdFileTypes.XML, + ""); + final XmlDocument document = dummyFile.getDocument(); + if (document != null) { + final XmlProlog prolog = document.getProlog(); + final Collection instructions = PsiTreeUtil.findChildrenOfType(prolog, XmlProcessingInstruction.class); + LOG.assertTrue(instructions.size() == 1); + final XmlDocument xmlDocument = containingFile.getDocument(); + if (xmlDocument != null) { + final XmlProlog xmlProlog = xmlDocument.getProlog(); + if (xmlProlog != null) { + final PsiElement element = xmlProlog.addBefore(instructions.iterator().next(), xmlProlog.getFirstChild()); + xmlProlog.addAfter(PsiParserFacade.SERVICE.getInstance(project).createWhiteSpaceFromText("\n\n"), element); + } + } + } + } + }.execute(); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + setText(getFamilyName()); + return element.isValid(); + } + + @NotNull + @Override + public String getFamilyName() { + return "Specify page language"; + } + + @Override + public boolean startInWriteAction() { + return false; + } +} diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxAnnotator.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxAnnotator.java index bcfb4dc897e4..2e04ca9c2e26 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxAnnotator.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxAnnotator.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.javaFX.fxml.refs; import com.intellij.codeInsight.intention.AddAnnotationFix; import com.intellij.codeInsight.intentions.XmlChooseColorIntentionAction; +import com.intellij.lang.ASTNode; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.Annotator; @@ -28,9 +29,7 @@ import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.presentation.java.SymbolPresentationUtil; -import com.intellij.psi.xml.XmlAttribute; -import com.intellij.psi.xml.XmlAttributeValue; -import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.xml.*; import com.intellij.ui.ColorUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.ui.ColorIcon; @@ -40,11 +39,13 @@ import org.jetbrains.plugins.javaFX.fxml.FxmlConstants; import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonClassNames; import org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory; import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; +import org.jetbrains.plugins.javaFX.fxml.codeInsight.intentions.JavaFxInjectPageLanguageIntention; import org.jetbrains.plugins.javaFX.fxml.codeInsight.intentions.JavaFxWrapWithDefineIntention; import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxDefaultPropertyElementDescriptor; import javax.swing.*; import java.awt.*; +import java.util.List; /** * User: anna @@ -100,6 +101,20 @@ public class JavaFxAnnotator implements Annotator { } } } + else if (element instanceof XmlTag) { + if (FxmlConstants.FX_SCRIPT.equals(((XmlTag)element).getName())) { + final XmlTagValue tagValue = ((XmlTag)element).getValue(); + if (!StringUtil.isEmptyOrSpaces(tagValue.getText())) { + final List langs = JavaFxPsiUtil.parseInjectedLanguages((XmlFile)element.getContainingFile()); + if (langs.isEmpty()) { + final ASTNode openTag = element.getNode().findChildByType(XmlTokenType.XML_NAME); + final Annotation annotation = + holder.createErrorAnnotation(openTag != null ? openTag.getPsi() : element, "Page language not specified."); + annotation.registerFix(new JavaFxInjectPageLanguageIntention()); + } + } + } + } } private static void attachColorIcon(final PsiElement element, AnnotationHolder holder, String attributeValueText) { diff --git a/plugins/javaFX/testData/quickfix/registerPageLanguage.fxml b/plugins/javaFX/testData/quickfix/registerPageLanguage.fxml new file mode 100644 index 000000000000..bc65823a2a8e --- /dev/null +++ b/plugins/javaFX/testData/quickfix/registerPageLanguage.fxml @@ -0,0 +1,6 @@ + + + ipt> + var foo; + + diff --git a/plugins/javaFX/testData/quickfix/registerPageLanguage_after.fxml b/plugins/javaFX/testData/quickfix/registerPageLanguage_after.fxml new file mode 100644 index 000000000000..eeac829d7175 --- /dev/null +++ b/plugins/javaFX/testData/quickfix/registerPageLanguage_after.fxml @@ -0,0 +1,8 @@ + + + + + + var foo; + +