[495132] Completion on resource path (src/href...) in HTML editor
diff --git a/web/bundles/org.eclipse.wst.html.ui/META-INF/MANIFEST.MF b/web/bundles/org.eclipse.wst.html.ui/META-INF/MANIFEST.MF
index 06e74df..8b28430 100755
--- a/web/bundles/org.eclipse.wst.html.ui/META-INF/MANIFEST.MF
+++ b/web/bundles/org.eclipse.wst.html.ui/META-INF/MANIFEST.MF
@@ -11,6 +11,7 @@
  org.eclipse.wst.html.ui.internal;x-internal:=true,
  org.eclipse.wst.html.ui.internal.autoedit;x-internal:=true,
  org.eclipse.wst.html.ui.internal.contentassist;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.contentassist.resources;x-internal:=true,
  org.eclipse.wst.html.ui.internal.contentoutline;x-internal:=true,
  org.eclipse.wst.html.ui.internal.contentproperties.ui;x-internal:=true,
  org.eclipse.wst.html.ui.internal.correction;x-internal:=true,
@@ -27,6 +28,7 @@
  org.eclipse.wst.html.ui.internal.taginfo;x-internal:=true,
  org.eclipse.wst.html.ui.internal.templates;x-internal:=true,
  org.eclipse.wst.html.ui.internal.text;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.text.correction;x-internal:=true,
  org.eclipse.wst.html.ui.internal.wizard;x-internal:=true,
  org.eclipse.wst.html.ui.views.contentoutline
 Import-Package: com.ibm.icu.util; version="3.8",
diff --git a/web/bundles/org.eclipse.wst.html.ui/plugin.properties b/web/bundles/org.eclipse.wst.html.ui/plugin.properties
index 892e155..03e9944 100644
--- a/web/bundles/org.eclipse.wst.html.ui/plugin.properties
+++ b/web/bundles/org.eclipse.wst.html.ui/plugin.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2005, 2011 IBM Corporation and others.
+# Copyright (c) 2005, 2019 IBM Corporation and others.
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License 2.0
 # which accompanies this distribution, and is available at
@@ -30,6 +30,7 @@
 HTML_Tag_context_type_Extension_Element.name=HTML Tag
 HTML_Attribute_context_type_Extension_Element.name=HTML Attribute
 HTML_Attribute_value_context_type_Extension_Element.name=HTML Attribute value
+proposalCategory.htmlAttributeValues=HTML Attribute value proposals
 ###############################################################################
 #org.eclipse.ui.newWizards extension point
 _UI_WIZARD_NAME = HTML File
diff --git a/web/bundles/org.eclipse.wst.html.ui/plugin.xml b/web/bundles/org.eclipse.wst.html.ui/plugin.xml
index fb54231..734fceb 100644
--- a/web/bundles/org.eclipse.wst.html.ui/plugin.xml
+++ b/web/bundles/org.eclipse.wst.html.ui/plugin.xml
@@ -656,6 +656,11 @@
           id="org.eclipse.wst.html.ui.proposalCategory.htmlTemplates"
           name="%proposalCategory.htmlTemplates">
     </proposalCategory>
+    <proposalCategory
+          icon="icons/full/obj16/attribute_obj.gif"
+          id="org.eclipse.wst.html.ui.proposalCategory.htmlAttributeValues"
+          name="%proposalCategory.htmlAttributeValues">
+    </proposalCategory>
     <proposalComputer
           activate="false"
           categoryId="org.eclipse.wst.html.ui.proposalCategory.htmlTags"
@@ -680,7 +685,55 @@
           </partitionType>
        </contentType>
     </proposalComputer>
-    <proposalComputerExtendedActivation
+    <proposalComputer
+          activate="false"
+          categoryId="org.eclipse.wst.html.ui.proposalCategory.htmlAttributeValues"
+          class="org.eclipse.wst.html.ui.internal.contentassist.resources.CSSWebResourcesCompletionProposalComputer"
+          id="org.eclipse.wst.html.ui.proposalComputer.html.resources.css">
+       <contentType
+             id="org.eclipse.wst.html.core.htmlsource">
+          <partitionType
+                id="org.eclipse.wst.html.HTML_DEFAULT">
+          </partitionType>
+       </contentType>
+    </proposalComputer>
+    <proposalComputer
+          activate="false"
+          categoryId="org.eclipse.wst.html.ui.proposalCategory.htmlAttributeValues"
+          class="org.eclipse.wst.html.ui.internal.contentassist.resources.ImageWebResourcesCompletionProposalComputer"
+          id="org.eclipse.wst.html.ui.proposalComputer.html.resources.image">
+       <contentType
+             id="org.eclipse.wst.html.core.htmlsource">
+          <partitionType
+                id="org.eclipse.wst.html.HTML_DEFAULT">
+          </partitionType>
+       </contentType>
+    </proposalComputer>
+    <proposalComputer
+          activate="false"
+          categoryId="org.eclipse.wst.html.ui.proposalCategory.htmlAttributeValues"
+          class="org.eclipse.wst.html.ui.internal.contentassist.resources.ScriptWebResourcesCompletionProposalComputer"
+          id="org.eclipse.wst.html.ui.proposalComputer.html.resources.script">
+       <contentType
+             id="org.eclipse.wst.html.core.htmlsource">
+          <partitionType
+                id="org.eclipse.wst.html.HTML_DEFAULT">
+          </partitionType>
+       </contentType>
+    </proposalComputer>
+    <proposalComputer
+          activate="false"
+          categoryId="org.eclipse.wst.html.ui.proposalCategory.htmlAttributeValues"
+          class="org.eclipse.wst.html.ui.internal.contentassist.resources.HrefWebResourcesCompletionProposalComputer"
+          id="org.eclipse.wst.html.ui.proposalComputer.html.resources.href">
+       <contentType
+             id="org.eclipse.wst.html.core.htmlsource">
+          <partitionType
+                id="org.eclipse.wst.html.HTML_DEFAULT">
+          </partitionType>
+       </contentType>
+    </proposalComputer>
+     <proposalComputerExtendedActivation
           id="org.eclipse.wst.css.ui.proposalComputer.css">
        <contentType
              id="org.eclipse.wst.html.core.htmlsource">
diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIMessages.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIMessages.java
index c38dca0..26956ba 100644
--- a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIMessages.java
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIMessages.java
@@ -1,5 +1,5 @@
 /**********************************************************************
- * Copyright (c) 2005, 2015 IBM Corporation and others. All rights reserved.   This
+ * Copyright (c) 2005, 2019 IBM Corporation and others. All rights reserved.   This
  * program and the accompanying materials are made available under the terms of
  * the Eclipse Public License 2.0 which accompanies this distribution, and is
  * available at https://www.eclipse.org/legal/epl-2.0/
@@ -229,4 +229,6 @@
 	public static String DoNotValidateAllAttributes;
 	public static String DoNotValidateAttributeAddInfo;
 	public static String DoNotValidateAllAttributesAddInfo;
+
+	public static String cannotGenerateImagePreview;
 }
diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIPluginResources.properties b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIPluginResources.properties
index 640f7fb..c7bd27b 100644
--- a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIPluginResources.properties
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIPluginResources.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2004, 2015 IBM Corporation and others.
+# Copyright (c) 2004, 2019 IBM Corporation and others.
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License 2.0
 # which accompanies this distribution, and is available at
@@ -198,4 +198,5 @@
 DoNotValidateAttribute=Ignore ''{0}'' attribute in validation
 DoNotValidateAllAttributes=Ignore all ''{0}'' attributes in validation
 DoNotValidateAttributeAddInfo=Adds the name of attribute to the list of ignored attributes for HTML Attribute Validator<br>You may edit the list of ignored attributes at <b>Web&#x2192;HTML Files&#x2192;Validation</b> Preference Page
-DoNotValidateAllAttributesAddInfo=Adds the pattern based on the attribute name to the list of ignored attributes for HTML Attribute Validator<br>You may edit the list of ignored attributes at <b>Web&#x2192;HTML Files&#x2192;Validation</b> Preference Page
\ No newline at end of file
+DoNotValidateAllAttributesAddInfo=Adds the pattern based on the attribute name to the list of ignored attributes for HTML Attribute Validator<br>You may edit the list of ignored attributes at <b>Web&#x2192;HTML Files&#x2192;Validation</b> Preference Page
+cannotGenerateImagePreview=Cannot generate image preview. Cause: {0}
diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/AbstractWebResourcesCompletionProposalComputer.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/AbstractWebResourcesCompletionProposalComputer.java
new file mode 100644
index 0000000..bc2ec32
--- /dev/null
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/AbstractWebResourcesCompletionProposalComputer.java
@@ -0,0 +1,131 @@
+/**

+ *  Copyright (c) 2013, 2019 Angelo ZERR and others

+ *  All rights reserved. This program and the accompanying materials

+ *  are made available under the terms of the Eclipse Public License 2.0

+ *  which accompanies this distribution, and is available at

+ *  https://www.eclipse.org/legal/epl-2.0/

+ *

+ *  SPDX-License-Identifier: EPL-2.0

+ *

+ *  Contributors:

+ *  Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation

+ *  Mickael Istria (Red Hat Inc.) - Extracted, refactored and moved to org.eclipse

+ *  Nitin Dahyabhai (IBM Corporation) - improve performance finding matching resources

+ */
+package org.eclipse.wst.html.ui.internal.contentassist.resources;

+

+import java.util.HashSet;

+import java.util.Set;

+

+import org.eclipse.core.resources.IFile;

+import org.eclipse.core.resources.IResource;

+import org.eclipse.core.resources.ResourcesPlugin;

+import org.eclipse.core.runtime.IPath;

+import org.eclipse.core.runtime.Path;

+import org.eclipse.core.runtime.jobs.Job;

+import org.eclipse.jface.text.contentassist.ICompletionProposal;

+import org.eclipse.swt.graphics.Image;

+import org.eclipse.ui.PlatformUI;

+import org.eclipse.wst.html.core.internal.validate.ModuleCoreSupport;

+import org.eclipse.wst.html.ui.internal.Logger;

+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;

+import org.eclipse.wst.sse.core.utils.StringUtils;

+import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext;

+import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal;

+import org.eclipse.wst.xml.core.internal.parser.regions.AttributeNameRegion;

+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;

+import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;

+import org.eclipse.wst.xml.ui.internal.contentassist.DefaultXMLCompletionProposalComputer;

+

