/*******************************************************************************
* Copyright (c) 2014-2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.editor.support.completions;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
import org.eclipse.jface.text.contentassist.ICompletionProposalSorter;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.StyledString.Styler;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.TextStyle;
import org.springframework.ide.eclipse.editor.support.EditorSupportActivator;
import org.springframework.ide.eclipse.editor.support.hover.HoverInfo;
import org.springframework.ide.eclipse.editor.support.hover.HoverInformationControlCreator;
import org.springframework.ide.eclipse.editor.support.hover.YPropertyHoverInfo;
import org.springframework.ide.eclipse.editor.support.util.ColorManager;
import org.springframework.ide.eclipse.editor.support.yaml.completions.AbstractPropertyProposal;
import org.springframework.ide.eclipse.editor.support.yaml.schema.YType;
import org.springframework.ide.eclipse.editor.support.yaml.schema.YTypeUtil;
import org.springframework.ide.eclipse.editor.support.yaml.schema.YTypedProperty;
import org.springframework.util.StringUtils;
/**
* @author Kris De Volder
*/
public class CompletionFactory {
public static final Styler HIGHLIGHT = new Styler() {
public void applyStyles(TextStyle textStyle) {
textStyle.foreground = ColorManager.getInstance().getColor(ColorManager.CYAN);
};
};
public static final Styler DEPRECATE = new Styler() {
public void applyStyles(TextStyle textStyle) {
textStyle.strikeout = true;
};
};
public static final Styler DEEMPHASIZE = new Styler() {
public void applyStyles(TextStyle textStyle) {
textStyle.foreground = ColorManager.getInstance().getColor(ColorManager.GREY);
};
};
public static final Styler NULL_STYLER = new Styler() {
public void applyStyles(TextStyle textStyle) {
};
};
public static Styler compose(final Styler s1, final Styler s2) {
if (s1==NULL_STYLER) {
return s2;
} else if (s2==NULL_STYLER) {
return s1;
} else {
return new Styler() {
@Override
public void applyStyles(TextStyle textStyle) {
s1.applyStyles(textStyle);
s2.applyStyles(textStyle);
}
};
}
}
public static final CompletionFactory DEFAULT = new CompletionFactory();
public ScoreableProposal simpleProposal(String name, String pattern, int sortingOrder, ProposalApplier applier, HoverInfo info) {
return simpleProposal(name, pattern, -(1.0+sortingOrder), applier, info);
}
public ScoreableProposal simpleProposal(String name, String pattern, double score, ProposalApplier applier, HoverInfo info) {
return new SimpleProposal(name, pattern, score, applier, info);
}
public ScoreableProposal simpleProposal(String name, String pattern, String label, double score, ProposalApplier applier, HoverInfo info) {
return new SimpleProposal(name, pattern, label, score, applier, info);
}
public static abstract class ScoreableProposal implements ICompletionProposal, ICompletionProposalExtension3, ICompletionProposalExtension4, ICompletionProposalExtension5, ICompletionProposalExtension6 {
private static final double DEEMP_VALUE = 100000; // should be large enough to move deemphasized stuff to bottom of list.
private double deemphasizedBy = 0.0;
public abstract double getBaseScore();
public final double getScore() {
return getBaseScore() - deemphasizedBy;
}
public ScoreableProposal deemphasize() {
deemphasizedBy+= DEEMP_VALUE;
return this;
}
public boolean isDeemphasized() {
return deemphasizedBy > 0;
}
@Override
public boolean isAutoInsertable() {
return !isDeemphasized();
}
public StyledString getStyledDisplayString() {
StyledString result = new StyledString();
highlightPattern(getHighlightPattern(), getBaseDisplayString(), result);
return result;
}
private void highlightPattern(String pattern, String data, StyledString result) {
Styler highlightStyle = CompletionFactory.HIGHLIGHT;
Styler plainStyle = isDeemphasized()?CompletionFactory.DEEMPHASIZE:CompletionFactory.NULL_STYLER;
if (isDeprecated()) {
highlightStyle = CompletionFactory.compose(highlightStyle, CompletionFactory.DEPRECATE);
plainStyle = CompletionFactory.compose(plainStyle, CompletionFactory.DEPRECATE);
}
if (StringUtils.hasText(pattern)) {
int dataPos = 0; int dataLen = data.length();
int patternPos = 0; int patternLen = pattern.length();
while (dataPos<dataLen && patternPos<patternLen) {
int pChar = pattern.charAt(patternPos++);
int highlightPos = data.indexOf(pChar, dataPos);
if (dataPos<highlightPos) {
result.append(data.substring(dataPos, highlightPos), plainStyle);
}
result.append(data.charAt(highlightPos), highlightStyle);
dataPos = highlightPos+1;
}
if (dataPos<dataLen) {
result.append(data.substring(dataPos), plainStyle);
}
} else { //no pattern to highlight
result.append(data, plainStyle);
}
}
protected abstract boolean isDeprecated();
protected abstract String getHighlightPattern();
protected abstract String getBaseDisplayString();
@Override
public IInformationControlCreator getInformationControlCreator() {
return new HoverInformationControlCreator("F2 for focus");
}
@Override
public String getAdditionalProposalInfo() {
HoverInfo hoverInfo = getAdditionalProposalInfo(new NullProgressMonitor());
if (hoverInfo!=null) {
return hoverInfo.getHtml();
}
return null;
}
@Override
public abstract HoverInfo getAdditionalProposalInfo(IProgressMonitor monitor);
@Override
public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {
return null;
}
@Override
public int getPrefixCompletionStart(IDocument document, int completionOffset) {
return completionOffset;
}
}
private static class SimpleProposal extends ScoreableProposal {
private String value;
private String label;
private ProposalApplier applier;
private double score;
private String pattern;
private HoverInfo hoverInfo;
public SimpleProposal(String value, String pattern, double score, ProposalApplier applier, HoverInfo info) {
this(value, pattern, value, score, applier, info);
}
public SimpleProposal(String value, String pattern, String label, double score, ProposalApplier applier, HoverInfo info) {
this.score = score;
this.value = value;
this.applier = applier;
this.pattern = pattern;
this.hoverInfo = info;
this.label = label;
}
@Override
public void apply(IDocument doc) {
try {
applier.apply(doc);
} catch (Exception e) {
EditorSupportActivator.log(e);
}
}
@Override
public Point getSelection(IDocument doc) {
try {
return applier.getSelection(doc);
} catch (Exception e) {
EditorSupportActivator.log(e);
}
return null;
}
@Override
public HoverInfo getAdditionalProposalInfo(IProgressMonitor monitor) {
return hoverInfo;
}
@Override
public String getDisplayString() {
return label.toString();
}
@Override
public Image getImage() {
return null;
}
@Override
public IContextInformation getContextInformation() {
return null;
}
@Override
public double getBaseScore() {
return score;
}
@Override
protected boolean isDeprecated() {
return false;
}
@Override
protected String getHighlightPattern() {
return pattern;
}
@Override
protected String getBaseDisplayString() {
return label.toString();
}
}
/**
* A sorter suitable for sorting proposals created by this factory
*/
public static final ICompletionProposalSorter SORTER = new ICompletionProposalSorter() {
public int compare(ICompletionProposal p1, ICompletionProposal p2) {
if (p1 instanceof ScoreableProposal && p2 instanceof ScoreableProposal) {
double s1 = ((ScoreableProposal)p1).getScore();
double s2 = ((ScoreableProposal)p2).getScore();
if (s1==s2) {
String name1 = ((ScoreableProposal)p1).getDisplayString();
String name2 = ((ScoreableProposal)p2).getDisplayString();
return name1.compareTo(name2);
} else {
return Double.compare(s2, s1);
}
}
return 0;
}
};
public ScoreableProposal beanProperty(IDocument doc, final String contextProperty, final YType contextType, final String pattern, final YTypedProperty p, final double score, ProposalApplier applier, final YTypeUtil typeUtil) {
return new AbstractPropertyProposal(doc, applier) {
@Override
public double getBaseScore() {
return score;
}
@Override
protected String niceTypeName(YType type) {
return typeUtil.niceTypeName(type);
}
@Override
protected YType getType() {
return p.getType();
}
@Override
protected String getHighlightPattern() {
return pattern;
}
@Override
protected String getBaseDisplayString() {
return p.getName();
}
@Override
public HoverInfo getAdditionalProposalInfo(IProgressMonitor monitor) {
return new YPropertyHoverInfo(contextProperty, contextType, p);
}
};
}
public ScoreableProposal valueProposal(String value, String pattern, YType yType, double score, ProposalApplier applier, HoverInfo info) {
return simpleProposal(value, pattern, score, applier, info);
}
public ScoreableProposal valueProposal(String value, String pattern, String label, YType yType, double score, ProposalApplier applier, HoverInfo info) {
return simpleProposal(value, pattern, label, score, applier, info);
}
public ScoreableProposal valueProposal(String value, String pattern, YType type, int order, ProposalApplier applier, HoverInfo info) {
return valueProposal(value, pattern, type, -(1.0+order), applier, info);
}
}