* Improve DLTK Source Indexer to use AST cache (DLTK-596)
* Remove DLTK Indexer JobManager wait for 50 milliseconds after each module parse, if there is no search requests.
diff --git a/core/plugins/org.eclipse.dltk.core/model/org/eclipse/dltk/internal/core/ScriptProject.java b/core/plugins/org.eclipse.dltk.core/model/org/eclipse/dltk/internal/core/ScriptProject.java
index 9977cd7..dfe7994 100644
--- a/core/plugins/org.eclipse.dltk.core/model/org/eclipse/dltk/internal/core/ScriptProject.java
+++ b/core/plugins/org.eclipse.dltk.core/model/org/eclipse/dltk/internal/core/ScriptProject.java
@@ -9,20 +9,63 @@
  *******************************************************************************/
 package org.eclipse.dltk.internal.core;
 
-import java.io.*;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
 import java.net.URI;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
-import org.eclipse.core.resources.*;
-import org.eclipse.core.runtime.*;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
 import org.eclipse.core.runtime.preferences.IScopeContext;
 import org.eclipse.dltk.compiler.util.ObjectVector;
-import org.eclipse.dltk.core.*;
+import org.eclipse.dltk.core.DLTKCore;
+import org.eclipse.dltk.core.DLTKLanguageManager;
+import org.eclipse.dltk.core.IBuildpathContainer;
+import org.eclipse.dltk.core.IBuildpathEntry;
+import org.eclipse.dltk.core.IDLTKLanguageToolkit;
+import org.eclipse.dltk.core.IModelElement;
+import org.eclipse.dltk.core.IModelMarker;
+import org.eclipse.dltk.core.IModelStatus;
+import org.eclipse.dltk.core.IModelStatusConstants;
+import org.eclipse.dltk.core.IProjectFragment;
+import org.eclipse.dltk.core.IRegion;
+import org.eclipse.dltk.core.IScriptFolder;
+import org.eclipse.dltk.core.IScriptProject;
+import org.eclipse.dltk.core.ISearchableEnvironment;
+import org.eclipse.dltk.core.ISourceModule;
+import org.eclipse.dltk.core.IType;
+import org.eclipse.dltk.core.ITypeHierarchy;
+import org.eclipse.dltk.core.ModelException;
+import org.eclipse.dltk.core.WorkingCopyOwner;
+import org.eclipse.dltk.core.search.indexing.IndexManager;
 import org.eclipse.dltk.internal.core.util.MementoTokenizer;
 import org.eclipse.dltk.internal.core.util.Messages;
 import org.eclipse.dltk.internal.core.util.Util;