+public abstract class AbstractWebResourcesCompletionProposalComputer extends DefaultXMLCompletionProposalComputer {

+

+	private Job completionComputerJob;

+	protected Set<Image> images = new HashSet<>();

+	

+

+	@Override

+	protected void addAttributeValueProposals(

+			final ContentAssistRequest contentAssistRequest,

+			final CompletionProposalInvocationContext context) {

+//		long time0 = System.currentTimeMillis();

+		IDOMNode element = (IDOMNode) contentAssistRequest.getNode();

+		final IPath referencePath = new Path(element.getModel().getBaseLocation());

+		if (referencePath.segmentCount() > 1) {

+			final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(referencePath);

+			if (file.exists() && matchRequest(contentAssistRequest)) {

+				String matchString = contentAssistRequest.getMatchString();

+				matchString = StringUtils.stripQuotes(matchString);

+				if (matchString.length() > 0 && (matchString.startsWith("\"") || matchString.startsWith("'"))) {

+					matchString = matchString.substring(1);

+				}

+				IPath runtimeReferencePath = ModuleCoreSupport.getRuntimePath(referencePath).removeLastSegments(1).makeAbsolute();

+//			this.completionComputerJob = new Job("Compute completion proposals") {

+//				@Override

+//				protected IStatus run(IProgressMonitor arg0) {

+				for (IPath path : findMatchingPaths(file)) {

+					if (!referencePath.equals(path)) {

+						String proposalText = null;

+						try {

+							IPath runtimeProposalPath = ModuleCoreSupport.getRuntimePath(path);

+							if (runtimeProposalPath != null && runtimeReferencePath != null) {

+								proposalText = runtimeProposalPath.makeRelativeTo(runtimeReferencePath).toString();

+							}

+							if (proposalText == null) {

+								proposalText = runtimeProposalPath.makeRelativeTo(referencePath.removeLastSegments(1)).toString();

+							}

+							if (proposalText.startsWith(matchString)) {

+								contentAssistRequest.addProposal(createCompletionProposal(contentAssistRequest, referencePath, runtimeReferencePath, path, runtimeProposalPath, proposalText));

+							}

+						}

+						catch (IllegalArgumentException ex) {

+							Logger.logException(ex);

+						}

+					}

+				}

+//					return Status.OK_STATUS;

+//				}

+//			};

+			}

+		}

+//		System.out.println("Generated proposals as " + getClass().getName() + " in " + (System.currentTimeMillis() - time0) + "ms.");

+	}

+

+	protected ICompletionProposal createCompletionProposal(ContentAssistRequest request, IPath referencePath, IPath runtimeReferencePath, IPath proposalPath, IPath runtimeProposalPath, String relativeProposal) {

+		String replacementString = '"' + relativeProposal + '"';

+		int cursorPosition = replacementString.length();

+		Image image = PlatformUI.getWorkbench().getEditorRegistry().getImageDescriptor(referencePath.lastSegment()).createImage();

+		if (image != null) {

+			this.images.add(image);

+		}

+		final int replacementLength = request.getRegion().getLength();

+		final int replacementOffset = request.getStartOffset();

+

+		return new CustomCompletionProposal(replacementString, replacementOffset, replacementLength, cursorPosition, image, relativeProposal, null, null, 0);

+	}

+

+	abstract protected IPath[] findMatchingPaths(IResource referenceResource);

+	abstract boolean matchRequest(ContentAssistRequest contentAssistRequest);

+

+	@Override

+	public void sessionEnded() {

+		if (this.completionComputerJob != null) {

+			this.completionComputerJob.cancel();

+		}

+		for (Image image : this.images) {

+			image.dispose();

+		}

+		super.sessionEnded();

+	}

+

+	protected String getCurrentAttributeName(ContentAssistRequest contentAssistRequest) {

+		String attributeName = null;

+		for (ITextRegion childRegion : contentAssistRequest.getDocumentRegion().getRegions().toArray()) {

+			if (childRegion instanceof AttributeNameRegion) {

+				attributeName = contentAssistRequest.getDocumentRegion().getText(childRegion);

+			} else if (childRegion.equals(contentAssistRequest.getRegion())) {

+				break;

+			}

+		}

+		return attributeName;

+	}

+}

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/CSSWebResourcesCompletionProposalComputer.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/CSSWebResourcesCompletionProposalComputer.java
new file mode 100644
index 0000000..bccbf8f
--- /dev/null
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/CSSWebResourcesCompletionProposalComputer.java
@@ -0,0 +1,64 @@
+/**

+ *  Copyright (c) 2013, 2019 Angelo ZERR and others

+ *  All rights reserved. This program and the accompanying materials

+ *  are made available under the terms of the Eclipse Public License 2.0

+ *  which accompanies this distribution, and is available at

+ *  https://www.eclipse.org/legal/epl-2.0/

+ *

+ *  SPDX-License-Identifier: EPL-2.0

+ *

+ *  Contributors:

+ *  Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation

+ *  Mickael Istria (Red Hat Inc.) - Extracted, refactored and moved to org.eclipse

+ *  Nitin Dahyabhai (IBM Corporation) - improve performance finding matching resources

+ */
+package org.eclipse.wst.html.ui.internal.contentassist.resources;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.eclipse.core.resources.IResource;

+import org.eclipse.core.resources.IResourceProxy;

+import org.eclipse.core.resources.IResourceProxyVisitor;

+import org.eclipse.core.runtime.CoreException;

+import org.eclipse.core.runtime.IPath;

+import org.eclipse.core.runtime.IStatus;

+import org.eclipse.core.runtime.Status;

+import org.eclipse.wst.css.core.internal.provisional.contenttype.ContentTypeIdForCSS;

+import org.eclipse.wst.html.ui.internal.HTMLUIPlugin;

+import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;

+import org.w3c.dom.Node;

+

+public class CSSWebResourcesCompletionProposalComputer extends

