blob: 932a8b05d8d33fc15ed6cbe02dcbd4acb6aed575 [file] [log] [blame]
/*
* Copyright 2000-2013 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.editor.impl;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.FoldRegion;
import com.intellij.openapi.editor.FoldingGroup;
import com.intellij.util.containers.hash.HashMap;
import java.util.Collection;
import java.util.Map;
class FoldingAnchorsOverlayStrategy {
private final EditorImpl myEditor;
public FoldingAnchorsOverlayStrategy(EditorImpl editor) {
myEditor = editor;
}
public Collection<DisplayedFoldingAnchor> getAnchorsToDisplay(int firstVisibleOffset, int lastVisibleOffset, FoldRegion activeFoldRegion) {
Map<Integer, DisplayedFoldingAnchor> result = new HashMap<Integer, DisplayedFoldingAnchor>();
FoldRegion[] visibleFoldRegions = myEditor.getFoldingModel().fetchVisible();
for (FoldRegion region : visibleFoldRegions) {
if (!region.isValid()) continue;
final int startOffset = region.getStartOffset();
if (startOffset > lastVisibleOffset) continue;
final int endOffset = getEndOffset(region);
if (endOffset < firstVisibleOffset) continue;
if (!isFoldingPossible(startOffset, endOffset)) continue;
final FoldingGroup group = region.getGroup();
if (group != null && myEditor.getFoldingModel().getFirstRegion(group, region) != region) continue;
//offset = Math.min(myEditor.getDocument().getTextLength() - 1, offset);
int foldStart = myEditor.offsetToVisualLine(startOffset);
if (!region.isExpanded()) {
tryAdding(result, region, foldStart, 0, DisplayedFoldingAnchor.Type.COLLAPSED, activeFoldRegion);
}
else {
//offset = Math.min(myEditor.getDocument().getTextLength() - 1, offset);
int foldEnd = myEditor.offsetToVisualLine(endOffset);
tryAdding(result, region, foldStart, foldEnd - foldStart, DisplayedFoldingAnchor.Type.EXPANDED_TOP, activeFoldRegion);
tryAdding(result, region, foldEnd, foldEnd - foldStart, DisplayedFoldingAnchor.Type.EXPANDED_BOTTOM, activeFoldRegion);
}
}
return result.values();
}
private static void tryAdding(Map<Integer, DisplayedFoldingAnchor> resultsMap,
FoldRegion region,
int visualLine,
int visualHeight,
DisplayedFoldingAnchor.Type type,
FoldRegion activeRegion) {
DisplayedFoldingAnchor prev = resultsMap.get(visualLine);
if (prev != null) {
if (prev.foldRegion == activeRegion) {
return;
}
if (region != activeRegion && prev.foldRegionVisualLines < visualHeight) {
return;
}
}
resultsMap.put(visualLine, new DisplayedFoldingAnchor(region, visualLine, visualHeight, type));
}
private int getEndOffset(FoldRegion foldRange) {
FoldingGroup group = foldRange.getGroup();
return group == null ? foldRange.getEndOffset() : myEditor.getFoldingModel().getEndOffset(group);
}
/**
* Allows to answer if there may be folding for the given offsets.
* <p/>
* The rule is that we can fold range that occupies multiple logical or visual lines.
*
* @param startOffset start offset of the target region to check
* @param endOffset end offset of the target region to check
* @return
*/
private boolean isFoldingPossible(int startOffset, int endOffset) {
Document document = myEditor.getDocument();
if (startOffset >= document.getTextLength()) {
return false;
}
int endOffsetToUse = Math.min(endOffset, document.getTextLength());
if (endOffsetToUse <= startOffset) {
return false;
}
if (document.getLineNumber(startOffset) != document.getLineNumber(endOffsetToUse)) {
return true;
}
return myEditor.getSettings().isAllowSingleLogicalLineFolding()
&& !myEditor.getSoftWrapModel().getSoftWrapsForRange(startOffset, endOffsetToUse).isEmpty();
}
}