[nobug] add quickfix for removing attributes, do some cleanup
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 26956ba..3a601df 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, 2019 IBM Corporation and others. All rights reserved.   This
+ * Copyright (c) 2005, 2020 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/
@@ -73,6 +73,7 @@
 	public static String AddBlockComment_tooltip; // resource bundle
 	public static String AddBlockComment_description; // resource bundle
 	public static String Remove_inline;
+	public static String RemoveAttributes;
 	public static String RemoveBlockComment_label; // resource bundle
 	public static String RemoveBlockComment_tooltip; // resource bundle
 	public static String RemoveBlockComment_description; // resource bundle
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 c7bd27b..18fa139 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, 2019 IBM Corporation and others.
+# Copyright (c) 2004, 2020 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
@@ -200,3 +200,4 @@
 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
 cannotGenerateImagePreview=Cannot generate image preview. Cause: {0}
+RemoveAttributes=Remove attributes
diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/editor/HTMLEditorPluginImages.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/editor/HTMLEditorPluginImages.java
index 63b767f..46151c0 100644
--- a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/editor/HTMLEditorPluginImages.java
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/editor/HTMLEditorPluginImages.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2013 IBM Corporation and others.
+ * Copyright (c) 2004, 2020 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 @@
 	public static final String IMG_OBJ_TAG_TEMPLATE = "icons/full/obj16/tag-template.gif";			//$NON-NLS-1$
 	public static final String IMG_OBJ_TAG_TITLE = "icons/full/obj16/tag-title.gif";			//$NON-NLS-1$
 	public static final String IMG_OBJ_TAG = "icons/full/obj16/tag.gif";			//$NON-NLS-1$
+	public static final String IMG_OBJ_ATTRIBUTE = "icons/full/obj16/attribute_obj.gif"; //$NON-NLS-1$
 	public static final String IMG_WIZBAN_NEWHTMLFILE = "icons/full/wizban/newhfile_wiz.png";			//$NON-NLS-1$
 	public static final String IMG_DTOOL_DO_NOT_VALIDATE = "icons/full/etool16/donotvalidate.png";			//$NON-NLS-1$
 }