+		AbstractWebResourcesCompletionProposalComputer {

+	ContentTypeSpecs fileMatcher = ContentTypeSpecs.createFor(ContentTypeIdForCSS.ContentTypeID_CSS);

+

+	@Override

+	protected IPath[] findMatchingPaths(IResource referenceResource) {

+		final List<IPath> res = new ArrayList<>();

+		try {

+			referenceResource.getProject().accept(new IResourceProxyVisitor() {

+				@Override

+				public boolean visit(IResourceProxy proxy) throws CoreException {

+					if (proxy.getType() == IResource.FILE && fileMatcher.matches(proxy.getName())) {

+						res.add(proxy.requestFullPath());

+					}

+					return true;

+				}

+			}, 0);

+		} catch (CoreException ex) {

+			HTMLUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, HTMLUIPlugin.ID, ex.getMessage(), ex));

+		}

+		return res.toArray(new IPath[res.size()]);

+	}

+

+	@Override

+	boolean matchRequest(ContentAssistRequest contentAssistRequest) {

+		Node node = contentAssistRequest.getNode();

+		Node relAttribute = node.getAttributes().getNamedItem("rel");

+		return

+			"link".equals(node.getNodeName()) &&

+			"href".equals(getCurrentAttributeName(contentAssistRequest)) &&

+			(relAttribute == null || "stylesheet".equals(relAttribute.getNodeValue()));

+	}

+}

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ContentTypeSpecs.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ContentTypeSpecs.java
new file mode 100644
index 0000000..9c36472
--- /dev/null
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ContentTypeSpecs.java
@@ -0,0 +1,82 @@
+/*******************************************************************************

+ * Copyright (c) 2004, 2019 IBM Corporation and others.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License 2.0

+ * which accompanies this distribution, and is available at

+ * https://www.eclipse.org/legal/epl-2.0/

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

+ * Contributors:

+ *     IBM Corporation - initial API and implementation

+ *     

+ *******************************************************************************/
+package org.eclipse.wst.html.ui.internal.contentassist.resources;

+

+import java.util.Arrays;

+import java.util.HashSet;

+import java.util.Set;

+

+import org.eclipse.core.runtime.Platform;

+import org.eclipse.core.runtime.content.IContentType;

+

+/**

+ * Cache for the filenames and filename extensions registered for a content

+ * type and all of its descendants

+ */

+public class ContentTypeSpecs {

+	public static ContentTypeSpecs createFor(String contentTypeId) {

+//		long startTime = System.currentTimeMillis();

+		IContentType baseContentType = Platform.getContentTypeManager().getContentType(contentTypeId);

+		String[] baseExtensions = baseContentType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);

+		Arrays.sort(baseExtensions);

+		Set<String> filenameExtensions = new HashSet<>();

+		Set<String> filenames = new HashSet<>();

+		IContentType[] contentTypes = Platform.getContentTypeManager().getAllContentTypes();

+		for (int i = 0, length = contentTypes.length; i < length; i++) {

+			if (contentTypes[i].isKindOf(baseContentType)) {

+				String[] fileExtension = contentTypes[i].getFileSpecs(IContentType.FILE_EXTENSION_SPEC);

+				for (int j = 0; j < fileExtension.length; j++) {

+					filenameExtensions.add(fileExtension[j]);

+				}

+				String[] names = contentTypes[i].getFileSpecs(IContentType.FILE_NAME_SPEC);

+				for (int j = 0; j < names.length; j++) {

+					filenames.add(names[j]);

+				}

+			}

+		}

+		// move the extensions of the base type to the start to match quicker

+		String[] stringExtensions = filenameExtensions.toArray(new String[filenameExtensions.size()]);

+		for (int i = stringExtensions.length - 1; i > 0; i--) {

+			if (Arrays.binarySearch(baseExtensions, stringExtensions[i]) >= 0) {

+				stringExtensions[i] = stringExtensions[i - 1];

+			}

+		}

+//		System.out.println("Discovered content types for " + contentTypeId + " in " + (System.currentTimeMillis() - startTime) + "ms");

+		return new ContentTypeSpecs(filenames.toArray(new String[filenames.size()]), stringExtensions);

+	}

+

+	String[] fFilenames = new String[0];

+	String[] fFilenameExtensions = new String[0];

+

+	private ContentTypeSpecs(String[] fileNames, String[] extensions) {

+		super();

+		fFilenames = fileNames;

+		fFilenameExtensions = extensions;

+		Arrays.sort(fileNames);

+	}

+

+	public boolean matches(String filename) {

+		if (Arrays.binarySearch(fFilenames, filename) >= 0) {

+			return true;

+		}

+		for (int i = 0; i < fFilenameExtensions.length; i++) {

+			if (filename.length() > fFilenameExtensions[i].length() + 1

+					&& filename.charAt(filename.length() - fFilenameExtensions[i].length() - 1) == '.'

+					&& filename.endsWith(fFilenameExtensions[i])) {

+				return true;

+			}

+		}

+		return false;

+	}

+}

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/HrefWebResourcesCompletionProposalComputer.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/HrefWebResourcesCompletionProposalComputer.java
new file mode 100644
index 0000000..6874339
--- /dev/null
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/HrefWebResourcesCompletionProposalComputer.java
@@ -0,0 +1,62 @@
+/**

+ *  Copyright (c) 2013, 2019 Angelo ZERR and others

+ *  All rights reserved. This program and the accompanying materials

+ *  are made available under the terms of the Eclipse Public License 2.0

+ *  which accompanies this distribution, and is available at

+ *  https://www.eclipse.org/legal/epl-2.0/

+ *

+ *  SPDX-License-Identifier: EPL-2.0

+ *

+ *  Contributors:

+ *  Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation

+ *  Mickael Istria (Red Hat Inc.) - Extracted, refactored and moved to org.eclipse

+ *  Nitin Dahyabhai (IBM Corporation) - improve performance finding matching resources

+ */
+package org.eclipse.wst.html.ui.internal.contentassist.resources;

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Locale;

