Snapshot of commit 84dc01e773388c2c72a1fc437f313dd5747e7809
from branch master of git://git.jetbrains.org/idea/community.git
diff --git a/xml/dom-impl/dom-impl.iml b/xml/dom-impl/dom-impl.iml
index cc3900c..8b2feb0 100644
--- a/xml/dom-impl/dom-impl.iml
+++ b/xml/dom-impl/dom-impl.iml
@@ -9,8 +9,7 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="dom-openapi" exported="" />
<orderEntry type="library" name="asm" level="project" />
- <orderEntry type="library" name="CGLIB" level="project" />
- <orderEntry type="module" module-name="resources" />
+ <orderEntry type="library" exported="" name="CGLIB" level="project" />
<orderEntry type="library" name="Xerces" level="project" />
<orderEntry type="module" module-name="xml" />
<orderEntry type="module" module-name="util" />
@@ -20,6 +19,7 @@
<orderEntry type="module" module-name="lang-impl" />
<orderEntry type="module" module-name="xml-openapi" />
<orderEntry type="library" name="DTDParser" level="project" />
+ <orderEntry type="module" module-name="platform-resources" />
</component>
<component name="copyright">
<Base>
diff --git a/xml/dom-impl/src/META-INF/DomPlugin.xml b/xml/dom-impl/src/META-INF/DomPlugin.xml
index 71ce3f7..3b2e0ff 100644
--- a/xml/dom-impl/src/META-INF/DomPlugin.xml
+++ b/xml/dom-impl/src/META-INF/DomPlugin.xml
@@ -17,6 +17,8 @@
<extensionPoint name="dom.uiControlsProvider"
interface="com.intellij.util.Consumer"/>
+
+ <extensionPoint name="moduleContextProvider" interface="com.intellij.util.xml.ModuleContextProvider"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
diff --git a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementAnnotationHolderImpl.java b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementAnnotationHolderImpl.java
index f381140..55b140a 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementAnnotationHolderImpl.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementAnnotationHolderImpl.java
@@ -27,7 +27,7 @@
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.*;
-import com.intellij.util.xml.impl.ConvertContextImpl;
+import com.intellij.util.xml.impl.ConvertContextFactory;
import com.intellij.util.xml.impl.DomManagerImpl;
import com.intellij.util.xml.reflect.DomCollectionChildDescription;
import org.jetbrains.annotations.NotNull;
@@ -122,7 +122,7 @@
if (converter instanceof ResolvingConverter) {
final ResolvingConverter resolvingConverter = (ResolvingConverter)converter;
ContainerUtil
- .addAll(result, resolvingConverter.getQuickFixes(new ConvertContextImpl(DomManagerImpl.getDomInvocationHandler(element))));
+ .addAll(result, resolvingConverter.getQuickFixes(ConvertContextFactory.createConvertContext(DomManagerImpl.getDomInvocationHandler(element))));
}
if (reference instanceof LocalQuickFixProvider) {
final LocalQuickFix[] localQuickFixes = ((LocalQuickFixProvider)reference).getQuickFixes();
diff --git a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java
index 5e8614d..31935b7 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java
@@ -132,7 +132,7 @@
hasBadResolve(domReference = new GenericDomValueReference(element)))) {
hasBadResolve = true;
final String errorMessage = converter
- .getErrorMessage(element.getStringValue(), new ConvertContextImpl(DomManagerImpl.getDomInvocationHandler(element)));
+ .getErrorMessage(element.getStringValue(), ConvertContextFactory.createConvertContext(DomManagerImpl.getDomInvocationHandler(element)));
if (errorMessage != null && XmlHighlightVisitor.getErrorDescription(domReference) != null) {
list.add(holder.createResolveProblem(element, domReference));
}
@@ -140,7 +140,7 @@
}
if (!hasBadResolve && psiReferences.length == 0 && element.getValue() == null && !PsiTreeUtil.hasErrorElements(valueElement)) {
final String errorMessage = converter
- .getErrorMessage(element.getStringValue(), new ConvertContextImpl(DomManagerImpl.getDomInvocationHandler(element)));
+ .getErrorMessage(element.getStringValue(), ConvertContextFactory.createConvertContext(DomManagerImpl.getDomInvocationHandler(element)));
if (errorMessage != null) {
list.add(holder.createProblem(element, errorMessage));
}
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/ConvertContextFactory.java b/xml/dom-impl/src/com/intellij/util/xml/impl/ConvertContextFactory.java
new file mode 100644
index 0000000..2ed9b0e
--- /dev/null
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/ConvertContextFactory.java
@@ -0,0 +1,18 @@
+package com.intellij.util.xml.impl;
+
+import com.intellij.util.xml.ConvertContext;
+import com.intellij.util.xml.DomElement;
+
+public class ConvertContextFactory {
+ public static ConvertContext createConvertContext(final DomElement element) {
+ return new ConvertContextImpl(DomManagerImpl.getDomInvocationHandler(element)) {
+ public DomElement getInvocationElement() {
+ return element;
+ }
+ };
+ }
+
+ public static ConvertContext createConvertContext(final DomInvocationHandler element) {
+ return new ConvertContextImpl(element);
+ }
+}
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/ConvertContextImpl.java b/xml/dom-impl/src/com/intellij/util/xml/impl/ConvertContextImpl.java
index 2fd958e..d776073 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/impl/ConvertContextImpl.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/ConvertContextImpl.java
@@ -21,7 +21,7 @@
}
@NotNull
- public final DomElement getInvocationElement() {
+ public DomElement getInvocationElement() {
return myHandler.getProxy();
}
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java
index 8df0195..058ce38 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java
@@ -18,6 +18,7 @@
import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.extensions.Extensions;
+import com.intellij.util.NotNullFunction;
import com.intellij.util.ReflectionAssignabilityCache;
import com.intellij.util.containers.ConcurrentFactoryMap;
import com.intellij.util.containers.FactoryMap;
@@ -60,18 +61,20 @@
}
};
- private final ConcurrentFactoryMap<Type, StaticGenericInfo> myGenericInfos = new ConcurrentFactoryMap<Type, StaticGenericInfo>() {
+ private final SofterCache<Type, StaticGenericInfo> myGenericInfos = SofterCache.create(new NotNullFunction<Type, StaticGenericInfo>() {
@NotNull
- protected StaticGenericInfo create(final Type type) {
+ @Override
+ public StaticGenericInfo fun(Type type) {
return new StaticGenericInfo(type);
}
- };
- private final ConcurrentFactoryMap<Class, InvocationCache> myInvocationCaches = new ConcurrentFactoryMap<Class, InvocationCache>() {
+ });
+ private final SofterCache<Class, InvocationCache> myInvocationCaches = SofterCache.create(new NotNullFunction<Class, InvocationCache>() {
@NotNull
- protected InvocationCache create(final Class key) {
+ @Override
+ public InvocationCache fun(Class key) {
return new InvocationCache(key);
}
- };
+ });
private final ConcurrentFactoryMap<Class<? extends DomElementVisitor>, VisitorDescription> myVisitorDescriptions =
new ConcurrentFactoryMap<Class<? extends DomElementVisitor>, VisitorDescription>() {
@NotNull
@@ -161,21 +164,15 @@
}
public final StaticGenericInfo getStaticGenericInfo(final Type type) {
- return myGenericInfos.get(type);
+ return myGenericInfos.getCachedValue(type);
}
final InvocationCache getInvocationCache(final Class type) {
- return myInvocationCaches.get(type);
+ return myInvocationCaches.getCachedValue(type);
}
public final VisitorDescription getVisitorDescription(Class<? extends DomElementVisitor> aClass) {
return myVisitorDescriptions.get(aClass);
}
- @TestOnly
- public void clearCachesInTests() {
- myInvocationCaches.clear();
- myGenericInfos.clear();
- myCachedImplementationClasses.clear();
- }
}
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomCompletionContributor.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomCompletionContributor.java
index 7d8b2cc..e0478c9 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomCompletionContributor.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomCompletionContributor.java
@@ -18,6 +18,7 @@
import com.intellij.codeInsight.completion.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
+import com.intellij.psi.filters.getters.XmlAttributeValueGetter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
@@ -75,7 +76,17 @@
final PsiElement parent = element.getParent();
if (parent instanceof XmlAttribute) {
final XmlAttributeDescriptor descriptor = ((XmlAttribute)parent).getDescriptor();
- if (descriptor != null && descriptor.isEnumerated()) return true;
+ if (descriptor != null && descriptor.isEnumerated()) {
+ return true;
+ }
+
+ String[] enumeratedValues = XmlAttributeValueGetter.getEnumeratedValues((XmlAttribute)parent);
+ if (enumeratedValues != null && enumeratedValues.length > 0) {
+ String value = descriptor == null ? null : descriptor.getDefaultValue();
+ if (value == null || enumeratedValues.length != 1 || !value.equals(enumeratedValues[0])) {
+ return true;
+ }
+ }
}
}
return false;
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomReferenceContributor.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomReferenceContributor.java
index 8dbfe5f..9c1d04b 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomReferenceContributor.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomReferenceContributor.java
@@ -24,7 +24,9 @@
*/
public class DomReferenceContributor extends PsiReferenceContributor{
public void registerReferenceProviders(final PsiReferenceRegistrar registrar) {
- registrar.registerReferenceProvider(XmlPatterns.xmlTag(), new GenericValueReferenceProvider());
- registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue(), new GenericValueReferenceProvider());
+ GenericValueReferenceProvider provider = new GenericValueReferenceProvider();
+
+ registrar.registerReferenceProvider(XmlPatterns.xmlTag(), provider);
+ registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue(), provider);
}
}
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DynamicGenericInfo.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DynamicGenericInfo.java
index a95cbe5..ffcc033 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/impl/DynamicGenericInfo.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DynamicGenericInfo.java
@@ -23,10 +23,11 @@
import com.intellij.openapi.util.RecursionManager;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlFile;
+import com.intellij.reference.SoftReference;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.Processor;
-import com.intellij.util.containers.ConcurrentWeakHashMap;
+import com.intellij.util.containers.ConcurrentHashMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.GenericDomValue;
@@ -46,7 +47,7 @@
* @author peter
*/
public class DynamicGenericInfo extends DomGenericInfoEx {
- private static final Key<ConcurrentWeakHashMap<ChildrenDescriptionsHolder, ChildrenDescriptionsHolder>> HOLDERS_CACHE = Key.create("DOM_CHILDREN_HOLDERS_CACHE");
+ private static final Key<SoftReference<ConcurrentHashMap<ChildrenDescriptionsHolder, ChildrenDescriptionsHolder>>> HOLDERS_CACHE = Key.create("DOM_CHILDREN_HOLDERS_CACHE");
private static final RecursionGuard ourGuard = RecursionManager.createGuard("dynamicGenericInfo");
private final StaticGenericInfo myStaticGenericInfo;
@NotNull private final DomInvocationHandler myInvocationHandler;
@@ -136,9 +137,11 @@
}
private static <T extends DomChildDescriptionImpl> ChildrenDescriptionsHolder<T> internChildrenHolder(XmlFile file, ChildrenDescriptionsHolder<T> holder) {
- ConcurrentWeakHashMap<ChildrenDescriptionsHolder, ChildrenDescriptionsHolder> cache = file.getUserData(HOLDERS_CACHE);
+ SoftReference<ConcurrentHashMap<ChildrenDescriptionsHolder, ChildrenDescriptionsHolder>> ref = file.getUserData(HOLDERS_CACHE);
+ ConcurrentHashMap<ChildrenDescriptionsHolder, ChildrenDescriptionsHolder> cache = ref == null ? null : ref.get();
if (cache == null) {
- file.putUserData(HOLDERS_CACHE, cache = new ConcurrentWeakHashMap<ChildrenDescriptionsHolder, ChildrenDescriptionsHolder>());
+ cache = new ConcurrentHashMap<ChildrenDescriptionsHolder, ChildrenDescriptionsHolder>();
+ file.putUserData(HOLDERS_CACHE, new SoftReference<ConcurrentHashMap<ChildrenDescriptionsHolder, ChildrenDescriptionsHolder>>(cache));
}
ChildrenDescriptionsHolder existing = cache.get(holder);
if (existing != null) {
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericDomValueReference.java b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericDomValueReference.java
index b934fe3..06a47b9 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericDomValueReference.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericDomValueReference.java
@@ -143,12 +143,12 @@
}
public String getUnresolvedMessagePattern() {
- final ConvertContextImpl context = getConvertContext();
+ final ConvertContext context = getConvertContext();
return getConverter().getErrorMessage(getStringValue(), context);
}
- public final ConvertContextImpl getConvertContext() {
- return new ConvertContextImpl(DomManagerImpl.getDomInvocationHandler(myGenericValue));
+ public final ConvertContext getConvertContext() {
+ return ConvertContextFactory.createConvertContext(DomManagerImpl.getDomInvocationHandler(myGenericValue));
}
public PsiElement handleElementRename(final String newElementName) throws IncorrectOperationException {
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java
index 4040ade..b457c9d 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java
@@ -89,17 +89,8 @@
return references;
}
- private static AbstractConvertContext createConvertContext(final PsiElement psiElement, final GenericDomValue domValue) {
- return new AbstractConvertContext() {
- @NotNull
- public DomElement getInvocationElement() {
- return domValue;
- }
-
- public PsiManager getPsiManager() {
- return psiElement.getManager();
- }
- };
+ private static ConvertContext createConvertContext(final PsiElement psiElement, final GenericDomValue domValue) {
+ return ConvertContextFactory.createConvertContext(domValue);
}
@Nullable
@@ -108,7 +99,7 @@
}
private PsiReference[] createReferences(final GenericDomValue domValue, final XmlElement psiElement, final Object converter) {
- AbstractConvertContext context = createConvertContext(psiElement, domValue);
+ ConvertContext context = createConvertContext(psiElement, domValue);
List<PsiReference> result = new ArrayList<PsiReference>();
String unresolvedText = ElementManipulators.getValueText(psiElement);
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/GetInvocation.java b/xml/dom-impl/src/com/intellij/util/xml/impl/GetInvocation.java
index 41e85a9..c2642f1 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/impl/GetInvocation.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/GetInvocation.java
@@ -9,10 +9,7 @@
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.xml.Converter;
-import com.intellij.util.xml.DomReferenceInjector;
-import com.intellij.util.xml.DomUtil;
-import com.intellij.util.xml.SubTag;
+import com.intellij.util.xml.*;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -43,8 +40,9 @@
handler.putUserData(DOM_VALUE_KEY, value = cachedValuesManager.createCachedValue(new CachedValueProvider<List<Pair<Converter,Object>>>() {
@Override
public Result<List<Pair<Converter,Object>>> compute() {
- List<Pair<Converter, Object>> list = ContainerUtil.createEmptyCOWList();
- return Result.create(list, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, domManager, ProjectRootManager.getInstance(project));
+ List<Pair<Converter, Object>> list = ContainerUtil.createLockFreeCopyOnWriteList();
+ return Result
+ .create(list, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, domManager, ProjectRootManager.getInstance(project));
}
}, false));
}
@@ -80,7 +78,7 @@
}
String tagValue = handler.getValue();
- ConvertContextImpl context = new ConvertContextImpl(handler);
+ ConvertContext context = ConvertContextFactory.createConvertContext(handler);
for (DomReferenceInjector each : DomUtil.getFileElement(handler).getFileDescription().getReferenceInjectors()) {
tagValue = each.resolveString(tagValue, context);
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java
index 57625e8..463c06d 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java
@@ -18,17 +18,22 @@
import com.intellij.openapi.Disposable;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.util.Disposer;
+import com.intellij.util.NotNullFunction;
import com.intellij.util.ReflectionCache;
+import com.intellij.util.SofterReference;
import com.intellij.util.containers.ConcurrentFactoryMap;
+import com.intellij.util.containers.ConcurrentHashMap;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.xml.DomReflectionUtil;
import com.intellij.util.xml.Implementation;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.concurrent.ConcurrentMap;
/**
* @author peter
@@ -45,18 +50,13 @@
private final MultiMap<String, DomImplementationClassEP> myImplementationClasses = new MultiMap<String, DomImplementationClassEP>();
- private final ConcurrentFactoryMap<Class, Class> myCache = new ConcurrentFactoryMap<Class, Class>() {
- @Nullable
- protected Class create(final Class concreteInterface) {
- final TreeSet<Class> set = new TreeSet<Class>(CLASS_COMPARATOR);
- findImplementationClassDFS(concreteInterface, set);
- if (!set.isEmpty()) {
- return set.first();
- }
- final Implementation implementation = DomReflectionUtil.findAnnotationDFS(concreteInterface, Implementation.class);
- return implementation == null ? null : implementation.value();
- }
- };
+ private final SofterCache<Class, Class> myCache = SofterCache.create(new NotNullFunction<Class, Class>() {
+ @NotNull
+ @Override
+ public Class fun(Class dom) {
+ return calcImplementationClass(dom);
+ }
+ });
ImplementationClassCache(ExtensionPointName<DomImplementationClassEP> epName) {
for (DomImplementationClassEP ep : epName.getExtensions()) {
@@ -64,6 +64,16 @@
}
}
+ private Class calcImplementationClass(Class concreteInterface) {
+ final TreeSet<Class> set = new TreeSet<Class>(CLASS_COMPARATOR);
+ findImplementationClassDFS(concreteInterface, set);
+ if (!set.isEmpty()) {
+ return set.first();
+ }
+ final Implementation implementation = DomReflectionUtil.findAnnotationDFS(concreteInterface, Implementation.class);
+ return implementation == null ? concreteInterface : implementation.value();
+ }
+
private void findImplementationClassDFS(final Class concreteInterface, SortedSet<Class> results) {
final Collection<DomImplementationClassEP> values = myImplementationClasses.get(concreteInterface.getName());
for (DomImplementationClassEP value : values) {
@@ -98,15 +108,13 @@
}
});
}
- myCache.clear();
+ myCache.clearCache();
}
public Class get(Class key) {
- return myCache.get(key);
+ Class impl = myCache.getCachedValue(key);
+ return impl == key ? null : impl;
}
- public void clear() {
- myCache.clear();
- }
}
diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/SofterCache.java b/xml/dom-impl/src/com/intellij/util/xml/impl/SofterCache.java
new file mode 100644
index 0000000..1e03744
--- /dev/null
+++ b/xml/dom-impl/src/com/intellij/util/xml/impl/SofterCache.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.intellij.util.xml.impl;
+
+import com.intellij.util.NotNullFunction;
+import com.intellij.util.SofterReference;
+import com.intellij.util.containers.ConcurrentHashMap;
+
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * @author peter
+ */
+public class SofterCache<T,V> {
+ private final NotNullFunction<T,V> myValueProvider;
+ private SofterReference<ConcurrentMap<T, V>> myCache;
+
+ public SofterCache(NotNullFunction<T, V> valueProvider) {
+ myValueProvider = valueProvider;
+ }
+
+ public static <T, V> SofterCache<T, V> create(NotNullFunction<T, V> valueProvider) {
+ return new SofterCache<T, V>(valueProvider);
+ }
+
+ public void clearCache() {
+ myCache = null;
+ }
+
+ public V getCachedValue(T key) {
+ SofterReference<ConcurrentMap<T, V>> ref = myCache;
+ ConcurrentMap<T, V> map = ref == null ? null : ref.get();
+ if (map == null) {
+ myCache = new SofterReference<ConcurrentMap<T, V>>(map = new ConcurrentHashMap<T, V>());
+ }
+ V value = map.get(key);
+ if (value == null) {
+ map.put(key, value = myValueProvider.fun(key));
+ }
+ return value;
+ }
+
+
+}
diff --git a/xml/dom-impl/src/com/intellij/util/xml/ui/TextControl.java b/xml/dom-impl/src/com/intellij/util/xml/ui/TextControl.java
index 89cf4db..c6dec44 100644
--- a/xml/dom-impl/src/com/intellij/util/xml/ui/TextControl.java
+++ b/xml/dom-impl/src/com/intellij/util/xml/ui/TextControl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * 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.
@@ -99,7 +99,7 @@
DialogBuilder builder = new DialogBuilder(project);
builder.setDimensionServiceKey("TextControl");
builder.setCenterPanel(textArea);
- builder.setPreferedFocusComponent(textArea);
+ builder.setPreferredFocusComponent(textArea);
builder.setTitle(UIBundle.message("big.text.control.window.title"));
builder.addCloseButton();
builder.show();
diff --git a/xml/dom-openapi/dom-openapi.iml b/xml/dom-openapi/dom-openapi.iml
index da874a7..f94d712 100644
--- a/xml/dom-openapi/dom-openapi.iml
+++ b/xml/dom-openapi/dom-openapi.iml
@@ -7,13 +7,13 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
- <orderEntry type="module" module-name="resources" />
<orderEntry type="module" module-name="xml-openapi" />
<orderEntry type="module" module-name="lang-api" />
<orderEntry type="module" module-name="platform-api" />
<orderEntry type="module" module-name="extensions" />
<orderEntry type="module" module-name="util" />
<orderEntry type="module" module-name="lang-impl" />
+ <orderEntry type="module" module-name="platform-resources" />
</component>
<component name="copyright">
<Base>
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/AbstractConvertContext.java b/xml/dom-openapi/src/com/intellij/util/xml/AbstractConvertContext.java
index dd2c03f..b3580d6 100644
--- a/xml/dom-openapi/src/com/intellij/util/xml/AbstractConvertContext.java
+++ b/xml/dom-openapi/src/com/intellij/util/xml/AbstractConvertContext.java
@@ -17,7 +17,12 @@
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
+import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
@@ -29,15 +34,6 @@
*/
public abstract class AbstractConvertContext extends ConvertContext {
- public static ConvertContext createConvertContext(final DomElement domElement) {
- return new AbstractConvertContext() {
- @NotNull
- public DomElement getInvocationElement() {
- return domElement;
- }
- };
- }
-
public final XmlTag getTag() {
return getInvocationElement().getXmlTag();
}
@@ -56,7 +52,7 @@
final DomFileElement<DomElement> fileElement = DomUtil.getFileElement(getInvocationElement());
if (fileElement == null) {
final XmlElement xmlElement = getInvocationElement().getXmlElement();
- return xmlElement == null? null : ModuleUtil.findModuleForPsiElement(xmlElement);
+ return xmlElement == null ? null : ModuleUtil.findModuleForPsiElement(xmlElement);
}
return fileElement.getRootElement().getModule();
}
@@ -65,4 +61,66 @@
return getFile().getManager();
}
+ @Nullable
+ public GlobalSearchScope getSearchScope() {
+ GlobalSearchScope scope = null;
+
+ Module[] modules = getConvertContextModules(this);
+ if (modules.length != 0) {
+
+ PsiFile file = getFile();
+ file = file.getOriginalFile();
+ VirtualFile virtualFile = file.getVirtualFile();
+ if (virtualFile != null) {
+ ProjectFileIndex fileIndex = ProjectRootManager.getInstance(file.getProject()).getFileIndex();
+ boolean tests = fileIndex.isInTestSourceContent(virtualFile);
+
+ for (Module module : modules) {
+ if (scope == null) {
+ scope = module.getModuleRuntimeScope(tests);
+ }
+ else {
+ scope.union(module.getModuleRuntimeScope(tests));
+ }
+ }
+ }
+ }
+ return scope; // ??? scope == null ? GlobalSearchScope.allScope(getProject()) : scope; ???
+ }
+
+ public static GlobalSearchScope getSearchScope(@NotNull ConvertContext context) {
+ Module[] modules = getConvertContextModules(context);
+ if (modules.length == 0) return null;
+
+ PsiFile file = context.getFile();
+ file = file.getOriginalFile();
+ VirtualFile virtualFile = file.getVirtualFile();
+ if (virtualFile == null) return null;
+ ProjectFileIndex fileIndex = ProjectRootManager.getInstance(file.getProject()).getFileIndex();
+ boolean tests = fileIndex.isInTestSourceContent(virtualFile);
+
+
+ GlobalSearchScope scope = null;
+ for (Module module : modules) {
+ if (scope == null) {
+ scope = module.getModuleRuntimeScope(tests);
+ }
+ else {
+ scope.union(module.getModuleRuntimeScope(tests));
+ }
+ }
+ return scope;
+ }
+
+
+ @NotNull
+ private static Module[] getConvertContextModules(@NotNull ConvertContext context) {
+ Module[] modules = ModuleContextProvider.getModules(context.getFile());
+ if (modules.length > 0) return modules;
+
+ final Module module = context.getModule();
+ if (module != null) return new Module[]{module};
+
+ return new Module[0];
+ }
}
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/ConvertContext.java b/xml/dom-openapi/src/com/intellij/util/xml/ConvertContext.java
index 2ee1b01..00550b0 100644
--- a/xml/dom-openapi/src/com/intellij/util/xml/ConvertContext.java
+++ b/xml/dom-openapi/src/com/intellij/util/xml/ConvertContext.java
@@ -18,6 +18,7 @@
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiManager;
+import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlFile;
@@ -56,6 +57,9 @@
@Nullable
public abstract Module getModule();
+
+ @Nullable
+ public abstract GlobalSearchScope getSearchScope();
public abstract PsiManager getPsiManager();
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/DomUtil.java b/xml/dom-openapi/src/com/intellij/util/xml/DomUtil.java
index 5803c6c..9a6de09 100644
--- a/xml/dom-openapi/src/com/intellij/util/xml/DomUtil.java
+++ b/xml/dom-openapi/src/com/intellij/util/xml/DomUtil.java
@@ -221,7 +221,6 @@
}
});
return result;
-
}
ProgressManager.checkCanceled();
@@ -241,21 +240,25 @@
if (attributes) {
for (final XmlAttribute attribute : tag.getAttributes()) {
if (!attribute.isValid()) {
- throw new AssertionError("Invalid attr: parent.valid=" + tag.isValid());
+ LOG.error("Invalid attr: parent.valid=" + tag.isValid());
+ continue;
}
GenericAttributeValue element = domManager.getDomElement(attribute);
- checkHasXml(attribute, element);
- ContainerUtil.addIfNotNull(element, result);
+ if (checkHasXml(attribute, element)) {
+ ContainerUtil.addIfNotNull(element, result);
+ }
}
}
if (tags) {
for (final XmlTag subTag : tag.getSubTags()) {
if (!subTag.isValid()) {
- throw new AssertionError("Invalid subtag: parent.valid=" + tag.isValid());
+ LOG.error("Invalid subtag: parent.valid=" + tag.isValid());
+ continue;
}
DomElement element = domManager.getDomElement(subTag);
- checkHasXml(subTag, element);
- ContainerUtil.addIfNotNull(element, result);
+ if (checkHasXml(subTag, element)) {
+ ContainerUtil.addIfNotNull(element, result);
+ }
}
}
return result;
@@ -263,10 +266,12 @@
return Collections.emptyList();
}
- private static void checkHasXml(XmlElement psi, DomElement dom) {
+ private static boolean checkHasXml(XmlElement psi, DomElement dom) {
if (dom != null && dom.getXmlElement() == null) {
- throw new AssertionError("No xml for dom " + dom + "; attr=" + psi + ", physical=" + psi.isPhysical());
+ LOG.error("No xml for dom " + dom + "; attr=" + psi + ", physical=" + psi.isPhysical());
+ return false;
}
+ return true;
}
public static <T> List<T> getDefinedChildrenOfType(@NotNull final DomElement parent, final Class<T> type, boolean tags, boolean attributes) {
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/ModuleContextProvider.java b/xml/dom-openapi/src/com/intellij/util/xml/ModuleContextProvider.java
new file mode 100644
index 0000000..7cf5525
--- /dev/null
+++ b/xml/dom-openapi/src/com/intellij/util/xml/ModuleContextProvider.java
@@ -0,0 +1,33 @@
+package com.intellij.util.xml;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.HashSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Set;
+
+public abstract class ModuleContextProvider {
+ public static final ExtensionPointName<ModuleContextProvider> EP_NAME = ExtensionPointName.create("com.intellij.moduleContextProvider");
+
+ @NotNull
+ public abstract Module[] getContextModules(@NotNull PsiFile context);
+
+ public static Module[] getModules(@Nullable PsiFile context) {
+ if (context == null) return Module.EMPTY_ARRAY;
+
+ final Set<Module> modules = new HashSet<Module>();
+ for (ModuleContextProvider moduleContextProvider : Extensions.getExtensions(EP_NAME)) {
+ ContainerUtil.addAllNotNull(modules, moduleContextProvider.getContextModules(context));
+ }
+ Module module = ModuleUtilCore.findModuleForPsiElement(context);
+ if (module != null) modules.add(module);
+
+ return modules.toArray(new Module[modules.size()]);
+ }
+}
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/converters/DelimitedListConverter.java b/xml/dom-openapi/src/com/intellij/util/xml/converters/DelimitedListConverter.java
index e8b12e6..2e5c825 100644
--- a/xml/dom-openapi/src/com/intellij/util/xml/converters/DelimitedListConverter.java
+++ b/xml/dom-openapi/src/com/intellij/util/xml/converters/DelimitedListConverter.java
@@ -107,7 +107,7 @@
}
public String toString(final List<T> ts, final ConvertContext context) {
- final StringBuffer buffer = new StringBuffer();
+ final StringBuilder buffer = new StringBuilder();
final char delimiter = getDefaultDelimiter();
for (T t : ts) {
final String s = toString(t);
@@ -151,6 +151,11 @@
return new MyPsiReference(element, new TextRange(start, end), context, genericDomValue, delimitersOnly);
}
+ @Override
+ public String toString() {
+ return super.toString() + " delimiters: " + myDelimiters;
+ }
+
protected class MyPsiReference extends PsiReferenceBase<PsiElement> implements EmptyResolveMessageProvider {
protected final ConvertContext myContext;
protected final GenericDomValue<List<T>> myGenericDomValue;
@@ -212,6 +217,11 @@
return bindElement;
}
+ @Override
+ public String toString() {
+ return super.toString() + " converter: " + DelimitedListConverter.this;
+ }
+
private Function<PsiElement, PsiElement> getSuperBindToElementFunction(final Ref<IncorrectOperationException> ref) {
return new Function<PsiElement, PsiElement>() {
public PsiElement fun(final PsiElement s) {
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/converters/PathReferenceConverter.java b/xml/dom-openapi/src/com/intellij/util/xml/converters/PathReferenceConverter.java
index c533e58..2d578a1 100644
--- a/xml/dom-openapi/src/com/intellij/util/xml/converters/PathReferenceConverter.java
+++ b/xml/dom-openapi/src/com/intellij/util/xml/converters/PathReferenceConverter.java
@@ -36,9 +36,8 @@
public final static Converter<PathReference> INSTANCE = new PathReferenceConverter();
public PathReference fromString(@Nullable final String s, final ConvertContext context) {
- Module module = context.getModule();
final XmlElement element = context.getXmlElement();
- return s == null || module == null || element == null ? null : PathReferenceManager.getInstance().getPathReference(s, module, element);
+ return s == null || element == null ? null : PathReferenceManager.getInstance().getPathReference(s, element);
}
public String toString(final PathReference t, final ConvertContext context) {
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/ui/CaptionComponent.java b/xml/dom-openapi/src/com/intellij/util/xml/ui/CaptionComponent.java
index 4743fc9..ed30d6b 100644
--- a/xml/dom-openapi/src/com/intellij/util/xml/ui/CaptionComponent.java
+++ b/xml/dom-openapi/src/com/intellij/util/xml/ui/CaptionComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * 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.
@@ -17,6 +17,7 @@
package com.intellij.util.xml.ui;
import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.JBColor;
import com.intellij.util.xml.DomElement;
import javax.swing.*;
@@ -46,6 +47,7 @@
public CaptionComponent(String text, Icon icon) {
super(new BorderLayout());
updateBorder();
+ myRootPanel.setBackground(new JBColor(new Color(243, 244, 229), new Color(42, 55, 62)));
add(myRootPanel, BorderLayout.CENTER);
setText(text);
@@ -54,7 +56,7 @@
private void updateBorder() {
if (myBordered) {
- myRootPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY));
+ myRootPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, JBColor.LIGHT_GRAY));
}
else {
myRootPanel.setBorder(BorderFactory.createEmptyBorder());
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/ui/ComboControl.java b/xml/dom-openapi/src/com/intellij/util/xml/ui/ComboControl.java
index db9c5ed..3984d19 100644
--- a/xml/dom-openapi/src/com/intellij/util/xml/ui/ComboControl.java
+++ b/xml/dom-openapi/src/com/intellij/util/xml/ui/ComboControl.java
@@ -112,24 +112,6 @@
};
}
- public static Factory<Collection<? extends Object>> createVariantsGetter(final GenericDomValue<?> reference) {
- return new Factory<Collection<? extends Object>>() {
- public Collection<? extends Object> create() {
- final Converter converter = reference.getConverter();
- if (converter instanceof ResolvingConverter) {
- return ((ResolvingConverter)converter).getVariants(new AbstractConvertContext() {
- @NotNull
- public DomElement getInvocationElement() {
- return reference;
- }
- });
-
- }
- return Collections.emptyList();
- }
- };
- }
-
public static Factory<List<Pair<String, Icon>>> createPresentationFunction(final Factory<Collection<? extends Object>> variantFactory) {
return new Factory<List<Pair<String, Icon>>>() {
public List<Pair<String, Icon>> create() {
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/ui/actions/generate/CreateDomElementAction.java b/xml/dom-openapi/src/com/intellij/util/xml/ui/actions/generate/CreateDomElementAction.java
index 2d3d1ed..198016b 100644
--- a/xml/dom-openapi/src/com/intellij/util/xml/ui/actions/generate/CreateDomElementAction.java
+++ b/xml/dom-openapi/src/com/intellij/util/xml/ui/actions/generate/CreateDomElementAction.java
@@ -49,7 +49,7 @@
protected abstract DomElement createElement(T context, Editor editor, PsiFile file, Project project);
@Override
- protected boolean isValidForFile(Project project, Editor editor, PsiFile file) {
+ protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
return getContextElement(editor) != null;
}
diff --git a/xml/dom-openapi/src/com/intellij/util/xml/ui/actions/generate/GenerateDomElementAction.java b/xml/dom-openapi/src/com/intellij/util/xml/ui/actions/generate/GenerateDomElementAction.java
index dad9336..dd6f1c0 100644
--- a/xml/dom-openapi/src/com/intellij/util/xml/ui/actions/generate/GenerateDomElementAction.java
+++ b/xml/dom-openapi/src/com/intellij/util/xml/ui/actions/generate/GenerateDomElementAction.java
@@ -50,6 +50,7 @@
this(generateProvider, null);
}
+ @NotNull
protected CodeInsightActionHandler getHandler() {
return new CodeInsightActionHandler() {
public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
@@ -82,7 +83,7 @@
return true;
}
- protected boolean isValidForFile(final Project project, final Editor editor, final PsiFile file) {
+ protected boolean isValidForFile(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
final DomElement element = DomUtil.getContextElement(editor);
return element != null && myProvider.isAvailableForElement(element);
}
diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomConcurrencyStressTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomConcurrencyStressTest.java
index c38cd10..e0aa679 100644
--- a/xml/dom-tests/tests/com/intellij/util/xml/DomConcurrencyStressTest.java
+++ b/xml/dom-tests/tests/com/intellij/util/xml/DomConcurrencyStressTest.java
@@ -97,27 +97,30 @@
}
private static void runThreads(int threadCount, final Runnable runnable) throws Throwable {
- final Ref<Throwable> exc = Ref.create(null);
+ for (int i=0; i<threadCount/8 + 1; i++) {
+ final Ref<Throwable> exc = Ref.create(null);
- final CountDownLatch reads = new CountDownLatch(threadCount);
- for (int j = 0; j < threadCount; j++) {
- new Thread(){
- @Override
- public void run() {
- try {
- runnable.run();
+ final CountDownLatch reads = new CountDownLatch(8);
+ for (int j = 0; j < 8; j++) {
+ new Thread(){
+ @Override
+ public void run() {
+ try {
+ runnable.run();
+ }
+ catch (Throwable e) {
+ exc.set(e);
+ }
+ finally {
+ reads.countDown();
+ }
}
- catch (Throwable e) {
- exc.set(e);
- } finally {
- reads.countDown();
- }
- }
- }.start();
- }
- reads.await();
- if (!exc.isNull()) {
- throw exc.get();
+ }.start();
+ }
+ reads.await();
+ if (!exc.isNull()) {
+ throw exc.get();
+ }
}
}
diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomHardCoreTestCase.java b/xml/dom-tests/tests/com/intellij/util/xml/DomHardCoreTestCase.java
index dc092fc..934bf29 100644
--- a/xml/dom-tests/tests/com/intellij/util/xml/DomHardCoreTestCase.java
+++ b/xml/dom-tests/tests/com/intellij/util/xml/DomHardCoreTestCase.java
@@ -35,12 +35,6 @@
}, myProject);
}
- @Override
- protected void tearDown() throws Exception {
- DomApplicationComponent.getInstance().clearCachesInTests();
- super.tearDown();
- }
-
protected DomManagerImpl getDomManager() {
return DomManagerImpl.getDomManager(getProject());
}
diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomIncludesTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomIncludesTest.java
index 20e19b8..16d2b6f 100644
--- a/xml/dom-tests/tests/com/intellij/util/xml/DomIncludesTest.java
+++ b/xml/dom-tests/tests/com/intellij/util/xml/DomIncludesTest.java
@@ -5,6 +5,7 @@
package com.intellij.util.xml;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction;
+import com.intellij.concurrency.JobLauncher;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -90,51 +91,51 @@
"<xi:include href=\"b.xml\" xpointer=\"xpointer(/xxx/*)\"/>" +
"</root>");
- final PsiFile fileB = createFile("b.xml",
- "<xxx><boy/><xi:include href=\"c.xml\" xpointer=\"xpointer(/xxx/*)\"/><child/><xi:include href=\"c.xml\" xpointer=\"xpointer(/xxx/*)\"/></xxx>");
- final PsiFile fileC = createFile("c.xml",
- "<xxx><child/><xi:include href=\"d.xml\" xpointer=\"xpointer(/xxx/*)\"/><boy/><xi:include href=\"d.xml\" xpointer=\"xpointer(/xxx/*)\"/></xxx>");
- final PsiFile fileD = createFile("d.xml",
- "<xxx><boy/><xi:include href=\"e.xml\" xpointer=\"xpointer(/xxx/*)\"/><child/><xi:include href=\"e.xml\" xpointer=\"xpointer(/xxx/*)\"/></xxx>");
- final PsiFile fileE = createFile("e.xml",
- "<xxx><boy/><child/><boy/><child/><boy/><child/><boy/><child/><boy/><child/><boy/><child/></xxx>");
+ final String textB =
+ "<xxx><boy/><xi:include href=\"c.xml\" xpointer=\"xpointer(/xxx/*)\"/><child/><xi:include href=\"c.xml\" xpointer=\"xpointer(/xxx/*)\"/></xxx>";
+ final PsiFile fileB = createFile("b.xml", textB);
+ final String textC =
+ "<xxx><child/><xi:include href=\"d.xml\" xpointer=\"xpointer(/xxx/*)\"/><boy/><xi:include href=\"d.xml\" xpointer=\"xpointer(/xxx/*)\"/></xxx>";
+ final PsiFile fileC = createFile("c.xml", textC);
+ final String textD =
+ "<xxx><boy/><xi:include href=\"e.xml\" xpointer=\"xpointer(/xxx/*)\"/><child/><xi:include href=\"e.xml\" xpointer=\"xpointer(/xxx/*)\"/></xxx>";
+ final PsiFile fileD = createFile("d.xml", textD);
+ final String textE = "<xxx><boy/><child/><boy/><child/><boy/><child/><boy/><child/><boy/><child/><boy/><child/></xxx>";
+ final PsiFile fileE = createFile("e.xml", textE);
final int threadCount = 100;
- final int iterationCount = Timings.adjustAccordingToMySpeed(800, true);
+ final int iterationCount = Timings.adjustAccordingToMySpeed(100, true);
System.out.println("iterationCount = " + iterationCount);
final CountDownLatch finished = new CountDownLatch(threadCount);
final AtomicReference<Exception> ex = new AtomicReference<Exception>();
- Thread[] threads = new Thread[threadCount];
for (int j = 0; j < threadCount; j++) {
- Thread thread = new Thread() {
+ JobLauncher.getInstance().submitToJobThread(0, new Runnable() {
@Override
public void run() {
+ try {
for (int k = 0; k < iterationCount; k++) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
- try {
final List<Boy> boys = rootElement.getBoys();
Thread.yield();
final List<Child> children = rootElement.getChildren();
Thread.yield();
assertEquals(boys, rootElement.getBoys());
assertEquals(children, rootElement.getChildren());
- }
- catch (Exception e) {
- ex.set(e);
- }
- finally {
- finished.countDown();
- }
}
});
Thread.yield();
}
+ }
+ catch (Exception e) {
+ ex.set(e);
+ }
+ finally {
+ finished.countDown();
+ }
}
- };
- threads[j] = thread;
- thread.start();
+ });
}
for (int i = 0; i < iterationCount; i++) {
@@ -154,12 +155,18 @@
}
});
Thread.sleep(10);
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ fileB.getViewProvider().getDocument().setText(textB);
+ fileC.getViewProvider().getDocument().setText(textC);
+ fileD.getViewProvider().getDocument().setText(textD);
+ fileE.getViewProvider().getDocument().setText(textE);
+ PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); //clear xinclude caches
+ }
+ });
}
finished.await();
- for (Thread thread : threads) {
- thread.join();
- }
final Exception exception = ex.get();
if (exception != null) {
throw exception;
diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomPerformanceTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomPerformanceTest.java
index 6e6b15b..054a32a 100644
--- a/xml/dom-tests/tests/com/intellij/util/xml/DomPerformanceTest.java
+++ b/xml/dom-tests/tests/com/intellij/util/xml/DomPerformanceTest.java
@@ -52,7 +52,7 @@
final MyElement newElement = createElement(DomUtil.getFile(element).getText(), MyElement.class);
- PlatformTestUtil.startPerformanceTest(getTestName(false), 200, new ThrowableRunnable() {
+ PlatformTestUtil.startPerformanceTest(getTestName(false), 300, new ThrowableRunnable() {
@Override
public void run() throws Exception {
newElement.acceptChildren(new DomElementVisitor() {
diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomTestCase.java b/xml/dom-tests/tests/com/intellij/util/xml/DomTestCase.java
index ce036c1..7531e48 100644
--- a/xml/dom-tests/tests/com/intellij/util/xml/DomTestCase.java
+++ b/xml/dom-tests/tests/com/intellij/util/xml/DomTestCase.java
@@ -39,12 +39,6 @@
getDomManager().addDomEventListener(myListener, myTestRootDisposable);
}
- @Override
- protected void tearDown() throws Exception {
- DomApplicationComponent.getInstance().clearCachesInTests();
- super.tearDown();
- }
-
protected void assertCached(final DomElement element, final XmlElement xmlElement) {
assertNotNull(xmlElement);
assertSame(element.getXmlTag(), xmlElement);
diff --git a/xml/dom-tests/tests/com/intellij/util/xml/stubs/DomStubTest.java b/xml/dom-tests/tests/com/intellij/util/xml/stubs/DomStubTest.java
index 2f0b09a..73b975d 100644
--- a/xml/dom-tests/tests/com/intellij/util/xml/stubs/DomStubTest.java
+++ b/xml/dom-tests/tests/com/intellij/util/xml/stubs/DomStubTest.java
@@ -58,12 +58,6 @@
}
@Override
- protected void tearDown() throws Exception {
- DomApplicationComponent.getInstance().clearCachesInTests();
- super.tearDown();
- }
-
- @Override
protected String getBasePath() {
return "/xml/dom-tests/testData/stubs";
}
diff --git a/xml/impl/resources/liveTemplates/zen_html.xml b/xml/impl/resources/liveTemplates/zen_html.xml
index 43025dc..7f537b5 100644
--- a/xml/impl/resources/liveTemplates/zen_html.xml
+++ b/xml/impl/resources/liveTemplates/zen_html.xml
@@ -12,6 +12,12 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
+ <template description="" name="c" toReformat="true" toShortenFQNames="true" value="<!-- $VAR0$ -->$END$">
+ <variable alwaysStopAt="true" defaultValue="" expression="" name="VAR0"/>
+ <context>
+ <option name="HTML_TEXT" value="true"/>
+ </context>
+ </template>
<template description="" name="cc:ie6" toReformat="true" toShortenFQNames="true" value="<!--[if lte IE 6]> $END$ <![endif]-->">
<context>
<option name="HTML_TEXT" value="true"/>
@@ -28,7 +34,13 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="" name="html:5" toReformat="true" toShortenFQNames="true" value="<!DOCTYPE HTML> <html lang="$ENV_LOCALE$"> <head> <meta charset="UTF-8"> <title></title> </head> <body> $END$ </body> </html>">
+ <template description="" name="html:5" toReformat="true" toShortenFQNames="true" value="<!doctype html> <html lang="$ENV_LOCALE$"> <head> <meta charset="UTF-8"> <title></title> </head> <body> $END$ </body> </html>">
+ <variable alwaysStopAt="true" defaultValue=""en-US"" expression="" name="ENV_LOCALE"/>
+ <context>
+ <option name="HTML_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="" name="!" toReformat="true" toShortenFQNames="true" value="<!doctype html> <html lang="$ENV_LOCALE$"> <head> <meta charset="UTF-8"> <title></title> </head> <body> $END$ </body> </html>">
<variable alwaysStopAt="true" defaultValue=""en-US"" expression="" name="ENV_LOCALE"/>
<context>
<option name="HTML_TEXT" value="true"/>
@@ -57,14 +69,14 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="datetime-local" name="..." id="...">" name="input:datetime-local" toReformat="true" toShortenFQNames="true" value="<input type="datetime-local" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="datetime-local" name="..." id="..."/>" name="input:datetime-local" toReformat="true" toShortenFQNames="true" value="<input type="datetime-local" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="reset" value="...">" name="input:reset" toReformat="true" toShortenFQNames="true" value="<input type="reset" value="$VAR0$">">
+ <template description="<input type="reset" value="..."/>" name="input:reset" toReformat="true" toShortenFQNames="true" value="<input type="reset" value="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
@@ -85,7 +97,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<img src="..." alt="...">" name="img" toReformat="true" toShortenFQNames="true" value="<img src="$VAR0$" alt="$VAR1$">">
+ <template description="<img src="..." alt="...">" name="img" toReformat="true" toShortenFQNames="true" value="<img src="$VAR0$" alt="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -97,7 +109,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<param name="..." value="...">" name="param" toReformat="true" toShortenFQNames="true" value="<param name="$VAR0$" value="$VAR1$">">
+ <template description="<param name="..." value="...">" name="param" toReformat="true" toShortenFQNames="true" value="<param name="$VAR0$" value="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -125,51 +137,52 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="radio" name="..." id="...">" name="input:radio" toReformat="true" toShortenFQNames="true" value="<input type="radio" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="radio" name="..." id="..."/>" name="input:radio" toReformat="true" toShortenFQNames="true" value="<input type="radio" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<link rel="stylesheet" type="text/css" href="...print.css" media="print">" name="link:print" toReformat="true" toShortenFQNames="true" value="<link rel="stylesheet" type="text/css" href="$END$print.css" media="print">">
+ <template description="<link rel="stylesheet" type="text/css" href="...print.css" media="print">" name="link:print" toReformat="true" toShortenFQNames="true" value="<link rel="stylesheet" type="text/css" href="$END$print.css" media="print"/>">
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<option>...</option>" name="opt" toReformat="true" toShortenFQNames="true" value="<option>$END$</option>">
+ <template description="<option value="...">...</option>" name="opt" toReformat="true" toShortenFQNames="true" value="<option value="$VAR0$">$END$</option>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="image" src="..." alt="...">" name="input:i" toReformat="true" toShortenFQNames="true" value="<input type="image" src="$VAR0$" alt="$VAR1$">">
+ <template description="<input type="image" src="..." alt="..."/>" name="input:i" toReformat="true" toShortenFQNames="true" value="<input type="image" src="$VAR0$" alt="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="hidden" name="...">" name="input:h" toReformat="true" toShortenFQNames="true" value="<input type="hidden" name="$VAR0$">">
+ <template description="<input type="hidden" name="..."/>" name="input:h" toReformat="true" toShortenFQNames="true" value="<input type="hidden" name="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="file" name="..." id="...">" name="input:f" toReformat="true" toShortenFQNames="true" value="<input type="file" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="file" name="..." id="..."/>" name="input:f" toReformat="true" toShortenFQNames="true" value="<input type="file" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="checkbox" name="..." id="...">" name="input:c" toReformat="true" toShortenFQNames="true" value="<input type="checkbox" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="checkbox" name="..." id="..."/>" name="input:c" toReformat="true" toShortenFQNames="true" value="<input type="checkbox" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="button" value="...">" name="input:b" toReformat="true" toShortenFQNames="true" value="<input type="button" value="$VAR0$">">
+ <template description="<input type="button" value="..."/>" name="input:b" toReformat="true" toShortenFQNames="true" value="<input type="button" value="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
@@ -181,39 +194,42 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="text" name="..." id="...">" name="input:t" toReformat="true" toShortenFQNames="true" value="<input type="text" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="text" name="..." id="..."/>" name="input:t" toReformat="true" toShortenFQNames="true" value="<input type="text" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="password" name="..." id="...">" name="input:p" toReformat="true" toShortenFQNames="true" value="<input type="password" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="password" name="..." id="..."/>" name="input:p" toReformat="true" toShortenFQNames="true" value="<input type="password" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="submit" value="...">" name="input:s" toReformat="true" toShortenFQNames="true" value="<input type="submit" value="$VAR0$">">
+ <template description="<input type="submit" value="..."/>" name="input:s" toReformat="true" toShortenFQNames="true" value="<input type="submit" value="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="radio" name="..." id="...">" name="input:r" toReformat="true" toShortenFQNames="true" value="<input type="radio" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="radio" name="..." id="..."/>" name="input:r" toReformat="true" toShortenFQNames="true" value="<input type="radio" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<iframe>...</iframe>" name="ifr" toReformat="true" toShortenFQNames="true" value="<iframe>$END$</iframe>">
+ <template description="<iframe src="..." frameborder="0">...</iframe>" name="ifr" toReformat="true" toShortenFQNames="true" value="<iframe src="$VAR0$" frameborder="0">$END$</iframe>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<embed>" name="emb" toReformat="true" toShortenFQNames="true" value="<embed>">
+ <template description="<embed src="..." type="..."/>" name="emb" toReformat="true" toShortenFQNames="true" value="<embed src="$VAR0$" type="$VAR1$"/>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -229,7 +245,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<link rel="alternate" type="application/atom+xml" title="Atom" href="atom.xml">" name="link:atom" toReformat="true" toShortenFQNames="true" value="<link rel="alternate" type="application/atom+xml" title="Atom" href="atom.xml">">
+ <template description="<link rel="alternate" type="application/atom+xml" title="Atom" href="atom.xml">" name="link:atom" toReformat="true" toShortenFQNames="true" value="<link rel="alternate" type="application/atom+xml" title="Atom" href="atom.xml"/>">
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -244,14 +260,14 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="search" name="..." id="...">" name="input:search" toReformat="true" toShortenFQNames="true" value="<input type="search" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="search" name="..." id="..."/>" name="input:search" toReformat="true" toShortenFQNames="true" value="<input type="search" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<area shape="rect" coords="..." href="..." alt="...">" name="area:r" toReformat="true" toShortenFQNames="true" value="<area shape="rect" coords="$VAR0$" href="$VAR1$" alt="$VAR2$">">
+ <template description="<area shape="rect" coords="..." href="..." alt="...">" name="area:r" toReformat="true" toShortenFQNames="true" value="<area shape="rect" coords="$VAR0$" href="$VAR1$" alt="$VAR2$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR2"/>
@@ -259,7 +275,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<area shape="poly" coords="..." href="..." alt="...">" name="area:p" toReformat="true" toShortenFQNames="true" value="<area shape="poly" coords="$VAR0$" href="$VAR1$" alt="$VAR2$">">
+ <template description="<area shape="poly" coords="..." href="..." alt="...">" name="area:p" toReformat="true" toShortenFQNames="true" value="<area shape="poly" coords="$VAR0$" href="$VAR1$" alt="$VAR2$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR2"/>
@@ -267,7 +283,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="date" name="..." id="...">" name="input:date" toReformat="true" toShortenFQNames="true" value="<input type="date" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="date" name="..." id="..."/>" name="input:date" toReformat="true" toShortenFQNames="true" value="<input type="date" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -280,20 +296,20 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="button" value="...">" name="input:button" toReformat="true" toShortenFQNames="true" value="<input type="button" value="$VAR0$">">
+ <template description="<input type="button" value="..."/>" name="input:button" toReformat="true" toShortenFQNames="true" value="<input type="button" value="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<area shape="default" href="..." alt="...">" name="area:d" toReformat="true" toShortenFQNames="true" value="<area shape="default" href="$VAR0$" alt="$VAR1$">">
+ <template description="<area shape="default" href="..." alt="...">" name="area:d" toReformat="true" toShortenFQNames="true" value="<area shape="default" href="$VAR0$" alt="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<area shape="circle" coords="..." href="..." alt="...">" name="area:c" toReformat="true" toShortenFQNames="true" value="<area shape="circle" coords="$VAR0$" href="$VAR1$" alt="$VAR2$">">
+ <template description="<area shape="circle" coords="..." href="..." alt="...">" name="area:c" toReformat="true" toShortenFQNames="true" value="<area shape="circle" coords="$VAR0$" href="$VAR1$" alt="$VAR2$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR2"/>
@@ -328,7 +344,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">" name="meta:utf" toReformat="true" toShortenFQNames="true" value="<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">">
+ <template description="<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">" name="meta:utf" toReformat="true" toShortenFQNames="true" value="<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>">
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -339,14 +355,14 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="time" name="..." id="...">" name="input:time" toReformat="true" toShortenFQNames="true" value="<input type="time" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="time" name="..." id="..."/>" name="input:time" toReformat="true" toShortenFQNames="true" value="<input type="time" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<link rel="shortcut icon" type="image/x-icon" href="...favicon.ico">" name="link:favicon" toReformat="true" toShortenFQNames="true" value="<link rel="shortcut icon" type="image/x-icon" href="$END$favicon.ico">">
+ <template description="<link rel="shortcut icon" type="image/x-icon" href="...favicon.ico">" name="link:favicon" toReformat="true" toShortenFQNames="true" value="<link rel="shortcut icon" type="image/x-icon" href="$END$favicon.ico"/>">
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -361,7 +377,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="email" name="..." id="...">" name="input:email" toReformat="true" toShortenFQNames="true" value="<input type="email" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="email" name="..." id="..."/>" name="input:email" toReformat="true" toShortenFQNames="true" value="<input type="email" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -405,7 +421,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<base href="...">" name="base" toReformat="true" toShortenFQNames="true" value="<base href="$VAR0$">">
+ <template description="<base href="...">" name="base" toReformat="true" toShortenFQNames="true" value="<base href="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
@@ -421,7 +437,9 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<object>...</object>" name="obj" toReformat="true" toShortenFQNames="true" value="<object>$END$</object>">
+ <template description="<object data="..." type="...">...</object>" name="obj" toReformat="true" toShortenFQNames="true" value="<object data="$VAR0$" type="$VAR1$">$END$</object>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -437,26 +455,29 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<acronym>...</acronym>" name="acr" toReformat="true" toShortenFQNames="true" value="<acronym>$END$</acronym>">
+ <template description="<acronym title="...">...</acronym>" name="acr" toReformat="true" toShortenFQNames="true" value="<acronym title="$VAR0$">$END$</acronym>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="password" name="..." id="...">" name="input:password" toReformat="true" toShortenFQNames="true" value="<input type="password" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="password" name="..." id="..."/>" name="input:password" toReformat="true" toShortenFQNames="true" value="<input type="password" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="file" name="..." id="...">" name="input:file" toReformat="true" toShortenFQNames="true" value="<input type="file" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="file" name="..." id="..."/>" name="input:file" toReformat="true" toShortenFQNames="true" value="<input type="file" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<textarea>...</textarea>" name="tarea" toReformat="true" toShortenFQNames="true" value="<textarea>$END$</textarea>">
+ <template description="<textarea name="..." id="..." cols="30" rows="10">...</textarea>" name="tarea" toReformat="true" toShortenFQNames="true" value="<textarea name="$VAR0$" id="$VAR1$" cols="30" rows="10">$END$</textarea>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -468,21 +489,21 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="number" name="..." id="...">" name="input:number" toReformat="true" toShortenFQNames="true" value="<input type="number" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="number" name="..." id="..."/>" name="input:number" toReformat="true" toShortenFQNames="true" value="<input type="number" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="range" name="..." id="...">" name="input:range" toReformat="true" toShortenFQNames="true" value="<input type="range" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="range" name="..." id="..."/>" name="input:range" toReformat="true" toShortenFQNames="true" value="<input type="range" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<area shape="..." coords="..." href="..." alt="...">" name="area" toReformat="true" toShortenFQNames="true" value="<area shape="$VAR0$" coords="$VAR1$" href="$VAR2$" alt="$VAR3$">">
+ <template description="<area shape="..." coords="..." href="..." alt="...">" name="area" toReformat="true" toShortenFQNames="true" value="<area shape="$VAR0$" coords="$VAR1$" href="$VAR2$" alt="$VAR3$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR2"/>
@@ -491,7 +512,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="image" src="..." alt="...">" name="input:image" toReformat="true" toShortenFQNames="true" value="<input type="image" src="$VAR0$" alt="$VAR1$">">
+ <template description="<input type="image" src="..." alt="..."/>" name="input:image" toReformat="true" toShortenFQNames="true" value="<input type="image" src="$VAR0$" alt="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -503,7 +524,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="month" name="..." id="...">" name="input:month" toReformat="true" toShortenFQNames="true" value="<input type="month" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="month" name="..." id="..."/>" name="input:month" toReformat="true" toShortenFQNames="true" value="<input type="month" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -515,7 +536,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<meta http-equiv="Content-Type" content="text/html;charset=Win-1251">" name="meta:win" toReformat="true" toShortenFQNames="true" value="<meta http-equiv="Content-Type" content="text/html;charset=Win-1251">">
+ <template description="<meta http-equiv="Content-Type" content="text/html;charset=Win-1251">" name="meta:win" toReformat="true" toShortenFQNames="true" value="<meta http-equiv="Content-Type" content="text/html;charset=windows-1251"/>">
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -536,19 +557,19 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<link rel="stylesheet" href="...">" name="link" toReformat="true" toShortenFQNames="true" value="<link rel="stylesheet" href="$VAR0$">">
+ <template description="<link rel="stylesheet" href="...">" name="link" toReformat="true" toShortenFQNames="true" value="<link rel="stylesheet" href="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="...">" name="input" toReformat="true" toShortenFQNames="true" value="<input type="$VAR0$">">
+ <template description="<input type="..."/>" name="input" toReformat="true" toShortenFQNames="true" value="<input type="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<link rel="alternate" type="application/rss+xml" title="RSS" href="...rss.xml">" name="link:rss" toReformat="true" toShortenFQNames="true" value="<link rel="alternate" type="application/rss+xml" title="RSS" href="$END$rss.xml">">
+ <template description="<link rel="alternate" type="application/rss+xml" title="RSS" href="...rss.xml">" name="link:rss" toReformat="true" toShortenFQNames="true" value="<link rel="alternate" type="application/rss+xml" title="RSS" href="$END$rss.xml"/>">
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -576,7 +597,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<link rel="apple-touch-icon" href="...favicon.png">" name="link:touch" toReformat="true" toShortenFQNames="true" value="<link rel="apple-touch-icon" href="$END$favicon.png">">
+ <template description="<link rel="apple-touch-icon" href="...favicon.png">" name="link:touch" toReformat="true" toShortenFQNames="true" value="<link rel="apple-touch-icon" href="$END$favicon.png"/>">
<context>
<option name="HTML_TEXT" value="true"/>
</context>
@@ -587,19 +608,19 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<link rel="stylesheet" type="text/css" href="....css" media="all">" name="link:css" toReformat="true" toShortenFQNames="true" value="<link rel="stylesheet" type="text/css" href="$END$.css" media="all">">
+ <template description="<link rel="stylesheet" type="text/css" href="....css" media="all">" name="link:css" toReformat="true" toShortenFQNames="true" value="<link rel="stylesheet" type="text/css" href="$END$.css" media="all"/>">
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="week" name="..." id="...">" name="input:week" toReformat="true" toShortenFQNames="true" value="<input type="week" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="week" name="..." id="..."/>" name="input:week" toReformat="true" toShortenFQNames="true" value="<input type="week" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<embed src="..." type="...">" name="embed" toReformat="true" toShortenFQNames="true" value="<embed src="$VAR0$" type="$VAR1$">">
+ <template description="<embed src="..." type="..."/>" name="embed" toReformat="true" toShortenFQNames="true" value="<embed src="$VAR0$" type="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -611,7 +632,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="datetime" name="..." id="...">" name="input:datetime" toReformat="true" toShortenFQNames="true" value="<input type="datetime" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="datetime" name="..." id="..."/>" name="input:datetime" toReformat="true" toShortenFQNames="true" value="<input type="datetime" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -634,7 +655,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="url" name="..." id="...">" name="input:url" toReformat="true" toShortenFQNames="true" value="<input type="url" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="url" name="..." id="..."/>" name="input:url" toReformat="true" toShortenFQNames="true" value="<input type="url" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -657,19 +678,20 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="color" name="..." id="...">" name="input:color" toReformat="true" toShortenFQNames="true" value="<input type="color" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="color" name="..." id="..."/>" name="input:color" toReformat="true" toShortenFQNames="true" value="<input type="color" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<meta http-equiv="X-UA-Compatible" content="IE=7">" name="meta:compat" toReformat="true" toShortenFQNames="true" value="<meta http-equiv="X-UA-Compatible" content="IE=7">">
+ <template description="<meta http-equiv="X-UA-Compatible" content="IE=7"/>" name="meta:compat" toReformat="true" toShortenFQNames="true" value="<meta http-equiv="X-UA-Compatible" content="$VAR0$"/>">
+ <variable alwaysStopAt="true" defaultValue=""IE=7"" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="hidden" name="...">" name="input:hidden" toReformat="true" toShortenFQNames="true" value="<input type="hidden" name="$VAR0$">">
+ <template description="<input type="hidden" name="..."/>" name="input:hidden" toReformat="true" toShortenFQNames="true" value="<input type="hidden" name="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
@@ -711,7 +733,7 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="" name="map+" toReformat="true" toShortenFQNames="true" value="<map name="$VAR0$"> <area shape="$VAR1$" coords="$VAR2$" href="$VAR3$" alt="$VAR4$"> </map>">
+ <template description="" name="map+" toReformat="true" toShortenFQNames="true" value="<map name="$VAR0$"> <area shape="$VAR1$" coords="$VAR2$" href="$VAR3$" alt="$VAR4$"/> </map>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR2"/>
@@ -721,20 +743,20 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="submit" value="...">" name="input:submit" toReformat="true" toShortenFQNames="true" value="<input type="submit" value="$VAR0$">">
+ <template description="<input type="submit" value="..."/>" name="input:submit" toReformat="true" toShortenFQNames="true" value="<input type="submit" value="$VAR0$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="text" name="..." id="...">" name="input:text" toReformat="true" toShortenFQNames="true" value="<input type="text" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="text" name="..." id="..."/>" name="input:text" toReformat="true" toShortenFQNames="true" value="<input type="text" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
<option name="HTML_TEXT" value="true"/>
</context>
</template>
- <template description="<input type="checkbox" name="..." id="...">" name="input:checkbox" toReformat="true" toShortenFQNames="true" value="<input type="checkbox" name="$VAR0$" id="$VAR1$">">
+ <template description="<input type="checkbox" name="..." id="..."/>" name="input:checkbox" toReformat="true" toShortenFQNames="true" value="<input type="checkbox" name="$VAR0$" id="$VAR1$"/>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -763,4 +785,9 @@
<option name="HTML_TEXT" value="true"/>
</context>
</template>
+ <template description="<figcaption>...</figcaption>" name="figc" toReformat="true" toShortenFQNames="true" value="<figcaption>$END$</figcaption>">
+ <context>
+ <option name="HTML_TEXT" value="true"/>
+ </context>
+ </template>
</templateSet>
diff --git a/xml/impl/resources/liveTemplates/zen_xsl.xml b/xml/impl/resources/liveTemplates/zen_xsl.xml
index dbd599b..39fb4fd 100644
--- a/xml/impl/resources/liveTemplates/zen_xsl.xml
+++ b/xml/impl/resources/liveTemplates/zen_xsl.xml
@@ -7,7 +7,13 @@
<option name="XSL_TEXT" value="true"/>
</context>
</template>
- <template description="<xsl:param name="..." select="..." />" name="par" toReformat="true" toShortenFQNames="true" value="<xsl:param name="$VAR0$" select="$VAR1$" />">
+ <template description="<xsl:param name="...">...</xsl:param>" name="par" toReformat="true" toShortenFQNames="true" value="<xsl:param name="$VAR0$" >$END$</xsl:param>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:param name="..." select="..." />" name="pare" toReformat="true" toShortenFQNames="true" value="<xsl:param name="$VAR0$" select="$VAR1$" />">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
<context>
@@ -82,6 +88,11 @@
<option name="XSL_TEXT" value="true"/>
</context>
</template>
+ <template description="<xsl:choose>...</xsl:choose>" name="ch" toReformat="true" toShortenFQNames="true" value="<xsl:choose>$END$</xsl:choose>">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
<template description="" name="choose+" toReformat="true" toShortenFQNames="true" value="<xsl:choose> <xsl:when test="$VAR0$">$VAR1$</xsl:when> <xsl:otherwise>$END$</xsl:otherwise> </xsl:choose>">
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
<variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
@@ -115,4 +126,112 @@
<option name="XSL_TEXT" value="true"/>
</context>
</template>
+ <template description="<xsl:sort select="..." order="..." />" name="sort" toReformat="true" toShortenFQNames="true" value="<xsl:sort select="$VAR0$" order="$VAR1$" />">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:processing-instruction name="...">...</xsl:processing-instruction>" name="proc" toReformat="true" toShortenFQNames="true" value="<xsl:processing-instruction name="$VAR0$">$END$</xsl:processing-instruction>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:for-each select="...">...</xsl:for-each>" name="for" toReformat="true" toShortenFQNames="true" value="<xsl:for-each select="$VAR0$">$END$</xsl:for-each>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:strip-space elements="..." />>" name="strip" toReformat="true" toShortenFQNames="true" value="<xsl:strip-space elements="$END$" />">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:preserve-space elements="..." />" name="pres" toReformat="true" toShortenFQNames="true" value="<xsl:preserve-space elements="$END$" />">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<namespace-alias stylesheet-prefix="..." result-prefix="..." />" name="nam" toReformat="true" toShortenFQNames="true" value="<namespace-alias stylesheet-prefix="$VAR0$" result-prefix="$END$" />">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:number value="..." />" name="num" toReformat="true" toShortenFQNames="true" value="<xsl:number value="$END$" />">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:fallback>...</xsl:fallback>" name="fall" toReformat="true" toShortenFQNames="true" value="<xsl:fallback>$END$</xsl:fallback>">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:message terminate="no">...</xsl:message>" name="msg" toReformat="true" toShortenFQNames="true" value="<xsl:message terminate="no">$END$</xsl:message>">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:comment>...</xsl:comment>" name="com" toReformat="true" toShortenFQNames="true" value="<xsl:comment>$END$</xsl:comment>">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:otherwise></xsl:otherwise>" name="ot" toReformat="true" toShortenFQNames="true" value="<xsl:otherwise>$END$</xsl:otherwise>">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:text></xsl:text>" name="tex" toReformat="true" toShortenFQNames="true" value="<xsl:text>$END$</xsl:text>">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:apply-imports/>" name="api" toReformat="true" toShortenFQNames="true" value="<xsl:apply-imports/>">
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:copy select="..."/>" name="cp" toReformat="true" toShortenFQNames="true" value="<xsl:copy select="$VAR0$"/>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:attribute-set name="...">...</xsl:attribute-set>" name="attrs" toReformat="true" toShortenFQNames="true" value="<xsl:attribute-set name="$VAR0$">$END$</xsl:attribute-set>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:element name="...">...</xsl:element>" name="elem" toReformat="true" toShortenFQNames="true" value="<xsl:element name="$VAR0$">$END$</xsl:element>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:include href="..."/>" name="inc" toReformat="true" toShortenFQNames="true" value="<xsl:include href="$VAR0$"/>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:import href="..."/>" name="imp" toReformat="true" toShortenFQNames="true" value="<xsl:import href="$VAR0$"/>">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
+ <template description="<xsl:key name="..." match="..." use="..." />" name="key" toReformat="true" toShortenFQNames="true" value="<xsl:key name="$VAR0$" match="$VAR1$" use="$VAR2$" />">
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR0"/>
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR1"/>
+ <variable alwaysStopAt="true" defaultValue="""" expression="" name="VAR2"/>
+ <context>
+ <option name="XSL_TEXT" value="true"/>
+ </context>
+ </template>
</templateSet>
diff --git a/xml/impl/src/com/intellij/codeInsight/actions/GenerateDTDAction.java b/xml/impl/src/com/intellij/codeInsight/actions/GenerateDTDAction.java
index 1aa5ae4..027bb15 100644
--- a/xml/impl/src/com/intellij/codeInsight/actions/GenerateDTDAction.java
+++ b/xml/impl/src/com/intellij/codeInsight/actions/GenerateDTDAction.java
@@ -42,6 +42,7 @@
*/
public class GenerateDTDAction extends BaseCodeInsightAction{
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.actions.GenerateDTDAction");
+ @NotNull
protected CodeInsightActionHandler getHandler(){
return new CodeInsightActionHandler(){
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
@@ -115,7 +116,7 @@
}
}
- protected boolean isValidForFile(Project project, Editor editor, PsiFile file){
+ protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file){
return file instanceof XmlFile;
}
}
diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlSmartEnterProcessor.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlSmartEnterProcessor.java
index e01a7e4..001f339 100644
--- a/xml/impl/src/com/intellij/codeInsight/completion/XmlSmartEnterProcessor.java
+++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlSmartEnterProcessor.java
@@ -38,10 +38,6 @@
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.XmlSmartEnterProcessor");
public boolean process(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile psiFile) {
- /*if (!completeEndTag(project, editor, psiFile)) {
- return XmlZenCodingTemplate.startZenCoding(editor, psiFile, null);
- }
- return true;*/
return completeEndTag(project, editor, psiFile);
}
diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java
index 96b111a..f97208d 100644
--- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java
+++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java
@@ -157,6 +157,8 @@
}
}
}
+ } else if (tokenType == XmlTokenType.XML_TAG_CHARACTERS && token.getParent() instanceof XmlProcessingInstruction) {
+ checkReferences(token);
}
}
diff --git a/xml/impl/src/com/intellij/codeInsight/intentions/XmlChooseColorIntentionAction.java b/xml/impl/src/com/intellij/codeInsight/intentions/XmlChooseColorIntentionAction.java
index 5bfdb3b..e92d59b 100644
--- a/xml/impl/src/com/intellij/codeInsight/intentions/XmlChooseColorIntentionAction.java
+++ b/xml/impl/src/com/intellij/codeInsight/intentions/XmlChooseColorIntentionAction.java
@@ -18,6 +18,8 @@
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
@@ -56,12 +58,11 @@
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
- if (!CodeInsightUtilBase.preparePsiElementForWrite(element)) return;
- invokeForLiteral(editor.getComponent(), element);
+ chooseColor(editor.getComponent(), element, getText(), false);
}
- private void invokeForLiteral(JComponent editorComponent, PsiElement element) {
- final XmlAttributeValue literal = PsiTreeUtil.getParentOfType(element, XmlAttributeValue.class);
+ public static void chooseColor(JComponent editorComponent, PsiElement element, String caption, boolean startInWriteAction) {
+ final XmlAttributeValue literal = PsiTreeUtil.getParentOfType(element, XmlAttributeValue.class, false);
if (literal == null) return;
final String text = StringUtil.unquoteString(literal.getValue());
final String hexPrefix = text.startsWith("#") ? "#" : "";
@@ -73,13 +74,28 @@
catch (NumberFormatException e) {
oldColor = Color.GRAY;
}
- Color color = ColorChooser.chooseColor(editorComponent, getText(), oldColor, true);
+ Color color = ColorChooser.chooseColor(editorComponent, caption, oldColor, true);
if (color == null) return;
if (!Comparing.equal(color, oldColor)) {
+ if (!CodeInsightUtilBase.preparePsiElementForWrite(element)) return;
final String newText = hexPrefix + ColorUtil.toHex(color);
final PsiManager manager = literal.getManager();
final XmlAttribute newAttribute = XmlElementFactory.getInstance(manager.getProject()).createXmlAttribute("name", newText);
- literal.replace(newAttribute.getValueElement());
+ final Runnable replaceRunnable = new Runnable() {
+ public void run() {
+ literal.replace(newAttribute.getValueElement());
+ }
+ };
+ if (startInWriteAction) {
+ new WriteCommandAction(element.getProject(), caption) {
+ @Override
+ protected void run(Result result) throws Throwable {
+ replaceRunnable.run();
+ }
+ }.execute();
+ } else {
+ replaceRunnable.run();
+ }
}
}
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/HtmlTextContextType.java b/xml/impl/src/com/intellij/codeInsight/template/HtmlTextContextType.java
index 5a61b4e..63b9593 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/HtmlTextContextType.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/HtmlTextContextType.java
@@ -29,7 +29,7 @@
* @author Eugene.Kudelevsky
*/
public class HtmlTextContextType extends TemplateContextType {
- protected HtmlTextContextType() {
+ public HtmlTextContextType() {
super("HTML_TEXT", CodeInsightBundle.message("dialog.edit.template.checkbox.html.text"), HtmlContextType.class);
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/XslTextContextType.java b/xml/impl/src/com/intellij/codeInsight/template/XslTextContextType.java
index 7f22c59..b9d0ba3 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/XslTextContextType.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/XslTextContextType.java
@@ -14,7 +14,7 @@
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.openapi.fileTypes.StdFileTypes;
-import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
@@ -23,13 +23,13 @@
* @author Eugene.Kudelevsky
*/
public class XslTextContextType extends TemplateContextType {
- protected XslTextContextType() {
+ public XslTextContextType() {
super("XSL_TEXT", CodeInsightBundle.message("dialog.edit.template.checkbox.xsl.text"), XmlContextType.class);
}
@Override
public boolean isInContext(@NotNull PsiFile file, int offset) {
- if (file.getFileType() == StdFileTypes.XML && FileUtil.getExtension(file.getName()).equals("xsl")) {
+ if (file.getFileType() == StdFileTypes.XML && FileUtilRt.extensionEquals(file.getName(), "xsl")) {
PsiElement element = file.findElementAt(offset);
return element == null || HtmlTextContextType.isInContext(element);
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetLexer.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetLexer.java
new file mode 100644
index 0000000..66aed8d
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetLexer.java
@@ -0,0 +1,153 @@
+/*
+ * 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 com.intellij.codeInsight.template.emmet;
+
+import com.intellij.codeInsight.template.emmet.tokens.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: zolotov
+ * Date: 1/25/13
+ */
+public class EmmetLexer {
+ private static final String DELIMS = ">^+*|()[]{}.#,='\" \0";
+
+ @Nullable
+ public List<ZenCodingToken> lex(@NotNull String text) {
+ text += ZenCodingTemplate.MARKER;
+ final List<ZenCodingToken> result = new ArrayList<ZenCodingToken>();
+
+ boolean inQuotes = false;
+ boolean inApostrophes = false;
+ int bracesStack = 0;
+
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < text.length(); i++) {
+ final char c = text.charAt(i);
+
+ if (inQuotes) {
+ builder.append(c);
+ if (c == '"') {
+ inQuotes = false;
+ result.add(new StringLiteralToken(builder.toString()));
+ builder = new StringBuilder();
+ }
+ continue;
+ }
+
+ if (inApostrophes) {
+ builder.append(c);
+ if (c == '\'') {
+ inApostrophes = false;
+ result.add(new StringLiteralToken(builder.toString()));
+ builder = new StringBuilder();
+ }
+ continue;
+ }
+
+ if (bracesStack > 0) {
+ builder.append(c);
+ if (c == '}') {
+ bracesStack--;
+ if (bracesStack == 0) {
+ result.add(new TextToken(builder.toString()));
+ builder = new StringBuilder();
+ }
+ }
+ else if (c == '{') {
+ bracesStack++;
+ }
+ continue;
+ }
+
+ if (DELIMS.indexOf(c) < 0) {
+ builder.append(c);
+ }
+ else {
+ // handle special case: ul+ template
+ if (c == '+' && (i == text.length() - 2 || text.charAt(i + 1) == ')')) {
+ builder.append(c);
+ continue;
+ }
+
+ if (builder.length() > 0) {
+ final String tokenText = builder.toString();
+ final int n = ZenCodingUtil.parseNonNegativeInt(tokenText);
+ if (n >= 0) {
+ result.add(new NumberToken(n));
+ }
+ else {
+ result.add(new IdentifierToken(tokenText));
+ }
+ builder = new StringBuilder();
+ }
+ if (c == '"') {
+ inQuotes = true;
+ builder.append(c);
+ }
+ else if (c == '\'') {
+ inApostrophes = true;
+ builder.append(c);
+ }
+ else if (c == '{') {
+ bracesStack = 1;
+ builder.append(c);
+ }
+ else if (c == '(') {
+ result.add(ZenCodingTokens.OPENING_R_BRACKET);
+ }
+ else if (c == ')') {
+ result.add(ZenCodingTokens.CLOSING_R_BRACKET);
+ }
+ else if (c == '[') {
+ result.add(ZenCodingTokens.OPENING_SQ_BRACKET);
+ }
+ else if (c == ']') {
+ result.add(ZenCodingTokens.CLOSING_SQ_BRACKET);
+ }
+ else if (c == '=') {
+ result.add(ZenCodingTokens.EQ);
+ }
+ else if (c == '.') {
+ result.add(ZenCodingTokens.DOT);
+ }
+ else if (c == '#') {
+ result.add(ZenCodingTokens.SHARP);
+ }
+ else if (c == ',') {
+ result.add(ZenCodingTokens.COMMA);
+ }
+ else if (c == ' ') {
+ result.add(ZenCodingTokens.SPACE);
+ }
+ else if (c == '|') {
+ result.add(ZenCodingTokens.PIPE);
+ }
+ else if (c != ZenCodingTemplate.MARKER) {
+ result.add(new OperationToken(c));
+ }
+ }
+ }
+ if (bracesStack != 0 || inQuotes || inApostrophes) {
+ return null;
+ }
+ return result;
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetParser.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetParser.java
new file mode 100644
index 0000000..337818e
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/EmmetParser.java
@@ -0,0 +1,232 @@
+/*
+ * 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 com.intellij.codeInsight.template.emmet;
+
+import com.intellij.codeInsight.template.CustomTemplateCallback;
+import com.intellij.codeInsight.template.emmet.generators.ZenCodingGenerator;
+import com.intellij.codeInsight.template.emmet.nodes.*;
+import com.intellij.codeInsight.template.emmet.tokens.*;
+import com.intellij.codeInsight.template.impl.TemplateImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * User: zolotov
+ * Date: 1/25/13
+ */
+public abstract class EmmetParser {
+ private final List<ZenCodingToken> myTokens;
+ protected final CustomTemplateCallback myCallback;
+ private final ZenCodingGenerator myGenerator;
+
+ private int myIndex = 0;
+
+ EmmetParser(List<ZenCodingToken> tokens, CustomTemplateCallback callback, ZenCodingGenerator generator) {
+ myTokens = tokens;
+ myCallback = callback;
+ myGenerator = generator;
+ }
+
+ public int getIndex() {
+ return myIndex;
+ }
+
+ @Nullable
+ public ZenCodingNode parse() {
+ ZenCodingNode add = parseAddOrMore();
+ if (add == null) {
+ return null;
+ }
+
+ ZenCodingNode result = add;
+
+ while (true) {
+ ZenCodingToken token = getToken();
+ if (token != ZenCodingTokens.PIPE) {
+ return result;
+ }
+
+ advance();
+ token = getToken();
+ if (!(token instanceof IdentifierToken)) {
+ return null;
+ }
+
+ final String filterSuffix = ((IdentifierToken)token).getText();
+ if (!ZenCodingUtil.checkFilterSuffix(filterSuffix)) {
+ return null;
+ }
+
+ advance();
+ result = new FilterNode(result, filterSuffix);
+ }
+ }
+
+ @Nullable
+ protected ZenCodingNode parseAddOrMore() {
+ ZenCodingNode mul = parseMul();
+
+ ZenCodingToken operationToken = getToken();
+ if (!(operationToken instanceof OperationToken)) {
+ return mul;
+ }
+ char sign = ((OperationToken)operationToken).getSign();
+
+ if (sign == '^') {
+ return parseClimbUpOperation(mul);
+ }
+ if (mul == null) {
+ return null;
+ }
+ if (sign == '+') {
+ advance();
+ ZenCodingNode add2 = parseAddOrMore();
+ if (add2 == null) {
+ return null;
+ }
+ return new AddOperationNode(mul, add2);
+ }
+ else if (sign == '>') {
+ return parseMoreOperation(mul);
+ }
+ return null;
+ }
+
+ protected ZenCodingNode parseClimbUpOperation(@Nullable ZenCodingNode leftPart) {
+ advance();
+ leftPart = leftPart != null ? leftPart : ZenEmptyNode.INSTANCE;
+ ZenCodingNode rigthPart = parseAddOrMore();
+ if (rigthPart == null) {
+ return null;
+ }
+ return new ClimbUpOperationNode(leftPart, rigthPart);
+ }
+
+ protected ZenCodingNode parseMoreOperation(@NotNull ZenCodingNode leftPart) {
+ advance();
+ ZenCodingNode rightPart = parseAddOrMore();
+ if (rightPart == null) {
+ return null;
+ }
+ return new MoreOperationNode(leftPart, rightPart);
+ }
+
+ protected int advance() {
+ return myIndex++;
+ }
+
+ @Nullable
+ private ZenCodingNode parseMul() {
+ ZenCodingNode exp = parseExpressionInBraces();
+ if (exp == null) {
+ return null;
+ }
+ ZenCodingToken operationToken = getToken();
+ if (!(operationToken instanceof OperationToken)) {
+ return exp;
+ }
+ if (((OperationToken)operationToken).getSign() != '*') {
+ return exp;
+ }
+ advance();
+ ZenCodingToken numberToken = getToken();
+ if (numberToken instanceof NumberToken) {
+ advance();
+ return new MulOperationNode(exp, ((NumberToken)numberToken).getNumber());
+ }
+ return new UnaryMulOperationNode(exp);
+ }
+
+ @Nullable
+ private ZenCodingNode parseExpressionInBraces() {
+ ZenCodingToken token = getToken();
+ if (token == ZenCodingTokens.OPENING_R_BRACKET) {
+ advance();
+ ZenCodingNode add = parseAddOrMore();
+ if (add == null) {
+ return null;
+ }
+ ZenCodingToken closingBrace = getToken();
+ if (closingBrace != ZenCodingTokens.CLOSING_R_BRACKET) {
+ return null;
+ }
+ advance();
+ return add;
+ }
+ else if (token instanceof TextToken) {
+ advance();
+ return new TextNode((TextToken)token);
+ }
+
+ final ZenCodingNode templateNode = parseTemplate();
+ if (templateNode == null) {
+ return null;
+ }
+
+ token = getToken();
+ if (token instanceof TextToken) {
+ advance();
+ return new MoreOperationNode(templateNode, new TextNode((TextToken)token));
+ }
+ return templateNode;
+ }
+
+ @Nullable
+ protected ZenCodingNode parseTemplate() {
+ ZenCodingToken token = getToken();
+ if (!(token instanceof IdentifierToken)) {
+ return null;
+ }
+ String templateKey = ((IdentifierToken)token).getText();
+ advance();
+
+ TemplateImpl template = myCallback.findApplicableTemplate(templateKey);
+ if (template == null && !ZenCodingUtil.isXML11ValidQName(templateKey)) {
+ return null;
+ }
+
+ final TemplateToken templateToken = new TemplateToken(templateKey);
+ if (!setTemplate(templateToken, template)) {
+ return null;
+ }
+ return new TemplateNode(templateToken);
+ }
+
+ @Nullable
+ protected String getDefaultTemplateKey() {
+ return null;
+ }
+
+ protected boolean setTemplate(final TemplateToken token, TemplateImpl template) {
+ if (template == null) {
+ template = myGenerator.createTemplateByKey(token.getKey());
+ }
+ if (template == null) {
+ return false;
+ }
+ return ZenCodingTemplate.doSetTemplate(token, template, myCallback);
+ }
+
+ @Nullable
+ protected ZenCodingToken getToken() {
+ if (myIndex < myTokens.size()) {
+ return myTokens.get(myIndex);
+ }
+ return null;
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/XmlEmmetParser.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/XmlEmmetParser.java
new file mode 100644
index 0000000..e0935b7
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/XmlEmmetParser.java
@@ -0,0 +1,369 @@
+/*
+ * 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 com.intellij.codeInsight.template.emmet;
+
+import com.google.common.base.Strings;
+import com.intellij.codeInsight.template.CustomTemplateCallback;
+import com.intellij.codeInsight.template.emmet.generators.ZenCodingGenerator;
+import com.intellij.codeInsight.template.emmet.nodes.*;
+import com.intellij.codeInsight.template.emmet.tokens.*;
+import com.intellij.codeInsight.template.impl.TemplateImpl;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.containers.Stack;
+import com.intellij.xml.util.HtmlUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.intellij.openapi.util.text.StringUtil.startsWithIgnoreCase;
+
+/**
+ * User: zolotov
+ * Date: 2/7/13
+ */
+public class XmlEmmetParser extends EmmetParser {
+ private static final String DEFAULT_TAG = "div";
+ private static final int DEFAULT_LOREM_LENGTH = 30;
+ private static final Pattern LOREM_PATTERN = Pattern.compile("(lorem|lipsum)(\\d*)");
+ @NonNls private static final String DEFAULT_INLINE_TAG = "span";
+ @NonNls private static final String LOREM_KEYWORD = "lorem";
+ @NonNls private static final String LIPSUM_KEYWORD = "lipsum";
+ private static final String ID = "id";
+ private static final String CLASS = "class";
+
+ private boolean hasTagContext = false;
+ private final Stack<String> tagLevel = new Stack<String>();
+
+ private static Map<String, String> parentChildTagMapping = new HashMap<String, String>() {{
+ put("p", "span");
+ put("ul", "li");
+ put("ol", "li");
+ put("table", "tr");
+ put("tr", "td");
+ put("tbody", "tr");
+ put("thead", "tr");
+ put("tfoot", "tr");
+ put("colgroup", "col");
+ put("select", "option");
+ put("optgroup", "option");
+ put("audio", "source");
+ put("video", "source");
+ put("object", "param");
+ put("map", "area");
+ }};
+
+ public XmlEmmetParser(List<ZenCodingToken> tokens,
+ CustomTemplateCallback callback,
+ ZenCodingGenerator generator) {
+ super(tokens, callback, generator);
+ PsiElement context = callback.getContext();
+ XmlTag parentTag = PsiTreeUtil.getParentOfType(context, XmlTag.class);
+ if (parentTag != null) {
+ hasTagContext = true;
+ tagLevel.push(parentTag.getName());
+ }
+ }
+
+ @NotNull
+ private static String getAttributeValueByToken(@Nullable ZenCodingToken token) {
+ if (token == null) {
+ return "";
+ }
+ if (token instanceof StringLiteralToken) {
+ final String text = ((StringLiteralToken)token).getText();
+ return text.substring(1, text.length() - 1);
+ }
+ else if (token instanceof IdentifierToken) {
+ return ((IdentifierToken)token).getText();
+ }
+ else if (token instanceof NumberToken) {
+ return Integer.toString(((NumberToken)token).getNumber());
+ }
+ else if (token == ZenCodingTokens.DOT || token == ZenCodingTokens.SHARP) {
+ return token.toString();
+ }
+ return "";
+ }
+
+ @Nullable
+ @Override
+ protected ZenCodingNode parseTemplate() {
+ ZenCodingToken token = getToken();
+ String templateKey = getDefaultTemplateKey();
+ boolean mustHaveSelector = true;
+
+ if (token instanceof IdentifierToken) {
+ templateKey = ((IdentifierToken)token).getText();
+ advance();
+ if (startsWithIgnoreCase(templateKey, LOREM_KEYWORD) || startsWithIgnoreCase(templateKey, LIPSUM_KEYWORD)) {
+ return parseLorem(templateKey);
+ }
+ mustHaveSelector = false;
+ }
+
+ if (templateKey == null) {
+ return null;
+ }
+
+ TemplateImpl template = myCallback.findApplicableTemplate(templateKey);
+ if (template == null && !ZenCodingUtil.isXML11ValidQName(templateKey)) {
+ return null;
+ }
+
+ final List<Pair<String, String>> attrList = parseSelectors();
+ if (mustHaveSelector && attrList.size() == 0) {
+ return null;
+ }
+
+ final TemplateToken templateToken = new TemplateToken(templateKey, attrList);
+ if (!setTemplate(templateToken, template)) {
+ return null;
+ }
+ return new TemplateNode(templateToken);
+ }
+
+ @Override
+ protected ZenCodingNode parseClimbUpOperation(@Nullable ZenCodingNode leftPart) {
+ popTagLevel();
+ return super.parseClimbUpOperation(leftPart);
+ }
+
+ @Override
+ protected ZenCodingNode parseMoreOperation(@NotNull ZenCodingNode leftPart) {
+ String parentTag = getParentTag(leftPart);
+ boolean hasParent = false;
+ if (!Strings.isNullOrEmpty(parentTag)) {
+ hasParent = true;
+ tagLevel.push(parentTag);
+ }
+ ZenCodingNode result = super.parseMoreOperation(leftPart);
+ if (result == null) {
+ return null;
+ }
+ if (hasParent) {
+ popTagLevel();
+ }
+ return result;
+ }
+
+ @Nullable
+ @Override
+ protected String getDefaultTemplateKey() {
+ return ZenCodingUtil.isHtml(myCallback) ? suggestTagName() : null;
+ }
+
+ @Nullable
+ private static String getParentTag(ZenCodingNode node) {
+ if (node instanceof TemplateNode) {
+ return ((TemplateNode)node).getTemplateToken().getKey();
+ }
+ else if (node instanceof MulOperationNode) {
+ ZenCodingNode leftOperand = ((MulOperationNode)node).getLeftOperand();
+ if (leftOperand instanceof TemplateNode) {
+ return ((TemplateNode)leftOperand).getTemplateToken().getKey();
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private ZenCodingNode parseLorem(String templateKey) {
+ Matcher matcher = LOREM_PATTERN.matcher(templateKey);
+ if (matcher.matches()) {
+ int loremWordsCount = DEFAULT_LOREM_LENGTH;
+ if (matcher.groupCount() > 1) {
+ String group = matcher.group(2);
+ loremWordsCount = group.isEmpty() ? DEFAULT_LOREM_LENGTH : Integer.parseInt(group);
+ }
+
+ final List<Pair<String, String>> attrList = parseSelectors();
+ ZenCodingToken token = getToken();
+ boolean isRepeating = token instanceof OperationToken && ((OperationToken)token).getSign() == '*';
+ if (!attrList.isEmpty() || isRepeating) {
+ String wrapTag = suggestTagName();
+ TemplateImpl template = myCallback.findApplicableTemplate(templateKey);
+ if (template == null && !ZenCodingUtil.isXML11ValidQName(templateKey)) {
+ return null;
+ }
+ final TemplateToken templateToken = new TemplateToken(wrapTag, attrList);
+ if (!setTemplate(templateToken, template)) {
+ return null;
+ }
+ return new MoreOperationNode(new TemplateNode(templateToken), new LoremNode(loremWordsCount));
+ }
+ else {
+ return new LoremNode(loremWordsCount);
+ }
+ }
+ else {
+ return null;
+ }
+ }
+
+ private String suggestTagName() {
+ if (!tagLevel.empty()) {
+ String parentTag = tagLevel.peek();
+ if (parentChildTagMapping.containsKey(parentTag)) {
+ return parentChildTagMapping.get(parentTag);
+ }
+ if (HtmlUtil.isPossiblyInlineTag(parentTag)) {
+ return DEFAULT_INLINE_TAG;
+ }
+ }
+ return DEFAULT_TAG;
+ }
+
+ private void popTagLevel() {
+ if (tagLevel.size() > (hasTagContext ? 1 : 0)) {
+ tagLevel.pop();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @NotNull
+ private List<Pair<String, String>> parseSelectors() {
+ final List<Pair<String, String>> result = new ArrayList<Pair<String, String>>();
+
+ int classAttrPosition = -1;
+ int idAttrPosition = -1;
+
+ final StringBuilder classAttrBuilder = new StringBuilder();
+ final StringBuilder idAttrBuilder = new StringBuilder();
+
+ while (true) {
+ final List<Pair<String, String>> attrList = parseSelector();
+ if (attrList == null) {
+ if (classAttrPosition != -1) {
+ result.set(classAttrPosition, new Pair<String, String>(CLASS, classAttrBuilder.toString()));
+ }
+ if (idAttrPosition != -1) {
+ result.set(idAttrPosition, new Pair<String, String>(ID, idAttrBuilder.toString()));
+ }
+ return result;
+ }
+
+ for (Pair<String, String> attr : attrList) {
+ if (CLASS.equals(attr.first)) {
+ if (classAttrBuilder.length() > 0) {
+ classAttrBuilder.append(' ');
+ }
+ classAttrBuilder.append(attr.second);
+ if (classAttrPosition == -1) {
+ classAttrPosition = result.size();
+ result.add(attr);
+ }
+ }
+ else if (ID.equals(attr.first)) {
+ if (idAttrBuilder.length() > 0) {
+ idAttrBuilder.append(' ');
+ }
+ idAttrBuilder.append(attr.second);
+ if (idAttrPosition == -1) {
+ idAttrPosition = result.size();
+ result.add(attr);
+ }
+ }
+ else {
+ result.add(attr);
+ }
+ }
+ }
+ }
+
+ @Nullable
+ private List<Pair<String, String>> parseSelector() {
+ ZenCodingToken token = getToken();
+ if (token == ZenCodingTokens.OPENING_SQ_BRACKET) {
+ advance();
+ final List<Pair<String, String>> attrList = parseAttributeList();
+ if (attrList == null || getToken() != ZenCodingTokens.CLOSING_SQ_BRACKET) {
+ return null;
+ }
+ advance();
+ return attrList;
+ }
+
+ if (token == ZenCodingTokens.DOT || token == ZenCodingTokens.SHARP) {
+ final String name = token == ZenCodingTokens.DOT ? CLASS : ID;
+ advance();
+ token = getToken();
+ final String value = getAttributeValueByToken(token);
+ if (!value.isEmpty()) {
+ advance();
+ }
+ return Collections.singletonList(new Pair<String, String>(name, value));
+ }
+
+ return null;
+ }
+
+ @Nullable
+ private List<Pair<String, String>> parseAttributeList() {
+ final List<Pair<String, String>> result = new ArrayList<Pair<String, String>>();
+ while (true) {
+ final Pair<String, String> attribute = parseAttribute();
+ if (attribute == null) {
+ return result;
+ }
+ result.add(attribute);
+
+ final ZenCodingToken token = getToken();
+ if (token != ZenCodingTokens.COMMA && token != ZenCodingTokens.SPACE) {
+ return result;
+ }
+ advance();
+ }
+ }
+
+ @Nullable
+ private Pair<String, String> parseAttribute() {
+ ZenCodingToken token = getToken();
+ if (!(token instanceof IdentifierToken)) {
+ return null;
+ }
+
+ final String name = ((IdentifierToken)token).getText();
+
+ advance();
+ token = getToken();
+ if (token != ZenCodingTokens.EQ) {
+ return new Pair<String, String>(name, "");
+ }
+
+ advance();
+ final StringBuilder attrValueBuilder = new StringBuilder();
+ String value;
+ do {
+ token = getToken();
+ value = getAttributeValueByToken(token);
+ attrValueBuilder.append(value);
+ if (token != null && token != ZenCodingTokens.CLOSING_SQ_BRACKET
+ && token != ZenCodingTokens.SPACE && token != ZenCodingTokens.COMMA) {
+ advance();
+ }
+ }
+ while (token != null && token != ZenCodingTokens.CLOSING_SQ_BRACKET
+ && token != ZenCodingTokens.SPACE && token != ZenCodingTokens.COMMA);
+ return new Pair<String, String>(name, attrValueBuilder.toString());
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingTemplate.java
new file mode 100644
index 0000000..f98078d
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingTemplate.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2000-2010 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.codeInsight.template.emmet;
+
+import com.intellij.application.options.editor.WebEditorOptions;
+import com.intellij.codeInsight.CodeInsightBundle;
+import com.intellij.codeInsight.template.*;
+import com.intellij.codeInsight.template.emmet.filters.SingleLineEmmetFilter;
+import com.intellij.codeInsight.template.emmet.filters.ZenCodingFilter;
+import com.intellij.codeInsight.template.emmet.generators.XmlZenCodingGenerator;
+import com.intellij.codeInsight.template.emmet.generators.ZenCodingGenerator;
+import com.intellij.codeInsight.template.emmet.nodes.*;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
+import com.intellij.codeInsight.template.emmet.tokens.TextToken;
+import com.intellij.codeInsight.template.emmet.tokens.ZenCodingToken;
+import com.intellij.codeInsight.template.impl.TemplateImpl;
+import com.intellij.codeInsight.template.impl.TemplateState;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.command.undo.UndoConstants;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorModificationUtil;
+import com.intellij.openapi.editor.ScrollType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.ui.InputValidatorEx;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.xml.XmlAttribute;
+import com.intellij.psi.xml.XmlDocument;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.LocalTimeCounter;
+import com.intellij.xml.XmlBundle;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class ZenCodingTemplate implements CustomLiveTemplate {
+ public static final char MARKER = '\0';
+ public static final String ATTRS = "ATTRS";
+
+ private static void addMissingAttributes(XmlTag tag, List<Pair<String, String>> value) {
+ List<Pair<String, String>> attr2value = new ArrayList<Pair<String, String>>(value);
+ for (Iterator<Pair<String, String>> iterator = attr2value.iterator(); iterator.hasNext(); ) {
+ Pair<String, String> pair = iterator.next();
+ if (tag.getAttribute(pair.first) != null) {
+ iterator.remove();
+ }
+ }
+ addAttributesBefore(tag, attr2value);
+ }
+
+ private static void addAttributesBefore(XmlTag tag, List<Pair<String, String>> attr2value) {
+ XmlAttribute firstAttribute = ArrayUtil.getFirstElement(tag.getAttributes());
+ XmlElementFactory factory = XmlElementFactory.getInstance(tag.getProject());
+ for (Pair<String, String> pair : attr2value) {
+ XmlAttribute xmlAttribute = factory.createXmlAttribute(pair.first, "");
+ if (firstAttribute != null) {
+ tag.addBefore(xmlAttribute, firstAttribute);
+ }
+ else {
+ tag.add(xmlAttribute);
+ }
+ }
+ }
+
+ @Nullable
+ public static ZenCodingGenerator findApplicableDefaultGenerator(@NotNull PsiElement context, boolean wrapping) {
+ for (ZenCodingGenerator generator : ZenCodingGenerator.getInstances()) {
+ if (generator.isMyContext(context, wrapping) && generator.isAppliedByDefault(context)) {
+ return generator;
+ }
+ }
+ return null;
+ }
+
+ @NotNull
+ private static XmlFile parseXmlFileInTemplate(String templateString, CustomTemplateCallback callback, boolean createPhysicalFile) {
+ XmlFile xmlFile = (XmlFile)PsiFileFactory.getInstance(callback.getProject())
+ .createFileFromText("dummy.xml", StdFileTypes.XML, templateString, LocalTimeCounter.currentTime(), createPhysicalFile);
+ VirtualFile vFile = xmlFile.getVirtualFile();
+ if (vFile != null) {
+ vFile.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE);
+ }
+ return xmlFile;
+ }
+
+ @Nullable
+ private static ZenCodingNode parse(@NotNull String text, @NotNull CustomTemplateCallback callback, @NotNull ZenCodingGenerator generator) {
+ List<ZenCodingToken> tokens = new EmmetLexer().lex(text);
+ if (tokens == null) {
+ return null;
+ }
+ if (!validate(tokens, generator)) {
+ return null;
+ }
+ EmmetParser parser = generator.createParser(tokens, callback, generator);
+ ZenCodingNode node = parser.parse();
+ if (parser.getIndex() != tokens.size() || node instanceof TextNode) {
+ return null;
+ }
+ return node;
+ }
+
+ private static boolean validate(@NotNull List<ZenCodingToken> tokens, @NotNull ZenCodingGenerator generator) {
+ for (ZenCodingToken token : tokens) {
+ if (token instanceof TextToken && !(generator instanceof XmlZenCodingGenerator)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean checkTemplateKey(@NotNull String key, CustomTemplateCallback callback, @NotNull ZenCodingGenerator generator) {
+ return parse(key, callback, generator) != null;
+ }
+
+ public void expand(String key, @NotNull CustomTemplateCallback callback) {
+ ZenCodingGenerator defaultGenerator = findApplicableDefaultGenerator(callback.getContext(), false);
+ assert defaultGenerator != null;
+ expand(key, callback, null, defaultGenerator);
+ }
+
+ @Nullable
+ private static ZenCodingGenerator findApplicableGenerator(ZenCodingNode node, PsiElement context, boolean wrapping) {
+ ZenCodingGenerator defaultGenerator = null;
+ List<ZenCodingGenerator> generators = ZenCodingGenerator.getInstances();
+ for (ZenCodingGenerator generator : generators) {
+ if (defaultGenerator == null && generator.isMyContext(context, wrapping) && generator.isAppliedByDefault(context)) {
+ defaultGenerator = generator;
+ }
+ }
+ while (node instanceof FilterNode) {
+ FilterNode filterNode = (FilterNode)node;
+ String suffix = filterNode.getFilter();
+ for (ZenCodingGenerator generator : generators) {
+ if (generator.isMyContext(context, wrapping)) {
+ if (suffix != null && suffix.equals(generator.getSuffix())) {
+ return generator;
+ }
+ }
+ }
+ node = filterNode.getNode();
+ }
+ return defaultGenerator;
+ }
+
+ private static List<ZenCodingFilter> getFilters(ZenCodingNode node, PsiElement context) {
+ List<ZenCodingFilter> result = new ArrayList<ZenCodingFilter>();
+
+ while (node instanceof FilterNode) {
+ FilterNode filterNode = (FilterNode)node;
+ String filterSuffix = filterNode.getFilter();
+ boolean filterFound = false;
+ for (ZenCodingFilter filter : ZenCodingFilter.getInstances()) {
+ if (filter.isMyContext(context) && filter.getSuffix().equals(filterSuffix)) {
+ filterFound = true;
+ result.add(filter);
+ }
+ }
+ assert filterFound;
+ node = filterNode.getNode();
+ }
+
+ for (ZenCodingFilter filter : ZenCodingFilter.getInstances()) {
+ if (filter.isMyContext(context) && filter.isAppliedByDefault(context)) {
+ result.add(filter);
+ }
+ }
+
+ Collections.reverse(result);
+ return result;
+ }
+
+
+ private static void expand(String key,
+ @NotNull CustomTemplateCallback callback,
+ String surroundedText,
+ @NotNull ZenCodingGenerator defaultGenerator) {
+ ZenCodingNode node = parse(key, callback, defaultGenerator);
+ assert node != null;
+ if (surroundedText == null) {
+ if (node instanceof TemplateNode) {
+ if (key.equals(((TemplateNode)node).getTemplateToken().getKey()) &&
+ callback.findApplicableTemplates(key).size() > 1) {
+ callback.startTemplate();
+ return;
+ }
+ }
+ callback.deleteTemplateKey(key);
+ }
+
+ PsiElement context = callback.getContext();
+ ZenCodingGenerator generator = findApplicableGenerator(node, context, false);
+ List<ZenCodingFilter> filters = getFilters(node, context);
+
+ expand(node, generator, filters, surroundedText, callback);
+ }
+
+ private static void expand(ZenCodingNode node,
+ ZenCodingGenerator generator,
+ List<ZenCodingFilter> filters,
+ String surroundedText,
+ CustomTemplateCallback callback) {
+ if (surroundedText != null) {
+ surroundedText = surroundedText.trim();
+ }
+
+ GenerationNode fakeParentNode = new GenerationNode(TemplateToken.EMPTY_TEMPLATE_TOKEN, -1, 1, surroundedText, true, null);
+ node.expand(-1, 1, surroundedText, callback, true, fakeParentNode);
+
+ List<GenerationNode> genNodes = fakeParentNode.getChildren();
+ LiveTemplateBuilder builder = new LiveTemplateBuilder();
+ int end = -1;
+ for (int i = 0, genNodesSize = genNodes.size(); i < genNodesSize; i++) {
+ GenerationNode genNode = genNodes.get(i);
+ TemplateImpl template = genNode.generate(callback, generator, filters, true);
+ int e = builder.insertTemplate(builder.length(), template, null);
+ if (end == -1 && end < builder.length()) {
+ end = e;
+ }
+ }
+ for (ZenCodingFilter filter : filters) {
+ if(filter instanceof SingleLineEmmetFilter) {
+ builder.setIsToReformat(false);
+ break;
+ }
+ }
+
+ callback.startTemplate(builder.buildTemplate(), null, new TemplateEditingAdapter() {
+ private TextRange myEndVarRange;
+ private Editor myEditor;
+
+ @Override
+ public void beforeTemplateFinished(TemplateState state, Template template) {
+ int variableNumber = state.getCurrentVariableNumber();
+ if (variableNumber >= 0 && template instanceof TemplateImpl) {
+ TemplateImpl t = (TemplateImpl)template;
+ while (variableNumber < t.getVariableCount()) {
+ String varName = t.getVariableNameAt(variableNumber);
+ if (LiveTemplateBuilder.isEndVariable(varName)) {
+ myEndVarRange = state.getVariableRange(varName);
+ myEditor = state.getEditor();
+ break;
+ }
+ variableNumber++;
+ }
+ }
+ }
+
+ @Override
+ public void templateFinished(Template template, boolean brokenOff) {
+ if (brokenOff && myEndVarRange != null && myEditor != null) {
+ int offset = myEndVarRange.getStartOffset();
+ if (offset >= 0 && offset != myEditor.getCaretModel().getOffset()) {
+ myEditor.getCaretModel().moveToOffset(offset);
+ myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
+ }
+ }
+ }
+ });
+ }
+
+ public void wrap(final String selection,
+ @NotNull final CustomTemplateCallback callback
+ ) {
+ InputValidatorEx validator = new InputValidatorEx() {
+ public String getErrorText(String inputString) {
+ if (!checkTemplateKey(inputString, callback)) {
+ return XmlBundle.message("zen.coding.incorrect.abbreviation.error");
+ }
+ return null;
+ }
+
+ public boolean checkInput(String inputString) {
+ return getErrorText(inputString) == null;
+ }
+
+ public boolean canClose(String inputString) {
+ return checkInput(inputString);
+ }
+ };
+ final String abbreviation = Messages
+ .showInputDialog(callback.getProject(), XmlBundle.message("zen.coding.enter.abbreviation.dialog.label"),
+ XmlBundle.message("zen.coding.title"), Messages.getQuestionIcon(), "", validator);
+ if (abbreviation != null) {
+ doWrap(selection, abbreviation, callback);
+ }
+ }
+
+ public static boolean checkTemplateKey(String inputString, CustomTemplateCallback callback) {
+ ZenCodingGenerator generator = findApplicableDefaultGenerator(callback.getContext(), true);
+ assert generator != null;
+ return checkTemplateKey(inputString, callback, generator);
+ }
+
+ public boolean isApplicable(PsiFile file, int offset, boolean wrapping) {
+ WebEditorOptions webEditorOptions = WebEditorOptions.getInstance();
+ if (!webEditorOptions.isZenCodingEnabled()) {
+ return false;
+ }
+ if (file == null) {
+ return false;
+ }
+ PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments();
+ PsiElement element = CustomTemplateCallback.getContext(file, offset);
+ return findApplicableDefaultGenerator(element, wrapping) != null;
+ }
+
+ public static void doWrap(final String selection,
+ final String abbreviation,
+ final CustomTemplateCallback callback) {
+ final ZenCodingGenerator defaultGenerator = findApplicableDefaultGenerator(callback.getContext(), true);
+ assert defaultGenerator != null;
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ CommandProcessor.getInstance().executeCommand(callback.getProject(), new Runnable() {
+ public void run() {
+ callback.fixInitialState(true);
+ ZenCodingNode node = parse(abbreviation, callback, defaultGenerator);
+ assert node != null;
+ PsiElement context = callback.getContext();
+ ZenCodingGenerator generator = findApplicableGenerator(node, context, true);
+ List<ZenCodingFilter> filters = getFilters(node, context);
+
+ EditorModificationUtil.deleteSelectedText(callback.getEditor());
+ PsiDocumentManager.getInstance(callback.getProject()).commitAllDocuments();
+
+ expand(node, generator, filters, selection, callback);
+ }
+ }, CodeInsightBundle.message("insert.code.template.command"), null);
+ }
+ });
+ }
+
+ @NotNull
+ public String getTitle() {
+ return XmlBundle.message("zen.coding.title");
+ }
+
+ public char getShortcut() {
+ return (char)WebEditorOptions.getInstance().getZenCodingExpandShortcut();
+ }
+
+ protected static boolean containsAttrsVar(TemplateImpl template) {
+ for (int i = 0; i < template.getVariableCount(); i++) {
+ String varName = template.getVariableNameAt(i);
+ if (ATTRS.equals(varName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String computeTemplateKey(@NotNull CustomTemplateCallback callback) {
+ ZenCodingGenerator generator = findApplicableDefaultGenerator(callback.getContext(), false);
+ if (generator == null) return null;
+ return generator.computeTemplateKey(callback);
+ }
+
+ public boolean supportsWrapping() {
+ return true;
+ }
+
+ public static boolean doSetTemplate(final TemplateToken token, TemplateImpl template, CustomTemplateCallback callback) {
+ token.setTemplate(template);
+ final XmlFile xmlFile = parseXmlFileInTemplate(template.getString(), callback, true);
+ token.setFile(xmlFile);
+ XmlDocument document = xmlFile.getDocument();
+ final XmlTag tag = document != null ? document.getRootTag() : null;
+ if (token.getAttribute2Value().size() > 0 && tag == null) {
+ return false;
+ }
+ if (tag != null) {
+ if (!containsAttrsVar(template) && token.getAttribute2Value().size() > 0) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ addMissingAttributes(tag, token.getAttribute2Value());
+ }
+ });
+ }
+ }
+ return true;
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingUtil.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingUtil.java
new file mode 100644
index 0000000..9b81e47
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/ZenCodingUtil.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2000-2010 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.codeInsight.template.emmet;
+
+import com.intellij.codeInsight.template.CustomTemplateCallback;
+import com.intellij.codeInsight.template.emmet.filters.ZenCodingFilter;
+import com.intellij.codeInsight.template.emmet.generators.ZenCodingGenerator;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import org.apache.xerces.util.XML11Char;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class ZenCodingUtil {
+ private static final char NUMBER_IN_ITERATION_PLACE_HOLDER = '$';
+ private static final String SURROUNDED_TEXT_MARKER = "$#";
+
+ private ZenCodingUtil() {
+ }
+
+ public static boolean containsSurroundedTextMarker(@NotNull String s) {
+ return s.contains(SURROUNDED_TEXT_MARKER);
+ }
+
+ public static String replaceMarkers(String s, int numberInIteration, int totalIterations, @Nullable String surroundedText) {
+ String by = Integer.toString(numberInIteration + 1);
+ StringBuilder builder = new StringBuilder(s.length());
+ int markerStartIndex = -1;
+ int i = 0;
+ int n = s.length();
+ while (i <= n) {
+ char c = i < n ? s.charAt(i) : 0;
+ if (c == NUMBER_IN_ITERATION_PLACE_HOLDER && (i == n - 1 || s.charAt(i + 1) != '#')) {
+ if (markerStartIndex == -1) {
+ markerStartIndex = i;
+ }
+ }
+ else {
+ int markersCount = i - markerStartIndex;
+ if (markerStartIndex != -1) {
+ // counter base
+ boolean decrement = false;
+ if(i < n && s.charAt(i) == '@') {
+ i++;
+ if (i < n && s.charAt(i) == '-') {
+ decrement = true;
+ i++;
+ }
+ StringBuilder base = new StringBuilder();
+ while (i < n && Character.isDigit(s.charAt(i))) {
+ base.append(s.charAt(i));
+ i++;
+ }
+ int baseInt = parseNonNegativeInt(base.toString()) - 1;
+ baseInt = baseInt >= 0 ? baseInt : 0;
+ if(baseInt >= 0) {
+ int byInt = decrement
+ ? totalIterations - numberInIteration
+ : numberInIteration + 1;
+ byInt += baseInt;
+ by = Integer.toString(byInt);
+ }
+ }
+ for (int k = 0, m = markersCount - by.length(); k < m; k++) {
+ builder.append('0');
+ }
+ builder.append(by);
+ markerStartIndex = -1;
+ c = i < n ? s.charAt(i) : 0;
+ }
+ if (i < n) {
+ if (c == NUMBER_IN_ITERATION_PLACE_HOLDER && surroundedText != null) {
+ builder.append(surroundedText);
+ i++;
+ }
+ else {
+ builder.append(c);
+ }
+ }
+ }
+ i++;
+ }
+ return builder.toString();
+ }
+
+ public static String getValue(String value, int numberInIteration, int totalIterations, String surroundedText) {
+ String s = replaceMarkers(value, numberInIteration, totalIterations, surroundedText);
+ return s.replace("\"", """);
+ }
+
+ public static int parseNonNegativeInt(@NotNull String s) {
+ try {
+ return Integer.parseInt(s);
+ }
+ catch (Throwable ignored) {
+ }
+ return -1;
+ }
+
+ public static boolean isXML11ValidQName(String str) {
+ final int colon = str.indexOf(':');
+ if (colon == 0 || colon == str.length() - 1) {
+ return false;
+ }
+ if (colon > 0) {
+ final String prefix = str.substring(0, colon);
+ final String localPart = str.substring(colon + 1);
+ return XML11Char.isXML11ValidNCName(prefix) && XML11Char.isXML11ValidNCName(localPart);
+ }
+ return XML11Char.isXML11ValidNCName(str);
+ }
+
+ public static boolean isHtml(CustomTemplateCallback callback) {
+ FileType type = callback.getFileType();
+ return type == StdFileTypes.HTML || type == StdFileTypes.XHTML;
+ }
+
+ public static boolean checkFilterSuffix(@NotNull String suffix) {
+ for (ZenCodingGenerator generator : ZenCodingGenerator.getInstances()) {
+ if (suffix.equals(generator.getSuffix())) {
+ return true;
+ }
+ }
+ for (ZenCodingFilter filter : ZenCodingFilter.getInstances()) {
+ if (suffix.equals(filter.getSuffix())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/BemEmmetFilter.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/BemEmmetFilter.java
new file mode 100644
index 0000000..f226864
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/BemEmmetFilter.java
@@ -0,0 +1,328 @@
+/*
+ * 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 com.intellij.codeInsight.template.emmet.filters;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import com.intellij.codeInsight.template.emmet.nodes.GenerationNode;
+import com.intellij.lang.xml.XMLLanguage;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static com.google.common.base.Strings.nullToEmpty;
+import static com.google.common.collect.Iterables.*;
+import static com.google.common.collect.Lists.newLinkedList;
+
+/**
+ * User: zolotov
+ * Date: 2/4/13
+ * <p/>
+ * Bem filter for emmet support.
+ * See the original source code here: https://github.com/emmetio/emmet/blob/master/javascript/filters/bem.js
+ * And documentation here: http://docs.emmet.io/filters/bem/
+ */
+public class BemEmmetFilter extends ZenCodingFilter {
+ private static final Key<BemState> BEM_STATE = Key.create("BEM_STATE");
+
+ private static final String ELEMENT_SEPARATOR = "__";
+ private static final String MODIFIER_SEPARATOR = "_";
+ private static final String SHORT_ELEMENT_PREFIX = "-";
+
+ private static final Splitter ELEMENTS_SPLITTER = Splitter.on(ELEMENT_SEPARATOR);
+ private static final Splitter MODIFIERS_SPLITTER = Splitter.on(MODIFIER_SEPARATOR).limit(2);
+ private static final Splitter CLASS_NAME_SPLITTER = Splitter.on(' ').trimResults().omitEmptyStrings();
+ private static final Joiner CLASS_NAME_JOINER = Joiner.on(' ');
+
+ private static final Function<String, String> CLASS_NAME_NORMALIZER = new Function<String, String>() {
+ @Override
+ public String apply(@NotNull String input) {
+ if(!input.startsWith(SHORT_ELEMENT_PREFIX)) {
+ return input;
+ }
+
+ StringBuilder result = new StringBuilder();
+ while (input.startsWith(SHORT_ELEMENT_PREFIX)) {
+ input = input.substring(SHORT_ELEMENT_PREFIX.length());
+ result.append(ELEMENT_SEPARATOR);
+ }
+ return result.append(input).toString();
+ }
+ };
+
+ private static final Predicate<String> BLOCK_NAME_PREDICATE = new Predicate<String>() {
+ @Override
+ public boolean apply(String className) {
+ return Pattern.compile("^[A-z]-").matcher(className).matches();
+ }
+ };
+ private static final Predicate<String> STARTS_WITH_LETTER = new Predicate<String>() {
+ @Override
+ public boolean apply(@Nullable String input) {
+ return input != null && input.length() > 0 && Character.isLetter(input.charAt(0));
+ }
+ };
+
+ @NotNull
+ @Override
+ public String getSuffix() {
+ return "bem";
+ }
+
+ @Override
+ public boolean isAppliedByDefault(@NotNull PsiElement context) {
+ return false; //todo: add setting for enabling this filter by default
+ }
+
+ @Override
+ public boolean isMyContext(@NotNull PsiElement context) {
+ return context.getLanguage() instanceof XMLLanguage;
+ }
+
+ @NotNull
+ @Override
+ public GenerationNode filterNode(@NotNull final GenerationNode node) {
+ final List<Pair<String, String>> attribute2Value = node.getTemplateToken().getAttribute2Value();
+ Pair<String, String> classNamePair = getClassPair(attribute2Value);
+ if (classNamePair != null) {
+ Iterable<String> classNames = extractClasses(classNamePair.second);
+ BEM_STATE.set(node, new BemState(suggestBlockName(classNames), null, null));
+ final Set<String> newClassNames = ImmutableSet.copyOf(concat(transform(classNames, new Function<String, Iterable<String>>() {
+ @Override
+ public Iterable<String> apply(String className) {
+ return processClassName(className, node);
+ }
+ })));
+ attribute2Value.add(Pair.create("class", CLASS_NAME_JOINER.join(newClassNames)));
+ }
+ return node;
+ }
+
+ private static Iterable<String> processClassName(String className, GenerationNode node) {
+ className = fillWithBemElements(className, node);
+ className = fillWithBemModifiers(className, node);
+
+ BemState nodeBemState = BEM_STATE.get(node);
+ BemState bemState = extractBemStateFromClassName(className);
+ List<String> result = newLinkedList();
+ if (!bemState.isEmpty()) {
+ String block = bemState.getBlock();
+ if (isNullOrEmpty(block)) {
+ block = nullToEmpty(nodeBemState != null ? nodeBemState.getBlock() : null);
+ bemState.setBlock(block);
+ }
+ String prefix = block;
+ String element = bemState.getElement();
+ if (!isNullOrEmpty(element)) {
+ prefix += ELEMENT_SEPARATOR + element;
+ }
+ result.add(prefix);
+ String modifier = bemState.getModifier();
+ if (!isNullOrEmpty(modifier)) {
+ result.add(prefix + MODIFIER_SEPARATOR + modifier);
+ }
+ BEM_STATE.set(node, bemState.copy());
+ }
+ else {
+ result.add(className);
+ }
+ return result;
+ }
+
+ @NotNull
+ private static BemState extractBemStateFromClassName(@NotNull String className) {
+ BemState result = new BemState();
+ if (className.contains(ELEMENT_SEPARATOR)) {
+ List<String> blockElements = newLinkedList(ELEMENTS_SPLITTER.split(className));
+ result.setBlock(getFirst(blockElements, ""));
+ if (blockElements.size() > 1) {
+ List<String> elementModifiers = newLinkedList(MODIFIERS_SPLITTER.split(blockElements.get(1)));
+ result.setElement(getFirst(elementModifiers, ""));
+ if (elementModifiers.size() > 1) {
+ result.setModifier(getLast(elementModifiers, ""));
+ }
+ }
+ }
+ else if (className.contains(MODIFIER_SEPARATOR)) {
+ Iterable<String> blockModifiers = MODIFIERS_SPLITTER.split(className);
+ result.setBlock(getFirst(blockModifiers, ""));
+ result.setModifier(getLast(blockModifiers, ""));
+ }
+ return result;
+ }
+
+ @NotNull
+ private static String fillWithBemElements(@NotNull String className, @NotNull GenerationNode node) {
+ return transformClassNameToBemFormat(className, ELEMENT_SEPARATOR, node);
+ }
+
+ @NotNull
+ private static String fillWithBemModifiers(@NotNull String className, @NotNull GenerationNode node) {
+ return transformClassNameToBemFormat(className, MODIFIER_SEPARATOR, node);
+ }
+
+ /**
+ * Adduction className to BEM format according to tags structure.
+ *
+ * @param className
+ * @param separator handling separator
+ * @param node current node
+ * @return class name in BEM format
+ */
+ @NotNull
+ private static String transformClassNameToBemFormat(@NotNull String className, @NotNull String separator, @NotNull GenerationNode node) {
+ Pair<String, Integer> cleanStringAndDepth = getCleanStringAndDepth(className, separator);
+ Integer depth = cleanStringAndDepth.second;
+ if (depth > 0) {
+ GenerationNode donor = node;
+ while (donor.getParent() != null && depth > 0) {
+ donor = donor.getParent();
+ depth--;
+ }
+
+ BemState bemState = BEM_STATE.get(donor);
+ if (bemState != null) {
+ String prefix = bemState.getBlock();
+ if (!isNullOrEmpty(prefix)) {
+ String element = bemState.getElement();
+ if (MODIFIER_SEPARATOR.equals(separator) && !isNullOrEmpty(element)) {
+ prefix = prefix + separator + element;
+ }
+ return prefix + separator + cleanStringAndDepth.first;
+ }
+ }
+ }
+ return className;
+ }
+
+ /**
+ * Counts separators at the start of className and retrieve className without these separators.
+ *
+ * @param name
+ * @param separator
+ * @return pair like <name_without_separator_at_the_start, count_of_separators_at_the_start_of_string>
+ */
+ @NotNull
+ private static Pair<String, Integer> getCleanStringAndDepth(@NotNull String name, @NotNull String separator) {
+ int result = 0;
+ while (name.startsWith(separator)) {
+ result++;
+ name = name.substring(separator.length());
+ }
+ return Pair.create(name, result);
+ }
+
+ /**
+ * Extract all normalized class names from class attribute value
+ *
+ * @param classAttributeValue
+ * @return collection of normalized class names
+ */
+ @NotNull
+ private static Iterable<String> extractClasses(String classAttributeValue) {
+ return transform(CLASS_NAME_SPLITTER.split(classAttributeValue), CLASS_NAME_NORMALIZER);
+ }
+
+ /**
+ * Suggest block name by class names.
+ * Returns first class started with pattern [a-z]-
+ * or first class started with letter.
+ *
+ * @param classNames
+ * @return suggested block name for given classes. Empty string if name can't be suggested.
+ */
+ @NotNull
+ private static String suggestBlockName(Iterable<String> classNames) {
+ return find(classNames, BLOCK_NAME_PREDICATE, find(classNames, STARTS_WITH_LETTER, ""));
+ }
+
+ /**
+ * Retrieve pair "class" => classAttributeValue from node attributeList
+ *
+ * @param attribute2Value node's attributes
+ * @return pointer to pair
+ */
+ @Nullable
+ private static Pair<String, String> getClassPair(@NotNull List<Pair<String, String>> attribute2Value) {
+ for (int i = 0; i < attribute2Value.size(); i++) {
+ Pair<String, String> pair = attribute2Value.get(i);
+ if ("class".equals(pair.first) && !isNullOrEmpty(pair.second)) {
+ return attribute2Value.remove(i);
+ }
+ }
+ return null;
+ }
+
+ private static class BemState {
+ @Nullable private String block;
+ @Nullable private String element;
+ @Nullable private String modifier;
+
+ private BemState() {
+ }
+
+ private BemState(@Nullable String block, @Nullable String element, @Nullable String modifier) {
+ this.block = block;
+ this.element = element;
+ this.modifier = modifier;
+ }
+
+ public void setModifier(@Nullable String modifier) {
+ this.modifier = modifier;
+ }
+
+ public void setElement(@Nullable String element) {
+ this.element = element;
+ }
+
+ public void setBlock(@Nullable String block) {
+ this.block = block;
+ }
+
+ @Nullable
+ public String getBlock() {
+ return block;
+ }
+
+ @Nullable
+ public String getElement() {
+ return element;
+ }
+
+ @Nullable
+ public String getModifier() {
+ return modifier;
+ }
+
+ public boolean isEmpty() {
+ return isNullOrEmpty(block) && isNullOrEmpty(element) && isNullOrEmpty(modifier);
+ }
+
+ public BemState copy() {
+ return new BemState(block, element, modifier);
+ }
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/CommentZenCodingFilter.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/CommentZenCodingFilter.java
similarity index 83%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/CommentZenCodingFilter.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/filters/CommentZenCodingFilter.java
index 612487f..82b6235 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/CommentZenCodingFilter.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/CommentZenCodingFilter.java
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.filters;
+package com.intellij.codeInsight.template.emmet.filters;
-import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlDocument;
@@ -23,16 +23,18 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import static com.google.common.base.Strings.isNullOrEmpty;
+
/**
* @author Eugene.Kudelevsky
*/
public class CommentZenCodingFilter extends ZenCodingFilter {
private static String buildCommentString(@Nullable String classAttr, @Nullable String idAttr) {
StringBuilder builder = new StringBuilder();
- if (idAttr != null) {
+ if (!isNullOrEmpty(idAttr)) {
builder.append('#').append(idAttr);
}
- if (classAttr != null) {
+ if (!isNullOrEmpty(classAttr)) {
builder.append('.').append(classAttr);
}
return builder.toString();
@@ -47,9 +49,9 @@
if (tag != null) {
String classAttr = tag.getAttributeValue("class");
String idAttr = tag.getAttributeValue("id");
- if (classAttr != null || idAttr != null) {
+ if (!isNullOrEmpty(classAttr) || !isNullOrEmpty(idAttr)) {
String commentString = buildCommentString(classAttr, idAttr);
- return "<!-- " + commentString + " -->\n" + text + "\n<!-- /" + commentString + " -->";
+ return text + "\n<!-- /" + commentString + " -->";
}
}
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/EscapeZenCodingFilter.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/EscapeZenCodingFilter.java
similarity index 90%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/EscapeZenCodingFilter.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/filters/EscapeZenCodingFilter.java
index 8c3abe8..3249b5a 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/EscapeZenCodingFilter.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/EscapeZenCodingFilter.java
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.filters;
+package com.intellij.codeInsight.template.emmet.filters;
-import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/SingleLineEmmetFilter.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/SingleLineEmmetFilter.java
new file mode 100644
index 0000000..c355644
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/SingleLineEmmetFilter.java
@@ -0,0 +1,52 @@
+/*
+ * 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 com.intellij.codeInsight.template.emmet.filters;
+
+import com.intellij.codeInsight.template.emmet.nodes.GenerationNode;
+import com.intellij.codeInsight.template.impl.TemplateImpl;
+import com.intellij.lang.xml.XMLLanguage;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: zolotov
+ * Date: 2/4/13
+ */
+public class SingleLineEmmetFilter extends ZenCodingFilter {
+ @NotNull
+ @Override
+ public String getSuffix() {
+ return "s";
+ }
+
+ @NotNull
+ @Override
+ public GenerationNode filterNode(@NotNull GenerationNode node) {
+ TemplateImpl template = node.getTemplateToken().getTemplate();
+ if (template != null) {
+ template.setToReformat(false);
+ }
+ for (GenerationNode generationNode : node.getChildren()) {
+ filterNode(generationNode);
+ }
+ return node;
+ }
+
+ @Override
+ public boolean isMyContext(@NotNull PsiElement context) {
+ return context.getLanguage() instanceof XMLLanguage;
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/TrimZenCodingFilter.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/TrimZenCodingFilter.java
new file mode 100644
index 0000000..1c63692
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/TrimZenCodingFilter.java
@@ -0,0 +1,74 @@
+package com.intellij.codeInsight.template.emmet.filters;
+
+import com.intellij.codeInsight.template.emmet.nodes.GenerationNode;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
+import com.intellij.lang.xml.XMLLanguage;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.XmlElementVisitor;
+import com.intellij.psi.xml.XmlDocument;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.psi.xml.XmlTagValue;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.regex.Pattern;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class TrimZenCodingFilter extends ZenCodingFilter {
+ private static final Pattern PATTERN = Pattern.compile("^([\\s|\u00a0])?[\\d|#|\\-|\\*|\u2022]+\\.?\\s*");
+
+ @NotNull
+ @Override
+ public String getSuffix() {
+ return "t";
+ }
+
+ @Override
+ public boolean isMyContext(@NotNull PsiElement context) {
+ return context.getLanguage() instanceof XMLLanguage;
+ }
+
+ @NotNull
+ @Override
+ public String filterText(@NotNull String text, @NotNull TemplateToken token) {
+ XmlDocument document = token.getFile().getDocument();
+ if (document != null) {
+ XmlTag tag = document.getRootTag();
+ if (tag != null && !tag.getText().isEmpty()) {
+ new XmlElementVisitor() {
+ @Override
+ public void visitXmlTag(XmlTag tag) {
+ if(!tag.isEmpty()) {
+ XmlTagValue tagValue = tag.getValue();
+ tagValue.setText(PATTERN.matcher(tagValue.getText()).replaceAll(""));
+ }
+ tag.acceptChildren(this);
+ }
+ }.visitXmlTag(tag);
+ return tag.getText();
+ } else {
+ return PATTERN.matcher(document.getText()).replaceAll("");
+ }
+ }
+ return text;
+ }
+
+ @NotNull
+ @Override
+ public GenerationNode filterNode(@NotNull GenerationNode node) {
+ doFilter(node);
+ return node;
+ }
+
+ private static void doFilter(GenerationNode node) {
+ final String surroundedText = node.getSurroundedText();
+ if (surroundedText != null) {
+ node.setSurroundedText(PATTERN.matcher(surroundedText).replaceAll(""));
+ } else if(node.getTemplateToken() == TemplateToken.EMPTY_TEMPLATE_TOKEN) {
+ }
+ for (GenerationNode child : node.getChildren()) {
+ doFilter(child);
+ }
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/XslZenCodingFilter.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/XslZenCodingFilter.java
similarity index 83%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/XslZenCodingFilter.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/filters/XslZenCodingFilter.java
index 3a05ca2..086fa7d 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/XslZenCodingFilter.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/XslZenCodingFilter.java
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.filters;
+package com.intellij.codeInsight.template.emmet.filters;
-import com.intellij.codeInsight.template.zencoding.generators.XmlZenCodingGenerator;
-import com.intellij.codeInsight.template.zencoding.generators.XmlZenCodingGeneratorImpl;
-import com.intellij.codeInsight.template.zencoding.nodes.GenerationNode;
-import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
+import com.intellij.codeInsight.template.emmet.generators.XmlZenCodingGenerator;
+import com.intellij.codeInsight.template.emmet.generators.XmlZenCodingGeneratorImpl;
+import com.intellij.codeInsight.template.emmet.nodes.GenerationNode;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
@@ -41,12 +41,11 @@
public GenerationNode filterNode(@NotNull final GenerationNode node) {
TemplateToken token = node.getTemplateToken();
if (token != null) {
- TemplateToken xmlToken = token;
- XmlDocument document = xmlToken.getFile().getDocument();
+ XmlDocument document = token.getFile().getDocument();
if (document != null) {
final XmlTag tag = document.getRootTag();
if (tag != null) {
- for (Pair<String, String> pair : xmlToken.getAttribute2Value()) {
+ for (Pair<String, String> pair : token.getAttribute2Value()) {
if (SELECT_ATTR_NAME.equals(pair.first)) {
return node;
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/ZenCodingFilter.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/ZenCodingFilter.java
similarity index 88%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/ZenCodingFilter.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/filters/ZenCodingFilter.java
index 5ab6ada..f5057f3 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/ZenCodingFilter.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/filters/ZenCodingFilter.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.filters;
+package com.intellij.codeInsight.template.emmet.filters;
-import com.intellij.codeInsight.template.zencoding.nodes.GenerationNode;
-import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
+import com.intellij.codeInsight.template.emmet.nodes.GenerationNode;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
@@ -36,6 +36,8 @@
new XslZenCodingFilter(),
new CommentZenCodingFilter(),
new EscapeZenCodingFilter(),
+ new SingleLineEmmetFilter(),
+ new BemEmmetFilter(),
new TrimZenCodingFilter()
};
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/LoremGenerator.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/LoremGenerator.java
new file mode 100644
index 0000000..a5f4aee
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/LoremGenerator.java
@@ -0,0 +1,154 @@
+/*
+ * 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 com.intellij.codeInsight.template.emmet.generators;
+
+import com.intellij.openapi.util.text.StringUtil;
+
+import java.util.*;
+
+/**
+ * User: zolotov
+ * Date: 1/31/13
+ */
+public class LoremGenerator {
+ static final String[] COMMON_P = "lorem ipsum dolor sit amet consectetur adipisicing elit".split(" ");
+ private static final String[] WORDS = new String[]{"exercitationem", "perferendis", "perspiciatis", "laborum", "eveniet",
+ "sunt", "iure", "nam", "nobis", "eum", "cum", "officiis", "excepturi",
+ "odio", "consectetur", "quasi", "aut", "quisquam", "vel", "eligendi",
+ "itaque", "non", "odit", "tempore", "quaerat", "dignissimos",
+ "facilis", "neque", "nihil", "expedita", "vitae", "vero", "ipsum",
+ "nisi", "animi", "cumque", "pariatur", "velit", "modi", "natus",
+ "iusto", "eaque", "sequi", "illo", "sed", "ex", "et", "voluptatibus",
+ "tempora", "veritatis", "ratione", "assumenda", "incidunt", "nostrum",
+ "placeat", "aliquid", "fuga", "provident", "praesentium", "rem",
+ "necessitatibus", "suscipit", "adipisci", "quidem", "possimus",
+ "voluptas", "debitis", "sint", "accusantium", "unde", "sapiente",
+ "voluptate", "qui", "aspernatur", "laudantium", "soluta", "amet",
+ "quo", "aliquam", "saepe", "culpa", "libero", "ipsa", "dicta",
+ "reiciendis", "nesciunt", "doloribus", "autem", "impedit", "minima",
+ "maiores", "repudiandae", "ipsam", "obcaecati", "ullam", "enim",
+ "totam", "delectus", "ducimus", "quis", "voluptates", "dolores",
+ "molestiae", "harum", "dolorem", "quia", "voluptatem", "molestias",
+ "magni", "distinctio", "omnis", "illum", "dolorum", "voluptatum", "ea",
+ "quas", "quam", "corporis", "quae", "blanditiis", "atque", "deserunt",
+ "laboriosam", "earum", "consequuntur", "hic", "cupiditate",
+ "quibusdam", "accusamus", "ut", "rerum", "error", "minus", "eius",
+ "ab", "ad", "nemo", "fugit", "officia", "at", "in", "id", "quos",
+ "reprehenderit", "numquam", "iste", "fugiat", "sit", "inventore",
+ "beatae", "repellendus", "magnam", "recusandae", "quod", "explicabo",
+ "doloremque", "aperiam", "consequatur", "asperiores", "commodi",
+ "optio", "dolor", "labore", "temporibus", "repellat", "veniam",
+ "architecto", "est", "esse", "mollitia", "nulla", "a", "similique",
+ "eos", "alias", "dolore", "tenetur", "deleniti", "porro", "facere",
+ "maxime", "corrupti"};
+
+ private final Random random;
+
+ public LoremGenerator() {
+ random = new Random();
+ }
+
+ /**
+ * Generate a paragraph of Lorem ipsum
+ *
+ * @param wordsCount words count in paragraph
+ * @param startWithCommon should paragraph start with common {@link this#COMMON_P}
+ * @return generated paragraph
+ */
+ public String generate(int wordsCount, boolean startWithCommon) {
+ Collection<String> sentences = new LinkedList<String>();
+ int totalWords = 0;
+ String[] words;
+
+ if (startWithCommon) {
+ words = Arrays.copyOfRange(COMMON_P, 0, Math.min(wordsCount, COMMON_P.length));
+ if (words.length > 5) {
+ words[4] += ',';
+ }
+ totalWords += words.length;
+ sentences.add(sentence(words, '.'));
+ }
+
+ while (totalWords < wordsCount) {
+ words = sample(WORDS, Math.min(rand(3, 12) * rand(1, 5), wordsCount - totalWords));
+ totalWords += words.length;
+ insertCommas(words);
+ sentences.add(sentence(words));
+ }
+
+ return StringUtil.join(sentences, " ");
+ }
+
+ private void insertCommas(String[] words) {
+ if (words.length <= 1) {
+ return;
+ }
+
+ int len = words.length;
+ int totalCommas;
+
+ if (len > 3 && len <= 6) {
+ totalCommas = rand(0, 1);
+ }
+ else if (len > 6 && len <= 12) {
+ totalCommas = rand(0, 2);
+ }
+ else {
+ totalCommas = rand(1, 4);
+ }
+
+ while (totalCommas > 0) {
+ int i = rand(0, words.length - 1);
+ String word = words[i];
+ if (!StringUtil.endsWithChar(word, ',')) {
+ words[i] = word + ",";
+ }
+ totalCommas--;
+ }
+ }
+
+ private String sentence(String[] words) {
+ return sentence(words, choice("?!..."));
+ }
+
+ private static String sentence(String[] words, char endChar) {
+ if (words.length > 0) {
+ words[0] = StringUtil.capitalize(words[0]);
+ }
+
+ return StringUtil.join(words, " ") + endChar;
+ }
+
+ private int rand(int from, int to) {
+ return random.nextInt(to - from) + from;
+ }
+
+ private String[] sample(String[] words, int wordsCount) {
+ int len = words.length;
+ int iterations = Math.min(len, wordsCount);
+ Set<String> result = new TreeSet<String>();
+ while (result.size() < iterations) {
+ int i = rand(0, len - 1);
+ result.add(words[i]);
+ }
+
+ return result.toArray(new String[result.size()]);
+ }
+
+ private char choice(String values) {
+ return values.charAt(rand(0, values.length() - 1));
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/generators/XmlZenCodingGenerator.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGenerator.java
similarity index 92%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/generators/XmlZenCodingGenerator.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGenerator.java
index 07f22e5..4febf21 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/generators/XmlZenCodingGenerator.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGenerator.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.generators;
+package com.intellij.codeInsight.template.emmet.generators;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.codeInsight.template.impl.TemplateImpl;
-import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlDocument;
@@ -73,7 +73,7 @@
public abstract String buildAttributesString(@NotNull List<Pair<String, String>> attribute2value,
boolean hasChildren,
int numberInIteration,
- @Nullable String surroundedText);
+ int totalIterations, @Nullable String surroundedText);
public abstract boolean isMyContext(@NotNull PsiElement context, boolean wrapping);
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/generators/XmlZenCodingGeneratorImpl.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGeneratorImpl.java
similarity index 95%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/generators/XmlZenCodingGeneratorImpl.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGeneratorImpl.java
index b9ee262..2fbfc13 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/generators/XmlZenCodingGeneratorImpl.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGeneratorImpl.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.generators;
+package com.intellij.codeInsight.template.emmet.generators;
import com.intellij.codeInsight.template.HtmlTextContextType;
-import com.intellij.codeInsight.template.zencoding.ZenCodingUtil;
+import com.intellij.codeInsight.template.emmet.ZenCodingUtil;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.lang.xml.XMLLanguage;
@@ -64,12 +64,12 @@
public String buildAttributesString(@NotNull List<Pair<String, String>> attribute2value,
boolean hasChildren,
int numberInIteration,
- @Nullable String surroundedText) {
+ int totalIterations, @Nullable String surroundedText) {
StringBuilder result = new StringBuilder();
for (Iterator<Pair<String, String>> it = attribute2value.iterator(); it.hasNext();) {
Pair<String, String> pair = it.next();
String name = pair.first;
- String value = ZenCodingUtil.getValue(pair.second, numberInIteration, surroundedText);
+ String value = ZenCodingUtil.getValue(pair.second, numberInIteration, totalIterations, surroundedText);
result.append(getAttributeString(name, value));
if (it.hasNext()) {
result.append(' ');
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/generators/ZenCodingGenerator.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/ZenCodingGenerator.java
similarity index 89%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/generators/ZenCodingGenerator.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/generators/ZenCodingGenerator.java
index abd3926..f4a53d7 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/generators/ZenCodingGenerator.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/ZenCodingGenerator.java
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.generators;
+package com.intellij.codeInsight.template.emmet.generators;
import com.intellij.codeInsight.template.CustomTemplateCallback;
+import com.intellij.codeInsight.template.emmet.EmmetParser;
+import com.intellij.codeInsight.template.emmet.XmlEmmetParser;
+import com.intellij.codeInsight.template.emmet.ZenCodingTemplate;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
+import com.intellij.codeInsight.template.emmet.tokens.ZenCodingToken;
import com.intellij.codeInsight.template.impl.TemplateImpl;
-import com.intellij.codeInsight.template.zencoding.ZenCodingTemplate;
-import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiElement;
@@ -154,4 +157,9 @@
}
return key;
}
+
+ @NotNull
+ public EmmetParser createParser(List<ZenCodingToken> tokens, CustomTemplateCallback callback, ZenCodingGenerator generator) {
+ return new XmlEmmetParser(tokens, callback, generator);
+ }
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/AddOperationNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/AddOperationNode.java
similarity index 71%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/AddOperationNode.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/AddOperationNode.java
index 1894ae7..60ab7cf 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/AddOperationNode.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/AddOperationNode.java
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.nodes;
+package com.intellij.codeInsight.template.emmet.nodes;
import com.intellij.codeInsight.template.CustomTemplateCallback;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -43,14 +44,25 @@
@NotNull
@Override
+ public List<ZenCodingNode> getChildren() {
+ return ContainerUtil.newLinkedList(myLeftOperand, myRightOperand);
+ }
+
+ @NotNull
+ @Override
public List<GenerationNode> expand(int numberInIteration,
- String surroundedText,
+ int totalIterations, String surroundedText,
CustomTemplateCallback callback,
- boolean insertSurroundedTextAtTheEnd) {
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
List<GenerationNode> result = new ArrayList<GenerationNode>();
- List<GenerationNode> leftNodes = myLeftOperand.expand(numberInIteration, surroundedText, callback, false);
+ List<GenerationNode> leftNodes = myLeftOperand.expand(numberInIteration, totalIterations, surroundedText, callback, false, parent);
result.addAll(leftNodes);
- result.addAll(myRightOperand.expand(numberInIteration, surroundedText, callback, insertSurroundedTextAtTheEnd));
+ result.addAll(myRightOperand.expand(numberInIteration, totalIterations, surroundedText, callback, insertSurroundedTextAtTheEnd, parent));
return result;
}
+
+ @Override
+ public String toString() {
+ return "+";
+ }
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/ClimbUpOperationNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/ClimbUpOperationNode.java
new file mode 100644
index 0000000..1648a36
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/ClimbUpOperationNode.java
@@ -0,0 +1,74 @@
+/*
+ * 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 com.intellij.codeInsight.template.emmet.nodes;
+
+import com.intellij.codeInsight.template.CustomTemplateCallback;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+import static com.intellij.util.containers.ContainerUtil.newArrayList;
+
+/**
+ * @author zolotov
+ */
+public class ClimbUpOperationNode extends ZenCodingNode {
+ private final ZenCodingNode myLeftOperand;
+ private final ZenCodingNode myRightOperand;
+
+ public ClimbUpOperationNode(ZenCodingNode leftOperand, ZenCodingNode rightOperand) {
+ myLeftOperand = leftOperand;
+ myRightOperand = rightOperand;
+ }
+
+ public ZenCodingNode getLeftOperand() {
+ return myLeftOperand;
+ }
+
+ public ZenCodingNode getRightOperand() {
+ return myRightOperand;
+ }
+
+ @NotNull
+ @Override
+ public List<GenerationNode> expand(int numberInIteration,
+ int totalIterations, String surroundedText,
+ CustomTemplateCallback callback,
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
+ List<GenerationNode> result = newArrayList();
+ result.addAll(myLeftOperand.expand(numberInIteration, totalIterations, surroundedText, callback, insertSurroundedTextAtTheEnd, parent));
+ GenerationNode grandParent = parent != null ? parent.getParent() : null;
+ if (grandParent != null) {
+ myRightOperand.expand(numberInIteration, totalIterations, surroundedText, callback, insertSurroundedTextAtTheEnd, grandParent);
+ }
+ else {
+ result.addAll(myRightOperand.expand(numberInIteration, totalIterations, surroundedText, callback, insertSurroundedTextAtTheEnd, parent));
+ }
+ return result;
+ }
+
+ @NotNull
+ @Override
+ public List<ZenCodingNode> getChildren() {
+ return ContainerUtil.newLinkedList(myLeftOperand, myRightOperand);
+ }
+
+ @Override
+ public String toString() {
+ return "^";
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/FilterNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/FilterNode.java
similarity index 77%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/FilterNode.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/FilterNode.java
index 7ff67f1..2de1c99 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/FilterNode.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/FilterNode.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.nodes;
+package com.intellij.codeInsight.template.emmet.nodes;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import org.jetbrains.annotations.NotNull;
@@ -43,9 +43,14 @@
@NotNull
@Override
public List<GenerationNode> expand(int numberInIteration,
- String surroundedText,
+ int totalIterations, String surroundedText,
CustomTemplateCallback callback,
- boolean insertSurroundedTextAtTheEnd) {
- return myNode.expand(numberInIteration, surroundedText, callback, insertSurroundedTextAtTheEnd);
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
+ return myNode.expand(numberInIteration, totalIterations, surroundedText, callback, insertSurroundedTextAtTheEnd, parent);
+ }
+
+ @Override
+ public String toString() {
+ return "Filter(" + myFilter + ")";
}
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/GenerationNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/GenerationNode.java
similarity index 79%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/GenerationNode.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/GenerationNode.java
index a07b521..771f1a8 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/GenerationNode.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/GenerationNode.java
@@ -13,23 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.nodes;
+package com.intellij.codeInsight.template.emmet.nodes;
+import com.google.common.base.Strings;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.codeInsight.template.LiveTemplateBuilder;
+import com.intellij.codeInsight.template.emmet.ZenCodingTemplate;
+import com.intellij.codeInsight.template.emmet.ZenCodingUtil;
+import com.intellij.codeInsight.template.emmet.filters.SingleLineEmmetFilter;
+import com.intellij.codeInsight.template.emmet.filters.ZenCodingFilter;
+import com.intellij.codeInsight.template.emmet.generators.XmlZenCodingGenerator;
+import com.intellij.codeInsight.template.emmet.generators.XmlZenCodingGeneratorImpl;
+import com.intellij.codeInsight.template.emmet.generators.ZenCodingGenerator;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.codeInsight.template.impl.TemplateImpl;
-import com.intellij.codeInsight.template.zencoding.ZenCodingTemplate;
-import com.intellij.codeInsight.template.zencoding.ZenCodingUtil;
-import com.intellij.codeInsight.template.zencoding.generators.XmlZenCodingGenerator;
-import com.intellij.codeInsight.template.zencoding.generators.XmlZenCodingGeneratorImpl;
-import com.intellij.codeInsight.template.zencoding.filters.ZenCodingFilter;
-import com.intellij.codeInsight.template.zencoding.generators.ZenCodingGenerator;
-import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
@@ -41,43 +44,55 @@
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
-import com.intellij.util.containers.IntArrayList;
import com.intellij.xml.util.HtmlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
+import static com.google.common.collect.Lists.newArrayList;
+
/**
* @author Eugene.Kudelevsky
*/
-public class GenerationNode {
+public class GenerationNode extends UserDataHolderBase {
private final TemplateToken myTemplateToken;
- private final List<GenerationNode> myChildren;
+ private final List<GenerationNode> myChildren = newArrayList();
private final int myNumberInIteration;
+ private final int myTotalIterations;
private String mySurroundedText;
private final boolean myInsertSurroundedTextAtTheEnd;
+ private GenerationNode myParent;
private boolean myContainsSurroundedTextMarker = false;
public GenerationNode(TemplateToken templateToken,
- List<GenerationNode> children,
int numberInIteration,
- String surroundedText,
- boolean insertSurroundedTextAtTheEnd) {
+ int totalIterations, String surroundedText,
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
myTemplateToken = templateToken;
- myChildren = children;
myNumberInIteration = numberInIteration;
+ myTotalIterations = totalIterations;
mySurroundedText = surroundedText;
myInsertSurroundedTextAtTheEnd = insertSurroundedTextAtTheEnd;
+ if(parent != null) {
+ parent.addChild(this);
+ }
}
public List<GenerationNode> getChildren() {
return myChildren;
}
- public void addChildren(Collection<GenerationNode> child) {
- myChildren.addAll(child);
+ public void addChild(GenerationNode child) {
+ child.setParent(this);
+ myChildren.add(child);
+ }
+
+ public void addChildren(Collection<GenerationNode> children) {
+ for (GenerationNode child : children) {
+ addChild(child);
+ }
}
public boolean isLeaf() {
@@ -105,9 +120,14 @@
boolean insertSurroundedText) {
myContainsSurroundedTextMarker = !(insertSurroundedText && myInsertSurroundedTextAtTheEnd);
+ boolean singleLineFilterEnabled = false;
+
GenerationNode generationNode = this;
for (ZenCodingFilter filter : filters) {
generationNode = filter.filterNode(generationNode);
+ if(filter instanceof SingleLineEmmetFilter) {
+ singleLineFilterEnabled = true;
+ }
}
if (generationNode != this) {
@@ -125,8 +145,7 @@
TemplateToken xmlTemplateToken = myTemplateToken;
List<Pair<String, String>> attr2value = new ArrayList<Pair<String, String>>(xmlTemplateToken.getAttribute2Value());
parentTemplate = invokeXmlTemplate(xmlTemplateToken, callback, generator, hasChildren, attr2value);
- predefinedValues =
- buildPredefinedValues(attr2value, (XmlZenCodingGenerator)generator, hasChildren);
+ predefinedValues = buildPredefinedValues(attr2value, (XmlZenCodingGenerator)generator, hasChildren);
}
else {
parentTemplate = invokeTemplate(myTemplateToken, hasChildren, callback, generator);
@@ -155,12 +174,12 @@
LiveTemplateBuilder.Marker marker = offset < builder.length() ? builder.createMarker(offset) : null;
CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(callback.getProject());
- String tabStr;
+ String indentStr;
if (callback.isInInjectedFragment()) {
- tabStr = "";
+ indentStr = "";
}
else if (settings.useTabCharacter(callback.getFileType())) {
- tabStr = "\t";
+ indentStr = "\t";
}
else {
StringBuilder tab = new StringBuilder();
@@ -168,7 +187,7 @@
while (tabSize-- > 0) {
tab.append(' ');
}
- tabStr = tab.toString();
+ indentStr = tab.toString();
}
for (int i = 0, myChildrenSize = myChildren.size(); i < myChildrenSize; i++) {
@@ -177,17 +196,17 @@
boolean blockTag = child.isBlockTag();
- if (blockTag && !isNewLineBefore(builder.getText(), offset)) {
- builder.insertText(offset, "\n" + tabStr, false);
- offset += tabStr.length() + 1;
+ if (!singleLineFilterEnabled && blockTag && !isNewLineBefore(builder.getText(), offset)) {
+ builder.insertText(offset, "\n" + indentStr, false);
+ offset += indentStr.length() + 1;
}
int e = builder.insertTemplate(offset, childTemplate, null);
offset = marker != null ? marker.getEndOffset() : builder.length();
- if (blockTag && !isNewLineAfter(builder.getText(), offset)) {
- builder.insertText(offset, "\n" + tabStr, false);
- offset += tabStr.length() + 1;
+ if (!singleLineFilterEnabled && blockTag && !isNewLineAfter(builder.getText(), offset)) {
+ builder.insertText(offset, "\n" + indentStr, false);
+ offset += indentStr.length() + 1;
}
if (end == -1 && e < offset) {
@@ -197,6 +216,9 @@
/*if (end != -1) {
builder.insertVariableSegment(end, TemplateImpl.END);
}*/
+ if(singleLineFilterEnabled) {
+ builder.setIsToReformat(false);
+ }
return builder.buildTemplate();
}
@@ -229,6 +251,11 @@
XmlDocument document = xmlFile.getDocument();
if (document != null) {
final XmlTag tag = document.getRootTag();
+ for (Pair<String, String> pair : attr2value) {
+ if (Strings.isNullOrEmpty(pair.second)) {
+ template.addVariable(pair.first, "", "", true);
+ }
+ }
if (tag != null) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
@@ -239,8 +266,8 @@
});
}
}
- template =
- (generator != null ? generator : XmlZenCodingGeneratorImpl.INSTANCE).generateTemplate(token, hasChildren, callback.getContext());
+ ZenCodingGenerator zenCodingGenerator = generator != null ? generator : XmlZenCodingGeneratorImpl.INSTANCE;
+ template = zenCodingGenerator.generateTemplate(token, hasChildren, callback.getContext());
removeVariablesWhichHasNoSegment(template);
return template;
}
@@ -315,16 +342,14 @@
for (int i = 0; i < template.getSegmentsCount(); i++) {
segments.add(template.getSegmentName(i));
}
- IntArrayList varsToRemove = new IntArrayList();
- for (int i = 0; i < template.getVariableCount(); i++) {
+ for (int i = template.getVariableCount() - 1; i >= 0; i--) {
String varName = template.getVariableNameAt(i);
if (!segments.contains(varName)) {
- varsToRemove.add(i);
+ template.removeVariable(i);
+ } else {
+ segments.remove(varName);
}
}
- for (int i = 0; i < varsToRemove.size(); i++) {
- template.removeVariable(varsToRemove.get(i));
- }
}
@Nullable
@@ -342,7 +367,7 @@
}
}
- String attributes = generator.buildAttributesString(attribute2value, hasChildren, myNumberInIteration, mySurroundedText);
+ String attributes = generator.buildAttributesString(attribute2value, hasChildren, myNumberInIteration, myTotalIterations, mySurroundedText);
attributes = attributes.length() > 0 ? ' ' + attributes : null;
Map<String, String> predefinedValues = null;
if (attributes != null) {
@@ -359,7 +384,10 @@
if (ZenCodingUtil.containsSurroundedTextMarker(pair.second)) {
myContainsSurroundedTextMarker = true;
}
- tag.setAttribute(pair.first, ZenCodingUtil.getValue(pair.second, myNumberInIteration, mySurroundedText));
+ tag.setAttribute(pair.first,
+ Strings.isNullOrEmpty(pair.second)
+ ? "$" + pair.first + "$"
+ : ZenCodingUtil.getValue(pair.second, myNumberInIteration, myTotalIterations, mySurroundedText));
iterator.remove();
}
}
@@ -398,4 +426,12 @@
public void setSurroundedText(String surroundedText) {
mySurroundedText = surroundedText;
}
+
+ public GenerationNode getParent() {
+ return myParent;
+ }
+
+ public void setParent(GenerationNode parent) {
+ myParent = parent;
+ }
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/LoremNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/LoremNode.java
new file mode 100644
index 0000000..c116390
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/LoremNode.java
@@ -0,0 +1,62 @@
+/*
+ * 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 com.intellij.codeInsight.template.emmet.nodes;
+
+import com.intellij.codeInsight.template.CustomTemplateCallback;
+import com.intellij.codeInsight.template.emmet.ZenCodingTemplate;
+import com.intellij.codeInsight.template.emmet.generators.LoremGenerator;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
+import com.intellij.codeInsight.template.impl.TemplateImpl;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class LoremNode extends ZenCodingNode {
+ private final int myWordsCount;
+ private final LoremGenerator myLoremGenerator;
+
+ public LoremNode(int wordsCount) {
+ myLoremGenerator = new LoremGenerator();
+ myWordsCount = wordsCount;
+ }
+
+ @NotNull
+ @Override
+ public List<GenerationNode> expand(int numberInIteration,
+ int totalIterations, String surroundedText,
+ CustomTemplateCallback callback,
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
+
+
+ final TemplateToken templateToken = new TemplateToken("", Collections.<Pair<String, String>>emptyList());
+ final TemplateImpl template = new TemplateImpl("", myLoremGenerator.generate(myWordsCount, numberInIteration <= 0), "");
+ ZenCodingTemplate.doSetTemplate(templateToken, template, callback);
+
+ final GenerationNode node = new GenerationNode(templateToken, numberInIteration,
+ totalIterations, surroundedText, insertSurroundedTextAtTheEnd, parent);
+ return Collections.singletonList(node);
+ }
+
+ @Override
+ public String toString() {
+ return "Lorem(" + myWordsCount + ")";
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/MoreOperationNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/MoreOperationNode.java
similarity index 69%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/MoreOperationNode.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/MoreOperationNode.java
index 8e32e50..fa80215 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/MoreOperationNode.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/MoreOperationNode.java
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.nodes;
+package com.intellij.codeInsight.template.emmet.nodes;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.openapi.util.text.LineTokenizer;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
/**
@@ -43,32 +43,27 @@
return myRightOperand;
}
- private static void addChildrenToAllLeafs(GenerationNode root, Collection<GenerationNode> children) {
- if (root.isLeaf()) {
- root.addChildren(children);
- }
- else {
- for (GenerationNode child : root.getChildren()) {
- addChildrenToAllLeafs(child, children);
- }
- }
+ @NotNull
+ @Override
+ public List<ZenCodingNode> getChildren() {
+ return ContainerUtil.newLinkedList(myLeftOperand, myRightOperand);
}
@NotNull
@Override
public List<GenerationNode> expand(int numberInIteration,
- String surroundedText,
+ int totalIterations, String surroundedText,
CustomTemplateCallback callback,
- boolean insertSurroundedTextAtTheEnd) {
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
if (myLeftOperand instanceof MulOperationNode || (myLeftOperand instanceof UnaryMulOperationNode && surroundedText != null)) {
List<GenerationNode> result = new ArrayList<GenerationNode>();
if (myLeftOperand instanceof MulOperationNode) {
MulOperationNode mul = (MulOperationNode)myLeftOperand;
for (int i = 0; i < mul.getRightOperand(); i++) {
- List<GenerationNode> parentNodes = mul.getLeftOperand().expand(i, surroundedText, callback, insertSurroundedTextAtTheEnd);
- List<GenerationNode> innerNodes = myRightOperand.expand(i, surroundedText, callback, insertSurroundedTextAtTheEnd);
+ List<GenerationNode> parentNodes = mul.getLeftOperand().expand(i, totalIterations, surroundedText, callback, insertSurroundedTextAtTheEnd,
+ parent);
for (GenerationNode parentNode : parentNodes) {
- addChildrenToAllLeafs(parentNode, innerNodes);
+ myRightOperand.expand(i, totalIterations, surroundedText, callback, insertSurroundedTextAtTheEnd, parentNode);
}
result.addAll(parentNodes);
}
@@ -78,21 +73,25 @@
String[] lines = LineTokenizer.tokenize(surroundedText, false);
for (int i = 0; i < lines.length; i++) {
String line = lines[i].trim();
- List<GenerationNode> parentNodes = unaryMul.getOperand().expand(i, line, callback, insertSurroundedTextAtTheEnd);
- List<GenerationNode> innerNodes = myRightOperand.expand(i, line, callback, insertSurroundedTextAtTheEnd);
+ List<GenerationNode> parentNodes = unaryMul.getOperand().expand(i, totalIterations, line, callback, insertSurroundedTextAtTheEnd, parent);
for (GenerationNode parentNode : parentNodes) {
- addChildrenToAllLeafs(parentNode, innerNodes);
+ myRightOperand.expand(i, totalIterations, line, callback, insertSurroundedTextAtTheEnd, parentNode);
}
result.addAll(parentNodes);
}
}
return result;
}
- List<GenerationNode> leftGenNodes = myLeftOperand.expand(numberInIteration, surroundedText, callback, insertSurroundedTextAtTheEnd);
+ List<GenerationNode> leftGenNodes = myLeftOperand.expand(numberInIteration, totalIterations, surroundedText, callback, insertSurroundedTextAtTheEnd,
+ parent);
for (GenerationNode leftGenNode : leftGenNodes) {
- List<GenerationNode> rightGenNodes = myRightOperand.expand(numberInIteration, surroundedText, callback, insertSurroundedTextAtTheEnd);
- addChildrenToAllLeafs(leftGenNode, rightGenNodes);
+ myRightOperand.expand(numberInIteration,totalIterations , surroundedText, callback, insertSurroundedTextAtTheEnd, leftGenNode);
}
return leftGenNodes;
}
+
+ @Override
+ public String toString() {
+ return ">";
+ }
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/MulOperationNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/MulOperationNode.java
similarity index 81%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/MulOperationNode.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/MulOperationNode.java
index bb5b324..ec9eee1 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/MulOperationNode.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/MulOperationNode.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.nodes;
+package com.intellij.codeInsight.template.emmet.nodes;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import org.jetbrains.annotations.NotNull;
@@ -44,13 +44,18 @@
@NotNull
@Override
public List<GenerationNode> expand(int numberInIteration,
- String surroundedText,
+ int totalIterations, String surroundedText,
CustomTemplateCallback callback,
- boolean insertSurroundedTextAtTheEnd) {
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
List<GenerationNode> result = new ArrayList<GenerationNode>();
for (int i = 0; i < myRightOperand; i++) {
- result.addAll(myLeftOperand.expand(i, surroundedText, callback, insertSurroundedTextAtTheEnd));
+ result.addAll(myLeftOperand.expand(i, myRightOperand, surroundedText, callback, insertSurroundedTextAtTheEnd, parent));
}
return result;
}
+
+ @Override
+ public String toString() {
+ return "*";
+ }
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/TemplateNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/TemplateNode.java
new file mode 100644
index 0000000..317459e
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/TemplateNode.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2010 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.codeInsight.template.emmet.nodes;
+
+import com.google.common.base.Joiner;
+import com.intellij.codeInsight.template.CustomTemplateCallback;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class TemplateNode extends ZenCodingNode {
+ private static final Joiner JOINER = Joiner.on(",");
+ private final TemplateToken myTemplateToken;
+
+ public TemplateNode(TemplateToken templateToken) {
+ myTemplateToken = templateToken;
+ }
+
+ public TemplateToken getTemplateToken() {
+ return myTemplateToken;
+ }
+
+ @NotNull
+ @Override
+ public List<GenerationNode> expand(int numberInIteration,
+ int totalIterations, String surroundedText,
+ CustomTemplateCallback callback,
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
+ GenerationNode node = new GenerationNode(myTemplateToken, numberInIteration, totalIterations,
+ surroundedText, insertSurroundedTextAtTheEnd, parent);
+ return Arrays.asList(node);
+ }
+
+ @Override
+ public String toString() {
+ String result = myTemplateToken.getKey();
+ List<Pair<String, String>> attributes = myTemplateToken.getAttribute2Value();
+ if (!attributes.isEmpty()) {
+ result += "[" + JOINER.join(myTemplateToken.getAttribute2Value()) + "]";
+ }
+ return "Template(" + result + ")";
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/TextNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/TextNode.java
similarity index 65%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/TextNode.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/TextNode.java
index 8e7eba7..f715aec 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/TextNode.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/TextNode.java
@@ -1,15 +1,14 @@
-package com.intellij.codeInsight.template.zencoding.nodes;
+package com.intellij.codeInsight.template.emmet.nodes;
import com.intellij.codeInsight.template.CustomTemplateCallback;
+import com.intellij.codeInsight.template.emmet.ZenCodingTemplate;
+import com.intellij.codeInsight.template.emmet.ZenCodingUtil;
+import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
+import com.intellij.codeInsight.template.emmet.tokens.TextToken;
import com.intellij.codeInsight.template.impl.TemplateImpl;
-import com.intellij.codeInsight.template.zencoding.ZenCodingTemplate;
-import com.intellij.codeInsight.template.zencoding.ZenCodingUtil;
-import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
-import com.intellij.codeInsight.template.zencoding.tokens.TextToken;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -31,18 +30,24 @@
@NotNull
@Override
public List<GenerationNode> expand(int numberInIteration,
- String surroundedText,
+ int totalIterations, String surroundedText,
CustomTemplateCallback callback,
- boolean insertSurroundedTextAtTheEnd) {
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
final TemplateToken templateToken = new TemplateToken("", Collections.<Pair<String, String>>emptyList());
final boolean containsSurroundedTextMarker = ZenCodingUtil.containsSurroundedTextMarker(myText);
- final String text = ZenCodingUtil.replaceMarkers(myText.replace("${nl}", "\n"), numberInIteration, surroundedText);
+ final String text = ZenCodingUtil.replaceMarkers(myText.replace("${nl}", "\n"), numberInIteration, totalIterations, surroundedText);
final TemplateImpl template = new TemplateImpl("", text, "");
ZenCodingTemplate.doSetTemplate(templateToken, template, callback);
- final GenerationNode node = new GenerationNode(templateToken, new ArrayList<GenerationNode>(), numberInIteration,
- containsSurroundedTextMarker ? null : surroundedText, insertSurroundedTextAtTheEnd);
+ final GenerationNode node = new GenerationNode(templateToken, numberInIteration, totalIterations,
+ containsSurroundedTextMarker ? null : surroundedText,
+ insertSurroundedTextAtTheEnd, parent);
return Collections.singletonList(node);
}
+
+ @Override
+ public String toString() {
+ return "Text(" + myText + ")";
+ }
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/UnaryMulOperationNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/UnaryMulOperationNode.java
similarity index 76%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/UnaryMulOperationNode.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/UnaryMulOperationNode.java
index 7efb182..78eb446 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/UnaryMulOperationNode.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/UnaryMulOperationNode.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.nodes;
+package com.intellij.codeInsight.template.emmet.nodes;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.openapi.util.text.LineTokenizer;
@@ -39,17 +39,22 @@
@NotNull
@Override
public List<GenerationNode> expand(int numberInIteration,
- String surroundedText,
+ int totalIterations, String surroundedText,
CustomTemplateCallback callback,
- boolean insertSurroundedTextAtTheEnd) {
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
if (surroundedText == null) {
- return myOperand.expand(numberInIteration, surroundedText, callback, insertSurroundedTextAtTheEnd);
+ return myOperand.expand(numberInIteration, totalIterations, surroundedText, callback, insertSurroundedTextAtTheEnd, parent);
}
String[] lines = LineTokenizer.tokenize(surroundedText, false);
List<GenerationNode> result = new ArrayList<GenerationNode>();
for (int i = 0; i < lines.length; i++) {
- result.addAll(myOperand.expand(i, lines[i].trim(), callback, insertSurroundedTextAtTheEnd));
+ result.addAll(myOperand.expand(i, lines.length, lines[i].trim(), callback, insertSurroundedTextAtTheEnd, parent));
}
return result;
}
+
+ @Override
+ public String toString() {
+ return "*";
+ }
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/ZenCodingNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/ZenCodingNode.java
similarity index 76%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/ZenCodingNode.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/ZenCodingNode.java
index f1993ad..550d05d 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/ZenCodingNode.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/ZenCodingNode.java
@@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.nodes;
+package com.intellij.codeInsight.template.emmet.nodes;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import org.jetbrains.annotations.NotNull;
+import java.util.Collections;
import java.util.List;
/**
@@ -26,7 +27,12 @@
public abstract class ZenCodingNode {
@NotNull
public abstract List<GenerationNode> expand(int numberInIteration,
- String surroundedText,
+ int totalIterations, String surroundedText,
CustomTemplateCallback callback,
- boolean insertSurroundedTextAtTheEnd);
+ boolean insertSurroundedTextAtTheEnd, GenerationNode parent);
+
+ @NotNull
+ public List<ZenCodingNode> getChildren() {
+ return Collections.emptyList();
+ }
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/ZenEmptyNode.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/ZenEmptyNode.java
new file mode 100644
index 0000000..9b92681
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/nodes/ZenEmptyNode.java
@@ -0,0 +1,45 @@
+/*
+ * 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 com.intellij.codeInsight.template.emmet.nodes;
+
+import com.intellij.codeInsight.template.CustomTemplateCallback;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * User: zolotov
+ * Date: 1/31/13
+ */
+public class ZenEmptyNode extends ZenCodingNode {
+ public static final ZenCodingNode INSTANCE = new ZenEmptyNode();
+
+ @NotNull
+ @Override
+ public List<GenerationNode> expand(int numberInIteration,
+ int totalIterations, String surroundedText,
+ CustomTemplateCallback callback,
+ boolean insertSurroundedTextAtTheEnd,
+ GenerationNode parent) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY_NODE";
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/processors/EmmetPreprocessor.java
similarity index 76%
copy from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingToken.java
copy to xml/impl/src/com/intellij/codeInsight/template/emmet/processors/EmmetPreprocessor.java
index b38c98e..1dbfee0 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/processors/EmmetPreprocessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2010 JetBrains s.r.o.
+ * 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.
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.processors;
/**
- * @author Eugene.Kudelevsky
+ * User: zolotov
+ * Date: 1/31/13
*/
-public abstract class ZenCodingToken {
+public interface EmmetPreprocessor {
+
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/processors/LoremProcessor.java
similarity index 74%
copy from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingToken.java
copy to xml/impl/src/com/intellij/codeInsight/template/emmet/processors/LoremProcessor.java
index b38c98e..d7eecfc 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/processors/LoremProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2010 JetBrains s.r.o.
+ * 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.
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.processors;
/**
- * @author Eugene.Kudelevsky
+ * User: zolotov
+ * Date: 1/31/13
*/
-public abstract class ZenCodingToken {
+public class LoremProcessor implements EmmetPreprocessor {
+
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/FilterToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/FilterToken.java
similarity index 93%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/FilterToken.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/FilterToken.java
index 739c9b6..0001590 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/FilterToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/FilterToken.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
/**
* @author Eugene.Kudelevsky
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/IdentifierToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/IdentifierToken.java
similarity index 85%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/IdentifierToken.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/IdentifierToken.java
index 2c42ebb..399eb51 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/IdentifierToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/IdentifierToken.java
@@ -1,4 +1,4 @@
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
import org.jetbrains.annotations.NotNull;
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/NumberToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/NumberToken.java
similarity index 93%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/NumberToken.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/NumberToken.java
index 5d4ae7c..fd85596 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/NumberToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/NumberToken.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
/**
* @author Eugene.Kudelevsky
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/OperationToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/OperationToken.java
similarity index 93%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/OperationToken.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/OperationToken.java
index 5c7c843..eee51cd 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/OperationToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/OperationToken.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
/**
* @author Eugene.Kudelevsky
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/StringLiteralToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/StringLiteralToken.java
similarity index 83%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/StringLiteralToken.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/StringLiteralToken.java
index 1c8428a..c853640 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/StringLiteralToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/StringLiteralToken.java
@@ -1,4 +1,4 @@
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
/**
* @author Eugene.Kudelevsky
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/TemplateToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/TemplateToken.java
similarity index 81%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/TemplateToken.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/TemplateToken.java
index 3bd3e5f..dac15a9 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/TemplateToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/TemplateToken.java
@@ -13,23 +13,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.xml.XmlFile;
+import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public class TemplateToken extends ZenCodingToken {
+ public final static TemplateToken EMPTY_TEMPLATE_TOKEN = new TemplateToken("", new ArrayList<Pair<String, String>>());
+
private final String myKey;
private TemplateImpl myTemplate;
private final List<Pair<String, String>> myAttribute2Value;
private XmlFile myFile;
+ public TemplateToken(String key) {
+ this(key, new ArrayList<Pair<String, String>>());
+ }
+
public TemplateToken(String key, List<Pair<String, String>> attribute2value) {
myKey = key;
myAttribute2Value = attribute2value;
@@ -55,6 +63,7 @@
myTemplate = template;
}
+ @Nullable
public TemplateImpl getTemplate() {
return myTemplate;
}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/TextToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/TextToken.java
similarity index 82%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/TextToken.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/TextToken.java
index e216302..a458d3b 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/TextToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/TextToken.java
@@ -1,4 +1,4 @@
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
/**
* @author Eugene.Kudelevsky
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingToken.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/ZenCodingToken.java
similarity index 91%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingToken.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/ZenCodingToken.java
index b38c98e..d2da772 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingToken.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/ZenCodingToken.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
/**
* @author Eugene.Kudelevsky
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingTokenImpl.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/ZenCodingTokenImpl.java
similarity index 81%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingTokenImpl.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/ZenCodingTokenImpl.java
index 749279c..ae9cf1b 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingTokenImpl.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/ZenCodingTokenImpl.java
@@ -1,4 +1,4 @@
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
/**
* @author Eugene.Kudelevsky
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingTokens.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/ZenCodingTokens.java
similarity index 93%
rename from xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingTokens.java
rename to xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/ZenCodingTokens.java
index 8911153..9db2d50 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/tokens/ZenCodingTokens.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/tokens/ZenCodingTokens.java
@@ -1,4 +1,4 @@
-package com.intellij.codeInsight.template.zencoding.tokens;
+package com.intellij.codeInsight.template.emmet.tokens;
/**
* @author Eugene.Kudelevsky
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingTemplate.java
deleted file mode 100644
index 29bac40..0000000
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingTemplate.java
+++ /dev/null
@@ -1,890 +0,0 @@
-/*
- * Copyright 2000-2010 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.codeInsight.template.zencoding;
-
-import com.intellij.application.options.editor.WebEditorOptions;
-import com.intellij.codeInsight.CodeInsightBundle;
-import com.intellij.codeInsight.template.*;
-import com.intellij.codeInsight.template.impl.TemplateImpl;
-import com.intellij.codeInsight.template.impl.TemplateState;
-import com.intellij.codeInsight.template.zencoding.filters.ZenCodingFilter;
-import com.intellij.codeInsight.template.zencoding.generators.XmlZenCodingGenerator;
-import com.intellij.codeInsight.template.zencoding.generators.ZenCodingGenerator;
-import com.intellij.codeInsight.template.zencoding.nodes.*;
-import com.intellij.codeInsight.template.zencoding.tokens.*;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.command.CommandProcessor;
-import com.intellij.openapi.command.undo.UndoConstants;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.EditorModificationUtil;
-import com.intellij.openapi.editor.ScrollType;
-import com.intellij.openapi.fileTypes.FileType;
-import com.intellij.openapi.fileTypes.StdFileTypes;
-import com.intellij.openapi.ui.InputValidatorEx;
-import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.TextRange;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.*;
-import com.intellij.psi.xml.XmlAttribute;
-import com.intellij.psi.xml.XmlDocument;
-import com.intellij.psi.xml.XmlFile;
-import com.intellij.psi.xml.XmlTag;
-import com.intellij.util.ArrayUtil;
-import com.intellij.util.LocalTimeCounter;
-import com.intellij.xml.XmlBundle;
-import org.apache.xerces.util.XML11Char;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-
-/**
- * @author Eugene.Kudelevsky
- */
-public class ZenCodingTemplate implements CustomLiveTemplate {
- public static final char MARKER = '\0';
- private static final String DELIMS = ">+*|()[]{}.#,='\" \0";
- public static final String ATTRS = "ATTRS";
- private static final String ID = "id";
- private static final String CLASS = "class";
- private static final String DEFAULT_TAG = "div";
-
- private static int parseNonNegativeInt(@NotNull String s) {
- try {
- return Integer.parseInt(s);
- }
- catch (Throwable ignored) {
- }
- return -1;
- }
-
- private static boolean isXML11ValidQName(String str) {
- final int colon = str.indexOf(':');
- if (colon == 0 || colon == str.length() - 1) {
- return false;
- }
- if (colon > 0) {
- final String prefix = str.substring(0, colon);
- final String localPart = str.substring(colon + 1);
- return XML11Char.isXML11ValidNCName(prefix) && XML11Char.isXML11ValidNCName(localPart);
- }
- return XML11Char.isXML11ValidNCName(str);
- }
-
- private static boolean isHtml(CustomTemplateCallback callback) {
- FileType type = callback.getFileType();
- return type == StdFileTypes.HTML || type == StdFileTypes.XHTML;
- }
-
- private static void addMissingAttributes(XmlTag tag, List<Pair<String, String>> value) {
- List<Pair<String, String>> attr2value = new ArrayList<Pair<String, String>>(value);
- for (Iterator<Pair<String, String>> iterator = attr2value.iterator(); iterator.hasNext();) {
- Pair<String, String> pair = iterator.next();
- if (tag.getAttribute(pair.first) != null) {
- iterator.remove();
- }
- }
- addAttributesBefore(tag, attr2value);
- }
-
- private static void addAttributesBefore(XmlTag tag, List<Pair<String, String>> attr2value) {
- XmlAttribute firstAttribute = ArrayUtil.getFirstElement(tag.getAttributes());
- XmlElementFactory factory = XmlElementFactory.getInstance(tag.getProject());
- for (Pair<String, String> pair : attr2value) {
- XmlAttribute xmlAttribute = factory.createXmlAttribute(pair.first, "");
- if (firstAttribute != null) {
- tag.addBefore(xmlAttribute, firstAttribute);
- }
- else {
- tag.add(xmlAttribute);
- }
- }
- }
-
- @Nullable
- private static ZenCodingGenerator findApplicableDefaultGenerator(@NotNull PsiElement context, boolean wrapping) {
- for (ZenCodingGenerator generator : ZenCodingGenerator.getInstances()) {
- if (generator.isMyContext(context, wrapping) && generator.isAppliedByDefault(context)) {
- return generator;
- }
- }
- return null;
- }
-
- @NotNull
- private static XmlFile parseXmlFileInTemplate(String templateString, CustomTemplateCallback callback, boolean createPhysicalFile) {
- XmlFile xmlFile = (XmlFile)PsiFileFactory.getInstance(callback.getProject())
- .createFileFromText("dummy.xml", StdFileTypes.XML, templateString, LocalTimeCounter.currentTime(), createPhysicalFile);
- VirtualFile vFile = xmlFile.getVirtualFile();
- if (vFile != null) {
- vFile.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE);
- }
- return xmlFile;
- }
-
- @Nullable
- private static ZenCodingNode parse(@NotNull String text, @NotNull CustomTemplateCallback callback, ZenCodingGenerator generator) {
- List<ZenCodingToken> tokens = lex(text);
- if (tokens == null) {
- return null;
- }
- if (generator != null && !validate(tokens, generator)) {
- return null;
- }
- MyParser parser = new MyParser(tokens, callback, generator);
- ZenCodingNode node = parser.parse();
- if (parser.myIndex != tokens.size() || node instanceof TextNode) {
- return null;
- }
- return node;
- }
-
- private static boolean validate(@NotNull List<ZenCodingToken> tokens, @NotNull ZenCodingGenerator generator) {
- for (ZenCodingToken token : tokens) {
- if (token instanceof TextToken && !(generator instanceof XmlZenCodingGenerator)) {
- return false;
- }
- }
- return true;
- }
-
- @Nullable
- private static List<ZenCodingToken> lex(@NotNull String text) {
- text += MARKER;
- final List<ZenCodingToken> result = new ArrayList<ZenCodingToken>();
-
- boolean inQuotes = false;
- boolean inApostrophes = false;
- int bracesStack = 0;
-
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < text.length(); i++) {
- final char c = text.charAt(i);
-
- if (inQuotes) {
- builder.append(c);
- if (c == '"') {
- inQuotes = false;
- result.add(new StringLiteralToken(builder.toString()));
- builder = new StringBuilder();
- }
- continue;
- }
-
- if (inApostrophes) {
- builder.append(c);
- if (c == '\'') {
- inApostrophes = false;
- result.add(new StringLiteralToken(builder.toString()));
- builder = new StringBuilder();
- }
- continue;
- }
-
- if (bracesStack > 0) {
- builder.append(c);
- if (c == '}') {
- bracesStack--;
- if (bracesStack == 0) {
- result.add(new TextToken(builder.toString()));
- builder = new StringBuilder();
- }
- }
- else if (c == '{') {
- bracesStack++;
- }
- continue;
- }
-
- if (DELIMS.indexOf(c) < 0) {
- builder.append(c);
- }
- else {
- // handle special case: ul+ template
- if (c == '+' && (i == text.length() - 2 || text.charAt(i + 1) == ')')) {
- builder.append(c);
- continue;
- }
-
- if (builder.length() > 0) {
- final String tokenText = builder.toString();
- final int n = parseNonNegativeInt(tokenText);
- if (n >= 0) {
- result.add(new NumberToken(n));
- }
- else {
- result.add(new IdentifierToken(tokenText));
- }
- builder = new StringBuilder();
- }
- if (c == '"') {
- inQuotes = true;
- builder.append(c);
- }
- else if (c == '\'') {
- inApostrophes = true;
- builder.append(c);
- }
- else if (c == '{') {
- bracesStack = 1;
- builder.append(c);
- }
- else if (c == '(') {
- result.add(ZenCodingTokens.OPENING_R_BRACKET);
- }
- else if (c == ')') {
- result.add(ZenCodingTokens.CLOSING_R_BRACKET);
- }
- else if (c == '[') {
- result.add(ZenCodingTokens.OPENING_SQ_BRACKET);
- }
- else if (c == ']') {
- result.add(ZenCodingTokens.CLOSING_SQ_BRACKET);
- }
- else if (c == '=') {
- result.add(ZenCodingTokens.EQ);
- }
- else if (c == '.') {
- result.add(ZenCodingTokens.DOT);
- }
- else if (c == '#') {
- result.add(ZenCodingTokens.SHARP);
- }
- else if (c == ',') {
- result.add(ZenCodingTokens.COMMA);
- }
- else if (c == ' ') {
- result.add(ZenCodingTokens.SPACE);
- }
- else if (c == '|') {
- result.add(ZenCodingTokens.PIPE);
- }
- else if (c != MARKER) {
- result.add(new OperationToken(c));
- }
- }
- }
- if (bracesStack != 0 || inQuotes || inApostrophes) {
- return null;
- }
- return result;
- }
-
-
- public static boolean checkTemplateKey(@NotNull String key, CustomTemplateCallback callback, ZenCodingGenerator generator) {
- return parse(key, callback, generator) != null;
- }
-
- public void expand(String key, @NotNull CustomTemplateCallback callback) {
- ZenCodingGenerator defaultGenerator = findApplicableDefaultGenerator(callback.getContext(), false);
- assert defaultGenerator != null;
- expand(key, callback, null, defaultGenerator);
- }
-
- @Nullable
- private static ZenCodingGenerator findApplicableGenerator(ZenCodingNode node, PsiElement context, boolean wrapping) {
- ZenCodingGenerator defaultGenerator = null;
- List<ZenCodingGenerator> generators = ZenCodingGenerator.getInstances();
- for (ZenCodingGenerator generator : generators) {
- if (defaultGenerator == null && generator.isMyContext(context, wrapping) && generator.isAppliedByDefault(context)) {
- defaultGenerator = generator;
- }
- }
- while (node instanceof FilterNode) {
- FilterNode filterNode = (FilterNode)node;
- String suffix = filterNode.getFilter();
- for (ZenCodingGenerator generator : generators) {
- if (generator.isMyContext(context, wrapping)) {
- if (suffix != null && suffix.equals(generator.getSuffix())) {
- return generator;
- }
- }
- }
- node = filterNode.getNode();
- }
- return defaultGenerator;
- }
-
- private static List<ZenCodingFilter> getFilters(ZenCodingNode node, PsiElement context) {
- List<ZenCodingFilter> result = new ArrayList<ZenCodingFilter>();
-
- while (node instanceof FilterNode) {
- FilterNode filterNode = (FilterNode)node;
- String filterSuffix = filterNode.getFilter();
- boolean filterFound = false;
- for (ZenCodingFilter filter : ZenCodingFilter.getInstances()) {
- if (filter.isMyContext(context) && filter.getSuffix().equals(filterSuffix)) {
- filterFound = true;
- result.add(filter);
- }
- }
- assert filterFound;
- node = filterNode.getNode();
- }
-
- for (ZenCodingFilter filter : ZenCodingFilter.getInstances()) {
- if (filter.isMyContext(context) && filter.isAppliedByDefault(context)) {
- result.add(filter);
- }
- }
-
- Collections.reverse(result);
- return result;
- }
-
-
- private static void expand(String key,
- @NotNull CustomTemplateCallback callback,
- String surroundedText,
- @NotNull ZenCodingGenerator defaultGenerator) {
- ZenCodingNode node = parse(key, callback, defaultGenerator);
- assert node != null;
- if (surroundedText == null) {
- if (node instanceof TemplateNode) {
- if (key.equals(((TemplateNode)node).getTemplateToken().getKey()) &&
- callback.findApplicableTemplates(key).size() > 1) {
- callback.startTemplate();
- return;
- }
- }
- callback.deleteTemplateKey(key);
- }
-
- PsiElement context = callback.getContext();
- ZenCodingGenerator generator = findApplicableGenerator(node, context, false);
- List<ZenCodingFilter> filters = getFilters(node, context);
-
- expand(node, generator, filters, surroundedText, callback);
- }
-
- private static void expand(ZenCodingNode node,
- ZenCodingGenerator generator,
- List<ZenCodingFilter> filters,
- String surroundedText,
- CustomTemplateCallback callback) {
- if (surroundedText != null) {
- surroundedText = surroundedText.trim();
- }
- List<GenerationNode> genNodes = node.expand(-1, surroundedText, callback, true);
- LiveTemplateBuilder builder = new LiveTemplateBuilder();
- int end = -1;
- for (int i = 0, genNodesSize = genNodes.size(); i < genNodesSize; i++) {
- GenerationNode genNode = genNodes.get(i);
- TemplateImpl template = genNode.generate(callback, generator, filters, true);
- int e = builder.insertTemplate(builder.length(), template, null);
- if (end == -1 && end < builder.length()) {
- end = e;
- }
- }
-
- callback.startTemplate(builder.buildTemplate(), null, new TemplateEditingAdapter() {
- private TextRange myEndVarRange;
- private Editor myEditor;
-
- @Override
- public void beforeTemplateFinished(TemplateState state, Template template) {
- int variableNumber = state.getCurrentVariableNumber();
- if (variableNumber >= 0 && template instanceof TemplateImpl) {
- TemplateImpl t = (TemplateImpl)template;
- while (variableNumber < t.getVariableCount()) {
- String varName = t.getVariableNameAt(variableNumber);
- if (LiveTemplateBuilder.isEndVariable(varName)) {
- myEndVarRange = state.getVariableRange(varName);
- myEditor = state.getEditor();
- break;
- }
- variableNumber++;
- }
- }
- }
-
- @Override
- public void templateFinished(Template template, boolean brokenOff) {
- if (brokenOff && myEndVarRange != null && myEditor != null) {
- int offset = myEndVarRange.getStartOffset();
- if (offset >= 0 && offset != myEditor.getCaretModel().getOffset()) {
- myEditor.getCaretModel().moveToOffset(offset);
- myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
- }
- }
- }
- });
- }
-
- public void wrap(final String selection,
- @NotNull final CustomTemplateCallback callback
- ) {
- InputValidatorEx validator = new InputValidatorEx() {
- public String getErrorText(String inputString) {
- if (!checkTemplateKey(inputString, callback)) {
- return XmlBundle.message("zen.coding.incorrect.abbreviation.error");
- }
- return null;
- }
-
- public boolean checkInput(String inputString) {
- return getErrorText(inputString) == null;
- }
-
- public boolean canClose(String inputString) {
- return checkInput(inputString);
- }
- };
- final String abbreviation = Messages
- .showInputDialog(callback.getProject(), XmlBundle.message("zen.coding.enter.abbreviation.dialog.label"),
- XmlBundle.message("zen.coding.title"), Messages.getQuestionIcon(), "", validator);
- if (abbreviation != null) {
- doWrap(selection, abbreviation, callback);
- }
- }
-
- public static boolean checkTemplateKey(String inputString, CustomTemplateCallback callback) {
- ZenCodingGenerator generator = findApplicableDefaultGenerator(callback.getContext(), true);
- assert generator != null;
- return checkTemplateKey(inputString, callback, generator);
- }
-
- public boolean isApplicable(PsiFile file, int offset, boolean wrapping) {
- WebEditorOptions webEditorOptions = WebEditorOptions.getInstance();
- if (!webEditorOptions.isZenCodingEnabled()) {
- return false;
- }
- if (file == null) {
- return false;
- }
- PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments();
- PsiElement element = CustomTemplateCallback.getContext(file, offset);
- return findApplicableDefaultGenerator(element, wrapping) != null;
- }
-
- protected static void doWrap(final String selection,
- final String abbreviation,
- final CustomTemplateCallback callback) {
- final ZenCodingGenerator defaultGenerator = findApplicableDefaultGenerator(callback.getContext(), true);
- assert defaultGenerator != null;
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- public void run() {
- CommandProcessor.getInstance().executeCommand(callback.getProject(), new Runnable() {
- public void run() {
- callback.fixInitialState(true);
- ZenCodingNode node = parse(abbreviation, callback, defaultGenerator);
- assert node != null;
- PsiElement context = callback.getContext();
- ZenCodingGenerator generator = findApplicableGenerator(node, context, true);
- List<ZenCodingFilter> filters = getFilters(node, context);
-
- EditorModificationUtil.deleteSelectedText(callback.getEditor());
- PsiDocumentManager.getInstance(callback.getProject()).commitAllDocuments();
-
- expand(node, generator, filters, selection, callback);
- }
- }, CodeInsightBundle.message("insert.code.template.command"), null);
- }
- });
- }
-
- @NotNull
- public String getTitle() {
- return XmlBundle.message("zen.coding.title");
- }
-
- public char getShortcut() {
- return (char)WebEditorOptions.getInstance().getZenCodingExpandShortcut();
- }
-
- protected static boolean containsAttrsVar(TemplateImpl template) {
- for (int i = 0; i < template.getVariableCount(); i++) {
- String varName = template.getVariableNameAt(i);
- if (ATTRS.equals(varName)) {
- return true;
- }
- }
- return false;
- }
-
- public String computeTemplateKey(@NotNull CustomTemplateCallback callback) {
- ZenCodingGenerator generator = findApplicableDefaultGenerator(callback.getContext(), false);
- if (generator == null) return null;
- return generator.computeTemplateKey(callback);
- }
-
- public boolean supportsWrapping() {
- return true;
- }
-
- public static boolean checkFilterSuffix(@NotNull String suffix) {
- for (ZenCodingGenerator generator : ZenCodingGenerator.getInstances()) {
- if (suffix.equals(generator.getSuffix())) {
- return true;
- }
- }
- for (ZenCodingFilter filter : ZenCodingFilter.getInstances()) {
- if (suffix.equals(filter.getSuffix())) {
- return true;
- }
- }
- return false;
- }
-
- public static boolean doSetTemplate(final TemplateToken token, TemplateImpl template, CustomTemplateCallback callback) {
- token.setTemplate(template);
- final XmlFile xmlFile = parseXmlFileInTemplate(template.getString(), callback, true);
- token.setFile(xmlFile);
- XmlDocument document = xmlFile.getDocument();
- final XmlTag tag = document != null ? document.getRootTag() : null;
- if (token.getAttribute2Value().size() > 0 && tag == null) {
- return false;
- }
- if (tag != null) {
- if (!containsAttrsVar(template) && token.getAttribute2Value().size() > 0) {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- public void run() {
- addMissingAttributes(tag, token.getAttribute2Value());
- }
- });
- }
- }
- return true;
- }
-
- private static class MyParser {
- private final List<ZenCodingToken> myTokens;
- private final CustomTemplateCallback myCallback;
- private final ZenCodingGenerator myGenerator;
- private int myIndex = 0;
-
- private MyParser(List<ZenCodingToken> tokens, CustomTemplateCallback callback, ZenCodingGenerator generator) {
- myTokens = tokens;
- myCallback = callback;
- myGenerator = generator;
- }
-
- @Nullable
- private ZenCodingNode parse() {
- ZenCodingNode add = parseAddOrMore();
- if (add == null) {
- return null;
- }
-
- ZenCodingNode result = add;
-
- while (true) {
- ZenCodingToken token = nextToken();
- if (token != ZenCodingTokens.PIPE) {
- return result;
- }
-
- myIndex++;
- token = nextToken();
- if (!(token instanceof IdentifierToken)) {
- return null;
- }
-
- final String filterSuffix = ((IdentifierToken)token).getText();
- if (!checkFilterSuffix(filterSuffix)) {
- return null;
- }
-
- myIndex++;
- result = new FilterNode(result, filterSuffix);
- }
- }
-
- @Nullable
- private ZenCodingNode parseAddOrMore() {
- ZenCodingNode mul = parseMul();
- if (mul == null) {
- return null;
- }
- ZenCodingToken operationToken = nextToken();
- if (!(operationToken instanceof OperationToken)) {
- return mul;
- }
- char sign = ((OperationToken)operationToken).getSign();
- if (sign == '+') {
- myIndex++;
- ZenCodingNode add2 = parseAddOrMore();
- if (add2 == null) {
- return null;
- }
- return new AddOperationNode(mul, add2);
- }
- else if (sign == '>') {
- myIndex++;
- ZenCodingNode more2 = parseAddOrMore();
- if (more2 == null) {
- return null;
- }
- return new MoreOperationNode(mul, more2);
- }
- return null;
- }
-
- @Nullable
- private ZenCodingNode parseMul() {
- ZenCodingNode exp = parseExpressionInBraces();
- if (exp == null) {
- return null;
- }
- ZenCodingToken operationToken = nextToken();
- if (!(operationToken instanceof OperationToken)) {
- return exp;
- }
- if (((OperationToken)operationToken).getSign() != '*') {
- return exp;
- }
- myIndex++;
- ZenCodingToken numberToken = nextToken();
- if (numberToken instanceof NumberToken) {
- myIndex++;
- return new MulOperationNode(exp, ((NumberToken)numberToken).getNumber());
- }
- return new UnaryMulOperationNode(exp);
- }
-
- @Nullable
- private ZenCodingNode parseExpressionInBraces() {
- ZenCodingToken token = nextToken();
- if (token == ZenCodingTokens.OPENING_R_BRACKET) {
- myIndex++;
- ZenCodingNode add = parseAddOrMore();
- if (add == null) {
- return null;
- }
- ZenCodingToken closingBrace = nextToken();
- if (closingBrace != ZenCodingTokens.CLOSING_R_BRACKET) {
- return null;
- }
- myIndex++;
- return add;
- }
- else if (token instanceof TextToken) {
- myIndex++;
- return new TextNode((TextToken)token);
-
- }
-
- final ZenCodingNode templateNode = parseTemplate();
- if (templateNode == null) {
- return null;
- }
-
- token = nextToken();
- if (token instanceof TextToken) {
- myIndex++;
- return new MoreOperationNode(templateNode, new TextNode((TextToken)token));
- }
- return templateNode;
- }
-
- @Nullable
- private ZenCodingNode parseTemplate() {
- final ZenCodingToken token = nextToken();
- String templateKey = isHtml(myCallback) ? DEFAULT_TAG : null;
- boolean mustHaveSelector = true;
-
- if (token instanceof IdentifierToken) {
- templateKey = ((IdentifierToken)token).getText();
- mustHaveSelector = false;
- myIndex++;
- }
-
- if (templateKey == null) {
- return null;
- }
-
- final TemplateImpl template = myCallback.findApplicableTemplate(templateKey);
- if (template == null && !isXML11ValidQName(templateKey)) {
- return null;
- }
-
- final List<Pair<String, String>> attrList = parseSelectors();
- if (mustHaveSelector && attrList.size() == 0) {
- return null;
- }
-
- final TemplateToken templateToken = new TemplateToken(templateKey, attrList);
-
- if (!setTemplate(templateToken, template)) {
- return null;
- }
- return new TemplateNode(templateToken);
- }
-
- @SuppressWarnings("unchecked")
- @NotNull
- private List<Pair<String, String>> parseSelectors() {
- final List<Pair<String, String>> result = new ArrayList<Pair<String, String>>();
-
- int classAttrPosition = -1;
- int idAttrPosition = -1;
-
- final StringBuilder classAttrBuilder = new StringBuilder();
- final StringBuilder idAttrBuilder = new StringBuilder();
-
- while (true) {
- final List<Pair<String, String>> attrList = parseSelector();
- if (attrList == null) {
- if (classAttrPosition != -1) {
- result.set(classAttrPosition, new Pair<String, String>(CLASS, classAttrBuilder.toString()));
- }
- if (idAttrPosition != -1) {
- result.set(idAttrPosition, new Pair<String, String>(ID, idAttrBuilder.toString()));
- }
- return result;
- }
-
- for (Pair<String, String> attr : attrList) {
- if (CLASS.equals(attr.first)) {
- if (classAttrBuilder.length() > 0) {
- classAttrBuilder.append(' ');
- }
- classAttrBuilder.append(attr.second);
- if (classAttrPosition == -1) {
- classAttrPosition = result.size();
- result.add(attr);
- }
- }
- else if (ID.equals(attr.first)) {
- if (idAttrBuilder.length() > 0) {
- idAttrBuilder.append(' ');
- }
- idAttrBuilder.append(attr.second);
- if (idAttrPosition == -1) {
- idAttrPosition = result.size();
- result.add(attr);
- }
- }
- else {
- result.add(attr);
- }
- }
- }
- }
-
- @Nullable
- private List<Pair<String, String>> parseSelector() {
- ZenCodingToken token = nextToken();
- if (token == ZenCodingTokens.OPENING_SQ_BRACKET) {
- myIndex++;
- final List<Pair<String, String>> attrList = parseAttributeList();
- if (attrList == null || nextToken() != ZenCodingTokens.CLOSING_SQ_BRACKET) {
- return null;
- }
- myIndex++;
- return attrList;
- }
-
- if (token == ZenCodingTokens.DOT || token == ZenCodingTokens.SHARP) {
- final String name = token == ZenCodingTokens.DOT ? CLASS : ID;
- myIndex++;
- token = nextToken();
- final String value = getAttributeValueByToken(token);
- myIndex++;
- return value != null ? Collections.singletonList(new Pair<String, String>(name, value)) : null;
- }
-
- return null;
- }
-
- private boolean setTemplate(final TemplateToken token, TemplateImpl template) {
- if (template == null) {
- template = myGenerator.createTemplateByKey(token.getKey());
- }
- if (template == null) {
- return false;
- }
- return doSetTemplate(token, template, myCallback);
- }
-
- @Nullable
- private List<Pair<String, String>> parseAttributeList() {
- final List<Pair<String, String>> result = new ArrayList<Pair<String, String>>();
- while (true) {
- final Pair<String, String> attribute = parseAttribute();
- if (attribute == null) {
- return result;
- }
- result.add(attribute);
-
- final ZenCodingToken token = nextToken();
- if (token != ZenCodingTokens.COMMA && token != ZenCodingTokens.SPACE) {
- return result;
- }
- myIndex++;
- }
- }
-
- @Nullable
- private Pair<String, String> parseAttribute() {
- ZenCodingToken token = nextToken();
- if (!(token instanceof IdentifierToken)) {
- return null;
- }
-
- final String name = ((IdentifierToken)token).getText();
-
- myIndex++;
- token = nextToken();
- if (token != ZenCodingTokens.EQ) {
- return new Pair<String, String>(name, "");
- }
-
- myIndex++;
- final StringBuilder attrValueBuilder = new StringBuilder();
- String value;
- do {
- token = nextToken();
- value = token != null && token == ZenCodingTokens.SHARP ? token.toString() : getAttributeValueByToken(token);
- if (value != null) {
- attrValueBuilder.append(value);
- myIndex++;
- }
- }
- while (value != null);
- return new Pair<String, String>(name, attrValueBuilder.toString());
- }
-
- @Nullable
- private static String getAttributeValueByToken(ZenCodingToken token) {
- if (token instanceof StringLiteralToken) {
- final String text = ((StringLiteralToken)token).getText();
- return text.substring(1, text.length() - 1);
- }
- else if (token instanceof IdentifierToken) {
- return ((IdentifierToken)token).getText();
- }
- else if (token instanceof NumberToken) {
- return Integer.toString(((NumberToken)token).getNumber());
- }
- return null;
- }
-
- @Nullable
- private ZenCodingToken nextToken() {
- if (myIndex < myTokens.size()) {
- return myTokens.get(myIndex);
- }
- return null;
- }
-
- }
-}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingUtil.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingUtil.java
deleted file mode 100644
index a72a2ed..0000000
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingUtil.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2000-2010 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.codeInsight.template.zencoding;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * @author Eugene.Kudelevsky
- */
-public class ZenCodingUtil {
- private static final char NUMBER_IN_ITERATION_PLACE_HOLDER = '$';
- private static final String SURROUNDED_TEXT_MARKER = "$#";
-
- private ZenCodingUtil() {
- }
-
- public static boolean containsSurroundedTextMarker(@NotNull String s) {
- return s.contains(SURROUNDED_TEXT_MARKER);
- }
-
- public static String replaceMarkers(String s, int numberInIteration, @Nullable String surroundedText) {
- final String by = Integer.toString(numberInIteration + 1);
- StringBuilder builder = new StringBuilder(s.length());
- int j = -1;
- int i = 0;
- int n = s.length();
- while (i <= n) {
- char c = i < n ? s.charAt(i) : 0;
- if (c == NUMBER_IN_ITERATION_PLACE_HOLDER && (i == n - 1 || s.charAt(i + 1) != '#')) {
- if (j == -1) {
- j = i;
- }
- }
- else {
- if (j != -1) {
- for (int k = 0, m = i - j - by.length(); k < m; k++) {
- builder.append('0');
- }
- builder.append(by);
- j = -1;
- }
- if (i < n) {
- if (c == NUMBER_IN_ITERATION_PLACE_HOLDER && surroundedText != null) {
- builder.append(surroundedText);
- i++;
- }
- else {
- builder.append(c);
- }
- }
- }
- i++;
- }
- return builder.toString();
- }
-
- public static String getValue(String value, int numberInIteration, String surroundedText) {
- String s = replaceMarkers(value, numberInIteration, surroundedText);
- return s.replace("\"", """);
- }
-}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/TrimZenCodingFilter.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/TrimZenCodingFilter.java
deleted file mode 100644
index f6c26f1..0000000
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/filters/TrimZenCodingFilter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.intellij.codeInsight.template.zencoding.filters;
-
-import com.intellij.codeInsight.template.zencoding.nodes.GenerationNode;
-import com.intellij.lang.xml.XMLLanguage;
-import com.intellij.psi.PsiElement;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.regex.Pattern;
-
-/**
- * @author Eugene.Kudelevsky
- */
-public class TrimZenCodingFilter extends ZenCodingFilter {
- private static final Pattern PATTERN = Pattern.compile("^([\\s|\u00a0])?[\\d|#|\\-|\\*|\u2022]+\\.?\\s*");
-
- @NotNull
- @Override
- public String getSuffix() {
- return "t";
- }
-
- @Override
- public boolean isMyContext(@NotNull PsiElement context) {
- return context.getLanguage() instanceof XMLLanguage;
- }
-
- @NotNull
- @Override
- public GenerationNode filterNode(@NotNull GenerationNode node) {
- doFilter(node);
- return node;
- }
-
- private static void doFilter(GenerationNode node) {
- final String surroundedText = node.getSurroundedText();
- if (surroundedText != null) {
- node.setSurroundedText(PATTERN.matcher(surroundedText).replaceAll(""));
- }
- for (GenerationNode child : node.getChildren()) {
- doFilter(child);
- }
- }
-}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/TemplateNode.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/TemplateNode.java
deleted file mode 100644
index 7a9040d..0000000
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/nodes/TemplateNode.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2000-2010 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.codeInsight.template.zencoding.nodes;
-
-import com.intellij.codeInsight.template.CustomTemplateCallback;
-import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * @author Eugene.Kudelevsky
- */
-public class TemplateNode extends ZenCodingNode {
- private final TemplateToken myTemplateToken;
-
- public TemplateNode(TemplateToken templateToken) {
- myTemplateToken = templateToken;
- }
-
- public TemplateToken getTemplateToken() {
- return myTemplateToken;
- }
-
- @NotNull
- @Override
- public List<GenerationNode> expand(int numberInIteration,
- String surroundedText,
- CustomTemplateCallback callback,
- boolean insertSurroundedTextAtTheEnd) {
- GenerationNode node =
- new GenerationNode(myTemplateToken, new ArrayList<GenerationNode>(), numberInIteration, surroundedText, insertSurroundedTextAtTheEnd);
- return Arrays.asList(node);
- }
-}
diff --git a/xml/impl/src/com/intellij/html/index/Html5CustomAttributesIndex.java b/xml/impl/src/com/intellij/html/index/Html5CustomAttributesIndex.java
index 152018c..8669b60 100644
--- a/xml/impl/src/com/intellij/html/index/Html5CustomAttributesIndex.java
+++ b/xml/impl/src/com/intellij/html/index/Html5CustomAttributesIndex.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2010 JetBrains s.r.o.
+ * 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.
@@ -21,6 +21,7 @@
import com.intellij.lexer.HtmlHighlightingLexer;
import com.intellij.lexer.Lexer;
import com.intellij.lexer.XHtmlHighlightingLexer;
+import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.vfs.LocalFileSystem;
@@ -78,8 +79,12 @@
private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() {
@Override
public boolean acceptInput(final VirtualFile file) {
- return (file.getFileSystem() == LocalFileSystem.getInstance() || file.getFileSystem() instanceof TempFileSystem) &&
- file.getFileType() == StdFileTypes.HTML || file.getFileType() == StdFileTypes.XHTML;
+ if (file.getFileSystem() != LocalFileSystem.getInstance() && !(file.getFileSystem() instanceof TempFileSystem)) {
+ return false;
+ }
+
+ final FileType fileType = file.getFileType();
+ return fileType == StdFileTypes.HTML || fileType == StdFileTypes.XHTML;
}
};
diff --git a/xml/impl/src/com/intellij/ide/browsers/BrowsersConfiguration.java b/xml/impl/src/com/intellij/ide/browsers/BrowsersConfiguration.java
index 4702fe6..64729f6 100644
--- a/xml/impl/src/com/intellij/ide/browsers/BrowsersConfiguration.java
+++ b/xml/impl/src/com/intellij/ide/browsers/BrowsersConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * 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.
@@ -22,7 +22,6 @@
import com.intellij.ide.browsers.impl.DefaultUrlOpener;
import com.intellij.openapi.components.*;
import com.intellij.openapi.util.Condition;
-import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.WindowsRegistryUtil;
import com.intellij.util.containers.HashMap;
@@ -205,23 +204,29 @@
}
}
+ /** @deprecated use {@link DefaultUrlOpener} (to remove in IDEA 13) */
+ @SuppressWarnings("unused")
public static void launchBrowser(final @NotNull BrowserFamily family, @NotNull final String url, String... parameters) {
- launchBrowser(family, url, false, parameters);
+ DefaultUrlOpener.launchBrowser(family, url, false, parameters);
}
+ /** @deprecated use {@link DefaultUrlOpener} (to remove in IDEA 13) */
+ @SuppressWarnings("unused")
public static void launchBrowser(final @NotNull BrowserFamily family,
@Nullable final String url,
final boolean forceOpenNewInstanceOnMac,
String... parameters) {
- DefaultUrlOpener.launchBrowser(family, url, parameters, Conditions.<String>alwaysTrue(), forceOpenNewInstanceOnMac);
+ DefaultUrlOpener.launchBrowser(family, url, forceOpenNewInstanceOnMac, parameters);
}
+ /** @deprecated use {@link DefaultUrlOpener} (to remove in IDEA 13) */
+ @SuppressWarnings("unused")
public static void launchBrowser(final @NotNull BrowserFamily family,
@NotNull final String url,
final boolean forceOpenNewInstanceOnMac,
final Condition<String> browserSpecificParametersFilter,
String... parameters) {
- DefaultUrlOpener.launchBrowser(family, url, parameters, browserSpecificParametersFilter, forceOpenNewInstanceOnMac);
+ DefaultUrlOpener.launchBrowser(family, url, forceOpenNewInstanceOnMac, parameters);
}
@Nullable
diff --git a/xml/impl/src/com/intellij/ide/browsers/impl/DefaultUrlOpener.java b/xml/impl/src/com/intellij/ide/browsers/impl/DefaultUrlOpener.java
index fc50f4d..ae37d74 100644
--- a/xml/impl/src/com/intellij/ide/browsers/impl/DefaultUrlOpener.java
+++ b/xml/impl/src/com/intellij/ide/browsers/impl/DefaultUrlOpener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * 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.
@@ -15,6 +15,8 @@
*/
package com.intellij.ide.browsers.impl;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.util.ExecUtil;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.browsers.BrowserSpecificSettings;
@@ -24,17 +26,14 @@
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Condition;
-import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
-import com.intellij.util.containers.ContainerUtil;
import com.intellij.xml.XmlBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.IOException;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -43,71 +42,63 @@
@Override
public boolean openUrl(BrowsersConfiguration.BrowserFamily family, @Nullable String url) {
- return launchBrowser(family, url, ArrayUtil.EMPTY_STRING_ARRAY, Conditions.<String>alwaysTrue(), false);
+ return launchBrowser(family, url, false);
}
- public static boolean launchBrowser(final BrowsersConfiguration.BrowserFamily family,
+ /** @deprecated use {@linkplain #launchBrowser(BrowsersConfiguration.BrowserFamily, String, boolean, String...)} (to remove in IDEA 13) */
+ @SuppressWarnings("unused")
+ public static boolean launchBrowser(@NotNull BrowsersConfiguration.BrowserFamily family,
@Nullable String url,
@NotNull String[] additionalParameters,
@NotNull Condition<String> browserSpecificParametersFilter,
- final boolean forceOpenNewInstanceOnMac) {
- final WebBrowserSettings settings = BrowsersConfiguration.getInstance().getBrowserSettings(family);
- final String path = settings.getPath();
+ boolean newWindowIfPossible) {
+ return launchBrowser(family, url, newWindowIfPossible, additionalParameters);
+ }
+
+ public static boolean launchBrowser(@NotNull BrowsersConfiguration.BrowserFamily family,
+ @Nullable String url,
+ boolean newWindowIfPossible,
+ @NotNull String... additionalParameters) {
+ WebBrowserSettings settings = BrowsersConfiguration.getInstance().getBrowserSettings(family);
+ String path = settings.getPath();
if (StringUtil.isEmpty(path)) {
- Messages.showErrorDialog(XmlBundle.message("browser.path.not.specified", family.getName()), XmlBundle.message("browser.path.not.specified.title"));
+ String message = XmlBundle.message("browser.path.not.specified", family.getName());
+ Messages.showErrorDialog(message, XmlBundle.message("browser.path.not.specified.title"));
return false;
}
+ List<String> command = BrowserUtil.getOpenBrowserCommand(path, newWindowIfPossible);
+ if (url != null) {
+ command.add(url);
+ }
+ addArgs(command, settings.getBrowserSpecificSettings(), additionalParameters);
+
try {
- BrowserSpecificSettings specificSettings = settings.getBrowserSpecificSettings();
- List<String> parameters = specificSettings == null
- ? (additionalParameters.length == 0 ? Collections.<String>emptyList() : new ArrayList<String>())
- : ContainerUtil.findAll(specificSettings.getAdditionalParameters(), browserSpecificParametersFilter);
- Collections.addAll(parameters, additionalParameters);
- doLaunchBrowser(path, url == null ? null : BrowserUtil.escapeUrl(url), forceOpenNewInstanceOnMac, parameters);
+ new GeneralCommandLine(command).createProcess();
return true;
}
- catch (IOException e) {
+ catch (ExecutionException e) {
Messages.showErrorDialog(e.getMessage(), XmlBundle.message("browser.error"));
return false;
}
}
- private static void doLaunchBrowser(String browserPath, @Nullable String url, boolean forceOpenNewInstanceOnMac, List<String> browserArgs)
- throws IOException {
- List<String> command = BrowserUtil.getOpenBrowserCommand(browserPath);
- addArgs(command, browserArgs, url, forceOpenNewInstanceOnMac);
- if (LOG.isDebugEnabled()) {
- LOG.debug("Launching browser: " + StringUtil.join(command, " "));
- }
- new ProcessBuilder(command).start();
- }
+ private static void addArgs(List<String> command, @Nullable BrowserSpecificSettings settings, String[] additional) {
+ String[] specific = settings != null ? settings.getAdditionalParameters() : ArrayUtil.EMPTY_STRING_ARRAY;
- private static void addArgs(List<String> command, List<String> browserArgs, @Nullable String url, boolean forceOpenNewInstanceOnMac) {
- if (SystemInfo.isMac && ExecUtil.getOpenCommandPath().equals(command.get(0))) {
- if (forceOpenNewInstanceOnMac) {
- command.add("-n");
- }
- if (url != null) {
- command.add(url);
- }
-
- if (!browserArgs.isEmpty()) {
- if (BrowserUtil.isOpenCommandSupportArgs()) {
- command.add("--args");
- command.addAll(browserArgs);
+ if (specific.length + additional.length > 0) {
+ if (SystemInfo.isMac && ExecUtil.getOpenCommandPath().equals(command.get(0))) {
+ if (!BrowserUtil.isOpenCommandSupportArgs()) {
+ LOG.warn("'open' command doesn't allow to pass command line arguments so they will be ignored: " +
+ Arrays.toString(specific) + " " + Arrays.toString(additional));
}
else {
- LOG.warn(
- "'open' command doesn't allow to pass command line arguments so they will be ignored: " + StringUtil.join(browserArgs, " "));
+ command.add("--args");
}
}
- }
- else {
- if (url != null) {
- command.add(url);
- }
- command.addAll(browserArgs);
+
+ Collections.addAll(command, specific);
+ Collections.addAll(command, additional);
}
}
}
diff --git a/xml/impl/src/com/intellij/ide/structureView/impl/xml/XmlTagTreeElement.java b/xml/impl/src/com/intellij/ide/structureView/impl/xml/XmlTagTreeElement.java
index 86a6350..a2ecfd7 100644
--- a/xml/impl/src/com/intellij/ide/structureView/impl/xml/XmlTagTreeElement.java
+++ b/xml/impl/src/com/intellij/ide/structureView/impl/xml/XmlTagTreeElement.java
@@ -39,6 +39,7 @@
public String getPresentableText() {
final XmlTag element = getElement();
+ if (element == null) return "*invalid*";
String id = element.getAttributeValue(ID_ATTR_NAME);
if (id == null) id = element.getAttributeValue(NAME_ATTR_NAME);
id = toCanonicalForm(id);
@@ -88,7 +89,7 @@
}
@Nullable
- protected static String toCanonicalForm(@Nullable String id) {
+ public static String toCanonicalForm(@Nullable String id) {
if (id != null) {
id = id.trim();
if (id.length() == 0) id = null;
diff --git a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java
index 9e53742..de54dc3 100644
--- a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java
+++ b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java
@@ -381,7 +381,7 @@
Element child = element.getChild(HTML_DEFAULT_DOCTYPE_ELEMENT);
if (child != null) {
String text = child.getText();
- if (FileUtil.toSystemIndependentName(text).endsWith("idea.jar!/resources/html5-schema/html5.rnc")) {
+ if (FileUtil.toSystemIndependentName(text).endsWith(".jar!/resources/html5-schema/html5.rnc")) {
text = HTML5_DOCTYPE_ELEMENT;
}
myDefaultHtmlDoctype = text;
diff --git a/xml/impl/src/com/intellij/javaee/MapExternalResourceDialog.java b/xml/impl/src/com/intellij/javaee/MapExternalResourceDialog.java
index eae3953..95f196d 100644
--- a/xml/impl/src/com/intellij/javaee/MapExternalResourceDialog.java
+++ b/xml/impl/src/com/intellij/javaee/MapExternalResourceDialog.java
@@ -98,7 +98,7 @@
}
};
searcher.search();
- new ConfigFilesTreeBuilder(mySchemasTree).buildTree(searcher, root);
+ new ConfigFilesTreeBuilder(mySchemasTree).buildTree(root, searcher);
TreeUtil.expandAll(mySchemasTree);
mySchemasTree.setRootVisible(false);
mySchemasTree.setShowsRootHandles(true);
diff --git a/xml/impl/src/com/intellij/lang/html/HtmlStructureViewBuilderProvider.java b/xml/impl/src/com/intellij/lang/html/HtmlStructureViewBuilderProvider.java
deleted file mode 100644
index 19a063d..0000000
--- a/xml/impl/src/com/intellij/lang/html/HtmlStructureViewBuilderProvider.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.intellij.lang.html;
-
-import com.intellij.ide.highlighter.HtmlFileType;
-import com.intellij.ide.structureView.StructureViewBuilder;
-import com.intellij.ide.structureView.StructureViewModel;
-import com.intellij.ide.structureView.StructureViewTreeElement;
-import com.intellij.ide.structureView.TreeBasedStructureViewBuilder;
-import com.intellij.ide.structureView.impl.xml.XmlStructureViewTreeModel;
-import com.intellij.ide.structureView.xml.XmlStructureViewBuilderProvider;
-import com.intellij.ide.util.treeView.smartTree.Sorter;
-import com.intellij.psi.xml.XmlDocument;
-import com.intellij.psi.xml.XmlFile;
-import com.intellij.psi.xml.XmlTag;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class HtmlStructureViewBuilderProvider implements XmlStructureViewBuilderProvider {
- @Nullable
- public StructureViewBuilder createStructureViewBuilder(@NotNull final XmlFile file) {
- if (file.getViewProvider().getVirtualFile().getFileType() != HtmlFileType.INSTANCE) return null;
-
- return new TreeBasedStructureViewBuilder() {
- public boolean isRootNodeShown() {
- return false;
- }
-
- @NotNull
- public StructureViewModel createStructureViewModel() {
- return new XmlStructureViewTreeModel(file) {
- @NotNull
- public Sorter[] getSorters() {
- return Sorter.EMPTY_ARRAY;
- }
-
- @NotNull
- public StructureViewTreeElement getRoot() {
- final XmlDocument document = ((XmlFile)getPsiFile()).getDocument();
- final XmlTag rootTag = document == null ? null : document.getRootTag();
-
- if (rootTag != null && "html".equalsIgnoreCase(rootTag.getLocalName())) {
- final XmlTag[] subTags = rootTag.getSubTags();
- if (subTags.length == 1 &&
- ("head".equalsIgnoreCase(subTags[0].getLocalName()) || "body".equalsIgnoreCase(subTags[0].getLocalName()))) {
- return new HtmlStructureViewElementProvider.HtmlTagTreeElement(subTags[0]);
- }
-
- return new HtmlStructureViewElementProvider.HtmlTagTreeElement(rootTag);
- }
-
- return super.getRoot();
- }
- };
- }
- };
- }
-}
diff --git a/xml/impl/src/com/intellij/lang/html/HtmlStructureViewElementProvider.java b/xml/impl/src/com/intellij/lang/html/HtmlStructureViewElementProvider.java
deleted file mode 100644
index 4be0765..0000000
--- a/xml/impl/src/com/intellij/lang/html/HtmlStructureViewElementProvider.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.intellij.lang.html;
-
-import com.intellij.ide.IdeBundle;
-import com.intellij.ide.highlighter.HtmlFileType;
-import com.intellij.ide.structureView.StructureViewTreeElement;
-import com.intellij.ide.structureView.impl.xml.XmlTagTreeElement;
-import com.intellij.ide.structureView.xml.XmlStructureViewElementProvider;
-import com.intellij.navigation.LocationPresentation;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.psi.xml.XmlTag;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Collections;
-import java.util.List;
-
-public class HtmlStructureViewElementProvider implements XmlStructureViewElementProvider {
-
- private static int MAX_TEXT_LENGTH = 50;
-
- @Nullable
- public StructureViewTreeElement createCustomXmlTagTreeElement(@NotNull final XmlTag tag) {
- if (tag.getContainingFile().getViewProvider().getVirtualFile().getFileType() != HtmlFileType.INSTANCE) return null;
-
- return new HtmlTagTreeElement(tag);
- }
-
- static class HtmlTagTreeElement extends XmlTagTreeElement implements LocationPresentation {
- public HtmlTagTreeElement(final XmlTag tag) {
- super(tag);
- }
-
- public String getPresentableText() {
- final XmlTag tag = getElement();
- if (tag == null) return IdeBundle.message("node.structureview.invalid");
-
- final String id = toCanonicalForm(tag.getAttributeValue("id"));
-
- final String classValue = tag.getAttributeValue("class");
- final List<String> classValues = classValue != null ? StringUtil.split(classValue, " ") : Collections.<String>emptyList();
-
- final StringBuilder text = new StringBuilder(tag.getLocalName());
-
- if (id != null) {
- text.append("#").append(id);
- }
-
- if (!classValues.isEmpty()) {
- text.append('.').append(StringUtil.join(classValues, "."));
- }
-
- return text.toString();
- }
-
- public String getLocationString() {
- final XmlTag tag = getElement();
- if (tag == null) return null;
-
- final String text = normalizeSpaces(tag.getValue().getTrimmedText());
- return text.isEmpty() ? null : shortenTextIfLong(text);
- }
-
- private static String normalizeSpaces(final String text) {
- final StringBuilder buf = new StringBuilder();
-
- for (char ch : text.toCharArray()) {
- if (ch <= ' ' || Character.isSpaceChar(ch)) {
- if (buf.length() == 0 || buf.charAt(buf.length() - 1) != ' ') {
- buf.append(' ');
- }
- }
- else {
- buf.append(ch);
- }
- }
-
- return buf.toString();
- }
-
- private static String shortenTextIfLong(final String text) {
- if (text.length() <= MAX_TEXT_LENGTH) return text;
-
- int index;
- for (index = MAX_TEXT_LENGTH; index > MAX_TEXT_LENGTH - 20; index--) {
- if (!Character.isLetter(text.charAt(index))) {
- break;
- }
- }
-
- final int endIndex = Character.isLetter(index) ? MAX_TEXT_LENGTH : index;
- return text.substring(0, endIndex) + "...";
- }
-
- public String getLocationPrefix() {
- return " ";
- }
-
- public String getLocationSuffix() {
- return "";
- }
- }
-}
diff --git a/xml/impl/src/com/intellij/lang/html/structureView/Html5SectionTreeElement.java b/xml/impl/src/com/intellij/lang/html/structureView/Html5SectionTreeElement.java
new file mode 100644
index 0000000..76b240e
--- /dev/null
+++ b/xml/impl/src/com/intellij/lang/html/structureView/Html5SectionTreeElement.java
@@ -0,0 +1,62 @@
+/*
+ * 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 com.intellij.lang.html.structureView;
+
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.xml.XmlTag;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+class Html5SectionTreeElement extends PsiTreeElementBase<XmlTag> {
+
+ private final Computable<Collection<StructureViewTreeElement>> myChildrenComputable;
+ private final String myHeader;
+
+ public Html5SectionTreeElement(final XmlTag tag, final Computable<Collection<StructureViewTreeElement>> childrenComputable, final @Nullable String header) {
+ super(tag);
+ myChildrenComputable = childrenComputable;
+ myHeader = header;
+ }
+
+ @NotNull
+ public Collection<StructureViewTreeElement> getChildrenBase() {
+ return myChildrenComputable.compute();
+ }
+
+ public String getPresentableText() {
+ if (myHeader != null) {
+ return HtmlTagTreeElement.normalizeSpacesAndShortenIfLong(myHeader);
+ }
+
+ final XmlTag tag = getElement();
+ return tag == null ? null : HtmlTagTreeElement.normalizeSpacesAndShortenIfLong(tag.getValue().getTrimmedText());
+ }
+
+ public String getLocationString() {
+ final XmlTag tag = getElement();
+ if (tag == null) return null;
+
+ return HtmlTagTreeElement.getTagPresentation(tag);
+ }
+
+ public boolean isSearchInLocationString() {
+ return true;
+ }
+}
diff --git a/xml/impl/src/com/intellij/lang/html/structureView/Html5SectionsNodeProvider.java b/xml/impl/src/com/intellij/lang/html/structureView/Html5SectionsNodeProvider.java
new file mode 100644
index 0000000..184ceee
--- /dev/null
+++ b/xml/impl/src/com/intellij/lang/html/structureView/Html5SectionsNodeProvider.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.intellij.lang.html.structureView;
+
+import com.intellij.ide.util.FileStructureNodeProvider;
+import com.intellij.ide.util.treeView.smartTree.ActionPresentation;
+import com.intellij.ide.util.treeView.smartTree.ActionPresentationData;
+import com.intellij.ide.util.treeView.smartTree.TreeElement;
+import com.intellij.openapi.actionSystem.Shortcut;
+import com.intellij.openapi.keymap.KeymapManager;
+import com.intellij.openapi.util.PropertyOwner;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.xml.XmlBundle;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class Html5SectionsNodeProvider implements FileStructureNodeProvider<Html5SectionTreeElement>, PropertyOwner {
+
+ public static final String ACTION_ID = "HTML5_OUTLINE_MODE";
+ public static final String HTML5_OUTLINE_PROVIDER_PROPERTY = "html5.sections.node.provider";
+
+ @NotNull
+ public String getName() {
+ return ACTION_ID;
+ }
+
+ @NotNull
+ public ActionPresentation getPresentation() {
+ return new ActionPresentationData(XmlBundle.message("html5.outline.mode"), null, null);
+ }
+
+ public String getCheckBoxText() {
+ return XmlBundle.message("html5.outline.mode");
+ }
+
+ public Shortcut[] getShortcut() {
+ return KeymapManager.getInstance().getActiveKeymap().getShortcuts("FileStructurePopup");
+ }
+
+ @NotNull
+ public String getPropertyName() {
+ return HTML5_OUTLINE_PROVIDER_PROPERTY;
+ }
+
+ public Collection<Html5SectionTreeElement> provideNodes(final TreeElement node) {
+ if (!(node instanceof HtmlFileTreeElement)) return Collections.emptyList();
+
+ final XmlFile xmlFile = ((HtmlFileTreeElement)node).getElement();
+ final XmlTag rootTag = xmlFile == null ? null : xmlFile.getRootTag();
+ if (rootTag == null) return Collections.emptyList();
+
+ return Html5SectionsProcessor.processAndGetRootSections(rootTag);
+ }
+}
diff --git a/xml/impl/src/com/intellij/lang/html/structureView/Html5SectionsProcessor.java b/xml/impl/src/com/intellij/lang/html/structureView/Html5SectionsProcessor.java
new file mode 100644
index 0000000..0b9f5cb
--- /dev/null
+++ b/xml/impl/src/com/intellij/lang/html/structureView/Html5SectionsProcessor.java
@@ -0,0 +1,289 @@
+/*
+ * 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 com.intellij.lang.html.structureView;
+
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.SortedList;
+import com.intellij.util.containers.Stack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.LinkedList;
+
+
+// Algorithm described at http://www.w3.org/html/wg/drafts/html/master/sections.html#outlines
+// One of implementations: http://hoyois.github.com/html5outliner/ (https://github.com/hoyois/html5outliner)
+class Html5SectionsProcessor {
+
+ private static class SectionHolder {
+ private final XmlTag myTag;
+ private LinkedList<Section> myChildren = new LinkedList<Section>();
+
+ private SectionHolder(final XmlTag tag) {
+ myTag = tag;
+ }
+
+ public void addChildSection(final Section section) {
+ myChildren.add(section);
+ }
+
+ public LinkedList<Section> getChildren() {
+ return myChildren;
+ }
+
+ public XmlTag getTag() {
+ return myTag;
+ }
+ }
+
+ private static class Section extends SectionHolder {
+ private Section myParent = null;
+ private XmlTag myHeader = null;
+
+ public Section(final XmlTag tag) {
+ super(tag);
+ }
+
+ public void addChildSection(final Section section) {
+ section.myParent = this;
+ super.addChildSection(section);
+ }
+
+ public XmlTag getHeader() {
+ return myHeader;
+ }
+
+ public void setHeader(final XmlTag header) {
+ myHeader = header;
+ }
+
+ public Section getParent() {
+ return myParent;
+ }
+ }
+
+ private static final String[] SECTIONING_ROOT_ELEMENTS = {"blockquote", "body", "details", "dialog", "fieldset", "figure", "td"};
+ private static final String[] SECTIONING_CONTENT_ELEMENTS = {"article", "aside", "nav", "section"};
+ private static final String[] HEADER_ELEMENTS = {"h1", "h2", "h3", "h4", "h5", "h6"};
+ private static final String HGROUP_ELEMENT = "hgroup";
+
+ private final Collection<SectionHolder> myRootSectionHolders = new SortedList<SectionHolder>(new Comparator<SectionHolder>() {
+ public int compare(final SectionHolder first, final SectionHolder second) {
+ return first.getTag().getTextRange().getStartOffset() - second.getTag().getTextRange().getStartOffset();
+ }
+ });
+
+ private SectionHolder myCurrentOutlinee = null;
+ private Section myCurrentSection = null;
+ private final Stack<SectionHolder> myStack = new Stack<SectionHolder>();
+
+ public static Collection<Html5SectionTreeElement> processAndGetRootSections(final XmlTag rootTag) {
+ final Html5SectionsProcessor processor = new Html5SectionsProcessor();
+ processRecursively(rootTag, processor);
+ return processor.getRootSections();
+ }
+
+ private static void processRecursively(final XmlTag tag, final Html5SectionsProcessor processor) {
+ if (tag.getAttribute("hidden") != null) return;
+
+ processor.tagEntered(tag);
+
+ if (!isHeader(tag)) {
+ for (final XmlTag subTag : tag.getSubTags()) {
+ processRecursively(subTag, processor);
+ }
+ }
+
+ processor.tagExited(tag);
+ }
+
+ private void tagEntered(final XmlTag tag) {
+ if (isSectioningContentElement(tag) || isSectioningRootElement(tag)) {
+ if (myCurrentOutlinee != null) {
+ myStack.push(myCurrentOutlinee);
+ }
+
+ myCurrentOutlinee = new SectionHolder(tag);
+ myCurrentSection = new Section(tag);
+ myCurrentOutlinee.addChildSection(myCurrentSection);
+ }
+ else if (myCurrentOutlinee == null) {
+ // do nothing
+ }
+ else if (isHeader(tag)) {
+ if (myCurrentSection.getHeader() == null) {
+ myCurrentSection.setHeader(tag);
+ }
+ else if (myCurrentOutlinee.getChildren().getLast().getHeader() == null ||
+ compareHeaderRanks(tag, myCurrentOutlinee.getChildren().getLast().getHeader()) >= 0) {
+ myCurrentSection = new Section(tag);
+ myCurrentSection.setHeader(tag);
+ myCurrentOutlinee.addChildSection(myCurrentSection);
+ }
+ else {
+ Section candidateSection = myCurrentSection;
+ do {
+ if (compareHeaderRanks(tag, candidateSection.getHeader()) < 0) {
+ myCurrentSection = new Section(tag);
+ myCurrentSection.setHeader(tag);
+ candidateSection.addChildSection(myCurrentSection);
+ break;
+ }
+ candidateSection = candidateSection.getParent();
+ }
+ while (true);
+ }
+ //myStack.push(); not needed, because our iterator doesn't enter hidden elements
+ }
+ }
+
+ private void tagExited(final XmlTag tag) {
+ if (!myStack.isEmpty() && myStack.peek().getTag() == tag) {
+ assert false;
+ }
+ else if (!myStack.isEmpty() && isHeader(tag)) {
+ // do nothing
+ }
+ else if (!myStack.isEmpty() && isSectioningContentElement(tag)) {
+ final SectionHolder exitedSectioningContent = myCurrentOutlinee;
+ assert exitedSectioningContent.getTag() == tag;
+
+ myCurrentOutlinee = myStack.pop();
+ myCurrentSection = myCurrentOutlinee.getChildren().getLast();
+
+ for (Section section : exitedSectioningContent.getChildren()) {
+ myCurrentSection.addChildSection(section);
+ }
+ }
+ else if (!myStack.isEmpty() && isSectioningRootElement(tag)) {
+ final SectionHolder exitedSectioningRoot = myCurrentOutlinee;
+ assert exitedSectioningRoot.getTag() == tag;
+ myRootSectionHolders.add(exitedSectioningRoot);
+
+ myCurrentOutlinee = myStack.pop();
+
+ myCurrentSection = myCurrentOutlinee.getChildren().getLast();
+ while (!myCurrentSection.getChildren().isEmpty()) {
+ myCurrentSection = myCurrentSection.getChildren().getLast();
+ }
+ }
+ else if (isSectioningContentElement(tag) || isSectioningRootElement(tag)) {
+ assert myStack.isEmpty();
+
+ assert myCurrentOutlinee.getTag() == tag;
+ myRootSectionHolders.add(myCurrentOutlinee);
+
+ // reset algorithm
+ myCurrentOutlinee = null;
+ myCurrentSection = null;
+ }
+ }
+
+ private Collection<Html5SectionTreeElement> getRootSections() {
+ final Collection<Html5SectionTreeElement> result = new ArrayList<Html5SectionTreeElement>();
+ for (SectionHolder sectionHolder : myRootSectionHolders) {
+ for (Section section : sectionHolder.getChildren()) {
+ result.add(createHtml5SectionTreeElement(section));
+ }
+ }
+ return result;
+ }
+
+ private static Html5SectionTreeElement createHtml5SectionTreeElement(final Section section) {
+ return new Html5SectionTreeElement(section.getTag(),
+ createChildrenComputable(section.getChildren()),
+ getHeaderText(section.getHeader()));
+ }
+
+ private static Computable<Collection<StructureViewTreeElement>> createChildrenComputable(final Collection<Section> children) {
+ return new Computable<Collection<StructureViewTreeElement>>() {
+ public Collection<StructureViewTreeElement> compute() {
+ final Collection<StructureViewTreeElement> result = new ArrayList<StructureViewTreeElement>();
+ for (Section section : children) {
+ result.add(createHtml5SectionTreeElement(section));
+ }
+ return result;
+ }
+ };
+ }
+
+ private static String getHeaderText(final @Nullable XmlTag header) {
+ if (header == null) return null;
+
+ if (HGROUP_ELEMENT.equalsIgnoreCase(header.getLocalName())) {
+ final StringBuilder buf = new StringBuilder();
+ for (XmlTag subTag : header.getSubTags()) {
+ if (ArrayUtil.contains(subTag.getLocalName().toLowerCase(), HEADER_ELEMENTS)) {
+ if (buf.length() > 0) {
+ buf.append(" ");
+ }
+ buf.append(subTag.getValue().getTrimmedText());
+ }
+ }
+
+ return buf.toString();
+ }
+
+ return header.getValue().getTrimmedText();
+ }
+
+ private static boolean isSectioningRootElement(final XmlTag tag) {
+ return ArrayUtil.contains(tag.getLocalName().toLowerCase(), SECTIONING_ROOT_ELEMENTS);
+ }
+
+ private static boolean isSectioningContentElement(final XmlTag tag) {
+ return ArrayUtil.contains(tag.getLocalName().toLowerCase(), SECTIONING_CONTENT_ELEMENTS);
+ }
+
+ private static boolean isHeader(final XmlTag tag) {
+ return ArrayUtil.contains(tag.getLocalName().toLowerCase(), HEADER_ELEMENTS) || HGROUP_ELEMENT.equalsIgnoreCase(tag.getLocalName());
+ }
+
+ private static int compareHeaderRanks(final @NotNull XmlTag header1, final @NotNull XmlTag header2) {
+ return getHeaderRank(header2) - getHeaderRank(header1);
+ }
+
+ private static int getHeaderRank(final XmlTag header) {
+ if (HGROUP_ELEMENT.equalsIgnoreCase(header.getLocalName())) {
+ int minIndex = HEADER_ELEMENTS.length;
+
+ for (XmlTag subTag : header.getSubTags()) {
+ final int index = ArrayUtil.indexOf(HEADER_ELEMENTS, subTag.getLocalName().toLowerCase());
+ if (index < minIndex) {
+ minIndex = index;
+ if (minIndex == 0) break;
+ }
+ }
+
+ if (minIndex == HEADER_ELEMENTS.length) {
+ // no headers is equivalent to <h1>
+ minIndex = 0;
+ }
+
+ return minIndex + 1;
+ }
+
+ final int index = ArrayUtil.indexOf(HEADER_ELEMENTS, header.getLocalName().toLowerCase());
+ if (index < 0) throw new IllegalArgumentException(header.getName());
+ return index + 1;
+ }
+}
diff --git a/xml/impl/src/com/intellij/lang/html/structureView/HtmlFileTreeElement.java b/xml/impl/src/com/intellij/lang/html/structureView/HtmlFileTreeElement.java
new file mode 100644
index 0000000..07a4070
--- /dev/null
+++ b/xml/impl/src/com/intellij/lang/html/structureView/HtmlFileTreeElement.java
@@ -0,0 +1,90 @@
+/*
+ * 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 com.intellij.lang.html.structureView;
+
+import com.intellij.ide.structureView.StructureViewFactoryEx;
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
+import com.intellij.ide.util.FileStructurePopup;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.psi.xml.XmlDocument;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+class HtmlFileTreeElement extends PsiTreeElementBase<XmlFile> {
+
+ private final boolean myInStructureViewPopup;
+
+ public HtmlFileTreeElement(final boolean inStructureViewPopup, final XmlFile xmlFile) {
+ super(xmlFile);
+ myInStructureViewPopup = inStructureViewPopup;
+ }
+
+ @NotNull
+ public Collection<StructureViewTreeElement> getChildrenBase() {
+ if (isHtml5SectionsMode()) {
+ return Collections.emptyList(); // Html5SectionsNodeProvider will return its structure
+ }
+
+ final XmlFile xmlFile = getElement();
+ if (xmlFile == null) return Collections.emptyList();
+
+ final XmlDocument document = xmlFile.getDocument();
+ final XmlTag rootTag = document == null ? null : document.getRootTag();
+
+ if (rootTag == null) return Collections.emptyList();
+
+ if ("html".equalsIgnoreCase(rootTag.getLocalName())) {
+ final XmlTag[] subTags = rootTag.getSubTags();
+ if (subTags.length == 1 &&
+ ("head".equalsIgnoreCase(subTags[0].getLocalName()) || "body".equalsIgnoreCase(subTags[0].getLocalName()))) {
+ return new HtmlTagTreeElement(subTags[0]).getChildrenBase();
+ }
+
+ return new HtmlTagTreeElement(rootTag).getChildrenBase();
+ }
+
+ return Arrays.<StructureViewTreeElement>asList(new HtmlTagTreeElement(rootTag));
+ }
+
+ private boolean isHtml5SectionsMode() {
+ final XmlFile xmlFile = getElement();
+ if (xmlFile == null) return false;
+
+ if (myInStructureViewPopup) {
+ final String propertyName = FileStructurePopup.getPropertyName(Html5SectionsNodeProvider.HTML5_OUTLINE_PROVIDER_PROPERTY);
+ if (PropertiesComponent.getInstance().getBoolean(propertyName, false)) {
+ return true;
+ }
+ }
+ else if (StructureViewFactoryEx.getInstanceEx(xmlFile.getProject()).isActionActive(Html5SectionsNodeProvider.ACTION_ID)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Nullable
+ public String getPresentableText() {
+ return toString(); // root element is not visible
+ }
+}
diff --git a/xml/impl/src/com/intellij/lang/html/structureView/HtmlStructureViewBuilderProvider.java b/xml/impl/src/com/intellij/lang/html/structureView/HtmlStructureViewBuilderProvider.java
new file mode 100644
index 0000000..3243fe7
--- /dev/null
+++ b/xml/impl/src/com/intellij/lang/html/structureView/HtmlStructureViewBuilderProvider.java
@@ -0,0 +1,43 @@
+/*
+ * 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 com.intellij.lang.html.structureView;
+
+import com.intellij.ide.highlighter.HtmlFileType;
+import com.intellij.ide.structureView.StructureViewBuilder;
+import com.intellij.ide.structureView.StructureViewModel;
+import com.intellij.ide.structureView.TreeBasedStructureViewBuilder;
+import com.intellij.ide.structureView.xml.XmlStructureViewBuilderProvider;
+import com.intellij.psi.xml.XmlFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class HtmlStructureViewBuilderProvider implements XmlStructureViewBuilderProvider {
+ @Nullable
+ public StructureViewBuilder createStructureViewBuilder(@NotNull final XmlFile file) {
+ if (file.getViewProvider().getVirtualFile().getFileType() != HtmlFileType.INSTANCE) return null;
+
+ return new TreeBasedStructureViewBuilder() {
+ public boolean isRootNodeShown() {
+ return false;
+ }
+
+ @NotNull
+ public StructureViewModel createStructureViewModel() {
+ return new HtmlStructureViewTreeModel(file);
+ }
+ };
+ }
+}
diff --git a/xml/impl/src/com/intellij/lang/html/structureView/HtmlStructureViewTreeModel.java b/xml/impl/src/com/intellij/lang/html/structureView/HtmlStructureViewTreeModel.java
new file mode 100644
index 0000000..230a89a
--- /dev/null
+++ b/xml/impl/src/com/intellij/lang/html/structureView/HtmlStructureViewTreeModel.java
@@ -0,0 +1,63 @@
+/*
+ * 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 com.intellij.lang.html.structureView;
+
+import com.intellij.ide.actions.ViewStructureAction;
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.structureView.impl.xml.XmlStructureViewTreeModel;
+import com.intellij.ide.util.treeView.smartTree.NodeProvider;
+import com.intellij.ide.util.treeView.smartTree.Sorter;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.ui.PlaceHolder;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+class HtmlStructureViewTreeModel extends XmlStructureViewTreeModel implements PlaceHolder<String> {
+
+ private final Collection<NodeProvider> myNodeProviders;
+
+ private String myStructureViewPlace;
+
+ public HtmlStructureViewTreeModel(final XmlFile file) {
+ super(file);
+ myNodeProviders = Arrays.<NodeProvider>asList(new Html5SectionsNodeProvider());
+ }
+
+ public void setPlace(final String place) {
+ myStructureViewPlace = place;
+ }
+
+ public String getPlace() {
+ return myStructureViewPlace;
+ }
+
+ @NotNull
+ public Sorter[] getSorters() {
+ return Sorter.EMPTY_ARRAY;
+ }
+
+ @NotNull
+ public Collection<NodeProvider> getNodeProviders() {
+ return myNodeProviders;
+ }
+
+ @NotNull
+ public StructureViewTreeElement getRoot() {
+ return new HtmlFileTreeElement(ViewStructureAction.isInStructureViewPopup(this), (XmlFile)getPsiFile());
+ }
+}
diff --git a/xml/impl/src/com/intellij/lang/html/structureView/HtmlTagTreeElement.java b/xml/impl/src/com/intellij/lang/html/structureView/HtmlTagTreeElement.java
new file mode 100644
index 0000000..d53bcbc
--- /dev/null
+++ b/xml/impl/src/com/intellij/lang/html/structureView/HtmlTagTreeElement.java
@@ -0,0 +1,133 @@
+/*
+ * 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 com.intellij.lang.html.structureView;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
+import com.intellij.ide.structureView.impl.xml.XmlTagTreeElement;
+import com.intellij.navigation.LocationPresentation;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+class HtmlTagTreeElement extends PsiTreeElementBase<XmlTag> implements LocationPresentation {
+ private static final int MAX_TEXT_LENGTH = 50;
+
+ public HtmlTagTreeElement(final XmlTag tag) {
+ super(tag);
+ }
+
+ @NotNull
+ public Collection<StructureViewTreeElement> getChildrenBase() {
+ final XmlTag tag = getElement();
+ if (tag == null || !tag.isValid()) return Collections.emptyList();
+
+ return ContainerUtil.map2List(tag.getSubTags(), new Function<XmlTag, StructureViewTreeElement>() {
+ public StructureViewTreeElement fun(final XmlTag subTag) {
+ return new HtmlTagTreeElement(subTag);
+ }
+ });
+ }
+
+ public String getPresentableText() {
+ final XmlTag tag = getElement();
+ if (tag == null) return IdeBundle.message("node.structureview.invalid");
+
+ return getTagPresentation(tag);
+ }
+
+ public String getLocationString() {
+ final XmlTag tag = getElement();
+ if (tag == null) return null;
+
+ final String text = normalizeSpacesAndShortenIfLong(tag.getValue().getTrimmedText());
+ return text.isEmpty() ? null : text;
+ }
+
+ public boolean isSearchInLocationString() {
+ return true;
+ }
+
+ public static String getTagPresentation(final @NotNull XmlTag tag) {
+ final String id = XmlTagTreeElement.toCanonicalForm(tag.getAttributeValue("id"));
+
+ final String classValue = tag.getAttributeValue("class");
+ final List<String> classValues = classValue != null ? StringUtil.split(classValue, " ") : Collections.<String>emptyList();
+
+ final StringBuilder text = new StringBuilder(tag.getLocalName());
+
+ if (id != null) {
+ text.append("#").append(id);
+ }
+
+ if (!classValues.isEmpty()) {
+ text.append('.').append(StringUtil.join(classValues, "."));
+ }
+
+ return text.toString();
+ }
+
+ @NotNull
+ public static String normalizeSpacesAndShortenIfLong(final @NotNull String text) {
+ return shortenTextIfLong(normalizeSpaces(text));
+ }
+
+ private static String normalizeSpaces(final String text) {
+ final StringBuilder buf = new StringBuilder();
+
+ for (char ch : text.toCharArray()) {
+ if (ch <= ' ' || Character.isSpaceChar(ch)) {
+ if (buf.length() == 0 || buf.charAt(buf.length() - 1) != ' ') {
+ buf.append(' ');
+ }
+ }
+ else {
+ buf.append(ch);
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private static String shortenTextIfLong(final String text) {
+ if (text.length() <= MAX_TEXT_LENGTH) return text;
+
+ int index;
+ for (index = MAX_TEXT_LENGTH; index > MAX_TEXT_LENGTH - 20; index--) {
+ if (!Character.isLetter(text.charAt(index))) {
+ break;
+ }
+ }
+
+ final int endIndex = Character.isLetter(index) ? MAX_TEXT_LENGTH : index;
+ return text.substring(0, endIndex) + "...";
+ }
+
+ public String getLocationPrefix() {
+ return " ";
+ }
+
+ public String getLocationSuffix() {
+ return "";
+ }
+}
diff --git a/xml/impl/src/com/intellij/psi/filters/getters/XmlAttributeValueGetter.java b/xml/impl/src/com/intellij/psi/filters/getters/XmlAttributeValueGetter.java
index ddf656d..929162b 100644
--- a/xml/impl/src/com/intellij/psi/filters/getters/XmlAttributeValueGetter.java
+++ b/xml/impl/src/com/intellij/psi/filters/getters/XmlAttributeValueGetter.java
@@ -41,6 +41,17 @@
return getApplicableAttributeVariants(context);
}
+ @Nullable
+ public static String[] getEnumeratedValues(XmlAttribute attribute) {
+ final XmlAttributeDescriptor descriptor = attribute.getDescriptor();
+ if (descriptor == null) {
+ return ArrayUtil.EMPTY_STRING_ARRAY;
+ }
+
+ return descriptor instanceof BasicXmlAttributeDescriptor ?
+ ((BasicXmlAttributeDescriptor)descriptor).getEnumeratedValues(attribute) : descriptor.getEnumeratedValues();
+ }
+
private Object[] getApplicableAttributeVariants(PsiElement _context) {
if (_context instanceof XmlTokenImpl && ((XmlTokenImpl)_context).getTokenType() == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) {
XmlAttribute attr = PsiTreeUtil.getParentOfType(_context, XmlAttribute.class);
@@ -53,9 +64,7 @@
return defaultValue == null ? ArrayUtil.EMPTY_OBJECT_ARRAY : new Object[]{defaultValue};
}
- String[] values = descriptor instanceof BasicXmlAttributeDescriptor ?
- ((BasicXmlAttributeDescriptor)descriptor).getEnumeratedValues(attr)
- : descriptor.getEnumeratedValues();
+ String[] values = getEnumeratedValues(attr);
final String[] strings = addSpecificCompletions(attr);
diff --git a/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java b/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java
index 48c1502..536575d 100644
--- a/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java
+++ b/xml/impl/src/com/intellij/psi/formatter/xml/HtmlPolicy.java
@@ -32,13 +32,12 @@
import java.util.Map;
public class HtmlPolicy extends XmlFormattingPolicy {
-
+
protected final CodeStyleSettings mySettings;
public HtmlPolicy(final CodeStyleSettings settings, final FormattingDocumentModel documentModel) {
super(documentModel);
mySettings = settings;
-
}
public boolean indentChildrenOf(final XmlTag parentTag) {
@@ -55,8 +54,8 @@
return false;
}
- if (mySettings.HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES > 0 && getLines(parentTag) > mySettings.HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES)
- {
+ if (mySettings.HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES > 0 &&
+ getLines(parentTag) > mySettings.HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES) {
return false;
}
else {
@@ -85,7 +84,7 @@
prevNode = prevNode.getTreePrev();
}
if (prevNode == null) return false;
- if (!(SourceTreeToPsiMap.treeElementToPsi(prevNode)instanceof XmlTag)) return false;
+ if (!(SourceTreeToPsiMap.treeElementToPsi(prevNode) instanceof XmlTag)) return false;
return checkName(xmlTag, mySettings.HTML_ELEMENTS_TO_INSERT_NEW_LINE_BEFORE);
}
@@ -180,7 +179,7 @@
public boolean isTextElement(XmlTag tag) {
return isInlineTag(tag);
- }
+ }
public int getTextWrap(final XmlTag tag) {
return mySettings.HTML_TEXT_WRAP;
@@ -243,5 +242,4 @@
public boolean shouldSaveSpacesBetweenTagAndText() {
return true;
}
-
}
diff --git a/xml/impl/src/com/intellij/psi/impl/cache/impl/idCache/XmlFilterLexer.java b/xml/impl/src/com/intellij/psi/impl/cache/impl/idCache/XmlFilterLexer.java
index e8e75da..96985f4 100644
--- a/xml/impl/src/com/intellij/psi/impl/cache/impl/idCache/XmlFilterLexer.java
+++ b/xml/impl/src/com/intellij/psi/impl/cache/impl/idCache/XmlFilterLexer.java
@@ -69,6 +69,9 @@
else if (tokenType == XmlElementType.XML_TEXT) {
scanWordsInToken(UsageSearchContext.IN_PLAIN_TEXT | UsageSearchContext.IN_FOREIGN_LANGUAGES, false, false);
}
+ else if (tokenType == XmlTokenType.XML_TAG_CHARACTERS) {
+ scanWordsInToken(UsageSearchContext.IN_PLAIN_TEXT | UsageSearchContext.IN_FOREIGN_LANGUAGES, false, false);
+ }
else if (!ourNoWordsTokenSet.contains(tokenType)) {
scanWordsInToken(UsageSearchContext.IN_PLAIN_TEXT, false, false);
}
diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/TagNameReference.java b/xml/impl/src/com/intellij/psi/impl/source/xml/TagNameReference.java
index c89fb9b..be38440 100644
--- a/xml/impl/src/com/intellij/psi/impl/source/xml/TagNameReference.java
+++ b/xml/impl/src/com/intellij/psi/impl/source/xml/TagNameReference.java
@@ -37,6 +37,7 @@
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
+import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
@@ -237,6 +238,20 @@
final Collection<String> namespaces,
@Nullable List<String> nsInfo) {
+ final List<String> variants = getTagNameVariants(element, namespaces, nsInfo, new Function<XmlElementDescriptor, String>() {
+ @Override
+ public String fun(XmlElementDescriptor descriptor) {
+ return descriptor.getName(element);
+ }
+ });
+ return ArrayUtil.toStringArray(variants);
+ }
+
+ public static <T> List<T> getTagNameVariants(final XmlTag element,
+ final Collection<String> namespaces,
+ @Nullable List<String> nsInfo,
+ final Function<XmlElementDescriptor, T> f) {
+
XmlElementDescriptor elementDescriptor = null;
String elementNamespace = null;
@@ -279,8 +294,8 @@
}
final boolean hasPrefix = StringUtil.isNotEmpty(element.getNamespacePrefix());
- final List<String> list = ContainerUtil.mapNotNull(variants, new NullableFunction<XmlElementDescriptor, String>() {
- public String fun(XmlElementDescriptor descriptor) {
+ return ContainerUtil.mapNotNull(variants, new NullableFunction<XmlElementDescriptor, T>() {
+ public T fun(XmlElementDescriptor descriptor) {
if (descriptor instanceof AnyXmlElementDescriptor) {
return null;
}
@@ -289,10 +304,9 @@
return null;
}
- return descriptor.getName(element);
+ return f.fun(descriptor);
}
});
- return ArrayUtil.toStringArray(list);
}
private static void processVariantsInNamespace(final String namespace,
diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlTokenImpl.java b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlTokenImpl.java
index 822e615..fa9a9c8 100644
--- a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlTokenImpl.java
+++ b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlTokenImpl.java
@@ -23,6 +23,7 @@
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.xml.IDTDElementType;
+import com.intellij.psi.xml.XmlProcessingInstruction;
import com.intellij.psi.xml.XmlToken;
import com.intellij.psi.xml.XmlTokenType;
import org.jetbrains.annotations.NotNull;
@@ -66,7 +67,8 @@
final IElementType elementType = getElementType();
if (elementType == XmlTokenType.XML_DATA_CHARACTERS ||
- elementType == XmlTokenType.XML_CHAR_ENTITY_REF
+ elementType == XmlTokenType.XML_CHAR_ENTITY_REF ||
+ (elementType == XmlTokenType.XML_TAG_CHARACTERS && getParent() instanceof XmlProcessingInstruction)
) {
return ReferenceProvidersRegistry.getReferencesFromProviders(this, XmlToken.class);
} else if (elementType == XmlTokenType.XML_NAME && getParent() instanceof PsiErrorElement) {
diff --git a/xml/impl/src/com/intellij/xml/DefaultXmlExtension.java b/xml/impl/src/com/intellij/xml/DefaultXmlExtension.java
index d692afa..f024e87 100644
--- a/xml/impl/src/com/intellij/xml/DefaultXmlExtension.java
+++ b/xml/impl/src/com/intellij/xml/DefaultXmlExtension.java
@@ -94,6 +94,7 @@
assert nsDescriptor != null;
final XmlElementDescriptor[] elementDescriptors = nsDescriptor.getRootElementsDescriptors(document);
for (XmlElementDescriptor elementDescriptor : elementDescriptors) {
+ LOG.assertTrue(elementDescriptor != null, "Null returned from " + nsDescriptor);
if (hasTag(elementDescriptor, tagName, new HashSet<XmlElementDescriptor>())) {
set.add(namespace);
break;
@@ -104,7 +105,7 @@
return set;
}
- private static boolean hasTag(XmlElementDescriptor elementDescriptor, String tagName, Set<XmlElementDescriptor> visited) {
+ private static boolean hasTag(@NotNull XmlElementDescriptor elementDescriptor, String tagName, Set<XmlElementDescriptor> visited) {
final String name = elementDescriptor.getDefaultName();
if (name.equals(tagName)) {
return true;
diff --git a/xml/impl/src/com/intellij/xml/actions/GenerateXmlTagAction.java b/xml/impl/src/com/intellij/xml/actions/GenerateXmlTagAction.java
index 5a2b4ea..ff74f8f 100644
--- a/xml/impl/src/com/intellij/xml/actions/GenerateXmlTagAction.java
+++ b/xml/impl/src/com/intellij/xml/actions/GenerateXmlTagAction.java
@@ -300,7 +300,7 @@
}
@Override
- protected boolean isValidForFile(Project project, Editor editor, PsiFile file) {
+ protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (!(file instanceof XmlFile)) return false;
XmlTag contextTag = getContextTag(editor, file);
return contextTag != null && contextTag.getDescriptor() != null;
diff --git a/xml/impl/src/com/intellij/xml/config/ConfigFilesTreeBuilder.java b/xml/impl/src/com/intellij/xml/config/ConfigFilesTreeBuilder.java
index f71505f..7cdf912 100644
--- a/xml/impl/src/com/intellij/xml/config/ConfigFilesTreeBuilder.java
+++ b/xml/impl/src/com/intellij/xml/config/ConfigFilesTreeBuilder.java
@@ -16,27 +16,26 @@
package com.intellij.xml.config;
import com.intellij.ide.presentation.VirtualFilePresentation;
+import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.TreeSpeedSearch;
-import com.intellij.util.containers.Convertor;
-import com.intellij.util.containers.MultiMap;
+import com.intellij.util.containers.*;
+import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.util.*;
+import java.util.HashSet;
-/**
- * @author Dmitry Avdeev
- * Date: 7/17/12
- */
public class ConfigFilesTreeBuilder {
private final JTree myTree;
@@ -46,42 +45,48 @@
installSearch(tree);
}
- public Set<PsiFile> buildTree(ConfigFileSearcher searcher, DefaultMutableTreeNode root) {
+ public Set<PsiFile> buildTree(DefaultMutableTreeNode root, ConfigFileSearcher... searchers) {
+ final Set<PsiFile> psiFiles = new com.intellij.util.containers.HashSet<PsiFile>();
- final MultiMap<Module,PsiFile> files = searcher.getFilesByModules();
- final MultiMap<VirtualFile, PsiFile> jars = searcher.getJars();
- final Set<PsiFile> psiFiles = buildModuleNodes(files, jars, root);
- final MultiMap<VirtualFile, PsiFile> virtualFiles = searcher.getVirtualFiles();
+ final MultiMap<Module, PsiFile> files = new MultiMap<Module, PsiFile>();
+ final MultiMap<VirtualFile, PsiFile> jars = new MultiMap<VirtualFile, PsiFile>();
+ final MultiMap<VirtualFile, PsiFile> virtualFiles = new MultiMap<VirtualFile, PsiFile>();
+
+ for (ConfigFileSearcher searcher : searchers) {
+ files.putAllValues(searcher.getFilesByModules());
+ jars.putAllValues(searcher.getJars());
+ virtualFiles.putAllValues(searcher.getVirtualFiles());
+ }
+
+ psiFiles.addAll(buildModuleNodes(files, jars, root));
for (Map.Entry<VirtualFile, Collection<PsiFile>> entry : virtualFiles.entrySet()) {
DefaultMutableTreeNode node = createFileNode(entry.getKey());
List<PsiFile> list = new ArrayList<PsiFile>(entry.getValue());
- Collections.sort(list, new Comparator<PsiFile>() {
- @Override
- public int compare(PsiFile o1, PsiFile o2) {
- return o1.getName().compareToIgnoreCase(o2.getName());
- }
- });
+ Collections.sort(list, FILE_COMPARATOR);
for (PsiFile file : list) {
node.add(createFileNode(file));
}
root.add(node);
}
+
return psiFiles;
}
- public void addFile(VirtualFile file) {
+ public DefaultMutableTreeNode addFile(VirtualFile file) {
final DefaultMutableTreeNode root = (DefaultMutableTreeNode)myTree.getModel().getRoot();
final DefaultMutableTreeNode treeNode = createFileNode(file);
root.add(treeNode);
DefaultTreeModel model = (DefaultTreeModel)myTree.getModel();
model.nodeStructureChanged(root);
+
+ return treeNode;
}
- public Set<PsiFile> buildModuleNodes(final MultiMap<Module,PsiFile> files,
- final MultiMap<VirtualFile, PsiFile> jars,
- DefaultMutableTreeNode root) {
+ public Set<PsiFile> buildModuleNodes(final MultiMap<Module, PsiFile> files,
+ final MultiMap<VirtualFile, PsiFile> jars,
+ DefaultMutableTreeNode root) {
final HashSet<PsiFile> psiFiles = new HashSet<PsiFile>();
final List<Module> modules = new ArrayList<Module>(files.keySet());
@@ -90,27 +95,35 @@
return o1.getName().compareTo(o2.getName());
}
});
- for (Module module: modules) {
+ for (Module module : modules) {
DefaultMutableTreeNode moduleNode = createFileNode(module);
root.add(moduleNode);
if (files.containsKey(module)) {
List<PsiFile> moduleFiles = new ArrayList<PsiFile>(files.get(module));
- Collections.sort(moduleFiles, FILE_COMPARATOR);
- for (PsiFile file: moduleFiles) {
- final DefaultMutableTreeNode fileNode = createFileNode(file);
- moduleNode.add(fileNode);
- psiFiles.add(file);
+
+ MultiMap<FileType, PsiFile> filesByType = new MultiMap<FileType, PsiFile>();
+ for (PsiFile file : moduleFiles) {
+ filesByType.putValue(file.getFileType(), file);
+ }
+ if (hasNonEmptyGroups(filesByType)) {
+ for (Map.Entry<FileType, Collection<PsiFile>> entry : filesByType.entrySet()) {
+ DefaultMutableTreeNode fileTypeNode = createFileNode(entry.getKey());
+ moduleNode.add(fileTypeNode);
+ addChildrenFiles(psiFiles, fileTypeNode, new ArrayList<PsiFile>(entry.getValue()));
+ }
+ } else {
+ addChildrenFiles(psiFiles, moduleNode, moduleFiles);
}
}
}
- for (VirtualFile file: jars.keySet()) {
+ for (VirtualFile file : jars.keySet()) {
final List<PsiFile> list = new ArrayList<PsiFile>(jars.get(file));
final PsiFile jar = list.get(0).getManager().findFile(file);
if (jar != null) {
final DefaultMutableTreeNode jarNode = createFileNode(jar);
root.add(jarNode);
Collections.sort(list, FILE_COMPARATOR);
- for (PsiFile psiFile: list) {
+ for (PsiFile psiFile : list) {
jarNode.add(createFileNode(psiFile));
psiFiles.add(psiFile);
}
@@ -119,6 +132,28 @@
return psiFiles;
}
+ private static String getFileTypeNodeName(FileType key) {
+ return StringUtil.capitalize(key.getDefaultExtension()) + " based context files" ;
+ }
+
+ private boolean hasNonEmptyGroups(MultiMap<FileType, PsiFile> filesByType) {
+ byte nonEmptyGroups = 0;
+ for (Map.Entry<FileType, Collection<PsiFile>> entry : filesByType.entrySet()) {
+ Collection<PsiFile> files = entry.getValue();
+ if (files != null && files.size() > 0) nonEmptyGroups++;
+ }
+ return nonEmptyGroups > 1;
+ }
+
+ private void addChildrenFiles(@NotNull Set<PsiFile> psiFiles, DefaultMutableTreeNode parentNode, @NotNull List<PsiFile> moduleFiles) {
+ Collections.sort(moduleFiles, FILE_COMPARATOR);
+ for (PsiFile file : moduleFiles) {
+ final DefaultMutableTreeNode fileNode = createFileNode(file);
+ parentNode.add(fileNode);
+ psiFiles.add(file);
+ }
+ }
+
protected DefaultMutableTreeNode createFileNode(Object file) {
return new DefaultMutableTreeNode(file);
}
@@ -132,13 +167,19 @@
public static void renderNode(Object value, boolean expanded, ColoredTreeCellRenderer renderer) {
if (!(value instanceof DefaultMutableTreeNode)) return;
final Object object = ((DefaultMutableTreeNode)value).getUserObject();
- if (object instanceof Module) {
+ if (object instanceof FileType) {
+ final FileType fileType = (FileType)object;
+ final Icon icon = fileType.getIcon();
+ renderer.setIcon(icon);
+ renderer.append(getFileTypeNodeName(fileType), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ } if (object instanceof Module) {
final Module module = (Module)object;
final Icon icon = ModuleType.get(module).getIcon();
renderer.setIcon(icon);
final String moduleName = module.getName();
renderer.append(moduleName, SimpleTextAttributes.REGULAR_ATTRIBUTES);
- } else if (object instanceof PsiFile) {
+ }
+ else if (object instanceof PsiFile) {
final PsiFile psiFile = (PsiFile)object;
final Icon icon = psiFile.getIcon(0);
renderer.setIcon(icon);
@@ -153,7 +194,8 @@
}
renderer.append(" (" + path + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
}
- } else if (object instanceof VirtualFile) {
+ }
+ else if (object instanceof VirtualFile) {
VirtualFile file = (VirtualFile)object;
renderer.setIcon(VirtualFilePresentation.getIcon(file));
renderer.append(file.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
@@ -172,11 +214,14 @@
final Object object = ((DefaultMutableTreeNode)treePath.getLastPathComponent()).getUserObject();
if (object instanceof Module) {
return ((Module)object).getName();
- } else if (object instanceof PsiFile) {
+ }
+ else if (object instanceof PsiFile) {
return ((PsiFile)object).getName();
- } else if (object instanceof VirtualFile) {
+ }
+ else if (object instanceof VirtualFile) {
return ((VirtualFile)object).getName();
- } else {
+ }
+ else {
return "";
}
}
diff --git a/xml/impl/src/com/intellij/xml/impl/schema/XmlNSDescriptorImpl.java b/xml/impl/src/com/intellij/xml/impl/schema/XmlNSDescriptorImpl.java
index 8c1582f..e984357 100644
--- a/xml/impl/src/com/intellij/xml/impl/schema/XmlNSDescriptorImpl.java
+++ b/xml/impl/src/com/intellij/xml/impl/schema/XmlNSDescriptorImpl.java
@@ -34,6 +34,7 @@
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.ContainerUtil;
import com.intellij.xml.XmlAttributeDescriptor;
import com.intellij.xml.XmlElementDescriptor;
import com.intellij.xml.XmlNSDescriptor;
@@ -674,7 +675,7 @@
final List<XmlElementDescriptor> result = new ArrayList<XmlElementDescriptor>();
public boolean execute(@NotNull final XmlTag element) {
- result.add(getElementDescriptor(element.getAttributeValue("name"),getDefaultNamespace()));
+ ContainerUtil.addIfNotNull(result, getElementDescriptor(element.getAttributeValue("name"), getDefaultNamespace()));
return true;
}
}
diff --git a/xml/impl/src/com/intellij/xml/util/HtmlUtil.java b/xml/impl/src/com/intellij/xml/util/HtmlUtil.java
index 653ad70..7d71cc4 100644
--- a/xml/impl/src/com/intellij/xml/util/HtmlUtil.java
+++ b/xml/impl/src/com/intellij/xml/util/HtmlUtil.java
@@ -127,7 +127,10 @@
};
// flow elements are block or inline, so they shuld not close <p> for example
- @NonNls private static final String[] POSSIBLY_INLINE_TAGS = {"object", "applet", "ins", "del", "button", "nobr"};
+ @NonNls private static final String[] POSSIBLY_INLINE_TAGS = {"a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br", "button",
+ "cite", "code", "del", "dfn", "em", "font", "i", "iframe", "img", "input", "ins",
+ "kbd", "label", "map", "object", "q", "s", "samp", "select", "small", "span", "strike",
+ "strong", "sub", "sup", "textarea", "tt", "u", "var"};
private static final Set<String> BLOCK_TAGS_MAP = new THashSet<String>();
diff --git a/xml/impl/src/com/intellij/xml/util/XmlDuplicatedIdInspection.java b/xml/impl/src/com/intellij/xml/util/XmlDuplicatedIdInspection.java
index e0a1854..19b2012 100644
--- a/xml/impl/src/com/intellij/xml/util/XmlDuplicatedIdInspection.java
+++ b/xml/impl/src/com/intellij/xml/util/XmlDuplicatedIdInspection.java
@@ -24,6 +24,7 @@
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.*;
import com.intellij.psi.html.HtmlTag;
+import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
@@ -50,6 +51,10 @@
if (!(file instanceof XmlFile)) {
return;
}
+ PsiFile baseFile = PsiUtilCore.getTemplateLanguageFile(file);
+ if (baseFile != file && !(baseFile instanceof XmlFile)) {
+ return;
+ }
final XmlRefCountHolder refHolder = XmlRefCountHolder.getRefCountHolder(value);
if (refHolder == null) return;
diff --git a/xml/impl/xml.iml b/xml/impl/xml.iml
index 0395394..840cd2b 100644
--- a/xml/impl/xml.iml
+++ b/xml/impl/xml.iml
@@ -16,6 +16,7 @@
<orderEntry type="library" name="XmlBeans" level="project" />
<orderEntry type="module" module-name="platform-resources" />
<orderEntry type="module" module-name="vcs-api" />
+ <orderEntry type="library" name="Guava" level="project" />
</component>
<component name="copyright">
<Base>
diff --git a/xml/openapi/src/com/intellij/ide/browsers/WebBrowserService.java b/xml/openapi/src/com/intellij/ide/browsers/WebBrowserService.java
index 040d637..224eb9c 100644
--- a/xml/openapi/src/com/intellij/ide/browsers/WebBrowserService.java
+++ b/xml/openapi/src/com/intellij/ide/browsers/WebBrowserService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2011 JetBrains s.r.o.
+ * 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.
@@ -35,5 +35,4 @@
@Nullable
public abstract String getUrlToOpen(@NotNull PsiElement psiElement, boolean preferLocalUrl) throws WebBrowserUrlProvider.BrowserException;
-
-}
+}
\ No newline at end of file
diff --git a/xml/openapi/src/com/intellij/openapi/editor/XmlHighlighterColors.java b/xml/openapi/src/com/intellij/openapi/editor/XmlHighlighterColors.java
index 17d1e1c..782f379 100644
--- a/xml/openapi/src/com/intellij/openapi/editor/XmlHighlighterColors.java
+++ b/xml/openapi/src/com/intellij/openapi/editor/XmlHighlighterColors.java
@@ -25,33 +25,38 @@
}
public static final TextAttributesKey
- XML_PROLOGUE = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("XML_PROLOGUE");
+ XML_PROLOGUE = TextAttributesKey.createTextAttributesKey("XML_PROLOGUE", HighlighterColors.TEXT);
public static final TextAttributesKey
- XML_COMMENT = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("XML_COMMENT");
- public static final TextAttributesKey XML_TAG = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("XML_TAG");
+ XML_COMMENT = TextAttributesKey.createTextAttributesKey("XML_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT);
+ public static final TextAttributesKey XML_TAG =
+ TextAttributesKey.createTextAttributesKey("XML_TAG", DefaultLanguageHighlighterColors.MARKUP_TAG);
public static final TextAttributesKey
- XML_TAG_NAME = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("XML_TAG_NAME");
+ XML_TAG_NAME = TextAttributesKey.createTextAttributesKey("XML_TAG_NAME", DefaultLanguageHighlighterColors.KEYWORD);
public static final TextAttributesKey
- XML_ATTRIBUTE_NAME = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("XML_ATTRIBUTE_NAME");
+ XML_ATTRIBUTE_NAME = TextAttributesKey.createTextAttributesKey("XML_ATTRIBUTE_NAME", DefaultLanguageHighlighterColors.MARKUP_ATTRIBUTE);
public static final TextAttributesKey
- XML_ATTRIBUTE_VALUE = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("XML_ATTRIBUTE_VALUE");
+ XML_ATTRIBUTE_VALUE = TextAttributesKey.createTextAttributesKey("XML_ATTRIBUTE_VALUE", DefaultLanguageHighlighterColors.STRING);
public static final TextAttributesKey
- XML_TAG_DATA = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("XML_TAG_DATA");
+ XML_TAG_DATA = TextAttributesKey.createTextAttributesKey("XML_TAG_DATA", HighlighterColors.TEXT);
public static final TextAttributesKey
- XML_ENTITY_REFERENCE = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("XML_ENTITY_REFERENCE");
+ XML_ENTITY_REFERENCE =
+ TextAttributesKey.createTextAttributesKey("XML_ENTITY_REFERENCE", DefaultLanguageHighlighterColors.MARKUP_ENTITY);
public static final TextAttributesKey
- HTML_COMMENT = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("HTML_COMMENT");
- public static final TextAttributesKey HTML_TAG = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("HTML_TAG");
+ HTML_COMMENT = TextAttributesKey.createTextAttributesKey("HTML_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT);
+ public static final TextAttributesKey HTML_TAG =
+ TextAttributesKey.createTextAttributesKey("HTML_TAG", DefaultLanguageHighlighterColors.MARKUP_TAG);
public static final TextAttributesKey
- HTML_TAG_NAME = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("HTML_TAG_NAME");
+ HTML_TAG_NAME = TextAttributesKey.createTextAttributesKey("HTML_TAG_NAME", DefaultLanguageHighlighterColors.KEYWORD);
public static final TextAttributesKey
- HTML_ATTRIBUTE_NAME = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("HTML_ATTRIBUTE_NAME");
+ HTML_ATTRIBUTE_NAME =
+ TextAttributesKey.createTextAttributesKey("HTML_ATTRIBUTE_NAME", DefaultLanguageHighlighterColors.MARKUP_ATTRIBUTE);
public static final TextAttributesKey
- HTML_ATTRIBUTE_VALUE = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("HTML_ATTRIBUTE_VALUE");
+ HTML_ATTRIBUTE_VALUE = TextAttributesKey.createTextAttributesKey("HTML_ATTRIBUTE_VALUE", DefaultLanguageHighlighterColors.STRING);
public static final TextAttributesKey
- HTML_ENTITY_REFERENCE = com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey("HTML_ENTITY_REFERENCE");
+ HTML_ENTITY_REFERENCE =
+ TextAttributesKey.createTextAttributesKey("HTML_ENTITY_REFERENCE", DefaultLanguageHighlighterColors.MARKUP_ENTITY);
public static final TextAttributesKey HTML_CODE =
- TextAttributesKey.createTextAttributesKey("HTML_CODE", HighlighterColors.TEXT.getDefaultAttributes());
+ TextAttributesKey.createTextAttributesKey("HTML_CODE", HighlighterColors.TEXT);
}
diff --git a/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/RncFileType.java b/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/RncFileType.java
index 1b5906e..4e5a501 100644
--- a/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/RncFileType.java
+++ b/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/RncFileType.java
@@ -21,7 +21,6 @@
import com.intellij.openapi.fileTypes.FileTypeConsumer;
import com.intellij.openapi.fileTypes.FileTypeFactory;
import com.intellij.openapi.fileTypes.LanguageFileType;
-import com.intellij.util.PairConsumer;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,7 +35,7 @@
public class RncFileType extends LanguageFileType {
public static final String RNC_EXT = "rnc";
- private static FileType INSTANCE;
+ private static final FileType INSTANCE = new RncFileType();
private RncFileType() {
super(RngCompactLanguage.INSTANCE);
@@ -64,20 +63,13 @@
return AllIcons.FileTypes.Text;
}
- public static synchronized FileType getInstance() {
- if (INSTANCE == null) {
- INSTANCE = new RncFileType();
- }
+ public static FileType getInstance() {
return INSTANCE;
}
public static class Factory extends FileTypeFactory {
public void createFileTypes(@NotNull FileTypeConsumer fileTypeConsumer) {
- fileTypeConsumer.consume(getInstance(), RNC_EXT);
- }
-
- public void createFileTypes(@NotNull PairConsumer<FileType, String> consumer) {
- consumer.consume(getInstance(), RNC_EXT);
+ fileTypeConsumer.consume(INSTANCE, RNC_EXT);
}
}
}
diff --git a/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/lexer/CompactSyntaxLexerAdapter.java b/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/lexer/CompactSyntaxLexerAdapter.java
index 4bfc344..06ba2f2 100644
--- a/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/lexer/CompactSyntaxLexerAdapter.java
+++ b/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/lexer/CompactSyntaxLexerAdapter.java
@@ -199,8 +199,8 @@
final Class<CompactSyntaxTokenManager> managerClass = CompactSyntaxTokenManager.class;
LOG.error("Unsupported version of RNGOM in classpath", e,
"Actual parameter types: " + Arrays.toString(managerClass.getConstructors()[0].getParameterTypes()),
- "Location of " + managerClass.getName() + ": " + managerClass.getProtectionDomain().getCodeSource().getLocation(),
- "Location of " + CharStream.class.getName() + ": " + CharStream.class.getProtectionDomain().getCodeSource().getLocation());
+ "Location of " + managerClass.getName() + ": " + managerClass.getProtectionDomain().getCodeSource(),
+ "Location of " + CharStream.class.getName() + ": " + CharStream.class.getProtectionDomain().getCodeSource());
throw e;
}
}
diff --git a/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/psi/impl/RncNameImpl.java b/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/psi/impl/RncNameImpl.java
index 1c4b2f6..26d3507 100644
--- a/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/psi/impl/RncNameImpl.java
+++ b/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/psi/impl/RncNameImpl.java
@@ -35,6 +35,7 @@
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import org.intellij.plugins.relaxNG.compact.RncElementTypes;
+import org.intellij.plugins.relaxNG.compact.RncFileType;
import org.intellij.plugins.relaxNG.compact.RncTokenTypes;
import org.intellij.plugins.relaxNG.compact.psi.*;
import org.intellij.plugins.relaxNG.compact.psi.util.EscapeUtil;
@@ -49,20 +50,13 @@
*/
public class RncNameImpl extends RncElementImpl implements RncName, PsiReference,
EmptyResolveMessageProvider, QuickFixProvider<RncNameImpl> {
+
private enum Kind {
NAMESPACE, DATATYPES
}
- private final Kind myKind;
public RncNameImpl(ASTNode node) {
super(node);
-
- final IElementType parent = node.getTreeParent().getElementType();
- if (parent == RncElementTypes.DATATYPE_PATTERN) {
- myKind = Kind.DATATYPES;
- } else {
- myKind = Kind.NAMESPACE;
- }
}
@Nullable
@@ -97,11 +91,20 @@
@Nullable
public PsiElement resolve() {
- final MyResolver resolver = new MyResolver(getPrefix(), myKind);
+ final MyResolver resolver = new MyResolver(getPrefix(), getKind());
getContainingFile().processDeclarations(resolver, ResolveState.initial(), this, this);
return resolver.getResult();
}
+ private Kind getKind() {
+ final IElementType parent = getNode().getTreeParent().getElementType();
+ if (parent == RncElementTypes.DATATYPE_PATTERN) {
+ return Kind.DATATYPES;
+ } else {
+ return Kind.NAMESPACE;
+ }
+ }
+
@NotNull
public String getCanonicalText() {
return getRangeInElement().substring(getText());
@@ -197,7 +200,7 @@
@NotNull
public String getFamilyName() {
- return "Create " + myReference.myKind.name().toLowerCase() + " declaration";
+ return "Create " + myReference.getKind().name().toLowerCase() + " declaration";
}
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
@@ -206,7 +209,10 @@
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
final String prefix = myReference.getPrefix();
- final RncFile psiFile = (RncFile)PsiFileFactory.getInstance(myReference.getProject()).createFileFromText("dummy.rnc", myReference.myKind.name().toLowerCase() + " " + prefix + " = \"###\"");
+ final PsiFileFactory factory = PsiFileFactory.getInstance(myReference.getProject());
+ final RncFile psiFile = (RncFile)factory.createFileFromText("dummy.rnc",
+ RncFileType.getInstance(),
+ myReference.getKind().name().toLowerCase() + " " + prefix + " = \"###\"");
final RncFile rncFile = (RncFile)myReference.getContainingFile();
final RncDecl[] declarations = rncFile.getDeclarations();
final RncDecl decl = psiFile.getDeclarations()[0];
@@ -230,7 +236,7 @@
CodeStyleManager.getInstance(e.getManager().getProject()).reformatNewlyAddedElement(blockNode, newNode);
- final SmartPsiElementPointer<RncDecl> p = SmartPointerManager.getInstance(project).createLazyPointer(e);
+ final SmartPsiElementPointer<RncDecl> p = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(e);
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
final RncDecl d = p.getElement();
diff --git a/xml/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxIncludeProvider.java b/xml/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxIncludeProvider.java
index 657cb60..702626f 100644
--- a/xml/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxIncludeProvider.java
+++ b/xml/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxIncludeProvider.java
@@ -81,7 +81,7 @@
boolean isRngTag = ApplicationLoader.RNG_NAMESPACE.equals(nsURI);
if (!isRNG) { // analyzing start tag
if (!isRngTag) {
- throw new NanoXmlUtil.ParserStoppedException();
+ stop();
} else {
isRNG = true;
}
diff --git a/xml/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxSymbolIndex.java b/xml/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxSymbolIndex.java
index e92869d..29ff4e0 100644
--- a/xml/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxSymbolIndex.java
+++ b/xml/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxSymbolIndex.java
@@ -6,6 +6,7 @@
import com.intellij.navigation.NavigationItem;
import com.intellij.navigation.PsiElementNavigationItem;
import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.JarFileSystem;
@@ -142,7 +143,8 @@
if (file.getFileSystem() instanceof JarFileSystem) {
return false; // there is lots and lots of custom XML inside zip files
}
- return file.getFileType() == StdFileTypes.XML || file.getFileType() == RncFileType.getInstance();
+ final FileType fileType = file.getFileType();
+ return fileType == StdFileTypes.XML || fileType == RncFileType.getInstance();
}
};
}
diff --git a/xml/tests/src/com/intellij/xml/structureView/HtmlFileStructureTest.java b/xml/tests/src/com/intellij/xml/structureView/HtmlFileStructureTest.java
new file mode 100644
index 0000000..d3d79b2
--- /dev/null
+++ b/xml/tests/src/com/intellij/xml/structureView/HtmlFileStructureTest.java
@@ -0,0 +1,48 @@
+package com.intellij.xml.structureView;
+
+import com.intellij.ide.util.FileStructurePopup;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.lang.html.structureView.Html5SectionsNodeProvider;
+import com.intellij.testFramework.FileStructureTestBase;
+
+public class HtmlFileStructureTest extends FileStructureTestBase {
+
+ private boolean myHtml5OutlineModeDefault;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ myHtml5OutlineModeDefault = PropertiesComponent.getInstance().getBoolean(getHtml5OutlineModePropertyName(), false);
+ setHtml5OutlineMode(true);
+ }
+
+ public void tearDown() throws Exception {
+ PropertiesComponent.getInstance().setValue(getHtml5OutlineModePropertyName(), String.valueOf(myHtml5OutlineModeDefault));
+ super.tearDown();
+ }
+
+ private static String getHtml5OutlineModePropertyName() {
+ return FileStructurePopup.getPropertyName(Html5SectionsNodeProvider.HTML5_OUTLINE_PROVIDER_PROPERTY);
+ }
+
+ protected String getFileExtension() {
+ return "html";
+ }
+
+ protected String getBasePath() {
+ return "/xml/tests/testData/structureView/";
+ }
+
+ protected boolean isCommunity() {
+ return true;
+ }
+
+ public void setHtml5OutlineMode(boolean enabled) throws Exception {
+ myPopup.setTreeActionState(Html5SectionsNodeProvider.class, enabled);
+ update();
+ }
+
+ public void testEmpty() throws Exception {checkTree();}
+ public void testSimple() throws Exception {checkTree();}
+ public void testNoSectioningRoot() throws Exception {checkTree();}
+ public void testImplicitSections() throws Exception {checkTree();}
+}
diff --git a/xml/tests/testData/structureView/Empty.html b/xml/tests/testData/structureView/Empty.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xml/tests/testData/structureView/Empty.html
diff --git a/xml/tests/testData/structureView/Empty.tree b/xml/tests/testData/structureView/Empty.tree
new file mode 100644
index 0000000..becaf0a
--- /dev/null
+++ b/xml/tests/testData/structureView/Empty.tree
@@ -0,0 +1 @@
++HtmlFile:Empty.html
\ No newline at end of file
diff --git a/xml/tests/testData/structureView/ImplicitSections.html b/xml/tests/testData/structureView/ImplicitSections.html
new file mode 100644
index 0000000..32314bf
--- /dev/null
+++ b/xml/tests/testData/structureView/ImplicitSections.html
@@ -0,0 +1,47 @@
+<html>
+<body>
+<article>
+ <div><h4>section</h4></div>
+ <h5>h5</h5>
+ <h4>h4</h4>
+ <h3>h3</h3>
+ <p>
+ <h4>hh4</h4>
+ <h5>hh5</h5>
+ <aside>
+ <div>
+ <hgroup>
+ <h4>h4</h4>
+
+ <h3>h3</h3>
+ </hgroup>
+ <h4>hh4</h4>
+ <h3>hh3</h3>
+ <figure>
+ <nav>
+ <div>
+ <hgroup>
+ </hgroup>
+ <h2>hhh2</h2>
+ <h1>hhh1</h1>
+ </div>
+ </nav>
+ </figure>
+ </div>
+ </aside>
+ <h6>hh6</h6>
+ <h6>hhh6</h6>
+ </p>
+ <h5>hhh5</h5>
+ <h4>g4</h4>
+ <h6>g6</h6>
+ <h3>g3</h3>
+ <h2>g2</h2>
+</article>
+<div>
+ <h3>body</h3>
+</div>
+<h1>body2</h1>
+<h2>body3</h2>
+</body>
+</html>
\ No newline at end of file
diff --git a/xml/tests/testData/structureView/ImplicitSections.tree b/xml/tests/testData/structureView/ImplicitSections.tree
new file mode 100644
index 0000000..9903463
--- /dev/null
+++ b/xml/tests/testData/structureView/ImplicitSections.tree
@@ -0,0 +1,24 @@
+-HtmlFile:ImplicitSections.html
+ -body
+ -[section]
+ h5
+ h4
+ -h3
+ -hh4
+ hh5
+ -h4 h3
+ hh4
+ hh3
+ hh6
+ hhh6
+ hhh5
+ -g4
+ g6
+ g3
+ g2
+ -body2
+ body3
+ -
+ -
+ hhh2
+ hhh1
\ No newline at end of file
diff --git a/xml/tests/testData/structureView/NoSectioningRoot.html b/xml/tests/testData/structureView/NoSectioningRoot.html
new file mode 100644
index 0000000..d0b4849
--- /dev/null
+++ b/xml/tests/testData/structureView/NoSectioningRoot.html
@@ -0,0 +1,17 @@
+<section>
+ <blockquote>blockquote</blockquote>
+ <body>body</body>
+ <details>details</details>
+ <dialog>dialog</dialog>
+ <fieldset>fieldset</fieldset>
+ <figure>figure</figure>
+ <td>td</td>
+ <hgroup>
+ <a/>
+ <h1>1</h1>
+ <h2>2</h2>
+ <div/>
+ <h3>3</h3>
+ <h4>4</h4>
+ </hgroup>
+</section>
\ No newline at end of file
diff --git a/xml/tests/testData/structureView/NoSectioningRoot.tree b/xml/tests/testData/structureView/NoSectioningRoot.tree
new file mode 100644
index 0000000..e63bb32
--- /dev/null
+++ b/xml/tests/testData/structureView/NoSectioningRoot.tree
@@ -0,0 +1,9 @@
+-HtmlFile:NoSectioningRoot.html
+ [1 2 3 4]
+ blockquote
+ body
+ details
+ dialog
+ fieldset
+ figure
+ td
\ No newline at end of file
diff --git a/xml/tests/testData/structureView/Simple.html b/xml/tests/testData/structureView/Simple.html
new file mode 100644
index 0000000..cb12824
--- /dev/null
+++ b/xml/tests/testData/structureView/Simple.html
@@ -0,0 +1,6 @@
+<html>
+<head><title>title</title></head>
+<body>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/xml/tests/testData/structureView/Simple.tree b/xml/tests/testData/structureView/Simple.tree
new file mode 100644
index 0000000..4f8e03e
--- /dev/null
+++ b/xml/tests/testData/structureView/Simple.tree
@@ -0,0 +1,2 @@
+-HtmlFile:Simple.html
+ []
\ No newline at end of file