@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);
    }
  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);
              }
            });
  }
  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);
    }
  }
  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 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;
 }
  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);
  }
 @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 boolean isHtml(CustomTemplateCallback callback) {
   FileType type = callback.getFileType();
   return type == StdFileTypes.HTML || type == StdFileTypes.XHTML;
 }
 public String computeTemplateKey(@NotNull CustomTemplateCallback callback) {
   ZenCodingGenerator generator = findApplicableDefaultGenerator(callback.getContext(), false);
   if (generator == null) return null;
   return generator.computeTemplateKey(callback);
 }
 public static boolean checkTemplateKey(String inputString, CustomTemplateCallback callback) {
   ZenCodingGenerator generator = findApplicableDefaultGenerator(callback.getContext(), true);
   assert generator != null;
   return checkTemplateKey(inputString, callback, generator);
 }
 public void expand(String key, @NotNull CustomTemplateCallback callback) {
   ZenCodingGenerator defaultGenerator =
       findApplicableDefaultGenerator(callback.getContext(), false);
   assert defaultGenerator != null;
   expand(key, callback, null, defaultGenerator);
 }