Skip to main content
aboutsummaryrefslogblamecommitdiffstats
blob: fdfffe1c2b3adbe500c43a800955ed7fd2b68ca4 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                                                                
                                                       



                                                                        
  


                                                                                 

                               
 
                           
                      
                                                 
 


















                                                           
                           

                                                     



                                                

                                         



                                                 


                                                 

                                             

                                       
                                     
                                      
                                         

                                       

                                        
                                                              
                                                                
                                            

   
                                                             


                                   
  
             
   
                                                                                                                                                                



                                                       
 
                                     
 
                                  
 
                                  
 
                                      
 
                                    
 
                                                               
 

                                                                 

                                                                              
                                                                           
                 
 
                         
                                                                  




                                                                         
                         
                                                      




                                                                                       



                                                                                                       


                                         

                 
 


                                                                             

                                                                                          
                                                                        
                                                


                                                
 




                                                                     
                                                                                  






                                                                          

                                                                                                             






                                                                          
                                                                                                 
                                                                  

                                                                               



                             







                                                                     

                                                                           
                                                         



















                                                                                                                                        



















                                                                                                                           















                                                                         





                                                                                              
                                                                                  

                                                                            






                                                                                          



















                                                                                                                               
                                                                                    
                                                            






                                                                                                     

                                                                                                 
                                              













                                                                                                                                 




                                                                     


                                 
 









                                                                                                                  




















































                                                                                                            
                                                        

                                                                   
                                                                                   































                                                                                                                                                                                     
 
                                                                             

















































                                                                                                                                                 

                                    
 




                                                 
 
                                      
 
                                                 
 


                                                              



                                                        

                                                                                                



                                                       



                                                   
 
                                                                































                                                                                                                           
                                                                  

































































                                                                                                       
                                              








                                                                                                      
                                             











                                                                                                       
                                              







                                                                                                     
                                             

                                                                 






















































                                                                                                        
                                                    







                                                                                                 
                                             







                                                                                               
                                           
         
 




                                                                         

                                                            

















                                                                                        
                                                   













                                                                                       
                                        





























                                                                                
                                                                       



                                                                                                                                                                  
 
         
 



                                                                 
                                                               




                                               
 
                 
                                                                


                                                                       
 


                                                   

                                                                















































                                                                                                                                                                 





                                                                                             
 
                                                                                                    
 

                                                                                 
 

                 
 
 
 
/*******************************************************************************
 * Copyright (c) 2000, 2017 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
 *******************************************************************************/

package org.eclipse.ui.console;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentAdapter;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.JFaceTextUtil;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.LineBackgroundEvent;
import org.eclipse.swt.custom.LineBackgroundListener;
import org.eclipse.swt.custom.LineStyleEvent;
import org.eclipse.swt.custom.LineStyleListener;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.internal.console.ConsoleDocumentAdapter;
import org.eclipse.ui.internal.console.ConsoleHyperlinkPosition;
import org.eclipse.ui.progress.WorkbenchJob;

/**
 * Default viewer used to display a <code>TextConsole</code>.
 * <p>
 * Clients may subclass this class.
 * </p>
 *
 * @since 3.1
 */
public class TextConsoleViewer extends SourceViewer implements LineStyleListener, LineBackgroundListener, MouseTrackListener, MouseMoveListener, MouseListener {
	/**
	 * Adapts document to the text widget.
	 */
	private ConsoleDocumentAdapter documentAdapter;

	private IHyperlink hyperlink;

	private Cursor handCursor;

	private Cursor textCursor;

	private int consoleWidth = -1;

	private TextConsole console;

	private IPropertyChangeListener propertyChangeListener;

	private IScrollLockStateProvider scrollLockStateProvider;

	private IDocumentListener documentListener = new IDocumentListener() {
		@Override
		public void documentAboutToBeChanged(DocumentEvent event) {
		}

		@Override
		public void documentChanged(DocumentEvent event) {
			updateLinks(event.fOffset);
		}
	};
	// event listener used to send event to hyperlink for IHyperlink2
	private Listener mouseUpListener = new Listener() {
		@Override
		public void handleEvent(Event event) {
			if (hyperlink != null) {
				String selection = getTextWidget().getSelectionText();
				if (selection.length() <= 0) {
					if (event.button == 1) {
						if (hyperlink instanceof IHyperlink2) {
							((IHyperlink2) hyperlink).linkActivated(event);
						} else {
							hyperlink.linkActivated();
						}
					}
				}
			}
		}
	};

