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);
              }
            }
          }
        });
  }
  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);
  }