Emmet support

- css abbreviations with values specifying
 - supplying css values with units
 - add important modifier
 - explicit vendor prefixing
This commit is contained in:
Alexander Zolotov
2013-02-15 17:15:40 +04:00
parent 3b4e3818e6
commit af3f68abf0
6 changed files with 97 additions and 101 deletions
@@ -219,7 +219,7 @@ public abstract class EmmetParser {
if (template == null) {
return false;
}
return ZenCodingTemplate.doSetTemplate(token, template, myCallback);
return token.setTemplate(template, myCallback);
}
@Nullable
@@ -30,30 +30,21 @@ 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.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
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;
@@ -62,32 +53,6 @@ import java.util.List;
*/
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) {
@@ -99,19 +64,10 @@ public class ZenCodingTemplate implements CustomLiveTemplate {
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) {
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;
@@ -246,7 +202,7 @@ public class ZenCodingTemplate implements CustomLiveTemplate {
}
}
for (ZenCodingFilter filter : filters) {
if(filter instanceof SingleLineEmmetFilter) {
if (filter instanceof SingleLineEmmetFilter) {
builder.setIsToReformat(false);
break;
}
@@ -333,8 +289,8 @@ public class ZenCodingTemplate implements CustomLiveTemplate {
}
public static void doWrap(final String selection,
final String abbreviation,
final CustomTemplateCallback callback) {
final String abbreviation,
final CustomTemplateCallback callback) {
final ZenCodingGenerator defaultGenerator = findApplicableDefaultGenerator(callback.getContext(), true);
assert defaultGenerator != null;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@@ -367,16 +323,6 @@ public class ZenCodingTemplate implements CustomLiveTemplate {
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;
@@ -386,25 +332,4 @@ public class ZenCodingTemplate implements CustomLiveTemplate {
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;
}
}
@@ -18,7 +18,6 @@ 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;
@@ -213,9 +212,6 @@ public class GenerationNode extends UserDataHolderBase {
end = e;
}
}
/*if (end != -1) {
builder.insertVariableSegment(end, TemplateImpl.END);
}*/
if(singleLineFilterEnabled) {
builder.setIsToReformat(false);
}
@@ -305,8 +301,7 @@ public class GenerationNode extends UserDataHolderBase {
if (flag) {
builder.append("></").append(tag.getName()).append('>');
final XmlTag tag1 = XmlElementFactory.getInstance(tag.getProject()).createTagFromText(builder.toString(), XMLLanguage.INSTANCE);
return tag1;
return XmlElementFactory.getInstance(tag.getProject()).createTagFromText(builder.toString(), XMLLanguage.INSTANCE);
}
return tag;
}
@@ -372,7 +367,7 @@ public class GenerationNode extends UserDataHolderBase {
Map<String, String> predefinedValues = null;
if (attributes != null) {
predefinedValues = new HashMap<String, String>();
predefinedValues.put(ZenCodingTemplate.ATTRS, attributes);
predefinedValues.put(TemplateToken.ATTRS, attributes);
}
return predefinedValues;
}
@@ -16,7 +16,6 @@
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;
@@ -48,8 +47,7 @@ public class LoremNode extends ZenCodingNode {
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);
templateToken.setTemplate(template, callback);
final GenerationNode node = new GenerationNode(templateToken, numberInIteration,
totalIterations, surroundedText, insertSurroundedTextAtTheEnd, parent);
return Collections.singletonList(node);
@@ -1,12 +1,10 @@
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.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
@@ -33,12 +31,12 @@ public class TextNode extends ZenCodingNode {
int totalIterations, String surroundedText,
CustomTemplateCallback callback,
boolean insertSurroundedTextAtTheEnd, GenerationNode parent) {
final TemplateToken templateToken = new TemplateToken("", Collections.<Pair<String, String>>emptyList());
final TemplateToken templateToken = new TemplateToken("");
final boolean containsSurroundedTextMarker = ZenCodingUtil.containsSurroundedTextMarker(myText);
final String text = ZenCodingUtil.replaceMarkers(myText.replace("${nl}", "\n"), numberInIteration, totalIterations, surroundedText);
final TemplateImpl template = new TemplateImpl("", text, "");
ZenCodingTemplate.doSetTemplate(templateToken, template, callback);
templateToken.setTemplate(template, callback);
final GenerationNode node = new GenerationNode(templateToken, numberInIteration, totalIterations,
containsSurroundedTextMarker ? null : surroundedText,
@@ -15,18 +15,33 @@
*/
package com.intellij.codeInsight.template.emmet.tokens;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.UndoConstants;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.XmlElementFactory;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public class TemplateToken extends ZenCodingToken {
public static final String ATTRS = "ATTRS";
public final static TemplateToken EMPTY_TEMPLATE_TOKEN = new TemplateToken("", new ArrayList<Pair<String, String>>());
private final String myKey;
@@ -55,12 +70,77 @@ public class TemplateToken extends ZenCodingToken {
myFile = file;
}
public String getKey() {
public String getKey() {
return myKey;
}
public void setTemplate(TemplateImpl template) {
public boolean setTemplate(TemplateImpl template, CustomTemplateCallback callback) {
myTemplate = template;
final XmlFile xmlFile = parseXmlFileInTemplate(template.getString(), callback, true);
setFile(xmlFile);
XmlDocument document = xmlFile.getDocument();
final XmlTag tag = document != null ? document.getRootTag() : null;
if (getAttribute2Value().size() > 0 && tag == null) {
return false;
}
if (tag != null) {
if (!containsAttrsVar(template) && getAttribute2Value().size() > 0) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
addMissingAttributes(tag, getAttribute2Value());
}
});
}
}
return true;
}
private 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;
}
@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;
}
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