mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
fix multi-caret completion issues, make TypedHandlers supporting block selection work also with multiple carets (IDEA-80056)
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
class Foo {{
|
||||
System.out.p<caret>
|
||||
System.out.p<caret>
|
||||
}}
|
||||
@@ -0,0 +1,4 @@
|
||||
class Foo {{
|
||||
System.out.append(<caret>)
|
||||
System.out.append(<caret>)
|
||||
}}
|
||||
@@ -52,6 +52,7 @@ import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.psi.statistics.StatisticsManager
|
||||
import com.intellij.psi.statistics.impl.StatisticsManagerImpl
|
||||
import com.intellij.testFramework.EditorTestUtil
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.annotations.NotNull
|
||||
|
||||
@@ -1141,6 +1142,56 @@ class Foo {{
|
||||
}}'''
|
||||
}
|
||||
|
||||
public void testMulticaret() {
|
||||
doTestMulticaret """
|
||||
class Foo {{
|
||||
<selection>t<caret></selection>x;
|
||||
<selection>t<caret></selection>x;
|
||||
}}""", '\n', '''
|
||||
class Foo {{
|
||||
toString()<caret>x;
|
||||
toString()<caret>x;
|
||||
}}'''
|
||||
}
|
||||
|
||||
public void testMulticaretTab() {
|
||||
doTestMulticaret """
|
||||
class Foo {{
|
||||
<selection>t<caret></selection>x;
|
||||
<selection>t<caret></selection>x;
|
||||
}}""", '\t', '''
|
||||
class Foo {{
|
||||
toString()<caret>;
|
||||
toString()<caret>;
|
||||
}}'''
|
||||
}
|
||||
|
||||
public void testMulticaretBackspace() {
|
||||
doTestMulticaret """
|
||||
class Foo {{
|
||||
<selection>t<caret></selection>;
|
||||
<selection>t<caret></selection>;
|
||||
}}""", '\b\t', '''
|
||||
class Foo {{
|
||||
toString()<caret>;
|
||||
toString()<caret>;
|
||||
}}'''
|
||||
}
|
||||
|
||||
private doTestMulticaret(final String textBefore, final String toType, final String textAfter) {
|
||||
EditorTestUtil.enableMultipleCarets()
|
||||
try {
|
||||
myFixture.configureByText "a.java", textBefore
|
||||
type 'toStr'
|
||||
assert lookup
|
||||
type toType
|
||||
myFixture.checkResult textAfter
|
||||
}
|
||||
finally {
|
||||
EditorTestUtil.disableMultipleCarets()
|
||||
}
|
||||
}
|
||||
|
||||
private doTestBlockSelection(final String textBefore, final String toType, final String textAfter) {
|
||||
myFixture.configureByText "a.java", textBefore
|
||||
edt {
|
||||
|
||||
+15
@@ -1403,6 +1403,21 @@ class Foo {{
|
||||
}
|
||||
}
|
||||
|
||||
public void testMulticaretTyping() {
|
||||
EditorTestUtil.enableMultipleCarets()
|
||||
try {
|
||||
configure()
|
||||
assert lookup
|
||||
type('p')
|
||||
assert lookup
|
||||
type('\n')
|
||||
checkResult()
|
||||
}
|
||||
finally {
|
||||
EditorTestUtil.disableMultipleCarets()
|
||||
}
|
||||
}
|
||||
|
||||
public void "test complete lowercase class name"() {
|
||||
myFixture.addClass("package foo; public class myClass {}")
|
||||
myFixture.configureByText "a.java", """
|
||||
|
||||
@@ -135,10 +135,7 @@ public class TypedHandler extends TypedActionHandlerBase {
|
||||
Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||
PsiFile file;
|
||||
|
||||
if (project == null
|
||||
|| editor.isColumnMode()
|
||||
|| editor.getCaretModel().getAllCarets().size() > 1
|
||||
|| (file = PsiUtilBase.getPsiFileInEditor(editor, project)) == null) {
|
||||
if (project == null || editor.isColumnMode() || (file = PsiUtilBase.getPsiFileInEditor(editor, project)) == null) {
|
||||
if (myOriginalHandler != null){
|
||||
myOriginalHandler.execute(editor, charTyped, dataContext);
|
||||
}
|
||||
@@ -174,13 +171,13 @@ public class TypedHandler extends TypedActionHandlerBase {
|
||||
}
|
||||
|
||||
if (!editor.isInsertMode()){
|
||||
myOriginalHandler.execute(originalEditor, charTyped, dataContext);
|
||||
if (myOriginalHandler != null) {
|
||||
myOriginalHandler.execute(originalEditor, charTyped, dataContext);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (editor.getSelectionModel().hasSelection()){
|
||||
EditorModificationUtil.deleteSelectedText(editor);
|
||||
}
|
||||
EditorModificationUtil.deleteSelectedTextForAllCarets(editor);
|
||||
|
||||
FileType fileType = getFileType(file, editor);
|
||||
|
||||
@@ -194,7 +191,7 @@ public class TypedHandler extends TypedActionHandlerBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (!editor.getSelectionModel().hasBlockSelection()) {
|
||||
if (!editor.getSelectionModel().hasBlockSelection() && editor.getCaretModel().getAllCarets().size() == 1) {
|
||||
if (')' == charTyped || ']' == charTyped || '}' == charTyped) {
|
||||
if (FileTypes.PLAIN_TEXT != fileType) {
|
||||
if (handleRParen(editor, fileType, charTyped)) return;
|
||||
@@ -206,12 +203,14 @@ public class TypedHandler extends TypedActionHandlerBase {
|
||||
}
|
||||
|
||||
long modificationStampBeforeTyping = editor.getDocument().getModificationStamp();
|
||||
myOriginalHandler.execute(originalEditor, charTyped, dataContext);
|
||||
if (myOriginalHandler != null) {
|
||||
myOriginalHandler.execute(originalEditor, charTyped, dataContext);
|
||||
}
|
||||
AutoHardWrapHandler.getInstance().wrapLineIfNecessary(editor, dataContext, modificationStampBeforeTyping);
|
||||
|
||||
if (('(' == charTyped || '[' == charTyped || '{' == charTyped) &&
|
||||
CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET &&
|
||||
!editor.getSelectionModel().hasBlockSelection() && fileType != FileTypes.PLAIN_TEXT) {
|
||||
!editor.getSelectionModel().hasBlockSelection() && editor.getCaretModel().getAllCarets().size() == 1 && fileType != FileTypes.PLAIN_TEXT) {
|
||||
handleAfterLParen(editor, fileType, charTyped);
|
||||
}
|
||||
else if ('}' == charTyped) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.
|
||||
@@ -80,7 +80,7 @@ public class LookupTypedHandler extends TypedHandlerDelegate {
|
||||
if (!lookup.performGuardedChange(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
EditorModificationUtil.typeInStringAtCaretHonorBlockSelection(editor, String.valueOf(charTyped), true);
|
||||
EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, String.valueOf(charTyped), true);
|
||||
}
|
||||
})) {
|
||||
return Result.STOP;
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.LineTokenizer;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.util.Producer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
@@ -51,6 +52,15 @@ public class EditorModificationUtil {
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
}
|
||||
|
||||
public static void deleteSelectedTextForAllCarets(@NotNull final Editor editor) {
|
||||
editor.getCaretModel().runForEachCaret(new CaretAction() {
|
||||
@Override
|
||||
public void perform(Caret caret) {
|
||||
deleteSelectedText(editor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void deleteBlockSelection(Editor editor) {
|
||||
SelectionModel selectionModel = editor.getSelectionModel();
|
||||
if (!selectionModel.hasBlockSelection()) return;
|
||||
@@ -81,11 +91,15 @@ public class EditorModificationUtil {
|
||||
editor.getSelectionModel().setBlockSelection(new LogicalPosition(startLine, caretColumn), new LogicalPosition(endLine, caretColumn));
|
||||
}
|
||||
|
||||
public static void insertStringAtCaret(Editor editor, String s) {
|
||||
public static void insertStringAtCaret(Editor editor, @NotNull String s) {
|
||||
insertStringAtCaret(editor, s, false, true);
|
||||
}
|
||||
|
||||
public static int insertStringAtCaret(Editor editor, String s, boolean toProcessOverwriteMode, boolean toMoveCaret) {
|
||||
public static int insertStringAtCaret(Editor editor, @NotNull String s, boolean toProcessOverwriteMode, boolean toMoveCaret) {
|
||||
return insertStringAtCaret(editor, s, toProcessOverwriteMode, toMoveCaret, s.length());
|
||||
}
|
||||
|
||||
public static int insertStringAtCaret(Editor editor, @NotNull String s, boolean toProcessOverwriteMode, boolean toMoveCaret, int caretShift) {
|
||||
final SelectionModel selectionModel = editor.getSelectionModel();
|
||||
if (selectionModel.hasSelection()) {
|
||||
editor.getCaretModel().moveToOffset(selectionModel.getSelectionStart(), true);
|
||||
@@ -120,7 +134,7 @@ public class EditorModificationUtil {
|
||||
document.replaceString(oldOffset, Math.min(endOffset, oldOffset + s.length()), s);
|
||||
}
|
||||
|
||||
int offset = oldOffset + s.length();
|
||||
int offset = oldOffset + filler.length() + caretShift;
|
||||
if (toMoveCaret){
|
||||
editor.getCaretModel().moveToOffset(offset, true);
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
@@ -365,6 +379,62 @@ public class EditorModificationUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void typeInStringAtCaretHonorMultipleCarets(final Editor editor, @NotNull final String str, final boolean toProcessOverwriteMode) {
|
||||
typeInStringAtCaretHonorMultipleCarets(editor, str, toProcessOverwriteMode, str.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts given string at each caret's position. Effective caret shift will be equal to <code>caretShift</code> for each caret.
|
||||
*/
|
||||
public static void typeInStringAtCaretHonorMultipleCarets(final Editor editor, @NotNull final String str, final boolean toProcessOverwriteMode, final int caretShift)
|
||||
throws ReadOnlyFragmentModificationException
|
||||
{
|
||||
Document doc = editor.getDocument();
|
||||
final SelectionModel selectionModel = editor.getSelectionModel();
|
||||
if (selectionModel.hasBlockSelection()) {
|
||||
RangeMarker guard = selectionModel.getBlockSelectionGuard();
|
||||
if (guard != null) {
|
||||
DocumentEvent evt = new MockDocumentEvent(doc, editor.getCaretModel().getOffset());
|
||||
ReadOnlyFragmentModificationException e = new ReadOnlyFragmentModificationException(evt, guard);
|
||||
EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(doc).handle(e);
|
||||
}
|
||||
else {
|
||||
final LogicalPosition start = selectionModel.getBlockStart();
|
||||
final LogicalPosition end = selectionModel.getBlockEnd();
|
||||
assert start != null;
|
||||
assert end != null;
|
||||
|
||||
int column = Math.min(start.column, end.column);
|
||||
int startLine = Math.min(start.line, end.line);
|
||||
int endLine = Math.max(start.line, end.line);
|
||||
deleteBlockSelection(editor);
|
||||
for (int i = startLine; i <= endLine; i++) {
|
||||
editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(i, column));
|
||||
insertStringAtCaret(editor, str, toProcessOverwriteMode, true, caretShift);
|
||||
}
|
||||
selectionModel.setBlockSelection(new LogicalPosition(startLine, column + str.length()),
|
||||
new LogicalPosition(endLine, column + str.length()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
editor.getCaretModel().runForEachCaret(new CaretAction() {
|
||||
@Override
|
||||
public void perform(Caret caret) {
|
||||
insertStringAtCaret(editor, str, toProcessOverwriteMode, true, caretShift);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void moveAllCaretsRelatively(@NotNull Editor editor, final int caretShift) {
|
||||
editor.getCaretModel().runForEachCaret(new CaretAction() {
|
||||
@Override
|
||||
public void perform(Caret caret) {
|
||||
caret.moveToOffset(caret.getOffset() + caretShift);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #pasteTransferable(Editor, Producer)} (to remove in IDEA 14) */
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public static TextRange pasteFromClipboard(Editor editor) {
|
||||
|
||||
+1
-12
@@ -64,18 +64,7 @@ public class TypedAction {
|
||||
try {
|
||||
final String str = String.valueOf(charTyped);
|
||||
CommandProcessor.getInstance().setCurrentCommandName(EditorBundle.message("typing.in.editor.command.name"));
|
||||
|
||||
if (editor.getCaretModel().getAllCarets().size() == 1) { // temporary fix for completion - going forward we shouldn't use this check
|
||||
EditorModificationUtil.typeInStringAtCaretHonorBlockSelection(editor, str, true);
|
||||
}
|
||||
else {
|
||||
editor.getCaretModel().runForEachCaret(new CaretAction() {
|
||||
@Override
|
||||
public void perform(Caret caret) {
|
||||
EditorModificationUtil.typeInStringAtCaretHonorBlockSelection(editor, str, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, str, true);
|
||||
}
|
||||
catch (ReadOnlyFragmentModificationException e) {
|
||||
EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(doc).handle(e);
|
||||
|
||||
@@ -53,12 +53,7 @@ public class CutAction extends EditorAction {
|
||||
});
|
||||
}
|
||||
editor.getSelectionModel().copySelectionToClipboard();
|
||||
editor.getCaretModel().runForEachCaret(new CaretAction() {
|
||||
@Override
|
||||
public void perform(Caret caret) {
|
||||
EditorModificationUtil.deleteSelectedText(editor);
|
||||
}
|
||||
});
|
||||
EditorModificationUtil.deleteSelectedTextForAllCarets(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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.
|
||||
@@ -82,8 +82,7 @@ public class PyMethodNameTypedHandler extends TypedHandlerDelegate {
|
||||
if (caretOffset == chars.length() || chars.charAt(caretOffset) != ':') {
|
||||
textToType += ':';
|
||||
}
|
||||
EditorModificationUtil.typeInStringAtCaretHonorBlockSelection(editor, textToType, true);
|
||||
editor.getCaretModel().moveToOffset(offset + 1 + pname.length()); // right after param name
|
||||
EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, textToType, true, 1 + pname.length()); // right after param name
|
||||
return Result.STOP;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user