Need help with TemplateBuilder

I'm working on a custom in place renamer for my XML tag. When I rename an attribute of a particular tag, I want also to rename attributes of other specific tags in the same file. My template builder method looks like this:

private static Template buildTemplate(@NotNull XmlAttribute attr, List<XmlTag> refs) {
TemplateBuilderImpl builder = new TemplateBuilderImpl(attr);

XmlAttributeValue attrValue = attr.getValueElement();
PsiElement valuePsi = attrValue.getFirstChild().getNextSibling();

builder.replaceElement(valuePsi,"PrimaryVariable", new TextExpression(attrValue.getValue()), true);

for (XmlTag ref : refs) {
XmlAttribute nextAttr = ref.getAttribute("name");
XmlAttributeValue nextValue = nextAttr.getValueElement();
PsiElement nextValuePsi = nextValue.getFirstChild().getNextSibling();
builder.replaceElement(nextValuePsi, "OtherVariable", "PrimaryVariable", false);

return builder.buildInlineTemplate();

When it is invoked, it throws an exception:

file: XmlFile:test.xml container: RangeMarker(2213,2231) 95 markers: [[HelloWorld1]1696, 1707], [[HelloWorld1]2147, 2158], [[HelloWorld1]2219, 2230]
at com.intellij.openapi.diagnostic.Logger.error(
at com.intellij.codeInsight.template.TemplateBuilderImpl.buildTemplate(
at com.intellij.codeInsight.template.TemplateBuilderImpl.buildInlineTemplate(
at com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(

Apparently the text ranges are not calculated correctly. I looked at the InplaceRefactoring class, but can't really understand what's going on there.



I tried passing the attr.getContainingFile() to the builder constructor, and got this:

Incorrect offsets: startOffset=2191, endOffset=4826, text length=2635
at com.intellij.openapi.diagnostic.Logger.error(
at com.intellij.openapi.editor.impl.DocumentImpl.createRangeMarker(
at com.intellij.openapi.editor.impl.DocumentImpl.createRangeMarker(
at com.intellij.codeInsight.template.impl.TemplateState.start(
at com.intellij.codeInsight.template.impl.TemplateManagerImpl.lambda$startTemplate$1(



Hi Eugene,

To the first exception: you start the template over attribute which would be a container in your case and should contain everything else you add to the template. But then you add refs which are tags and I assume that they are not children of the attribute - so the exception.

When you pass the whole file, then the previous issue should be fixed. It looks like that the caret position is left as it was and it should be shifted as you replace your real values with variables.

That's all is very overcomplicated and it would be really better if the common scenario would be used instead. Why do you want to implement the template yourself and not reuse existing inplace renamer? 




Hi Anna,

I guess the answer for your question is "I don't know how to reuse existing in place renamer" :) Perhaps you can point me to an example?

Here's the situation. I have a tag named "flow" which is a declaration. Then I might have multiple "flow-ref" tags which are references to the declared flow. E.g. <flow name="Hello"/> and multiple <flow-ref name="Hello"/> in multiple XML files in my project. I've implemented FlowRefPsiReference class which extends PsiReferenceBase<XmlAttributeValue>. When I rename the attribute in one <flow-ref> , the rest of the attributes in other refs and in <flow> tag are renamed correctly. However, if I rename attribute in the <flow> tag, nothing happens. So for some reason it only works one way, from reference to the declaration, but not the other way around. That's why I implemented my own RenameHandler, extending the XmlTagRenameHandler. Just like in its parent, there is a rename dialog, which works perfectly, and the in place renamer, which I'm struggling with :)



Just to clarify, when I'm talking about renaming, I mean changing the value of the attribute "name", not renaming the XML tag itself or the attribute name.



I remember how you asked about ReferencesSearch, was there any progress? If you would find the references, then the inplace rename should work out of the box


No, unfortunately, I made no progress with ReferencesSearch, but I implemented my own way to search for reference tags. 

I guess for now I will disable the inplace renamer and leave only dialog option. 

I was even considering the option of implementing the entire language support, however, I cannot use any custom extension, my files still must be XML.  



I think that the problem was with resolve and isReferenceTo, did you check that methods and how they are invoked during references search?

Writing the language support is not that easy but you may take a look at JavaFX plugin: javafx is based on xml as well and there are a number of specific searches and references there.


Thanks, Anna, I'll take a look at the JavaFX!



Please sign in to leave a comment.