gt: 			Region region = new Region(contentOffset + start, matcher.end(1)
context: /*******************************************************************************
   * Copyright (c) 2011 Benjamin Muskalla 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:
   *     Benjamin Muskalla <benjamin.muskalla@tasktop.com> - initial API and implementation
   *******************************************************************************/
  package org.eclipse.egit.internal.mylyn.ui;
  
  import java.io.File;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.egit.core.Activator;
  import org.eclipse.egit.core.RepositoryCache;
  import org.eclipse.egit.core.RepositoryUtil;
  import org.eclipse.egit.ui.internal.commit.CommitEditor;
  import org.eclipse.egit.ui.internal.commit.RepositoryCommit;
  import org.eclipse.jface.dialogs.MessageDialog;
  import org.eclipse.jface.text.BadLocationException;
  import org.eclipse.jface.text.IDocument;
  import org.eclipse.jface.text.IRegion;
  import org.eclipse.jface.text.ITextViewer;
  import org.eclipse.jface.text.Region;
  import org.eclipse.jface.text.TextViewer;
  import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
  import org.eclipse.jface.text.hyperlink.IHyperlink;
  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  import org.eclipse.jgit.errors.MissingObjectException;
  import org.eclipse.jgit.lib.ObjectId;
  import org.eclipse.jgit.lib.Repository;
  import org.eclipse.jgit.revwalk.RevCommit;
  import org.eclipse.jgit.revwalk.RevWalk;
  import org.eclipse.osgi.util.NLS;
  import org.eclipse.swt.widgets.Shell;
  
  /**
   * Detects Git commit ids in task descriptions and allows users to open them in
   * the commit editor.
   */
  public class CommitHyperlinkDetector extends AbstractHyperlinkDetector {
  
  	private static final Pattern PATTERN_COMMIT_ID = Pattern
  			.compile("(?<!\\w)([0-9a-f]{8}([0-9a-f]{32})?)"); //$NON-NLS-1$
  
  	private static class CommitHyperlink implements IHyperlink {
  
  		private IRegion region;
  
  		private String objectId;
  
  		private final Shell shell;
  
  		public CommitHyperlink(IRegion region, String objectId, Shell shell) {
  			this.shell = shell;
  			Assert.isNotNull(objectId);
  			Assert.isNotNull(region);
  			Assert.isNotNull(shell);
  
  			this.region = region;
  			this.objectId = objectId;
  		}
  
  		public IRegion getHyperlinkRegion() {
  			return region;
  		}
  
  		public String getTypeLabel() {
  			return null;
  		}
  
  		public String getHyperlinkText() {
  			return objectId;
  		}
  
  		public void open() {
  			try {
  				RepositoryCommit commit;
  				commit = searchCommit();
  				if (commit != null)
  					CommitEditor.openQuiet(commit);
  				else
  					informCommitNotFound();
  			} catch (IOException e) {
  				// ignore
  			}
  		}
  
  		private void informCommitNotFound() {
  			MessageDialog
  					.openWarning(
  							shell,
  							Messages.CommitHyperlinkDetector_CommitNotFound,
  							NLS.bind(
  									Messages.CommitHyperlinkDetector_CommitNotFoundInRepositories,
  									objectId));
  		}
  
  		private RepositoryCommit searchCommit() throws IOException {
  			RepositoryUtil repositoryUtil = Activator.getDefault()
  					.getRepositoryUtil();
  			List<String> configuredRepositories = repositoryUtil
  					.getConfiguredRepositories();
  			RepositoryCache repositoryCache = Activator.getDefault()
  					.getRepositoryCache();
  			for (String repoDir : configuredRepositories) {
  				Repository repository = repositoryCache
  						.lookupRepository(new File(repoDir));
  				RevCommit commit = getCommit(repository);
  				if (commit != null)
  					return new RepositoryCommit(repository, commit);
  			}
  
  			return null;
  		}
  
  		private RevCommit getCommit(Repository repository) throws IOException {
  			RevWalk revWalk = null;
  			try {
  				revWalk = new RevWalk(repository);
  				return revWalk.parseCommit(ObjectId.fromString(objectId));
  			} catch (MissingObjectException e) {
  				// ignore
  				return null;
  			} catch (IncorrectObjectTypeException e) {
  				// ignore
  				return null;
  			} finally {
  				if (revWalk != null)
  					revWalk.release();
  			}
  
  		}
  
  		@Override
  		public String toString() {
  			StringBuilder builder = new StringBuilder();
  			builder.append("CommitHyperlink [region="); //$NON-NLS-1$
  			builder.append(region);
  			builder.append(", objectId="); //$NON-NLS-1$
  			builder.append(objectId);
  			builder.append("]"); //$NON-NLS-1$
  			return builder.toString();
  		}
  
  	}
  
  	/**
  	 * Detects and returns all available hyperlinks for the given
  	 * {@link TextViewer} which link to a Git commit.
  	 */
  	public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
  			final IRegion region, boolean canShowMultipleHyperlinks) {
  		IDocument document = textViewer.getDocument();
  		if (document == null || document.getLength() == 0) {
  			return null;
  		}
  
  		String content;
  		int contentOffset;
  		int index;
  		try {
  			if (region.getLength() == 0) {
  				// expand the region to include the whole line
  				IRegion lineInfo = document.getLineInformationOfOffset(region
  						.getOffset());
  				int lineLength = lineInfo.getLength();
  				int lineOffset = lineInfo.getOffset();
  				int lineEnd = lineOffset + lineLength;
  				int regionEnd = region.getOffset() + region.getLength();
  				if (lineOffset < region.getOffset()) {
  					int regionLength = Math.max(regionEnd, lineEnd)
  							- lineOffset;
  					contentOffset = lineOffset;
  					content = document.get(lineOffset, regionLength);
  					index = region.getOffset() - lineOffset;
  				} else {
  					// the line starts after region, may never happen
  					int regionLength = Math.max(regionEnd, lineEnd)
  							- region.getOffset();
  					contentOffset = region.getOffset();
  					content = document.get(contentOffset, regionLength);
  					index = 0;
  				}
  			} else {
  				content = document.get(region.getOffset(), region.getLength());
  				contentOffset = region.getOffset();
  				index = -1;
  			}
  		} catch (BadLocationException ex) {
  			return null;
  		}
  
  		List<IHyperlink> hyperlinks = detectHyperlinks(textViewer, content,
  				index, contentOffset);
  		if (hyperlinks == null) {
  			return null;
  		}
  
  		// filter hyperlinks that do not match original region
  		if (region.getLength() == 0) {
  			for (Iterator<IHyperlink> it = hyperlinks.iterator(); it.hasNext();) {
  				IHyperlink hyperlink = it.next();
  				IRegion hyperlinkRegion = hyperlink.getHyperlinkRegion();
  				if (!isInRegion(region, hyperlinkRegion)) {
  					it.remove();
  				}
  			}
  		}
  		if (hyperlinks.isEmpty()) {
  			return null;
  		}
  		return hyperlinks.toArray(new IHyperlink[hyperlinks.size()]);
  	}
  
  	private List<IHyperlink> detectHyperlinks(ITextViewer textViewer,
  			String content, int index, int contentOffset) {
  		Shell shell = textViewer.getTextWidget().getShell();
  		List<IHyperlink> links = null;
  		Matcher matcher = PATTERN_COMMIT_ID.matcher(content);
  		while (matcher.find()) {
  			if (index != -1
  					&& (index < matcher.start() || index > matcher.end())) {
  				continue;
  			}
  			if (links == null) {
  				links = new ArrayList<IHyperlink>();
  			}
  			int start = matcher.start(1);
  