blob: 744c3c499ec42471caac168d82b2f8da50b6b0c7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2011 David Green and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* David Green - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.wikitext.ui.editor.assist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.templates.ContextTypeRegistry;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.jface.text.templates.TemplateCompletionProcessor;
import org.eclipse.jface.text.templates.TemplateContext;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.jface.text.templates.TemplateException;
import org.eclipse.jface.text.templates.TemplateProposal;
import org.eclipse.jface.text.templates.persistence.TemplateStore;
import org.eclipse.mylyn.internal.wikitext.ui.WikiTextUiPlugin;
import org.eclipse.mylyn.wikitext.parser.markup.MarkupLanguage;
import org.eclipse.swt.graphics.Image;
/**
* @author David Green
*/
public class MarkupTemplateCompletionProcessor extends TemplateCompletionProcessor {
public static final String CONTEXT_ID = "org.eclipse.mylyn.wikitext.ui.editor"; //$NON-NLS-1$
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{([^\\}]+)\\}"); //$NON-NLS-1$
private static final class ProposalComparator implements Comparator<ICompletionProposal> {
public int compare(ICompletionProposal o1, ICompletionProposal o2) {
return ((TemplateProposal) o2).getRelevance() - ((TemplateProposal) o1).getRelevance();
}
}
private static final Comparator<ICompletionProposal> proposalComparator = new ProposalComparator();
private static final Template[] NO_TEMPLATES = new Template[0];
private final SourceTemplateContextType contextType;
private Templates templates;
public MarkupTemplateCompletionProcessor() {
ContextTypeRegistry contextTypeRegistry = WikiTextTemplateAccess.getInstance().getContextTypeRegistry();
contextType = (SourceTemplateContextType) contextTypeRegistry.getContextType(SourceTemplateContextType.ID);
if (contextType == null) {
throw new IllegalStateException();
}
}
@Override
protected TemplateContextType getContextType(ITextViewer viewer, IRegion region) {
return contextType;
}
@Override
protected Image getImage(Template template) {
return null;
}
@Override
protected Template[] getTemplates(String contextTypeId) {
if (contextType.getId().equals(contextTypeId)) {
Template[] computedTemplates = null;
if (templates != null) {
computedTemplates = templates.getTemplate().toArray(new Template[templates.getTemplate().size()]);
}
TemplateStore templateStore = WikiTextTemplateAccess.getInstance().getTemplateStore();
if (templateStore != null) {
Template[] customTemplates = templateStore.getTemplates(contextTypeId);
if (customTemplates != null && customTemplates.length > 0) {
if (computedTemplates == null) {
computedTemplates = customTemplates;
} else {
List<Template> allTemplates = new ArrayList<>(
computedTemplates.length + customTemplates.length);
allTemplates.addAll(Arrays.asList(computedTemplates));
allTemplates.addAll(Arrays.asList(customTemplates));
computedTemplates = allTemplates.toArray(new Template[allTemplates.size()]);
}
}
}
if (computedTemplates != null) {
return computedTemplates;
}
}
return NO_TEMPLATES;
}
/**
* Override to improve matching accuracy
*/
@Override
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection();
// adjust offset to end of normalized selection
if (selection.getOffset() == offset) {
offset = selection.getOffset() + selection.getLength();
}
String prefix = extractPrefix(viewer, offset);
Region region = new Region(offset - prefix.length(), prefix.length());
TemplateContext context = createContext(viewer, region);
if (context == null) {
return new ICompletionProposal[0];
}
Region selectionRegion = new Region(selection.getOffset(), selection.getLength());
TemplateContext selectionContext = createContext(viewer, selectionRegion);
int lineOffset = 0;
try {
IRegion lineInformationOfOffset = viewer.getDocument().getLineInformationOfOffset(offset);
lineOffset = offset - lineInformationOfOffset.getOffset();
} catch (BadLocationException e1) {
// ignore
}
String selectionText = selection.getText();
context.setVariable("selection", selectionText); //$NON-NLS-1$
selectionContext.setVariable("selection", selectionText); //$NON-NLS-1$
context.setVariable("text", selectionText); //$NON-NLS-1$
selectionContext.setVariable("text", selectionText); //$NON-NLS-1$
Template[] templates = getTemplates(context.getContextType().getId());
List<ICompletionProposal> matches = new ArrayList<>(templates.length);
for (Template template : templates) {
try {
context.getContextType().validate(template.getPattern());
} catch (TemplateException e) {
continue;
}
if (!template.matches(prefix, context.getContextType().getId())) {
continue;
}
boolean selectionBasedMatch = isSelectionBasedMatch(template, context);
if (template.getName().startsWith(prefix) || selectionBasedMatch) {
int relevance = getRelevance(template, lineOffset, prefix);
if (selectionBasedMatch) {
matches.add(createProposal(template, selectionContext, (IRegion) selectionRegion, relevance));
} else {
matches.add(createProposal(template, context, (IRegion) region, relevance));
}
}
}
Collections.sort(matches, proposalComparator);
return matches.toArray(new ICompletionProposal[matches.size()]);
}
private int getRelevance(Template template, int lineOffset, String prefix) {
boolean blockTemplate = templates == null ? false : templates.isBlock(template);
if (blockTemplate) {
if (template.getName().startsWith(prefix)) {
return lineOffset == 0 ? 95 : 75;
}
return lineOffset == 0 ? 85 : 0;
}
return super.getRelevance(template, prefix);
}
private boolean isSelectionBasedMatch(Template template, TemplateContext context) {
String pattern = template.getPattern();
Set<String> vars = new HashSet<>();
Matcher matcher = VARIABLE_PATTERN.matcher(pattern);
while (matcher.find()) {
String variableName = matcher.group(1);
if (vars.add(variableName)) {
String variable = context.getVariable(variableName);
if (variable != null && variable.length() > 0) {
return true;
}
}
}
return false;
}
public void setMarkupLanguage(MarkupLanguage markupLanguage) {
templates = markupLanguage == null
? null
: WikiTextUiPlugin.getDefault().getTemplates().get(markupLanguage.getName());
}
}