+

+import org.eclipse.core.resources.IResource;

+import org.eclipse.core.resources.IResourceProxy;

+import org.eclipse.core.resources.IResourceProxyVisitor;

+import org.eclipse.core.runtime.CoreException;

+import org.eclipse.core.runtime.IPath;

+import org.eclipse.core.runtime.IStatus;

+import org.eclipse.core.runtime.Status;

+import org.eclipse.core.runtime.content.IContentTypeManager;

+import org.eclipse.wst.html.ui.internal.HTMLUIPlugin;

+import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;

+import org.w3c.dom.Node;

+

+public class HrefWebResourcesCompletionProposalComputer extends AbstractWebResourcesCompletionProposalComputer {

+

+	ContentTypeSpecs fileMatcher = ContentTypeSpecs.createFor(IContentTypeManager.CT_TEXT);

+

+	@Override

+	protected IPath[] findMatchingPaths(IResource referenceResource) {

+		final List<IPath> res = new ArrayList<>();

+		try {

+			referenceResource.getProject().accept(new IResourceProxyVisitor() {

+				@Override

+				public boolean visit(IResourceProxy proxy) throws CoreException {

+					if (proxy.getType() == IResource.FILE && (proxy.getName().endsWith(".txt") || fileMatcher.matches(proxy.getName()))) {

+						res.add(proxy.requestFullPath());

+					}

+					return true;

+				}

+			}, 0);

+		}

+		catch (CoreException ex) {

+			HTMLUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, HTMLUIPlugin.ID, ex.getMessage(), ex));

+		}

+		return res.toArray(new IPath[res.size()]);

+	}

+

+	@Override

+	boolean matchRequest(ContentAssistRequest contentAssistRequest) {

+		Node node = contentAssistRequest.getNode();

+		return "a".equals(node.getLocalName().toLowerCase(Locale.US)) && "href".equals(getCurrentAttributeName(contentAssistRequest).toLowerCase(Locale.US));

+	}

+}

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ImageCompletionProposal.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ImageCompletionProposal.java
new file mode 100644
index 0000000..e412024
--- /dev/null
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ImageCompletionProposal.java
@@ -0,0 +1,61 @@
+/**

+ *  Copyright (c) 2016, 2019 Red Hat Inc. and others

+ *  All rights reserved. This program and the accompanying materials

+ *  are made available under the terms of the Eclipse Public License 2.0

+ *  which accompanies this distribution, and is available at

+ *  https://www.eclipse.org/legal/epl-2.0/

+ *

+ *  SPDX-License-Identifier: EPL-2.0

+ *

+ *  Contributors:

+ *  Mickael Istria (Red Hat Inc.)

+ */
+package org.eclipse.wst.html.ui.internal.contentassist.resources;

+

+import java.net.URL;

+

+import org.eclipse.jface.internal.text.html.BrowserInformationControl;

+import org.eclipse.jface.resource.JFaceResources;

+import org.eclipse.jface.text.AbstractReusableInformationControlCreator;

+import org.eclipse.jface.text.IDocument;

+import org.eclipse.jface.text.IInformationControl;

+import org.eclipse.jface.text.IInformationControlCreator;

+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;

+import org.eclipse.swt.graphics.Image;

+import org.eclipse.swt.widgets.Shell;

+import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal;

+

+public class ImageCompletionProposal extends CustomCompletionProposal implements ICompletionProposalExtension3 {

+

+	private AbstractReusableInformationControlCreator controlCreator;

+

+	public ImageCompletionProposal(String replacementString, int replacementOffset, int replacementLength,

+			int cursorPosition, Image image, String resourcePath, URL previewUrl, int i) {

+		super(replacementString, replacementOffset, replacementLength, cursorPosition, image, resourcePath, null, "<img src='" + previewUrl.toString() +"'/>", i);

+		

+	}

+

+	@Override

+	public IInformationControlCreator getInformationControlCreator() {

+		if (this.controlCreator == null) {

+			this.controlCreator = new AbstractReusableInformationControlCreator() {

+				@Override

+				protected IInformationControl doCreateInformationControl(Shell parent) {

+					return new BrowserInformationControl(parent, JFaceResources.DIALOG_FONT, false);

+				}

+			};

+		}

+		return this.controlCreator;

+	}

+

+	@Override

+	public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {

+		return null;

+	}

+

+	@Override

+	public int getPrefixCompletionStart(IDocument document, int completionOffset) {

+		return 0;

+	}

+

+}

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ImageWebResourcesCompletionProposalComputer.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ImageWebResourcesCompletionProposalComputer.java
new file mode 100644
index 0000000..78c6147
--- /dev/null
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ImageWebResourcesCompletionProposalComputer.java
@@ -0,0 +1,94 @@
+/**

+ *  Copyright (c) 2013, 2019 Angelo ZERR and others

+ *  All rights reserved. This program and the accompanying materials

+ *  are made available under the terms of the Eclipse Public License 2.0

+ *  which accompanies this distribution, and is available at

+ *  https://www.eclipse.org/legal/epl-2.0/

+ *

+ *  SPDX-License-Identifier: EPL-2.0

+ *

+ *  Contributors:

+ *  Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation

+ *  Mickael Istria (Red Hat Inc.) - Extracted, refactored and moved to org.eclipse

+ *  Nitin Dahyabhai (IBM Corporation) - improve performance finding matching resources

+ */
+package org.eclipse.wst.html.ui.internal.contentassist.resources;

+

+import java.net.URL;

