mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -85,6 +85,12 @@ public interface FoldingModel {
|
||||
@Nullable
|
||||
FoldRegion getCollapsedRegionAtOffset(int offset);
|
||||
|
||||
/**
|
||||
* Returns fold region with given boundaries, if it exists, or <code>null</code> 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)}.
|
||||
|
||||
@@ -76,10 +76,11 @@ public class CodeInsightSettings implements PersistentStateComponent<Element>, 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;
|
||||
|
||||
+68
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-2
@@ -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();
|
||||
|
||||
-189
@@ -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);
|
||||
}
|
||||
}
|
||||
+43
@@ -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;
|
||||
}
|
||||
}
|
||||
+138
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -59,11 +59,11 @@ public class FoldingUpdate {
|
||||
private static final Key<ParameterizedCachedValue<Runnable, Couple<Boolean>>> CODE_FOLDING_KEY = Key.create("code folding");
|
||||
private static final Key<String> CODE_FOLDING_FILE_EXTENSION_KEY = Key.create("code folding file extension");
|
||||
|
||||
private static final Comparator<PsiElement> COMPARE_BY_OFFSET = new Comparator<PsiElement>() {
|
||||
private static final Comparator<PsiElement> COMPARE_BY_OFFSET_REVERSED = new Comparator<PsiElement>() {
|
||||
@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<PsiElement, Collection<FoldingDescriptor>> createMap() {
|
||||
return new TreeMap<PsiElement, Collection<FoldingDescriptor>>(COMPARE_BY_OFFSET);
|
||||
return new TreeMap<PsiElement, Collection<FoldingDescriptor>>(COMPARE_BY_OFFSET_REVERSED);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
class A {
|
||||
<caret>// comment
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class A {
|
||||
|
||||
<caret>// comment
|
||||
}
|
||||
+45
@@ -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(" <caret>text",
|
||||
" <caret>text");
|
||||
}
|
||||
|
||||
public void testAtLineStart() {
|
||||
doTest("line1\n<caret>line2",
|
||||
"line1<caret>line2");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+27
-22
@@ -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<Boolean> 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) {
|
||||
|
||||
+210
-121
@@ -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<FoldRegion> myRegions = ContainerUtil.newArrayList();
|
||||
|
||||
private static final Comparator<FoldRegion> BY_END_OFFSET = new Comparator<FoldRegion>() {
|
||||
@@ -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<FoldRegion>();
|
||||
}
|
||||
|
||||
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<FoldRegion> topLevels = new ArrayList<FoldRegion>(myRegions.size() / 2);
|
||||
List<FoldRegion> visible = new ArrayList<FoldRegion>(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<FoldRegion> 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<FoldRegion> allCollapsed = new ArrayList<FoldRegion>();
|
||||
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> T getUserData(@NotNull Key<T> key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+7
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
+9
@@ -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) {
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
|
||||
private final FileEditorManager myFileEditorManager;
|
||||
private final LafManager myLafManager;
|
||||
private final Map<String, Balloon> myWindow2Balloon = new HashMap<String, Balloon>();
|
||||
private Pair<String, Integer> 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);
|
||||
}
|
||||
|
||||
@@ -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<StripeButton, WindowInfoImpl> myButton2Info;
|
||||
private final HashMap<InternalDecorator, WindowInfoImpl> myDecorator2Info;
|
||||
private final HashMap<String, Float> myId2SplitProportion;
|
||||
private Pair<ToolWindow, Integer> 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<Component>() {
|
||||
@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<Resizer, Component> 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<Resizer, Component> 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<Resizer, Component> 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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -578,7 +578,8 @@
|
||||
id="EnterBetweenBracesHandler"/>
|
||||
<enterHandlerDelegate implementation="com.intellij.codeInsight.editorActions.enter.EnterAfterJavadocTagHandler"/>
|
||||
|
||||
<backspaceHandlerDelegate implementation="com.intellij.codeInsight.editorActions.IndentingBackspaceHandler" />
|
||||
<backspaceHandlerDelegate implementation="com.intellij.codeInsight.editorActions.SimpleIndentingBackspaceHandler" />
|
||||
<backspaceHandlerDelegate implementation="com.intellij.codeInsight.editorActions.SmartIndentingBackspaceHandler" />
|
||||
|
||||
<codeInsight.linkHandler prefix="#inspection/" handlerClass="com.intellij.codeInsight.hint.InspectionDescriptionLinkHandler"/>
|
||||
<codeInsight.linkHandler prefix="#navigation/" handlerClass="com.intellij.codeInsight.hint.NavigationLinkHandler"/>
|
||||
|
||||
@@ -774,6 +774,9 @@
|
||||
<action id="ResizeToolWindowDown">
|
||||
<keyboard-shortcut first-keystroke="control shift DOWN"/>
|
||||
</action>
|
||||
<action id="MaximizeToolWindow">
|
||||
<keyboard-shortcut first-keystroke="control shift QUOTE"/>
|
||||
</action>
|
||||
<action id="HideSideWindows"/>
|
||||
<action id="ShowPopupMenu">
|
||||
<keyboard-shortcut first-keystroke="CONTEXT_MENU"/>
|
||||
|
||||
@@ -355,6 +355,7 @@
|
||||
<action id="HideAllWindows" class="com.intellij.ide.actions.HideAllToolWindowsAction"/>
|
||||
<action id="CloseActiveTab" class="com.intellij.ide.actions.CloseActiveTabAction"/>
|
||||
<action id="JumpToLastWindow" class="com.intellij.ide.actions.JumpToLastWindowAction"/>
|
||||
<action id="MaximizeToolWindow" class="com.intellij.openapi.wm.impl.MaximizeToolWindowAction"/>
|
||||
<separator/>
|
||||
<action id="TogglePinnedMode" class="com.intellij.ide.actions.TogglePinnedModeAction"/>
|
||||
<action id="ToggleDockMode" class="com.intellij.ide.actions.ToggleDockModeAction"/>
|
||||
|
||||
@@ -86,6 +86,20 @@ public class FoldingTest extends AbstractEditorTest {
|
||||
});
|
||||
}
|
||||
|
||||
public void testIntersectsWithRegionFarInStorageOrder() {
|
||||
myModel.runBatchFoldingOperation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FoldRegion region = myModel.addFoldRegion(0, 10, ".");
|
||||
assertNotNull(region);
|
||||
region = myModel.addFoldRegion(1, 5, ".");
|
||||
assertNotNull(region);
|
||||
region = myModel.addFoldRegion(6, 11, ".");
|
||||
assertNull(region);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testAddEmptyRegion() {
|
||||
FoldRegion region = null;
|
||||
try {
|
||||
|
||||
@@ -654,6 +654,15 @@ public class Mock {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMaximized(@NotNull ToolWindow wnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyByBalloon(@NotNull final String toolWindowId, @NotNull final MessageType type, @NotNull final String htmlBody) {
|
||||
}
|
||||
|
||||
+1
-3
@@ -180,7 +180,6 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
|
||||
@Override
|
||||
public VirtualFile copyFileToProject(@NotNull @NonNls final String sourceFilePath, @NotNull @NonNls final String targetPath) {
|
||||
final String testDataPath = getTestDataPath();
|
||||
assert testDataPath != null : "test data path not specified";
|
||||
|
||||
File fromFile = new File(testDataPath + "/" + sourceFilePath);
|
||||
if (!fromFile.exists()) {
|
||||
@@ -236,7 +235,6 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
|
||||
@Override
|
||||
public VirtualFile copyDirectoryToProject(@NotNull @NonNls final String sourceFilePath, @NotNull @NonNls final String targetPath) {
|
||||
final String testDataPath = getTestDataPath();
|
||||
assert testDataPath != null : "test data path not specified";
|
||||
|
||||
final File fromFile = new File(testDataPath + "/" + sourceFilePath);
|
||||
if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
|
||||
@@ -1276,7 +1274,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
|
||||
|
||||
@Override
|
||||
public boolean isToolEnabled(HighlightDisplayKey key, PsiElement element) {
|
||||
return key != null && key.toString() != null && myAvailableTools.containsKey(key.toString()) && !myDisabledInspections.contains(key.toString());
|
||||
return key != null && myAvailableTools.containsKey(key.toString()) && !myDisabledInspections.contains(key.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
<lang.emacs language="Python" implementationClass="com.jetbrains.python.editor.PyEmacsHandler"/>
|
||||
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyAnnotatingVisitor"/>
|
||||
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyDumbAwareAnnotator"/>
|
||||
<backspaceHandlerDelegate implementation="com.jetbrains.python.editor.PythonBackspaceHandler"/>
|
||||
<quoteHandler fileType="Python" className="com.jetbrains.python.editor.PythonQuoteHandler"/>
|
||||
<enterHandlerDelegate implementation="com.jetbrains.python.editor.PythonEnterHandler"/>
|
||||
<enterHandlerDelegate implementation="com.jetbrains.python.editor.PyEnterAtIndentHandler" order="first"/>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user