From e3fd6eb90065193d6bc0f3fbeefa6671d31978e7 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 8 Jul 2014 18:40:07 +0200 Subject: [PATCH] don't add a newline when completing with smart enter shortcut (IDEA-126726) --- .../smartEnter/JavaSmartEnterProcessor.java | 54 ++++++++++++------- .../smartEnter/PlainEnterProcessor.java | 42 +++++++++------ .../normal/SmartEnterNoNewLine.java | 9 ++++ .../normal/SmartEnterNoNewLine_after.java | 9 ++++ .../normal/SmartEnterWithNewLine.java | 9 ++++ .../normal/SmartEnterWithNewLine_after.java | 11 ++++ .../completion/NormalCompletionTest.groovy | 3 ++ .../smartEnter/SmartEnterProcessor.java | 4 ++ .../completion/CodeCompletionHandlerBase.java | 7 +-- 9 files changed, 106 insertions(+), 42 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/SmartEnterNoNewLine.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/SmartEnterNoNewLine_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/SmartEnterWithNewLine.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/SmartEnterWithNewLine_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java index cea07113fe24..90c26bc6073b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java @@ -47,7 +47,21 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.editorActions.smartEnter.JavaSmartEnterProcessor"); private static final Fixer[] ourFixers; - private static final EnterProcessor[] ourEnterProcessors; + private static final EnterProcessor[] ourEnterProcessors = { + new CommentBreakerEnterProcessor(), + new AfterSemicolonEnterProcessor(), + new LeaveCodeBlockEnterProcessor(), + new PlainEnterProcessor() + }; + private static final EnterProcessor[] ourAfterCompletionEnterProcessors = { + new AfterSemicolonEnterProcessor(), + new EnterProcessor() { + @Override + public boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) { + return PlainEnterProcessor.expandCodeBlock(editor, psiElement); + } + } + }; static { final List fixers = new ArrayList(); @@ -78,15 +92,7 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { fixers.add(new MissingArrayInitializerBraceFixer()); fixers.add(new MissingArrayConstructorBracketFixer()); fixers.add(new EnumFieldFixer()); - //ourFixers.add(new CompletionFixer()); ourFixers = fixers.toArray(new Fixer[fixers.size()]); - - List processors = new ArrayList(); - processors.add(new CommentBreakerEnterProcessor()); - processors.add(new AfterSemicolonEnterProcessor()); - processors.add(new LeaveCodeBlockEnterProcessor()); - processors.add(new PlainEnterProcessor()); - ourEnterProcessors = processors.toArray(new EnterProcessor[processors.size()]); } private int myFirstErrorOffset = Integer.MAX_VALUE; @@ -102,13 +108,22 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { public boolean process(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile psiFile) { FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.complete.statement"); + return invokeProcessor(editor, psiFile, false); + } + + @Override + public boolean processAfterCompletion(@NotNull Editor editor, @NotNull PsiFile psiFile) { + return invokeProcessor(editor, psiFile, true); + } + + private boolean invokeProcessor(Editor editor, PsiFile psiFile, boolean afterCompletion) { final Document document = editor.getDocument(); - final String textForRollback = document.getText(); + final CharSequence textForRollback = document.getImmutableCharSequence(); try { editor.putUserData(SMART_ENTER_TIMESTAMP, editor.getDocument().getModificationStamp()); myFirstErrorOffset = Integer.MAX_VALUE; mySkipEnter = false; - process(project, editor, psiFile, 0); + process(editor, psiFile, 0, afterCompletion); } catch (TooManyAttemptsException e) { document.replaceString(0, document.getTextLength(), textForRollback); @@ -118,8 +133,7 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { return true; } - - private void process(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file, final int attempt) throws TooManyAttemptsException { + private void process(@NotNull final Editor editor, @NotNull final PsiFile file, final int attempt, boolean afterCompletion) throws TooManyAttemptsException { if (attempt > MAX_ATTEMPTS) throw new TooManyAttemptsException(); try { @@ -148,18 +162,18 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { for (PsiElement psiElement : queue) { for (Fixer fixer : ourFixers) { fixer.apply(editor, this, psiElement); - if (LookupManager.getInstance(project).getActiveLookup() != null) { + if (LookupManager.getInstance(file.getProject()).getActiveLookup() != null) { return; } - if (isUncommited(project) || !psiElement.isValid()) { + if (isUncommited(file.getProject()) || !psiElement.isValid()) { moveCaretInsideBracesIfAny(editor, file); - process(project, editor, file, attempt + 1); + process(editor, file, attempt + 1, afterCompletion); return; } } } - doEnter(atCaret, editor); + doEnter(atCaret, editor, afterCompletion); } catch (IncorrectOperationException e) { LOG.error(e); @@ -187,7 +201,7 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { } - private void doEnter(PsiElement atCaret, Editor editor) throws IncorrectOperationException { + private void doEnter(PsiElement atCaret, Editor editor, boolean afterCompletion) throws IncorrectOperationException { final PsiFile psiFile = atCaret.getContainingFile(); final RangeMarker rangeMarker = createRangeMarker(atCaret); @@ -205,7 +219,7 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { } atCaret = CodeInsightUtil.findElementInRange(psiFile, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(), atCaret.getClass()); - for (EnterProcessor processor : ourEnterProcessors) { + for (EnterProcessor processor : afterCompletion ? ourAfterCompletionEnterProcessors : ourEnterProcessors) { if(atCaret == null){ // Can't restore element at caret after enter processor execution! break; @@ -214,7 +228,7 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { if (processor.doEnter(editor, atCaret, isModified(editor))) return; } - if (!isModified(editor)) { + if (!isModified(editor) && !afterCompletion) { plainEnter(editor); } else { if (myFirstErrorOffset == Integer.MAX_VALUE) { diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/PlainEnterProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/PlainEnterProcessor.java index 0fe58925d884..6bafcc00d983 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/PlainEnterProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/PlainEnterProcessor.java @@ -42,29 +42,37 @@ import org.jetbrains.annotations.Nullable; public class PlainEnterProcessor implements EnterProcessor { @Override public boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) { + if (expandCodeBlock(editor, psiElement)) return true; + + getEnterHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE).execute(editor, ((EditorEx)editor).getDataContext()); + return true; + } + + static boolean expandCodeBlock(Editor editor, PsiElement psiElement) { PsiCodeBlock block = getControlStatementBlock(editor.getCaretModel().getOffset(), psiElement); if (processExistingBlankLine(editor, block, psiElement)) { return true; } - EditorActionHandler enterHandler = getEnterHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE); - if (block != null) { - PsiElement firstElement = block.getFirstBodyElement(); - if (firstElement == null) { - firstElement = block.getRBrace(); - // Plain enter processor inserts enter after the end of line, hence, we don't want to use it here because the line ends with - // the empty braces block. So, we get the following in case of default handler usage: - // Before: - // if (condition[caret]) {} - // After: - // if (condition) {} - // [caret] - enterHandler = getEnterHandler(IdeActions.ACTION_EDITOR_ENTER); - } - editor.getCaretModel().moveToOffset(firstElement != null ? - firstElement.getTextRange().getStartOffset() : - block.getTextRange().getEndOffset()); + if (block == null) { + return false; } + EditorActionHandler enterHandler = getEnterHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE); + PsiElement firstElement = block.getFirstBodyElement(); + if (firstElement == null) { + firstElement = block.getRBrace(); + // Plain enter processor inserts enter after the end of line, hence, we don't want to use it here because the line ends with + // the empty braces block. So, we get the following in case of default handler usage: + // Before: + // if (condition[caret]) {} + // After: + // if (condition) {} + // [caret] + enterHandler = getEnterHandler(IdeActions.ACTION_EDITOR_ENTER); + } + editor.getCaretModel().moveToOffset(firstElement != null ? + firstElement.getTextRange().getStartOffset() : + block.getTextRange().getEndOffset()); enterHandler.execute(editor, ((EditorEx)editor).getDataContext()); return true; } diff --git a/java/java-tests/testData/codeInsight/completion/normal/SmartEnterNoNewLine.java b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterNoNewLine.java new file mode 100644 index 000000000000..49f9b65ec7b3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterNoNewLine.java @@ -0,0 +1,9 @@ + +class Tester { + private void build(EntityBuilder builder) { + } + + public void test1(int abc, int abd) { + System.out.println(a); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/SmartEnterNoNewLine_after.java b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterNoNewLine_after.java new file mode 100644 index 000000000000..ce2b8956d9ac --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterNoNewLine_after.java @@ -0,0 +1,9 @@ + +class Tester { + private void build(EntityBuilder builder) { + } + + public void test1(int abc, int abd) { + System.out.println(abc); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/SmartEnterWithNewLine.java b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterWithNewLine.java new file mode 100644 index 000000000000..d21cac7e5efa --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterWithNewLine.java @@ -0,0 +1,9 @@ + +class Tester { + private void build(EntityBuilder builder) { + } + + public void test1(int abc, int abd) { + if(a) + } +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/SmartEnterWithNewLine_after.java b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterWithNewLine_after.java new file mode 100644 index 000000000000..1b70dd9760a1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/SmartEnterWithNewLine_after.java @@ -0,0 +1,11 @@ + +class Tester { + private void build(EntityBuilder builder) { + } + + public void test1(int abc, int abd) { + if (abc) { + + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 79a94aaa8148..acd6bca0341f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -920,6 +920,9 @@ public class ListUtils { } public void testSmartEnterWrapsConstructorCall() throws Throwable { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) } + public void testSmartEnterNoNewLine() { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) } + public void testSmartEnterWithNewLine() { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) } + public void testTabReplacesMethodNameWithLocalVariableName() throws Throwable { doTest('\t'); } public void testMethodParameterAnnotationClass() throws Throwable { doTest(); } public void testPrimitiveCastOverwrite() throws Throwable { doTest '\t' } diff --git a/platform/lang-api/src/com/intellij/codeInsight/editorActions/smartEnter/SmartEnterProcessor.java b/platform/lang-api/src/com/intellij/codeInsight/editorActions/smartEnter/SmartEnterProcessor.java index 2e92ef17d017..0a3f5061034b 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/editorActions/smartEnter/SmartEnterProcessor.java +++ b/platform/lang-api/src/com/intellij/codeInsight/editorActions/smartEnter/SmartEnterProcessor.java @@ -36,6 +36,10 @@ import org.jetbrains.annotations.Nullable; public abstract class SmartEnterProcessor { public abstract boolean process(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile psiFile); + public boolean processAfterCompletion(@NotNull final Editor editor, @NotNull final PsiFile psiFile) { + return process(psiFile.getProject(), editor, psiFile); + } + protected void reformat(PsiElement atCaret) throws IncorrectOperationException { final TextRange range = atCaret.getTextRange(); final PsiFile file = atCaret.getContainingFile(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index e039cec3a424..e0a3be5f423d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -772,11 +772,8 @@ public class CodeCompletionHandlerBase { if (context.getCompletionChar() == Lookup.COMPLETE_STATEMENT_SELECT_CHAR) { final Language language = PsiUtilBase.getLanguageInEditor(editor, project); if (language != null) { - final List processors = SmartEnterProcessors.INSTANCE.forKey(language); - if (processors.size() > 0) { - for (SmartEnterProcessor processor : processors) { - processor.process(project, editor, indicator.getParameters().getOriginalFile()); - } + for (SmartEnterProcessor processor : SmartEnterProcessors.INSTANCE.forKey(language)) { + if (processor.processAfterCompletion(editor, indicator.getParameters().getOriginalFile())) break; } } }