+import java.util.ArrayList;

+import java.util.List;

+

+import org.eclipse.core.resources.IResource;

+import org.eclipse.core.resources.IResourceProxy;

+import org.eclipse.core.resources.IResourceProxyVisitor;

+import org.eclipse.core.runtime.CoreException;

+import org.eclipse.core.runtime.IPath;

+import org.eclipse.core.runtime.IStatus;

+import org.eclipse.core.runtime.Status;

+import org.eclipse.jface.text.contentassist.ICompletionProposal;

+import org.eclipse.osgi.util.NLS;

+import org.eclipse.swt.graphics.Image;

+import org.eclipse.ui.PlatformUI;

+import org.eclipse.wst.html.ui.internal.HTMLUIMessages;

+import org.eclipse.wst.html.ui.internal.HTMLUIPlugin;

+import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal;

+import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;

+import org.w3c.dom.Node;

+

+public class ImageWebResourcesCompletionProposalComputer extends

+		AbstractWebResourcesCompletionProposalComputer {

+

+	ContentTypeSpecs fileMatcher = ContentTypeSpecs.createFor("org.eclipse.ui.content-type.images");

+

+	@Override

+	protected ICompletionProposal createCompletionProposal(ContentAssistRequest request, IPath referencePath, IPath runtimeReferencePath, IPath proposalPath, IPath runtimeProposalPath, String relativeProposal) {

+		String replacementString = '"' + relativeProposal + '"';

+		int cursorPosition = replacementString.length();

+		Image image = PlatformUI.getWorkbench().getEditorRegistry().getImageDescriptor(proposalPath.lastSegment()).createImage();

+		if (image != null) {

+			super.images.add(image);

+		}

+		final int replacementLength = request.getRegion().getLength();

+		final int replacementOffset = request.getStartOffset();

+		URL previewURL = null;

+		String previewErrorInfo = null;

+		try {

+			previewURL =  new URL("platform://resource/" + proposalPath);

+		} catch (Exception ex) {

+			previewErrorInfo = NLS.bind(HTMLUIMessages.cannotGenerateImagePreview, ex.getMessage());

+		}

+		

+		if (previewURL != null) {

+			return new ImageCompletionProposal(replacementString, replacementOffset, replacementLength, cursorPosition, image, relativeProposal, previewURL, 0);

+		} else {

+			return new CustomCompletionProposal(replacementString, replacementOffset, replacementLength, cursorPosition, image, relativeProposal, null, previewErrorInfo, 0);

+		}

+	}

+	

+	@Override

+	protected IPath[] findMatchingPaths(IResource referenceResource) {

+		final List<IPath> res = new ArrayList<>();

+		try {

+			referenceResource.getProject().accept(new IResourceProxyVisitor() {

+				@Override

+				public boolean visit(IResourceProxy proxy) throws CoreException {

+					if (proxy.getType() == IResource.FILE && fileMatcher.matches(proxy.getName())) {

+						res.add(proxy.requestFullPath());

+					}

+					return true;

+				}

+			}, 0);

+		} catch (CoreException ex) {

+			HTMLUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, HTMLUIPlugin.ID, ex.getMessage(), ex));

+		}

+		return res.toArray(new IPath[res.size()]);

+	}

+

+	@Override

+	boolean matchRequest(ContentAssistRequest contentAssistRequest) {

+		Node node = contentAssistRequest.getNode();

+		return

+			"img".equals(node.getLocalName()) &&

+			"src".equals(getCurrentAttributeName(contentAssistRequest));

+	}

+}

diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ScriptWebResourcesCompletionProposalComputer.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ScriptWebResourcesCompletionProposalComputer.java
new file mode 100644
index 0000000..eca7f6b
--- /dev/null
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentassist/resources/ScriptWebResourcesCompletionProposalComputer.java
@@ -0,0 +1,63 @@
+/**

+ *  Copyright (c) 2013, 2019 Angelo ZERR and others

+ *  All rights reserved. This program and the accompanying materials

+ *  are made available under the terms of the Eclipse Public License 2.0

+ *  which accompanies this distribution, and is available at

+ *  https://www.eclipse.org/legal/epl-2.0/

+ *

+ *  SPDX-License-Identifier: EPL-2.0

+ *

+ *  Contributors:

+ *  Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation

+ *  Mickael Istria (Red Hat Inc.) - Extracted, refactored and moved to org.eclipse

+ *  Nitin Dahyabhai (IBM Corporation) - improve performance finding matching resources

+ */
+package org.eclipse.wst.html.ui.internal.contentassist.resources;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.eclipse.core.resources.IResource;

+import org.eclipse.core.resources.IResourceProxy;

+import org.eclipse.core.resources.IResourceProxyVisitor;

+import org.eclipse.core.runtime.CoreException;

+import org.eclipse.core.runtime.IPath;

+import org.eclipse.core.runtime.IStatus;

+import org.eclipse.core.runtime.Status;

+import org.eclipse.wst.html.ui.internal.HTMLUIPlugin;

+import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;

+import org.w3c.dom.Node;

+

+public class ScriptWebResourcesCompletionProposalComputer extends

+		AbstractWebResourcesCompletionProposalComputer {

+

+	ContentTypeSpecs fileMatcher = ContentTypeSpecs.createFor("org.eclipse.wst.jsdt.core.jsSource");

+

+	@Override

+	protected IPath[] findMatchingPaths(IResource referenceResource) {

+		final List<IPath> res = new ArrayList<>();

+		try {

+			referenceResource.getProject().accept(new IResourceProxyVisitor() {

+				@Override

+				public boolean visit(IResourceProxy proxy) throws CoreException {

+					if (proxy.getType() == IResource.FILE && fileMatcher.matches(proxy.getName())) {

+						res.add(proxy.requestFullPath());

+					}

+					return true;

+				}

+			}, 0);

+		} catch (CoreException ex) {

+			HTMLUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, HTMLUIPlugin.ID, ex.getMessage(), ex));

+		}