	// to store to user scroll lock action
	private AtomicBoolean userHoldsScrollLock = new AtomicBoolean(false);

	WorkbenchJob revealJob = new WorkbenchJob("Reveal End of Document") {//$NON-NLS-1$
		@Override
		public IStatus runInUIThread(IProgressMonitor monitor) {
			scrollToEndOfDocument();
			return Status.OK_STATUS;
		}
	};

	// reveal the end of the document
	private void scrollToEndOfDocument() {
		StyledText textWidget = getTextWidget();
		if (textWidget != null && !textWidget.isDisposed()) {
			int lineCount = textWidget.getLineCount();
			textWidget.setTopIndex(lineCount > 0 ? lineCount - 1 : 0);
		}

	}

	// set the scroll Lock setting for Console Viewer and Console View
	private void setScrollLock(boolean lock) {
		userHoldsScrollLock.set(lock);
		if (scrollLockStateProvider != null && scrollLockStateProvider.getAutoScrollLock() != lock) {
			scrollLockStateProvider.setAutoScrollLock(lock);
		}
	}

	// set the scroll Lock setting for Console Viewer and Console View
	private boolean checkEndOfDocument() {
		StyledText textWidget = getTextWidget();
		if (textWidget != null && !textWidget.isDisposed()) {
			int partialBottomIndex = JFaceTextUtil.getPartialBottomIndex(textWidget);
			int lineCount = textWidget.getLineCount();
			int delta = textWidget.getVerticalBar().getIncrement();
			return lineCount - partialBottomIndex < delta;
		}
		return false;
	}

	private boolean isEmptyDocument() {
		StyledText textWidget = getTextWidget();
		if (textWidget != null && !textWidget.isDisposed()) {
			return (textWidget.getLineCount() <= 1);
		}
		return false;
	}

	private IPositionUpdater positionUpdater = new IPositionUpdater() {
		@Override
		public void update(DocumentEvent event) {
			try {
				IDocument document = getDocument();
				if (document != null) {
					Position[] positions = document.getPositions(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY);
					for (int i = 0; i < positions.length; i++) {
						Position position = positions[i];
						if (position.offset == event.fOffset && position.length<=event.fLength) {
							position.delete();
						}
						if (position.isDeleted) {
							document.removePosition(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY, position);
						}
					}
				}
			} catch (BadPositionCategoryException e) {
			}
		}
	};

	/**
	 * Constructs a new viewer in the given parent for the specified console.
	 *
	 * @param parent the containing composite
	 * @param console the text console
	 * @param scrollLockStateProvider the scroll lock state provider
	 * @since 3.6
	 */
	public TextConsoleViewer(Composite parent, TextConsole console, IScrollLockStateProvider scrollLockStateProvider) {
		this(parent, console);
		this.scrollLockStateProvider = scrollLockStateProvider;


	}

