// Copyright (c) 2004-2008 by Leif Frenzel - see http://leiffrenzel.de // This code is made available under the terms of the Eclipse Public License, // version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html package net.sf.eclipsefp.haskell.ui.internal.editors.haskell.text; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import net.sf.eclipsefp.haskell.buildwrapper.types.Location; import net.sf.eclipsefp.haskell.buildwrapper.types.OutlineDef; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.HaskellEditor; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.projection.ProjectionAnnotation; import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; import org.eclipse.ui.texteditor.IDocumentProvider; /** <p>provides folding regions for documents in the Haskell editor.</p> * * @author Leif Frenzel */ public class HaskellFoldingStructureProvider { private final HaskellEditor editor; public HaskellFoldingStructureProvider( final HaskellEditor editor ) { this.editor = editor; } public void updateFoldingRegions(final List<OutlineDef> outlineDefs) { ProjectionAnnotationModel model = getAnnModel(); // And this is how we really get the editor's document without waiting for the // source viewer. IDocumentProvider docProvider = editor.getDocumentProvider(); // may be null if we're late and editor has closed if (docProvider!=null){ IDocument document = docProvider.getDocument( editor.getEditorInput() ); if (model!=null){ Set<Location> blocks=new HashSet<>(); for (OutlineDef def : outlineDefs){ // only blocks that are more than one line long can be folded if (def.getLocation()!=null && def.getLocation().getEndLine()>def.getLocation().getStartLine()){ blocks.add( def.getLocation() ); if (def.getCommentStartLine()!=null && def.getCommentStartLine()<def.getLocation().getStartLine()-1){ try { IRegion r1=document.getLineInformation( def.getCommentStartLine()-1 ); int end=def.getLocation().getStartOffset( document ); String del=document.getLineDelimiter( def.getLocation().getStartLine()-1 ); r1=new Region(r1.getOffset(),end-r1.getOffset()-del.length()); blocks.add( new Location( editor.getTitle(), document, r1 ) ); } catch (BadLocationException ignore){ //noop } } } } Set<Position> regions=new HashSet<>(); for (Location l:blocks){ Position p=createPosition( document, l.getStartLine(), l.getEndLine() ); if (p!=null){ regions.add( p ); } } updateFoldingRegions( model, regions ); } } } private Position createPosition( final IDocument document, final int startLine, final int endLine ) { Position result = null; try { int start = document.getLineOffset( startLine -1 ); int endLine2=Math.min( document.getNumberOfLines()-1, endLine -1 ); int end = document.getLineOffset( endLine2) + document.getLineLength( endLine2); result = new Position( start, end - start ); } catch( final BadLocationException badlox ) { // ignored System.err.println(startLine+","+endLine); //$NON-NLS-1$ badlox.printStackTrace(); } return result; } private ProjectionAnnotationModel getAnnModel() { Class<ProjectionAnnotationModel> cls = ProjectionAnnotationModel.class; return ( ProjectionAnnotationModel )editor.getAdapter( cls ); } private void updateFoldingRegions( final ProjectionAnnotationModel model, final Set<Position> currentRegions ) { Annotation[] deletions = computeDifferences( model, currentRegions ); Map<ProjectionAnnotation, Position> additionsMap = new HashMap<>(); Iterator<Position> it = currentRegions.iterator(); while( it.hasNext() ) { additionsMap.put( new ProjectionAnnotation(), it.next() ); } if( deletions.length != 0 || additionsMap.size() != 0 ) { model.modifyAnnotations( deletions, additionsMap, new Annotation[] {} ); } } private Annotation[] computeDifferences( final ProjectionAnnotationModel mdl, final Set<Position> current ) { List<ProjectionAnnotation> deletions= new ArrayList<>(); Iterator<?> iter = mdl.getAnnotationIterator(); while( iter.hasNext() ) { Object annotation = iter.next(); if( annotation instanceof ProjectionAnnotation ) { Position position = mdl.getPosition( ( Annotation )annotation ); if( current.contains( position ) ) { current.remove( position ); } else { deletions.add( (ProjectionAnnotation) annotation ); } } } return deletions.toArray( new Annotation[ deletions.size() ] ); } }