+		return res.toArray(new IPath[res.size()]);

+	}

+

+	@Override

+	boolean matchRequest(ContentAssistRequest contentAssistRequest) {

+		Node node = contentAssistRequest.getNode();

+		return

+			"script".equals(node.getLocalName()) &&

+			"src".equals(getCurrentAttributeName(contentAssistRequest));

+	}

+

+}

diff --git a/web/tests/org.eclipse.wst.html.ui.tests/META-INF/MANIFEST.MF b/web/tests/org.eclipse.wst.html.ui.tests/META-INF/MANIFEST.MF
index e76a7c3..081b186 100644
--- a/web/tests/org.eclipse.wst.html.ui.tests/META-INF/MANIFEST.MF
+++ b/web/tests/org.eclipse.wst.html.ui.tests/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name.0
 Bundle-SymbolicName: org.eclipse.wst.html.ui.tests; singleton:=true
-Bundle-Version: 1.1.0.qualifier
+Bundle-Version: 1.1.100.qualifier
 Bundle-Activator: org.eclipse.wst.html.ui.tests.HTMLUITestsPlugin
 Bundle-Vendor: %Bundle-Vendor.0
 Bundle-Localization: plugin
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/pom.xml b/web/tests/org.eclipse.wst.html.ui.tests/pom.xml
index 48e4d7d..5836e38 100644
--- a/web/tests/org.eclipse.wst.html.ui.tests/pom.xml
+++ b/web/tests/org.eclipse.wst.html.ui.tests/pom.xml
@@ -21,7 +21,7 @@
 
   <groupId>org.eclipse.webtools.sourceediting</groupId>
   <artifactId>org.eclipse.wst.html.ui.tests</artifactId>
-  <version>1.1.0-SNAPSHOT</version>
+  <version>1.1.100-SNAPSHOT</version>
   <packaging>eclipse-test-plugin</packaging>
 
   <properties>
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/src/org/eclipse/wst/html/ui/tests/contentassist/TestHTMLContentAssistComputers.java b/web/tests/org.eclipse.wst.html.ui.tests/src/org/eclipse/wst/html/ui/tests/contentassist/TestHTMLContentAssistComputers.java
index a65cce6..b416b2d 100644
--- a/web/tests/org.eclipse.wst.html.ui.tests/src/org/eclipse/wst/html/ui/tests/contentassist/TestHTMLContentAssistComputers.java
+++ b/web/tests/org.eclipse.wst.html.ui.tests/src/org/eclipse/wst/html/ui/tests/contentassist/TestHTMLContentAssistComputers.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010, 2018 IBM Corporation and others.
+ * Copyright (c) 2010, 2019 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -18,15 +18,8 @@
 import java.util.Iterator;
 import java.util.Map;
 
-import junit.extensions.TestSetup;
-import junit.framework.Assert;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
-import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.contentassist.ContentAssistant;
 import org.eclipse.jface.text.contentassist.ICompletionProposal;
@@ -43,6 +36,12 @@
 import org.eclipse.wst.sse.ui.StructuredTextEditor;
 import org.eclipse.wst.sse.ui.internal.StructuredTextViewer;
 
+import junit.extensions.TestSetup;
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
 public class TestHTMLContentAssistComputers extends TestCase {
 	/** The name of the project that all of these tests will use */
 	private static final String PROJECT_NAME = "TestHTMLContentAssistComputers";
@@ -98,37 +97,37 @@
 	
 	public void testEmptyDocument() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {7, 6, 1, 7};
+		int[] expectedProposalCounts = new int[] {7, 6, 1, 0, 7};
 		runProposalTest("test0.html", 0, 0, expectedProposalCounts);
 	}
 	
 	public void testAfterDocTypeBeforeHTMLTagProposals() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {8, 8, 0, 8};
+		int[] expectedProposalCounts = new int[] {8, 8, 0, 0, 8};
 		runProposalTest("test1.html", 1, 0, expectedProposalCounts);
 	}
 	
 	public void testAfterDocTypeBeforeEmptyDocProposals() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {9, 8, 1, 9};
+		int[] expectedProposalCounts = new int[] {9, 8, 1, 0, 9};
 		runProposalTest("test2.html", 1, 0, expectedProposalCounts);
 	}
 	
 	public void testBodyTagChildElementProposals() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {78, 8, 70, 78};
+		int[] expectedProposalCounts = new int[] {78, 8, 70, 0, 78};
 		runProposalTest("test1.html", 8, 0, expectedProposalCounts);
 	}
 	
 	public void testPTagChildElementProposals() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {54, 8, 46, 54};
+		int[] expectedProposalCounts = new int[] {54, 8, 46, 0, 54};
 		runProposalTest("test1.html", 14, 0, expectedProposalCounts);
 	}
 	
 	public void testDIVTagChildElementProposals() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {78, 8, 70, 78};
+		int[] expectedProposalCounts = new int[] {78, 8, 70, 0, 78};
 		runProposalTest("test1.html", 18, 0, expectedProposalCounts);
 	}
 	
@@ -140,33 +139,64 @@
 	
 	public void testDIVTagAttributeNameProposals() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {18, 0, 18, 18};
