Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
Copyright (c) 2009 itemis AG (http://www.itemis.eu) 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 /
  
  package org.eclipse.xtext.ui.editor.syntaxcoloring;
  
 import java.util.List;
 
 import  org.eclipse.jface.text.BadLocationException;
 import  org.eclipse.jface.text.BadPositionCategoryException;
 import  org.eclipse.jface.text.DocumentEvent;
 import  org.eclipse.jface.text.IDocument;
 import  org.eclipse.jface.text.IDocumentListener;
 import  org.eclipse.jface.text.IPositionUpdater;
 import  org.eclipse.jface.text.IRegion;
 import  org.eclipse.jface.text.ISynchronizable;
 import  org.eclipse.jface.text.ITextInputListener;
 import  org.eclipse.jface.text.ITextPresentationListener;
 import  org.eclipse.jface.text.Position;
 import  org.eclipse.jface.text.Region;
 import  org.eclipse.jface.text.TextAttribute;
 import  org.eclipse.jface.text.TextPresentation;
 import  org.eclipse.jface.text.source.SourceViewer;
 import  org.eclipse.swt.custom.StyleRange;
Highlighting presenter - UI thread implementation. Initially copied from org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightingPresenter

Author(s):
Sebastian Zarnekow
 
 public class HighlightingPresenter implements ITextPresentationListener, ITextInputListener, IDocumentListener {
 
 	private static final Logger log = Logger.getLogger(HighlightingPresenter.class);

Highlighting position updater.
 
 	private class HighlightingPositionUpdater implements IPositionUpdater {

The position category.
 
 		private final String fCategory;

Creates a new updater for the given category.

Parameters:
category the new category.
 
 		public HighlightingPositionUpdater(String category) {
 			 = category;
 		}
 
 		/*
 		 * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
 		 */
 		public void update(DocumentEvent event) {
 
 			int eventOffset = event.getOffset();
 			int eventOldLength = event.getLength();
 			int eventEnd = eventOffset + eventOldLength;
 
 			try {
 				Position[] positions = event.getDocument().getPositions();
 
 				for (int i = 0; i != positions.length; i++) {
 
 					AttributedPosition position = (AttributedPositionpositions[i];
 
 					// Also update deleted positions because they get deleted by the background thread and removed/invalidated only in the UI runnable
 					//					if (position.isDeleted())
 					//						continue;
 
 					int offset = position.getOffset();
 					int length = position.getLength();
 					int end = offset + length;
 
 					if (offset > eventEnd)
 						updateWithPrecedingEvent(positionevent);
 					else if (end < eventOffset)
 						updateWithSucceedingEvent(positionevent);
 					else if (offset <= eventOffset && end >= eventEnd)
 						updateWithIncludedEvent(positionevent);
 					else if (offset <= eventOffset)
 						updateWithOverEndEvent(positionevent);
 					else if (end >= eventEnd)
 						updateWithOverStartEvent(positionevent);
 					else
 						updateWithIncludingEvent(positionevent);
 				}
catch (BadPositionCategoryException e) {
				// ignore and return
			}
		}

Update the given position with the given event. The event precedes the position.

Parameters:
position The position
event The event
		private void updateWithPrecedingEvent(AttributedPosition position, DocumentEvent event) {
			String newText = event.getText();
			int eventNewLength = newText != null ? newText.length() : 0;
			int deltaLength = eventNewLength - event.getLength();
			position.setOffset(position.getOffset() + deltaLength);
		}

Update the given position with the given event. The event succeeds the position.

Parameters:
position The position
event The event
		private void updateWithSucceedingEvent(AttributedPosition position, DocumentEvent event) {
		}

Update the given position with the given event. The event is included by the position.

Parameters:
position The position
event The event
		private void updateWithIncludedEvent(AttributedPosition position, DocumentEvent event) {
			int eventOffset = event.getOffset();
			String newText = event.getText();
			if (newText == null)
				newText = ""//$NON-NLS-1$
			int eventNewLength = newText.length();
			int deltaLength = eventNewLength - event.getLength();
			int offset = position.getOffset();
			int length = position.getLength();
			int end = offset + length;
			int includedLength = 0;
			while (includedLength < eventNewLength && Character.isJavaIdentifierPart(newText.charAt(includedLength)))
				includedLength++;
			if (includedLength == eventNewLength)
				position.setLength(length + deltaLength);
			else {
				int newLeftLength = eventOffset - offset + includedLength;
				int excludedLength = eventNewLength;
				while (excludedLength > 0 && Character.isJavaIdentifierPart(newText.charAt(excludedLength - 1)))
					excludedLength--;
				int newRightOffset = eventOffset + excludedLength;
				int newRightLength = end + deltaLength - newRightOffset;
				if (newRightLength == 0) {
					position.setLength(newLeftLength);
else {
					if (newLeftLength == 0) {
						position.update(newRightOffsetnewRightLength);
else {
						position.setLength(newLeftLength);
						addPositionFromUI(newRightOffsetnewRightLengthposition.getHighlighting());
					}
				}
			}
		}

Update the given position with the given event. The event overlaps with the end of the position.

Parameters:
position The position
event The event
		private void updateWithOverEndEvent(AttributedPosition position, DocumentEvent event) {
			String newText = event.getText();
			if (newText == null)
				newText = ""//$NON-NLS-1$
			int eventNewLength = newText.length();
			int includedLength = 0;
			while (includedLength < eventNewLength && Character.isJavaIdentifierPart(newText.charAt(includedLength)))
				includedLength++;
			position.setLength(event.getOffset() - position.getOffset() + includedLength);
		}

Update the given position with the given event. The event overlaps with the start of the position.

Parameters:
position The position
event The event
		private void updateWithOverStartEvent(AttributedPosition position, DocumentEvent event) {
			int eventOffset = event.getOffset();
			int eventEnd = eventOffset + event.getLength();
			String newText = event.getText();
			if (newText == null)
				newText = ""//$NON-NLS-1$
			int eventNewLength = newText.length();
			int excludedLength = eventNewLength;
			while (excludedLength > 0 && Character.isJavaIdentifierPart(newText.charAt(excludedLength - 1)))
				excludedLength--;
			int deleted = eventEnd - position.getOffset();
			int inserted = eventNewLength - excludedLength;
			position.update(eventOffset + excludedLengthposition.getLength() - deleted + inserted);
		}

Update the given position with the given event. The event includes the position.

Parameters:
position The position
event The event
		private void updateWithIncludingEvent(AttributedPosition position, DocumentEvent event) {
			position.delete();
			position.update(event.getOffset(), 0);
		}
	}

Position updater
	private final IPositionUpdater fPositionUpdater = new HighlightingPositionUpdater(getPositionCategory());

The source viewer this highlighting reconciler is installed on
The background presentation reconciler
UI's current highlighted positions - can contain null elements
UI position lock
	private final Object fPositionLock = new Object();

true if the current reconcile is canceled.
	private boolean fIsCanceled = false;

Creates and returns a new highlighted position with the given offset, length and highlighting.

NOTE: Also called from background thread.

Parameters:
offset The offset
length The length
highlighting The highlighting
Returns:
The new highlighted position
	public AttributedPosition createHighlightedPosition(int offsetint length, TextAttribute highlighting) {
		// TODO: reuse deleted positions
		return new AttributedPosition(offsetlengthhighlighting);
	}

Adds all current positions to the given list.

NOTE: Called from background thread.

Parameters:
list The list
	public void addAllPositions(List<AttributedPositionlist) {
		synchronized () {
		}
	}

Create a text presentation in the background.

NOTE: Called from background thread.

Parameters:
addedPositions the added positions
removedPositions the removed positions
Returns:
the text presentation or null, if reconciliation should be canceled
	public TextPresentation createPresentation(List<AttributedPositionaddedPositions,
			List<AttributedPositionremovedPositions) {
		SourceViewer sourceViewer = ;
		if (sourceViewer == null || presentationReconciler == null)
			return null;
		if (isCanceled())
			return null;
		IDocument document = sourceViewer.getDocument();
		if (document == null)
			return null;
		int minStart = .;
		int maxEnd = .;
		for (int i = 0, n = removedPositions.size(); i < ni++) {
			Position position = removedPositions.get(i);
			int offset = position.getOffset();
			minStart = Math.min(minStartoffset);
			maxEnd = Math.max(maxEndoffset + position.getLength());
		}
		for (int i = 0, n = addedPositions.size(); i < ni++) {
			Position position = addedPositions.get(i);
			int offset = position.getOffset();
			minStart = Math.min(minStartoffset);
			maxEnd = Math.max(maxEndoffset + position.getLength());
		}
		if (minStart < maxEnd)
			try {
				return presentationReconciler
						.createRepairDescription(new Region(minStartmaxEnd - minStart), document);
catch (RuntimeException e) {
			}
		return null;
	}

Create a runnable for updating the presentation.

NOTE: Called from background thread.

Parameters:
textPresentation the text presentation
addedPositions the added positions
removedPositions the removed positions
Returns:
the runnable or null, if reconciliation should be canceled
	public Runnable createUpdateRunnable(final TextPresentation textPresentation,
			List<AttributedPositionaddedPositionsList<AttributedPositionremovedPositions) {
		if ( == null || textPresentation == null)
			return null;
		// TODO: do clustering of positions and post multiple fast runnables
		final AttributedPosition[] added = new AttributedPosition[addedPositions.size()];
		addedPositions.toArray(added);
		final AttributedPosition[] removed = new AttributedPosition[removedPositions.size()];
		removedPositions.toArray(removed);
		if (isCanceled())
			return null;
		Runnable runnable = new Runnable() {
			public void run() {
				updatePresentation(textPresentationaddedremoved);
			}
		};
		return runnable;
	}
		return new Runnable() {
			public void run() {
				if ( != null)
			}
		};
	}

Invalidate the presentation of the positions based on the given added positions and the existing deleted positions. Also unregisters the deleted positions from the document and patches the positions of this presenter.

NOTE: Indirectly called from background thread by UI runnable.

Parameters:
textPresentation the text presentation or null, if the presentation should computed in the UI thread
addedPositions the added positions
removedPositions the removed positions
	public void updatePresentation(TextPresentation textPresentationAttributedPosition[] addedPositions,
			AttributedPosition[] removedPositions) {
		if ( == null)
			return;
		//		checkOrdering("added positions: ", Arrays.asList(addedPositions)); //$NON-NLS-1$
		//		checkOrdering("removed positions: ", Arrays.asList(removedPositions)); //$NON-NLS-1$
		//		checkOrdering("old positions: ", fPositions); //$NON-NLS-1$
		// TODO: double-check consistency with document.getPositions(...)
		// TODO: reuse removed positions
		if (isCanceled())
			return;
		IDocument document = .getDocument();
		if (document == null)
			return;
		String positionCategory = getPositionCategory();
		List<AttributedPositionremovedPositionsList = Arrays.asList(removedPositions);
		try {
			synchronized () {
				int newSize = Math.max(.size() + addedPositions.length - removedPositions.length, 10);
				/*
				 * The following loop is a kind of merge sort: it merges two List<Position>, each
				 * sorted by position.offset, into one new list. The first of the two is the
				 * previous list of positions (oldPositions), from which any deleted positions get
				 * removed on the fly. The second of two is the list of added positions. The result
				 * is stored in newPositions.
				 */
				List<AttributedPositionnewPositions = new ArrayList<AttributedPosition>(newSize);
				AttributedPosition position = null;
				AttributedPosition addedPosition = null;
				for (int i = 0, j = 0, n = oldPositions.size(), m = addedPositions.lengthi < n || position != null
						|| j < m || addedPosition != null;) {
					// loop variant: i + j < old(i + j)
					// a) find the next non-deleted Position from the old list
					while (position == null && i < n) {
						position = oldPositions.get(i++);
						if (position.isDeleted() || contain(removedPositionsListposition)) {
							document.removePosition(positionCategoryposition);
							position = null;
						}
					}
					// b) find the next Position from the added list
					if (addedPosition == null && j < m) {
						addedPosition = addedPositions[j++];
						document.addPosition(positionCategoryaddedPosition);
					}
					// c) merge: add the next of position/addedPosition with the lower offset
					if (position != null) {
						if (addedPosition != null)
							if (position.getOffset() <= addedPosition.getOffset()) {
								newPositions.add(position);
								position = null;
else {
								newPositions.add(addedPosition);
								addedPosition = null;
							}
						else {
							newPositions.add(position);
							position = null;
						}
else if (addedPosition != null) {
						newPositions.add(addedPosition);
						addedPosition = null;
					}
				}
				 = newPositions;
			}
catch (BadPositionCategoryException e) {
			// Should not happen
			.debug(e.getMessage(), e);
catch (BadLocationException e) {
			// Should not happen
			.debug(e.getMessage(), e);
		}
		//		checkOrdering("new positions: ", fPositions); //$NON-NLS-1$
		if (textPresentation != null)
			.changeTextPresentation(textPresentationfalse);
		else
	}
	//	private void checkOrdering(String s, List positions) {
	//		Position previous= null;
	//		for (int i= 0, n= positions.size(); i < n; i++) {
	//			Position current= (Position) positions.get(i);
	//			if (previous != null && previous.getOffset() + previous.getLength() > current.getOffset())
	//				return;
	//		}
	//	}
Returns true iff the positions contain the position.

Parameters:
positions the positions, must be ordered by offset but may overlap
position the position
Returns:
true iff the positions contain the position
	private boolean contain(List<AttributedPositionpositionsAttributedPosition position) {
		return indexOf(positionsposition) != -1;
	}

Returns index of the position in the positions, -1 if not found.

Parameters:
positions the positions, must be ordered by offset but may overlap
position the position
Returns:
the index
	private int indexOf(List<AttributedPositionpositionsAttributedPosition position) {
		int index = computeIndexAtOffset(positionsposition.getOffset());
		int size = positions.size();
		while (index < size) {
			if (positions.get(index) == position)
				return index;
			index++;
		}
		return -1;
	}

Insert the given position in fPositions, s.t. the offsets remain in linear order.

Parameters:
position The position for insertion
	private void insertPosition(AttributedPosition position) {
		.add(iposition);
	}

Returns the index of the first position with an offset greater than the given offset.

Parameters:
positions the positions, must be ordered by offset and must not overlap
offset the offset
Returns:
the index of the last position with an offset greater than the given offset
Since:
2.5
	protected int computeIndexAfterOffset(List<AttributedPositionpositionsint offset) {
		int i = -1;
		int j = positions.size();
		while (j - i > 1) {
			int k = (i + j) >> 1;
			AttributedPosition position = positions.get(k);
			if (position.getOffset() > offset)
				j = k;
			else
				i = k;
		}
		return j;
	}

Returns the index of the first position with an offset equal or greater than the given offset.

Parameters:
positions the positions, must be ordered by offset and must not overlap
offset the offset
Returns:
the index of the last position with an offset equal or greater than the given offset
Since:
2.5
	protected int computeIndexEndingAtOrEnclosingOffset(List<AttributedPositionpositionsint offset) {
		int i = -1;
		int j = positions.size();
		while (j - i > 1) {
			int k = (i + j) >> 1;
			AttributedPosition position = positions.get(k);
			int positionOffset = position.getOffset();
			if (positionOffset <= offset && positionOffset + position.getLength() > offset) {
				return k;
			}
			if (position.getOffset() >= offset)
				j = k;
			else
				i = k;
		}
		return j;
	}

Returns the index of the first position with an offset equal or greater than the given offset.

Parameters:
positions the positions, must be ordered by offset and must not overlap
offset the offset
Returns:
the index of the last position with an offset equal or greater than the given offset
Since:
2.5
	protected int computeIndexAtOffset(List<AttributedPositionpositionsint offset) {
		int i = -1;
		int j = positions.size();
		while (j - i > 1) {
			int k = (i + j) >> 1;
			AttributedPosition position = positions.get(k);
			if (position.getOffset() >= offset)
				j = k;
			else
				i = k;
		}
		return j;
	}
	/*
	 * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation)
	 */
	public void applyTextPresentation(TextPresentation textPresentation) {
		IRegion region = textPresentation.getExtent();
		int i = computeIndexEndingAtOrEnclosingOffset(region.getOffset());
		int n = computeIndexAtOffset(region.getOffset() + region.getLength());
		if (n - i > 2) {
			List<StyleRange> ranges = new ArrayList<StyleRange>(n - i);
			for (; i < ni++) {
				if (!position.isDeleted())
					ranges.add(position.createStyleRange());
			}
			StyleRange[] array = new StyleRange[ranges.size()];
			array = ranges.toArray(array);
			textPresentation.replaceStyleRanges(array);
else {
			for (; i < ni++) {
				if (!position.isDeleted())
					textPresentation.replaceStyleRange(position.createStyleRange());
			}
		}
	}
	/*
	 * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
	 */
	public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
		releaseDocument(oldInput);
	}
	/*
	 * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
	 */
	public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
		manageDocument(newInput);
	}
	/*
	 * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
	 */
	public void documentAboutToBeChanged(DocumentEvent event) {
	}
	/*
	 * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
	 */
	public void documentChanged(DocumentEvent event) {
	}

Returns:
Returns true iff the current reconcile is canceled.

NOTE: Also called from background thread.

	public boolean isCanceled() {
		IDocument document =  != null ? .getDocument() : null;
		if (document == null)
			return ;
		synchronized (getLockObject(document)) {
			return fIsCanceled;
		}
	}

Set whether or not the current reconcile is canceled.

Parameters:
isCanceled true iff the current reconcile is canceled
	public void setCanceled(boolean isCanceled) {
		IDocument document =  != null ? .getDocument() : null;
		if (document == null) {
			 = isCanceled;
			return;
		}
		synchronized (getLockObject(document)) {
			fIsCanceled = isCanceled;
		}
	}

Parameters:
document the document
Returns:
the document's lock object
	private Object getLockObject(IDocument document) {
		if (document instanceof ISynchronizable) {
			Object lock = ((ISynchronizable) document).getLockObject();
			if (lock != null)
				return lock;
		}
		return document;
	}

Install this presenter on the given source viewer and background presentation reconciler.

Parameters:
sourceViewer the source viewer
backgroundPresentationReconciler the background presentation reconciler, can be null, in that case HighlightingPresenter.createPresentation(List, List) should not be called
	public void install(XtextSourceViewer sourceViewerXtextPresentationReconciler backgroundPresentationReconciler) {
		 = sourceViewer;
		 = backgroundPresentationReconciler;
	}

Uninstall this presenter.
	public void uninstall() {
		if ( != null) {
			 = null;
		}
	}

Invalidate text presentation of positions with the given highlighting.

Parameters:
highlighting The highlighting
	public void highlightingStyleChanged(TextAttribute highlighting) {
		for (int i = 0, n = .size(); i < ni++) {
			if (position.getHighlighting() == highlighting)
				.invalidateTextPresentation(position.getOffset(), position.getLength());
		}
	}

Invalidate text presentation of all positions.
	private void invalidateTextPresentation() {
		for (int i = 0, n = .size(); i < ni++) {
			.invalidateTextPresentation(position.getOffset(), position.getLength());
		}
	}

Add a position with the given range and highlighting unconditionally, only from UI thread. The position will also be registered on the document. The text presentation is not invalidated.

Parameters:
offset The range offset
length The range length
highlighting
	private void addPositionFromUI(int offsetint length, TextAttribute highlighting) {
		AttributedPosition position = createHighlightedPosition(offsetlengthhighlighting);
		synchronized () {
			insertPosition(position);
		}
		IDocument document = .getDocument();
		if (document == null)
			return;
		String positionCategory = getPositionCategory();
		try {
			document.addPosition(positionCategoryposition);
catch (BadLocationException e) {
			// Should not happen
			.debug(e.getMessage(), e);
catch (BadPositionCategoryException e) {
			// Should not happen
			.debug(e.getMessage(), e);
		}
	}

Reset to initial state.
	private void resetState() {
		synchronized () {
		}
	}

Start managing the given document.

Parameters:
document The document
	private void manageDocument(IDocument document) {
		if (document != null) {
			document.addPositionCategory(getPositionCategory());
			document.addPositionUpdater();
			document.addDocumentListener(this);
		}
	}

Stop managing the given document.

Parameters:
document The document
	private void releaseDocument(IDocument document) {
		if (document != null) {
			document.removeDocumentListener(this);
			document.removePositionUpdater();
			try {
				document.removePositionCategory(getPositionCategory());
catch (BadPositionCategoryException e) {
				// Should not happen
				.debug(e.getMessage(), e);
			}
		}
	}

Returns:
The reconciler position's category.
		return toString();
	}
New to GrepCode? Check out our FAQ X