boolean (final @NotNull PsiElement start, final PsiElement end, String newName) { final XmlTag outerTemplate = XsltCodeInsightUtil.getTemplateTag(start, false); if (outerTemplate == null) { return false; } final XmlTag parentScope = PsiTreeUtil.getParentOfType(start, XmlTag.class, true); if (parentScope == null) { return false; } final StringBuilder sb = new StringBuilder("\n"); final Set<String> vars = new LinkedHashSet<>(); final int startOffset = start.getTextRange().getStartOffset(); final int endOffset = end.getTextRange().getEndOffset(); PsiElement e = start; while (e != null) { if (e instanceof XmlTag tag) { if (XsltSupport.isVariable(tag)) { final XsltVariable variable = XsltElementFactory.getInstance().wrapElement(tag, XsltVariable.class); final LocalSearchScope searchScope = new LocalSearchScope(parentScope); final Query<PsiReference> query = ReferencesSearch.search(variable, searchScope); for (PsiReference reference : query) { final XmlElement context = PsiTreeUtil.getContextOfType(reference.getElement(), XmlElement.class, true); if (context == null || context.getTextRange().getStartOffset() > endOffset) { return false; } } } } sb.append(e.getText()); final List<XPathVariableReference> references = RefactoringUtil.collectVariableReferences(e); for (XPathVariableReference reference : references) { final XPathVariable variable = reference.resolve(); if (variable instanceof XsltVariable) { final XmlTag var = ((XsltVariable)variable).getTag(); if (var.getTextRange().getStartOffset() < startOffset) { // don't pass through global parameters and variables if (XsltCodeInsightUtil.getTemplateTag(variable, false) != null) { vars.add(variable.getName()); } } } else if (variable == null) { // TODO just copy unresolved refs or fail? vars.add(reference.getReferencedName()); } } if (e == end) { break; } e = e.getNextSibling(); } sb.append("\n"); final String s = newName == null ? Messages.showInputDialog(start.getProject(), XPathBundle.message("dialog.message.template.name"), getRefactoringName(), Messages.getQuestionIcon()) : newName; if (s != null) { WriteCommandAction.runWriteCommandAction(start.getProject(), ()-> { final PsiFile containingFile = start.getContainingFile(); XmlTag templateTag = parentScope.createChildTag("template", XsltSupport.XSLT_NS, sb.toString(), false); templateTag.setAttribute("name", s); final PsiElement dummy = XmlElementFactory.getInstance(start.getProject()).createDisplayText(" "); final PsiElement outerParent = outerTemplate.getParent(); final PsiElement element = outerParent.addAfter(dummy, outerTemplate); templateTag = (XmlTag)outerParent.addAfter(templateTag, element); final TextRange adjust = templateTag.getTextRange(); final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(start.getProject()); final Document doc = psiDocumentManager.getDocument(containingFile); assert doc != null; psiDocumentManager.doPostponedOperationsAndUnblockDocument(doc); CodeStyleManager.getInstance(start.getManager().getProject()).adjustLineIndent(containingFile, adjust); final PsiElement parent = start.getParent(); XmlTag callTag = parentScope.createChildTag("call-template", XsltSupport.XSLT_NS, null, false); callTag.setAttribute("name", s); if (start instanceof XmlToken && ((XmlToken)start).getTokenType() == XmlTokenType.XML_DATA_CHARACTERS) { assert start == end; callTag = (XmlTag)start.replace(callTag); } else { callTag = (XmlTag)parent.addBefore(callTag, start); parent.deleteChildRange(start, end); } for (String var : vars) { final XmlTag param = templateTag.createChildTag("param", XsltSupport.XSLT_NS, null, false); param.setAttribute("name", var); RefactoringUtil.addParameter(templateTag, param); final XmlTag arg = RefactoringUtil.addWithParam(callTag); arg.setAttribute("name", var); arg.setAttribute("select", "$" + var); } }); } return true; }