Allow more than one folding range per AST node [r=kb]

This commit is contained in:
Oleg Shpynov
2011-07-12 09:24:24 +04:00
committed by Roman Chernyatchik
parent b5677bd343
commit d85a64e20b
3 changed files with 70 additions and 48 deletions
@@ -32,6 +32,7 @@ import org.jetbrains.annotations.Nullable;
public interface FoldingBuilder {
/**
* Builds the folding regions for the specified node in the AST tree and its children.
* Note that you can have several folding regions for one AST node, i.e. several FoldingDescriptior with similar AST node.
*
* @param node the node for which folding is requested.
* @param document the document for which folding is built. Can be used to retrieve line
@@ -37,6 +37,7 @@ import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.ParameterizedCachedValue;
import com.intellij.psi.util.ParameterizedCachedValueProvider;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -86,7 +87,7 @@ public class FoldingUpdate {
final Editor editor,
final boolean applyDefaultState) {
final TreeMap<PsiElement, FoldingDescriptor> elementsToFoldMap = new TreeMap<PsiElement, FoldingDescriptor>(COMPARE_BY_OFFSET);
final FoldingMap elementsToFoldMap = new FoldingMap();
getFoldingsFor(file instanceof PsiCompiledElement ? (PsiFile)((PsiCompiledElement)file).getMirror() : file, document, elementsToFoldMap, quick);
final UpdateFoldRegionsOperation operation = new UpdateFoldRegionsOperation(project, editor, file, elementsToFoldMap, applyDefaultState, false);
@@ -120,8 +121,7 @@ public class FoldingUpdate {
List<DocumentWindow> injectedDocuments = InjectedLanguageUtil.getCachedInjectedDocuments(file);
if (injectedDocuments.isEmpty()) return null;
final List<EditorWindow> injectedEditors = new ArrayList<EditorWindow>();
final List<PsiFile> injectedFiles = new ArrayList<PsiFile>();
final List<Map<PsiElement, FoldingDescriptor>> maps = new ArrayList<Map<PsiElement, FoldingDescriptor>>();
final List<FoldingMap> maps = new ArrayList<FoldingMap>();
for (DocumentWindow injectedDocument : injectedDocuments) {
PsiFile injectedFile = PsiDocumentManager.getInstance(project).getPsiFile(injectedDocument);
if (injectedFile == null || !injectedFile.isValid() || !injectedDocument.isValid()) continue;
@@ -129,8 +129,7 @@ public class FoldingUpdate {
if (!(injectedEditor instanceof EditorWindow)) continue;
injectedEditors.add((EditorWindow)injectedEditor);
injectedFiles.add(injectedFile);
Map<PsiElement, FoldingDescriptor> map = new TreeMap<PsiElement, FoldingDescriptor>(COMPARE_BY_OFFSET);
final FoldingMap map = new FoldingMap();
maps.add(map);
getFoldingsFor(injectedFile, injectedDocument, map, false);
}
@@ -141,8 +140,8 @@ public class FoldingUpdate {
EditorWindow injectedEditor = injectedEditors.get(i);
PsiFile injectedFile = injectedFiles.get(i);
if (!injectedEditor.getDocument().isValid()) continue;
Map<PsiElement, FoldingDescriptor> map = maps.get(i);
UpdateFoldRegionsOperation op = new UpdateFoldRegionsOperation(project, injectedEditor, injectedFile, map, applyDefaultState, true);
FoldingMap map = maps.get(i);
UpdateFoldRegionsOperation op = new UpdateFoldRegionsOperation(project, injectedEditor, map, applyDefaultState, true);
injectedEditor.getFoldingModel().runBatchFoldingOperationDoNotCollapseCaret(op);
}
@@ -151,7 +150,10 @@ public class FoldingUpdate {
};
}
private static void getFoldingsFor(@NotNull PsiFile file, @NotNull Document document, @NotNull Map<PsiElement, FoldingDescriptor> elementsToFoldMap, boolean quick) {
private static void getFoldingsFor(@NotNull PsiFile file,
@NotNull Document document,
@NotNull FoldingMap elementsToFoldMap,
boolean quick) {
final FileViewProvider viewProvider = file.getViewProvider();
for (final Language language : viewProvider.getLanguages()) {
final PsiFile psi = viewProvider.getPsi(language);
@@ -167,10 +169,17 @@ public class FoldingUpdate {
" and called on file " + psi +
" is outside document range: " + docRange);
}
elementsToFoldMap.put(descriptor.getElement().getPsi(), descriptor);
elementsToFoldMap.putValue(descriptor.getElement().getPsi(), descriptor);
}
}
}
}
public static class FoldingMap extends MultiMap<PsiElement, FoldingDescriptor>{
@Override
protected Map<PsiElement, Collection<FoldingDescriptor>> createMap() {
return new TreeMap<PsiElement, Collection<FoldingDescriptor>>(COMPARE_BY_OFFSET);
}
}
}
@@ -33,6 +33,7 @@ import com.intellij.psi.SmartPointerManager;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -43,20 +44,20 @@ import static com.intellij.util.containers.CollectionFactory.newTroveMap;
* @author cdr
*/
class UpdateFoldRegionsOperation implements Runnable {
static final Key<Boolean> ALLOW_FOLDING_ON_CARET_LINE_KEY = Key.create("AllowFoldingOnCaretLine.KEY");
private final Project myProject;
private final Editor myEditor;
private final PsiFile myFile;
private final boolean myApplyDefaultState;
private final Map<PsiElement, FoldingDescriptor> myElementsToFoldMap;
private final FoldingUpdate.FoldingMap myElementsToFoldMap;
private final boolean myForInjected;
UpdateFoldRegionsOperation(@NotNull Project project,
@NotNull Editor editor,
@NotNull PsiFile file,
@NotNull Map<PsiElement, FoldingDescriptor> elementsToFoldMap,
@NotNull FoldingUpdate.FoldingMap elementsToFoldMap,
boolean applyDefaultState,
boolean forInjected) {
myProject = project;
@@ -79,7 +80,7 @@ class UpdateFoldRegionsOperation implements Runnable {
List<FoldRegion> newRegions = addNewRegions(info, foldingModel, rangeToExpandStatusMap, shouldExpand, groupExpand);
applyExpandStatus(newRegions, shouldExpand, groupExpand);
// Reset the key.
myEditor.putUserData(ALLOW_FOLDING_ON_CARET_LINE_KEY, false);
}
@@ -102,19 +103,18 @@ class UpdateFoldRegionsOperation implements Runnable {
Map<FoldingGroup, Boolean> groupExpand) {
List<FoldRegion> newRegions = arrayList();
SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject);
for (final Map.Entry<PsiElement, FoldingDescriptor> entry : myElementsToFoldMap.entrySet()) {
for (PsiElement element : myElementsToFoldMap.keySet()) {
ProgressManager.checkCanceled();
PsiElement element = entry.getKey();
final FoldingDescriptor descriptor = entry.getValue();
FoldingGroup group = descriptor.getGroup();
TextRange range = descriptor.getRange();
String placeholder = descriptor.getPlaceholderText();
FoldRegion region = foldingModel.createFoldRegion(range.getStartOffset(), range.getEndOffset(),
placeholder == null ? "..." : placeholder,
group,
descriptor.isNonExpandable());
if (region == null) continue;
final Collection<FoldingDescriptor> descriptors = myElementsToFoldMap.get(element);
for (FoldingDescriptor descriptor : descriptors) {
FoldingGroup group = descriptor.getGroup();
TextRange range = descriptor.getRange();
String placeholder = descriptor.getPlaceholderText();
FoldRegion region = foldingModel.createFoldRegion(range.getStartOffset(), range.getEndOffset(),
placeholder == null ? "..." : placeholder,
group,
descriptor.isNonExpandable());
if (region == null) continue;
PsiElement psi = descriptor.getElement().getPsi();
@@ -126,15 +126,25 @@ class UpdateFoldRegionsOperation implements Runnable {
info.addRegion(region, smartPointerManager.createSmartPsiElementPointer(psi, myFile));
newRegions.add(region);
boolean expandStatus = !descriptor.isNonExpandable() && shouldExpandNewRegion(element, range, rangeToExpandStatusMap);
if (group == null) {
shouldExpand.put(region, expandStatus);
}
else {
final Boolean alreadyExpanded = groupExpand.get(group);
groupExpand.put(group, alreadyExpanded == null ? expandStatus : alreadyExpanded.booleanValue() || expandStatus);
if (psi == null || !psi.isValid() || !foldingModel.addFoldRegion(region)) {
region.dispose();
continue;
}
info.addRegion(region, smartPointerManager.createSmartPsiElementPointer(psi));
newRegions.add(region);
boolean expandStatus = !descriptor.isNonExpandable() && shouldExpandNewRegion(element, range, rangeToExpandStatusMap);
if (group == null) {
shouldExpand.put(region, expandStatus);
}
else {
final Boolean alreadyExpanded = groupExpand.get(group);
groupExpand.put(group, alreadyExpanded == null ? expandStatus : alreadyExpanded.booleanValue() || expandStatus);
}
}
}
return newRegions;
}
@@ -174,21 +184,23 @@ class UpdateFoldRegionsOperation implements Runnable {
if (isInjected != myForInjected) continue;
}
if (element != null && myElementsToFoldMap.containsKey(element)) {
final FoldingDescriptor descriptor = myElementsToFoldMap.get(element);
TextRange range = descriptor.getRange();
if (!region.isValid() ||
region.getGroup() != null ||
descriptor.getGroup() != null ||
region.getStartOffset() != range.getStartOffset() ||
region.getEndOffset() != range.getEndOffset() ||
!region.getPlaceholderText().equals(descriptor.getPlaceholderText()) ||
range.getLength() < 2
) {
rangeToExpandStatusMap.put(range, region.isExpanded());
toRemove.add(region);
}
else {
myElementsToFoldMap.remove(element);
final Collection<FoldingDescriptor> descriptors = myElementsToFoldMap.get(element);
for (FoldingDescriptor descriptor : descriptors) {
TextRange range = descriptor.getRange();
if (!region.isValid() ||
region.getGroup() != null ||
descriptor.getGroup() != null ||
region.getStartOffset() != range.getStartOffset() ||
region.getEndOffset() != range.getEndOffset() ||
!region.getPlaceholderText().equals(descriptor.getPlaceholderText()) ||
range.getLength() < 2
) {
rangeToExpandStatusMap.put(range, region.isExpanded());
toRemove.add(region);
}
else {
myElementsToFoldMap.remove(element);
}
}
}
else if (region.isValid() && info.isLightRegion(region)) {