From df32f16d402e0d2ca5517c68b939d17d73e57125 Mon Sep 17 00:00:00 2001 From: "Denis.Zhdanov" Date: Fri, 24 Aug 2012 10:53:16 +0400 Subject: [PATCH] IDEA-90499 Ctrl+Backspace deletes delimiters instead of whitespaces only and leads to SyntaxErrors in code Differentiate quotes processing between 'delete to word start' and 'move to word start' actions --- .../psi/codeStyle/CodeStyleSettings.java | 4 +- .../actions/DeleteToWordStartAction.java | 93 +++++++++++++++++-- .../editor/actions/EditorActionUtil.java | 36 ++----- 3 files changed, 97 insertions(+), 36 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java index d7839337b8f8..37d71ce6f92a 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java @@ -755,7 +755,7 @@ public class CodeStyleSettings extends CommonCodeStyleSettings implements Clonea * @see FileTypeIndentOptionsProvider * @see LanguageCodeStyleSettingsProvider */ - public IndentOptions getIndentOptions(FileType fileType) { + public IndentOptions getIndentOptions(@Nullable FileType fileType) { IndentOptions indentOptions = getLanguageIndentOptions(fileType); if (indentOptions != null) return indentOptions; @@ -771,7 +771,7 @@ public class CodeStyleSettings extends CommonCodeStyleSettings implements Clonea } @Nullable - private IndentOptions getLanguageIndentOptions(FileType fileType) { + private IndentOptions getLanguageIndentOptions(@Nullable FileType fileType) { if (fileType == null || !(fileType instanceof LanguageFileType)) return null; Language lang = ((LanguageFileType)fileType).getLanguage(); CommonCodeStyleSettings langSettings = getCommonSettings(lang); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordStartAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordStartAction.java index 36c139db623e..2003a410c773 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordStartAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordStartAction.java @@ -24,31 +24,110 @@ */ package com.intellij.openapi.editor.actions; +import com.intellij.openapi.editor.CaretModel; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.actionSystem.DataContext; +import gnu.trove.TIntHashSet; +import gnu.trove.TIntIntHashMap; +import org.jetbrains.annotations.NotNull; public class DeleteToWordStartAction extends TextComponentEditorAction { + + /** + * We need to provide special processing for quote symbols. + *

+ * Examples: + * + * + * + * + * + * + * + * + * + * + * + *
Text before action call + * Text after action call + *
one "two" [caret]one [caret]
one "two[caret]"one "[caret]"
+ */ + private static final TIntHashSet QUOTE_SYMBOLS = new TIntHashSet(); + + static { + QUOTE_SYMBOLS.add('\''); + QUOTE_SYMBOLS.add('\"'); + } + + private static final int[] QUOTE_SYMBOLS_ARRAY = QUOTE_SYMBOLS.toArray(); + public DeleteToWordStartAction() { super(new Handler()); } private static class Handler extends EditorWriteActionHandler { + + @NotNull private final TIntIntHashMap myQuotesNumber = new TIntIntHashMap(); + @Override public void executeWriteAction(Editor editor, DataContext dataContext) { CommandProcessor.getInstance().setCurrentCommandGroupId(EditorActionUtil.DELETE_COMMAND_GROUP); deleteToWordStart(editor); } + + private void deleteToWordStart(Editor editor) { + CharSequence text = editor.getDocument().getCharsSequence(); + CaretModel caretModel = editor.getCaretModel(); + int endOffset = caretModel.getOffset(); + int minOffset = editor.getDocument().getLineStartOffset(caretModel.getLogicalPosition().line); + + myQuotesNumber.clear(); + for (int i : QUOTE_SYMBOLS_ARRAY) { + myQuotesNumber.put(i, 0); + } + countQuotes(myQuotesNumber, text, minOffset, endOffset); + + EditorActionUtil.moveCaretToPreviousWord(editor, false); + + for (int offset = caretModel.getOffset(); offset > minOffset; offset = caretModel.getOffset()) { + char previous = text.charAt(offset - 1); + char current = text.charAt(offset); + if (QUOTE_SYMBOLS.contains(current)) { + if (Character.isWhitespace(previous)) { + break; + } + if (myQuotesNumber.get(current) % 2 == 0) { + // Was 'one "two" [caret]', now 'one "two[caret]"', we want to get 'one [caret]"two"' + EditorActionUtil.moveCaretToPreviousWord(editor, false); + continue; + } + break; + } + + if (QUOTE_SYMBOLS.contains(previous)) { + if (myQuotesNumber.get(previous) % 2 == 0) { + // Was 'one "two[caret]", now 'one "[caret]two"', we want 'one [caret]"two"' + editor.getCaretModel().moveToOffset(offset - 1); + } + } + break; + } + + int startOffset = caretModel.getOffset(); + Document document = editor.getDocument(); + document.deleteString(startOffset, endOffset); + } } - private static void deleteToWordStart(Editor editor) { - int endOffset = editor.getCaretModel().getOffset(); - EditorActionUtil.moveCaretToPreviousWord(editor, false); - int startOffset = editor.getCaretModel().getOffset(); - Document document = editor.getDocument(); - document.deleteString(startOffset, endOffset); + private static void countQuotes(@NotNull TIntIntHashMap holder, @NotNull CharSequence text, int start, int end) { + for (int i = end - 1; i >= start; i--) { + char c = text.charAt(i); + if (holder.containsKey(c)) { + holder.put(c, holder.get(c) + 1); + } + } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java index a7a047627dea..b01d2bef3d8d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java @@ -44,7 +44,6 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.EditorPopupHandler; -import gnu.trove.TIntHashSet; import java.awt.*; import java.awt.event.MouseEvent; @@ -65,23 +64,16 @@ public class EditorActionUtil { * expect the caret to be located at the near future. */ public static final Key EXPECTED_CARET_OFFSET = Key.create("expectedEditorOffset"); - - protected static final Object EDIT_COMMAND_GROUP = Key.create("EditGroup"); - public static final Object DELETE_COMMAND_GROUP = Key.create("DeleteGroup"); - - private static final TIntHashSet SPECIAL_NON_ID_SYMBOLS = new TIntHashSet(); - - static { - SPECIAL_NON_ID_SYMBOLS.add('\''); - SPECIAL_NON_ID_SYMBOLS.add('\"'); - } + + protected static final Object EDIT_COMMAND_GROUP = Key.create("EditGroup"); + public static final Object DELETE_COMMAND_GROUP = Key.create("DeleteGroup"); private EditorActionUtil() { } /** * Tries to change given editor's viewport position in vertical dimension by the given number of visual lines. - * + * * @param editor target editor which viewport position should be changed * @param lineShift defines viewport position's vertical change length * @param columnShift defines viewport position's horizontal change length @@ -102,7 +94,7 @@ public class EditorActionUtil { if (!moveCaret) { return; } - + Rectangle viewRectangle = editor.getScrollingModel().getVisibleArea(); int lineNumber = editor.getCaretModel().getVisualPosition().line; if (viewRectangle != null) { @@ -228,26 +220,16 @@ public class EditorActionUtil { final boolean firstIsIdentifierPart = Character.isJavaIdentifierPart(prev); final boolean secondIsIdentifierPart = Character.isJavaIdentifierPart(current); - if (!firstIsIdentifierPart && secondIsIdentifierPart && !SPECIAL_NON_ID_SYMBOLS.contains(prev)) { + if (!firstIsIdentifierPart && secondIsIdentifierPart) { return true; } if (isCamel && firstIsIdentifierPart && secondIsIdentifierPart && isHumpBound(text, offset, true)) { return true; } - - if (Character.isWhitespace(current)) { - return false; - } - else if (Character.isWhitespace(prev)) { - return true; - } - else if (SPECIAL_NON_ID_SYMBOLS.contains(current)) { - return false; - } - else { - return firstIsIdentifierPart && !secondIsIdentifierPart; - } + + return (Character.isWhitespace(prev) || firstIsIdentifierPart) && + !Character.isWhitespace(current) && !secondIsIdentifierPart; } private static boolean isLowerCaseOrDigit(char c) {