blob: 2a3a7c207739dc15593078fc689d374687b21547 [file] [log] [blame]
nitind958d79a2004-11-23 19:23:00 +00001/*****************************************************************************
2 * Copyright (c) 2004 IBM Corporation and others. All rights reserved. This
3 * program and the accompanying materials are made available under the terms
4 * of the Eclipse Public License v1.0 which accompanies this distribution, and
5 * is available at http://www.eclipse.org/legal/epl-v10.html
6 *
7 * Contributors: IBM Corporation - initial API and implementation
8 ****************************************************************************/
david_williamsaaadf2b2005-04-11 07:28:31 +00009package org.eclipse.wst.css.ui.internal.contentassist;
nitind958d79a2004-11-23 19:23:00 +000010
11
12
13import org.eclipse.jface.text.ITextViewer;
14import org.eclipse.jface.text.contentassist.ICompletionProposal;
15import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
david_williams63219a22005-04-10 01:59:51 +000016import org.eclipse.wst.css.core.internal.provisional.adapters.ICSSModelAdapter;
17import org.eclipse.wst.css.core.internal.provisional.document.ICSSDocument;
18import org.eclipse.wst.css.core.internal.provisional.document.ICSSModel;
19import org.eclipse.wst.css.core.internal.provisional.document.ICSSNode;
nitind057e5c82005-06-16 04:12:26 +000020import org.eclipse.wst.css.ui.internal.templates.TemplateContextTypeIdsCSS;
david_williamsaaadf2b2005-04-11 07:28:31 +000021import org.eclipse.wst.html.core.internal.htmlcss.StyleAdapterFactory;
david_williamsb5d05632006-02-27 09:24:00 +000022import org.eclipse.wst.sse.core.StructuredModelManager;
david_williams4ad020f2005-04-18 08:00:30 +000023import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter;
24import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
25import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
nitind958d79a2004-11-23 19:23:00 +000026import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils;
david_williams4ad020f2005-04-18 08:00:30 +000027import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
david_williams12ff79e2005-04-13 13:59:30 +000028import org.eclipse.wst.xml.ui.internal.contentassist.XMLContentAssistUtilities;
david_williamsf3680f02005-04-13 22:43:54 +000029import org.eclipse.wst.xml.ui.internal.util.SharedXMLEditorPluginImageHelper;
nitind958d79a2004-11-23 19:23:00 +000030
31public class CSSContentAssistProcessor implements IContentAssistProcessor {
32
33 private int fDocumentOffset = 0;
34 private char fQuote = 0;
nitind057e5c82005-06-16 04:12:26 +000035 private CSSTemplateCompletionProcessor fTemplateProcessor = null;
nitind958d79a2004-11-23 19:23:00 +000036
37 /**
38 * Return a list of proposed code completions based on the specified
39 * location within the document that corresponds to the current cursor
40 * position within the text-editor control.
41 *
42 * @param documentPosition
43 * a location within the document
44 * @return an array of code-assist items
45 */
46 public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentPosition) {
47
david_williams2a5d1622006-08-05 03:45:07 +000048 IndexedRegion indexedNode = ContentAssistUtils.getNodeAt(viewer, documentPosition + fDocumentOffset);
david_williamsc39caaf2005-04-05 06:07:16 +000049 IDOMNode xNode = null;
50 IDOMNode parent = null;
nitind958d79a2004-11-23 19:23:00 +000051 CSSProposalArranger arranger = null;
nitind057e5c82005-06-16 04:12:26 +000052 boolean isEmptyDocument = false;
nitind958d79a2004-11-23 19:23:00 +000053
54 // bail if we couldn't get an indexed node
david_williamscc023632005-03-08 02:56:22 +000055 // if(indexedNode == null) return new ICompletionProposal[0];
david_williamsc39caaf2005-04-05 06:07:16 +000056 if (indexedNode instanceof IDOMNode) {
57 xNode = (IDOMNode) indexedNode;
58 parent = (IDOMNode) xNode.getParentNode();
nitind958d79a2004-11-23 19:23:00 +000059 }
60 // need to get in here if there in the no 0 region <style>|</style>
61 // case
david_williams2a5d1622006-08-05 03:45:07 +000062 if ((xNode != null) && xNode.getNodeName().equalsIgnoreCase(HTML40Namespace.ElementName.STYLE)) {
nitind958d79a2004-11-23 19:23:00 +000063 // now we know the cursor is in a <style> tag w/out region
david_williamscc023632005-03-08 02:56:22 +000064 IStructuredModel cssModel = getCSSModel(xNode);
65 if (cssModel != null) {
66 // adjust offsets for embedded style
67 int offset = documentPosition;
68 int pos = 0;
69 IndexedRegion keyIndexedNode = cssModel.getIndexedRegion(pos);
70 if (keyIndexedNode == null) {
71 keyIndexedNode = (IndexedRegion) ((ICSSModel) cssModel).getDocument();
72 }
73 arranger = new CSSProposalArranger(pos, (ICSSNode) keyIndexedNode, offset, (char) 0);
nitind958d79a2004-11-23 19:23:00 +000074 }
david_williams2a5d1622006-08-05 03:45:07 +000075 } else if ((parent != null) && parent.getNodeName().equalsIgnoreCase(HTML40Namespace.ElementName.STYLE)) {
nitind958d79a2004-11-23 19:23:00 +000076 // now we know the cursor is in a <style> tag with a region
77 // use the parent because that will be the <style> tag
david_williamscc023632005-03-08 02:56:22 +000078 IStructuredModel cssModel = getCSSModel(parent);
79 if (cssModel != null) {
80 // adjust offsets for embedded style
81 int offset = indexedNode.getStartOffset();
82 int pos = documentPosition - offset;
83 IndexedRegion keyIndexedNode = cssModel.getIndexedRegion(pos);
84 if (keyIndexedNode == null) {
85 keyIndexedNode = (IndexedRegion) ((ICSSModel) cssModel).getDocument();
86 }
87 arranger = new CSSProposalArranger(pos, (ICSSNode) keyIndexedNode, offset, (char) 0);
nitind958d79a2004-11-23 19:23:00 +000088 }
nitind057e5c82005-06-16 04:12:26 +000089 } else if (indexedNode instanceof IDOMNode) {
nitind958d79a2004-11-23 19:23:00 +000090 // get model for node w/ style attribute
david_williamsc39caaf2005-04-05 06:07:16 +000091 IStructuredModel cssModel = getCSSModel((IDOMNode) indexedNode);
david_williamscc023632005-03-08 02:56:22 +000092 if (cssModel != null) {
93 IndexedRegion keyIndexedNode = cssModel.getIndexedRegion(documentPosition - fDocumentOffset);
94 if (keyIndexedNode == null) {
95 keyIndexedNode = (IndexedRegion) ((ICSSModel) cssModel).getDocument();
96 }
97 if (keyIndexedNode instanceof ICSSNode) {
98 // inline style for a tag, not embedded
99 arranger = new CSSProposalArranger(documentPosition, (ICSSNode) keyIndexedNode, fDocumentOffset, fQuote);
100 }
101 }
nitind057e5c82005-06-16 04:12:26 +0000102 } else if (indexedNode instanceof ICSSNode) {
nitind958d79a2004-11-23 19:23:00 +0000103 // when editing external CSS using CSS Designer, ICSSNode is
104 // passed.
105 ICSSDocument cssdoc = ((ICSSNode) indexedNode).getOwnerDocument();
106 if (cssdoc != null) {
david_williamscc023632005-03-08 02:56:22 +0000107 IStructuredModel cssModel = cssdoc.getModel();
108 if (cssModel != null) {
109 IndexedRegion keyIndexedNode = cssModel.getIndexedRegion(documentPosition - fDocumentOffset);
110 if (keyIndexedNode == null) {
111 keyIndexedNode = (IndexedRegion) ((ICSSModel) cssModel).getDocument();
112 }
113 if (keyIndexedNode instanceof ICSSNode) {
114 // inline style for a tag, not embedded
115 arranger = new CSSProposalArranger(documentPosition, (ICSSNode) keyIndexedNode, fDocumentOffset, fQuote);
116 }
117 }
nitind958d79a2004-11-23 19:23:00 +0000118 }
david_williams2a5d1622006-08-05 03:45:07 +0000119 } else if ((indexedNode == null) && isViewerEmpty(viewer)) {
nitind057e5c82005-06-16 04:12:26 +0000120 isEmptyDocument = true;
nitind958d79a2004-11-23 19:23:00 +0000121 // the top of empty CSS Document
david_williamscc023632005-03-08 02:56:22 +0000122 IStructuredModel cssModel = null;
123 try {
124 cssModel = StructuredModelManager.getModelManager().getExistingModelForRead(viewer.getDocument());
125 if (cssModel instanceof ICSSModel) {
126 IndexedRegion keyIndexedNode = cssModel.getIndexedRegion(documentPosition - fDocumentOffset);
127 if (keyIndexedNode == null) {
128 keyIndexedNode = (IndexedRegion) ((ICSSModel) cssModel).getDocument();
129 }
130 if (keyIndexedNode instanceof ICSSNode) {
131 // inline style for a tag, not embedded
132 arranger = new CSSProposalArranger(documentPosition, (ICSSNode) keyIndexedNode, fDocumentOffset, fQuote);
133 }
134 }
nitind057e5c82005-06-16 04:12:26 +0000135 } finally {
david_williamscc023632005-03-08 02:56:22 +0000136 if (cssModel != null)
137 cssModel.releaseFromRead();
nitind958d79a2004-11-23 19:23:00 +0000138 }
139 }
140
david_williamscc023632005-03-08 02:56:22 +0000141 ICompletionProposal[] proposals = new ICompletionProposal[0];
142 if (arranger != null) {
143 fDocumentOffset = 0;
144 proposals = arranger.getProposals();
nitind958d79a2004-11-23 19:23:00 +0000145
nitind057e5c82005-06-16 04:12:26 +0000146 ICompletionProposal[] newfileproposals = new ICompletionProposal[0];
147 ICompletionProposal[] anyproposals = new ICompletionProposal[0];
148 // add template proposals
149 if (getTemplateCompletionProcessor() != null) {
150 if (isEmptyDocument) {
151 getTemplateCompletionProcessor().setContextType(TemplateContextTypeIdsCSS.NEW);
152 newfileproposals = getTemplateCompletionProcessor().computeCompletionProposals(viewer, documentPosition);
153 }
154 getTemplateCompletionProcessor().setContextType(TemplateContextTypeIdsCSS.ALL);
155 anyproposals = getTemplateCompletionProcessor().computeCompletionProposals(viewer, documentPosition);
156 }
157
david_williamscc023632005-03-08 02:56:22 +0000158 // add end tag if parent is not closed
david_williams2a5d1622006-08-05 03:45:07 +0000159 ICompletionProposal endTag = XMLContentAssistUtilities.computeXMLEndTagProposal(viewer, documentPosition, indexedNode, HTML40Namespace.ElementName.STYLE, SharedXMLEditorPluginImageHelper.IMG_OBJ_TAG_GENERIC);
nitind057e5c82005-06-16 04:12:26 +0000160
161 // add the additional proposals
162 int additionalLength = newfileproposals.length + anyproposals.length;
163 additionalLength = (endTag != null) ? ++additionalLength : additionalLength;
164 if (additionalLength > 0) {
165 ICompletionProposal[] plusOnes = new ICompletionProposal[proposals.length + additionalLength];
166 int appendPos = proposals.length;
167 // add end tag proposal
168 if (endTag != null) {
169 System.arraycopy(proposals, 0, plusOnes, 1, proposals.length);
170 plusOnes[0] = endTag;
171 ++appendPos;
172 } else {
173 System.arraycopy(proposals, 0, plusOnes, 0, proposals.length);
174 }
175 // add items in newfileproposals
176 for (int i = 0; i < newfileproposals.length; ++i) {
177 plusOnes[appendPos + i] = newfileproposals[i];
178 }
179 // add items in anyproposals
180 appendPos = appendPos + newfileproposals.length;
181 for (int i = 0; i < anyproposals.length; ++i) {
182 plusOnes[appendPos + i] = anyproposals[i];
183 }
184 proposals = plusOnes;
david_williamscc023632005-03-08 02:56:22 +0000185 }
nitind958d79a2004-11-23 19:23:00 +0000186 }
david_williamscc023632005-03-08 02:56:22 +0000187 return proposals;
nitind958d79a2004-11-23 19:23:00 +0000188 }
189
190 /**
191 * Returns true if there is no text or it's all white space, otherwise
192 * returns false
193 *
194 * @param treeNode
195 * @param textViewer
196 * @return boolean
197 */
198 private boolean isViewerEmpty(ITextViewer textViewer) {
199 boolean isEmpty = false;
200 String text = textViewer.getTextWidget().getText();
david_williams2a5d1622006-08-05 03:45:07 +0000201 if ((text == null) || ((text != null) && text.trim().equals(""))) //$NON-NLS-1$
nitind958d79a2004-11-23 19:23:00 +0000202 isEmpty = true;
203 return isEmpty;
204 }
205
206 /**
207 * Get CSSModel for an indexed node
208 *
209 * @param indexedNode
210 * @return IStructuredModel
211 */
david_williamscc023632005-03-08 02:56:22 +0000212 // private IStructuredModel getCSSModel(IndexedRegion indexedNode) {
213 // if (indexedNode == null) return null;
214 // Node node = (Node)indexedNode;
215 // INodeNotifier notifier = (INodeNotifier)node.getParentNode();
216 // if (notifier == null) return null;
217 // INodeAdapter adapter =
nitind958d79a2004-11-23 19:23:00 +0000218 // StyleAdapterFactory.getInstance().adapt(notifier);
david_williamscc023632005-03-08 02:56:22 +0000219 // if (adapter == null || !(adapter instanceof CSSModelAdapter)) return
nitind958d79a2004-11-23 19:23:00 +0000220 // null;
david_williamscc023632005-03-08 02:56:22 +0000221 // CSSModelAdapter modelAdapter = (CSSModelAdapter)adapter;
222 // return modelAdapter.getModel();
223 // }
nitind958d79a2004-11-23 19:23:00 +0000224 /**
225 * Returns the CSSmodel for a given XML node.
226 *
227 * @param element
228 * @return IStructuredModel
229 */
david_williamsc39caaf2005-04-05 06:07:16 +0000230 private IStructuredModel getCSSModel(IDOMNode element) {
nitind958d79a2004-11-23 19:23:00 +0000231 if (element == null)
232 return null;
233 INodeAdapter adapter = StyleAdapterFactory.getInstance().adapt(element);
david_williams2a5d1622006-08-05 03:45:07 +0000234 if ((adapter == null) || !(adapter instanceof ICSSModelAdapter))
nitind958d79a2004-11-23 19:23:00 +0000235 return null;
236 ICSSModelAdapter modelAdapter = (ICSSModelAdapter) adapter;
237 return modelAdapter.getModel();
238 }
239
240 /**
241 * Returns information about possible contexts based on the specified
242 * location within the document that corresponds to the current cursor
243 * position within the text viewer.
244 *
245 * @param viewer
246 * the viewer whose document is used to compute the possible
247 * contexts
248 * @param documentPosition
249 * an offset within the document for which context information
250 * should be computed
251 * @return an array of context information objects or <code>null</code>
252 * if no context could be found
253 */
254 public org.eclipse.jface.text.contentassist.IContextInformation[] computeContextInformation(org.eclipse.jface.text.ITextViewer viewer, int documentOffset) {
255 return null;
256 }
257
258 /**
259 * Returns the characters which when entered by the user should
260 * automatically trigger the presentation of possible completions.
261 *
262 * @return the auto activation characters for completion proposal or
263 * <code>null</code> if no auto activation is desired
264 */
265 public char[] getCompletionProposalAutoActivationCharacters() {
266 return null;
267 }
268
269 /**
270 * Returns the characters which when entered by the user should
271 * automatically trigger the presentation of context information.
272 *
273 * @return the auto activation characters for presenting context
274 * information or <code>null</code> if no auto activation is
275 * desired
276 */
277 public char[] getContextInformationAutoActivationCharacters() {
278 return null;
279 }
280
281 /**
282 * Returns a validator used to determine when displayed context
283 * information should be dismissed. May only return <code>null</code> if
284 * the processor is incapable of computing context information.
285 *
286 * @return a context information validator, or <code>null</code> if the
287 * processor is incapable of computing context information
288 */
289 public org.eclipse.jface.text.contentassist.IContextInformationValidator getContextInformationValidator() {
290 return null;
291 }
292
293 /**
294 * Return the reason why computeProposals was not able to find any
295 * completions.
296 *
297 * @return an error message or null if no error occurred
298 */
299 public String getErrorMessage() {
300 return null;
301 }
302
303 /**
304 * Insert the method's description here. Creation date: (2001/05/22
305 * 10:37:05)
306 *
307 * @param offset
308 * int
309 */
310 public void setDocumentOffset(int offset) {
311 fDocumentOffset = offset;
312 }
313
314 /**
315 *
316 * @param quote
317 * char
318 */
319 public void setQuoteCharOfStyleAttribute(char quote) {
320 fQuote = quote;
321 }
nitind057e5c82005-06-16 04:12:26 +0000322
323 private CSSTemplateCompletionProcessor getTemplateCompletionProcessor() {
324 if (fTemplateProcessor == null) {
325 fTemplateProcessor = new CSSTemplateCompletionProcessor();
326 }
327 return fTemplateProcessor;
328 }
nitind958d79a2004-11-23 19:23:00 +0000329}