diff --git a/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightSettings.java b/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightSettings.java index 99becc715a44..2d368c5f86cb 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightSettings.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightSettings.java @@ -76,10 +76,11 @@ public class CodeInsightSettings implements PersistentStateComponent, C public boolean SHOW_FULL_SIGNATURES_IN_PARAMETER_INFO = false; - @MagicConstant(intValues = {OFF, AUTOINDENT}) + @MagicConstant(intValues = {OFF, AUTOINDENT, INDENT}) public int SMART_BACKSPACE = AUTOINDENT; public static final int OFF = 0; public static final int AUTOINDENT = 1; + public static final int INDENT = 2; public boolean SMART_INDENT_ON_ENTER = true; public boolean INSERT_BRACE_ON_ENTER = true; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/AbstractIndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/AbstractIndentingBackspaceHandler.java new file mode 100644 index 000000000000..bbe65098e818 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/AbstractIndentingBackspaceHandler.java @@ -0,0 +1,68 @@ +/* + * 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.editorActions; + +import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.lang.Language; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; + +abstract class AbstractIndentingBackspaceHandler extends BackspaceHandlerDelegate { + private final int myMode; + private boolean myEnabled; + + AbstractIndentingBackspaceHandler(int mode) { + myMode = mode; + } + + @Override + public void beforeCharDeleted(char c, PsiFile file, Editor editor) { + myEnabled = false; + if (!StringUtil.isWhiteSpace(c)) { + return; + } + int mode = getBackspaceMode(file.getLanguage()); + if (mode != myMode) { + return; + } + doBeforeCharDeleted(c, file, editor); + myEnabled = true; + } + + @Override + public boolean charDeleted(char c, PsiFile file, Editor editor) { + if (!myEnabled) { + return false; + } + return doCharDeleted(c, file, editor); + } + + protected abstract void doBeforeCharDeleted(char c, PsiFile file, Editor editor); + + protected abstract boolean doCharDeleted(char c, PsiFile file, Editor editor); + + private static int getBackspaceMode(Language language) { + LanguageCodeStyleSettingsProvider codeStyleSettingsProvider = LanguageCodeStyleSettingsProvider.forLanguage(language); + if (codeStyleSettingsProvider != null && codeStyleSettingsProvider.isIndentBasedLanguageSemantics()) { + return CodeInsightSettings.INDENT; + } + else { + return CodeInsightSettings.getInstance().SMART_BACKSPACE; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/BackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/BackspaceHandler.java index 62088f6bff8c..78a81f855915 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/BackspaceHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/BackspaceHandler.java @@ -23,6 +23,7 @@ import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; @@ -36,6 +37,7 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.util.PsiUtilBase; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -167,7 +169,7 @@ public class BackspaceHandler extends EditorWriteActionHandler { if (editor.getSelectionModel().hasSelection() || editor.getSelectionModel().hasBlockSelection()) return null; final LogicalPosition caretPos = editor.getCaretModel().getLogicalPosition(); - if (caretPos.line == 0 || caretPos.column == 0) { + if (caretPos.column == 0) { return null; } if (!isWhitespaceBeforeCaret(editor)) { @@ -175,13 +177,21 @@ public class BackspaceHandler extends EditorWriteActionHandler { } // Decrease column down to indentation * n - final int indent = CodeStyleSettingsManager.getSettings(file.getProject()).getIndentSize(file.getFileType()); + final int indent = CodeStyleSettingsManager.getSettings(file.getProject()).getIndentOptionsByFile(file).INDENT_SIZE; int column = (caretPos.column - 1) / indent * indent; if (column < 0) { column = 0; } return new LogicalPosition(caretPos.line, column); } + + public static void deleteToTargetPosition(@NotNull Editor editor, @NotNull LogicalPosition pos) { + final int offset = editor.getCaretModel().getOffset(); + final int targetOffset = editor.logicalPositionToOffset(pos); + editor.getSelectionModel().setSelection(targetOffset, offset); + EditorModificationUtil.deleteSelectedText(editor); + editor.getCaretModel().moveToLogicalPosition(pos); + } public static boolean isWhitespaceBeforeCaret(Editor editor) { final LogicalPosition caretPos = editor.getCaretModel().getLogicalPosition(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandler.java new file mode 100644 index 000000000000..17e0e0227bfd --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandler.java @@ -0,0 +1,43 @@ +/* + * 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.editorActions; + +import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.psi.PsiFile; + +public class SimpleIndentingBackspaceHandler extends AbstractIndentingBackspaceHandler { + private LogicalPosition myTargetPosition; + + public SimpleIndentingBackspaceHandler() { + super(CodeInsightSettings.INDENT); + } + + @Override + protected void doBeforeCharDeleted(char c, PsiFile file, Editor editor) { + myTargetPosition = BackspaceHandler.getBackspaceUnindentPosition(file, editor); + } + + @Override + protected boolean doCharDeleted(char c, PsiFile file, Editor editor) { + if (myTargetPosition != null) { + BackspaceHandler.deleteToTargetPosition(editor, myTargetPosition); + return true; + } + return false; + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java similarity index 85% rename from platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java rename to platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java index 7429f557b29c..8814534d6ea9 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java @@ -17,7 +17,9 @@ package com.intellij.codeInsight.editorActions; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeStyle.CodeStyleFacade; -import com.intellij.formatting.*; +import com.intellij.formatting.FormatterEx; +import com.intellij.formatting.FormattingModel; +import com.intellij.formatting.FormattingModelBuilder; import com.intellij.lang.LanguageFormatting; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.CaretModel; @@ -33,40 +35,30 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; -import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; -/** - * Makes Backspace action delete all whitespace till next valid indent position - */ -public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { - private static final Logger LOG = Logger.getInstance(IndentingBackspaceHandler.class); +public class SmartIndentingBackspaceHandler extends AbstractIndentingBackspaceHandler { + private static final Logger LOG = Logger.getInstance(SmartIndentingBackspaceHandler.class); - private boolean isApplicable; private boolean caretWasAtLineStart; private String precalculatedSpacing; + public SmartIndentingBackspaceHandler() { + super(CodeInsightSettings.AUTOINDENT); + } + @Override - public void beforeCharDeleted(char c, PsiFile file, Editor editor) { - if (CodeInsightSettings.getInstance().SMART_BACKSPACE != CodeInsightSettings.AUTOINDENT || !StringUtil.isWhiteSpace(c)) { - isApplicable = false; - return; - } - LanguageCodeStyleSettingsProvider codeStyleSettingsProvider = LanguageCodeStyleSettingsProvider.forLanguage(file.getLanguage()); - if (codeStyleSettingsProvider != null && codeStyleSettingsProvider.isIndentBasedLanguageSemantics()) { - isApplicable = false; - return; - } + protected void doBeforeCharDeleted(char c, PsiFile file, Editor editor) { Document document = editor.getDocument(); CharSequence charSequence = document.getCharsSequence(); CaretModel caretModel = editor.getCaretModel(); int caretOffset = caretModel.getOffset(); LogicalPosition pos = caretModel.getLogicalPosition(); - isApplicable = true; caretWasAtLineStart = pos.column == 0; precalculatedSpacing = null; - if (caretWasAtLineStart && pos.line > 0 && caretOffset < charSequence.length() && !StringUtil.isWhiteSpace(charSequence.charAt(caretOffset))) { + if (caretWasAtLineStart && pos.line > 0 && caretOffset < charSequence.length() + && !StringUtil.isWhiteSpace(charSequence.charAt(caretOffset))) { int prevLineEnd = document.getLineEndOffset(pos.line - 1); if (prevLineEnd > 0 && !StringUtil.isWhiteSpace(charSequence.charAt(prevLineEnd - 1))) { PsiDocumentManager.getInstance(file.getProject()).commitDocument(document); @@ -76,11 +68,7 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { } @Override - public boolean charDeleted(char c, PsiFile file, Editor editor) { - if (!isApplicable) { - return false; - } - + protected boolean doCharDeleted(char c, PsiFile file, Editor editor) { Project project = file.getProject(); Document document = editor.getDocument(); CaretModel caretModel = editor.getCaretModel(); @@ -153,6 +141,17 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { return true; } + private static String getSpacing(PsiFile file, int offset) { + FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file); + if (builder == null) { + return ""; + } + CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(file.getProject()); + FormattingModel model = builder.createModel(file, settings); + int spacing = FormatterEx.getInstance().getSpacingForBlockAtOffset(model, offset); + return StringUtil.repeatSymbol(' ', spacing); + } + private static int getTabSize(@NotNull CodeStyleFacade codeStyleFacade, @NotNull Document document) { VirtualFile file = FileDocumentManager.getInstance().getFile(document); FileType fileType = file == null ? null : file.getFileType(); @@ -175,15 +174,4 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { } return width; } - - private static String getSpacing(PsiFile file, int offset) { - FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file); - if (builder == null) { - return ""; - } - CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(file.getProject()); - FormattingModel model = builder.createModel(file, settings); - int spacing = FormatterEx.getInstance().getSpacingForBlockAtOffset(model, offset); - return StringUtil.repeatSymbol(' ', spacing); - } } diff --git a/platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandlerTest.java b/platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandlerTest.java new file mode 100644 index 000000000000..514cf7fcdcf1 --- /dev/null +++ b/platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandlerTest.java @@ -0,0 +1,45 @@ +/* + * 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.editorActions; + +import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.testFramework.LightPlatformCodeInsightTestCase; + +public class SimpleIndentingBackspaceHandlerTest extends LightPlatformCodeInsightTestCase { + public void testBasicUnindent() { + doTest(" text", + " text"); + } + + public void testAtLineStart() { + doTest("line1\nline2", + "line1line2"); + } + + private void doTest(String before, String after) { + int savedMode = CodeInsightSettings.getInstance().SMART_BACKSPACE; + try { + CodeInsightSettings.getInstance().SMART_BACKSPACE = CodeInsightSettings.INDENT; + configureFromFileText(getTestName(false) + ".txt", before); + executeAction(IdeActions.ACTION_EDITOR_BACKSPACE); + checkResultByText(after); + } + finally { + CodeInsightSettings.getInstance().SMART_BACKSPACE = savedMode; + } + } +} diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 43c887388073..04d82a551392 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -578,7 +578,8 @@ id="EnterBetweenBracesHandler"/> - + + diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml index 5e4cb1964c76..395c968e5882 100644 --- a/python/src/META-INF/python-core.xml +++ b/python/src/META-INF/python-core.xml @@ -39,7 +39,6 @@ - diff --git a/python/src/com/jetbrains/python/editor/PythonBackspaceHandler.java b/python/src/com/jetbrains/python/editor/PythonBackspaceHandler.java deleted file mode 100644 index 3a056f64639b..000000000000 --- a/python/src/com/jetbrains/python/editor/PythonBackspaceHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.jetbrains.python.editor; - -import com.intellij.codeInsight.editorActions.BackspaceHandler; -import com.intellij.codeInsight.editorActions.BackspaceHandlerDelegate; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorModificationUtil; -import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.psi.PsiFile; -import com.jetbrains.python.PythonFileType; - -/** - * @author yole - */ -public class PythonBackspaceHandler extends BackspaceHandlerDelegate { - private LogicalPosition myTargetPosition; - - public void beforeCharDeleted(final char c, final PsiFile file, final Editor editor) { - if (PythonFileType.INSTANCE != file.getFileType()) return; - myTargetPosition = BackspaceHandler.getBackspaceUnindentPosition(file, editor); - } - - public boolean charDeleted(final char c, final PsiFile file, final Editor editor) { - if (myTargetPosition != null) { - // Remove all the following spaces before moving to targetPosition - final int offset = editor.getCaretModel().getOffset(); - final int targetOffset = editor.logicalPositionToOffset(myTargetPosition); - editor.getSelectionModel().setSelection(targetOffset, offset); - EditorModificationUtil.deleteSelectedText(editor); - editor.getCaretModel().moveToLogicalPosition(myTargetPosition); - myTargetPosition = null; - return true; - } - return false; - } -}