@@ -2381,7 +2424,10 @@
 			throws ModelException {
 		IProjectFragment[] allRoots = this.getAllProjectFragments();
 		if (!path.isAbsolute()) {
-			throw new IllegalArgumentException(Messages.path_mustBeAbsolute);
+			if (path.segmentCount() == 0
+					|| !path.segment(0).equals(IndexManager.SPECIAL_BUILTIN)) {
+				throw new IllegalArgumentException(Messages.path_mustBeAbsolute);
+			}
 		}
 		for (int i = 0; i < allRoots.length; i++) {
 			IProjectFragment buildpathRoot = allRoots[i];
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/BasicSearchEngine.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/BasicSearchEngine.java
index 6c998af..b65afda 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/BasicSearchEngine.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/BasicSearchEngine.java
@@ -304,8 +304,8 @@
 						int indexMatchLength = indexMatchPaths.length;
 						SearchDocument[] indexMatches = new SearchDocument[indexMatchLength];
 						for (int j = 0; j < indexMatchLength; j++) {
-							indexMatches[j] = participant
-									.getDocument(indexMatchPaths[j]);
+							indexMatches[j] = participant.getDocument(
+									indexMatchPaths[j], null);
 						}
 						SearchDocument[] matches = MatchLocator
 								.addWorkingCopies(pattern, indexMatches,
@@ -408,8 +408,8 @@
 						int indexMatchLength = indexMatchPaths.length;
 						SearchDocument[] indexMatches = new SearchDocument[indexMatchLength];
 						for (int j = 0; j < indexMatchLength; j++) {
-							indexMatches[j] = participant
-									.getDocument(indexMatchPaths[j]);
+							indexMatches[j] = participant.getDocument(
+									indexMatchPaths[j], null);
 						}
 						SearchDocument[] matches = MatchLocator
 								.addWorkingCopies(pattern, indexMatches,
@@ -1446,7 +1446,9 @@
 							pattern,
 							new SearchDocument[] { new DLTKSearchDocument(
 									enclosingElement.getPath().toString(),
-									contents, participant, external) },
+									contents, participant, external,
+									enclosingElement.getScriptProject()
+											.getProject()) },
 							getWorkingCopies(enclosingElement), participant);
 					participant.locateMatches(documents, pattern, scope,
 							requestor, monitor);
@@ -1533,7 +1535,8 @@
 	/**
 	 * @see SearchEngine#createTypeNameMatch(IType, int) for detailed comment.
 	 */
-	public static MethodNameMatch createMethodNameMatch(IMethod method, int modifiers) {
+	public static MethodNameMatch createMethodNameMatch(IMethod method,
+			int modifiers) {
 		return new DLTKSearchMethodNameMatch(method, modifiers);
 	}
 
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/DLTKSearchParticipant.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/DLTKSearchParticipant.java
index d78c03c..7d913e8 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/DLTKSearchParticipant.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/DLTKSearchParticipant.java
@@ -10,6 +10,7 @@
 package org.eclipse.dltk.core.search;
 
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
@@ -63,10 +64,10 @@
 		return "DLTK"; //$NON-NLS-1$
 	}
 
-	public SearchDocument getDocument(String documentPath) {
+	public SearchDocument getDocument(String documentPath, IProject project) {
 		return new DLTKSearchDocument(documentPath,
 				getDocumentContents(documentPath), this,
-				isExternal(documentPath));
+				isExternal(documentPath), project);
 	}
 
 	private boolean isExternal(String documentPath) {
@@ -110,7 +111,7 @@
 	 * (non-Javadoc)
 	 * 
 	 * @see SearchParticipant#locateMatches(SearchDocument[], SearchPattern,
-	 *      IDLTKSearchScope, SearchRequestor, IProgressMonitor)
+	 * IDLTKSearchScope, SearchRequestor, IProgressMonitor)
 	 */
 	public void locateMatches(SearchDocument[] indexMatches,
 			SearchPattern pattern, IDLTKSearchScope scope,
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/SearchDocument.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/SearchDocument.java
index cb67035..cd0f1dc 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/SearchDocument.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/SearchDocument.java
@@ -9,6 +9,8 @@
  *******************************************************************************/
 package org.eclipse.dltk.core.search;
 
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IPath;
 import org.eclipse.dltk.core.search.indexing.InternalSearchDocument;
 
 /**
@@ -23,6 +25,8 @@
 public abstract class SearchDocument extends InternalSearchDocument {
 	private String documentPath;
 	private SearchParticipant participant;
+	private IProject project;
+	public IPath fullPath;
 
 	/**
 	 * Creates a new search document. The given document path is a string that
@@ -35,15 +39,18 @@
 	 * @param participant
 	 *            the participant that creates the search document
 	 */
-	protected SearchDocument(String documentPath, SearchParticipant participant) {
+	protected SearchDocument(String documentPath,
+			SearchParticipant participant, IProject project) {
 		this.documentPath = documentPath;
 		this.participant = participant;
+		this.project = project;
 	}
 
 	/**
 	 * Adds the given index entry (category and key) coming from this document
 	 * to the index. This method must be called from
-	 * {@link SearchParticipant#indexDocument(SearchDocument document, org.eclipse.core.runtime.IPath indexPath)}.
+	 * {@link SearchParticipant#indexDocument(SearchDocument document, org.eclipse.core.runtime.IPath indexPath)}
+	 * .
 	 * 
 	 * @param category
 	 *            the category of the index entry
@@ -99,7 +106,7 @@
 		String ret = new String(contents);
 		return ret;
 	}
-	
+
 	/**
 	 * Returns the encoding for this document.
 	 * <p>
@@ -134,11 +141,16 @@
 	/**
 	 * Removes all index entries from the index for the given document. This
 	 * method must be called from
-	 * {@link SearchParticipant#indexDocument(SearchDocument document, org.eclipse.core.runtime.IPath indexPath)}.
+	 * {@link SearchParticipant#indexDocument(SearchDocument document, org.eclipse.core.runtime.IPath indexPath)}
+	 * .
 	 */
 	public void removeAllIndexEntries() {
 		super.removeAllIndexEntries();
 	}
 
 	public abstract boolean isExternal();
+
+	public IProject getProject() {
+		return this.project;
+	}
 }
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/SearchParticipant.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/SearchParticipant.java
index 8ab3906..f009d98 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/SearchParticipant.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/SearchParticipant.java
@@ -9,6 +9,7 @@
  *******************************************************************************/
 package org.eclipse.dltk.core.search;
 
+import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.ResourcesPlugin;
@@ -22,37 +23,40 @@
 import org.eclipse.dltk.internal.core.Model;
 import org.eclipse.dltk.internal.core.ModelManager;
 
-
 /**
  * A search participant describes a particular extension to a generic search
  * mechanism, permitting combined search actions which will involve all required
  * participants.
  * <p>
- * A search participant is involved in the indexing phase and in the search phase. 
- * The indexing phase consists in taking one or more search documents, parse them, and
- * add index entries in an index chosen by the participant. An index is identified by a
- * path on disk.
- * The search phase consists in selecting the indexes corresponding to a search pattern
- * and a search scope, from these indexes the search infrastructure extracts the document paths
- * that match the search pattern asking the search participant for the corresponding document,
- * finally the search participant is asked to locate the matches precisely in these search documents.
+ * A search participant is involved in the indexing phase and in the search
+ * phase. The indexing phase consists in taking one or more search documents,
+ * parse them, and add index entries in an index chosen by the participant. An
+ * index is identified by a path on disk. The search phase consists in selecting
+ * the indexes corresponding to a search pattern and a search scope, from these
+ * indexes the search infrastructure extracts the document paths that match the
+ * search pattern asking the search participant for the corresponding document,
+ * finally the search participant is asked to locate the matches precisely in
+ * these search documents.
  * </p>
  * <p>
- * This class is intended to be subclassed by clients. During the indexing phase, 
- * a subclass will be called with the following requests in order:
+ * This class is intended to be subclassed by clients. During the indexing
+ * phase, a subclass will be called with the following requests in order:
  * <ul>
  * <li>{@link #scheduleDocumentIndexing(SearchDocument, IPath)}</li>
  * <li>{@link #indexDocument(SearchDocument, IPath)}</li>
  * </ul>
- * During the search phase, a subclass will be called with the following requests in order:
+ * During the search phase, a subclass will be called with the following
+ * requests in order:
  * <ul>
  * <li>{@link #selectIndexes(SearchPattern, IJavaSearchScope)}</li>
  * <li>one or more {@link #getDocument(String)}</li>
- * <li>{@link #locateMatches(SearchDocument[], SearchPattern, IJavaSearchScope, SearchRequestor, IProgressMonitor)}</li>
+ * <li>
+ * {@link #locateMatches(SearchDocument[], SearchPattern, IJavaSearchScope, SearchRequestor, IProgressMonitor)}
+ * </li>
  * </ul>
  * </p>
  * 
-	 *
+ * 
  */
 public abstract class SearchParticipant {
 
@@ -62,12 +66,12 @@
 	protected SearchParticipant() {
 		// do nothing
 	}
-	
+
 	/**
 	 * Notification that this participant's help is needed in a search.
 	 * <p>
-	 * This method should be re-implemented in subclasses that need to do something
-	 * when the participant is needed in a search.
+	 * This method should be re-implemented in subclasses that need to do
+	 * something when the participant is needed in a search.
 	 * </p>
 	 */
 	public void beginSearching() {
@@ -77,8 +81,8 @@
 	/**
 	 * Notification that this participant's help is no longer needed.
 	 * <p>
-	 * This method should be re-implemented in subclasses that need to do something
-	 * when the participant is no longer needed in a search.
+	 * This method should be re-implemented in subclasses that need to do
+	 * something when the participant is no longer needed in a search.
 	 * </p>
 	 */
 	public void doneSearching() {
@@ -88,8 +92,8 @@
 	/**
 	 * Returns a displayable name of this search participant.
 	 * <p>
-	 * This method should be re-implemented in subclasses that need to 
-	 * display a meaningfull name.
+	 * This method should be re-implemented in subclasses that need to display a
+	 * meaningfull name.
 	 * </p>
 	 * 
 	 * @return the displayable name of this search participant
@@ -99,42 +103,51 @@
 	}
 
 	/**
-	 * Returns a search document for the given path.
-	 * The given document path is a string that uniquely identifies the document.
-	 * Most of the time it is a workspace-relative path, but it can also be a file system path, or a path inside a zip file.
+	 * Returns a search document for the given path. The given document path is
+	 * a string that uniquely identifies the document. Most of the time it is a
+	 * workspace-relative path, but it can also be a file system path, or a path
+	 * inside a zip file.
 	 * <p>
-	 * Implementors of this method can either create an instance of their own subclass of 
-	 * {@link SearchDocument} or return an existing instance of such a subclass.
+	 * Implementors of this method can either create an instance of their own
+	 * subclass of {@link SearchDocument} or return an existing instance of such
+	 * a subclass.
 	 * </p>
 	 * 
-	 * @param documentPath the path of the document.
+	 * @param documentPath
+	 *            the path of the document.
 	 * @return a search document
 	 */
-	public abstract SearchDocument getDocument(String documentPath);
-	
+	public abstract SearchDocument getDocument(String documentPath,
+			IProject project);
+
 	/**
-	 * Indexes the given document in the given index. A search participant
-	 * asked to index a document should parse it and call 
+	 * Indexes the given document in the given index. A search participant asked
+	 * to index a document should parse it and call
 	 * {@link SearchDocument#addIndexEntry(char[], char[])} as many times as
 	 * needed to add index entries to the index. If delegating to another
 	 * participant, it should use the original index location (and not the
 	 * delegatee's one). In the particular case of delegating to the default
-	 * search participant (see {@link SearchEngine#getDefaultSearchParticipant()}),
-	 * the provided document's path must be a path ending with one of the 
-	 * {@link org.eclipse.dltk.core.DLTKCore#getScriptLikeExtensions() Script-like extensions} 
-	 * or with '.class'.
+	 * search participant (see
+	 * {@link SearchEngine#getDefaultSearchParticipant()}), the provided
+	 * document's path must be a path ending with one of the
+	 * {@link org.eclipse.dltk.core.DLTKCore#getScriptLikeExtensions()
+	 * Script-like extensions} or with '.class'.
 	 * <p>
-	 * The given index location must represent a path in the file system to a file that
-	 * either already exists or is going to be created. If it exists, it must be an index file,
-	 * otherwise its data might be overwritten.
-	 * </p><p>
+	 * The given index location must represent a path in the file system to a
+	 * file that either already exists or is going to be created. If it exists,
+	 * it must be an index file, otherwise its data might be overwritten.
+	 * </p>
+	 * <p>
 	 * Clients are not expected to call this method.
 	 * </p>
 	 * 
-	 * @param document the document to index
-	 * @param indexLocation the location in the file system to the index
+	 * @param document
+	 *            the document to index
+	 * @param indexLocation
+	 *            the location in the file system to the index
 	 */
-	public abstract void indexDocument(SearchDocument document, IPath indexLocation);
+	public abstract void indexDocument(SearchDocument document,
+			IPath indexLocation);
 
 	/**
 	 * Locates the matches in the given documents using the given search pattern
@@ -142,29 +155,44 @@
 	 * method is called by the search engine once it has search documents
 	 * matching the given pattern in the given search scope.
 	 * <p>
-	 * Note that a participant (e.g. a JSP participant) can pre-process the contents of the given documents, 
-	 * create its own documents whose contents are Script compilation units and delegate the match location 
-	 * to the default participant (see {@link SearchEngine#getDefaultSearchParticipant()}). Passing its own
-	 * {@link SearchRequestor} this particpant can then map the match positions back to the original
-	 * contents, create its own matches and report them to the original requestor.
-	 * </p><p>
-	 * Implementors of this method should check the progress monitor
-	 * for cancelation when it is safe and appropriate to do so.  The cancelation
-	 * request should be propagated to the caller by throwing 
+	 * Note that a participant (e.g. a JSP participant) can pre-process the
+	 * contents of the given documents, create its own documents whose contents
+	 * are Script compilation units and delegate the match location to the
+	 * default participant (see
+	 * {@link SearchEngine#getDefaultSearchParticipant()}). Passing its own
+	 * {@link SearchRequestor} this particpant can then map the match positions
+	 * back to the original contents, create its own matches and report them to
+	 * the original requestor.
+	 * </p>
+	 * <p>
+	 * Implementors of this method should check the progress monitor for
+	 * cancelation when it is safe and appropriate to do so. The cancelation
+	 * request should be propagated to the caller by throwing
 	 * <code>OperationCanceledException</code>.
 	 * </p>
 	 * 
-	 * @param documents the documents to locate matches in
-	 * @param pattern the search pattern to use when locating matches
-	 * @param scope the scope to limit the search to
-	 * @param requestor the requestor to report matches to
-	 * @param monitor the progress monitor to report progress to,
-	 * or <code>null</code> if no progress should be reported
-	 * @throws CoreException if the requestor had problem accepting one of the matches
+	 * @param documents
+	 *            the documents to locate matches in
+	 * @param pattern
+	 *            the search pattern to use when locating matches
+	 * @param scope
+	 *            the scope to limit the search to
+	 * @param requestor
+	 *            the requestor to report matches to
+	 * @param monitor
+	 *            the progress monitor to report progress to, or
+	 *            <code>null</code> if no progress should be reported
+	 * @throws CoreException
+	 *             if the requestor had problem accepting one of the matches
 	 */
-	public abstract void locateMatches(SearchDocument[] documents, SearchPattern pattern, IDLTKSearchScope scope, SearchRequestor requestor, IProgressMonitor monitor) throws CoreException;
+	public abstract void locateMatches(SearchDocument[] documents,
+			SearchPattern pattern, IDLTKSearchScope scope,
+			SearchRequestor requestor, IProgressMonitor monitor)
+			throws CoreException;
+
 	/**
 	 * Return correct source modules from selected index matches.
+	 * 
 	 * @param matches
 	 * @param pattern
 	 * @param scope
@@ -173,81 +201,99 @@
 	 */
 	public abstract ISourceModule[] locateModules(SearchDocument[] matches,
 			SearchPattern pattern, IDLTKSearchScope scope,
-			IProgressMonitor subMonitor)  throws CoreException;
+			IProgressMonitor subMonitor) throws CoreException;
 
 	/**
-	 * Removes the index for a given path. 
+	 * Removes the index for a given path.
 	 * <p>
-	 * The given index location must represent a path in the file system to a file that
-	 * already exists and must be an index file, otherwise nothing will be done.
-	 * </p><p>
-	 * It is strongly recommended to use this method instead of deleting file directly
-	 * otherwise cached index will not be removed.
+	 * The given index location must represent a path in the file system to a
+	 * file that already exists and must be an index file, otherwise nothing
+	 * will be done.
+	 * </p>
+	 * <p>
+	 * It is strongly recommended to use this method instead of deleting file
+	 * directly otherwise cached index will not be removed.
 	 * </p>
 	 * 
-	 * @param indexLocation the location in the file system to the index
-	 *
+	 * @param indexLocation
+	 *            the location in the file system to the index
+	 * 
 	 */
-	public void removeIndex(IPath indexLocation){
+	public void removeIndex(IPath indexLocation) {
 		IndexManager manager = ModelManager.getModelManager().getIndexManager();
 		manager.removeIndexPath(indexLocation);
 	}
 
 	/**
-	 * Schedules the indexing of the given document.
-	 * Once the document is ready to be indexed, 
-	 * {@link #indexDocument(SearchDocument, IPath) indexDocument(document, indexPath)}
-	 * will be called in a different thread than the caller's thread.
+	 * Schedules the indexing of the given document. Once the document is ready
+	 * to be indexed, {@link #indexDocument(SearchDocument, IPath)
+	 * indexDocument(document, indexPath)} will be called in a different thread
+	 * than the caller's thread.
 	 * <p>
-	 * The given index location must represent a path in the file system to a file that
-	 * either already exists or is going to be created. If it exists, it must be an index file,
-	 * otherwise its data might be overwritten.
-	 * </p><p>
-	 * When the index is no longer needed, clients should use {@link #removeIndex(IPath) }
-	 * to discard it.
+	 * The given index location must represent a path in the file system to a
+	 * file that either already exists or is going to be created. If it exists,
+	 * it must be an index file, otherwise its data might be overwritten.
+	 * </p>
+	 * <p>
+	 * When the index is no longer needed, clients should use
+	 * {@link #removeIndex(IPath) } to discard it.
 	 * </p>
 	 * 
-	 * @param document the document to index
-	 * @param indexLocation the location on the file system of the index
+	 * @param document
+	 *            the document to index
+	 * @param indexLocation
+	 *            the location on the file system of the index
 	 */
-	public final void scheduleDocumentIndexing(SearchDocument document, IPath indexLocation) {
+	public final void scheduleDocumentIndexing(SearchDocument document,
+			IPath indexLocation) {
 		IPath documentPath = new Path(document.getPath());
 		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
 		Object file = Model.getTarget(root, documentPath, true);
 		IPath containerPath = documentPath;
 		if (file instanceof IResource) {
-			containerPath = ((IResource)file).getProject().getFullPath();
+			containerPath = ((IResource) file).getProject().getFullPath();
 		} else if (file == null) {
-			containerPath = documentPath.removeLastSegments(documentPath.segmentCount()-1);
+			containerPath = documentPath.removeLastSegments(documentPath
+					.segmentCount() - 1);
 		}
 		IndexManager manager = ModelManager.getModelManager().getIndexManager();
 		String osIndexLocation = indexLocation.toOSString();
-		// TODO (frederic) should not have to create index manually, should expose API that recreates index instead
+		// TODO (frederic) should not have to create index manually, should
+		// expose API that recreates index instead
 		manager.ensureIndexExists(osIndexLocation, containerPath);
-		manager.scheduleDocumentIndexing(document, containerPath, osIndexLocation, this);
+		manager.scheduleDocumentIndexing(document, containerPath,
+				osIndexLocation, this);
 	}
+
 	/**
 	 * Returns the collection of index locations to consider when performing the
 	 * given search query in the given scope. The search engine calls this
 	 * method before locating matches.
 	 * <p>
-	 * An index location represents a path in the file system to a file that holds index information. 
-	 * </p><p>
+	 * An index location represents a path in the file system to a file that
+	 * holds index information.
+	 * </p>
+	 * <p>
 	 * Clients are not expected to call this method.
 	 * </p>
 	 * 
-	 * @param query the search pattern to consider
-	 * @param scope the given search scope
+	 * @param query
+	 *            the search pattern to consider
+	 * @param scope
+	 *            the given search scope
 	 * @return the collection of index paths to consider
 	 */
-	public abstract IPath[] selectIndexes(SearchPattern query, IDLTKSearchScope scope);
-	
-	public abstract IPath[] selectMixinIndexes(SearchPattern query, IDLTKSearchScope scope);
+	public abstract IPath[] selectIndexes(SearchPattern query,
+			IDLTKSearchScope scope);
+
+	public abstract IPath[] selectMixinIndexes(SearchPattern query,
+			IDLTKSearchScope scope);
 
 	public abstract void skipNotMixin();
-	
+
 	/**
-	 * Used then searcj, if selected index are skipped, it not inclouded in search.
+	 * Used then searcj, if selected index are skipped, it not inclouded in
+	 * search.
 	 */
 	public abstract boolean isSkipped(Index index);
 }
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/AddBuiltinFolderToIndex.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/AddBuiltinFolderToIndex.java
index fc728d1..c0d6d60 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/AddBuiltinFolderToIndex.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/AddBuiltinFolderToIndex.java
@@ -170,10 +170,11 @@
 		IPath dpath = new Path(path).setDevice(null);
 		DLTKSearchDocument entryDocument = new DLTKSearchDocument(dpath
 				.toString(), Path.EMPTY, contents.toCharArray(), participant,
-				true);
+				true, this.project);
 		entryDocument.parser = parser;
 		entryDocument.requestor = requestor;
 		entryDocument.toolkit = toolkit;
+		entryDocument.fullPath = this.containerPath.append(dpath);
 		this.manager.indexDocument(entryDocument, participant, index,
 				this.containerPath);
 	}
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/AddExternalFolderToIndex.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/AddExternalFolderToIndex.java
index 3541711..ef887db 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/AddExternalFolderToIndex.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/AddExternalFolderToIndex.java
@@ -291,7 +291,10 @@
 		IPath dpath = new Path(path).removeFirstSegments(
 				containerPath.segmentCount()).setDevice(null);
 		DLTKSearchDocument entryDocument = new DLTKSearchDocument(dpath
-				.toString(), this.containerPath, contents, participant, true);
+				.toString(), this.containerPath, contents, participant, true,
+				project);
+		entryDocument.fullPath = EnvironmentPathUtils.getLocalPath(ffile
+				.getFullPath());
 		entryDocument.parser = parser;
 		entryDocument.requestor = requestor;
 		entryDocument.toolkit = toolkit;
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/IndexManager.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/IndexManager.java
index 7ca4b55..8873fe0 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/IndexManager.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/IndexManager.java
@@ -135,10 +135,11 @@
 		SearchParticipant participant = SearchEngine
 				.getDefaultSearchParticipant();
 		SearchDocument document = participant.getDocument(resource
-				.getFullPath().toString());
+				.getFullPath().toString(), resource.getProject());
 		((InternalSearchDocument) document).parser = parser;
 		((InternalSearchDocument) document).requestor = requestor;
 		((InternalSearchDocument) document).toolkit = toolkit;
