/******************************************************************************* * Copyright (c) 2017 Red Hat Inc. and others * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * Lucas Bullen (Red Hat Inc.) - initial implementation *******************************************************************************/ package org.eclipse.ui.genericeditor.examples.dotproject; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; 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.reconciler.DirtyRegion; import org.eclipse.jface.text.reconciler.IReconcilingStrategy; import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.projection.ProjectionAnnotation; import org.eclipse.jface.text.source.projection.ProjectionViewer; public class FoldingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension { private IDocument document; private String oldDocument; private ProjectionViewer projectionViewer; private List oldAnnotations = new ArrayList<>(); private List oldPositions = new ArrayList<>(); @Override public void setDocument(IDocument document) { this.document = document; } public void setProjectionViewer(ProjectionViewer projectionViewer) { this.projectionViewer = projectionViewer; } @Override public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { initialReconcile(); } @Override public void reconcile(IRegion partition) { initialReconcile(); } @Override public void initialReconcile() { if(document.get().equals(oldDocument)) return; oldDocument = document.get(); List positions = getNewPositionsOfAnnotations(); List positionsToRemove = new ArrayList<>(); List annotationToRemove = new ArrayList<>(); for (Position position : oldPositions) { if(!positions.contains(position)) { projectionViewer.getProjectionAnnotationModel().removeAnnotation(oldAnnotations.get(oldPositions.indexOf(position))); positionsToRemove.add(position); annotationToRemove.add(oldAnnotations.get(oldPositions.indexOf(position))); }else { positions.remove(position); } } oldPositions.removeAll(positionsToRemove); oldAnnotations.removeAll(annotationToRemove); for (Position position : positions) { Annotation annotation = new ProjectionAnnotation(); projectionViewer.getProjectionAnnotationModel().addAnnotation(annotation, position); oldPositions.add(position); oldAnnotations.add(annotation); } } private static enum SearchingFor { START_OF_TAG, START_OF_WORD, END_OF_WORD, END_OF_LINE } private List getNewPositionsOfAnnotations(){ List positions = new ArrayList<>(); Map startOfAnnotation = new HashMap<>(); SearchingFor searchingFor = SearchingFor.START_OF_TAG; int characters = document.getLength(); int currentCharIndex = 0; int wordStartIndex = 0; int sectionStartIndex = 0; String word = ""; try { while (currentCharIndex < characters) { char currentChar = document.getChar(currentCharIndex); switch (searchingFor) { case START_OF_TAG: if(currentChar == '<') { char nextChar = document.getChar(currentCharIndex+1); if(nextChar != '?') { sectionStartIndex = currentCharIndex; searchingFor = SearchingFor.START_OF_WORD; } } break; case START_OF_WORD: if(Character.isLetter(currentChar)) { wordStartIndex = currentCharIndex; searchingFor = SearchingFor.END_OF_WORD; } break; case END_OF_WORD: if(!Character.isLetter(currentChar)) { word = document.get(wordStartIndex, currentCharIndex - wordStartIndex); if(startOfAnnotation.containsKey(word)) { searchingFor = SearchingFor.END_OF_LINE; }else { startOfAnnotation.put(word, sectionStartIndex); searchingFor = SearchingFor.START_OF_TAG; } } break; case END_OF_LINE: if(currentChar == '\n') { int start = startOfAnnotation.get(word); if(document.getLineOfOffset(start) != document.getLineOfOffset(currentCharIndex)) { positions.add(new Position(start,currentCharIndex + 1 - start)); } startOfAnnotation.remove(word); searchingFor = SearchingFor.START_OF_TAG; } break; } currentCharIndex++; } } catch (BadLocationException e) { // skip the remainder of file due to error } return positions; } @Override public void setProgressMonitor(IProgressMonitor monitor) { // no progress monitor used } }