+		int[] expectedProposalCounts = new int[] {18, 0, 18, 0, 18};
 		runProposalTest("test1.html", 17, 5, expectedProposalCounts);
 	}
 	
-	public void testFinishClosingTagNamePropsoals() throws Exception {
+	public void testFinishClosingTagNameProposals() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {1, 0, 1, 1};
+		int[] expectedProposalCounts = new int[] {1, 0, 1, 0, 1};
 		runProposalTest("test4.html", 9, 9, expectedProposalCounts);
 	}
 	
-	public void testFinishClosingTagPropsoals() throws Exception {
+	public void testFinishClosingTagProposals() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {2, 0, 2, 2};
+		int[] expectedProposalCounts = new int[] {2, 0, 2, 0, 2};
 		runProposalTest("test4.html", 10, 0, expectedProposalCounts);
 	}
 	
-	public void testFinishClosingTagNamePropsoalsXHTML() throws Exception {
+	public void testFinishClosingTagNameProposalsXHTML() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {1, 0, 1, 1};
+		int[] expectedProposalCounts = new int[] {1, 0, 1, 0, 1};
 		runProposalTest("test5.xhtml", 9, 9, expectedProposalCounts);
 	}
 	
-	public void testFinishClosingTagPropsoalsXHTML() throws Exception {
+	public void testFinishClosingTagProposalsXHTML() throws Exception {
 		// default page, templates page, tags page, default page again
-		int[] expectedProposalCounts = new int[] {2, 0, 2, 2};
+		int[] expectedProposalCounts = new int[] {1, 0, 2, 0, 2};
 		runProposalTest("test5.xhtml", 10, 0, expectedProposalCounts);
 	}
+
+	public void testResourceProposalsForAHref() throws Exception {
+		// default page, templates page, tags page, default page again
+		int[] expectedProposalCounts = new int[]{1, 0, 0, 0, 1};
+		ICompletionProposal[][] proposals = runProposalTest("testResources.html", 13, 19, expectedProposalCounts);
+		assertEquals("the expected text file proposals", "targets/empty.css", proposals[0][0].getDisplayString());
+		assertEquals("the expected text file proposals", "targets/empty.js", proposals[0][1].getDisplayString());
+		assertEquals("the expected text file proposals", "targets/empty.txt", proposals[0][2].getDisplayString());
+	}
+	
+	public void testResourceProposalsForImgSrc() throws Exception {
+		// default page, templates page, tags page, default page again
+		int[] expectedProposalCounts = new int[]{1, 0, 0, 0, 1};
+		ICompletionProposal[][] proposals = runProposalTest("testResources.html", 13, 52, expectedProposalCounts);
+		assertEquals("the expected graphics file proposals", "targets/empty.gif", proposals[0][0].getDisplayString());
+		assertEquals("the expected graphics file proposals", "targets/empty.png", proposals[0][1].getDisplayString());
+	}
+	
+	public void testResourceProposalsForLinkHref() throws Exception {
+		// default page, templates page, tags page, default page again
+		int[] expectedProposalCounts = new int[]{1, 0, 0, 0, 1};
+		ICompletionProposal[][] proposals = runProposalTest("testResources.html", 5, 22, expectedProposalCounts);
+		assertEquals("the expected CSS file proposals", "targets/empty.css", proposals[0][0].getDisplayString());
+	}
+	
+	public void testResourceProposalsForScriptSrc() throws Exception {
+		// default page, templates page, tags page, default page again
+		int[] expectedProposalCounts = new int[]{1, 0, 0, 0, 1};
+		ICompletionProposal[][] proposals = runProposalTest("testResources.html", 10, 46, expectedProposalCounts);
+		assertEquals("the expected JS file proposals", "targets/empty.js", proposals[0][0].getDisplayString());
+	}
 	
 	/**
 	 * <p>Run a proposal test by opening the given file and invoking content assist for
@@ -180,7 +210,7 @@
 	 * @param expectedProposalCounts
 	 * @throws Exception
 	 */
-	private static void runProposalTest(String fileName,
+	private static ICompletionProposal[][] runProposalTest(String fileName,
 			int lineNum, int lineRelativeCharOffset,
 			int[] expectedProposalCounts) throws Exception{
 		
@@ -192,6 +222,8 @@
 		ICompletionProposal[][] pages = getProposals(viewer, offset, expectedProposalCounts.length);
 		
 		verifyProposalCounts(pages, expectedProposalCounts);
+		
+		return pages;
 	}
 	
 	/**
@@ -384,7 +416,7 @@
 			}
 			
 			//remove project
-			fProject.delete(true, new NullProgressMonitor());
+//			fProject.delete(true, new NullProgressMonitor());
 			
 			//restore properties
 			if (previousWTPAutoTestNonInteractivePropValue != null) {
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.css b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.css
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.js b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.js
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.png b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.png
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.txt b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/alsoempty.txt
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.css b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.css
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.gif b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.gif
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.gif
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.js b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.js
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.png b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.png
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.txt b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/targets/empty.txt
diff --git a/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/testResources.html b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/testResources.html
new file mode 100644
index 0000000..208d17a
--- /dev/null
+++ b/web/tests/org.eclipse.wst.html.ui.tests/testresources/contentassist/testResources.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<link href="targets/e" rel="stylesheet" type="text/css">
+<title>Insert title here</title>
+</head>
+<body>
+
+<script type="text/javascript" src="targets/e"></script>
+
+<p>
+<a href="targets/e" id="tester"><img src="targets/e" alt="just for testing"/></a>
+</p>
+
+<div >
+
+</div>
+
+<!-- 
+
+ -->
+
+</body>
+</html>
\ No newline at end of file