blob: 72190dfa3e41ae9d91db0b77abc0eb8ab77d4d3e [file] [log] [blame]
/**
* 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.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
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.HTMLUIPlugin;
import org.eclipse.wst.html.ui.internal.Logger;
import org.eclipse.wst.html.ui.internal.wizard.FacetModuleCoreSupport;
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().getTextLength();
final int replacementOffset = request.getStartOffset();
return new CustomCompletionProposal(replacementString, replacementOffset, replacementLength, cursorPosition, image, relativeProposal, null, proposalPath.toString(), 0);
}
protected IPath[] findMatchingPaths(IResource referenceResource) {
ContentTypeSpecs fileMatcher = createFilenameMatcher();
final List<IPath> res = new ArrayList<>();
IWorkspaceRoot root = referenceResource.getWorkspace().getRoot();
IPath referencePath = referenceResource.getFullPath();
IPath[] roots = FacetModuleCoreSupport.getAcceptableRootPaths(referenceResource.getProject());
/*
* If editing a file not within one of the roots, offer everything
* project-wide. The deployment information is either wrong or the
* user is intentionally editing something that won't be deployed, and
* they know better than us.
*/
boolean referencePathWithinValidRoot = false;
for (int i = 0; i < roots.length; i++) {
referencePathWithinValidRoot |= roots[i].isPrefixOf(referencePath);
}
if (!referencePathWithinValidRoot) {
roots = new IPath[]{referenceResource.getProject().getFullPath()};
}
for (int i = 0; i < roots.length; i++) {
try {
root.findMember(roots[i]).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 !proxy.isDerived();
}
}, IResource.NONE);
}
catch (CoreException ex) {
HTMLUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, HTMLUIPlugin.ID, ex.getMessage(), ex));
}
}
return res.toArray(new IPath[res.size()]);
}
abstract boolean matchRequest(ContentAssistRequest contentAssistRequest);
abstract ContentTypeSpecs createFilenameMatcher();
@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;
}
}