From 8d4406a15e4c26f00594221794ba64c4a66d3306 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 19 Dec 2016 13:02:00 +0100 Subject: [PATCH 1/2] IDEA-165594 Auto completion adds unnecessary spaces before and after colon in case of method reference --- .../codeInsight/lookup/VariableLookupItem.java | 7 ++++++- .../normal/FinishMethodReferenceWithColon.java | 11 +++++++++++ .../normal/FinishMethodReferenceWithColon_after.java | 11 +++++++++++ .../completion/Normal8CompletionTest.groovy | 6 ++++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/FinishMethodReferenceWithColon.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/FinishMethodReferenceWithColon_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java b/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java index 9c652cfb82aa..f582048e9f82 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java @@ -215,7 +215,7 @@ public class VariableLookupItem extends LookupItem implements Typed TailType.COMMA.processTail(context.getEditor(), context.getTailOffset()); AutoPopupController.getInstance(context.getProject()).autoPopupParameterInfo(context.getEditor(), null); } - else if (completionChar == ':' && getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailType.UNKNOWN) { + else if (completionChar == ':' && getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailType.UNKNOWN && isTernaryCondition(ref)) { context.setAddCompletionChar(false); TailType.COND_EXPR_COLON.processTail(context.getEditor(), context.getTailOffset()); } @@ -231,6 +231,11 @@ public class VariableLookupItem extends LookupItem implements Typed } } + private static boolean isTernaryCondition(PsiReferenceExpression ref) { + PsiElement parent = ref == null ? null : ref.getParent(); + return parent instanceof PsiConditionalExpression && ref == ((PsiConditionalExpression)parent).getThenExpression(); + } + public static void makeFinalIfNeeded(@NotNull InsertionContext context, @NotNull PsiVariable variable) { PsiElement place = context.getFile().findElementAt(context.getTailOffset() - 1); if (!Registry.is("java.completion.make.outer.variables.final") || diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/FinishMethodReferenceWithColon.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/FinishMethodReferenceWithColon.java new file mode 100644 index 000000000000..8f03430b7345 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/FinishMethodReferenceWithColon.java @@ -0,0 +1,11 @@ +import java.util.stream.Stream; + +abstract class ColonAutoComplete { + void dv(Stream stream, Checker checker) { + stream.filter(checkx) + } +} + +interface Checker { + public boolean check(Integer s); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/FinishMethodReferenceWithColon_after.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/FinishMethodReferenceWithColon_after.java new file mode 100644 index 000000000000..856ad34cd8be --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/FinishMethodReferenceWithColon_after.java @@ -0,0 +1,11 @@ +import java.util.stream.Stream; + +abstract class ColonAutoComplete { + void dv(Stream stream, Checker checker) { + stream.filter(checker:x) + } +} + +interface Checker { + public boolean check(Integer s); +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/Normal8CompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/Normal8CompletionTest.groovy index 85a07c74b31d..890a05549f95 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/Normal8CompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/Normal8CompletionTest.groovy @@ -265,4 +265,10 @@ class Test88 { myFixture.type('l\t') checkResultByFile(getTestName(false) + "_after.java") } + + void testFinishMethodReferenceWithColon() { + configureByTestName() + myFixture.type(':') + checkResultByFile(getTestName(false) + "_after.java") + } } \ No newline at end of file From ff87813589758ca0f0e5fdfcc79f617223a62244 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 19 Dec 2016 15:25:48 +0100 Subject: [PATCH 2/2] make create file actions WriteActionAware to avoid showing dialogs in write actions (IDEA-165398) --- .../ide/actions/CreateClassAction.java | 5 + .../com/intellij/ide/util/PackageUtil.java | 7 +- .../impl/file/JavaDirectoryServiceImpl.java | 4 + .../ide/actions/CreateElementActionBase.java | 8 +- .../intellij/ide/actions/ElementCreator.java | 94 +++++++++---------- .../ide/actions/CreateFileAction.java | 11 ++- .../actions/CreateFileFromTemplateDialog.java | 7 ++ .../ide/actions/CreateFromTemplateAction.java | 8 +- .../CreateTemplateInPackageAction.java | 9 +- .../actions/GeneratePluginClassAction.java | 5 + 10 files changed, 92 insertions(+), 66 deletions(-) diff --git a/java/java-impl/src/com/intellij/ide/actions/CreateClassAction.java b/java/java-impl/src/com/intellij/ide/actions/CreateClassAction.java index 7100e2d7505b..6e998df1000f 100644 --- a/java/java-impl/src/com/intellij/ide/actions/CreateClassAction.java +++ b/java/java-impl/src/com/intellij/ide/actions/CreateClassAction.java @@ -100,6 +100,11 @@ public class CreateClassAction extends JavaCreateTemplateInPackageAction additionalProperties) throws IncorrectOperationException { + if (askToDefineVariables) { + LOG.assertTrue(!ApplicationManager.getApplication().isWriteAccessAllowed()); + } //checkCreateClassOrInterface(dir, name); Project project = dir.getProject(); diff --git a/platform/lang-api/src/com/intellij/ide/actions/CreateElementActionBase.java b/platform/lang-api/src/com/intellij/ide/actions/CreateElementActionBase.java index 6655980346b5..0af5ababd162 100644 --- a/platform/lang-api/src/com/intellij/ide/actions/CreateElementActionBase.java +++ b/platform/lang-api/src/com/intellij/ide/actions/CreateElementActionBase.java @@ -19,6 +19,7 @@ package com.intellij.ide.actions; import com.intellij.ide.IdeView; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.application.WriteActionAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.InputValidator; import com.intellij.openapi.util.text.StringUtil; @@ -34,7 +35,7 @@ import javax.swing.*; * * @since 5.1 */ -public abstract class CreateElementActionBase extends CreateInDirectoryActionBase { +public abstract class CreateElementActionBase extends CreateInDirectoryActionBase implements WriteActionAware { protected CreateElementActionBase() { } @@ -109,6 +110,11 @@ public abstract class CreateElementActionBase extends CreateInDirectoryActionBas return CreateElementActionBase.this.create(newName, myDirectory); } + @Override + public boolean startInWriteAction() { + return CreateElementActionBase.this.startInWriteAction(); + } + @Override public String getActionName(String newName) { return CreateElementActionBase.this.getActionName(myDirectory, newName); diff --git a/platform/lang-api/src/com/intellij/ide/actions/ElementCreator.java b/platform/lang-api/src/com/intellij/ide/actions/ElementCreator.java index 935b0b0a051b..5d1ac159d889 100644 --- a/platform/lang-api/src/com/intellij/ide/actions/ElementCreator.java +++ b/platform/lang-api/src/com/intellij/ide/actions/ElementCreator.java @@ -20,26 +20,28 @@ import com.intellij.CommonBundle; import com.intellij.history.LocalHistory; import com.intellij.history.LocalHistoryAction; import com.intellij.ide.IdeBundle; -import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.application.WriteActionAware; +import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.UndoConfirmationPolicy; -import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.SmartPointerManager; import com.intellij.psi.SmartPsiElementPointer; -import com.intellij.psi.util.PsiUtilCore; -import com.intellij.util.SmartList; +import com.intellij.util.ThrowableRunnable; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; /** * @author peter */ -public abstract class ElementCreator { +public abstract class ElementCreator implements WriteActionAware { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.ElementCreator"); private final Project myProject; private final String myErrorTitle; @@ -59,52 +61,48 @@ public abstract class ElementCreator { return PsiElement.EMPTY_ARRAY; } - final Exception[] exception = new Exception[1]; - final SmartPsiElementPointer[][] myCreatedElements = {null}; - - final String commandName = getActionName(inputString); - new WriteCommandAction(myProject, commandName) { - @Override - protected void run(@NotNull Result result) throws Throwable { - LocalHistoryAction action = LocalHistoryAction.NULL; - try { - action = LocalHistory.getInstance().startAction(commandName); - - PsiElement[] psiElements = create(inputString); - myCreatedElements[0] = new SmartPsiElementPointer[psiElements.length]; - SmartPointerManager manager = SmartPointerManager.getInstance(myProject); - for (int i = 0; i < myCreatedElements[0].length; i++) { - myCreatedElements[0][i] = manager.createSmartPsiElementPointer(psiElements[i]); - } - } - catch (Exception ex) { - exception[0] = ex; - } - finally { - action.finish(); - } - } - - @Override - protected UndoConfirmationPolicy getUndoConfirmationPolicy() { - return UndoConfirmationPolicy.REQUEST_CONFIRMATION; - } - }.execute(); - - if (exception[0] != null) { - LOG.info(exception[0]); - String errorMessage = CreateElementActionBase.filterMessage(exception[0].getMessage()); - if (errorMessage == null || errorMessage.length() == 0) { - errorMessage = exception[0].toString(); - } - Messages.showMessageDialog(myProject, errorMessage, myErrorTitle, Messages.getErrorIcon()); + Ref> createdElements = Ref.create(); + Exception exception = executeCommand(getActionName(inputString), () -> { + PsiElement[] psiElements = create(inputString); + SmartPointerManager manager = SmartPointerManager.getInstance(myProject); + createdElements.set(ContainerUtil.map(psiElements, manager::createSmartPsiElementPointer)); + }); + if (exception != null) { + handleException(exception); return PsiElement.EMPTY_ARRAY; } - List result = new SmartList<>(); - for (final SmartPsiElementPointer pointer : myCreatedElements[0]) { - ContainerUtil.addIfNotNull(result, pointer.getElement()); + return ContainerUtil.mapNotNull(createdElements.get(), SmartPsiElementPointer::getElement).toArray(PsiElement.EMPTY_ARRAY); + } + + @Nullable + private Exception executeCommand(String commandName, ThrowableRunnable invokeCreate) { + final Exception[] exception = new Exception[1]; + CommandProcessor.getInstance().executeCommand(myProject, () -> { + LocalHistoryAction action = LocalHistory.getInstance().startAction(commandName); + try { + if (startInWriteAction()) { + WriteAction.run(invokeCreate); + } else { + invokeCreate.run(); + } + } + catch (Exception ex) { + exception[0] = ex; + } + finally { + action.finish(); + } + }, commandName, null, UndoConfirmationPolicy.REQUEST_CONFIRMATION); + return exception[0]; + } + + private void handleException(Exception t) { + LOG.info(t); + String errorMessage = CreateElementActionBase.filterMessage(t.getMessage()); + if (errorMessage == null || errorMessage.length() == 0) { + errorMessage = t.toString(); } - return PsiUtilCore.toPsiElementArray(result); + Messages.showMessageDialog(myProject, errorMessage, myErrorTitle, Messages.getErrorIcon()); } } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileAction.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileAction.java index b55241004bfc..b5f1932e7ae2 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileAction.java @@ -20,6 +20,7 @@ import com.intellij.icons.AllIcons; import com.intellij.ide.IdeBundle; import com.intellij.internal.statistic.UsageTrigger; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.ex.FileTypeChooser; import com.intellij.openapi.project.DumbAware; @@ -80,7 +81,12 @@ public class CreateFileAction extends CreateElementActionBase implements DumbAwa @NotNull protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { MkDirs mkdirs = new MkDirs(newName, directory); - return new PsiElement[]{mkdirs.directory.createFile(getFileName(mkdirs.newName))}; + return new PsiElement[]{WriteAction.compute(() -> mkdirs.directory.createFile(getFileName(mkdirs.newName)))}; + } + + public static PsiDirectory findOrCreateSubdirectory(@NotNull PsiDirectory parent, @NotNull String subdirName) { + final PsiDirectory sub = parent.findSubdirectory(subdirName); + return sub == null ? WriteAction.compute(() -> parent.createSubdirectory(subdirName)) : sub; } public static class MkDirs { @@ -109,8 +115,7 @@ public class CreateFileAction extends CreateElementActionBase implements DumbAwa directory = parentDirectory; } else if (!".".equals(dir)){ - final PsiDirectory sub = directory.findSubdirectory(dir); - directory = sub == null ? directory.createSubdirectory(dir) : sub; + directory = findOrCreateSubdirectory(directory, dir); } firstToken = false; } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java index e584694f946d..ec072cbdb78c 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java @@ -174,6 +174,11 @@ public class CreateFileFromTemplateDialog extends DialogWrapper { return PsiElement.EMPTY_ARRAY; } + @Override + public boolean startInWriteAction() { + return creator.startInWriteAction(); + } + @Override protected String getActionName(String newName) { return creator.getActionName(newName, myDialog.getKindCombo().getSelectedName()); @@ -211,5 +216,7 @@ public class CreateFileFromTemplateDialog extends DialogWrapper { @NotNull String getActionName(@NotNull String name, @NotNull String templateName); + + boolean startInWriteAction(); } } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateFromTemplateAction.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateFromTemplateAction.java index e17a49944cba..5c07768d9dc0 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateFromTemplateAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateFromTemplateAction.java @@ -19,6 +19,7 @@ import com.intellij.CommonBundle; import com.intellij.ide.IdeView; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.WriteActionAware; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileDocumentManager; @@ -38,7 +39,7 @@ import java.util.Map; /** * @author Eugene.Kudelevsky */ -public abstract class CreateFromTemplateAction extends AnAction { +public abstract class CreateFromTemplateAction extends AnAction implements WriteActionAware { protected static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.CreateFromTemplateAction"); public CreateFromTemplateAction(String text, String description, Icon icon) { @@ -72,6 +73,11 @@ public abstract class CreateFromTemplateAction extends AnA return CreateFromTemplateAction.this.createFile(name, templateName, dir); } + @Override + public boolean startInWriteAction() { + return CreateFromTemplateAction.this.startInWriteAction(); + } + @Override @NotNull public String getActionName(@NotNull String name, @NotNull String templateName) { diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateTemplateInPackageAction.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateTemplateInPackageAction.java index be851472ac07..2d32ca5521ec 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateTemplateInPackageAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateTemplateInPackageAction.java @@ -89,14 +89,7 @@ public abstract class CreateTemplateInPackageAction extend String[] names = className.split("\\."); for (int i = 0; i < names.length - 1; i++) { - String name = names[i]; - PsiDirectory subDir = dir.findSubdirectory(name); - - if (subDir == null) { - subDir = dir.createSubdirectory(name); - } - - dir = subDir; + dir = CreateFileAction.findOrCreateSubdirectory(dir, names[i]); } className = names[names.length - 1]; diff --git a/plugins/devkit/src/actions/GeneratePluginClassAction.java b/plugins/devkit/src/actions/GeneratePluginClassAction.java index ad51b8074a05..807e52f27987 100644 --- a/plugins/devkit/src/actions/GeneratePluginClassAction.java +++ b/plugins/devkit/src/actions/GeneratePluginClassAction.java @@ -127,6 +127,11 @@ public abstract class GeneratePluginClassAction extends CreateElementActionBase return fileIndex.getModuleForFile(vFile); } + @Override + public boolean startInWriteAction() { + return false; + } + @NotNull protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { final Project project = directory.getProject();