Run live template programmatically in another file (and replace variable)

Hi,

use-case is following: I have XML file and being on certain XML attribute I want to generate function (live template) in script file named according to that XML attribute. So far I have code

@Override
public void actionPerformed(AnActionEvent e) {
if (e == null) {
return;
}
final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
final Document document = editor.getDocument();

PsiFile psiFile = PsiDocumentManager.getInstance(e.getProject()).getPsiFile(document);
int caret = editor.getCaretModel().getOffset();
PsiElement element = PsiTreeUtil.getParentOfType(psiFile.findElementAt(caret), XmlAttributeValue.class);

String vslFileName = getValueFromElement(element, 0);
String vsfFunctionName = getValueFromElement(element, 1);
PsiFile vslFile = getVslFile(vslFileName, e.getProject());
if (StringUtils.isNotEmpty(vslFileName) && vslFile != null) {
int lineCount = countLines(vslFile.getText());
OpenFileDescriptor openFileDesc = new OpenFileDescriptor(e.getProject(), vslFile.getVirtualFile(), lineCount + 2, 0);
Editor vslEditor = FileEditorManager.getInstance(e.getProject()).openTextEditor(openFileDesc, true);
Template vslTemplate = TemplateSettings.getInstance().getTemplate("func", "VSL");

if (vslEditor != null && vslTemplate != null) {
TemplateManager.getInstance(e.getProject()).startTemplate(vslEditor, vslTemplate);
}
}
}

 

It's basically working but there are 2 major issues:

1) It won't add generated code from live template at the end of file if last row is "}". There has to be also empty row after, otherwise is breaks previous function.

2) I want to have pre-filled function name so basically replace $FUNCNAME$ in code. I can do that for FileTemplate because there is setText() method but not for Template.

 

Thank you very much.

  

 

7 comments
Comment actions Permalink

Hi.

 

1) As soon as you have an editor, you free to add any modification in document, including adding new line at the end of the file and putting caret just before it.

2) There is a com.intellij.codeInsight.template.TemplateManager#startTemplate method that accepts map of predefined variables.

0
Comment actions Permalink

2) Obviously I overlooked it, thx.

1) Not so easy, Document#setText method is complaining on read-only mode. Has to be modified in separate thread? Please would you like to post some example code how to safely add new lines  - related my code.

0
Comment actions Permalink

Please provide full output. If the document is really in read-only mode, you won't be able to run live template on it as well.

 

Btw, I'd rather use insertString/replaceString method or EditorModificationUtil instead of setText.

0
Comment actions Permalink

Oh...I was wrong about exception, it's:

Assertion failed: Write access is allowed inside write-action only (see com.intellij.openapi.application.Application.runWriteAction())
java.lang.Throwable
at com.intellij.openapi.diagnostic.Logger.assertTrue(Logger.java:168)
at com.intellij.openapi.application.impl.ApplicationImpl.assertWriteAccessAllowed(ApplicationImpl.java:1334)
at com.intellij.openapi.editor.impl.DocumentImpl.assertWriteAccess(DocumentImpl.java:611)
at com.intellij.openapi.editor.impl.DocumentImpl.replaceString(DocumentImpl.java:543)
at com.intellij.openapi.editor.impl.DocumentImpl.lambda$setText$3(DocumentImpl.java:975)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:149)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:109)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:99)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:85)
at com.intellij.openapi.editor.impl.DocumentImpl.setText(DocumentImpl.java:980)
at com.vendavo.plugin.actions.GenerateVslFunction.actionPerformed(GenerateVslFunction.java:53)
at com.intellij.openapi.actionSystem.ex.ActionUtil$1.run(ActionUtil.java:215)
at com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAware(ActionUtil.java:232)
at com.intellij.ui.popup.PopupFactoryImpl$ActionPopupStep.performAction(PopupFactoryImpl.java:974)
at com.intellij.ui.popup.PopupFactoryImpl$ActionPopupStep.performAction(PopupFactoryImpl.java:964)
at com.intellij.ui.popup.PopupFactoryImpl$ActionPopupStep.lambda$onChosen$0(PopupFactoryImpl.java:958)
at com.intellij.openapi.application.TransactionGuardImpl.performUserActivity(TransactionGuardImpl.java:193)
at com.intellij.ui.popup.AbstractPopup.lambda$null$7(AbstractPopup.java:1396)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:827)
at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:655)
at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:365)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

 

I will try EditorModificationUtil way.

0
Comment actions Permalink

Well, the exception says what you should to do: wrap document modification with com.intellij.openapi.application.Application.runWriteAction().

Also CommandProcessor.getInstance.executeCommand would be useful in future, just read exception messages carefully ;)

0
Comment actions Permalink

Ouch, very sorry...so lame. So final code might be:

CommandProcessor.getInstance().executeCommand(
e.getProject(), () -> ApplicationManager.getApplication().runWriteAction(() -> {
try {
EditorModificationUtil.insertStringAtCaret(vslEditor, " \\\n\\\n");
}
catch (Exception ex) {
ex.printStackTrace();
}
}),
"Add new lines at the end of vsl script file",
null
);

Only issue is how to insert new lines, I tried "\r\n", separate, both...it does not work correctly - it removes last character of editor "}" and there is one backslash extra :(

 

0
Comment actions Permalink

> Only issue is how to insert new lines

"\n" is the new line. "\r" should not be used.

Btw, using WriteAction#runWriteCommandAction might be more handy. 

0

Please sign in to leave a comment.