\ No newline at end of file
diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/HTMLSyntaxValidationQuickFixProcessor.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/HTMLSyntaxValidationQuickFixProcessor.java
index 1774986..cdf08ef 100644
--- a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/HTMLSyntaxValidationQuickFixProcessor.java
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/HTMLSyntaxValidationQuickFixProcessor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015 IBM Corporation and others.
+ * Copyright (c) 2014, 2020 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
@@ -42,9 +42,11 @@
 import org.eclipse.wst.html.core.internal.preferences.HTMLCorePreferenceNames;
 import org.eclipse.wst.html.core.internal.validate.StringMatcher;
 import org.eclipse.wst.html.ui.internal.HTMLUIMessages;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
 import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils;
 import org.eclipse.wst.sse.ui.internal.reconcile.TemporaryAnnotation;
 import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
 import org.w3c.dom.Element;
 
 public class HTMLSyntaxValidationQuickFixProcessor implements IQuickAssistProcessor {
@@ -113,11 +115,11 @@
 		if (model == null)
 			return null;
 
-		List proposals = new ArrayList();
+		List<ICompletionProposal> proposals = new ArrayList<>();
 		if (model instanceof IAnnotationModelExtension2) {
-			Iterator iter = ((IAnnotationModelExtension2) model).getAnnotationIterator(documentOffset, length, true, true);
+			Iterator<Annotation> iter = ((IAnnotationModelExtension2) model).getAnnotationIterator(documentOffset, length, true, true);
 			while (iter.hasNext()) {
-				Annotation anno = (Annotation) iter.next();
+				Annotation anno = iter.next();
 				if (canFix(anno)) {
 					int offset = -1;
 					
@@ -133,15 +135,21 @@
 					if (!(node instanceof Element))
 						continue;
 
-					Object adapter = (node instanceof IAdaptable ? ((IAdaptable)node).getAdapter(IResource.class) : null);
-					IProject project = (adapter instanceof IResource ? ((IResource)adapter).getProject() : null);
+					// dangling attributes
+					IStructuredDocumentRegion documentRegion = ContentAssistUtils.getStructuredDocumentRegion(viewer, documentOffset);
+					if (documentRegion != null && documentRegion.getNumberOfRegions() > 2 && DOMRegionContext.XML_END_TAG_OPEN.equals(documentRegion.getFirstRegion().getType())) {
+						proposals.add(new RemoveAttributesProposal(documentRegion, HTMLUIMessages.RemoveAttributes));
+					}
 
-					IScopeContext[] fLookupOrder = new IScopeContext[] {new InstanceScope(), new DefaultScope()};
+					Object adapter = (node instanceof IAdaptable ? ((IAdaptable) node).getAdapter(IResource.class) : null);
+					IProject project = (adapter instanceof IResource ? ((IResource) adapter).getProject() : null);
+
+					IScopeContext[] fLookupOrder = new IScopeContext[] {InstanceScope.INSTANCE, DefaultScope.INSTANCE};
 					if (project != null) {
 						ProjectScope projectScope = new ProjectScope(project);
 						if(projectScope.getNode(getPreferenceNodeQualifier())
 								.getBoolean(getProjectSettingsKey(), false))
-							fLookupOrder = new IScopeContext[] {projectScope, new InstanceScope(), new DefaultScope()};
+							fLookupOrder = new IScopeContext[] {projectScope, InstanceScope.INSTANCE, DefaultScope.INSTANCE};
 					}
 					
 					boolean ignore = fPreferenceService.getBoolean(
@@ -152,7 +160,7 @@
 							getPreferenceNodeQualifier(), HTMLCorePreferenceNames.ELEMENT_NAMES_TO_IGNORE, 
 							HTMLCorePreferenceNames.ELEMENT_NAMES_TO_IGNORE_DEFAULT, fLookupOrder);
 
-					Set result = new HashSet();
+					Set<String> result = new HashSet<>();
 					if (ignoreList.trim().length() > 0) {
 						String[] names = ignoreList.split(","); //$NON-NLS-1$
 						for (int i = 0; names != null && i < names.length; i++) {
@@ -198,7 +206,7 @@
 		if (proposals.isEmpty())
 			return null;
 
-		return (ICompletionProposal[]) proposals.toArray(new ICompletionProposal[proposals.size()]);
+		return proposals.toArray(new ICompletionProposal[proposals.size()]);
 
 	}
 
@@ -206,9 +214,9 @@
 		return node.getNodeName();
 	}
 	
-	private boolean shouldShowQuickFix(Set lcIgnoredPatterns, String attrName) {
+	private boolean shouldShowQuickFix(Set<String> lcIgnoredPatterns, String attrName) {
 		// Check the attribute name absence in ignore list
-		String [] lcPatterns = (String[])lcIgnoredPatterns.toArray(new String[0]);
+		String [] lcPatterns = lcIgnoredPatterns.toArray(new String[0]);
 		for (int i = 0; i < lcPatterns.length; i++) {
 			StringMatcher strMatcher = new StringMatcher(lcPatterns[i]);
 			if (strMatcher.match(attrName.toLowerCase())) {
diff --git a/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/RemoveAttributesProposal.java b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/RemoveAttributesProposal.java
new file mode 100644
index 0000000..8ee7aab
--- /dev/null
+++ b/web/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/RemoveAttributesProposal.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2020 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.text.correction;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.wst.html.ui.internal.Logger;
+import org.eclipse.wst.html.ui.internal.editor.HTMLEditorPluginImageHelper;
+import org.eclipse.wst.html.ui.internal.editor.HTMLEditorPluginImages;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+
+public class RemoveAttributesProposal implements ICompletionProposal {
+
+	private IStructuredDocumentRegion fTag;
+	private String fDisplayString;
+
+	public RemoveAttributesProposal(IStructuredDocumentRegion tag, String displayString) {
+		fTag = tag;
+		fDisplayString = displayString;
+	}
+
+	boolean isEndType(String regionType) {
+		return regionType.equals(DOMRegionContext.XML_TAG_CLOSE) || regionType.equals(DOMRegionContext.XML_EMPTY_TAG_CLOSE);
+	}
+
+	/*
+	 * @see ICompletionProposal#apply(IDocument)
+	 */
+	public void apply(IDocument document) {
+		ITextRegionList regions = fTag.getRegions();
+		if (fTag.getNumberOfRegions() > 2) {
+			int removalStart = fTag.getTextEndOffset(regions.get(1));
+			int removalEnd = fTag.getStartOffset(fTag.getLastRegion());
+			if (!isEndType(fTag.getLastRegion().getType())) {
+				removalEnd = removalStart;
+			}
+			try {
+				document.replace(removalStart, removalEnd - removalStart, "");
+			}
+			catch (BadLocationException e) {
+				Logger.logException(e);
+			}
+		}
+	}
+
+	/*
+	 * @see ICompletionProposal#getDisplayString()
+	 */
+	public String getDisplayString() {
+		return fDisplayString;
+	}
+
+	/*
+	 * @see ICompletionProposal#getAdditionalProposalInfo()
+	 */
+	public String getAdditionalProposalInfo() {
+		return null;
+	}
+
+	/*
+	 * @see ICompletionProposal#getImage()
+	 */
+	public Image getImage() {
+		return HTMLEditorPluginImageHelper.getInstance().getImage(HTMLEditorPluginImages.IMG_OBJ_ATTRIBUTE);
+	}
+
+	/*
+	 * @see ICompletionProposal#getSelection(IDocument)
+	 */
+	public Point getSelection(IDocument document) {
+		return new Point(fTag.getEndOffset(fTag.getRegions().get(1)), 0);
+	}
+
+	/*
+	 * @see ICompletionProposal#getContextInformation()
+	 */
+	public IContextInformation getContextInformation() {
+		return null;
+	}
+
+	/*
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	public boolean equals(Object obj) {
+		if (obj instanceof RemoveAttributesProposal) {
+			RemoveAttributesProposal p = (RemoveAttributesProposal) obj;
+			return this.fTag.equals(p.fTag);
+		}
+		return false;
+	}
+}