diff options
author | amywu | 2006-06-14 05:08:57 +0000 |
---|---|---|
committer | amywu | 2006-06-14 05:08:57 +0000 |
commit | 91c25668b070581fe23302535f43218de5ae94d8 (patch) | |
tree | 4dd7c6788a3402343860338d664875aa0b7af102 /bundles/org.eclipse.wst.dtd.ui/src/org/eclipse/wst/dtd/ui/internal | |
parent | 17320ce8c78b0ce4ed422893bd04c66834404c4d (diff) | |
download | webtools.sourceediting-91c25668b070581fe23302535f43218de5ae94d8.tar.gz webtools.sourceediting-91c25668b070581fe23302535f43218de5ae94d8.tar.xz webtools.sourceediting-91c25668b070581fe23302535f43218de5ae94d8.zip |
[73271] Folding for XML/HTML family languages editors
Diffstat (limited to 'bundles/org.eclipse.wst.dtd.ui/src/org/eclipse/wst/dtd/ui/internal')
-rw-r--r-- | bundles/org.eclipse.wst.dtd.ui/src/org/eclipse/wst/dtd/ui/internal/projection/StructuredTextFoldingProviderDTD.java | 403 |
1 files changed, 311 insertions, 92 deletions
diff --git a/bundles/org.eclipse.wst.dtd.ui/src/org/eclipse/wst/dtd/ui/internal/projection/StructuredTextFoldingProviderDTD.java b/bundles/org.eclipse.wst.dtd.ui/src/org/eclipse/wst/dtd/ui/internal/projection/StructuredTextFoldingProviderDTD.java index 0f171596ca..b66b3117d6 100644 --- a/bundles/org.eclipse.wst.dtd.ui/src/org/eclipse/wst/dtd/ui/internal/projection/StructuredTextFoldingProviderDTD.java +++ b/bundles/org.eclipse.wst.dtd.ui/src/org/eclipse/wst/dtd/ui/internal/projection/StructuredTextFoldingProviderDTD.java @@ -11,22 +11,32 @@ *******************************************************************************/ package org.eclipse.wst.dtd.ui.internal.projection; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.Platform; -import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.text.ITextInputListener; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.projection.IProjectionListener; import org.eclipse.jface.text.source.projection.ProjectionAnnotation; +import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; import org.eclipse.jface.text.source.projection.ProjectionViewer; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; import org.eclipse.wst.dtd.core.internal.DTDFile; import org.eclipse.wst.dtd.core.internal.DTDNode; import org.eclipse.wst.dtd.core.internal.TopLevelNode; import org.eclipse.wst.dtd.core.internal.document.DTDModelImpl; import org.eclipse.wst.dtd.core.internal.event.IDTDFileListener; import org.eclipse.wst.dtd.core.internal.event.NodesEvent; +import org.eclipse.wst.dtd.ui.internal.Logger; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; @@ -36,10 +46,11 @@ import org.w3c.dom.Node; /** * Updates the projection model of a structured model for DTD. */ -public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingProvider, IProjectionListener, IDTDFileListener { +public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingProvider, IProjectionListener, IDTDFileListener, ITextInputListener { private final static boolean debugProjectionPerf = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.dtd.ui/projectionperf")); //$NON-NLS-1$ //$NON-NLS-2$ private class TagProjectionAnnotation extends ProjectionAnnotation { + private boolean fIsVisible = false; /* workaround for BUG85874 */ private Node fNode; public TagProjectionAnnotation(Node node, boolean isCollapsed) { @@ -54,11 +65,171 @@ public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingP public void setNode(Node node) { fNode = node; } + + /** + * Does not paint hidden annotations. Annotations are hidden when they + * only span one line. + * + * @see ProjectionAnnotation#paint(org.eclipse.swt.graphics.GC, + * org.eclipse.swt.widgets.Canvas, + * org.eclipse.swt.graphics.Rectangle) + */ + public void paint(GC gc, Canvas canvas, Rectangle rectangle) { + /* workaround for BUG85874 */ + /* + * only need to check annotations that are expanded because hidden + * annotations should never have been given the chance to + * collapse. + */ + if (!isCollapsed()) { + // working with rectangle, so line height + FontMetrics metrics = gc.getFontMetrics(); + if (metrics != null) { + // do not draw annotations that only span one line and + // mark them as not visible + if ((rectangle.height / metrics.getHeight()) <= 1) { + fIsVisible = false; + return; + } + } + } + fIsVisible = true; + super.paint(gc, canvas, rectangle); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.source.projection.ProjectionAnnotation#markCollapsed() + */ + public void markCollapsed() { + /* workaround for BUG85874 */ + // do not mark collapsed if annotation is not visible + if (fIsVisible) + super.markCollapsed(); + } + } + + /** + * Listens to document to be aware of when to update the projection + * annotation model. + */ + class DocumentListener implements IDocumentListener { + public void documentAboutToBeChanged(DocumentEvent event) { + if (fDocument == event.getDocument()) + fIsDocumentChanging = true; + } + + public void documentChanged(DocumentEvent event) { + // register a post notification replace so that projection + // annotation model will be updated after all documentChanged + // listeners have been notified + IDocument document = event.getDocument(); + if (document instanceof IDocumentExtension && fDocument == document) { + if (fViewer != null && fQueuedNodeChanges != null && !fQueuedNodeChanges.isEmpty()) { + ((IDocumentExtension) document).registerPostNotificationReplace(this, new PostDocumentChangedListener()); + } + } + } + } + + /** + * Essentially a post document changed listener because it is called after + * documentchanged has been fired. + */ + class PostDocumentChangedListener implements IDocumentExtension.IReplace { + public void perform(IDocument document, IDocumentListener owner) { + applyAnnotationModelChanges(); + fIsDocumentChanging = false; + } + } + + /** + * Contains node and an indication on how it changed + */ + class NodeChange { + static final int ADD = 1; + static final int REMOVE = 2; + + private Node fNode; + private int fChangeType; + + public NodeChange(Node node, int changeType) { + fNode = node; + fChangeType = changeType; + } + + public Node getNode() { + return fNode; + } + + public int getChangeType() { + return fChangeType; + } } - private IDocument fDocument; - private ProjectionViewer fViewer; + IDocument fDocument; + ProjectionViewer fViewer; + private boolean fProjectionNeedsToBeEnabled = false; + /** + * Listener to fProjectionViewer's document + */ + private IDocumentListener fDocumentListener; + /** + * Indicates whether or not document is in the middle of changing + */ + boolean fIsDocumentChanging = false; + /** + * List of changed nodes that need to be recalculated + */ + List fQueuedNodeChanges = null; + /** + * Processes all the queued node changes and updates projection annotation + * model. + */ + void applyAnnotationModelChanges() { + if (fViewer != null && fQueuedNodeChanges != null && !fQueuedNodeChanges.isEmpty()) { + ProjectionAnnotationModel annotationModel = fViewer.getProjectionAnnotationModel(); + + // go through all the pending annotation changes and apply them to + // the projection annotation model + while (!fQueuedNodeChanges.isEmpty()) { + NodeChange changes = (NodeChange) fQueuedNodeChanges.remove(0); + if (changes.getChangeType() == NodeChange.ADD) { + // add + Node node = changes.getNode(); + Position newPos = createProjectionPosition(node); + if (newPos != null) { + TagProjectionAnnotation newAnnotation = new TagProjectionAnnotation(node, false); + // add to map containing annotations to add + try { + annotationModel.addAnnotation(newAnnotation, newPos); + } + catch (Exception e) { + // if anything goes wrong, log it and continue + Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e); + } + } + } + else if (changes.getChangeType() == NodeChange.REMOVE) { + // remove + Node node = changes.getNode(); + TagProjectionAnnotation anno = findExistingAnnotation(node); + if (anno != null) { + try { + annotationModel.removeAnnotation(anno); + } + catch (Exception e) { + // if anything goes wrong, log it and continue + Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e); + } + } + } + } + fQueuedNodeChanges = null; + } + } /** * Goes through every node creates projection annotation if needed @@ -81,9 +252,10 @@ public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingP } } - long end = System.currentTimeMillis(); - if (debugProjectionPerf) + if (debugProjectionPerf) { + long end = System.currentTimeMillis(); System.out.println("StructuredTextFoldingProviderDTD.addAllAnnotations: " + (end - start)); //$NON-NLS-1$ + } } /** @@ -102,20 +274,7 @@ public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingP int start = inode.getStartOffset(); int end = inode.getEndOffset(); if (start >= 0 && start < end) { - try { - int startLine = fDocument.getLineOfOffset(start); - int endLine = fDocument.getLineOfOffset(end); - // checks if projection start/end region is on the - // same line - if ((startLine < endLine) && (endLine + 1 < fDocument.getNumberOfLines())) { - int offset = fDocument.getLineOffset(startLine); - int endOffset = fDocument.getLineOffset(endLine + 1); - pos = new Position(offset, endOffset - offset); - } - } - catch (BadLocationException x) { - // Logger.log(Logger.WARNING_DEBUG, null, x); - } + pos = new Position(start, end - start); } } } @@ -146,22 +305,23 @@ public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingP /** * Get the dtd file for the fDocument * - * @param document - * @return + * @return DTDFile if it exists, null otherwise */ private DTDFile getDTDFile() { DTDFile dtdFile = null; - IStructuredModel sModel = null; - try { - sModel = StructuredModelManager.getModelManager().getExistingModelForRead(fDocument); - if (sModel instanceof DTDModelImpl) { - dtdFile = ((DTDModelImpl) sModel).getDTDFile(); + if (fDocument != null) { + IStructuredModel sModel = null; + try { + sModel = StructuredModelManager.getModelManager().getExistingModelForRead(fDocument); + if (sModel instanceof DTDModelImpl) { + dtdFile = ((DTDModelImpl) sModel).getDTDFile(); + } } - } - finally { - if (sModel != null) { - sModel.releaseFromRead(); + finally { + if (sModel != null) { + sModel.releaseFromRead(); + } } } return dtdFile; @@ -175,24 +335,24 @@ public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingP if (!isInstalled()) return; - // remove dtd file listener from old dtd file + // remove old info + projectionDisabled(); + + fDocument = fViewer.getDocument(); DTDFile file = getDTDFile(); - if (file != null) { - file.removeDTDFileListener(this); - } - // clear out all annotations - if (fViewer.getProjectionAnnotationModel() != null) - fViewer.getProjectionAnnotationModel().removeAllAnnotations(); + if (fDocument != null && file != null && fViewer.getProjectionAnnotationModel() != null) { + if (fDocumentListener == null) + fDocumentListener = new DocumentListener(); + + fDocument.addDocumentListener(fDocumentListener); - fDocument = fViewer.getDocument(); - file = getDTDFile(); - if (file != null) { // add dtd file listener to new dtd file file.addDTDFileListener(this); addAllAnnotations(file); } + fProjectionNeedsToBeEnabled = false; } /** @@ -207,6 +367,7 @@ public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingP } fViewer = viewer; fViewer.addProjectionListener(this); + fViewer.addTextInputListener(this); } private boolean isInstalled() { @@ -228,79 +389,103 @@ public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingP } public void nodeChanged(DTDNode node) { + /* + * Don't believe this is necessary (used to need it to only add + * projection annotations to elements that span more than one line, + * but now just always add projection annotation) + */ + // long start = System.currentTimeMillis(); + // // recalculate projection annotations for node + // // check if this was even a projectable node to start with + // if (isNodeProjectable(node)) { + // // find the existing annotation + // TagProjectionAnnotation anno = findExistingAnnotation(node); + // // if able to project node see if projection annotation was + // // already created and create new if needed + // Position newPos = createProjectionPosition(node); + // if (newPos != null && anno == null) { + // TagProjectionAnnotation newAnnotation = new + // TagProjectionAnnotation(node, false); + // // add to map containing annotations to add + // fViewer.getProjectionAnnotationModel().addAnnotation(newAnnotation, + // newPos); + // } + // // if not able to project node see if projection annotation was + // // already created and remove it + // if (newPos == null && anno != null) { + // fViewer.getProjectionAnnotationModel().removeAnnotation(anno); + // } + // } + // + // long end = System.currentTimeMillis(); + // if (debugProjectionPerf) { + // String nodeName = node != null ? node.getNodeName() : "null"; + // //$NON-NLS-1$ + // System.out.println("StructuredTextFoldingProviderDTD.nodeChanged (" + // + nodeName + "):" + (end - start)); //$NON-NLS-1$ //$NON-NLS-2$ + // } + } + + public void nodesAdded(NodesEvent event) { long start = System.currentTimeMillis(); - // recalculate projection annotations for node - // check if this was even a projectable node to start with - if (isNodeProjectable(node)) { - // find the existing annotation - TagProjectionAnnotation anno = findExistingAnnotation(node); - // if able to project node see if projection annotation was - // already created and create new if needed - Position newPos = createProjectionPosition(node); - if (newPos != null && anno == null) { - TagProjectionAnnotation newAnnotation = new TagProjectionAnnotation(node, false); - // add to map containing annotations to add - fViewer.getProjectionAnnotationModel().addAnnotation(newAnnotation, newPos); - } - // if not able to project node see if projection annotation was - // already created and remove it - if (newPos == null && anno != null) { - fViewer.getProjectionAnnotationModel().removeAnnotation(anno); - } - } + processNodesEvent(event, NodeChange.ADD); - long end = System.currentTimeMillis(); if (debugProjectionPerf) { - String nodeName = node != null ? node.getNodeName() : "null"; //$NON-NLS-1$ - System.out.println("StructuredTextFoldingProviderDTD.nodeChanged (" + nodeName + "):" + (end - start)); //$NON-NLS-1$ //$NON-NLS-2$ + long end = System.currentTimeMillis(); + System.out.println("StructuredTextFoldingProviderDTD.nodesAdded: " + (end - start)); //$NON-NLS-1$ } } - public void nodesAdded(NodesEvent event) { - long start = System.currentTimeMillis(); - - // add projection annotations for all nodes in event.getNodes() + /** + * Goes through every changed node in event and add to queue of node + * changes that will be recalculated for projection annotation model. + * + * @param event + * @param changeType + */ + private void processNodesEvent(NodesEvent event, int changeType) { List nodes = event.getNodes(); Iterator it = nodes.iterator(); while (it.hasNext()) { DTDNode node = (DTDNode) it.next(); if (isNodeProjectable(node)) { - // add - Position newPos = createProjectionPosition(node); - if (newPos != null) { - TagProjectionAnnotation newAnnotation = new TagProjectionAnnotation(node, false); - // add to map containing annotations to add - fViewer.getProjectionAnnotationModel().addAnnotation(newAnnotation, newPos); + if (fQueuedNodeChanges == null) { + fQueuedNodeChanges = new ArrayList(); + } + + int existingIndex = fQueuedNodeChanges.indexOf(node); + if (existingIndex > -1) { + // node is already queued up + NodeChange existingChange = (NodeChange) fQueuedNodeChanges.remove(existingIndex); + // don't add if added then removed node or vice versa + if (existingChange.getChangeType() == changeType) { + NodeChange newChange = new NodeChange(node, changeType); + fQueuedNodeChanges.add(newChange); + } + } + else { + // not queued up yet, so queue node + NodeChange newChange = new NodeChange(node, changeType); + fQueuedNodeChanges.add(newChange); } } } - - long end = System.currentTimeMillis(); - if (debugProjectionPerf) - System.out.println("StructuredTextFoldingProviderDTD.nodesAdded: " + (end - start)); //$NON-NLS-1$ + // if document isn't changing, go ahead and apply it + if (!fIsDocumentChanging) { + applyAnnotationModelChanges(); + } } public void nodesRemoved(NodesEvent event) { long start = System.currentTimeMillis(); - // remove projection annotations for all nodes in event.getNodes() - List nodes = event.getNodes(); - Iterator it = nodes.iterator(); - while (it.hasNext()) { - DTDNode node = (DTDNode) it.next(); - // check if removed node was projectable in the first place - if (isNodeProjectable(node)) { - // remove - TagProjectionAnnotation anno = findExistingAnnotation(node); - if (anno != null) - fViewer.getProjectionAnnotationModel().removeAnnotation(anno); - } - } + processNodesEvent(event, NodeChange.REMOVE); - long end = System.currentTimeMillis(); - if (debugProjectionPerf) + if (debugProjectionPerf) { + long end = System.currentTimeMillis(); System.out.println("StructuredTextFoldingProviderDTD.nodesRemoved: " + (end - start)); //$NON-NLS-1$ + } } public void projectionDisabled() { @@ -309,13 +494,46 @@ public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingP file.removeDTDFileListener(this); } + // remove document listener + if (fDocumentListener != null && fDocument != null) { + fDocument.removeDocumentListener(fDocumentListener); + fDocument = null; + + // clear out list of queued changes since it may no longer be + // accurate + if (fQueuedNodeChanges != null) { + fQueuedNodeChanges.clear(); + fQueuedNodeChanges = null; + } + } + fDocument = null; + fProjectionNeedsToBeEnabled = false; } public void projectionEnabled() { initialize(); } + public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { + // if folding is enabled and new document is going to be a totally + // different document, disable projection + if (fDocument != null && fDocument != newInput) { + // disable projection and disconnect everything + projectionDisabled(); + fProjectionNeedsToBeEnabled = true; + } + } + + public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { + // if projection was previously enabled before input document changed + // and new document is different than old document + if (fProjectionNeedsToBeEnabled && fDocument == null && newInput != null) { + projectionEnabled(); + fProjectionNeedsToBeEnabled = false; + } + } + /** * Disconnect this IStructuredTextFoldingProvider from projection viewer */ @@ -324,6 +542,7 @@ public class StructuredTextFoldingProviderDTD implements IStructuredTextFoldingP projectionDisabled(); fViewer.removeProjectionListener(this); + fViewer.addTextInputListener(this); fViewer = null; } } |