public void expandAll(final boolean expanded) { if (myDuringSynchronize) return; myDuringSynchronize = true; try { for (int i = 0; i < myCount; i++) { final int index = i; final FoldingModelEx model = myEditors[index].getFoldingModel(); model.runBatchFoldingOperation(new Runnable() { @Override public void run() { for (FoldedBlock folding : getFoldedBlocks()) { FoldRegion region = folding.getRegion(index); if (region != null) region.setExpanded(expanded); } } }); } } finally { myDuringSynchronize = false; } }
@Override public void run() { EditorFoldingInfo info = EditorFoldingInfo.get(myEditor); FoldingModelEx foldingModel = (FoldingModelEx)myEditor.getFoldingModel(); Map<TextRange,Boolean> rangeToExpandStatusMap = newTroveMap(); // FoldingUpdate caches instances of our object, so they must be immutable. FoldingUpdate.FoldingMap elementsToFold = new FoldingUpdate.FoldingMap(myElementsToFoldMap); removeInvalidRegions(info, foldingModel, elementsToFold, rangeToExpandStatusMap); Map<FoldRegion, Boolean> shouldExpand = newTroveMap(); Map<FoldingGroup, Boolean> groupExpand = newTroveMap(); List<FoldRegion> newRegions = addNewRegions(info, foldingModel, elementsToFold, rangeToExpandStatusMap, shouldExpand, groupExpand); applyExpandStatus(newRegions, shouldExpand, groupExpand); }
public static boolean isEnabled(@NotNull Editor editor) { if (editor.getSelectionModel().hasSelection()) { int start = editor.getSelectionModel().getSelectionStart(); int end = editor.getSelectionModel().getSelectionEnd(); if (start + 1 >= end) { return false; } if (start < end && editor.getDocument().getCharsSequence().charAt(end - 1) == '\n') end--; FoldRegion region = FoldingUtil.findFoldRegion(editor, start, end); if (region == null) { return !((FoldingModelEx) editor.getFoldingModel()).intersectsRegion(start, end); } else { return EditorFoldingInfo.get(editor).getPsiElement(region) == null; } } else { return FoldingUtil.getFoldRegionsAtOffset(editor, editor.getCaretModel().getOffset()).length > 0; } }
@Override public void run() { EditorFoldingInfo info = EditorFoldingInfo.get(myEditor); FoldingModelEx foldingModel = (FoldingModelEx)myEditor.getFoldingModel(); Map<TextRange,Boolean> rangeToExpandStatusMap = new THashMap<>(); removeInvalidRegions(info, foldingModel, rangeToExpandStatusMap); Map<FoldRegion, Boolean> shouldExpand = new THashMap<>(); Map<FoldingGroup, Boolean> groupExpand = new THashMap<>(); List<FoldRegion> newRegions = addNewRegions(info, foldingModel, rangeToExpandStatusMap, shouldExpand, groupExpand); applyExpandStatus(newRegions, shouldExpand, groupExpand); foldingModel.clearDocumentRangesModificationStatus(); }
public void testIDEA127145() { PsiFile file = myFixture.addFileToProject("Program.java", "import java.io.InputStream;\n" + "import java.util.HashMap;\n" + "import java.util.Map;\n" + "\n" + "class Program {\n" + " private static InputStream getFile(String name, Map<String, Object> args) {\n" + " return Program.class.getResourceAsStream(name);\n" + " }\n" + "\n" + " public static void main(String[] args) {\n" + " // Ctrl + B or Ctrl + Left Mouse Button work correctly for following string:\n" + " final String name = \"file.sql\";\n" + " // But it jumps only to folder in following case:\n" + " final InputStream inputStream = getFile(\"dir/fil<caret>e.sql\", new HashMap<String, Object>());\n" + " }\n" + "}"); PsiFile fileSql = myFixture.addFileToProject("dir/file.sql", "select 1;"); myFixture.configureFromExistingVirtualFile(file.getVirtualFile()); Editor editor = myFixture.getEditor(); CodeFoldingManager.getInstance(getProject()).buildInitialFoldings(editor); FoldingModelEx foldingModel = (FoldingModelEx)editor.getFoldingModel(); foldingModel.rebuild(); myFixture.doHighlighting(); PsiElement element = GotoDeclarationAction.findTargetElement(getProject(), editor, editor.getCaretModel().getOffset()); assertTrue("Should navigate to: file.sql instead of " + element, element != null && element.equals(fileSql)); }
private void destroyFoldings(final int index) { final FoldingModelEx model = myEditors[index].getFoldingModel(); model.runBatchFoldingOperation(new Runnable() { @Override public void run() { for (FoldedBlock folding : getFoldedBlocks()) { FoldRegion region = folding.getRegion(index); if (region != null) model.removeFoldRegion(region); } } }); }
@Override public int getLeadSelectionOffset() { validateContext(false); int caretOffset = getOffset(); if (hasSelection()) { RangeMarker marker = mySelectionMarker; if (marker != null) { int startOffset = marker.getStartOffset(); int endOffset = marker.getEndOffset(); if (caretOffset != startOffset && caretOffset != endOffset) { // Try to check if current selection is tweaked by fold region. FoldingModelEx foldingModel = myEditor.getFoldingModel(); FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(caretOffset); if (foldRegion != null) { if (foldRegion.getStartOffset() == startOffset) { return endOffset; } else if (foldRegion.getEndOffset() == endOffset) { return startOffset; } } } if (caretOffset == endOffset) { return startOffset; } else { return endOffset; } } } return caretOffset; }
@Override public void setUp() throws Exception { super.setUp(); init("I don't know what you mean by `glory,'\" Alice said" + "Humpty Dumpty smiled contemptuously. \"Of course you don't -- till I tell you. I meant `there's a nice knock-down argument for you!'" + "But glory doesn't mean `a nice knock-down argument,'\" Alice objected." + "When I use a word,\" Humpty Dumpty said, in a rather scornful tone, \"it means just what I choose it to mean -- neither more nor less." + "The question is,\" said Alice, \"whether you can make words mean so many different things." + "The question is,\" said Humpty Dumpty, \"which is to be master -- that's all.", TestFileType.TEXT); myModel = (FoldingModelEx)myEditor.getFoldingModel(); }
@Override public void buildInitialFoldings(@NotNull final Editor editor) { final Project project = editor.getProject(); if (project == null || !project.equals(myProject) || editor.isDisposed()) return; if (!((FoldingModelEx)editor.getFoldingModel()).isFoldingEnabled()) return; if (!FoldingUpdate.supportsDumbModeFolding(editor)) return; Document document = editor.getDocument(); PsiDocumentManager.getInstance(myProject).commitDocument(document); CodeFoldingState foldingState = buildInitialFoldings(document); if (foldingState != null) { foldingState.setToEditor(editor); } }
@Nullable @Override public CodeFoldingState buildInitialFoldings(@NotNull final Document document) { if (myProject.isDisposed()) { return null; } ApplicationManager.getApplication().assertReadAccessAllowed(); PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(myProject); if (psiDocumentManager.isUncommited(document)) { // skip building foldings for uncommitted document, CodeFoldingPass invoked by daemon will do it later return null; } //Do not save/restore folding for code fragments final PsiFile file = psiDocumentManager.getPsiFile(document); if (file == null || !file.isValid() || !file.getViewProvider().isPhysical() && !ApplicationManager.getApplication().isUnitTestMode()) { return null; } final FoldingUpdate.FoldingMap foldingMap = FoldingUpdate.getFoldingsFor(file, document, true); return new CodeFoldingState() { @Override public void setToEditor(@NotNull final Editor editor) { ApplicationManagerEx.getApplicationEx().assertIsDispatchThread(); if (myProject.isDisposed() || editor.isDisposed()) return; final FoldingModelEx foldingModel = (FoldingModelEx)editor.getFoldingModel(); if (!foldingModel.isFoldingEnabled()) return; if (isFoldingsInitializedInEditor(editor)) return; if (DumbService.isDumb(myProject) && !FoldingUpdate.supportsDumbModeFolding(editor)) return; foldingModel.runBatchFoldingOperationDoNotCollapseCaret(new UpdateFoldRegionsOperation(myProject, editor, file, foldingMap, YES, false)); initFolding(editor); } }; }
@Override public int getLeadSelectionOffset() { validateContext(false); int caretOffset = myEditor.getCaretModel().getOffset(); if (hasSelection()) { MyRangeMarker marker = mySelectionMarker; if (marker != null) { int startOffset = marker.getStartOffset(); int endOffset = marker.getEndOffset(); if (caretOffset != startOffset && caretOffset != endOffset) { // Try to check if current selection is tweaked by fold region. FoldingModelEx foldingModel = myEditor.getFoldingModel(); FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(caretOffset); if (foldRegion != null) { if (foldRegion.getStartOffset() == startOffset) { return endOffset; } else if (foldRegion.getEndOffset() == endOffset) { return startOffset; } } } if (caretOffset == endOffset) { return startOffset; } else { return endOffset; } } } return caretOffset; }
@Override public void run() { EditorFoldingInfo info = EditorFoldingInfo.get(myEditor); FoldingModelEx foldingModel = (FoldingModelEx)myEditor.getFoldingModel(); Map<TextRange,Boolean> rangeToExpandStatusMap = newTroveMap(); removeInvalidRegions(info, foldingModel, rangeToExpandStatusMap); Map<FoldRegion, Boolean> shouldExpand = newTroveMap(); Map<FoldingGroup, Boolean> groupExpand = newTroveMap(); List<FoldRegion> newRegions = addNewRegions(info, foldingModel, rangeToExpandStatusMap, shouldExpand, groupExpand); applyExpandStatus(newRegions, shouldExpand, groupExpand); }
public static void resetInfo(@NotNull Editor editor) { EditorFoldingInfo info = editor.getUserData(KEY); if (info != null) { info.dispose(); } FoldingModel foldingModel = editor.getFoldingModel(); if (foldingModel instanceof FoldingModelEx) { ((FoldingModelEx)foldingModel).rebuild(); } editor.putUserData(KEY, null); }
private void init(EditorView view, int startOffset, int startLogicalLine, int currentOrPrevWrapIndex, int nextFoldingIndex, @Nullable Runnable quickEvaluationListener) { myQuickEvaluationListener = quickEvaluationListener; myView = view; EditorImpl editor = view.getEditor(); myDocument = editor.getDocument(); FoldingModelEx foldingModel = editor.getFoldingModel(); FoldRegion[] regions = foldingModel.fetchTopLevel(); myRegions = regions == null ? FoldRegion.EMPTY_ARRAY : regions; SoftWrapModelImpl softWrapModel = editor.getSoftWrapModel(); List<? extends SoftWrap> softWraps = softWrapModel.getRegisteredSoftWraps(); SoftWrap currentOrPrevWrap = currentOrPrevWrapIndex < 0 || currentOrPrevWrapIndex >= softWraps.size() ? null : softWraps.get(currentOrPrevWrapIndex); SoftWrap followingWrap = (currentOrPrevWrapIndex + 1) < 0 || (currentOrPrevWrapIndex + 1) >= softWraps.size() ? null : softWraps.get(currentOrPrevWrapIndex + 1); myVisualLineStartOffset = mySegmentStartOffset = startOffset; myCurrentFoldRegionIndex = nextFoldingIndex; myCurrentEndLogicalLine = startLogicalLine; myCurrentX = myView.getInsets().left; if (mySegmentStartOffset == 0) { myCurrentX += myView.getPrefixTextWidthInPixels(); } else if (currentOrPrevWrap != null && mySegmentStartOffset == currentOrPrevWrap.getStart()) { myCurrentX += currentOrPrevWrap.getIndentInPixels(); myCurrentVisualColumn = currentOrPrevWrap.getIndentInColumns(); } myNextWrapOffset = followingWrap == null ? Integer.MAX_VALUE : followingWrap.getStart(); setInlaysAndFragmentIterator(); }
@Override public int getLeadSelectionOffset() { validateContext(false); int caretOffset = getOffset(); if (hasSelection()) { RangeMarker marker = mySelectionMarker; if (marker != null && marker.isValid()) { int startOffset = marker.getStartOffset(); int endOffset = marker.getEndOffset(); if (caretOffset != startOffset && caretOffset != endOffset) { // Try to check if current selection is tweaked by fold region. FoldingModelEx foldingModel = myEditor.getFoldingModel(); FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(caretOffset); if (foldRegion != null) { if (foldRegion.getStartOffset() == startOffset) { return endOffset; } else if (foldRegion.getEndOffset() == endOffset) { return startOffset; } } } if (caretOffset == endOffset) { return startOffset; } else { return endOffset; } } } return caretOffset; }
@Override public void buildInitialFoldings(@Nonnull final Editor editor) { final Project project = editor.getProject(); if (project == null || !project.equals(myProject) || editor.isDisposed()) return; if (!((FoldingModelEx)editor.getFoldingModel()).isFoldingEnabled()) return; if (!FoldingUpdate.supportsDumbModeFolding(editor)) return; Document document = editor.getDocument(); PsiDocumentManager.getInstance(myProject).commitDocument(document); CodeFoldingState foldingState = buildInitialFoldings(document); if (foldingState != null) { foldingState.setToEditor(editor); } }
@Nullable @Override public CodeFoldingState buildInitialFoldings(@Nonnull final Document document) { if (myProject.isDisposed()) { return null; } ApplicationManager.getApplication().assertReadAccessAllowed(); PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(myProject); if (psiDocumentManager.isUncommited(document)) { // skip building foldings for uncommitted document, CodeFoldingPass invoked by daemon will do it later return null; } //Do not save/restore folding for code fragments final PsiFile file = psiDocumentManager.getPsiFile(document); if (file == null || !file.isValid() || !file.getViewProvider().isPhysical() && !ApplicationManager.getApplication().isUnitTestMode()) { return null; } final List<FoldingUpdate.RegionInfo> regionInfos = FoldingUpdate.getFoldingsFor(file, document, true); return editor -> { ApplicationManagerEx.getApplicationEx().assertIsDispatchThread(); if (myProject.isDisposed() || editor.isDisposed()) return; final FoldingModelEx foldingModel = (FoldingModelEx)editor.getFoldingModel(); if (!foldingModel.isFoldingEnabled()) return; if (isFoldingsInitializedInEditor(editor)) return; if (DumbService.isDumb(myProject) && !FoldingUpdate.supportsDumbModeFolding(editor)) return; foldingModel.runBatchFoldingOperationDoNotCollapseCaret(new UpdateFoldRegionsOperation(myProject, editor, file, regionInfos, UpdateFoldRegionsOperation.ApplyDefaultStateMode.YES, false, false)); initFolding(editor); }; }
@Override public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) { editor.getFoldingModel().runBatchFoldingOperation(new Runnable() { @Override public void run() { final EditorFoldingInfo info = EditorFoldingInfo.get(editor); FoldingModelEx model = (FoldingModelEx) editor.getFoldingModel(); PsiElement element = file.findElementAt(editor.getCaretModel().getOffset() - 1); if (!(element instanceof PsiJavaToken) || ((PsiJavaToken) element).getTokenType() != JavaTokenType.RBRACE) { element = file.findElementAt(editor.getCaretModel().getOffset()); } if (element == null) return; PsiCodeBlock block = PsiTreeUtil.getParentOfType(element, PsiCodeBlock.class); FoldRegion previous = null; FoldRegion myPrevious = null; while (block != null) { int start = block.getTextRange().getStartOffset(); int end = block.getTextRange().getEndOffset(); FoldRegion existing = FoldingUtil.findFoldRegion(editor, start, end); if (existing != null) { if (existing.isExpanded()) { existing.setExpanded(false); editor.getCaretModel().moveToOffset(existing.getEndOffset()); return; } previous = existing; if (info.getPsiElement(existing) == null) myPrevious = existing; block = PsiTreeUtil.getParentOfType(block, PsiCodeBlock.class); continue; } if (!model.intersectsRegion(start, end)) { FoldRegion region = model.addFoldRegion(start, end, ourPlaceHolderText); LOG.assertTrue(region != null); region.setExpanded(false); if (myPrevious != null && info.getPsiElement(region) == null) { info.removeRegion(myPrevious); model.removeFoldRegion(myPrevious); } int offset = block.getTextRange().getEndOffset() < editor.getCaretModel().getOffset() ? start : end; editor.getCaretModel().moveToOffset(offset); return; } else break; } if (previous != null) { previous.setExpanded(false); if (myPrevious != null) { info.removeRegion(myPrevious); model.removeFoldRegion(myPrevious); } editor.getCaretModel().moveToOffset(previous.getEndOffset()); } } }); }
@Override public LightweightHint show(@NotNull final Editor editor, @NotNull Point p, boolean alignToRight, @NotNull TooltipGroup group, @NotNull HintHint intInfo) { LightweightHint hint; final JComponent editorComponent = editor.getComponent(); TextRange range = myDocumentFragment.getTextRange(); int startOffset = range.getStartOffset(); int endOffset = range.getEndOffset(); Document doc = myDocumentFragment.getDocument(); int endLine = doc.getLineNumber(endOffset); int startLine = doc.getLineNumber(startOffset); JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane(); // There is a possible case that collapsed folding region is soft wrapped, hence, we need to anchor // not logical but visual line start. VisualPosition visual = editor.offsetToVisualPosition(startOffset); p = editor.visualPositionToXY(visual); p = SwingUtilities.convertPoint( ((EditorEx)editor).getGutterComponentEx(), p, layeredPane ); p.x -= 3; p.y += editor.getLineHeight(); Point screenPoint = new Point(p); SwingUtilities.convertPointToScreen(screenPoint, layeredPane); int maxLineCount = (ScreenUtil.getScreenRectangle(screenPoint).height - screenPoint.y) / editor.getLineHeight(); if (endLine - startLine > maxLineCount) { endOffset = doc.getLineEndOffset(Math.max(0, Math.min(startLine + maxLineCount, doc.getLineCount() - 1))); } if (endOffset < startOffset) return null; FoldingModelEx foldingModel = (FoldingModelEx)editor.getFoldingModel(); foldingModel.setFoldingEnabled(false); TextRange textRange = new TextRange(startOffset, endOffset); hint = EditorFragmentComponent.showEditorFragmentHintAt(editor, textRange, p.y, false, false, true, true, true); foldingModel.setFoldingEnabled(true); return hint; }
private VisualLineFragmentsIterator(EditorView view, int offset, boolean beforeSoftWrap, @Nullable Runnable quickEvaluationListener) { myQuickEvaluationListener = quickEvaluationListener; myView = view; EditorImpl editor = view.getEditor(); myDocument = editor.getDocument(); FoldingModelEx foldingModel = editor.getFoldingModel(); FoldRegion[] regions = foldingModel.fetchTopLevel(); myRegions = regions == null ? FoldRegion.EMPTY_ARRAY : regions; int visualLineStartOffset = EditorUtil.getNotFoldedLineStartOffset(editor, offset); SoftWrapModelImpl softWrapModel = editor.getSoftWrapModel(); List<? extends SoftWrap> softWraps = softWrapModel.getRegisteredSoftWraps(); int currentOrPrevWrapIndex = softWrapModel.getSoftWrapIndex(offset); if (currentOrPrevWrapIndex < 0) { currentOrPrevWrapIndex = - currentOrPrevWrapIndex - 2; } else if (beforeSoftWrap) { currentOrPrevWrapIndex--; } SoftWrap currentOrPrevWrap = currentOrPrevWrapIndex < 0 || currentOrPrevWrapIndex >= softWraps.size() ? null : softWraps.get(currentOrPrevWrapIndex); SoftWrap followingWrap = (currentOrPrevWrapIndex + 1) >= softWraps.size() ? null : softWraps.get(currentOrPrevWrapIndex + 1); if (currentOrPrevWrap != null && currentOrPrevWrap.getStart() > visualLineStartOffset) { visualLineStartOffset = currentOrPrevWrap.getStart(); } myVisualLineStartOffset = mySegmentStartOffset = visualLineStartOffset; myCurrentFoldRegionIndex = foldingModel.getLastCollapsedRegionBefore(mySegmentStartOffset) + 1; myCurrentEndLogicalLine = myDocument.getLineNumber(mySegmentStartOffset); if (mySegmentStartOffset == 0) { myCurrentX = myView.getPrefixTextWidthInPixels(); } else if (currentOrPrevWrap != null && mySegmentStartOffset == currentOrPrevWrap.getStart()) { myCurrentX = currentOrPrevWrap.getIndentInPixels(); myCurrentVisualColumn = currentOrPrevWrap.getIndentInColumns(); } myNextWrapOffset = followingWrap == null ? Integer.MAX_VALUE : followingWrap.getStart(); setFragmentIterator(); }
private <T> T calculate(@NotNull MappingStrategy<T> strategy) throws IllegalStateException { T eagerMatch = strategy.eagerMatch(); if (eagerMatch != null) { return eagerMatch; } EditorPosition position = strategy.buildInitialPosition(); // Folding model doesn't return information about fold regions if their 'enabled' state is set to 'false'. Unfortunately, // such situation occurs not only on manual folding disabling but on internal processing as well. E.g. 'enabled' is set // to 'false' during fold preview representation. So, we set it to 'true' in order to retrieve fold regions and restore // to previous state after that. FoldingModelEx foldingModel = myEditor.getFoldingModel(); boolean foldingState = foldingModel.isFoldingEnabled(); foldingModel.setFoldingEnabled(true); CompositeDataProvider provider; try { provider = new CompositeDataProvider( new SoftWrapsDataProvider(myStorage), new FoldingDataProvider(myEditor.getFoldingModel().fetchTopLevel()), getTabulationDataProvider(position.visualLine) ); } finally { foldingModel.setFoldingEnabled(foldingState); } provider.advance(position.offset); while (provider.hasData()) { Pair<SoftWrapDataProviderKeys, ?> data = provider.getData(); T result = null; int sortingKey = provider.getSortingKey(); // There is a possible case that, say, fold region is soft wrapped. We don't want to perform unnecessary then. if (position.offset <= sortingKey) { result = strategy.advance(position, sortingKey); if (result != null) { return result; } } switch (data.first) { case SOFT_WRAP: result = strategy.processSoftWrap(position, (SoftWrap)data.second); break; case COLLAPSED_FOLDING: result = strategy.processFoldRegion(position, (FoldRegion)data.second); break; case TABULATION: result = strategy.processTabulation(position, (TabData)data.second); break; } if (result != null) { return result; } provider.next(); } return strategy.build(position); }
private List<FoldRegion> addNewRegions(@NotNull EditorFoldingInfo info, @NotNull FoldingModelEx foldingModel, FoldingUpdate.FoldingMap elementsToFold, @NotNull Map<TextRange, Boolean> rangeToExpandStatusMap, @NotNull Map<FoldRegion, Boolean> shouldExpand, @NotNull Map<FoldingGroup, Boolean> groupExpand) { List<FoldRegion> newRegions = newArrayList(); SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject); for (PsiElement element : elementsToFold.keySet()) { ProgressManager.checkCanceled(); final Collection<FoldingDescriptor> descriptors = elementsToFold.get(element); for (FoldingDescriptor descriptor : descriptors) { FoldingGroup group = descriptor.getGroup(); TextRange range = descriptor.getRange(); String placeholder = descriptor.getPlaceholderText(); if (range.getEndOffset() > myEditor.getDocument().getTextLength()) { LOG.error(String.format("Invalid folding descriptor detected (%s). It ends beyond the document range (%d)", descriptor, myEditor.getDocument().getTextLength())); continue; } FoldRegion region = foldingModel.createFoldRegion(range.getStartOffset(), range.getEndOffset(), placeholder == null ? "..." : placeholder, group, descriptor.isNonExpandable()); if (region == null) continue; PsiElement psi = descriptor.getElement().getPsi(); if (psi == null || !psi.isValid() || !foldingModel.addFoldRegion(region) || !myFile.isValid()) { 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; }
FoldingModelWindow(@NotNull FoldingModelEx delegate, @NotNull DocumentWindow documentWindow, @NotNull EditorWindow editorWindow) { myDelegate = delegate; myDocumentWindow = documentWindow; myEditorWindow = editorWindow; }
@Override public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) { editor.getFoldingModel().runBatchFoldingOperation(new Runnable() { @Override public void run() { final EditorFoldingInfo info = EditorFoldingInfo.get(editor); FoldingModelEx model = (FoldingModelEx) editor.getFoldingModel(); PsiElement element = file.findElementAt(editor.getCaretModel().getOffset() - 1); if (!(element instanceof PsiJavaToken) || ((PsiJavaToken) element).getTokenType() != JavaTokenType.RBRACE) { element = file.findElementAt(editor.getCaretModel().getOffset()); } if (element == null) return; PsiCodeBlock block = PsiTreeUtil.getParentOfType(element, PsiCodeBlock.class); FoldRegion previous = null; FoldRegion myPrevious = null; while (block != null) { int start = block.getTextRange().getStartOffset(); int end = block.getTextRange().getEndOffset(); FoldRegion existing = FoldingUtil.findFoldRegion(editor, start, end); if (existing != null) { previous = existing; if (info.getPsiElement(existing) == null) myPrevious = existing; block = PsiTreeUtil.getParentOfType(block, PsiCodeBlock.class); continue; } if (!model.intersectsRegion(start, end)) { FoldRegion region = model.addFoldRegion(start, end, ourPlaceHolderText); LOG.assertTrue(region != null); region.setExpanded(false); if (myPrevious != null && info.getPsiElement(region) == null) { info.removeRegion(myPrevious); model.removeFoldRegion(myPrevious); } int offset = block.getTextRange().getEndOffset() < editor.getCaretModel().getOffset() ? start : end; editor.getCaretModel().moveToOffset(offset); return; } else break; } if (previous != null) { previous.setExpanded(false); if (myPrevious != null) { info.removeRegion(myPrevious); model.removeFoldRegion(myPrevious); } editor.getCaretModel().moveToOffset(previous.getEndOffset()); } } }); }
@Override public LightweightHint show(@NotNull final Editor editor, @NotNull Point p, boolean alignToRight, @NotNull TooltipGroup group, @NotNull HintHint intInfo) { LightweightHint hint; final JComponent editorComponent = editor.getComponent(); TextRange range = myDocumentFragment.getTextRange(); int startOffset = range.getStartOffset(); int endOffset = range.getEndOffset(); Document doc = myDocumentFragment.getDocument(); int endLine = doc.getLineNumber(endOffset); int startLine = doc.getLineNumber(startOffset); JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane(); // There is a possible case that collapsed folding region is soft wrapped, hence, we need to anchor // not logical but visual line start. VisualPosition visual = editor.offsetToVisualPosition(startOffset); p = editor.visualPositionToXY(visual); p = SwingUtilities.convertPoint( ((EditorEx)editor).getGutterComponentEx(), p, layeredPane ); p.x -= 3; p.y += editor.getLineHeight(); Point screenPoint = new Point(p); SwingUtilities.convertPointToScreen(screenPoint, layeredPane); int maxLineCount = (ScreenUtil.getScreenRectangle(screenPoint).height - screenPoint.y) / editor.getLineHeight(); if (endLine - startLine > maxLineCount) { endOffset = doc.getLineEndOffset(Math.max(0, Math.min(startLine + maxLineCount, doc.getLineCount() - 1))); } FoldingModelEx foldingModel = (FoldingModelEx)editor.getFoldingModel(); foldingModel.setFoldingEnabled(false); TextRange textRange = new TextRange(startOffset, endOffset); hint = EditorFragmentComponent.showEditorFragmentHintAt(editor, textRange, p.x, p.y, false, false, true); foldingModel.setFoldingEnabled(true); return hint; }
public void testDuplicateRegions() { StringBuilder text = new StringBuilder(); for (int i = 0; i < 450; i++) { text.append('a'); } Editor editor = EditorFactory.getInstance().createEditor(new DocumentImpl(text)); try { final Ref<Boolean> expandedStatus = new Ref<Boolean>(); final int startOffset = 6; final int endOffset = 16; final FoldingModelEx model = (FoldingModelEx)editor.getFoldingModel(); model.runBatchFoldingOperation(new Runnable() { @Override public void run() { model.addFoldRegion(2, 20, ".."); model.addFoldRegion(4, 18, ".."); FoldRegion oldRegion = model.addFoldRegion(startOffset, endOffset, ".."); assertNotNull(oldRegion); expandedStatus.set(!oldRegion.isExpanded()); } }); assertEquals(3, model.getAllFoldRegions().length); final Ref<FoldRegion> newRegion = new Ref<FoldRegion>(); model.runBatchFoldingOperation(new Runnable() { @Override public void run() { newRegion.set(model.createFoldRegion(startOffset, endOffset, "..", null, false)); assertNotNull(newRegion.get()); newRegion.get().setExpanded(expandedStatus.get()); boolean additionFlag = model.addFoldRegion(newRegion.get()); assertTrue(additionFlag); } }); FoldRegion fetched = model.fetchOutermost(startOffset); assertSame(newRegion.get(), fetched); assertEquals(3, model.getAllFoldRegions().length); } finally { EditorFactory.getInstance().releaseEditor(editor); } }
private List<FoldRegion> addNewRegions(@NotNull EditorFoldingInfo info, @NotNull FoldingModelEx foldingModel, @NotNull Map<TextRange, Boolean> rangeToExpandStatusMap, @NotNull Map<FoldRegion, Boolean> shouldExpand, @NotNull Map<FoldingGroup, Boolean> groupExpand) { List<FoldRegion> newRegions = newArrayList(); SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject); for (PsiElement element : myElementsToFoldMap.keySet()) { ProgressManager.checkCanceled(); final Collection<FoldingDescriptor> descriptors = myElementsToFoldMap.get(element); for (FoldingDescriptor descriptor : descriptors) { FoldingGroup group = descriptor.getGroup(); TextRange range = descriptor.getRange(); String placeholder = descriptor.getPlaceholderText(); if (range.getEndOffset() > myEditor.getDocument().getTextLength()) { LOG.error(String.format("Invalid folding descriptor detected (%s). It ends beyond the document range (%d)", descriptor, myEditor.getDocument().getTextLength())); continue; } FoldRegion region = foldingModel.createFoldRegion(range.getStartOffset(), range.getEndOffset(), placeholder == null ? "..." : placeholder, group, descriptor.isNonExpandable()); if (region == null) continue; PsiElement psi = descriptor.getElement().getPsi(); if (psi == null || !psi.isValid() || !foldingModel.addFoldRegion(region)) { region.dispose(); continue; } 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); } } } return newRegions; }
@Override public void buildInitialFoldings(@NotNull final Editor editor) { ApplicationManagerEx.getApplicationEx().assertIsDispatchThread(editor.getComponent()); final Project project = editor.getProject(); if (project == null || !project.equals(myProject)) return; final Document document = editor.getDocument(); //Do not save/restore folding for code fragments final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document); if (file == null || !file.getViewProvider().isPhysical() && !ApplicationManager.getApplication().isUnitTestMode()) return; final FoldingModelEx foldingModel = (FoldingModelEx)editor.getFoldingModel(); if (!foldingModel.isFoldingEnabled()) return; if (project.isDisposed() || editor.isDisposed() || !file.isValid()) return; PsiDocumentManager.getInstance(myProject).commitDocument(document); Runnable operation = new Runnable() { @Override public void run() { Runnable runnable = updateFoldRegions(editor, true, true); if (runnable != null) { runnable.run(); } if (myProject.isDisposed() || editor.isDisposed()) return; foldingModel.runBatchFoldingOperation(new Runnable() { @Override public void run() { DocumentFoldingInfo documentFoldingInfo = getDocumentFoldingInfo(document); Editor[] editors = EditorFactory.getInstance().getEditors(document, myProject); for (Editor otherEditor : editors) { if (otherEditor == editor) continue; documentFoldingInfo.loadFromEditor(otherEditor); break; } documentFoldingInfo.setToEditor(editor); documentFoldingInfo.clear(); } }); } }; UIUtil.invokeLaterIfNeeded(operation); }
public FoldingModelWindow(@NotNull FoldingModelEx delegate, @NotNull DocumentWindow documentWindow, @NotNull EditorWindow editorWindow) { myDelegate = delegate; myDocumentWindow = documentWindow; myEditorWindow = editorWindow; }
@Override public LightweightHint show(@Nonnull final Editor editor, @Nonnull Point p, boolean alignToRight, @Nonnull TooltipGroup group, @Nonnull HintHint intInfo) { LightweightHint hint; final JComponent editorComponent = editor.getComponent(); TextRange range = myDocumentFragment.getTextRange(); int startOffset = range.getStartOffset(); int endOffset = range.getEndOffset(); Document doc = myDocumentFragment.getDocument(); int endLine = doc.getLineNumber(endOffset); int startLine = doc.getLineNumber(startOffset); JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane(); // There is a possible case that collapsed folding region is soft wrapped, hence, we need to anchor // not logical but visual line start. VisualPosition visual = editor.offsetToVisualPosition(startOffset); p = editor.visualPositionToXY(visual); p = SwingUtilities.convertPoint( ((EditorEx)editor).getGutterComponentEx(), p, layeredPane ); p.x -= 3; p.y += editor.getLineHeight(); Point screenPoint = new Point(p); SwingUtilities.convertPointToScreen(screenPoint, layeredPane); int maxLineCount = (ScreenUtil.getScreenRectangle(screenPoint).height - screenPoint.y) / editor.getLineHeight(); if (endLine - startLine > maxLineCount) { endOffset = doc.getLineEndOffset(Math.max(0, Math.min(startLine + maxLineCount, doc.getLineCount() - 1))); } if (endOffset < startOffset) return null; FoldingModelEx foldingModel = (FoldingModelEx)editor.getFoldingModel(); foldingModel.setFoldingEnabled(false); TextRange textRange = new TextRange(startOffset, endOffset); hint = EditorFragmentComponent.showEditorFragmentHintAt(editor, textRange, p.y, false, false, true, true, true); foldingModel.setFoldingEnabled(true); return hint; }
private List<FoldRegion> addNewRegions(@Nonnull EditorFoldingInfo info, @Nonnull FoldingModelEx foldingModel, @Nonnull Map<TextRange, Boolean> rangeToExpandStatusMap, @Nonnull Map<FoldRegion, Boolean> shouldExpand, @Nonnull Map<FoldingGroup, Boolean> groupExpand) { List<FoldRegion> newRegions = new ArrayList<>(); SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject); for (FoldingUpdate.RegionInfo regionInfo : myRegionInfos) { ProgressManager.checkCanceled(); FoldingDescriptor descriptor = regionInfo.descriptor; FoldingGroup group = descriptor.getGroup(); TextRange range = descriptor.getRange(); String placeholder = null; try { placeholder = descriptor.getPlaceholderText(); } catch (IndexNotReadyException ignore) { } if (range.getEndOffset() > myEditor.getDocument().getTextLength()) { LOG.error(String.format("Invalid folding descriptor detected (%s). It ends beyond the document range (%d)", descriptor, myEditor.getDocument().getTextLength())); continue; } FoldRegion region = foldingModel.createFoldRegion(range.getStartOffset(), range.getEndOffset(), placeholder == null ? "..." : placeholder, group, descriptor.isNonExpandable()); if (region == null) continue; if (descriptor.isNonExpandable()) region.putUserData(FoldingModelImpl.SELECT_REGION_ON_CARET_NEARBY, Boolean.TRUE); PsiElement psi = descriptor.getElement().getPsi(); if (psi == null || !psi.isValid() || !myFile.isValid()) { region.dispose(); continue; } if (descriptor.canBeRemovedWhenCollapsed()) region.putUserData(CAN_BE_REMOVED_WHEN_COLLAPSED, Boolean.TRUE); region.putUserData(COLLAPSED_BY_DEFAULT, regionInfo.collapsedByDefault); region.putUserData(SIGNATURE, regionInfo.signature); info.addRegion(region, smartPointerManager.createSmartPsiElementPointer(psi)); newRegions.add(region); boolean expandStatus = !descriptor.isNonExpandable() && shouldExpandNewRegion(range, rangeToExpandStatusMap, regionInfo.collapsedByDefault); 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; }
private boolean regionCanBeRemovedWhenCollapsed(FoldRegion region) { return Boolean.TRUE.equals(region.getUserData(CAN_BE_REMOVED_WHEN_COLLAPSED)) || ((FoldingModelEx)myEditor.getFoldingModel()).hasDocumentRegionChangedFor(region) || !region.isValid() || isRegionInCaretLine(region); }
FoldingModelWindow(@Nonnull FoldingModelEx delegate, @Nonnull DocumentWindow documentWindow, @Nonnull EditorWindow editorWindow) { myDelegate = delegate; myDocumentWindow = documentWindow; myEditorWindow = editorWindow; }