/******************************************************************************* * Copyright (c) 2010, 2017 xored software, Inc. and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * xored software, Inc. - initial API and Implementation (Alex Panchenko) *******************************************************************************/ package org.eclipse.dltk.ui.text.folding; import java.util.ArrayList; import java.util.List; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.ui.PreferenceConstants; import org.eclipse.dltk.ui.text.IPartitioningProvider; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentPartitioner; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.rules.FastPartitioner; /** * Abstract implementation of {@link IFoldingBlockProvider} to fold * comments/documentation based on document partitioning. * * Extend it and in the body of computeFoldableBlocks() make a few calls to * {@link #computeBlocksForPartitionType(IFoldingContent, String, IFoldingBlockKind, boolean)} * * @since 2.0 */ public abstract class PartitioningFoldingBlockProvider implements IFoldingBlockProvider { private final IPartitioningProvider partitioningProvider; public PartitioningFoldingBlockProvider( IPartitioningProvider partitioningProvider) { this.partitioningProvider = partitioningProvider; } private int fBlockLinesMin; private boolean fDocsFolding; private boolean fCommentsFolding; private boolean fFoldNewLines; private boolean fInitCollapseComments; private boolean fInitCollapseDocs; private boolean fInitCollapseHeaderComments; @Override public void initializePreferences(IPreferenceStore preferenceStore) { fBlockLinesMin = preferenceStore .getInt(PreferenceConstants.EDITOR_FOLDING_LINES_LIMIT); fDocsFolding = preferenceStore .getBoolean(PreferenceConstants.EDITOR_DOCS_FOLDING_ENABLED); fCommentsFolding = preferenceStore.getBoolean( PreferenceConstants.EDITOR_COMMENTS_FOLDING_ENABLED); fFoldNewLines = preferenceStore.getBoolean( PreferenceConstants.EDITOR_COMMENT_FOLDING_JOIN_NEWLINES); fInitCollapseComments = preferenceStore .getBoolean(PreferenceConstants.EDITOR_FOLDING_INIT_COMMENTS); fInitCollapseHeaderComments = preferenceStore.getBoolean( PreferenceConstants.EDITOR_FOLDING_INIT_HEADER_COMMENTS); fInitCollapseDocs = preferenceStore .getBoolean(PreferenceConstants.EDITOR_FOLDING_INIT_DOCS); } protected boolean isFoldingDocs() { return fDocsFolding; } protected void setFoldingDocs(boolean value) { this.fDocsFolding = value; } protected boolean isFoldingComments() { return fCommentsFolding; } protected void setFoldingComments(boolean value) { this.fCommentsFolding = value; } protected boolean isJoinCommentsSeparatedByEmptyLines() { return fFoldNewLines; } protected void setJoinCommentsSeparatedByEmptyLines(boolean value) { this.fFoldNewLines = value; } protected boolean isCollapseComments() { return fInitCollapseComments; } protected boolean isCollapseHeaderComment() { return fInitCollapseHeaderComments; } protected boolean isCollapseDocs() { return fInitCollapseDocs; } protected void setCollapseComments(boolean value) { this.fInitCollapseComments = value; } protected void setCollapseHeaderComment(boolean value) { this.fInitCollapseHeaderComments = value; } protected void setCollapseDocs(boolean value) { this.fInitCollapseDocs = value; } @Override public int getMinimalLineCount() { return fBlockLinesMin; } protected void setMinimalLineCount(int value) { this.fBlockLinesMin = value; } protected IFoldingBlockRequestor requestor; @Override public void setRequestor(IFoldingBlockRequestor requestor) { this.requestor = requestor; } private List<ITypedRegion> computePartitioning(Document d) { // TODO TextUtilities.computePartitioning() ? List<ITypedRegion> docRegionList = new ArrayList<>(); int offset = 0; for (;;) { try { ITypedRegion region = TextUtilities.getPartition(d, partitioningProvider.getPartitioning(), offset, true); docRegionList.add(region); offset = region.getLength() + region.getOffset() + 1; } catch (BadLocationException e1) { break; } } return docRegionList; } protected void computeBlocksForPartitionType(IFoldingContent content, String partition, IFoldingBlockKind kind, boolean collapse) { try { final String contents = content.getSourceContents(); if (contents == null || contents.length() == 0) { return; } Document document = new Document(contents); installDocumentStuff(document); ITypedRegion start = null; ITypedRegion lastRegion = null; List<IRegion> regions = new ArrayList<>(); for (ITypedRegion region : computePartitioning(document)) { if (region.getType().equals(partition) && startsAtLineBegin(document, region)) { if (start == null) start = region; } else if (start != null && (isBlankRegion(document, region) || isEmptyRegion(document, region) && isJoinCommentsSeparatedByEmptyLines())) { // blanks or empty lines // TODO introduce line limit for collapseEmptyLines() ? } else { if (start != null) { assert lastRegion != null; int offset0 = start.getOffset(); int length0 = lastRegion.getOffset() + lastRegion.getLength() - offset0 - 1; length0 = contents.substring(offset0, offset0 + length0) .trim().length(); regions.add(new Region(offset0, length0)); } start = null; } lastRegion = region; } if (start != null) { assert lastRegion != null; int offset0 = start.getOffset(); int length0 = lastRegion.getOffset() - offset0 + lastRegion.getLength() - 1; regions.add(new Region(offset0, length0)); } reportRegions(document, regions, kind, collapse); removeDocumentStuff(document); } catch (BadLocationException e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } } /** * @param document * @param regions * @param kind * @param collapse * @throws BadLocationException */ protected void reportRegions(Document document, List<IRegion> regions, IFoldingBlockKind kind, boolean collapse) throws BadLocationException { for (IRegion region : regions) { // TODO Object element = null; requestor.acceptBlock(region.getOffset(), region.getOffset() + region.getLength(), kind, element, collapse); } } /** * Tests if the specified region contains only space or tab characters. * * @param document * @param region * @return * @throws BadLocationException */ protected boolean isBlankRegion(IDocument document, ITypedRegion region) throws BadLocationException { String value = document.get(region.getOffset(), region.getLength()); for (int i = 0; i < value.length(); ++i) { char ch = value.charAt(i); if (ch != ' ' && ch != '\t') { return false; } } return true; } private boolean startsAtLineBegin(Document d, ITypedRegion region) throws BadLocationException { int lineStart = d.getLineOffset(d.getLineOfOffset(region.getOffset())); if (lineStart != region.getOffset()) { if (!isEmptyRegion(d, lineStart, region.getOffset() - lineStart)) { return false; } } return true; } protected boolean isEmptyRegion(IDocument d, ITypedRegion r) throws BadLocationException { return isEmptyRegion(d, r.getOffset(), r.getLength()); } protected boolean isEmptyRegion(IDocument d, int offset, int length) throws BadLocationException { return d.get(offset, length).trim().length() == 0; } /** * Installs a partitioner with <code>document</code>. * * @param document * the document */ private void installDocumentStuff(Document document) { final IDocumentPartitioner partitioner = new FastPartitioner( partitioningProvider.createPartitionScanner(), partitioningProvider.getPartitionContentTypes()); partitioner.connect(document); document.setDocumentPartitioner(partitioningProvider.getPartitioning(), partitioner); } /** * Removes partitioner with <code>document</code>. * * @param document * the document */ private void removeDocumentStuff(Document document) { final String partitioning = partitioningProvider.getPartitioning(); final IDocumentPartitioner partitioner = document .getDocumentPartitioner(partitioning); if (partitioner != null) { document.setDocumentPartitioner(partitioning, null); partitioner.disconnect(); } } }