multi-caret typing fixes for Java files

This commit is contained in:
Dmitry Batrak
2014-03-26 12:41:22 +04:00
parent 8b26747557
commit eb997fbc03
22 changed files with 300 additions and 72 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -20,7 +20,6 @@ import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.completion.JavaClassReferenceCompletionContributor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.fileTypes.FileType;
@@ -132,7 +131,7 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
return Result.CONTINUE;
}
if (PsiTreeUtil.getParentOfType(leaf, PsiCodeBlock.class, false, PsiMember.class) != null) {
EditorModificationUtil.insertStringAtCaret(editor, "{", false, true);
EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, "{");
TypedHandler.indentOpenedBrace(project, editor);
return Result.STOP;
}
@@ -170,8 +169,7 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
char charAt = editor.getDocument().getCharsSequence().charAt(offset);
if (charAt != ';') return false;
editor.getCaretModel().moveToOffset(offset + 1);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
EditorModificationUtil.moveAllCaretsRelatively(editor, 1);
return true;
}
@@ -211,8 +209,7 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
}
if (balance == 0) {
editor.getCaretModel().moveToOffset(offset + 1);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
EditorModificationUtil.moveAllCaretsRelatively(editor, 1);
return true;
}
@@ -251,7 +248,12 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
}
if (balance == 1) {
editor.getDocument().insertString(offset, ">");
if (editor.getCaretModel().supportsMultipleCarets()) {
EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, ">", 0);
}
else {
editor.getDocument().insertString(offset, ">");
}
}
}

View File

