/******************************************************************************* * Copyright (c) 2009 IBM Corporation 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: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.ui.editor.configuration; import java.util.*; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.text.*; import org.eclipse.jface.text.presentation.IPresentationRepairer; import org.eclipse.php.internal.core.documentModel.partitioner.PHPPartitionTypes; import org.eclipse.php.internal.core.format.FormatterUtils; import org.eclipse.swt.custom.StyleRange; import org.eclipse.wst.sse.core.internal.text.rules.SimpleStructuredTypedRegion; import org.eclipse.wst.sse.ui.internal.provisional.style.StructuredPresentationReconciler; public class PHPStructuredPresentationReconciler extends StructuredPresentationReconciler { private Map<String, IPresentationRepairer> fRepairers; private static final Set<String> fTypeSet = new HashSet<>(); static { fTypeSet.add(FormatterUtils.PARTITION_CSS_STYLE); fTypeSet.add(FormatterUtils.PARTITION_JS_SCRIPT); } ITypedRegion getWholeRegion(ITypedRegion[] wholePartitions, ITypedRegion originalRegion) { if (fTypeSet.contains(originalRegion.getType())) { int i = 0; int jumpto = -1; while (i < wholePartitions.length) { ITypedRegion r = wholePartitions[i]; if (wholePartitions[i].getType().equals(originalRegion.getType())) { jumpto = getFollowingCSS(wholePartitions, i, originalRegion.getType()); r = new SimpleStructuredTypedRegion(r.getOffset(), wholePartitions[jumpto].getOffset() + wholePartitions[jumpto].getLength() - r.getOffset(), originalRegion.getType()); if (originalRegion.getOffset() >= r.getOffset() && originalRegion.getOffset() + originalRegion.getLength() <= r.getOffset() + r.getLength()) { return r; } else { i = jumpto + 2; } } else { i++; } } } return null; } @Override protected TextPresentation createPresentation(IRegion damage, IDocument document) { try { int validLength = Math.min(damage.getLength(), document.getLength() - damage.getOffset()); if (fRepairers == null || fRepairers.isEmpty()) { TextPresentation presentation = new TextPresentation(damage, 1); presentation.setDefaultStyleRange(new StyleRange(damage.getOffset(), validLength, null, null)); return presentation; } // TextPresentation presentation = new TextPresentation(damage, // 1000); ITypedRegion[] partitions = TextUtilities.computePartitioning(document, getDocumentPartitioning(), damage.getOffset(), validLength, false); if (containSpecialType(partitions)) { // when modify editor content the damage region is not equal to // document's region,so we need to adjust the damage region's // start and length if (partitions != null && partitions.length > 0) { int start = damage.getOffset(); int length = validLength; ITypedRegion[] wholePartitions = TextUtilities.computePartitioning(document, getDocumentPartitioning(), 0, document.getLength(), false); // determine start ITypedRegion startTypedRegion = partitions[0]; ITypedRegion endTypedRegion = partitions[partitions.length - 1]; if (PHPPartitionTypes.PHP_DEFAULT.equals(startTypedRegion.getType()) && partitions.length >= 2) { startTypedRegion = partitions[1]; } ITypedRegion newRegion = getWholeRegion(wholePartitions, startTypedRegion); if (newRegion != null) { start = newRegion.getOffset(); length = endTypedRegion.getOffset() + endTypedRegion.getLength() - start; } if (PHPPartitionTypes.PHP_DEFAULT.equals(endTypedRegion.getType()) && partitions.length >= 2) { endTypedRegion = partitions[partitions.length - 2]; } newRegion = getWholeRegion(wholePartitions, endTypedRegion); if (newRegion != null) { length = newRegion.getOffset() + newRegion.getLength() - start; } if (start != damage.getOffset() || length != validLength) { partitions = TextUtilities.computePartitioning(document, getDocumentPartitioning(), start, length, false); } } if (partitions == null) { return null; } List<StyleRange> fRangeSet = new LinkedList<>(); int jumpto = -1; for (int i = 0; i < partitions.length; i++) { ITypedRegion r = partitions[i]; if (fTypeSet.contains(r.getType()) && (i + 2 < partitions.length) && PHPPartitionTypes.PHP_DEFAULT.equals(partitions[i + 1].getType()) && r.getType().equals(partitions[i + 2].getType())) { if (i > jumpto) { jumpto = getFollowingCSS(partitions, i, r.getType()); r = new SimpleStructuredTypedRegion(r.getOffset(), partitions[jumpto].getOffset() + partitions[jumpto].getLength() - r.getOffset(), r.getType()); IPresentationRepairer repairer = getRepairer(r.getType()); if (repairer != null) { TextPresentation presentation = new TextPresentation(damage, 1000); repairer.createPresentation(presentation, r); for (Iterator<StyleRange> iterator = presentation.getAllStyleRangeIterator(); iterator .hasNext();) { StyleRange styleRange = (StyleRange) iterator.next(); // the styleRange's scope may be out of // the // region see // https://bugs.eclipse.org/bugs/attachment.cgi?id=179715 if (styleRange.start < r.getOffset() || (styleRange.start + styleRange.length > r.getOffset() + r.getLength())) { continue; } for (int j = i + 1; j < jumpto; j = j + 2) { ITypedRegion typedRegion = partitions[j]; if (styleRange.start < typedRegion.getOffset() && styleRange.start + styleRange.length > typedRegion.getOffset() + typedRegion.getLength()) { int end = styleRange.start + styleRange.length; styleRange.length = typedRegion.getOffset() - styleRange.start; fRangeSet.add(styleRange); styleRange = new StyleRange( typedRegion.getOffset() + typedRegion.getLength(), end - (typedRegion.getOffset() + typedRegion.getLength()), styleRange.foreground, styleRange.background, styleRange.fontStyle); } else if (styleRange.start < typedRegion.getOffset() && styleRange.start + styleRange.length > typedRegion.getOffset()) { styleRange.length = typedRegion.getOffset() - styleRange.start; break; } else if (styleRange.start >= typedRegion.getOffset() && styleRange.start + styleRange.length <= typedRegion.getOffset() + typedRegion.getLength()) { styleRange = null; break; } else if (styleRange.start > typedRegion.getOffset() && styleRange.start < typedRegion.getOffset() + typedRegion.getLength() && styleRange.start + styleRange.length > typedRegion.getOffset() + typedRegion.getLength()) { styleRange.length = styleRange.start + styleRange.length - (typedRegion.getOffset() + typedRegion.getLength()); styleRange.start = typedRegion.getOffset() + typedRegion.getLength(); } else if (styleRange.start + styleRange.length < typedRegion.getOffset()) { break; } } if (styleRange != null) { fRangeSet.add(styleRange); } } } } } else { if (i > jumpto || i < jumpto && !r.getType().equals(partitions[jumpto].getType())) {// jumpto // partition // has // been // added IPresentationRepairer repairer = getRepairer(r.getType()); if (repairer != null) { TextPresentation presentation = new TextPresentation(damage, 1000); repairer.createPresentation(presentation, r); for (Iterator<StyleRange> iterator = presentation.getAllStyleRangeIterator(); iterator .hasNext();) { StyleRange styleRange = iterator.next(); fRangeSet.add(styleRange); } } } } } if (fRangeSet.isEmpty()) { return null; } Collections.sort(fRangeSet, new Comparator<StyleRange>() { @Override public int compare(StyleRange o1, StyleRange o2) { return o1.start - o2.start; } }); List<StyleRange> fRanges = new ArrayList<>(); StyleRange[] rangeArray = fRangeSet.toArray(new StyleRange[fRangeSet.size()]); StyleRange lastRange = rangeArray[0]; fRanges.add(lastRange); for (int i = 1; i < rangeArray.length; i++) { StyleRange styleRange = rangeArray[i]; // do not add duplicate ranges if (styleRange.start == lastRange.start && styleRange.length == lastRange.length) { continue; } else { fRanges.add(styleRange); lastRange = styleRange; } } TextPresentation presentation = new TextPresentation(damage, 1000); presentation = new TextPresentation(damage, fRanges.size()); for (Iterator<StyleRange> iterator = fRanges.iterator(); iterator.hasNext();) { StyleRange styleRange = (StyleRange) iterator.next(); if (styleRange.start + styleRange.length <= damage.getOffset()) { continue; } else if (styleRange.start <= damage.getOffset() && styleRange.start + styleRange.length > damage.getOffset() && styleRange.start + styleRange.length <= damage.getOffset() + validLength) { int rangeEnd = styleRange.start + styleRange.length; styleRange.start = damage.getOffset(); styleRange.length = rangeEnd - damage.getOffset(); addStyleRange(presentation, styleRange); } else if (styleRange.start >= damage.getOffset() && styleRange.start < damage.getOffset() + validLength && styleRange.start + styleRange.length > damage.getOffset() + validLength) { styleRange.length = damage.getOffset() + validLength - styleRange.start; addStyleRange(presentation, styleRange); } else if (styleRange.start >= damage.getOffset() && styleRange.start + styleRange.length <= damage.getOffset() + validLength) { addStyleRange(presentation, styleRange); } } return presentation; } else { TextPresentation presentation = new TextPresentation(damage, 1000); List<ITypedRegion> damagedPartitions = findDamagedPartitions(partitions, damage); // performance optimisation: if only PHPScriptRegion has been // damaged and not fully reparsed we can try to create // presentation for updated tokens only. if (damagedPartitions.size() == 1) { ITypedRegion r = damagedPartitions.get(0); IPresentationRepairer repairer = getRepairer(damagedPartitions.get(0).getType()); if (repairer != null) { if (r.getType().equals(PHPPartitionTypes.PHP_DEFAULT)) { if (repairer instanceof StructuredDocumentDamagerRepairer) { TextPresentation newPresentation = ((StructuredDocumentDamagerRepairer) repairer) .getPresentation(r, damage); if (newPresentation != null) { presentation = newPresentation; } } } repairer.createPresentation(presentation, r); } } else { for (int i = 0; i < damagedPartitions.size(); i++) { ITypedRegion r = damagedPartitions.get(i); IPresentationRepairer repairer = getRepairer(r.getType()); if (repairer != null) repairer.createPresentation(presentation, r); } } // OLD CODE // for (int i = 0; i < partitions.length; i++) { // ITypedRegion r = partitions[i]; // IPresentationRepairer repairer = getRepairer(r.getType()); // if (repairer != null) // repairer.createPresentation(presentation, r); // } return presentation; } } catch (BadLocationException x) { /* ignored in platform PresentationReconciler, too */ } return null; } private List<ITypedRegion> findDamagedPartitions(ITypedRegion[] partitions, IRegion damage) { List<ITypedRegion> damagedPartitions = new ArrayList<>(); int damageEnd = damage.getOffset() + damage.getLength(); for (int i = 0; i < partitions.length; i++) { ITypedRegion p = partitions[i]; int pEnd = p.getOffset() + p.getLength(); // damage starts within current partition if (damage.getOffset() >= p.getOffset() && damage.getOffset() <= pEnd) { damagedPartitions.add(p); if (damageEnd < pEnd) { // damage covers only this one partition break; } } // damages starts in previous partition and ends here else if (damage.getOffset() < p.getOffset() && damageEnd <= pEnd) { damagedPartitions.add(p); break; } // damage starts in previous partition and ends in next one else if (damage.getOffset() < p.getOffset() && damageEnd > pEnd) { damagedPartitions.add(p); } } return damagedPartitions; } private boolean containSpecialType(ITypedRegion[] partitions) { for (int i = 0; i < partitions.length; i++) { ITypedRegion r = partitions[i]; if (fTypeSet.contains(r.getType())) { return true; } } return false; } private void addStyleRange(TextPresentation presentation, StyleRange styleRange) { if (styleRange.length > 0) { presentation.addStyleRange(styleRange); } } // protected TextPresentation createPresentation(IRegion damage, // IDocument document) { // try { // int validLength = Math.min(damage.getLength(), document.getLength() // - damage.getOffset()); // // if (fRepairers == null || fRepairers.isEmpty()) { // TextPresentation presentation = new TextPresentation(damage, 1); // presentation.setDefaultStyleRange(new StyleRange(damage // .getOffset(), validLength, null, null)); // return presentation; // } // // TextPresentation presentation = new TextPresentation(damage, 1000); // // ITypedRegion[] partitions = TextUtilities.computePartitioning( // document, getDocumentPartitioning(), damage.getOffset(), // validLength, false); // for (int i = 0; i < partitions.length; i++) { // ITypedRegion r = partitions[i]; // IPresentationRepairer repairer = getRepairer(r.getType()); // if (repairer != null) // repairer.createPresentation(presentation, r); // } // // return presentation; // // } catch (BadLocationException x) { // /* ignored in platform PresentationReconciler, too */ // } // // return null; // } private int getFollowingCSS(ITypedRegion[] partitions, int i, String type) { int result = i; i++; for (; i < partitions.length; i = i + 2) { if (i + 1 < partitions.length && partitions[i].getType().equals(PHPPartitionTypes.PHP_DEFAULT) && partitions[i + 1].getType().equals(type)) { result = result + 2; } else { break; } } return result; } @Override public void setRepairer(IPresentationRepairer repairer, String contentType) { super.setRepairer(repairer, contentType); Assert.isNotNull(contentType); if (fRepairers == null) fRepairers = new HashMap<>(); if (repairer == null) fRepairers.remove(contentType); else fRepairers.put(contentType, repairer); } }