IDEA-45030 Add ability to specify cursor position in File Templates (e.g. special macros as for Live Templates)

This commit is contained in:
Dmitry Avdeev
2015-09-17 16:10:23 +03:00
parent 027802f13a
commit 555c34b6d1
8 changed files with 114 additions and 19 deletions
@@ -30,6 +30,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Nullable;
@@ -98,7 +99,7 @@ public class CreatePackageInfoAction extends CreateFromTemplateActionBase implem
final PsiPackage aPackage = directoryService.getPackage(directory);
if (aPackage != null) {
final String qualifiedName = aPackage.getQualifiedName();
if (com.intellij.openapi.util.text.StringUtil.isEmpty(qualifiedName) || nameHelper.isQualifiedName(qualifiedName)) {
if (StringUtil.isEmpty(qualifiedName) || nameHelper.isQualifiedName(qualifiedName)) {
return true;
}
}
@@ -114,12 +115,6 @@ public class CreatePackageInfoAction extends CreateFromTemplateActionBase implem
return new AttributesDefaults(FileTemplateUtil.INTERNAL_PACKAGE_INFO_TEMPLATE_NAME).withFixedName(true);
}
@Nullable
@Override
protected AnAction getReplacedAction(FileTemplate selectedTemplate) {
return null;
}
@Override
protected FileTemplate getTemplate(Project project, PsiDirectory dir) {
return FileTemplateManager.getInstance(project).getInternalTemplate(FileTemplateUtil.INTERNAL_PACKAGE_INFO_TEMPLATE_NAME);
@@ -0,0 +1,58 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.fileTemplates;
import com.intellij.ide.fileTemplates.actions.CreateFromTemplateActionBase;
import com.intellij.ide.fileTemplates.impl.CustomFileTemplate;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
/**
* @author Dmitry Avdeev
*/
public class EmbeddedLiveTemplatesTest extends LightPlatformCodeInsightFixtureTestCase {
public void testCreateFromTemplateAction() throws Exception {
myFixture.configureByText(PlainTextFileType.INSTANCE, "");
CustomFileTemplate template = new CustomFileTemplate("foo", "txt");
template.setText("Put caret here: #[[$END$]]# end of template");
template.setHasEmbeddedLiveTemplate(true);
myFixture.testAction(new TestAction(template));
VirtualFile[] files = FileEditorManager.getInstance(getProject()).getSelectedFiles();
myFixture.openFileInEditor(files[0]);
myFixture.checkResult("Put caret here: <caret> end of template");
}
private static class TestAction extends CreateFromTemplateActionBase {
private final FileTemplate myTemplate;
public TestAction(FileTemplate template) {
super("", "", null);
myTemplate = template;
}
@Override
protected FileTemplate getTemplate(Project project, PsiDirectory dir) {
return myTemplate;
}
}
}
@@ -81,6 +81,10 @@ public interface FileTemplate extends Cloneable {
void setReformatCode(boolean reformat);
boolean isHasEmbeddedLiveTemplate();
void setHasEmbeddedLiveTemplate(boolean hasEmbeddedLiveTemplate);
FileTemplate clone();
@NotNull String[] getUnsetAttributes(@NotNull Properties properties, Project project) throws ParseException;
@@ -18,12 +18,10 @@ package com.intellij.ide.fileTemplates.actions;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import org.jetbrains.annotations.Nullable;
public class CreateFromTemplateAction extends CreateFromTemplateActionBase {
@@ -34,12 +32,6 @@ public class CreateFromTemplateAction extends CreateFromTemplateActionBase {
myTemplate = template;
}
@Override
@Nullable
protected AnAction getReplacedAction(final FileTemplate template) {
return null;
}
@Override
protected FileTemplate getTemplate(final Project project, final PsiDirectory dir) {
return myTemplate;
@@ -16,15 +16,24 @@
package com.intellij.ide.fileTemplates.actions;
import com.intellij.codeInsight.template.TemplateManager;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.ide.IdeView;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.ui.CreateFromTemplateDialog;
import com.intellij.ide.util.DirectoryChooserUtil;
import com.intellij.openapi.actionSystem.*;
import com.intellij.ide.util.EditorHelper;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
@@ -48,7 +57,7 @@ public abstract class CreateFromTemplateActionBase extends AnAction {
Project project = dir.getProject();
FileTemplate selectedTemplate = getTemplate(project, dir);
if(selectedTemplate != null){
if (selectedTemplate != null){
AnAction action = getReplacedAction(selectedTemplate);
if (action != null) {
action.actionPerformed(e);
@@ -62,18 +71,39 @@ public abstract class CreateFromTemplateActionBase extends AnAction {
if (createdElement != null) {
elementCreated(dialog, createdElement);
view.selectElement(createdElement);
if (selectedTemplate.isHasEmbeddedLiveTemplate() && createdElement instanceof PsiFile) {
startLiveTemplate((PsiFile)createdElement);
}
}
}
}
}
private static void startLiveTemplate(PsiFile file) {
Project project = file.getProject();
final Editor editor = EditorHelper.openInEditor(file);
if (editor == null) return;
final TemplateImpl template = new TemplateImpl("", file.getText(), "");
template.setInline(true);
WriteCommandAction.runWriteCommandAction(project, new Runnable() {
@Override
public void run() {
editor.getDocument().setText(template.getTemplateText());
}
});
TemplateManager.getInstance(project).startTemplate(editor, template);
}
@Nullable
protected PsiDirectory getTargetDirectory(DataContext dataContext, IdeView view) {
return DirectoryChooserUtil.getOrChooseDirectory(view);
}
@Nullable
protected abstract AnAction getReplacedAction(final FileTemplate selectedTemplate);
protected AnAction getReplacedAction(final FileTemplate selectedTemplate) {
return null;
}
protected abstract FileTemplate getTemplate(final Project project, final PsiDirectory dir);
@@ -39,6 +39,7 @@ public abstract class FileTemplateBase implements FileTemplate {
@Nullable
private String myText;
private boolean myShouldReformatCode = DEFAULT_REFORMAT_CODE_VALUE;
private boolean myHasEmbeddedLiveTemplate;
@Override
public final boolean isReformatCode() {
@@ -113,4 +114,14 @@ public abstract class FileTemplateBase implements FileTemplate {
public boolean isTemplateOfType(@NotNull final FileType fType) {
return fType.equals(FileTypeManagerEx.getInstanceEx().getFileTypeByExtension(getExtension()));
}
@Override
public boolean isHasEmbeddedLiveTemplate() {
return myHasEmbeddedLiveTemplate;
}
@Override
public void setHasEmbeddedLiveTemplate(boolean hasEmbeddedLiveTemplate) {
myHasEmbeddedLiveTemplate = hasEmbeddedLiveTemplate;
}
}
@@ -29,6 +29,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
@@ -90,6 +91,11 @@ public class CreateFromTemplateDialog extends DialogWrapper {
}
public PsiElement create(){
if (ApplicationManager.getApplication().isUnitTestMode()) {
doCreate(myTemplate.getName() + "." + myTemplate.getExtension());
Disposer.dispose(getDisposable());
return myCreatedElement;
}
if (myAttrPanel != null) {
if (myAttrPanel.hasSomethingToAsk()) {
show();
@@ -1610,7 +1610,6 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
Editor editor = instance.openTextEditor(new OpenFileDescriptor(project, file), false);
if (editor != null) {
editor.getCaretModel().moveToOffset(0);
DaemonCodeAnalyzer.getInstance(getProject()).restart();
}
return editor;