diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/FoldingModel.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/FoldingModel.java
index 289c8166ec8f..96080cf19986 100644
--- a/platform/editor-ui-api/src/com/intellij/openapi/editor/FoldingModel.java
+++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/FoldingModel.java
@@ -85,6 +85,12 @@ public interface FoldingModel {
@Nullable
FoldRegion getCollapsedRegionAtOffset(int offset);
+ /**
+ * Returns fold region with given boundaries, if it exists, or null otherwise.
+ */
+ @Nullable
+ FoldRegion getFoldRegion(int startOffset, int endOffset);
+
/**
* Runs an operation which is allowed to modify fold regions in the editor by calling
* {@link #addFoldRegion(int, int, String)} and {@link #removeFoldRegion(FoldRegion)}.
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/IndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java
deleted file mode 100644
index 7429f557b29c..000000000000
--- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java
+++ /dev/null
@@ -1,189 +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.intellij.codeInsight.editorActions;
-
-import com.intellij.codeInsight.CodeInsightSettings;
-import com.intellij.codeStyle.CodeStyleFacade;
-import com.intellij.formatting.*;
-import com.intellij.lang.LanguageFormatting;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.LogicalPosition;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
-import com.intellij.openapi.fileTypes.FileType;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-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);
-
- private boolean isApplicable;
- private boolean caretWasAtLineStart;
- private String precalculatedSpacing;
-
- @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;
- }
- 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))) {
- int prevLineEnd = document.getLineEndOffset(pos.line - 1);
- if (prevLineEnd > 0 && !StringUtil.isWhiteSpace(charSequence.charAt(prevLineEnd - 1))) {
- PsiDocumentManager.getInstance(file.getProject()).commitDocument(document);
- precalculatedSpacing = getSpacing(file, caretOffset);
- }
- }
- }
-
- @Override
- public boolean charDeleted(char c, PsiFile file, Editor editor) {
- if (!isApplicable) {
- return false;
- }
-
- Project project = file.getProject();
- Document document = editor.getDocument();
- CaretModel caretModel = editor.getCaretModel();
-
- int caretOffset = caretModel.getOffset();
- int offset = CharArrayUtil.shiftForward(document.getCharsSequence(), caretOffset, " \t");
- int beforeWhitespaceOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), offset - 1, " \t") + 1;
- LogicalPosition logicalPosition = caretOffset < offset ? editor.offsetToLogicalPosition(offset) : caretModel.getLogicalPosition();
- int lineStartOffset = document.getLineStartOffset(logicalPosition.line);
- if (lineStartOffset < beforeWhitespaceOffset) {
- if (caretWasAtLineStart && beforeWhitespaceOffset <= offset) {
- String spacing;
- if (precalculatedSpacing == null) {
- PsiDocumentManager.getInstance(project).commitDocument(document);
- spacing = getSpacing(file, offset);
- }
- else {
- spacing = precalculatedSpacing;
- }
- if (beforeWhitespaceOffset < offset || !spacing.isEmpty()) {
- document.replaceString(beforeWhitespaceOffset, offset, spacing);
- caretModel.moveToOffset(beforeWhitespaceOffset + spacing.length());
- return true;
- }
- }
- return false;
- }
-
- PsiDocumentManager.getInstance(project).commitDocument(document);
- CodeStyleFacade codeStyleFacade = CodeStyleFacade.getInstance(project);
- String indent = codeStyleFacade.getLineIndent(document, lineStartOffset);
- if (indent == null) {
- return false;
- }
-
- int tabSize = getTabSize(codeStyleFacade, document);
- int targetColumn = getWidth(indent, tabSize);
-
- if (logicalPosition.column == targetColumn) {
- if (caretOffset < offset) {
- caretModel.moveToLogicalPosition(logicalPosition);
- return true;
- }
- return false;
- }
-
- if (caretWasAtLineStart || logicalPosition.column > targetColumn) {
- document.replaceString(lineStartOffset, offset, indent);
- caretModel.moveToLogicalPosition(new LogicalPosition(logicalPosition.line, targetColumn));
- return true;
- }
-
- if (logicalPosition.line == 0) {
- return false;
- }
-
- int prevLineStartOffset = document.getLineStartOffset(logicalPosition.line - 1);
- int prevLineEndOffset = document.getLineEndOffset(logicalPosition.line - 1);
- int targetOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), prevLineEndOffset - 1, " \t") + 1;
-
- if (prevLineStartOffset < targetOffset) {
- String spacing = getSpacing(file, offset);
- document.replaceString(targetOffset, offset, spacing);
- caretModel.moveToOffset(targetOffset + spacing.length());
- }
- else {
- document.replaceString(prevLineStartOffset, offset, indent);
- caretModel.moveToLogicalPosition(new LogicalPosition(logicalPosition.line - 1, targetColumn));
- }
- return true;
- }
-
- private static int getTabSize(@NotNull CodeStyleFacade codeStyleFacade, @NotNull Document document) {
- VirtualFile file = FileDocumentManager.getInstance().getFile(document);
- FileType fileType = file == null ? null : file.getFileType();
- return codeStyleFacade.getTabSize(fileType);
- }
-
- private static int getWidth(@NotNull String indent, int tabSize) {
- int width = 0;
- for (int i = 0; i < indent.length(); i++) {
- char c = indent.charAt(i);
- switch (c) {
- case '\t':
- width = tabSize * (width / tabSize + 1);
- break;
- default:
- LOG.error("Unexpected whitespace character: " + ((int)c));
- case ' ':
- width++;
- }
- }
- 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/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/SmartIndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java
new file mode 100644
index 000000000000..8ab82c453ba1
--- /dev/null
+++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java
@@ -0,0 +1,138 @@
+/*
+ * 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.codeStyle.CodeStyleFacade;
+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.*;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+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.util.text.CharArrayUtil;
+import org.jetbrains.annotations.NotNull;
+
+public class SmartIndentingBackspaceHandler extends AbstractIndentingBackspaceHandler {
+ private static final Logger LOG = Logger.getInstance(SmartIndentingBackspaceHandler.class);
+
+ private String myReplacement;
+ private int myStartOffset;
+
+ public SmartIndentingBackspaceHandler() {
+ super(CodeInsightSettings.AUTOINDENT);
+ }
+
+ @Override
+ protected void doBeforeCharDeleted(char c, PsiFile file, Editor editor) {
+ Project project = file.getProject();
+ Document document = editor.getDocument();
+ CharSequence charSequence = document.getImmutableCharSequence();
+ CaretModel caretModel = editor.getCaretModel();
+ int caretOffset = caretModel.getOffset();
+ LogicalPosition pos = caretModel.getLogicalPosition();
+ int lineStartOffset = document.getLineStartOffset(pos.line);
+ int beforeWhitespaceOffset = CharArrayUtil.shiftBackward(charSequence, caretOffset - 1, " \t") + 1;
+ if (beforeWhitespaceOffset != lineStartOffset) {
+ myReplacement = null;
+ return;
+ }
+ PsiDocumentManager.getInstance(project).commitDocument(document);
+ CodeStyleFacade codeStyleFacade = CodeStyleFacade.getInstance(project);
+ myReplacement = codeStyleFacade.getLineIndent(document, lineStartOffset);
+ if (myReplacement == null) {
+ return;
+ }
+ int tabSize = getTabSize(codeStyleFacade, document);
+ int targetColumn = getWidth(myReplacement, tabSize);
+ int endOffset = CharArrayUtil.shiftForward(charSequence, caretOffset, " \t");
+ LogicalPosition logicalPosition = caretOffset < endOffset ? editor.offsetToLogicalPosition(endOffset) : pos;
+ int currentColumn = logicalPosition.column;
+ if (currentColumn > targetColumn) {
+ myStartOffset = lineStartOffset;
+ }
+ else if (logicalPosition.line == 0) {
+ myStartOffset = 0;
+ myReplacement = "";
+ }
+ else {
+ int prevLineEndOffset = document.getLineEndOffset(logicalPosition.line - 1);
+ myStartOffset = CharArrayUtil.shiftBackward(charSequence, prevLineEndOffset - 1, " \t") + 1;
+ if (myStartOffset != document.getLineStartOffset(logicalPosition.line - 1)) {
+ myReplacement = getSpacing(file, endOffset);
+ }
+ }
+ }
+
+ @Override
+ protected boolean doCharDeleted(char c, PsiFile file, Editor editor) {
+ if (myReplacement == null) {
+ return false;
+ }
+
+ Document document = editor.getDocument();
+ CaretModel caretModel = editor.getCaretModel();
+ int endOffset = CharArrayUtil.shiftForward(document.getImmutableCharSequence(), caretModel.getOffset(), " \t");
+
+ document.replaceString(myStartOffset, endOffset, myReplacement);
+ caretModel.moveToOffset(myStartOffset + myReplacement.length());
+
+ 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();
+ return codeStyleFacade.getTabSize(fileType);
+ }
+
+ private static int getWidth(@NotNull String indent, int tabSize) {
+ int width = 0;
+ for (int i = 0; i < indent.length(); i++) {
+ char c = indent.charAt(i);
+ switch (c) {
+ case '\t':
+ width = tabSize * (width / tabSize + 1);
+ break;
+ default:
+ LOG.error("Unexpected whitespace character: " + ((int)c));
+ case ' ':
+ width++;
+ }
+ }
+ return width;
+ }
+}
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUpdate.java b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUpdate.java
index 62f641b37807..07bd92cb687a 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUpdate.java
+++ b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUpdate.java
@@ -59,11 +59,11 @@ public class FoldingUpdate {
private static final Key>> CODE_FOLDING_KEY = Key.create("code folding");
private static final Key CODE_FOLDING_FILE_EXTENSION_KEY = Key.create("code folding file extension");
- private static final Comparator COMPARE_BY_OFFSET = new Comparator() {
+ private static final Comparator COMPARE_BY_OFFSET_REVERSED = new Comparator() {
@Override
public int compare(PsiElement element, PsiElement element1) {
- int startOffsetDiff = element.getTextRange().getStartOffset() - element1.getTextRange().getStartOffset();
- return startOffsetDiff == 0 ? element.getTextRange().getEndOffset() - element1.getTextRange().getEndOffset() : startOffsetDiff;
+ int startOffsetDiff = element1.getTextRange().getStartOffset() - element.getTextRange().getStartOffset();
+ return startOffsetDiff == 0 ? element1.getTextRange().getEndOffset() - element.getTextRange().getEndOffset() : startOffsetDiff;
}
};
@@ -284,7 +284,7 @@ public class FoldingUpdate {
@NotNull
@Override
protected Map> createMap() {
- return new TreeMap>(COMPARE_BY_OFFSET);
+ return new TreeMap>(COMPARE_BY_OFFSET_REVERSED);
}
@NotNull
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUtil.java
index f150f9924cb8..8a00fc94d2cb 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUtil.java
+++ b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUtil.java
@@ -29,17 +29,9 @@ public class FoldingUtil {
private FoldingUtil() {}
@Nullable
- public static FoldRegion findFoldRegion(Editor editor, int startOffset, int endOffset) {
- FoldRegion[] foldRegions = editor.getFoldingModel().getAllFoldRegions();
- for (FoldRegion region : foldRegions) {
- if (region.isValid() &&
- region.getStartOffset() == startOffset
- && region.getEndOffset() == endOffset) {
- return region;
- }
- }
-
- return null;
+ public static FoldRegion findFoldRegion(@NotNull Editor editor, int startOffset, int endOffset) {
+ FoldRegion region = editor.getFoldingModel().getFoldRegion(startOffset, endOffset);
+ return region != null && region.isValid() ? region : null;
}
@Nullable
diff --git a/platform/lang-impl/src/com/intellij/injected/editor/FoldingModelWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/FoldingModelWindow.java
index d87aa78c7ea3..3c839f208312 100644
--- a/platform/lang-impl/src/com/intellij/injected/editor/FoldingModelWindow.java
+++ b/platform/lang-impl/src/com/intellij/injected/editor/FoldingModelWindow.java
@@ -26,6 +26,7 @@ import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.ArrayList;
@@ -114,6 +115,19 @@ public class FoldingModelWindow implements FoldingModelEx{
return host; //todo convert to window?
}
+ @Nullable
+ @Override
+ public FoldRegion getFoldRegion(int startOffset, int endOffset) {
+ TextRange range = new TextRange(startOffset, endOffset);
+ TextRange hostRange = myDocumentWindow.injectedToHost(range);
+ FoldRegion hostRegion = myDelegate.getFoldRegion(hostRange.getStartOffset(), hostRange.getEndOffset());
+ if (hostRegion == null) {
+ return null;
+ }
+ FoldingRegionWindow window = hostRegion.getUserData(FOLD_REGION_WINDOW);
+ return window != null && window.getEditor() == myEditorWindow ? window : null;
+ }
+
@Override
public void runBatchFoldingOperation(@NotNull Runnable operation) {
myDelegate.runBatchFoldingOperation(operation);
diff --git a/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab-after.java b/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab-after.java
new file mode 100644
index 000000000000..de6981762dd0
--- /dev/null
+++ b/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab-after.java
@@ -0,0 +1,3 @@
+class A {
+ // comment
+}
diff --git a/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab.java b/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab.java
new file mode 100644
index 000000000000..3d98bc8d7b2f
--- /dev/null
+++ b/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab.java
@@ -0,0 +1,4 @@
+class A {
+
+ // comment
+}
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-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java b/platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java
index 62864d01b91a..e6311fbae473 100644
--- a/platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java
+++ b/platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java
@@ -37,6 +37,8 @@ import java.awt.event.MouseEvent;
* @author Vladimir Kondratyev
*/
public class ThreeComponentsSplitter extends JPanel implements Disposable {
+ public static final int MIN_SIZE = 48;
+
private int myDividerWidth;
/**
* /------/
@@ -189,14 +191,22 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable {
else {
firstCompontSize = getFirstSize();
lastComponentSize = getLastSize();
- int sizeLack = firstCompontSize + lastComponentSize - (componentSize - dividersCount * dividerWidth);
+ int sizeLack = firstCompontSize + lastComponentSize - (componentSize - dividersCount * dividerWidth - MIN_SIZE);
if (sizeLack > 0) {
- // Lacking size. Reduce first component's size, inner -> empty
- firstCompontSize -= sizeLack;
- innerComponentSize = 0;
+ // Lacking size. Reduce first & last component's size, inner -> MIN_SIZE
+ double firstSizeRatio = (double)firstCompontSize / (firstCompontSize + lastComponentSize);
+ if (firstCompontSize > 0) {
+ firstCompontSize -= sizeLack * firstSizeRatio;
+ firstCompontSize = Math.max(MIN_SIZE, firstCompontSize);
+ }
+ if (lastComponentSize > 0) {
+ lastComponentSize -= sizeLack * (1 - firstSizeRatio);
+ lastComponentSize = Math.max(MIN_SIZE, lastComponentSize);
+ }
+ innerComponentSize = MIN_SIZE;
}
else {
- innerComponentSize = componentSize - dividersCount * dividerWidth - getFirstSize() - getLastSize();
+ innerComponentSize = Math.max(MIN_SIZE, componentSize - dividersCount * dividerWidth - getFirstSize() - getLastSize());
}
if (!innerVisible()) {
@@ -395,6 +405,29 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable {
return lastVisible() ? myLastSize : 0;
}
+ public int getMinSize(boolean first) {
+ return getMinSize(first? myFirstComponent : myLastComponent);
+ }
+
+ public int getMaxSize(boolean first) {
+ final int size = getOrientation() ? this.getHeight() : this.getWidth();
+ return size - (first? myLastSize: myFirstSize) - MIN_SIZE;
+ }
+
+ private int getMinSize(JComponent component) {
+ if (isHonorMinimumSize()) {
+ if (component != null && myFirstComponent != null && myFirstComponent.isVisible() && myLastComponent != null && myLastComponent.isVisible()) {
+ if (getOrientation()) {
+ return component.getMinimumSize().height;
+ }
+ else {
+ return component.getMinimumSize().width;
+ }
+ }
+ }
+ return MIN_SIZE;
+ }
+
@Override
public void dispose() {
myLastComponent = null;
@@ -633,23 +666,24 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable {
myGlassPane.setCursor(getResizeCursor(), myListener);
myPoint = SwingUtilities.convertPoint(this, e.getPoint(), ThreeComponentsSplitter.this);
+ final int size = getOrientation() ? ThreeComponentsSplitter.this.getHeight() : ThreeComponentsSplitter.this.getWidth();
if (getOrientation()) {
- if (getHeight() > 0 || myDividerZone > 0) {
+ if (size > 0 || myDividerZone > 0) {
if (myIsFirst) {
- setFirstSize(Math.max(getMinSize(myFirstComponent), myPoint.y));
+ setFirstSize(Math.min(size - myLastSize - MIN_SIZE, Math.max(getMinSize(myFirstComponent), myPoint.y)));
}
else {
- setLastSize(Math.max(getMinSize(myLastComponent), ThreeComponentsSplitter.this.getHeight() - myPoint.y - getDividerWidth()));
+ setLastSize(Math.min(size - myFirstSize - MIN_SIZE, Math.max(getMinSize(myLastComponent), size - myPoint.y - getDividerWidth())));
}
}
}
else {
- if (getWidth() > 0 || myDividerZone > 0) {
+ if (size > 0 || myDividerZone > 0) {
if (myIsFirst) {
- setFirstSize(Math.max(getMinSize(myFirstComponent), myPoint.x));
+ setFirstSize(Math.min(size - myLastSize - MIN_SIZE, Math.max(getMinSize(myFirstComponent), myPoint.x)));
}
else {
- setLastSize(Math.max(getMinSize(myLastComponent), ThreeComponentsSplitter.this.getWidth() - myPoint.x - getDividerWidth()));
+ setLastSize(Math.min(size - myFirstSize - MIN_SIZE, Math.max(getMinSize(myLastComponent), size - myPoint.x - getDividerWidth())));
}
}
}
@@ -670,20 +704,6 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable {
}
}
- private int getMinSize(JComponent component) {
- if (isHonorMinimumSize()) {
- if (component != null && myFirstComponent != null && myFirstComponent.isVisible() && myLastComponent != null && myLastComponent.isVisible()) {
- if (getOrientation()) {
- return component.getMinimumSize().height;
- }
- else {
- return component.getMinimumSize().width;
- }
- }
- }
- return 0;
- }
-
protected void processMouseEvent(MouseEvent e) {
super.processMouseEvent(e);
if (!isShowing()) {
diff --git a/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java b/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java
index b616429a11c8..654f43e0c2ec 100644
--- a/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java
+++ b/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java
@@ -154,4 +154,8 @@ public abstract class ToolWindowManager {
@Nullable
public abstract Balloon getToolWindowBalloon(String id);
+
+ public abstract boolean isMaximized(@NotNull ToolWindow wnd);
+
+ public abstract void setMaximized(@NotNull ToolWindow wnd, boolean maximized);
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentUndoProvider.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentUndoProvider.java
index 5895026caaf5..777ec3a6ea1e 100644
--- a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentUndoProvider.java
+++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentUndoProvider.java
@@ -20,12 +20,11 @@ import com.intellij.openapi.command.undo.DocumentReference;
import com.intellij.openapi.command.undo.DocumentReferenceManager;
import com.intellij.openapi.command.undo.UndoConstants;
import com.intellij.openapi.command.undo.UndoManager;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
-import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
@@ -33,7 +32,6 @@ import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
public class DocumentUndoProvider implements Disposable {
- private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.command.impl.DocumentUndoProvider");
private static final Key UNDOING_EDITOR_CHANGE = Key.create("DocumentUndoProvider.UNDOING_EDITOR_CHANGE");
private final Project myProject;
@@ -61,32 +59,39 @@ public class DocumentUndoProvider implements Disposable {
if (doc != null) doc.putUserData(UNDOING_EDITOR_CHANGE, null);
}
- private class MyEditorDocumentListener extends DocumentAdapter {
+ private class MyEditorDocumentListener implements DocumentListener {
+ @Override
+ public void beforeDocumentChange(DocumentEvent e) {
+ Document document = e.getDocument();
+ if (shouldBeIgnored(document)) return;
+
+ UndoManagerImpl undoManager = getUndoManager();
+ if (undoManager.isActive() && isUndoable(document) && (undoManager.isUndoInProgress() || undoManager.isRedoInProgress()) &&
+ document.getUserData(UNDOING_EDITOR_CHANGE) != Boolean.TRUE) {
+ throw new IllegalStateException("Do not change documents during undo as it will break undo sequence.");
+ }
+ }
+
@Override
public void documentChanged(final DocumentEvent e) {
Document document = e.getDocument();
-
- // if we don't ignore copy's events, we will receive notification
- // for the same event twice (from original document too)
- // and undo will work incorrectly
- if (UndoManagerImpl.isCopy(document)) return;
-
- if (allEditorsAreViewersFor(document)) return;
- if (!shouldRecordActions(document)) return;
+ if (shouldBeIgnored(document)) return;
UndoManagerImpl undoManager = getUndoManager();
- if (!undoManager.isActive() || !isUndoable(document)) {
+ if (undoManager.isActive() && isUndoable(document)) {
+ registerUndoableAction(e);
+ }
+ else {
registerNonUndoableAction(document);
- return;
}
-
- if (undoManager.isUndoInProgress() || undoManager.isRedoInProgress()) {
- if (document.getUserData(UNDOING_EDITOR_CHANGE) != Boolean.TRUE) {
- LOG.error("Do not change documents during undo as it will break undo sequence.");
- }
- }
-
- registerUndoableAction(e);
+ }
+
+ private boolean shouldBeIgnored(Document document) {
+ return UndoManagerImpl.isCopy(document) // if we don't ignore copy's events, we will receive notification
+ // for the same event twice (from original document too)
+ // and undo will work incorrectly
+ || allEditorsAreViewersFor(document)
+ || !shouldRecordActions(document);
}
private boolean shouldRecordActions(final Document document) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionsTree.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionsTree.java
index 32e31900b804..b339d670989f 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionsTree.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionsTree.java
@@ -15,10 +15,8 @@
*/
package com.intellij.openapi.editor.impl;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.FoldRegion;
-import com.intellij.openapi.editor.RangeMarker;
-import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.util.Key;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -30,19 +28,11 @@ import java.util.*;
* User: cdr
*/
abstract class FoldRegionsTree {
-
- @SuppressWarnings("UseOfArchaicSystemPropertyAccessors")
- public static final boolean DEBUG = Boolean.getBoolean("idea.editor.debug.folding");
-
- private FoldRegion[] myCachedVisible;
- private FoldRegion[] myCachedTopLevelRegions;
- private int[] myCachedEndOffsets;
- private int[] myCachedStartOffsets;
- private int[] myCachedFoldedLines;
- int myCachedLastIndex = -1;
+ @NotNull private CachedData myCachedData = new CachedData();
//sorted using RangeMarker.BY_START_OFFSET comparator
//i.e., first by start offset, then, if start offsets are equal, by end offset
+ @NotNull
private List myRegions = ContainerUtil.newArrayList();
private static final Comparator BY_END_OFFSET = new Comparator() {
@@ -60,31 +50,19 @@ abstract class FoldRegionsTree {
void clear() {
clearCachedValues();
- if (myRegions != null) {
- for (FoldRegion region : myRegions) {
- region.dispose();
- }
+ for (FoldRegion region : myRegions) {
+ region.dispose();
}
myRegions = new ArrayList();
}
void clearCachedValues() {
- myCachedVisible = null;
- myCachedTopLevelRegions = null;
- myCachedEndOffsets = null;
- myCachedStartOffsets = null;
- myCachedFoldedLines = null;
- }
-
- private boolean isFoldingEnabledAndUpToDate() {
- return isFoldingEnabled() && myCachedVisible != null;
+ myCachedData = new CachedData();
}
protected abstract boolean isFoldingEnabled();
- protected abstract boolean isBatchFoldingProcessing();
-
void rebuild() {
List topLevels = new ArrayList(myRegions.size() / 2);
List visible = new ArrayList(myRegions.size());
@@ -116,13 +94,13 @@ abstract class FoldRegionsTree {
myRegions = allValid;
}
- myCachedTopLevelRegions = toFoldArray(topLevels);
- myCachedVisible = toFoldArray(visible);
+ FoldRegion[] topLevelRegions = toFoldArray(topLevels);
+ FoldRegion[] visibleRegions = toFoldArray(visible);
- Arrays.sort(myCachedTopLevelRegions, BY_END_OFFSET);
- Arrays.sort(myCachedVisible, BY_END_OFFSET_REVERSE);
+ Arrays.sort(topLevelRegions, BY_END_OFFSET);
+ Arrays.sort(visibleRegions, BY_END_OFFSET_REVERSE);
- updateCachedOffsets();
+ updateCachedOffsets(visibleRegions, topLevelRegions);
}
private static void removeRegionsWithSameStartOffset(List regions, FoldRegion region) {
@@ -142,93 +120,87 @@ abstract class FoldRegionsTree {
}
void updateCachedOffsets() {
+ CachedData cachedData = myCachedData;
+ updateCachedOffsets(cachedData.visibleRegions, cachedData.topLevelRegions);
+ }
+
+ private void updateCachedOffsets(FoldRegion[] visibleRegions, FoldRegion[] topLevelRegions) {
if (!isFoldingEnabled()) {
return;
}
- if (myCachedVisible == null) {
+ if (visibleRegions == null) {
rebuild();
return;
}
- for (FoldRegion foldRegion : myCachedVisible) {
+ for (FoldRegion foldRegion : visibleRegions) {
if (!foldRegion.isValid()) {
rebuild();
return;
}
}
- int length = myCachedTopLevelRegions.length;
- if (myCachedEndOffsets == null || myCachedEndOffsets.length != length) {
- if (length != 0) {
- myCachedEndOffsets = new int[length];
- myCachedStartOffsets = new int[length];
- myCachedFoldedLines = new int[length];
- }
- else {
- myCachedEndOffsets = ArrayUtil.EMPTY_INT_ARRAY;
- myCachedStartOffsets = ArrayUtil.EMPTY_INT_ARRAY;
- myCachedFoldedLines = ArrayUtil.EMPTY_INT_ARRAY;
- }
- }
-
+ int length = topLevelRegions.length;
+ int[] startOffsets = length == 0 ? ArrayUtil.EMPTY_INT_ARRAY : new int[length];
+ int[] endOffsets = length == 0 ? ArrayUtil.EMPTY_INT_ARRAY : new int[length];
+ int[] foldedLines = length == 0 ? ArrayUtil.EMPTY_INT_ARRAY : new int[length];
+
int sum = 0;
for (int i = 0; i < length; i++) {
- FoldRegion region = myCachedTopLevelRegions[i];
- myCachedStartOffsets[i] = region.getStartOffset();
- myCachedEndOffsets[i] = region.getEndOffset() - 1;
+ FoldRegion region = topLevelRegions[i];
+ startOffsets[i] = region.getStartOffset();
+ endOffsets[i] = region.getEndOffset() - 1;
Document document = region.getDocument();
sum += document.getLineNumber(region.getEndOffset()) - document.getLineNumber(region.getStartOffset());
- myCachedFoldedLines[i] = sum;
+ foldedLines[i] = sum;
}
+
+ myCachedData = new CachedData(visibleRegions, topLevelRegions, startOffsets, endOffsets, foldedLines);
}
boolean addRegion(@NotNull FoldRegion range) {
- // During batchProcessing elements are inserted in ascending order,
- // binary search find acceptable insertion place first time
- boolean canUseCachedValue = false;
- if (isBatchFoldingProcessing() && myCachedLastIndex >= 0 && myCachedLastIndex < myRegions.size()) {
- FoldRegion lastRegion = myRegions.get(myCachedLastIndex);
- if (RangeMarker.BY_START_OFFSET.compare(lastRegion, range) < 0) {
- canUseCachedValue = myCachedLastIndex == (myRegions.size() - 1)
- || RangeMarker.BY_START_OFFSET.compare(range, myRegions.get(myCachedLastIndex + 1)) <= 0;
+ int start = range.getStartOffset();
+ int end = range.getEndOffset();
+ int insertionIndex = myRegions.size();
+ for (int i = 0; i < myRegions.size(); i++) {
+ FoldRegion region = myRegions.get(i);
+ int rStart = region.getStartOffset();
+ int rEnd = region.getEndOffset();
+ if (rStart < start) {
+ if (region.isValid() && start < rEnd && rEnd < end) {
+ return false;
+ }
}
- }
- int index = canUseCachedValue ? myCachedLastIndex + 1 : Collections.binarySearch(myRegions, range, RangeMarker.BY_START_OFFSET);
- if (index < 0) index = -index - 1;
-
- if (index < myRegions.size()) {
- FoldRegion foldRegion = myRegions.get(index);
- if (TextRange.areSegmentsEqual(foldRegion, range)) {
- return false;
+ else if (rStart == start) {
+ if (rEnd == end) {
+ return false;
+ }
+ else if (rEnd > end) {
+ insertionIndex = Math.min(insertionIndex, i);
+ }
}
- }
-
- for (int i = index - 1; i >=0; --i) {
- final FoldRegion region = myRegions.get(i);
- if (region.getEndOffset() < range.getStartOffset()) break;
- if (region.isValid() && intersects(region, range)) {
- return false;
+ else {
+ insertionIndex = Math.min(insertionIndex, i);
+ if (rStart > end) {
+ break;
+ }
+ if (region.isValid() && rStart < end && end < rEnd) {
+ return false;
+ }
}
}
- for (int i = index; i < myRegions.size(); i++) {
- final FoldRegion region = myRegions.get(i);
- if (region.getStartOffset() > range.getEndOffset()) break;
- if (region.isValid() && intersects(region, range)) {
- return false;
- }
- }
-
- myRegions.add(myCachedLastIndex = index,range);
+ myRegions.add(insertionIndex, range);
return true;
}
@Nullable
FoldRegion fetchOutermost(int offset) {
- if (!isFoldingEnabledAndUpToDate()) return null;
+ CachedData cachedData = myCachedData;
+ if (cachedData.isUnavailable()) return null;
- final int[] starts = myCachedStartOffsets;
- final int[] ends = myCachedEndOffsets;
+ final int[] starts = cachedData.startOffsets;
+ final int[] ends = cachedData.endOffsets;
if (starts == null || ends == null) {
return null;
}
@@ -244,20 +216,7 @@ abstract class FoldRegionsTree {
start = i + 1;
}
else {
- // We encountered situation when cached data is inconsistent. It's not clear what produced that, so, the following was done:
- // 1. Corresponding check was added and cached data is rebuilt in case of inconsistency;
- // 2. Debug asserts are activated if dedicated flag is on (it's off by default);
- if (myCachedStartOffsets[i] != myCachedTopLevelRegions[i].getStartOffset()) {
- if (DEBUG) {
- assert false :
- "inconsistent cached fold data detected. Start offsets: " + Arrays.toString(myCachedStartOffsets)
- + ", end offsets: " + Arrays.toString(myCachedEndOffsets) + ", top regions: " + Arrays.toString(myCachedTopLevelRegions)
- + ", visible regions: " + Arrays.toString(myCachedVisible);
- }
- rebuild();
- return fetchOutermost(offset);
- }
- return myCachedTopLevelRegions[i];
+ return cachedData.topLevelRegions[i];
}
}
@@ -265,34 +224,26 @@ abstract class FoldRegionsTree {
}
FoldRegion[] fetchVisible() {
- if (!isFoldingEnabledAndUpToDate()) return FoldRegion.EMPTY_ARRAY;
- return myCachedVisible;
+ CachedData cachedData = myCachedData;
+ return cachedData.isUnavailable() ? FoldRegion.EMPTY_ARRAY : cachedData.visibleRegions;
}
@Nullable
FoldRegion[] fetchTopLevel() {
- if (!isFoldingEnabledAndUpToDate()) return null;
- return myCachedTopLevelRegions;
+ CachedData cachedData = myCachedData;
+ return cachedData.isUnavailable() ? null : cachedData.topLevelRegions;
}
private static boolean contains(FoldRegion outer, FoldRegion inner) {
return outer.getStartOffset() <= inner.getStartOffset() && outer.getEndOffset() >= inner.getEndOffset();
}
- private static boolean intersects(FoldRegion r1, FoldRegion r2) {
- final int s1 = r1.getStartOffset();
- final int s2 = r2.getStartOffset();
- final int e1 = r1.getEndOffset();
- final int e2 = r2.getEndOffset();
- return s1 < s2 && s2 < e1 && e1 < e2 || s2 < s1 && s1 < e2 && e2 < e1;
- }
-
static boolean contains(FoldRegion region, int offset) {
return region.getStartOffset() < offset && region.getEndOffset() > offset;
}
public FoldRegion[] fetchCollapsedAt(int offset) {
- if (!isFoldingEnabledAndUpToDate()) return FoldRegion.EMPTY_ARRAY;
+ if (myCachedData.isUnavailable()) return FoldRegion.EMPTY_ARRAY;
ArrayList allCollapsed = new ArrayList();
for (FoldRegion region : myRegions) {
if (!region.isExpanded() && contains(region, offset)) {
@@ -316,7 +267,7 @@ abstract class FoldRegionsTree {
}
FoldRegion[] fetchAllRegions() {
- if (!isFoldingEnabledAndUpToDate()) return FoldRegion.EMPTY_ARRAY;
+ if (myCachedData.isUnavailable()) return FoldRegion.EMPTY_ARRAY;
return toFoldArray(myRegions);
}
@@ -326,14 +277,19 @@ abstract class FoldRegionsTree {
}
int getFoldedLinesCountBefore(int offset) {
- int idx = getLastTopLevelIndexBefore(offset);
+ CachedData snapshot = myCachedData;
+ int idx = getLastTopLevelIndexBefore(snapshot, offset);
if (idx == -1) return 0;
- return myCachedFoldedLines[idx];
+ return snapshot.foldedLines[idx];
}
public int getLastTopLevelIndexBefore(int offset) {
- int[] endOffsets = myCachedEndOffsets;
- if (!isFoldingEnabledAndUpToDate() || endOffsets == null) return -1;
+ return getLastTopLevelIndexBefore(myCachedData, offset);
+ }
+
+ private static int getLastTopLevelIndexBefore(CachedData snapshot, int offset) {
+ int[] endOffsets = snapshot.endOffsets;
+ if (snapshot.isUnavailable() || endOffsets == null) return -1;
offset--; // end offsets are decremented in cache
int start = 0;
@@ -353,4 +309,137 @@ abstract class FoldRegionsTree {
return end;
}
+
+ @Nullable
+ public FoldRegion getRegionAt(int startOffset, int endOffset) {
+ int index = Collections.binarySearch(myRegions, new DummyFoldRegion(startOffset, endOffset), RangeMarker.BY_START_OFFSET);
+ return index < 0 ? null : myRegions.get(index);
+ }
+
+ private class CachedData implements Cloneable {
+ private final FoldRegion[] visibleRegions;
+ private final FoldRegion[] topLevelRegions;
+ private final int[] startOffsets;
+ private final int[] endOffsets;
+ private final int[] foldedLines;
+
+ private CachedData() {
+ this.visibleRegions = null;
+ this.topLevelRegions = null;
+ this.startOffsets = null;
+ this.endOffsets = null;
+ this.foldedLines = null;
+ }
+
+ private CachedData(FoldRegion[] visibleRegions, FoldRegion[] topLevelRegions, int[] startOffsets, int[] endOffsets, int[] foldedLines) {
+ this.visibleRegions = visibleRegions;
+ this.topLevelRegions = topLevelRegions;
+ this.startOffsets = startOffsets;
+ this.endOffsets = endOffsets;
+ this.foldedLines = foldedLines;
+ }
+
+ private boolean isUnavailable() {
+ return !isFoldingEnabled() || visibleRegions == null;
+ }
+ }
+
+ private static class DummyFoldRegion implements FoldRegion {
+ private final int myStartOffset;
+ private final int myEndOffset;
+
+ private DummyFoldRegion(int startOffset, int endOffset) {
+ myStartOffset = startOffset;
+ myEndOffset = endOffset;
+ }
+
+ @Override
+ public boolean isExpanded() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setExpanded(boolean expanded) {
+ throw new UnsupportedOperationException();
+ }
+
+ @NotNull
+ @Override
+ public String getPlaceholderText() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Editor getEditor() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public FoldingGroup getGroup() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean shouldNeverExpand() {
+ throw new UnsupportedOperationException();
+ }
+
+ @NotNull
+ @Override
+ public Document getDocument() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getStartOffset() {
+ return myStartOffset;
+ }
+
+ @Override
+ public int getEndOffset() {
+ return myEndOffset;
+ }
+
+ @Override
+ public boolean isValid() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setGreedyToLeft(boolean greedy) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setGreedyToRight(boolean greedy) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isGreedyToRight() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isGreedyToLeft() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void dispose() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public T getUserData(@NotNull Key key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putUserData(@NotNull Key key, @Nullable T value) {
+ throw new UnsupportedOperationException();
+ }
+ }
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java
index 7dbd0e3e9211..cec6a03feaec 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java
@@ -79,11 +79,6 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
protected boolean isFoldingEnabled() {
return FoldingModelImpl.this.isFoldingEnabled();
}
-
- @Override
- protected boolean isBatchFoldingProcessing() {
- return myIsBatchFoldingProcessing;
- }
};
myFoldRegionsProcessed = false;
refreshSettings();
@@ -210,12 +205,9 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
}
myIsBatchFoldingProcessing = true;
- myFoldTree.myCachedLastIndex = -1;
try {
operation.run();
} finally {
- myFoldTree.myCachedLastIndex = -1;
-
if (!oldBatchFlag) {
if (myFoldRegionsProcessed) {
notifyBatchFoldingProcessingDone(moveCaret);
@@ -253,6 +245,13 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
return myFoldTree.fetchOutermost(offset);
}
+ @Nullable
+ @Override
+ public FoldRegion getFoldRegion(int startOffset, int endOffset) {
+ assertReadAccess();
+ return myFoldTree.getRegionAt(startOffset, endOffset);
+ }
+
@Override
@Nullable
public FoldRegion getFoldingPlaceholderAt(Point p) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentFoldingModel.java b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentFoldingModel.java
index e8a0fd840b8e..a6350f7b3e4c 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentFoldingModel.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentFoldingModel.java
@@ -18,6 +18,7 @@ package com.intellij.openapi.editor.textarea;
import com.intellij.openapi.editor.FoldRegion;
import com.intellij.openapi.editor.FoldingModel;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
/**
* @author Denis Zhdanov
@@ -55,6 +56,12 @@ public class TextComponentFoldingModel implements FoldingModel {
return null;
}
+ @Nullable
+ @Override
+ public FoldRegion getFoldRegion(int startOffset, int endOffset) {
+ return null;
+ }
+
@Override
public void runBatchFoldingOperation(@NotNull Runnable operation) {
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java
index bf9a015bdfeb..371d847f1699 100644
--- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java
@@ -402,6 +402,7 @@ public final class InternalDecorator extends JPanel implements Queryable, DataPr
resize.add(new ResizeToolWindowAction.Right(myToolWindow, this));
resize.add(new ResizeToolWindowAction.Up(myToolWindow, this));
resize.add(new ResizeToolWindowAction.Down(myToolWindow, this));
+ resize.add(ActionManager.getInstance().getAction("MaximizeToolWindow"));
group.add(resize);
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java
new file mode 100644
index 000000000000..29d68ad59080
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java
@@ -0,0 +1,60 @@
+/*
+ * 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.openapi.wm.impl;
+
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowManager;
+import org.jetbrains.annotations.NotNull;
+
+public class MaximizeToolWindowAction extends AnAction {
+ public MaximizeToolWindowAction() {
+ super();
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ Project project = e.getProject();
+ if (project == null || project.isDisposed()) return;
+ ToolWindow toolWindow = e.getData(PlatformDataKeys.TOOL_WINDOW);
+ if (toolWindow == null) return;
+ ToolWindowManager manager = ToolWindowManager.getInstance(project);
+ manager.setMaximized(toolWindow, !manager.isMaximized(toolWindow));
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabled(true);
+ Project project = e.getProject();
+ if (project == null || project.isDisposed()) {
+ e.getPresentation().setEnabled(false);
+ return;
+ }
+ ToolWindow toolWindow = e.getData(PlatformDataKeys.TOOL_WINDOW);
+ if (toolWindow == null) {
+ e.getPresentation().setEnabled(false);
+ return;
+ }
+ ToolWindowManager manager = ToolWindowManager.getInstance(project);
+ e.getPresentation().setText(manager.isMaximized(toolWindow) ?
+ ActionsBundle.message("action.ResizeToolWindowMaximize.text.alternative") :
+ ActionsBundle.message("action.ResizeToolWindowMaximize.text"));
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java
index 101d3ab2762b..7d64b1180e15 100644
--- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java
@@ -25,9 +25,11 @@ import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.impl.*;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
+import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.ToolWindowType;
import com.intellij.openapi.wm.impl.content.ToolWindowContentUi;
import com.intellij.ui.InplaceButton;
@@ -210,6 +212,12 @@ public abstract class ToolWindowHeader extends JPanel implements Disposable, UIS
};
}
+ void switchMaximizedState(Project project) {
+ if (project == null || project.isDisposed()) return;
+ ToolWindowManager mgr = ToolWindowManager.getInstance(project);
+ mgr.setMaximized(myToolWindow, !mgr.isMaximized(myToolWindow));
+ }
+
@Override
public void uiSettingsChanged(UISettings source) {
clearCaches();
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java
index 7b9a7494c640..5fa360235cb2 100644
--- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java
@@ -198,6 +198,15 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx {
return null;
}
+ @Override
+ public boolean isMaximized(@NotNull ToolWindow wnd) {
+ return false;
+ }
+
+ @Override
+ public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
+ }
+
@Override
public void initToolWindow(@NotNull ToolWindowEP bean) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java
index c0c27ba8c128..8acebb5b5bfd 100644
--- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java
@@ -120,6 +120,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
private final FileEditorManager myFileEditorManager;
private final LafManager myLafManager;
private final Map myWindow2Balloon = new HashMap();
+ private Pair myMaximizedToolwindowSize = null;
private KeyState myCurrentState = KeyState.waiting;
private final Alarm myWaiterForSecondPress = new Alarm();
@@ -1982,6 +1983,16 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
myToolWindowsPane.stretchWidth(toolWindow, value);
}
+ @Override
+ public boolean isMaximized(@NotNull ToolWindow wnd) {
+ return myToolWindowsPane.isMaximized(wnd);
+ }
+
+ @Override
+ public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
+ myToolWindowsPane.setMaximized(wnd, maximized);
+ }
+
public void stretchHeight(ToolWindowImpl toolWindow, int value) {
myToolWindowsPane.stretchHeight(toolWindow, value);
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java
index d1232749d3c8..3606939167a8 100644
--- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java
@@ -22,20 +22,27 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.ui.ThreeComponentsSplitter;
+import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.wm.IdeGlassPaneUtil;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowType;
-import com.intellij.openapi.wm.ex.ToolWindowEx;
import com.intellij.openapi.wm.impl.commands.FinalizableCommand;
import com.intellij.ui.ScreenUtil;
import com.intellij.ui.components.JBLayeredPane;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.FadeInFadeOut;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
@@ -58,6 +65,7 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
private final HashMap myButton2Info;
private final HashMap myDecorator2Info;
private final HashMap myId2SplitProportion;
+ private Pair myMaximizedProportion = null;
/**
* This panel is the layered pane where all sliding tool windows are located. The DEFAULT
* layer contains splitters. The PALETTE layer contains all sliding tool windows.
@@ -192,6 +200,24 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
super.addNotify();
if (ScreenUtil.isStandardAddRemoveNotify(this)) {
UISettings.getInstance().addUISettingsListener(myUISettingsListener, myDisposable);
+ IdeGlassPaneUtil.find(this).addMousePreprocessor(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2 && e.getModifiersEx() == 0) {
+ e = SwingUtilities.convertMouseEvent(e.getComponent(), e, ToolWindowsPane.this);
+ Component component = SwingUtilities.getDeepestComponentAt(ToolWindowsPane.this, e.getX(), e.getY());
+ Component header = component == null ? null : UIUtil.findParentByCondition((JComponent)component, new Condition() {
+ @Override
+ public boolean value(Component component) {
+ return component instanceof ToolWindowHeader;
+ }
+ });
+ if (header instanceof ToolWindowHeader) {
+ ((ToolWindowHeader)header).switchMaximizedState(myFrame.getProject());
+ }
+ }
+ }
+ }, this);
}
}
@@ -490,7 +516,21 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
}
private void stretch(ToolWindow wnd, int value) {
- if (!wnd.isVisible()) return;
+ Pair pair = findResizerAndComponent(wnd);
+ if (pair == null) return;
+
+ boolean vertical = wnd.getAnchor() == ToolWindowAnchor.TOP || wnd.getAnchor() == ToolWindowAnchor.BOTTOM;
+ int actualSize = (vertical ? pair.second.getHeight() : pair.second.getWidth()) + value;
+ boolean first = wnd.getAnchor() == ToolWindowAnchor.LEFT || wnd.getAnchor() == ToolWindowAnchor.TOP;
+ int maxValue = vertical ? myVerticalSplitter.getMaxSize(first) : myHorizontalSplitter.getMaxSize(first);
+ int minValue = vertical ? myVerticalSplitter.getMinSize(first) : myHorizontalSplitter.getMinSize(first);;
+
+ pair.first.setSize(Math.max(minValue, Math.min(maxValue, actualSize)));
+ }
+
+ @Nullable
+ private Pair findResizerAndComponent(ToolWindow wnd) {
+ if (!wnd.isVisible()) return null;
Resizer resizer = null;
Component cmp = null;
@@ -534,26 +574,7 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
}
}
- if (resizer == null) return;
-
- int currentValue = wnd.getAnchor().isHorizontal() ? cmp.getHeight() : cmp.getWidth();
-
- int actualSize = currentValue + value;
-
- int minValue =
- wnd.getAnchor().isHorizontal() ? ((ToolWindowEx)wnd).getDecorator().getHeaderHeight() : 16 + myHorizontalSplitter.getDividerWidth();
- int maxValue = wnd.getAnchor().isHorizontal() ? myLayeredPane.getHeight() : myLayeredPane.getWidth();
-
-
- if (actualSize < minValue) {
- actualSize = minValue;
- }
-
- if (actualSize > maxValue) {
- actualSize = maxValue;
- }
-
- resizer.setSize(actualSize);
+ return resizer != null ? Pair.create(resizer, cmp) : null;
}
private void updateLayout() {
@@ -598,6 +619,27 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
}
}
+ public boolean isMaximized(@NotNull ToolWindow wnd) {
+ return myMaximizedProportion != null && myMaximizedProportion.first == wnd;
+ }
+
+ public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
+ Pair resizerAndComponent = findResizerAndComponent(wnd);
+ if (resizerAndComponent == null) return;
+
+ if (!maximized) {
+ ToolWindow maximizedWindow = myMaximizedProportion.first;
+ assert maximizedWindow == wnd;
+ resizerAndComponent.first.setSize(myMaximizedProportion.second);
+ myMaximizedProportion = null;
+ } else {
+ int size = wnd.getAnchor().isHorizontal() ? resizerAndComponent.second.getHeight() : resizerAndComponent.second.getWidth();
+ stretch(wnd, Short.MAX_VALUE);
+ myMaximizedProportion = Pair.create(wnd, size);
+ }
+ doLayout();
+ }
+
interface Resizer {
void setSize(int size);
diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java
index 04033418742f..84cc23a37ca9 100644
--- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java
+++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java
@@ -427,6 +427,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo
private DockWindow createWindowFor(@Nullable String id, DockContainer container) {
String windowId = id != null ? id : String.valueOf(myWindowIdCounter++);
DockWindow window = new DockWindow(windowId, myProject, container, container instanceof DockContainer.Dialog);
+ Disposer.register(container, window);
window.setDimensionKey("dock-window-" + windowId);
myWindows.put(container, window);
return window;
diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties
index 58adbf219537..138a5e3b60b5 100644
--- a/platform/platform-resources-en/src/messages/ActionsBundle.properties
+++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties
@@ -842,6 +842,9 @@ action.ResizeToolWindowUp.text=Stretch to Top
action.ResizeToolWindowUp.description=Resize active tool window to the top
action.ResizeToolWindowDown.text=Stretch to Bottom
action.ResizeToolWindowDown.description=Resize active tool window to the bottom
+action.ResizeToolWindowMaximize.text=Maximize tool window
+action.ResizeToolWindowMaximize.text.alternative=Restore tool window size
+action.ResizeToolWindowMaximize.description=Maximize tool widow
action.IncrementWindowWidth.text=Increment Width
action.DecrementWindowWidth.text=Decrement Width
action.IncrementWindowHeight.text=Increment Height
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/platform/platform-resources/src/idea/Keymap_Default.xml b/platform/platform-resources/src/idea/Keymap_Default.xml
index 405316ff8468..8907cc61f633 100644
--- a/platform/platform-resources/src/idea/Keymap_Default.xml
+++ b/platform/platform-resources/src/idea/Keymap_Default.xml
@@ -774,6 +774,9 @@
+
+
+