+		document.fullPath = resource.getFullPath();
 		String indexLocation = this.computeIndexLocation(containerPath);
 		this.scheduleDocumentIndexing(document, containerPath, indexLocation,
 				participant);
@@ -155,7 +156,7 @@
 		SearchParticipant participant = SearchEngine
 				.getDefaultSearchParticipant();
 		SearchDocument document = participant.getDocument(resource
-				.getFullPath().toString());
+				.getFullPath().toString(), resource.getProject());
 		String indexLocation = this.computeIndexLocation(containerPath);
 		this.scheduleDocumentIndexing(document, containerPath, indexLocation,
 				participant);
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/SourceIndexer.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/SourceIndexer.java
index 7be221b..f5257ce 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/SourceIndexer.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/indexing/SourceIndexer.java
@@ -15,10 +15,15 @@
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.dltk.core.DLTKCore;
+import org.eclipse.dltk.core.IProjectFragment;
 import org.eclipse.dltk.core.IScriptFolder;
 import org.eclipse.dltk.core.IScriptProject;
 import org.eclipse.dltk.core.ISourceElementParser;
 import org.eclipse.dltk.core.ISourceModule;
+import org.eclipse.dltk.core.ISourceModuleInfoCache;
+import org.eclipse.dltk.core.ModelException;
+import org.eclipse.dltk.core.ISourceModuleInfoCache.ISourceModuleInfo;
+import org.eclipse.dltk.core.environment.EnvironmentPathUtils;
 import org.eclipse.dltk.core.search.IDLTKSearchScope;
 import org.eclipse.dltk.core.search.SearchDocument;
 import org.eclipse.dltk.internal.core.ModelManager;
