invoke TypedHandlerDelegate.checkAutoPopup only for the primary caret (completion popup is supposed to be shown only for it)

(for IDEA-135620)
This commit is contained in:
Dmitry Batrak
2016-10-24 14:55:04 +03:00
parent 89032a69ea
commit 8917ba2e4c
3 changed files with 80 additions and 84 deletions
@@ -23,6 +23,8 @@ import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
/**
* Handler, extending IDE behaviour on typing in editor.
*
* @author yole
*/
public abstract class TypedHandlerDelegate {
@@ -30,17 +32,22 @@ public abstract class TypedHandlerDelegate {
/**
* If the specified character triggers auto-popup, schedules the auto-popup appearance. This method is called even
* in overwrite mode, when the rest of typed handler delegate methods are not called.
* in overwrite mode, when the rest of typed handler delegate methods are not called. It is invoked only for the primary caret.
*/
public Result checkAutoPopup(char charTyped, final Project project, final Editor editor, final PsiFile file) {
return Result.CONTINUE;
}
/**
* Called before selected text is deleted.
* This method is supposed to be overridden by handlers having custom behaviour with respect to selection.
*/
public Result beforeSelectionRemoved(char c, final Project project, final Editor editor, final PsiFile file) {
return Result.CONTINUE;
}
/**
* Called before the specified character typed by the user is inserted in the editor.
*
* @return true if the typing has been processed (in this case, no further delegates are called and the character is not inserted),
* false otherwise.
*/
public Result beforeCharTyped(char c, final Project project, final Editor editor, final PsiFile file, final FileType fileType) {
return Result.CONTINUE;
@@ -23,10 +23,10 @@ import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
@@ -37,52 +37,59 @@ import org.jetbrains.annotations.NotNull;
public class SelectionQuotingTypedHandler extends TypedHandlerDelegate {
public static final ExtensionPointName<DequotingFilter> EP_NAME =
ExtensionPointName.create("com.intellij.selectionDequotingFilter");
private TextRange myReplacedTextRange;
private boolean myRestoreStickySelection;
private boolean myLtrSelection;
@Override
public Result checkAutoPopup(char c, Project project, Editor editor, PsiFile psiFile) {
// TODO[oleg] provide adequate API not to use this hack
// beforeCharTyped always works with removed selection
public Result beforeSelectionRemoved(char c, Project project, Editor editor, PsiFile file) {
SelectionModel selectionModel = editor.getSelectionModel();
if(CodeInsightSettings.getInstance().SURROUND_SELECTION_ON_QUOTE_TYPED && selectionModel.hasSelection() && isDelimiter(c)) {
if(CodeInsightSettings.getInstance().SURROUND_SELECTION_ON_QUOTE_TYPED && selectionModel.hasSelection() && isDelimiter(c)) {
String selectedText = selectionModel.getSelectedText();
if (selectedText.length() < 1) {
return super.checkAutoPopup(c, project, editor, psiFile);
}
final int selectionStart = selectionModel.getSelectionStart();
final int selectionEnd = selectionModel.getSelectionEnd();
if (selectedText.length() > 1) {
final char firstChar = selectedText.charAt(0);
final char lastChar = selectedText.charAt(selectedText.length() - 1);
if (isSimilarDelimiters(firstChar, c) && lastChar == getMatchingDelimiter(firstChar) &&
(isQuote(firstChar) || firstChar != c) && !shouldSkipReplacementOfQuotesOrBraces(psiFile, editor, selectedText, c) &&
selectedText.indexOf(lastChar, 1) == selectedText.length() - 1) {
selectedText = selectedText.substring(1, selectedText.length() - 1);
if (!StringUtil.isEmpty(selectedText)) {
final int selectionStart = selectionModel.getSelectionStart();
final int selectionEnd = selectionModel.getSelectionEnd();
if (selectedText.length() > 1) {
final char firstChar = selectedText.charAt(0);
final char lastChar = selectedText.charAt(selectedText.length() - 1);
if (isSimilarDelimiters(firstChar, c) && lastChar == getMatchingDelimiter(firstChar) &&
(isQuote(firstChar) || firstChar != c) && !shouldSkipReplacementOfQuotesOrBraces(file, editor, selectedText, c) &&
selectedText.indexOf(lastChar, 1) == selectedText.length() - 1) {
selectedText = selectedText.substring(1, selectedText.length() - 1);
}
}
final int caretOffset = selectionModel.getSelectionStart();
final char c2 = getMatchingDelimiter(c);
final String newText = String.valueOf(c) + selectedText + c2;
boolean ltrSelection = selectionModel.getLeadSelectionOffset() != selectionModel.getSelectionEnd();
boolean restoreStickySelection = editor instanceof EditorEx && ((EditorEx)editor).isStickySelection();
selectionModel.removeSelection();
editor.getDocument().replaceString(selectionStart, selectionEnd, newText);
TextRange replacedTextRange = Registry.is("editor.smarterSelectionQuoting")
? new TextRange(caretOffset + 1, caretOffset + newText.length() - 1)
: new TextRange(caretOffset, caretOffset + newText.length());
// selection is removed here
if (replacedTextRange.getEndOffset() <= editor.getDocument().getTextLength()) {
if (restoreStickySelection) {
EditorEx editorEx = (EditorEx)editor;
CaretModel caretModel = editorEx.getCaretModel();
caretModel.moveToOffset(ltrSelection ? replacedTextRange.getStartOffset() : replacedTextRange.getEndOffset());
editorEx.setStickySelection(true);
caretModel.moveToOffset(ltrSelection ? replacedTextRange.getEndOffset() : replacedTextRange.getStartOffset());
}
else {
if (ltrSelection || editor instanceof EditorWindow) {
editor.getSelectionModel().setSelection(replacedTextRange.getStartOffset(), replacedTextRange.getEndOffset());
}
else {
editor.getSelectionModel().setSelection(replacedTextRange.getEndOffset(), replacedTextRange.getStartOffset());
}
if (Registry.is("editor.smarterSelectionQuoting")) {
editor.getCaretModel().moveToOffset(ltrSelection ? replacedTextRange.getEndOffset() : replacedTextRange.getStartOffset());
}
}
}
return Result.STOP;
}
final int caretOffset = selectionModel.getSelectionStart();
final char c2 = getMatchingDelimiter(c);
final String newText = String.valueOf(c) + selectedText + c2;
myLtrSelection = selectionModel.getLeadSelectionOffset() != selectionModel.getSelectionEnd();
if (editor instanceof EditorEx) {
myRestoreStickySelection = ((EditorEx)editor).isStickySelection();
}
else {
myRestoreStickySelection = false;
}
selectionModel.removeSelection();
editor.getDocument().replaceString(selectionStart, selectionEnd, newText);
if (Registry.is("editor.smarterSelectionQuoting")) {
myReplacedTextRange = new TextRange(caretOffset + 1, caretOffset + newText.length() - 1);
} else {
myReplacedTextRange = new TextRange(caretOffset, caretOffset + newText.length());
}
return Result.STOP;
}
return super.checkAutoPopup(c, project, editor, psiFile);
return super.beforeSelectionRemoved(c, project, editor, file);
}
private static boolean shouldSkipReplacementOfQuotesOrBraces(PsiFile psiFile, Editor editor, String selectedText, char c) {
@@ -116,36 +123,6 @@ public class SelectionQuotingTypedHandler extends TypedHandlerDelegate {
return (isBracket(c1) && isBracket(c2)) || (isQuote(c1) && isQuote(c2));
}
@Override
public Result beforeCharTyped(final char charTyped, final Project project, final Editor editor, final PsiFile file, final FileType fileType) {
// TODO[oleg] remove this hack when API changes
if (myReplacedTextRange != null) {
if (myReplacedTextRange.getEndOffset() <= editor.getDocument().getTextLength()) {
if (myRestoreStickySelection && editor instanceof EditorEx) {
EditorEx editorEx = (EditorEx)editor;
CaretModel caretModel = editorEx.getCaretModel();
caretModel.moveToOffset(myLtrSelection ? myReplacedTextRange.getStartOffset() : myReplacedTextRange.getEndOffset());
editorEx.setStickySelection(true);
caretModel.moveToOffset(myLtrSelection ? myReplacedTextRange.getEndOffset() : myReplacedTextRange.getStartOffset());
}
else {
if (myLtrSelection || editor instanceof EditorWindow) {
editor.getSelectionModel().setSelection(myReplacedTextRange.getStartOffset(), myReplacedTextRange.getEndOffset());
}
else {
editor.getSelectionModel().setSelection(myReplacedTextRange.getEndOffset(), myReplacedTextRange.getStartOffset());
}
if (Registry.is("editor.smarterSelectionQuoting")) {
editor.getCaretModel().moveToOffset(myLtrSelection ? myReplacedTextRange.getEndOffset() : myReplacedTextRange.getStartOffset());
}
}
}
myReplacedTextRange = null;
return Result.STOP;
}
return Result.CONTINUE;
}
public static abstract class DequotingFilter {
public abstract boolean skipReplacementQuotesOrBraces(@NotNull PsiFile file,
@NotNull Editor editor,
@@ -162,18 +162,20 @@ public class TypedHandler extends TypedActionHandlerBase {
final TypedHandlerDelegate[] delegates = Extensions.getExtensions(TypedHandlerDelegate.EP_NAME);
boolean handled = false;
for (TypedHandlerDelegate delegate : delegates) {
final TypedHandlerDelegate.Result result = delegate.checkAutoPopup(charTyped, project, editor, file);
handled = result == TypedHandlerDelegate.Result.STOP;
if (result != TypedHandlerDelegate.Result.CONTINUE) {
break;
if (caret == originalEditor.getCaretModel().getPrimaryCaret()) {
boolean handled = false;
for (TypedHandlerDelegate delegate : delegates) {
final TypedHandlerDelegate.Result result = delegate.checkAutoPopup(charTyped, project, editor, file);
handled = result == TypedHandlerDelegate.Result.STOP;
if (result != TypedHandlerDelegate.Result.CONTINUE) {
break;
}
}
}
if (!handled) {
autoPopupCompletion(editor, charTyped, project, file);
autoPopupParameterInfo(editor, charTyped, project, file);
if (!handled) {
autoPopupCompletion(editor, charTyped, project, file);
autoPopupParameterInfo(editor, charTyped, project, file);
}
}
if (!editor.isInsertMode()) {
@@ -181,6 +183,16 @@ public class TypedHandler extends TypedActionHandlerBase {
return;
}
for (TypedHandlerDelegate delegate : delegates) {
final TypedHandlerDelegate.Result result = delegate.beforeSelectionRemoved(charTyped, project, editor, file);
if (result == TypedHandlerDelegate.Result.STOP) {
return;
}
if (result == TypedHandlerDelegate.Result.DEFAULT) {
break;
}
}
EditorModificationUtil.deleteSelectedText(editor);
FileType fileType = getFileType(file, editor);