package net.lucky_dip.sasseditor.editor.contentassist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import net.lucky_dip.hamleditor.HTMLCSSKeywords;
import net.lucky_dip.sasseditor.editor.SassEditor;
import net.lucky_dip.sasseditor.editor.scanners.SassPartitionScanner;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPartitioningException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PlatformUI;
import com.aptana.ide.core.StringUtils;
import com.aptana.ide.editor.css.CSSLanguageEnvironment;
import com.aptana.ide.editor.html.HTMLLanguageEnvironment;
import com.aptana.ide.metadata.IMetadataEnvironment;
public class SassContentAssistantProcessor implements IContentAssistProcessor
{
private String getEditorContentsPath()
{
IEditorInput pathEditor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
.getActiveEditor().getEditorInput();
if (pathEditor instanceof IFileEditorInput)
{
IFileEditorInput fileEI = (IFileEditorInput) pathEditor;
return fileEI.getFile().getProject().getFullPath().toPortableString();
}
return null;
}
private String getPrefix(ITextViewer viewer, int offset) throws BadLocationException
{
IDocument doc = viewer.getDocument();
if (doc == null || offset > doc.getLength())
return null;
int length = 0;
while (--offset >= 0
&& (Character.isJavaIdentifierPart(doc.getChar(offset)) || doc.getChar(offset) == '.'
|| doc.getChar(offset) == '#' || doc.getChar(offset) == '!' || doc.getChar(offset) == '-'))
length++;
return doc.get(offset + 1, length);
}
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset)
{
IDocument doc = viewer.getDocument();
List<CompletionProposal> res = new ArrayList<CompletionProposal>();
try
{
ITypedRegion region = null;
if (doc instanceof IDocumentExtension3)
region = ((IDocumentExtension3) doc).getPartition(SassEditor.SASS_PARTITIONING, offset > 0 ? offset - 1
: 0, false);
String prefix = getPrefix(viewer, offset);
if (prefix != null && prefix.startsWith("."))
{
res.addAll(getCSSClassCompletions(offset, prefix));
}
else if (prefix != null && prefix.startsWith("#"))
{
// FIXME We may be completing a color hex value! This happens if preceding text is "color: " or '!var_name = '
res.addAll(getHTMLIDCompletions(offset, prefix));
}
else if (region != null && region.getType().equals(SassPartitionScanner.SASS_ATTRIBUTE))
{
res.addAll(getCSSPropertyCompletions(offset, prefix));
}
else if (region != null && region.getType().equals(SassPartitionScanner.SASS_CONSTANT))
{
res.addAll(getSassVariableCompletions(doc, offset, prefix));
}
else
{
// We may be trying to complete an html tag, or a CSS psuedo class, or a CSS attribute name!
// FIXME See if we can narrow this down any by indentation!
// FIXME This also may be completing the value for a CSS property here (variables or actual normal values)!
res.addAll(getHTMLTagCompletions(offset, prefix));
res.addAll(getCSSPropertyCompletions(offset, prefix));
res.addAll(getCSSPsuedoClassCompletions(offset, prefix));
}
}
catch (Exception e)
{
e.printStackTrace();
}
return (ICompletionProposal[]) res.toArray(new ICompletionProposal[res.size()]);
}
private Collection<? extends CompletionProposal> getCSSPsuedoClassCompletions(int offset, String prefix)
{
Collection<CompletionProposal> res = new ArrayList<CompletionProposal>();
for (String pseudoClass : HTMLCSSKeywords.CSS_PSEUDO_CLASSES)
{
if (!pseudoClass.startsWith(prefix))
continue;
CompletionProposal cp = new CompletionProposal(pseudoClass, offset - prefix.length(), prefix.length(),
pseudoClass.length());
res.add(cp);
}
return res;
}
private Collection<CompletionProposal> getCSSPropertyCompletions(int offset, String prefix)
{
Collection<CompletionProposal> res = new ArrayList<CompletionProposal>();
List<String> matches = new ArrayList<String>(HTMLCSSKeywords.getCssAttributeMatches(prefix, offset));
Collections.sort(matches);
for (String match : matches)
{
// Add colons and space after (but not in display string)
String replacementString = match + ": ";
CompletionProposal cp = new CompletionProposal(replacementString, offset - prefix.length(),
prefix.length(), replacementString.length(), null, match, null, null);
res.add(cp);
}
return res;
}
private Collection<CompletionProposal> getHTMLTagCompletions(int offset, String prefix)
{
// FIXME Hooking up to the CSS environment, we get back 40 tags, whereas hooking up to HTML we get 105!
Collection<CompletionProposal> completionProposals = new ArrayList<CompletionProposal>();
IMetadataEnvironment environment = (IMetadataEnvironment) HTMLLanguageEnvironment.getInstance()
.getRuntimeEnvironment();
String[] em = environment.getAllElements();
Arrays.sort(em);
for (String e : em)
{
if (e.equalsIgnoreCase("!doctype") || !e.startsWith(prefix))
continue;
String replaceString = e;
Image image = null;
String displayString = replaceString;
String additionalPropsalInfo = environment.getElementDocumentation(e);
completionProposals.add(new CompletionProposal(replaceString, offset - prefix.length(), prefix.length(),
replaceString.length(), image, displayString, null, additionalPropsalInfo));
}
return completionProposals;
// return HTMLCSSKeywords.getHtmlTagMatches(prefix, offset, region);
}
private Collection<? extends CompletionProposal> getSassVariableCompletions(IDocument doc, int offset, String prefix)
throws BadLocationException, BadPartitioningException
{
List<CompletionProposal> res = new ArrayList<CompletionProposal>();
ITypedRegion[] regions = null;
if (doc instanceof IDocumentExtension3)
regions = ((IDocumentExtension3) doc).computePartitioning(SassEditor.SASS_PARTITIONING, 0, doc.getLength(),
false);
String[] variableNames = getConstants(doc, regions);
for (String cname : variableNames)
{
String replacementString = "!" + cname;
if (replacementString.equals(prefix) || !replacementString.startsWith(prefix))
continue;
String displayString = cname;
CompletionProposal cp = new CompletionProposal(replacementString, offset - prefix.length(),
prefix.length(), replacementString.length(), null, displayString, null, null);
res.add(cp);
}
return res;
}
private Collection<CompletionProposal> getHTMLIDCompletions(int offset, String prefix)
{
List<CompletionProposal> res = new ArrayList<CompletionProposal>();
Collection<String> ids = CSSLanguageEnvironment.getInstance().getIds(getEditorContentsPath(), "");
if (ids != null)
{
for (String e : ids)
{
if (StringUtils.EMPTY.equals(e))
continue;
String trimmedValue = "#" + StringUtils.trimStringQuotes(e);
CompletionProposal cp = new CompletionProposal(trimmedValue, offset - prefix.length(), prefix.length(),
trimmedValue.length());
res.add(cp);
}
}
return res;
}
private Collection<CompletionProposal> getCSSClassCompletions(int offset, String prefix)
{
List<CompletionProposal> res = new ArrayList<CompletionProposal>();
Collection<String> classes = CSSLanguageEnvironment.getInstance().getClasses(getEditorContentsPath(), "");
if (classes != null)
{
for (String e : classes)
{
if (StringUtils.EMPTY.equals(e))
continue;
String trimmedValue = "." + StringUtils.trimStringQuotes(e);
CompletionProposal cp = new CompletionProposal(trimmedValue, offset - prefix.length(), prefix.length(),
trimmedValue.length());
res.add(cp);
}
}
return res;
}
private String[] getConstants(IDocument document, ITypedRegion[] regions)
{
if (regions == null)
return new String[0];
List<String> res = new ArrayList<String>();
for (int i = 0; i < regions.length; i++)
{
if (regions[i].getType().equals(SassPartitionScanner.SASS_CONSTANT))
{
try
{
int offset = regions[i].getOffset() + 1;
int length = regions[i].getLength() - 1;
if (length > 0)
{
String constant = document.get(offset, length);
if (!res.contains(constant))
{
res.add(constant);
}
}
}
catch (BadLocationException e)
{
e.printStackTrace();
}
}
}
Collections.sort(res);
return res.toArray(new String[res.size()]);
}
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset)
{
return null;
}
public char[] getCompletionProposalAutoActivationCharacters()
{
return null;
}
public char[] getContextInformationAutoActivationCharacters()
{
return null;
}
public IContextInformationValidator getContextInformationValidator()
{
return null;
}
public String getErrorMessage()
{
return null;
}
}