diff --git a/platform/core-impl/src/com/intellij/util/CachedValueImpl.java b/platform/core-impl/src/com/intellij/util/CachedValueImpl.java index 9af99b876cac..d2751d8fdaa2 100644 --- a/platform/core-impl/src/com/intellij/util/CachedValueImpl.java +++ b/platform/core-impl/src/com/intellij/util/CachedValueImpl.java @@ -24,11 +24,12 @@ */ package com.intellij.util; +import com.intellij.openapi.project.Project; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import org.jetbrains.annotations.NotNull; -public abstract class CachedValueImpl extends CachedValueBase implements CachedValue { +public class CachedValueImpl extends CachedValueBase implements CachedValue { private final CachedValueProvider myProvider; public CachedValueImpl(@NotNull CachedValueProvider provider) { @@ -50,4 +51,9 @@ public abstract class CachedValueImpl extends CachedValueBase implements C public T getValue() { return getValueWithLock(null); } + + @Override + public boolean isFromMyProject(Project project) { + return true; + } } diff --git a/platform/util/src/com/intellij/util/PairProcessor.java b/platform/util/src/com/intellij/util/PairProcessor.java index 4b744aec2922..282739dec1d2 100644 --- a/platform/util/src/com/intellij/util/PairProcessor.java +++ b/platform/util/src/com/intellij/util/PairProcessor.java @@ -26,5 +26,11 @@ public interface PairProcessor { return true; } }; + PairProcessor FALSE = new PairProcessor() { + @Override + public boolean process(Object o, Object o1) { + return false; + } + }; boolean process(S s, T t); } diff --git a/plugins/IntelliLang/IntelliLang-tests/IntelliLang-tests.iml b/plugins/IntelliLang/IntelliLang-tests/IntelliLang-tests.iml new file mode 100644 index 000000000000..559be398f4b4 --- /dev/null +++ b/plugins/IntelliLang/IntelliLang-tests/IntelliLang-tests.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/plugins/IntelliLang/IntelliLang-tests/test/org/intellij/plugins/intelliLang/ReferenceInjectionTest.java b/plugins/IntelliLang/IntelliLang-tests/test/org/intellij/plugins/intelliLang/ReferenceInjectionTest.java new file mode 100644 index 000000000000..69b3d0a9354b --- /dev/null +++ b/plugins/IntelliLang/IntelliLang-tests/test/org/intellij/plugins/intelliLang/ReferenceInjectionTest.java @@ -0,0 +1,26 @@ +package org.intellij.plugins.intelliLang; + +import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; +import org.intellij.plugins.intelliLang.inject.InjectLanguageAction; +import org.intellij.plugins.intelliLang.inject.UnInjectLanguageAction; +import org.intellij.plugins.intelliLang.references.FileReferenceInjector; + +/** + * @author Dmitry Avdeev + * Date: 02.08.13 + */ +public class ReferenceInjectionTest extends LightPlatformCodeInsightFixtureTestCase { + + public void testInjectReference() throws Exception { + + myFixture.configureByText("foo.xml", "r\"/>"); + assertNull(myFixture.getReferenceAtCaretPosition()); + + InjectLanguageAction.invokeImpl(getProject(), myFixture.getEditor(), myFixture.getFile(), new FileReferenceInjector()); + assertNotNull(myFixture.getReferenceAtCaretPosition()); + + UnInjectLanguageAction.invokeImpl(getProject(), myFixture.getEditor(), myFixture.getFile()); + assertNull(myFixture.getReferenceAtCaretPosition()); + } +} diff --git a/plugins/IntelliLang/src/META-INF/plugin.xml b/plugins/IntelliLang/src/META-INF/plugin.xml index 7affec3fafd0..a7bc30e9fc9b 100644 --- a/plugins/IntelliLang/src/META-INF/plugin.xml +++ b/plugins/IntelliLang/src/META-INF/plugin.xml @@ -16,7 +16,9 @@ + + @@ -48,5 +50,12 @@ serviceImplementation="org.intellij.plugins.intelliLang.Configuration$Prj"/> + + + + + + + diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/Configuration.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/Configuration.java index 6cb44e0d2369..867833638a08 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/Configuration.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/Configuration.java @@ -15,6 +15,7 @@ */ package org.intellij.plugins.intelliLang; +import com.intellij.lang.Language; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.command.undo.GlobalUndoableAction; @@ -30,11 +31,14 @@ import com.intellij.psi.PsiCompiledElement; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiLanguageInjectionHost; +import com.intellij.psi.util.CachedValue; +import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.*; import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; +import com.intellij.util.containers.MultiMap; import gnu.trove.THashMap; import gnu.trove.THashSet; import org.intellij.plugins.intelliLang.inject.InjectorUtils; @@ -65,6 +69,12 @@ import java.util.*; public class Configuration implements PersistentStateComponent, ModificationTracker { static final Logger LOG = Logger.getInstance(Configuration.class.getName()); + private static final Condition LANGUAGE_INJECTION_CONDITION = new Condition() { + @Override + public boolean value(BaseInjection o) { + return Language.findLanguageByID(o.getInjectedLanguageId()) != null; + } + }; @State( name = Configuration.COMPONENT_NAME, @@ -126,6 +136,13 @@ public class Configuration implements PersistentStateComponent, Modific return myParentConfiguration.getDefaultInjections(); } + @Override + protected Collection getAllInjections() { + Collection injections = super.getAllInjections(); + injections.addAll(myParentConfiguration.getAllInjections()); + return injections; + } + @NotNull @Override public List getInjections(final String injectorId) { @@ -149,8 +166,8 @@ public class Configuration implements PersistentStateComponent, Modific List originalInjections, boolean forceLevel) { if (!forceLevel && !originalInjections.isEmpty()) { - if (myParentConfiguration.replaceInjections(Collections.emptyList(), originalInjections, forceLevel)) { - myParentConfiguration.replaceInjections(newInjections, Collections.emptyList(), forceLevel); + if (myParentConfiguration.replaceInjections(Collections.emptyList(), originalInjections, false)) { + myParentConfiguration.replaceInjections(newInjections, Collections.emptyList(), false); return true; } } @@ -190,6 +207,26 @@ public class Configuration implements PersistentStateComponent, Modific } }; + protected Collection getAllInjections() { + ArrayList injections = new ArrayList(); + for (List list : myInjections.values()) { + injections.addAll(list); + } + return injections; + } + + private CachedValue> myInjectionsById = new CachedValueImpl>(new CachedValueProvider>() { + @Nullable + @Override + public Result> compute() { + MultiMap map = new MultiMap(); + for (BaseInjection injection : getAllInjections()) { + map.putValue(injection.getInjectedLanguageId(), injection); + } + return Result.create(map, Configuration.this); + } + }); + private volatile long myModificationCount; public Configuration() { @@ -206,7 +243,7 @@ public class Configuration implements PersistentStateComponent, Modific supports.put(support.getId(), support); } loadStateOld(element, supports.get(LanguageInjectionSupport.XML_SUPPORT_ID), supports.get(LanguageInjectionSupport.JAVA_SUPPORT_ID)); - for (Element child : (List)element.getChildren("injection")){ + for (Element child : element.getChildren("injection")){ final String key = child.getAttributeValue("injector-id"); final LanguageInjectionSupport support = supports.get(key); final BaseInjection injection = support == null ? new BaseInjection(key) : support.createInjection(child); @@ -350,6 +387,10 @@ public class Configuration implements PersistentStateComponent, Modific return Collections.emptyList(); } + public Collection getInjectionsByLanguageId(String languageId) { + return myInjectionsById.getValue().get(languageId); + } + @Nullable public static Configuration load(final InputStream is) throws IOException, JDOMException { try { @@ -438,7 +479,7 @@ public class Configuration implements PersistentStateComponent, Modific } } - public void configurationModified() { + private void configurationModified() { myModificationCount ++; } @@ -492,6 +533,9 @@ public class Configuration implements PersistentStateComponent, Modific } } + /** + * @param injectorId see {@link org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport#getId()} + */ @NotNull public List getInjections(final String injectorId) { return Collections.unmodifiableList(myInjections.get(injectorId)); @@ -506,7 +550,10 @@ public class Configuration implements PersistentStateComponent, Modific public boolean process(final List add, final List remove) { replaceInjectionsWithUndoInner(add, remove); - FileContentUtil.reparseOpenedFiles(); + if (ContainerUtil.find(add, LANGUAGE_INJECTION_CONDITION) != null || ContainerUtil.find(remove, + LANGUAGE_INJECTION_CONDITION) != null) { + FileContentUtil.reparseOpenedFiles(); + } return true; } }); diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java index 6ba500af8a10..68869a4eb445 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java @@ -16,7 +16,6 @@ package org.intellij.plugins.intelliLang.inject; -import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.fileTypes.FileTypeManager; @@ -49,10 +48,6 @@ public abstract class AbstractLanguageInjectionSupport extends LanguageInjection return false; } - public boolean addInjectionInPlace(final Language language, final PsiLanguageInjectionHost psiElement) { - return false; - } - public boolean removeInjectionInPlace(final PsiLanguageInjectionHost psiElement) { return false; } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java index f5876f4d69af..267148f0adba 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java @@ -17,12 +17,9 @@ package org.intellij.plugins.intelliLang.inject; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.injected.editor.EditorWindow; -import com.intellij.lang.Language; -import com.intellij.lang.LanguageUtil; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.PopupChooserBuilder; @@ -32,6 +29,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiLanguageInjectionHost; +import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.PsiModificationTrackerImpl; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.ui.ListCellRendererWrapper; import com.intellij.ui.components.JBList; @@ -39,14 +38,13 @@ import com.intellij.util.FileContentUtil; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; -import com.intellij.util.ui.EmptyIcon; import org.intellij.plugins.intelliLang.Configuration; +import org.intellij.plugins.intelliLang.references.Injectable; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -80,12 +78,13 @@ public class InjectLanguageAction implements IntentionAction { } public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - doChooseLanguageToInject(editor, new Processor() { - public boolean process(final Language language) { - if (project.isDisposed()) return false; + doChooseLanguageToInject(editor, new Processor() { + public boolean process(final Injectable injectable) { ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { - invokeImpl(project, editor, file, language); + if (!project.isDisposed()) { + invokeImpl(project, editor, file, injectable); + } } }); return false; @@ -93,46 +92,50 @@ public class InjectLanguageAction implements IntentionAction { }); } - private static void invokeImpl(Project project, Editor editor, PsiFile file, Language language) { + public static void invokeImpl(Project project, Editor editor, PsiFile file, Injectable injectable) { final PsiLanguageInjectionHost host = findInjectionHost(editor, file); if (host == null) return; - if (defaultFunctionalityWorked(host, language)) return; + if (defaultFunctionalityWorked(host, injectable.getId())) return; + try { for (LanguageInjectionSupport support : InjectorUtils.getActiveInjectionSupports()) { - if (support.addInjectionInPlace(language, host)) return; + if (support.addInjectionInPlace(injectable, host)) { + ((PsiModificationTrackerImpl)PsiManager.getInstance(project).getModificationTracker()).incCounter(); + return; + } } - TemporaryPlacesRegistry.getInstance(project).getLanguageInjectionSupport().addInjectionInPlace(language, host); + TemporaryPlacesRegistry.getInstance(project).getLanguageInjectionSupport().addInjectionInPlace(injectable, host); } finally { - FileContentUtil.reparseFiles(project, Collections.emptyList(), true); + if (injectable.getLanguage() != null) { // no need for reference injection + FileContentUtil.reparseFiles(project, Collections.emptyList(), true); + } } } - private static boolean defaultFunctionalityWorked(final PsiLanguageInjectionHost host, final Language language) { - return Configuration.getProjectInstance(host.getProject()).setHostInjectionEnabled(host, Collections.singleton(language.getID()), true); + private static boolean defaultFunctionalityWorked(final PsiLanguageInjectionHost host, String id) { + return Configuration.getProjectInstance(host.getProject()).setHostInjectionEnabled(host, Collections.singleton(id), true); } - private static boolean doChooseLanguageToInject(Editor editor, final Processor onChosen) { - final Language[] languages = InjectedLanguage.getAvailableLanguages(); - Arrays.sort(languages, LanguageUtil.LANGUAGE_COMPARATOR); + private static boolean doChooseLanguageToInject(Editor editor, final Processor onChosen) { + final List injectables = Injectable.getAllInjectables(); - final JList list = new JBList(languages); - list.setCellRenderer(new ListCellRendererWrapper() { + final JList list = new JBList(injectables); + list.setCellRenderer(new ListCellRendererWrapper() { @Override - public void customize(JList list, Language language, int index, boolean selected, boolean hasFocus) { - final FileType ft = language.getAssociatedFileType(); - setIcon(ft != null ? ft.getIcon() : EmptyIcon.ICON_16); - setText(language.getDisplayName() + (ft != null ? " (" + ft.getDescription() + ")" : "")); + public void customize(JList list, Injectable language, int index, boolean selected, boolean hasFocus) { + setIcon(language.getIcon()); + setText(language.getDisplayName()); } }); new PopupChooserBuilder(list).setItemChoosenCallback(new Runnable() { public void run() { - onChosen.process((Language)list.getSelectedValue()); + onChosen.process((Injectable)list.getSelectedValue()); } }).setFilteringEnabled(new Function() { @Override public String fun(Object language) { - return ((Language)language).getDisplayName(); + return ((Injectable)language).getDisplayName(); } }) .createPopup().showInBestPositionFor(editor); diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java index 5645bc7cc383..ff7a87d75524 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java @@ -28,6 +28,7 @@ import com.intellij.ui.SimpleColoredText; import com.intellij.util.Consumer; import org.intellij.plugins.intelliLang.Configuration; import org.intellij.plugins.intelliLang.inject.config.BaseInjection; +import org.intellij.plugins.intelliLang.references.Injectable; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -56,7 +57,26 @@ public abstract class LanguageInjectionSupport { public abstract boolean useDefaultInjector(final PsiElement host); - public abstract boolean addInjectionInPlace(final Language language, final PsiLanguageInjectionHost psiElement); + public boolean addInjectionInPlace(final Language language, final PsiLanguageInjectionHost psiElement) { + return addInjectionInPlace(language.getID(), psiElement); + } + + public boolean addInjectionInPlace(final String id, final PsiLanguageInjectionHost psiElement) { + return false; + } + + public final boolean addInjectionInPlace(final Injectable injectable, final PsiLanguageInjectionHost psiElement) { + if (injectable.getLanguage() == null) { + boolean result = addInjectionInPlace(injectable.getId(), psiElement); + if (result) { + psiElement.putUserData(INJECTOR_SUPPORT, this); + } + return result; + } + else { + return addInjectionInPlace(injectable.getLanguage(), psiElement); + } + } public abstract boolean removeInjectionInPlace(final PsiLanguageInjectionHost psiElement); diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/UnInjectLanguageAction.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/UnInjectLanguageAction.java index 896c27f7c09c..d340eda42775 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/UnInjectLanguageAction.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/UnInjectLanguageAction.java @@ -25,14 +25,14 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiLanguageInjectionHost; +import com.intellij.psi.*; +import com.intellij.psi.impl.PsiModificationTrackerImpl; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.util.FileContentUtil; import com.intellij.util.IncorrectOperationException; import gnu.trove.THashSet; import org.intellij.plugins.intelliLang.Configuration; +import org.intellij.plugins.intelliLang.references.InjectedReferencesContributor; import org.jetbrains.annotations.NotNull; import java.util.Collections; @@ -55,10 +55,13 @@ public class UnInjectLanguageAction implements IntentionAction, LowPriorityActio public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { final int offset = editor.getCaretModel().getOffset(); - final PsiFile psiFile = InjectedLanguageUtil.findInjectedPsiNoCommit(file, offset); - if (psiFile == null) return false; - final LanguageInjectionSupport support = psiFile.getUserData(LanguageInjectionSupport.INJECTOR_SUPPORT); - return support != null; + PsiElement element = InjectedLanguageUtil.findInjectedPsiNoCommit(file, offset); + if (element == null) { + PsiReference reference = file.findReferenceAt(offset); + if (reference == null) return false; + return reference.getElement().getUserData(InjectedReferencesContributor.INJECTED_REFERENCE) != null; + } + return element.getUserData(LanguageInjectionSupport.INJECTOR_SUPPORT) != null; } public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { @@ -69,9 +72,21 @@ public class UnInjectLanguageAction implements IntentionAction, LowPriorityActio }); } - private static void invokeImpl(Project project, Editor editor, PsiFile file) { - final PsiFile psiFile = InjectedLanguageUtil.findInjectedPsiNoCommit(file, editor.getCaretModel().getOffset()); - if (psiFile == null) return; + public static void invokeImpl(Project project, Editor editor, PsiFile file) { + int offset = editor.getCaretModel().getOffset(); + final PsiFile psiFile = InjectedLanguageUtil.findInjectedPsiNoCommit(file, offset); + if (psiFile == null) { + PsiReference reference = file.findReferenceAt(offset); + if (reference != null) { + PsiElement element = reference.getElement(); + LanguageInjectionSupport support = element.getUserData(LanguageInjectionSupport.INJECTOR_SUPPORT); + if (support != null && element instanceof PsiLanguageInjectionHost) { + support.removeInjectionInPlace((PsiLanguageInjectionHost)element); + ((PsiModificationTrackerImpl)PsiManager.getInstance(project).getModificationTracker()).incCounter(); + } + } + return; + } final PsiLanguageInjectionHost host = InjectedLanguageManager.getInstance(project).getInjectionHost(psiFile); if (host == null) return; final LanguageInjectionSupport support = psiFile.getUserData(LanguageInjectionSupport.INJECTOR_SUPPORT); diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/BaseInjection.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/BaseInjection.java index f26995bba5f1..ed6b836d7c23 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/BaseInjection.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/BaseInjection.java @@ -65,7 +65,7 @@ public class BaseInjection implements Injection, PersistentStateComponent placeElements = (List)element.getChildren("place"); + final List placeElements = element.getChildren("place"); myPlaces = InjectionPlace.ARRAY_FACTORY.create(placeElements.size()); for (int i = 0, placeElementsSize = placeElements.size(); i < placeElementsSize; i++) { Element placeElement = placeElements.get(i); @@ -384,6 +384,10 @@ public class BaseInjection implements Injection, PersistentStateComponent" +getDisplayName(); diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/FileReferenceInjector.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/FileReferenceInjector.java new file mode 100644 index 000000000000..1b28df2208dc --- /dev/null +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/FileReferenceInjector.java @@ -0,0 +1,49 @@ +/* + * 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.intellij.plugins.intelliLang.references; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; + +/** + * @author Dmitry Avdeev + * Date: 01.08.13 + */ +public class FileReferenceInjector extends ReferenceInjector { + + @NotNull + @Override + public String getId() { + return "file-reference"; + } + + @NotNull + @Override + public String getDisplayName() { + return "File Reference"; + } + + @NotNull + @Override + public PsiReference[] getReferences(@NotNull PsiElement element, @NotNull ProcessingContext context, @NotNull TextRange range) { + String text = range.substring(element.getText()); + return new FileReferenceSet(text, element, range.getStartOffset(), null, true).getAllReferences(); + } +} diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/Injectable.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/Injectable.java new file mode 100644 index 000000000000..3cee69134201 --- /dev/null +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/Injectable.java @@ -0,0 +1,100 @@ +/* + * 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.intellij.plugins.intelliLang.references; + +import com.intellij.lang.Language; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.util.ui.EmptyIcon; +import org.intellij.plugins.intelliLang.inject.InjectedLanguage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author Dmitry Avdeev + * Date: 01.08.13 + */ +public abstract class Injectable implements Comparable { + + /** Unique ID among injected language and reference injector IDs */ + @NotNull + public abstract String getId(); + + @NotNull + public abstract String getDisplayName(); + + @NotNull + public Icon getIcon() { + return EmptyIcon.ICON_16; + } + + @Override + public int compareTo(@NotNull Injectable o) { + return getDisplayName().compareTo(o.getDisplayName()); + } + + /** + * @return null for reference injections + */ + @Nullable + public abstract Language getLanguage(); + + public static List getAllInjectables() { + Language[] languages = InjectedLanguage.getAvailableLanguages(); + List list = new ArrayList(); + for (Language language : languages) { + list.add(fromLanguage(language)); + } + list.addAll(Arrays.asList(ReferenceInjector.EXTENSION_POINT_NAME.getExtensions())); + Collections.sort(list); + return list; + } + + public static Injectable fromLanguage(final Language language) { + return new Injectable() { + @NotNull + @Override + public String getId() { + return language.getID(); + } + + @NotNull + @Override + public String getDisplayName() { + final FileType ft = language.getAssociatedFileType(); + return language.getDisplayName() + (ft != null ? " (" + ft.getDescription() + ")" : ""); + } + + @NotNull + @Override + public Icon getIcon() { + final FileType ft = language.getAssociatedFileType(); + //noinspection ConstantConditions + return ft != null && ft.getIcon() != null ? ft.getIcon() : EmptyIcon.ICON_16; + } + + @Override + public Language getLanguage() { + return language; + } + }; + } +} diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/InjectedReferencesContributor.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/InjectedReferencesContributor.java new file mode 100644 index 000000000000..6a9c8544650a --- /dev/null +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/InjectedReferencesContributor.java @@ -0,0 +1,65 @@ +/* + * 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.intellij.plugins.intelliLang.references; + +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.TextRange; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.*; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ProcessingContext; +import org.intellij.plugins.intelliLang.Configuration; +import org.intellij.plugins.intelliLang.inject.config.BaseInjection; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; + +/** + * @author Dmitry Avdeev + * Date: 01.08.13 + */ +public class InjectedReferencesContributor extends PsiReferenceContributor { + + public static final Key INJECTED_REFERENCE = Key.create("injected reference"); + + @Override + public void registerReferenceProviders(PsiReferenceRegistrar registrar) { + registrar.registerReferenceProvider(PlatformPatterns.psiElement(), new PsiReferenceProvider() { + @NotNull + @Override + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { + element.putUserData(INJECTED_REFERENCE, null); + ReferenceInjector[] extensions = ReferenceInjector.EXTENSION_POINT_NAME.getExtensions(); + PsiReference[] references = new PsiReference[0]; + Configuration configuration = Configuration.getProjectInstance(element.getProject()); + for (ReferenceInjector extension : extensions) { + Collection injections = configuration.getInjectionsByLanguageId(extension.getId()); + for (BaseInjection injection : injections) { + if (injection.acceptForReference(element)) { + element.putUserData(INJECTED_REFERENCE, extension); + List area = injection.getInjectedArea(element); + for (TextRange range : area) { + references = ArrayUtil.mergeArrays(references, extension.getReferences(element, context, range)); + } + } + } + } + return references; + } + }); + } +} diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/ReferenceInjector.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/ReferenceInjector.java new file mode 100644 index 000000000000..e2cf413c4af8 --- /dev/null +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/references/ReferenceInjector.java @@ -0,0 +1,41 @@ +/* + * 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.intellij.plugins.intelliLang.references; + +import com.intellij.lang.Language; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; + +/** + * @author Dmitry Avdeev + * Date: 01.08.13 + */ +public abstract class ReferenceInjector extends Injectable { + + final static ExtensionPointName EXTENSION_POINT_NAME = ExtensionPointName.create("org.intellij.intelliLang.referenceInjector"); + + @Override + public final Language getLanguage() { + return null; + } + + @NotNull + public abstract PsiReference[] getReferences(@NotNull PsiElement element, @NotNull final ProcessingContext context, @NotNull TextRange range); +} diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlAttributeInjection.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlAttributeInjection.java index 4120f603b88c..5b2f291ae202 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlAttributeInjection.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlAttributeInjection.java @@ -17,7 +17,6 @@ package org.intellij.plugins.intelliLang.inject.config; import com.intellij.openapi.util.JDOMExternalizer; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.patterns.compiler.PatternCompiler; import com.intellij.psi.PsiElement; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; @@ -26,9 +25,6 @@ import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import java.util.Collections; -import java.util.List; - import static org.intellij.plugins.intelliLang.inject.InjectorUtils.appendStringPattern; public class XmlAttributeInjection extends AbstractTagInjection { @@ -151,4 +147,13 @@ public class XmlAttributeInjection extends AbstractTagInjection { return result.toString(); } + @Override + public boolean acceptForReference(PsiElement element) { + if (element instanceof XmlAttributeValue) { + PsiElement parent = element.getParent(); + return parent instanceof XmlAttribute && acceptsPsiElement(parent); + } + return false; + } + } diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjectionSupport.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjectionSupport.java index e3bea1aaa51c..57fb1b846d21 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjectionSupport.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjectionSupport.java @@ -16,7 +16,6 @@ package org.intellij.plugins.intelliLang.inject.xml; -import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.options.Configurable; @@ -89,13 +88,13 @@ public class XmlLanguageInjectionSupport extends AbstractLanguageInjectionSuppor return false; } - public boolean addInjectionInPlace(final Language language, final PsiLanguageInjectionHost psiElement) { + public boolean addInjectionInPlace(final String id, final PsiLanguageInjectionHost psiElement) { if (!isMine(psiElement)) return false; if (psiElement instanceof XmlAttributeValue) { - return doInjectInAttributeValue((XmlAttributeValue)psiElement, language.getID()); + return doInjectInAttributeValue((XmlAttributeValue)psiElement, id); } else if (psiElement instanceof XmlText) { - return doInjectInXmlText((XmlText)psiElement, language.getID()); + return doInjectInXmlText((XmlText)psiElement, id); } return false; }