X-Git-Url: http://secure.phpeclipse.com
diff --git a/archive/net.sourceforge.phpeclipse.css.ui/src/net/sourceforge/phpeclipse/css/ui/internal/text/CssContentAssistProcessor.java b/archive/net.sourceforge.phpeclipse.css.ui/src/net/sourceforge/phpeclipse/css/ui/internal/text/CssContentAssistProcessor.java
new file mode 100644
index 0000000..c3a712e
--- /dev/null
+++ b/archive/net.sourceforge.phpeclipse.css.ui/src/net/sourceforge/phpeclipse/css/ui/internal/text/CssContentAssistProcessor.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2003-2004 Christopher Lenz and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * Christopher Lenz - initial API and implementation
+ *
+ * $Id: CssContentAssistProcessor.java,v 1.1 2004-09-02 18:11:48 jsurfer Exp $
+ */
+
+package net.sourceforge.phpeclipse.css.ui.internal.text;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import net.sourceforge.phpeclipse.core.model.ISourceReference;
+import net.sourceforge.phpeclipse.css.core.internal.text.CssTextUtils;
+import net.sourceforge.phpeclipse.css.core.model.IAtRule;
+import net.sourceforge.phpeclipse.css.core.model.IDeclaration;
+import net.sourceforge.phpeclipse.css.core.model.IPropertyInfo;
+import net.sourceforge.phpeclipse.css.core.model.IRule;
+import net.sourceforge.phpeclipse.css.core.model.IStyleRule;
+import net.sourceforge.phpeclipse.css.core.model.IStyleSheet;
+import net.sourceforge.phpeclipse.css.core.profiles.IProfile;
+import net.sourceforge.phpeclipse.css.ui.CssUI;
+import net.sourceforge.phpeclipse.css.ui.internal.CssDocumentProvider;
+import net.sourceforge.phpeclipse.css.ui.internal.CssUIPreferences;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+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.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Content assist processor for the CSS editor.
+ *
+ * TODO If a completion proposal is requested before the style sheet has been
+ * reconciled, we might not get any proposals, or they might be false.
+ */
+public class CssContentAssistProcessor implements IContentAssistProcessor {
+
+ // Constants ---------------------------------------------------------------
+
+ private static final String AUTOACTIVATION_TRIGGERS =
+ CssUIPreferences.CONTENTASSIST_AUTOACTIVATION_TRIGGERS;
+
+ private static final String ORDER_PROPOSALS =
+ CssUIPreferences.CONTENTASSIST_ORDER_PROPOSALS;
+
+ // Instance Variables ------------------------------------------------------
+
+ /**
+ * The preference store.
+ */
+ private IPreferenceStore store;
+
+ /**
+ * The current CSS profile.
+ */
+ private IProfile profile;
+
+ /**
+ * The associated text editor, if any.
+ */
+ private ITextEditor editor;
+
+ // Constructors ------------------------------------------------------------
+
+ /**
+ * Constructor.
+ *
+ * @param profile The CSS profile to use
+ */
+ public CssContentAssistProcessor(IPreferenceStore store, IProfile profile,
+ ITextEditor editor) {
+ this.store = store;
+ this.profile = profile;
+ this.editor = editor;
+ }
+
+ // IContentAssistProcessor -------------------------------------------------
+
+ /*
+ * @see IContentAssistProcessor#computeCompletionProposals
+ */
+ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer,
+ int documentOffset) {
+ List retVal = Collections.EMPTY_LIST;
+ IDocument document = viewer.getDocument();
+ try {
+ String prefix = getPrefix(document, documentOffset);
+ if (prefix.startsWith("@")) { //$NON-NLS-1$
+ retVal = proposeAtKeywords(document, documentOffset,
+ prefix.substring(1));
+ } else if (prefix.startsWith(":")) { //$NON-NLS-1$
+ retVal = proposePseudoClasses(document, documentOffset,
+ prefix.substring(1));
+ } else {
+ retVal = proposeProperties(document, documentOffset, prefix);
+ }
+ if ((store != null) && store.getBoolean(ORDER_PROPOSALS)) {
+ sortProposals(retVal);
+ }
+ } catch (BadLocationException e) {
+ CssUI.log(
+ "Unable to compute completion proposals", e); //$NON-NLS-1$
+ }
+ return (ICompletionProposal[]) retVal.toArray(
+ new ICompletionProposal[retVal.size()]);
+ }
+
+ /*
+ * @see IContentAssistProcessor#computeContextInformation
+ */
+ public IContextInformation[] computeContextInformation(ITextViewer viewer,
+ int documentOffset) {
+ return new IContextInformation[0];
+ }
+
+ /*
+ * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters
+ */
+ public char[] getCompletionProposalAutoActivationCharacters() {
+ String chars = store.getString(AUTOACTIVATION_TRIGGERS);
+ if (chars == null) {
+ return null;
+ }
+ return chars.toCharArray();
+ }
+
+ /*
+ * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters
+ */
+ public char[] getContextInformationAutoActivationCharacters() {
+ return null;
+ }
+
+ /*
+ * @see IContentAssistProcessor#getErrorMessage
+ */
+ public String getErrorMessage() {
+ return null;
+ }
+
+ /*
+ * @see IContentAssistProcessor#getContextInformationValidator
+ */
+ public IContextInformationValidator getContextInformationValidator() {
+ return null;
+ }
+
+ // Private Methods ---------------------------------------------------------
+
+ /**
+ * Returns the part of the word immediately before the position at which
+ * content assist was requested. The prefix is lower-cased before it is
+ * returned, to enable case-insensitive matching.
+ *
+ * @param document the document
+ * @param offset the offset into the document
+ * @return the prefix
+ */
+ private String getPrefix(IDocument document, int offset) {
+ try {
+ int startPos = offset;
+ while (startPos > 0) {
+ char c = document.getChar(startPos - 1);
+ if (!CssTextUtils.isCssIdentifierPart(c)
+ && (c != '@') && (c != ':')) {
+ break;
+ }
+ startPos--;
+ if ((c == '@') || (c == ':')) {
+ break;
+ }
+ }
+ if (startPos < offset) {
+ return document.get(startPos, offset - startPos).toLowerCase();
+ }
+ } catch (BadLocationException e) {
+ e.printStackTrace();
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the parsed model of the document loaded in the editor, or
+ * null
if the editor hasn't been set or the model couldn't be
+ * retrieved.
+ *
+ * @return the parsed model
+ */
+ private IStyleSheet getStyleSheet() {
+ if (editor != null) {
+ IDocumentProvider provider = editor.getDocumentProvider();
+ if (provider instanceof CssDocumentProvider) {
+ return ((CssDocumentProvider) provider).getStyleSheet(
+ editor.getEditorInput());
+ }
+ }
+ return null;
+ }
+
+ public boolean isAtKeyword(IDocument document, int offset)
+ throws BadLocationException {
+ IStyleSheet styleSheet = getStyleSheet();
+ if (styleSheet != null) {
+ IRule rule = styleSheet.getRuleAt(offset);
+ if (rule instanceof IAtRule) {
+ ISourceReference name = ((IAtRule) rule).getName();
+ if (name != null) {
+ IRegion region = name.getSourceRegion();
+ for (int j = region.getOffset(); j < offset; j++) {
+ char ch = document.getChar(j);
+ if (!CssTextUtils.isCssIdentifierPart(ch)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ } else if (rule == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether the document contains a property at the specified
+ * offset, or at least whether a property is theoretically allowed at that
+ * offset.
+ *
+ * @param document the document
+ * @param offset the offset into the document
+ * @return true
if the specified offset is a legal position for
+ * a property, false
otherwise
+ * @throws BadLocationException if there was a problem accessing the
+ * document
+ */
+ public boolean isProperty(IDocument document, int offset)
+ throws BadLocationException {
+ IStyleSheet styleSheet = getStyleSheet();
+ if (styleSheet != null) {
+ IRule rule = styleSheet.getRuleAt(offset);
+ if (rule != null) {
+ IDeclaration declaration = rule.getDeclarationAt(offset);
+ if (declaration != null) {
+ IRegion region = declaration.getSourceRegion();
+ for (int j = region.getOffset(); j < offset; j++) {
+ if (document.getChar(j) == ':') {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ IRegion region = rule.getSourceRegion();
+ for (int j = region.getOffset(); j < offset; j++) {
+ if (document.getChar(j) == '{') {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether the document contains a selector at the specified
+ * offset, or at least whether a selector is theoretically allowed at that
+ * offset.
+ */
+ public boolean isSelector(IDocument document, int offset)
+ throws BadLocationException {
+ IStyleSheet styleSheet = getStyleSheet();
+ if (styleSheet != null) {
+ IRule rule = styleSheet.getRuleAt(offset);
+ if (rule instanceof IStyleRule) {
+ ISourceReference selector = ((IStyleRule) rule).getSelector();
+ if (selector != null) {
+ IRegion region = selector.getSourceRegion();
+ for (int j = region.getOffset(); j < offset; j++) {
+ if (document.getChar(j) == '{') {
+ return false;
+ }
+ }
+ return true;
+ }
+ } else if (rule == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private List proposeAtKeywords(IDocument document, int offset,
+ String prefix) throws BadLocationException {
+ List proposals = new ArrayList();
+ if (isAtKeyword(document, offset)) {
+ Image icon = CssUI.getDefault().getImageRegistry().get(
+ CssUI.ICON_AT_RULE);
+ Collection atRuleNames = profile.getAtKeywords();
+ for (Iterator i = atRuleNames.iterator(); i.hasNext(); ) {
+ String atRuleName = (String) i.next();
+ if (atRuleName.startsWith(prefix)) {
+ ICompletionProposal proposal = new CompletionProposal(
+ atRuleName, offset - prefix.length(), prefix.length(),
+ atRuleName.length(), icon, atRuleName, null, null);
+ proposals.add(proposal);
+ }
+ }
+ }
+ return proposals;
+ }
+
+ /**
+ * Computes the completion proposals for properties. A property may only
+ * appear at the left-hand side of a declaration, so we check whether the
+ * document offset for which the proposals were requested is a legal
+ * position for a property, and only then compute the actual proposals.
+ *
+ * @param document the document
+ * @param offset the offset into the document at which completion was
+ * requested
+ * @param prefix the string immediately before the offset
+ * @return the list of {@link ICompletionProposal}s
+ */
+ private List proposeProperties(IDocument document, int offset,
+ String prefix) throws BadLocationException {
+ List proposals = new ArrayList();
+ if (isProperty(document, offset - 1)) {
+ Image propertyIcon =
+ CssUI.getDefault().getImageRegistry().get(
+ CssUI.ICON_PROPERTY);
+ Image shorthandIcon =
+ CssUI.getDefault().getImageRegistry().get(
+ CssUI.ICON_SHORTHAND);
+ Collection propertyNames = profile.getProperties();
+ for (Iterator i = propertyNames.iterator(); i.hasNext(); ) {
+ String propertyName = (String) i.next();
+ if (propertyName.startsWith(prefix)) {
+ Image icon = propertyIcon;
+ IPropertyInfo info = profile.getPropertyInfo(propertyName);
+ if (info.isShorthand()) {
+ icon = shorthandIcon;
+ }
+ ICompletionProposal proposal = new CompletionProposal(
+ propertyName, offset - prefix.length(),
+ prefix.length(), propertyName.length(), icon,
+ propertyName, null, info.getDescription());
+ proposals.add(proposal);
+ }
+ }
+ }
+ return proposals;
+ }
+
+ private List proposePseudoClasses(IDocument document, int offset,
+ String prefix) throws BadLocationException {
+ List proposals = new ArrayList();
+ if (isSelector(document, offset - 1)) {
+ Image icon = CssUI.getDefault().getImageRegistry().get(
+ CssUI.ICON_PSEUDO_CLASS);
+ Collection pseudoClassNames = profile.getPseudoClassNames();
+ for (Iterator i = pseudoClassNames.iterator(); i.hasNext(); ) {
+ String pseudoClassName = (String) i.next();
+ if (pseudoClassName.startsWith(prefix)) {
+ ICompletionProposal proposal = new CompletionProposal(
+ pseudoClassName, offset - prefix.length(),
+ prefix.length(), pseudoClassName.length(), icon,
+ pseudoClassName, null, null);
+ proposals.add(proposal);
+ }
+ }
+ }
+ return proposals;
+ }
+
+ private void sortProposals(List proposals) {
+ Collections.sort(proposals, new Comparator() {
+ public int compare(Object o1, Object o2) {
+ String s1 = ((ICompletionProposal) o1).getDisplayString();
+ if (s1 == null) {
+ return -1;
+ }
+ String s2 = ((ICompletionProposal) o2).getDisplayString();
+ return s1.compareToIgnoreCase(s2);
+ }
+ });
+ }
+
+}