/* * Copyright 2000-2014 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.codeInsight.template.CustomTemplateCallback; import com.intellij.codeInsight.template.emmet.ZenCodingTemplate; import com.intellij.codeInsight.template.emmet.tokens.TemplateToken; import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.diagnostic.AttachmentFactory; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlDocument; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlTokenType; import com.intellij.xml.util.HtmlUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; /** * @author Eugene.Kudelevsky */ public abstract class XmlZenCodingGenerator extends ZenCodingGenerator { @Override public TemplateImpl generateTemplate(@NotNull TemplateToken token, boolean hasChildren, @NotNull PsiElement context) { String s = toString(token, hasChildren, context); TemplateImpl tokenTemplate = token.getTemplate(); assert tokenTemplate != null; TemplateImpl template = tokenTemplate.copy(); template.setString(s); return template; } @Override public TemplateImpl createTemplateByKey(@NotNull String key) { StringBuilder builder = new StringBuilder("<"); builder.append(key).append('>'); if (!HtmlUtil.isSingleHtmlTag(key)) { builder.append("$END$'); } return new TemplateImpl("", builder.toString(), ""); } @NotNull private String toString(@NotNull TemplateToken token, boolean hasChildren, @NotNull PsiElement context) { XmlFile file = token.getFile(); XmlDocument document = file.getDocument(); if (document != null) { XmlTag tag = document.getRootTag(); if (tag != null) { return toString(tag, token.getAttribute2Value(), hasChildren, context); } } return file.getText(); } public abstract String toString(@NotNull XmlTag tag, @NotNull List> attribute2Value, boolean hasChildren, @NotNull PsiElement context); @NotNull public abstract String buildAttributesString(@NotNull List> attribute2value, boolean hasChildren, int numberInIteration, int totalIterations, @Nullable String surroundedText); @Override public abstract boolean isMyContext(@NotNull PsiElement context, boolean wrapping); @Nullable @Override public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { Editor editor = callback.getEditor(); int currentOffset = editor.getCaretModel().getOffset(); int startOffset = Math.min(editor.getDocument().getLineStartOffset(editor.getDocument().getLineNumber(currentOffset)), currentOffset); CharSequence documentText = editor.getDocument().getCharsSequence(); PsiElement prevVisibleLeaf = callback.getContext(); while (prevVisibleLeaf != null) { TextRange textRange = prevVisibleLeaf.getTextRange(); final int endOffset = textRange.getEndOffset(); if (endOffset <= currentOffset) { if (endOffset <= startOffset) { break; } IElementType prevType = prevVisibleLeaf.getNode().getElementType(); if (prevType == XmlTokenType.XML_TAG_END || prevType == XmlTokenType.XML_EMPTY_ELEMENT_END) { startOffset = endOffset; break; } } prevVisibleLeaf = PsiTreeUtil.prevVisibleLeaf(prevVisibleLeaf); } if (startOffset < 0 || currentOffset > documentText.length() || currentOffset < startOffset) { Logger.getInstance(getClass()).error("Error while calculating emmet abbreviation. Offset: " + currentOffset + "; Start: " + startOffset, AttachmentFactory.createAttachment(editor.getDocument())); return null; } String key = computeKey(documentText.subSequence(startOffset, currentOffset)); return !StringUtil.isEmpty(key) && ZenCodingTemplate.checkTemplateKey(key, callback, this) ? key : null; } }