IDEA-97264 IntelliLang support for "Path to File" language

This commit is contained in:
Dmitry Avdeev
2013-08-05 11:25:32 +04:00
parent 66d0794395
commit e1fd938f87
17 changed files with 466 additions and 59 deletions
@@ -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<T> extends CachedValueBase<T> implements CachedValue<T> {
public class CachedValueImpl<T> extends CachedValueBase<T> implements CachedValue<T> {
private final CachedValueProvider<T> myProvider;
public CachedValueImpl(@NotNull CachedValueProvider<T> provider) {
@@ -50,4 +51,9 @@ public abstract class CachedValueImpl<T> extends CachedValueBase<T> implements C
public T getValue() {
return getValueWithLock(null);
}
@Override
public boolean isFromMyProject(Project project) {
return true;
}
}
@@ -26,5 +26,11 @@ public interface PairProcessor<S, T> {
return true;
}
};
PairProcessor FALSE = new PairProcessor() {
@Override
public boolean process(Object o, Object o1) {
return false;
}
};
boolean process(S s, T t);
}
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="testFramework-java" />
<orderEntry type="module" module-name="IntelliLang" />
<orderEntry type="module" module-name="IntelliLang-xml" />
<orderEntry type="module" module-name="xml" />
<orderEntry type="module" module-name="IntelliLang-java" />
</component>
</module>
@@ -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", "<foo xmlns=\"http://foo.bar\" \n" +
" xxx=\"ba<caret>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());
}
}
@@ -16,7 +16,9 @@
<extensionPoints>
<extensionPoint name="languageSupport" interface="org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport"/>
<extensionPoint name="injectionConfig" beanClass="org.intellij.plugins.intelliLang.inject.LanguageInjectionConfigBean"/>
<extensionPoint name="referenceInjector" interface="org.intellij.plugins.intelliLang.references.ReferenceInjector"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
<errorHandler implementation="com.intellij.diagnostic.ITNReporter" />
<intentionAction>
@@ -48,5 +50,12 @@
serviceImplementation="org.intellij.plugins.intelliLang.Configuration$Prj"/>
<compileServer.plugin classpath="intellilang-jps-plugin.jar"/>
<psi.referenceContributor implementation="org.intellij.plugins.intelliLang.references.InjectedReferencesContributor"/>
</extensions>
<extensions defaultExtensionNs="org.intellij.intelliLang">
<referenceInjector implementation="org.intellij.plugins.intelliLang.references.FileReferenceInjector"/>
</extensions>
</idea-plugin>
@@ -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<Element>, ModificationTracker {
static final Logger LOG = Logger.getInstance(Configuration.class.getName());
private static final Condition<BaseInjection> LANGUAGE_INJECTION_CONDITION = new Condition<BaseInjection>() {
@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<Element>, Modific
return myParentConfiguration.getDefaultInjections();
}
@Override
protected Collection<BaseInjection> getAllInjections() {
Collection<BaseInjection> injections = super.getAllInjections();
injections.addAll(myParentConfiguration.getAllInjections());
return injections;
}
@NotNull
@Override
public List<BaseInjection> getInjections(final String injectorId) {
@@ -149,8 +166,8 @@ public class Configuration implements PersistentStateComponent<Element>, Modific
List<? extends BaseInjection> originalInjections,
boolean forceLevel) {
if (!forceLevel && !originalInjections.isEmpty()) {
if (myParentConfiguration.replaceInjections(Collections.<BaseInjection>emptyList(), originalInjections, forceLevel)) {
myParentConfiguration.replaceInjections(newInjections, Collections.<BaseInjection>emptyList(), forceLevel);
if (myParentConfiguration.replaceInjections(Collections.<BaseInjection>emptyList(), originalInjections, false)) {
myParentConfiguration.replaceInjections(newInjections, Collections.<BaseInjection>emptyList(), false);
return true;
}
}
@@ -190,6 +207,26 @@ public class Configuration implements PersistentStateComponent<Element>, Modific
}
};
protected Collection<BaseInjection> getAllInjections() {
ArrayList<BaseInjection> injections = new ArrayList<BaseInjection>();
for (List<BaseInjection> list : myInjections.values()) {
injections.addAll(list);
}
return injections;
}
private CachedValue<MultiMap<String, BaseInjection>> myInjectionsById = new CachedValueImpl<MultiMap<String, BaseInjection>>(new CachedValueProvider<MultiMap<String, BaseInjection>>() {
@Nullable
@Override
public Result<MultiMap<String, BaseInjection>> compute() {
MultiMap<String, BaseInjection> map = new MultiMap<String, BaseInjection>();
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<Element>, 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>)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<Element>, Modific
return Collections.emptyList();
}
public Collection<BaseInjection> 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<Element>, Modific
}
}
public void configurationModified() {
private void configurationModified() {
myModificationCount ++;
}
@@ -492,6 +533,9 @@ public class Configuration implements PersistentStateComponent<Element>, Modific
}
}
/**
* @param injectorId see {@link org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport#getId()}
*/
@NotNull
public List<BaseInjection> getInjections(final String injectorId) {
return Collections.unmodifiableList(myInjections.get(injectorId));
@@ -506,7 +550,10 @@ public class Configuration implements PersistentStateComponent<Element>, Modific
public boolean process(final List<? extends BaseInjection> add,
final List<? extends BaseInjection> 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;
}
});
@@ -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;
}
@@ -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<Language>() {
public boolean process(final Language language) {
if (project.isDisposed()) return false;
doChooseLanguageToInject(editor, new Processor<Injectable>() {
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.<VirtualFile>emptyList(), true);
if (injectable.getLanguage() != null) { // no need for reference injection
FileContentUtil.reparseFiles(project, Collections.<VirtualFile>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<Language> onChosen) {
final Language[] languages = InjectedLanguage.getAvailableLanguages();
Arrays.sort(languages, LanguageUtil.LANGUAGE_COMPARATOR);
private static boolean doChooseLanguageToInject(Editor editor, final Processor<Injectable> onChosen) {
final List<Injectable> injectables = Injectable.getAllInjectables();
final JList list = new JBList(languages);
list.setCellRenderer(new ListCellRendererWrapper<Language>() {
final JList list = new JBList(injectables);
list.setCellRenderer(new ListCellRendererWrapper<Injectable>() {
@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<Object, String>() {
@Override
public String fun(Object language) {
return ((Language)language).getDisplayName();
return ((Injectable)language).getDisplayName();
}
})
.createPopup().showInBestPositionFor(editor);
@@ -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);
@@ -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);
@@ -65,7 +65,7 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
private Pattern myCompiledValuePattern;
private boolean mySingleFile;
public BaseInjection(final String id) {
public BaseInjection(@NotNull final String id) {
mySupportId = id;
}
@@ -244,7 +244,7 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen
setValuePattern(element.getChildText("value-pattern"));
mySingleFile = element.getChild("single-file") != null;
readExternalImpl(element);
final List<Element> placeElements = (List<Element>)element.getChildren("place");
final List<Element> 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<Elemen
}
}
public boolean acceptForReference(PsiElement element) {
return acceptsPsiElement(element);
}
@Override
public String toString() {
return getInjectedLanguageId()+ "->" +getDisplayName();
@@ -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();
}
}
@@ -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<Injectable> {
/** 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<Injectable> getAllInjectables() {
Language[] languages = InjectedLanguage.getAvailableLanguages();
List<Injectable> list = new ArrayList<Injectable>();
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;
}
};
}
}
@@ -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<ReferenceInjector> 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<BaseInjection> injections = configuration.getInjectionsByLanguageId(extension.getId());
for (BaseInjection injection : injections) {
if (injection.acceptForReference(element)) {
element.putUserData(INJECTED_REFERENCE, extension);
List<TextRange> area = injection.getInjectedArea(element);
for (TextRange range : area) {
references = ArrayUtil.mergeArrays(references, extension.getReferences(element, context, range));
}
}
}
}
return references;
}
});
}
}
@@ -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<ReferenceInjector> 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);
}
@@ -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;
}
}
@@ -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;
}