@@ -0,0 +1,8 @@
class Foo {
void m() {
if (true)
{<caret>
if (true)
{<caret>
}
}

View File

@@ -0,0 +1,8 @@
class Foo {
void m() {
if (true)
<caret>
if (true)
<caret>
}
}

View File

@@ -0,0 +1,10 @@
class Foo {
void m() {
if (true) {
System.out.println();
}<caret>
if (true) {
System.out.println();
}<caret>
}
}

View File

@@ -0,0 +1,10 @@
class Foo {
void m() {
if (true) {
System.out.println();
<caret>
if (true) {
System.out.println();
<caret>
}
}

View File

@@ -0,0 +1,10 @@
class Foo {
void m() {
if (true) {
List<<caret>>
}
if (true) {
List<<caret>>
}
}
}

View File

@@ -0,0 +1,10 @@
class Foo {
void m() {
if (true) {
List<caret>
}
if (true) {
List<caret>
}
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.out.println("<caret>");
System.out.println("<caret>");
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.out.println(<caret>);
System.out.println(<caret>);
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.exit(<caret>)
System.exit(<caret>)
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.exit<caret>
System.exit<caret>
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
java.util.List<String> l1 = new java.util.ArrayList<><caret>();
java.util.List<String> l2 = new java.util.ArrayList<><caret>();
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
java.util.List<String> l1 = new java.util.ArrayList<<caret>>();
java.util.List<String> l2 = new java.util.ArrayList<<caret>>();
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.out.println(""<caret>);
System.out.println(""<caret>);
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.out.println("<caret>");
System.out.println("<caret>");
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.exit()<caret>
System.exit()<caret>
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.exit(<caret>)
System.exit(<caret>)
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.out.println();<caret>
System.out.println();<caret>
}
}

View File

@@ -0,0 +1,6 @@
class Foo {
void m() {
System.out.println()<caret>;
System.out.println()<caret>;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2000-2014 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.codeInsight;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
import java.io.File;
public class JavaTypingTest extends LightPlatformCodeInsightFixtureTestCase {
public void testMulticaretIndentLBrace() {
doTest('{');
}
public void testMulticaretIndentRBrace() {
doTest('}');
}
public void testMulticaretSkipSemicolon() {
doTest(';');
}
public void testMulticaretSkipGt() {
doTest('>');
}
public void testMulticaretInsertGt() {
doTest('<');
}
public void testMulticaretSkipRParen() {
doTest(')');
}
public void testMulticaretInsertRParen() {
doTest('(');
}
public void testMulticaretSkipQuote() {
doTest('"');
}
public void testMulticaretInsertQuote() {
doTest('"');
}
private void doTest(char c) {
myFixture.configureByFile(getTestName(true) + "_before.java");
myFixture.type(c);
myFixture.checkResultByFile(getTestName(true) + "_after.java");
}
@Override
protected String getTestDataPath() {
return PlatformTestUtil.getCommunityPath().replace(File.separatorChar, '/') + "/java/java-tests/testData/codeInsight/typing";
}
}

View File

@@ -191,7 +191,7 @@ public class TypedHandler extends TypedActionHandlerBase {
}
}
if (!editor.getSelectionModel().hasBlockSelection() && editor.getCaretModel().getCaretCount() == 1) {
if (!editor.getSelectionModel().hasBlockSelection()) {
if (')' == charTyped || ']' == charTyped || '}' == charTyped) {
if (FileTypes.PLAIN_TEXT != fileType) {
if (handleRParen(editor, fileType, charTyped)) return;
@@ -210,7 +210,7 @@ public class TypedHandler extends TypedActionHandlerBase {
if (('(' == charTyped || '[' == charTyped || '{' == charTyped) &&
CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET &&
!editor.getSelectionModel().hasBlockSelection() && editor.getCaretModel().getCaretCount() == 1 && fileType != FileTypes.PLAIN_TEXT) {
!editor.getSelectionModel().hasBlockSelection() && fileType != FileTypes.PLAIN_TEXT) {
handleAfterLParen(editor, fileType, charTyped);
}
else if ('}' == charTyped) {
@@ -361,7 +361,12 @@ public class TypedHandler extends TypedActionHandlerBase {
else {
throw new AssertionError("Unknown char "+lparenChar);
}
editor.getDocument().insertString(offset, text);
if (editor.getCaretModel().supportsMultipleCarets()) {
EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, text, 0);
}
else {
editor.getDocument().insertString(offset, text);
}
}
}
@@ -417,8 +422,7 @@ public class TypedHandler extends TypedActionHandlerBase {
if (!matched) return false;
editor.getCaretModel().moveToOffset(offset + 1);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
EditorModificationUtil.moveAllCaretsRelatively(editor, 1);
return true;
}
@@ -436,8 +440,7 @@ public class TypedHandler extends TypedActionHandlerBase {
if (offset < length && chars.charAt(offset) == quote){
if (isClosingQuote(editor, quoteHandler, offset)){
editor.getCaretModel().moveToOffset(offset + 1);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
EditorModificationUtil.moveAllCaretsRelatively(editor, 1);
return true;
}
}
@@ -456,7 +459,9 @@ public class TypedHandler extends TypedActionHandlerBase {
}
}
myOriginalHandler.execute(editor, quote, dataContext);
if (myOriginalHandler != null) {
myOriginalHandler.execute(editor, quote, dataContext);
}
offset = editor.getCaretModel().getOffset();
if (quoteHandler instanceof MultiCharQuoteHandler) {
@@ -464,7 +469,12 @@ public class TypedHandler extends TypedActionHandlerBase {
if (closingQuote != null && hasNonClosedLiterals(editor, quoteHandler, offset - 1)) {
if (offset == document.getTextLength() ||
!Character.isUnicodeIdentifierPart(document.getCharsSequence().charAt(offset))) { //any better heuristic or an API?
document.insertString(offset, closingQuote);
if (editor.getCaretModel().supportsMultipleCarets()) {
EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, closingQuote.toString(), 0);
}
else {
document.insertString(offset, closingQuote);
}
return true;
}
}
@@ -473,7 +483,12 @@ public class TypedHandler extends TypedActionHandlerBase {
if (isOpeningQuote(editor, quoteHandler, offset - 1) && hasNonClosedLiterals(editor, quoteHandler, offset - 1)) {
if (offset == document.getTextLength() ||
!Character.isUnicodeIdentifierPart(document.getCharsSequence().charAt(offset))) { //any better heuristic or an API?
document.insertString(offset, String.valueOf(quote));
if (editor.getCaretModel().supportsMultipleCarets()) {
EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, String.valueOf(quote), 0);
}
else {
document.insertString(offset, String.valueOf(quote));
}
}
}
@@ -558,67 +573,72 @@ public class TypedHandler extends TypedActionHandlerBase {
}
private static void indentBrace(@NotNull final Project project, @NotNull final Editor editor, final char braceChar) {
final int offset = editor.getCaretModel().getOffset() - 1;
final Document document = editor.getDocument();
CharSequence chars = document.getCharsSequence();
if (offset < 0 || chars.charAt(offset) != braceChar) return;
editor.getCaretModel().runForEachCaret(new CaretAction() {
@Override
public void perform(Caret caret) {
final int offset = editor.getCaretModel().getOffset() - 1;
final Document document = editor.getDocument();
CharSequence chars = document.getCharsSequence();
if (offset < 0 || chars.charAt(offset) != braceChar) return;
int spaceStart = CharArrayUtil.shiftBackward(chars, offset - 1, " \t");
if (spaceStart < 0 || chars.charAt(spaceStart) == '\n' || chars.charAt(spaceStart) == '\r'){
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
documentManager.commitDocument(document);
int spaceStart = CharArrayUtil.shiftBackward(chars, offset - 1, " \t");
if (spaceStart < 0 || chars.charAt(spaceStart) == '\n' || chars.charAt(spaceStart) == '\r'){
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
documentManager.commitDocument(document);
final PsiFile file = documentManager.getPsiFile(document);
if (file == null || !file.isWritable()) return;
PsiElement element = file.findElementAt(offset);
if (element == null) return;
final PsiFile file = documentManager.getPsiFile(document);
if (file == null || !file.isWritable()) return;
PsiElement element = file.findElementAt(offset);
if (element == null) return;
EditorHighlighter highlighter = ((EditorEx)editor).getHighlighter();
HighlighterIterator iterator = highlighter.createIterator(offset);
EditorHighlighter highlighter = ((EditorEx)editor).getHighlighter();
HighlighterIterator iterator = highlighter.createIterator(offset);
final FileType fileType = file.getFileType();
BraceMatcher braceMatcher = BraceMatchingUtil.getBraceMatcher(fileType, iterator);
boolean rBraceToken = braceMatcher.isRBraceToken(iterator, chars, fileType);
final boolean isBrace = braceMatcher.isLBraceToken(iterator, chars, fileType) || rBraceToken;
int lBraceOffset = -1;
final FileType fileType = file.getFileType();
BraceMatcher braceMatcher = BraceMatchingUtil.getBraceMatcher(fileType, iterator);
boolean rBraceToken = braceMatcher.isRBraceToken(iterator, chars, fileType);
final boolean isBrace = braceMatcher.isLBraceToken(iterator, chars, fileType) || rBraceToken;
int lBraceOffset = -1;
if (CodeInsightSettings.getInstance().REFORMAT_BLOCK_ON_RBRACE &&
rBraceToken &&
braceMatcher.isStructuralBrace(iterator, chars, fileType) && offset > 0) {
lBraceOffset = BraceMatchingUtil.findLeftLParen(
highlighter.createIterator(offset - 1),
braceMatcher.getOppositeBraceTokenType(iterator.getTokenType()),
editor.getDocument().getCharsSequence(),
fileType
);
}
if (element.getNode() != null && isBrace) {
final int finalLBraceOffset = lBraceOffset;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run(){
try{
int newOffset;
if (finalLBraceOffset != -1) {
RangeMarker marker = document.createRangeMarker(offset, offset + 1);
CodeStyleManager.getInstance(project).reformatRange(file, finalLBraceOffset, offset, true);
newOffset = marker.getStartOffset();
marker.dispose();
} else {
newOffset = CodeStyleManager.getInstance(project).adjustLineIndent(file, offset);
}
editor.getCaretModel().moveToOffset(newOffset + 1);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
editor.getSelectionModel().removeSelection();
}
catch(IncorrectOperationException e){
LOG.error(e);
}
if (CodeInsightSettings.getInstance().REFORMAT_BLOCK_ON_RBRACE &&
rBraceToken &&
braceMatcher.isStructuralBrace(iterator, chars, fileType) && offset > 0) {
lBraceOffset = BraceMatchingUtil.findLeftLParen(
highlighter.createIterator(offset - 1),
braceMatcher.getOppositeBraceTokenType(iterator.getTokenType()),
editor.getDocument().getCharsSequence(),
fileType
);
}
});
if (element.getNode() != null && isBrace) {
final int finalLBraceOffset = lBraceOffset;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run(){
try{
int newOffset;
if (finalLBraceOffset != -1) {
RangeMarker marker = document.createRangeMarker(offset, offset + 1);
CodeStyleManager.getInstance(project).reformatRange(file, finalLBraceOffset, offset, true);
newOffset = marker.getStartOffset();
marker.dispose();
} else {
newOffset = CodeStyleManager.getInstance(project).adjustLineIndent(file, offset);
}
editor.getCaretModel().moveToOffset(newOffset + 1);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
editor.getSelectionModel().removeSelection();
}
catch(IncorrectOperationException e){
LOG.error(e);
}
}
});
}
}
}
}
});
}
}

View File

@@ -415,6 +415,14 @@ public class EditorModificationUtil {
}
}
public static void typeInStringAtCaretHonorMultipleCarets(final Editor editor, @NotNull final String str) {
typeInStringAtCaretHonorMultipleCarets(editor, str, true, str.length());
}
public static void typeInStringAtCaretHonorMultipleCarets(final Editor editor, @NotNull final String str, final int caretShift) {
typeInStringAtCaretHonorMultipleCarets(editor, str, true, caretShift);
}
public static void typeInStringAtCaretHonorMultipleCarets(final Editor editor, @NotNull final String str, final boolean toProcessOverwriteMode) {
typeInStringAtCaretHonorMultipleCarets(editor, str, toProcessOverwriteMode, str.length());
}