@@ -61,8 +66,12 @@
 		IPath path = new Path(documentPath);
 		ISourceElementParser parser = ((InternalSearchDocument) this.document).parser;
 		if (!this.document.isExternal()) {
-			IProject project = ResourcesPlugin.getWorkspace().getRoot()
-					.getProject(path.segment(0));
+			IProject project = document.getProject();
+			if (project == null) {
+				project = ResourcesPlugin.getWorkspace().getRoot().getProject(
+						path.segment(0));
+			}
+
 			IScriptProject scriptProject = DLTKCore.create(project);
 
 			if (requestor == null) {
@@ -78,9 +87,9 @@
 			parser.setRequestor(requestor);
 			String pkgName = ""; //$NON-NLS-1$
 			IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
-			// ISourceModule sourceModule = null;
+			ISourceModule module = null;
 			if (file.exists()) {
-				ISourceModule module = (ISourceModule) DLTKCore.create(file);
+				module = (ISourceModule) DLTKCore.create(file);
 				if (module != null) {
 					// sourceModule = module;
 					IScriptFolder folder = (IScriptFolder) module.getParent();
@@ -88,6 +97,7 @@
 				}
 			}
 			requestor.setPackageName(pkgName);
+			// We need to get already
 			// Launch the parser
 			char[] source = null;
 			char[] name = null;
@@ -100,18 +110,21 @@
 			if (source == null || name == null)
 				return; // could not retrieve document info (e.g. resource was
 			// discarded)
-			parser.parseSourceModule(source, null, name);
+
+			/**
+			 * Using cache to build module.
+			 */
+			ISourceModuleInfo info = null;
+			ISourceModuleInfoCache cache = ModelManager.getModelManager()
+					.getSourceModuleInfoCache();
+			if (module != null) {
+				info = cache.get(module);
+			}
+
+			parser.parseSourceModule(source, info, name);
 
 		} else { // This is for external documents
 			if (parser == null || requestor == null) {
-				// parser =
-				// ModelManager.getModelManager().indexManager.
-				// getSourceElementParser(scriptProject,
-				// requestor);
-				if (DLTKCore.DEBUG) {
-					System.err
-							.println("TODO: Add getSourceElementParser here."); //$NON-NLS-1$
-				}
 				return;
 			} else {
 				parser.setRequestor(requestor);
@@ -137,7 +150,55 @@
 			if (source == null || name == null)
 				return; // could not retrieve document info (e.g. resource was
 			// discarded)
-			parser.parseSourceModule(source, null, name);
+
+			// We need to obtain ISourceModule handle to do caching. This will
+			// improve parsing performance.
+			ISourceModuleInfo info = null;
+
+			if (document.getProject() != null) {
+				IProject project = document.getProject();
+				IScriptProject scriptProject = DLTKCore.create(project);
+				try {
+					IProjectFragment[] fragments = scriptProject
+							.getProjectFragments();
+					IProjectFragment frag = null;
+					for (int i = 0; i < fragments.length; i++) {
+						IPath fragmentPath = EnvironmentPathUtils
+								.getLocalPath(fragments[i].getPath());
+						if (fragments[i].isExternal()
+								&& fragmentPath.isPrefixOf(document.fullPath)) {
+							if (frag != null
+									&& frag.getPath().isPrefixOf(
+											fragments[i].getPath())) {
+								frag = fragments[i];
+							} else {
+								frag = fragments[i];
+							}
+						}
+					}
+					if (frag != null) {
+						IPath fragmentRelativePath = document.fullPath
+								.removeFirstSegments(frag.getPath()
+										.segmentCount());
+						IScriptFolder folder = frag
+								.getScriptFolder(fragmentRelativePath
+										.removeLastSegments(1));
+						ISourceModule module = folder
+								.getSourceModule(document.fullPath
+										.lastSegment());
+						if (module.exists()) {
+							info = ModelManager.getModelManager()
+									.getSourceModuleInfoCache().get(module);
+						}
+					}
+				} catch (ModelException e) {
+					if (DLTKCore.DEBUG) {
+						e.printStackTrace();
+					}
+				}
+			}
+
+			parser.parseSourceModule(source, info, name);
 
 			long ended = System.currentTimeMillis();
 
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/matching/MatchLocator.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/matching/MatchLocator.java
index dccd987..d6f1d86 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/matching/MatchLocator.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/core/search/matching/MatchLocator.java
@@ -178,7 +178,8 @@
 		WorkingCopyDocument(org.eclipse.dltk.core.ISourceModule workingCopy,
 				SearchParticipant participant, boolean external) {
 			super(workingCopy.getPath().toString(), getContents(workingCopy),
-					participant, external);
+					participant, external, workingCopy.getScriptProject()
+							.getProject());
 			this.workingCopy = workingCopy;
 		}
 
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinBuilder.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinBuilder.java
index 41909b8..e0414a4 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinBuilder.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinBuilder.java
@@ -156,7 +156,8 @@
 
 				DLTKSearchDocument document = new DLTKSearchDocument(element
 						.getPath().toString(), containerPath, null,
-						participant, element instanceof ExternalSourceModule);
+						participant, element instanceof ExternalSourceModule,
+						element.getScriptProject().getProject());
 				// System.out.println("mixin indexing:" + document.getPath());
 				((InternalSearchDocument) document).toolkit = toolkit;
 				String containerRelativePath = null;
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinIndexRequest.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinIndexRequest.java
index 9dadac7..df4c460 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinIndexRequest.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinIndexRequest.java
@@ -88,7 +88,8 @@
 		final IPath path = module.getPath();
 		final DLTKSearchDocument document = new DLTKSearchDocument(path
 				.toString(), containerPath, null, participant,
-				module instanceof ExternalSourceModule);
+				module instanceof ExternalSourceModule, module
+						.getScriptProject().getProject());
 		document.toolkit = toolkit;
 		final String relativePath = containerRelativePath(containerPath,
 				module, path);
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinProjectIndexer.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinProjectIndexer.java
index b05155e..030f2c5 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinProjectIndexer.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/mixin/MixinProjectIndexer.java
@@ -49,10 +49,12 @@
 		try {
 			final IProjectFragment fragment = project.findProjectFragment(path);
 			if (fragment != null) {
-				final MixinIndexRequest request = new MixinExternalProjectFragmentRequest(
-						fragment, DLTKLanguageManager
-								.getLanguageToolkit(fragment));
-				requestIfNotWaiting(request);
+				if (!path.segment(0).equals(IndexManager.SPECIAL_BUILTIN)) {
+					final MixinIndexRequest request = new MixinExternalProjectFragmentRequest(
+							fragment, DLTKLanguageManager
+									.getLanguageToolkit(fragment));
+					requestIfNotWaiting(request);
+				}
 			} else {
 				DLTKCore.warn(NLS.bind(
 						Messages.MixinIndexer_unknownProjectFragment, path));
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/search/DLTKSearchDocument.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/search/DLTKSearchDocument.java
index 506f9dd..da93150 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/search/DLTKSearchDocument.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/search/DLTKSearchDocument.java
@@ -9,6 +9,7 @@
  *******************************************************************************/
 package org.eclipse.dltk.internal.core.search;
 
+import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
@@ -20,20 +21,23 @@
 	protected char[] charContents;
 	private boolean external;
 
-	public DLTKSearchDocument(String path,
-			char[] contents, SearchParticipant participant, boolean external) {
-		super(path, participant);
+	public DLTKSearchDocument(String path, char[] contents,
+			SearchParticipant participant, boolean external, IProject project) {
+		super(path, participant, project);
 		this.charContents = contents;
 		this.external = external;
 	}
-	
-	public DLTKSearchDocument(String path, IPath containerPath, char[] contents, SearchParticipant participant, boolean external) {
-		super(IDLTKSearchScope.FILE_ENTRY_SEPARATOR + path, participant );
+
+	public DLTKSearchDocument(String path, IPath containerPath,
+			char[] contents, SearchParticipant participant, boolean external,
+			IProject project) {
+		super(IDLTKSearchScope.FILE_ENTRY_SEPARATOR + path, participant,
+				project);
 		this.charContents = contents;
 		this.external = external;
 	}
-	
-	public String getContents() {		
+
+	public String getContents() {
 		return new String(charContents);
 	}
 
@@ -53,7 +57,7 @@
 	public String toString() {
 		return "SearchDocument for " + getPath(); //$NON-NLS-1$
 	}
-	
+
 	public boolean isExternal() {
 		return external;
 	}
diff --git a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/search/processing/JobManager.java b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/search/processing/JobManager.java
index d3634c9..3dbde95 100644
--- a/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/search/processing/JobManager.java
+++ b/core/plugins/org.eclipse.dltk.core/search/org/eclipse/dltk/internal/core/search/processing/JobManager.java
@@ -19,7 +19,6 @@
 import org.eclipse.dltk.internal.core.util.Messages;
 import org.eclipse.dltk.internal.core.util.Util;
 
-
 public abstract class JobManager implements Runnable {
 
 	/* queue of jobs to execute */
@@ -32,14 +31,16 @@
 	protected Thread processingThread;
 	protected Job progressJob;
 
-	/* counter indicating whether job execution is enabled or not, disabled if <= 0 
-	    it cannot go beyond 1 */
+	/*
+	 * counter indicating whether job execution is enabled or not, disabled if
+	 * <= 0 it cannot go beyond 1
+	 */
 	private int enableCount = 1;
 
 	public final static boolean VERBOSE = DLTKCore.VERBOSE_JOBMANAGER;
 	/* flag indicating that the activation has completed */
 	public boolean activated = false;
-	
+
 	private int awaitingClients = 0;
 
 	/**
@@ -48,30 +49,36 @@
 	public void activateProcessing() {
 		this.activated = true;
 	}
+
 	/**
 	 * Answer the amount of awaiting jobs.
 	 */
 	public synchronized int awaitingJobsCount() {
-		// pretend busy in case concurrent job attempts performing before activated
+		// pretend busy in case concurrent job attempts performing before
+		// activated
 		return this.activated ? this.jobEnd - this.jobStart + 1 : 1;
 	}
+
 	/**
 	 * Answers the first job in the queue, or null if there is no job available
-	 * Until the job has completed, the job manager will keep answering the same job.
+	 * Until the job has completed, the job manager will keep answering the same
+	 * job.
 	 */
 	public synchronized IJob currentJob() {
 		if (this.enableCount > 0 && this.jobStart <= this.jobEnd)
 			return this.awaitingJobs[this.jobStart];
 		return null;
 	}
+
 	public void disable() {
 		this.enableCount--;
 		if (VERBOSE)
 			Util.verbose("DISABLING background indexing"); //$NON-NLS-1$
 	}
+
 	/**
-	 * Remove the index from cache for a given project.
-	 * Passing null as a job family discards them all.
+	 * Remove the index from cache for a given project. Passing null as a job
+	 * family discards them all.
 	 */
 	public void discardJobs(String jobFamily) {
 
@@ -81,20 +88,22 @@
 		try {
 			IJob currentJob;
 			// cancel current job if it belongs to the given family
-			synchronized(this){
+			synchronized (this) {
 				currentJob = this.currentJob();
 				disable();
 			}
-			if (currentJob != null && (jobFamily == null || currentJob.belongsTo(jobFamily))) {
+			if (currentJob != null
+					&& (jobFamily == null || currentJob.belongsTo(jobFamily))) {
 				currentJob.cancel();
 
 				// wait until current active job has finished
-				while (this.processingThread != null && this.executing){
+				while (this.processingThread != null && this.executing) {
 					try {
 						if (VERBOSE)
-							Util.verbose("-> waiting end of current background job - " + currentJob); //$NON-NLS-1$
+							Util
+									.verbose("-> waiting end of current background job - " + currentJob); //$NON-NLS-1$
 						Thread.sleep(50);
-					} catch(InterruptedException e){
+					} catch (InterruptedException e) {
 						// ignore
 					}
 				}
@@ -102,16 +111,19 @@
 
 			// flush and compact awaiting jobs
 			int loc = -1;
-			synchronized(this) {
+			synchronized (this) {
 				for (int i = this.jobStart; i <= this.jobEnd; i++) {
 					currentJob = this.awaitingJobs[i];
 					if (currentJob != null) { // sanity check
 						this.awaitingJobs[i] = null;
-						if (!(jobFamily == null || currentJob.belongsTo(jobFamily))) { // copy down, compacting
+						if (!(jobFamily == null || currentJob
+								.belongsTo(jobFamily))) { // copy down,
+							// compacting
 							this.awaitingJobs[++loc] = currentJob;
 						} else {
 							if (VERBOSE)
-								Util.verbose("-> discarding background job  - " + currentJob); //$NON-NLS-1$
+								Util
+										.verbose("-> discarding background job  - " + currentJob); //$NON-NLS-1$
 							currentJob.cancel();
 						}
 					}
@@ -123,25 +135,33 @@
 			enable();
 		}
 		if (VERBOSE)
-			Util.verbose("DISCARD   DONE with background job family - " + jobFamily); //$NON-NLS-1$
+			Util
+					.verbose("DISCARD   DONE with background job family - " + jobFamily); //$NON-NLS-1$
 	}
+
 	public synchronized void enable() {
 		this.enableCount++;
 		if (VERBOSE)
 			Util.verbose("ENABLING  background indexing"); //$NON-NLS-1$
-		this.notifyAll(); // wake up the background thread if it is waiting (context must be synchronized)			
+		this.notifyAll(); // wake up the background thread if it is waiting
+		// (context must be synchronized)
 	}
+
 	public synchronized boolean isJobWaiting(IJob request) {
-		for (int i = this.jobEnd; i > this.jobStart; i--) // don't check job at jobStart, as it may have already started
-			if (request.equals(this.awaitingJobs[i])) return true;
+		for (int i = this.jobEnd; i > this.jobStart; i--)
+			// don't check job at jobStart, as it may have already started
+			if (request.equals(this.awaitingJobs[i]))
+				return true;
 		return false;
 	}
+
 	/**
-	 * Advance to the next available job, once the current one has been completed.
-	 * Note: clients awaiting until the job count is zero are still waiting at this point.
+	 * Advance to the next available job, once the current one has been
+	 * completed. Note: clients awaiting until the job count is zero are still
+	 * waiting at this point.
 	 */
 	protected synchronized void moveToNextJob() {
-		//if (!enabled) return;
+		// if (!enabled) return;
 
 		if (this.jobStart <= this.jobEnd) {
 			this.awaitingJobs[this.jobStart++] = null;
@@ -151,28 +171,31 @@
 			}
 		}
 	}
+
 	/**
 	 * When idle, give chance to do something
 	 */
 	protected void notifyIdle(long idlingTime) {
 		// do nothing
 	}
+
 	/**
-	 * This API is allowing to run one job in concurrence with background processing.
-	 * Indeed since other jobs are performed in background, resource sharing might be 
-	 * an issue.Therefore, this functionality allows a given job to be run without
-	 * colliding with background ones.
-	 * Note: multiple thread might attempt to perform concurrent jobs at the same time,
-	 *            and should synchronize (it is deliberately left to clients to decide whether
-	 *            concurrent jobs might interfere or not. In general, multiple read jobs are ok).
-	 *
-	 * Waiting policy can be:
-	 * 		IJobConstants.ForceImmediateSearch
-	 * 		IJobConstants.CancelIfNotReadyToSearch
-	 * 		IJobConstants.WaitUntilReadyToSearch
-	 *
+	 * This API is allowing to run one job in concurrence with background
+	 * processing. Indeed since other jobs are performed in background, resource
+	 * sharing might be an issue.Therefore, this functionality allows a given
+	 * job to be run without colliding with background ones. Note: multiple
+	 * thread might attempt to perform concurrent jobs at the same time, and
+	 * should synchronize (it is deliberately left to clients to decide whether
+	 * concurrent jobs might interfere or not. In general, multiple read jobs
+	 * are ok).
+	 * 
+	 * Waiting policy can be: IJobConstants.ForceImmediateSearch
+	 * IJobConstants.CancelIfNotReadyToSearch
+	 * IJobConstants.WaitUntilReadyToSearch
+	 * 
 	 */
-	public boolean performConcurrentJob(IJob searchJob, int waitingPolicy, IProgressMonitor progress) {
+	public boolean performConcurrentJob(IJob searchJob, int waitingPolicy,
+			IProgressMonitor progress) {
 		if (VERBOSE)
 			Util.verbose("STARTING  concurrent job - " + searchJob); //$NON-NLS-1$
 
@@ -185,91 +208,103 @@
 		if (awaitingJobsCount() > 0) {
 			switch (waitingPolicy) {
 
-				case IJob.ForceImmediate :
-					if (VERBOSE)
-						Util.verbose("-> NOT READY - forcing immediate - " + searchJob);//$NON-NLS-1$
-					try {
-						disable(); // pause indexing
-						status = searchJob.execute(progress == null ? null : new SubProgressMonitor(progress, concurrentJobWork));
-					} finally {
-						enable();
-					}
-					if (VERBOSE)
-						Util.verbose("FINISHED  concurrent job - " + searchJob); //$NON-NLS-1$
-					return status;
+			case IJob.ForceImmediate:
+				if (VERBOSE)
+					Util
+							.verbose("-> NOT READY - forcing immediate - " + searchJob);//$NON-NLS-1$
+				try {
+					disable(); // pause indexing
+					status = searchJob.execute(progress == null ? null
+							: new SubProgressMonitor(progress,
+									concurrentJobWork));
+				} finally {
+					enable();
+				}
+				if (VERBOSE)
+					Util.verbose("FINISHED  concurrent job - " + searchJob); //$NON-NLS-1$
+				return status;
 
-				case IJob.CancelIfNotReady :
-					if (VERBOSE)
-						Util.verbose("-> NOT READY - cancelling - " + searchJob); //$NON-NLS-1$
-					if (VERBOSE)
-						Util.verbose("CANCELED concurrent job - " + searchJob); //$NON-NLS-1$
-					throw new OperationCanceledException();
+			case IJob.CancelIfNotReady:
+				if (VERBOSE)
+					Util.verbose("-> NOT READY - cancelling - " + searchJob); //$NON-NLS-1$
+				if (VERBOSE)
+					Util.verbose("CANCELED concurrent job - " + searchJob); //$NON-NLS-1$
+				throw new OperationCanceledException();
 
-				case IJob.WaitUntilReady :
-					int awaitingWork;
-					IJob previousJob = null;
-					IJob currentJob;
-					IProgressMonitor subProgress = null;
-					int totalWork = this.awaitingJobsCount();
-					if (progress != null && totalWork > 0) {
-						subProgress = new SubProgressMonitor(progress, concurrentJobWork / 2);
-						subProgress.beginTask("", totalWork); //$NON-NLS-1$
-						concurrentJobWork = concurrentJobWork / 2;
+			case IJob.WaitUntilReady:
+				int awaitingWork;
+				IJob previousJob = null;
+				IJob currentJob;
+				IProgressMonitor subProgress = null;
+				int totalWork = this.awaitingJobsCount();
+				if (progress != null && totalWork > 0) {
+					subProgress = new SubProgressMonitor(progress,
+							concurrentJobWork / 2);
+					subProgress.beginTask("", totalWork); //$NON-NLS-1$
+					concurrentJobWork = concurrentJobWork / 2;
+				}
+				// use local variable to avoid potential NPE (see bug 20435 NPE
+				// when searchingscriptmethod
+				// and bug 42760 NullPointerException in JobManager when
+				// searching)
+				Thread t = this.processingThread;
+				int originalPriority = t == null ? -1 : t.getPriority();
+				try {
+					if (t != null)
+						t.setPriority(Thread.currentThread().getPriority());
+					synchronized (this) {
+						this.awaitingClients++;
 					}
-					// use local variable to avoid potential NPE (see bug 20435 NPE when searchingscriptmethod
-					// and bug 42760 NullPointerException in JobManager when searching)
-					Thread t = this.processingThread;
-					int originalPriority = t == null ? -1 : t.getPriority();
-					try {
-						if (t != null)
-							t.setPriority(Thread.currentThread().getPriority());
-						synchronized(this) {
-							this.awaitingClients++;
-						}
-						while ((awaitingWork = awaitingJobsCount()) > 0) {
-							if (subProgress != null && subProgress.isCanceled())
-								throw new OperationCanceledException();
-							currentJob = currentJob();
-							// currentJob can be null when jobs have been added to the queue but job manager is not enabled
-							if (currentJob != null && currentJob != previousJob) {
-								if (VERBOSE)
-									Util.verbose("-> NOT READY - waiting until ready - " + searchJob);//$NON-NLS-1$
-								if (subProgress != null) {
-									subProgress.subTask(
-										Messages.bind(Messages.manager_filesToIndex, Integer.toString(awaitingWork))); 
-									subProgress.worked(1);
-								}
-								previousJob = currentJob;
+					while ((awaitingWork = awaitingJobsCount()) > 0) {
+						if (subProgress != null && subProgress.isCanceled())
+							throw new OperationCanceledException();
+						currentJob = currentJob();
+						// currentJob can be null when jobs have been added to
+						// the queue but job manager is not enabled
+						if (currentJob != null && currentJob != previousJob) {
+							if (VERBOSE)
+								Util
+										.verbose("-> NOT READY - waiting until ready - " + searchJob);//$NON-NLS-1$
+							if (subProgress != null) {
+								subProgress.subTask(Messages.bind(
+										Messages.manager_filesToIndex, Integer
+												.toString(awaitingWork)));
+								subProgress.worked(1);
 							}
-							try {
-								if (VERBOSE)
-									Util.verbose("-> GOING TO SLEEP - " + searchJob);//$NON-NLS-1$
-								Thread.sleep(searchJob instanceof WaitJob ? 250
+							previousJob = currentJob;
+						}
+						try {
+							if (VERBOSE)
+								Util
+										.verbose("-> GOING TO SLEEP - " + searchJob);//$NON-NLS-1$
+							Thread.sleep(searchJob instanceof WaitJob ? 250
 									: 50);
-							} catch (InterruptedException e) {
-								// ignore
-							}
+						} catch (InterruptedException e) {
+							// ignore
 						}
-					} finally {
-						synchronized(this) {
-							this.awaitingClients--;
-						}
-						if (t != null && originalPriority > -1 && t.isAlive())
-							t.setPriority(originalPriority);
 					}
-					if (subProgress != null)
-						subProgress.done();
+				} finally {
+					synchronized (this) {
+						this.awaitingClients--;
+					}
+					if (t != null && originalPriority > -1 && t.isAlive())
+						t.setPriority(originalPriority);
+				}
+				if (subProgress != null)
+					subProgress.done();
 			}
 		}
-		status = searchJob.execute(progress == null ? null : new SubProgressMonitor(progress, concurrentJobWork));
+		status = searchJob.execute(progress == null ? null
+				: new SubProgressMonitor(progress, concurrentJobWork));
 		if (progress != null)
 			progress.done();
 		if (VERBOSE)
 			Util.verbose("FINISHED  concurrent job - " + searchJob); //$NON-NLS-1$
 		return status;
 	}
+
 	public abstract String processName();
-	
+
 	private static final class WaitJob implements IJob {
 		public boolean belongsTo(String jobFamily) {
 			return false;
@@ -300,9 +335,11 @@
 
 		// append the job to the list of ones to process later on
 		int size = this.awaitingJobs.length;
-		if (++this.jobEnd == size) { // when growing, relocate jobs starting at position 0
+		if (++this.jobEnd == size) { // when growing, relocate jobs starting at
+			// position 0
 			this.jobEnd -= this.jobStart;
-			System.arraycopy(this.awaitingJobs, this.jobStart, this.awaitingJobs = new IJob[size * 2], 0, this.jobEnd);
+			System.arraycopy(this.awaitingJobs, this.jobStart,
+					this.awaitingJobs = new IJob[size * 2], 0, this.jobEnd);
 			this.jobStart = 0;
 		}
 		this.awaitingJobs[this.jobEnd] = job;
@@ -312,6 +349,7 @@
 		}
 		notifyAll(); // wake up the background thread if it is waiting
 	}
+
 	/**
 	 * Flush current state
 	 */
@@ -325,11 +363,13 @@
 			/* initiate background processing */
 			this.processingThread = new Thread(this, this.processName());
 			this.processingThread.setDaemon(true);
-			// less prioritary by default, priority is raised if clients are actively waiting on it
-			this.processingThread.setPriority(Thread.NORM_PRIORITY-1); 
+			// less prioritary by default, priority is raised if clients are
+			// actively waiting on it
+			this.processingThread.setPriority(Thread.NORM_PRIORITY - 1);
 			this.processingThread.start();
 		}
 	}
+
 	/**
 	 * Infinite loop performing resource indexing
 	 */
@@ -342,12 +382,16 @@
 				ProgressJob(String name) {
 					super(name);
 				}
+
 				protected IStatus run(IProgressMonitor monitor) {
 					int awaitingJobsCount;
 					monitor.beginTask(Messages.manager_indexingTask,
 							IProgressMonitor.UNKNOWN);
-					while (!monitor.isCanceled() && (awaitingJobsCount = awaitingJobsCount()) > 0) {
-						monitor.subTask(Messages.bind(Messages.manager_filesToIndex, Integer.toString(awaitingJobsCount))); 
+					while (!monitor.isCanceled()
+							&& (awaitingJobsCount = awaitingJobsCount()) > 0) {
+						monitor.subTask(Messages.bind(
+								Messages.manager_filesToIndex, Integer
+										.toString(awaitingJobsCount)));
 						try {
 							Thread.sleep(500);
 						} catch (InterruptedException e) {
@@ -363,10 +407,13 @@
 				try {
 					IJob job;
 					synchronized (this) {
-						// handle shutdown case when notifyAll came before the wait but after the while loop was entered
-						if (this.processingThread == null) continue;
+						// handle shutdown case when notifyAll came before the
+						// wait but after the while loop was entered
+						if (this.processingThread == null)
+							continue;
 
-						// must check for new job inside this sync block to avoid timing hole
+						// must check for new job inside this sync block to
+						// avoid timing hole
 						if ((job = currentJob()) == null) {
 							if (this.progressJob != null) {
 								this.progressJob.cancel();
@@ -375,15 +422,18 @@
 							if (idlingStart < 0)
 								idlingStart = System.currentTimeMillis();
 							else
-								notifyIdle(System.currentTimeMillis() - idlingStart);
-							this.wait(); // wait until a new job is posted (or reenabled:38901)
+								notifyIdle(System.currentTimeMillis()
+										- idlingStart);
+							this.wait(); // wait until a new job is posted (or
+							// reenabled:38901)
 						} else {
 							idlingStart = -1;
 						}
 					}
 					if (job == null) {
 						notifyIdle(System.currentTimeMillis() - idlingStart);
-						// just woke up, delay before processing any new jobs, allow some time for the active thread to finish
+						// just woke up, delay before processing any new jobs,
+						// allow some time for the active thread to finish
 						Thread.sleep(500);
 						continue;
 					}
@@ -394,50 +444,56 @@
 					try {
 						this.executing = true;
 						if (this.progressJob == null) {
-							this.progressJob = new ProgressJob(Messages.manager_indexingInProgress); 
+							this.progressJob = new ProgressJob(
+									Messages.manager_indexingInProgress);
 							this.progressJob.setPriority(Job.LONG);
 							// this.progressJob.setSystem(true);
 							this.progressJob.schedule();
 						}
-						/*boolean status = */job.execute(null);
-						//if (status == FAILED) request(job);
+						/* boolean status = */job.execute(null);
+						// if (status == FAILED) request(job);
 					} finally {
 						this.executing = false;
 						if (VERBOSE)
 							Util.verbose("FINISHED background job - " + job); //$NON-NLS-1$
 						moveToNextJob();
-						if (this.awaitingClients == 0)
-							Thread.sleep(50);
+						// if (this.awaitingClients == 0)
+						// Thread.sleep(50);
 					}
-				} catch (InterruptedException e) { // background indexing was interrupted
+				} catch (InterruptedException e) { // background indexing was
+					// interrupted
 				}
 			}
 		} catch (RuntimeException e) {
 			if (this.processingThread != null) { // if not shutting down
 				// log exception
 				Util.log(e, "Background Indexer Crash Recovery"); //$NON-NLS-1$
-				
+
 				// keep job manager alive
 				this.discardJobs(null);
 				this.processingThread = null;
-				this.reset(); // this will fork a new thread with no waiting jobs, some indexes will be inconsistent
+				this.reset(); // this will fork a new thread with no waiting
+				// jobs, some indexes will be inconsistent
 			}
 			throw e;
 		} catch (Error e) {
 			if (this.processingThread != null && !(e instanceof ThreadDeath)) {
 				// log exception
 				Util.log(e, "Background Indexer Crash Recovery"); //$NON-NLS-1$
-				
+
 				// keep job manager alive
 				this.discardJobs(null);
 				this.processingThread = null;
-				this.reset(); // this will fork a new thread with no waiting jobs, some indexes will be inconsistent
+				this.reset(); // this will fork a new thread with no waiting
+				// jobs, some indexes will be inconsistent
 			}
 			throw e;
 		}
 	}
+
 	/**
-	 * Stop background processing, and wait until the current job is completed before returning
+	 * Stop background processing, and wait until the current job is completed
+	 * before returning
 	 */
 	public void shutdown() {
 
@@ -445,12 +501,18 @@
 			Util.verbose("Shutdown"); //$NON-NLS-1$
 
 		disable();
-		discardJobs(null); // will wait until current executing job has completed
+		discardJobs(null); // will wait until current executing job has
+		// completed
 		Thread thread = this.processingThread;
 		try {
-			if (thread != null) { // see http://bugs.eclipse.org/bugs/show_bug.cgi?id=31858
+			if (thread != null) { // see
+				// http://bugs.eclipse.org/bugs/show_bug.cgi
+				// ?id=31858
 				synchronized (this) {
-					this.processingThread = null; // mark the job manager as shutting down so that the thread will stop by itself
+					this.processingThread = null; // mark the job manager as
+					// shutting down so that the
+					// thread will stop by
+					// itself
 					this.notifyAll(); // ensure its awake so it can be shutdown
 				}
 				// in case processing thread is handling a job
@@ -465,14 +527,17 @@
 			// ignore
 		}
 	}
+
 	public String toString() {
 		StringBuffer buffer = new StringBuffer(10);
 		buffer.append("Enable count:").append(this.enableCount).append('\n'); //$NON-NLS-1$
 		int numJobs = this.jobEnd - this.jobStart + 1;
 		buffer.append("Jobs in queue:").append(numJobs).append('\n'); //$NON-NLS-1$
 		for (int i = 0; i < numJobs && i < 15; i++) {
-			buffer.append(i).append(" - job["+i+"]: ").append(this.awaitingJobs[this.jobStart+i]).append('\n'); //$NON-NLS-1$ //$NON-NLS-2$
+			buffer
+					.append(i)
+					.append(" - job[" + i + "]: ").append(this.awaitingJobs[this.jobStart + i]).append('\n'); //$NON-NLS-1$ //$NON-NLS-2$
 		}
 		return buffer.toString();
-	}	
+	}
 }