	/**
	 * Constructs a new viewer in the given parent for the specified console.
	 *
	 * @param parent containing widget
	 * @param console text console
	 */
	public TextConsoleViewer(Composite parent, TextConsole console) {
		super(parent, null, SWT.V_SCROLL | SWT.H_SCROLL);
		this.console = console;

		IDocument document = console.getDocument();
		setDocument(document);

		StyledText styledText = getTextWidget();
		styledText.setDoubleClickEnabled(true);
		styledText.addLineStyleListener(this);
		styledText.addLineBackgroundListener(this);
		styledText.setEditable(true);
		styledText.setBackground(console.getBackground());
		setFont(console.getFont());
		styledText.addMouseTrackListener(this);
		styledText.addListener(SWT.MouseUp, mouseUpListener);
		// event listener used to send event to vertical scroll bar
		styledText.getVerticalBar().addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				// scroll lock if vertical scroll bar dragged, OR selection on
				// vertical bar used
				if (e.detail == SWT.TOP || e.detail == SWT.HOME) {
					// selecting TOP or HOME should lock
					setScrollLock(true);
				}
				if (e.detail == SWT.ARROW_UP || e.detail == SWT.PAGE_UP) {
					if (isEmptyDocument()) {
						setScrollLock(false);
					} else {
						setScrollLock(true);
					}
				} else if (e.detail == SWT.END || e.detail == SWT.BOTTOM) {
					// selecting BOTTOM or END from vertical scroll makes it
					// reveal the end
					setScrollLock(false);
				} else if (e.detail == SWT.DRAG) {
					if (checkEndOfDocument()) {
						setScrollLock(false);
					} else {
						setScrollLock(true);
					}
				} else if ((e.detail == SWT.PAGE_DOWN || e.detail == SWT.ARROW_DOWN) && checkEndOfDocument()) {
					// unlock if Down at the end of document
					setScrollLock(false);
				}
			}
		});
		styledText.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				// lock the scroll if PAGE_UP ,HOME or TOP selected
				if (e.keyCode == SWT.HOME || e.keyCode == SWT.TOP) {
					setScrollLock(true);
				} else if ((e.keyCode == SWT.PAGE_UP || e.keyCode == SWT.ARROW_UP)) {
					if (isEmptyDocument()) {
						setScrollLock(false);
					} else {
						setScrollLock(true);
					}

				} else if (e.keyCode == SWT.END || e.keyCode == SWT.BOTTOM) {
					setScrollLock(false);// selecting END makes it reveal the
					// end
				} else if ((e.keyCode == SWT.PAGE_DOWN || e.keyCode == SWT.ARROW_DOWN) && checkEndOfDocument()) {
					// unlock if Down at the end of document
					setScrollLock(false);
				}
			}
		});
		styledText.addMouseWheelListener(new MouseWheelListener() {
			@Override
			public void mouseScrolled(MouseEvent e) {
				if (e.count < 0) { // Mouse dragged down
					if (checkEndOfDocument()) {
						setScrollLock(false);
					}
				} else if (!userHoldsScrollLock.get()) {
					if (isEmptyDocument()) {
						setScrollLock(false);
					} else {
						setScrollLock(true);
					}
				}
			}
		});

		styledText.addVerifyListener(new VerifyListener() {
			@Override
			public void verifyText(VerifyEvent e) {
				// unlock the auto lock if user starts typing only if it was not manual lock
				if (scrollLockStateProvider != null && !scrollLockStateProvider.getScrollLock()) {
					setScrollLock(false);
				}
			}
		});

		ColorRegistry colorRegistry = JFaceResources.getColorRegistry();
		propertyChangeListener = new HyperlinkColorChangeListener();
		colorRegistry.addListener(propertyChangeListener);

		revealJob.setSystem(true);
		document.addDocumentListener(documentListener);
		document.addPositionUpdater(positionUpdater);
	}

	/**
	 * Sets the tab width used by this viewer.
	 *
	 * @param tabWidth
	 *            the tab width used by this viewer
	 */
	public void setTabWidth(int tabWidth) {
		StyledText styledText = getTextWidget();
		int oldWidth = styledText.getTabs();
		if (tabWidth != oldWidth) {
			styledText.setTabs(tabWidth);
		}
	}

	/**
	 * Sets the font used by this viewer.
	 *
	 * @param font
	 *            the font used by this viewer
	 */
	public void setFont(Font font) {
		StyledText styledText = getTextWidget();
		Font oldFont = styledText.getFont();
		if (oldFont == font) {
			return;
		}
		if (font == null || !(font.equals(oldFont))) {
			styledText.setFont(font);
		}
	}

	/**
	 * Positions the cursor at the end of the document.
	 */
	protected void revealEndOfDocument() {
		revealJob.schedule(50);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.swt.custom.LineStyleListener#lineGetStyle(org.eclipse.swt.custom.LineStyleEvent)
	 */
	@Override
	public void lineGetStyle(LineStyleEvent event) {
		IDocument document = getDocument();
		if (document != null && document.getLength() > 0) {
			ArrayList<StyleRange> ranges = new ArrayList<StyleRange>();
			int offset = event.lineOffset;
			int length = event.lineText.length();

			StyleRange[] partitionerStyles = ((IConsoleDocumentPartitioner) document.getDocumentPartitioner()).getStyleRanges(event.lineOffset, event.lineText.length());
			if (partitionerStyles != null) {
				for (int i = 0; i < partitionerStyles.length; i++) {
					ranges.add(partitionerStyles[i]);
				}
			} else {
				ranges.add(new StyleRange(offset, length, null, null));
			}

			try {
				Position[] positions = getDocument().getPositions(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY);
				Position[] overlap = findPosition(offset, length, positions);
				Color color = JFaceColors.getHyperlinkText(Display.getCurrent());
				if (overlap != null) {
					for (int i = 0; i < overlap.length; i++) {
						Position position = overlap[i];
						StyleRange linkRange = new StyleRange(position.offset, position.length, color, null);
						linkRange.underline = true;
						override(ranges, linkRange);
					}
				}
			} catch (BadPositionCategoryException e) {
			}

			if (ranges.size() > 0) {
				event.styles = ranges.toArray(new StyleRange[ranges.size()]);
			}
		}
	}

	private void override(List<StyleRange> ranges, StyleRange newRange) {
		if (ranges.isEmpty()) {
			ranges.add(newRange);
			return;
		}

		int start = newRange.start;
		int end = start + newRange.length;
		for (int i = 0; i < ranges.size(); i++) {
			StyleRange existingRange = ranges.get(i);
			int rEnd = existingRange.start + existingRange.length;
			if (end <= existingRange.start || start >= rEnd) {
				continue;
			}

			if (start < existingRange.start && end > existingRange.start) {
				start = existingRange.start;
			}

			if (start >= existingRange.start && end <= rEnd) {
				existingRange.length = start - existingRange.start;
				ranges.add(++i, newRange);
				if (end != rEnd) {
					ranges.add(++i, new StyleRange(end, rEnd - end - 1, existingRange.foreground, existingRange.background));
				}
				return;
			} else if (start >= existingRange.start && start < rEnd) {
				existingRange.length = start - existingRange.start;
				ranges.add(++i, newRange);
			} else if (end >= rEnd) {
				ranges.remove(i);
			} else {
				ranges.add(++i, new StyleRange(end + 1, rEnd - end + 1, existingRange.foreground, existingRange.background));
			}
		}
	}

	/**
	 * Binary search for the positions overlapping the given range
	 *
	 * @param offset
	 *            the offset of the range
	 * @param length
	 *            the length of the range
	 * @param positions
	 *            the positions to search
	 * @return the positions overlapping the given range, or <code>null</code>
	 */
	private Position[] findPosition(int offset, int length, Position[] positions) {

		if (positions.length == 0) {
			return null;
		}

		int rangeEnd = offset + length;
		int left = 0;
		int right = positions.length - 1;
		int mid = 0;
		Position position = null;

		while (left < right) {

			mid = (left + right) / 2;

			position = positions[mid];
			if (rangeEnd < position.getOffset()) {
				if (left == mid) {
					right = left;
				} else {
					right = mid - 1;
				}
			} else if (offset > (position.getOffset() + position.getLength() - 1)) {
				if (right == mid) {
					left = right;
				} else {
					left = mid + 1;
				}
			} else {
				left = right = mid;
			}
		}

		List<Position> list = new ArrayList<Position>();
		int index = left - 1;
		if (index >= 0) {
			position = positions[index];
			while (index >= 0 && (position.getOffset() + position.getLength()) > offset) {
				index--;
				if (index > 0) {
					position = positions[index];
				}
			}
		}
		index++;
		position = positions[index];
		while (index < positions.length && (position.getOffset() < rangeEnd)) {
			list.add(position);
			index++;
			if (index < positions.length) {
				position = positions[index];
			}
		}

		if (list.isEmpty()) {
			return null;
		}
		return list.toArray(new Position[list.size()]);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.swt.custom.LineBackgroundListener#lineGetBackground(org.eclipse.swt.custom.LineBackgroundEvent)
	 */
	@Override
	public void lineGetBackground(LineBackgroundEvent event) {
		event.lineBackground = null;
	}

	/**
	 * Returns the hand cursor.
	 *
	 * @return the hand cursor
	 */
	protected Cursor getHandCursor() {
		if (handCursor == null) {
			handCursor = new Cursor(ConsolePlugin.getStandardDisplay(), SWT.CURSOR_HAND);
		}
		return handCursor;
	}

	/**
	 * Returns the text cursor.
	 *
	 * @return the text cursor
	 */
	protected Cursor getTextCursor() {
		if (textCursor == null) {
			textCursor = new Cursor(ConsolePlugin.getStandardDisplay(), SWT.CURSOR_IBEAM);
		}
		return textCursor;
	}

	/**
	 * Notification a hyperlink has been entered.
	 *
	 * @param link
	 *            the link that was entered
	 */
	protected void linkEntered(IHyperlink link) {
		Control control = getTextWidget();
		if (hyperlink != null) {
			linkExited(hyperlink);
		}
		hyperlink = link;
		hyperlink.linkEntered();
		control.setCursor(getHandCursor());
		control.redraw();
		control.addMouseListener(this);
	}

	/**
	 * Notification a link was exited.
	 *
	 * @param link
	 *            the link that was exited
	 */
	protected void linkExited(IHyperlink link) {
		link.linkExited();
		hyperlink = null;
		Control control = getTextWidget();
		control.setCursor(getTextCursor());
		control.redraw();
		control.removeMouseListener(this);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent)
	 */
	@Override
	public void mouseEnter(MouseEvent e) {
		getTextWidget().addMouseMoveListener(this);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent)
	 */
	@Override
	public void mouseExit(MouseEvent e) {
		getTextWidget().removeMouseMoveListener(this);
		if (hyperlink != null) {
			linkExited(hyperlink);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent)
	 */
	@Override
	public void mouseHover(MouseEvent e) {
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
	 */
	@Override
	public void mouseMove(MouseEvent e) {
		Point p = new Point(e.x, e.y);
		int offset = getTextWidget().getOffsetAtPoint(p);
		updateLinks(offset);
	}

	/**
	 * The cursor has just be moved to the given offset, the mouse has hovered
	 * over the given offset. Update link rendering.
	 *
	 * @param offset
	 */
	protected void updateLinks(int offset) {
		if (offset >= 0) {
			IHyperlink link = getHyperlink(offset);
			if (link != null) {
				if (link.equals(hyperlink)) {
					return;
				}
				linkEntered(link);
				return;
			}
		}
		if (hyperlink != null) {
			linkExited(hyperlink);
		}
	}

	/**
	 * Returns the currently active hyperlink or <code>null</code> if none.
	 *
	 * @return the currently active hyperlink or <code>null</code> if none
	 */
	public IHyperlink getHyperlink() {
		return hyperlink;
	}

	/**
	 * Returns the hyperlink at the specified offset, or <code>null</code> if
	 * none.
	 *
	 * @param offset
	 *            offset at which a hyperlink has been requested
	 * @return hyperlink at the specified offset, or <code>null</code> if none
	 */
	public IHyperlink getHyperlink(int offset) {
		if (offset >= 0 && console != null) {
			return console.getHyperlink(offset);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
	 */
	@Override
	public void mouseDoubleClick(MouseEvent e) {
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
	 */
	@Override
	public void mouseDown(MouseEvent e) {
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
	 */
	@Override
	public void mouseUp(MouseEvent e) {
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.jface.text.TextViewer#createDocumentAdapter()
	 */
	@Override
	protected IDocumentAdapter createDocumentAdapter() {
		if (documentAdapter == null) {
			documentAdapter = new ConsoleDocumentAdapter(consoleWidth = -1);
		}
		return documentAdapter;
	}

	/**
	 * Sets the console to have a fixed character width. Use -1 to indicate that
	 * a fixed width should not be used.
	 *
	 * @param width
	 *            fixed character width of the console, or -1
	 */
	public void setConsoleWidth(int width) {
		if (consoleWidth != width) {
			consoleWidth = width;
			ConsolePlugin.getStandardDisplay().asyncExec(new Runnable() {
				@Override
				public void run() {
					if (documentAdapter != null) {
						documentAdapter.setWidth(consoleWidth);
					}
				}
			});
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.jface.text.TextViewer#handleDispose()
	 */
	@Override
	protected void handleDispose() {
		IDocument document = getDocument();
		if (document != null) {
			document.removeDocumentListener(documentListener);
			document.removePositionUpdater(positionUpdater);
		}

		StyledText styledText = getTextWidget();
		styledText.removeLineStyleListener(this);
		styledText.removeLineBackgroundListener(this);
		styledText.removeMouseTrackListener(this);

		if(handCursor != null) {
			handCursor.dispose();
		}
		handCursor = null;
		if(textCursor != null) {
			textCursor.dispose();
		}
		textCursor = null;
		hyperlink = null;
		console = null;

		ColorRegistry colorRegistry = JFaceResources.getColorRegistry();
		colorRegistry.removeListener(propertyChangeListener);

		super.handleDispose();
	}

	class HyperlinkColorChangeListener implements IPropertyChangeListener {
		@Override
		public void propertyChange(PropertyChangeEvent event) {
			if (event.getProperty().equals(JFacePreferences.ACTIVE_HYPERLINK_COLOR) || event.getProperty().equals(JFacePreferences.HYPERLINK_COLOR)) {
				getTextWidget().redraw();
			}
		}

	}

	/*
	 * work around to memory leak in TextViewer$WidgetCommand
	 */
	@Override
	protected void updateTextListeners(WidgetCommand cmd) {
		super.updateTextListeners(cmd);
		cmd.preservedText = null;
		cmd.event = null;
		cmd.text = null;
	}

	@Override
	protected void internalRevealRange(int start, int end) {
		StyledText textWidget = getTextWidget();
		int startLine = documentAdapter.getLineAtOffset(start);
		int endLine = documentAdapter.getLineAtOffset(end);

		int top = textWidget.getTopIndex();
		if (top > -1) {
			// scroll vertically
			@SuppressWarnings("deprecation")
			int lines = getVisibleLinesInViewport();
			int bottom = top + lines;

			// two lines at the top and the bottom should always be left
			// if window is smaller than 5 lines, always center position is
			// chosen
			int bufferZone = 2;
			if (startLine >= top + bufferZone && startLine <= bottom - bufferZone && endLine >= top + bufferZone && endLine <= bottom - bufferZone) {

				// do not scroll at all as it is already visible
			} else {
				int delta = Math.max(0, lines - (endLine - startLine));
				textWidget.setTopIndex(startLine - delta / 3);
				updateViewportListeners(INTERNAL);
			}

			// scroll horizontally
			if (endLine < startLine) {
				endLine += startLine;
				startLine = endLine - startLine;
				endLine -= startLine;
			}

			int startPixel = -1;
			int endPixel = -1;

			if (endLine > startLine) {
				// reveal the beginning of the range in the start line
				IRegion extent = getExtent(start, start);
				startPixel = extent.getOffset() + textWidget.getHorizontalPixel();
				endPixel = startPixel;
			} else {
				IRegion extent = getExtent(start, end);
				startPixel = extent.getOffset() + textWidget.getHorizontalPixel();
				endPixel = startPixel + extent.getLength();
			}

			int visibleStart = textWidget.getHorizontalPixel();
			int visibleEnd = visibleStart + textWidget.getClientArea().width;

			// scroll only if not yet visible
			if (startPixel < visibleStart || visibleEnd < endPixel) {
				// set buffer zone to 10 pixels
				bufferZone = 10;
				int newOffset = visibleStart;
				int visibleWidth = visibleEnd - visibleStart;
				int selectionPixelWidth = endPixel - startPixel;

				if (startPixel < visibleStart) {
					newOffset = startPixel;
				} else if (selectionPixelWidth + bufferZone < visibleWidth) {
					newOffset = endPixel + bufferZone - visibleWidth;
				} else {
					newOffset = startPixel;
				}

				float index = ((float) newOffset) / ((float) getAverageCharWidth());

				textWidget.setHorizontalIndex(Math.round(index));
			}

		}
	}


}

Back to the top