| /******************************************************************************* |
| * Copyright (c) 2010 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| * |
| *******************************************************************************/ |
| package org.eclipse.wst.xml.ui.internal.contentassist; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.Vector; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jface.text.ITextViewer; |
| import org.eclipse.jface.text.contentassist.ICompletionProposal; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer; |
| import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext; |
| import org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer; |
| import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; |
| import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; |
| import org.eclipse.wst.xml.ui.internal.Logger; |
| import org.eclipse.wst.xml.ui.internal.XMLUIMessages; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| /** |
| * <p>Implements the framework for making proposals in XML type documents. Deals with |
| * all the nastiness needed to figure out where in an XML type document the content |
| * assist was invoked and then calls one of many abstract methods depending on the |
| * area the content assist was invoked. In this way implementers only have to worry about |
| * implementing what to do in each situation while not having to deal with figuring out |
| * which situation the content assist was invoked in.</p> |
| * |
| * @base org.eclipse.wst.xml.ui.internal.contentassist.AbstractContentAssistProcessor |
| * @see DefaultXMLCompletionProposalComputer |
| */ |
| public abstract class AbstractXMLCompletionProposalComputer implements ICompletionProposalComputer { |
| /** |
| * ISSUE: this is a bit of hidden JSP knowledge that was implemented this |
| * way for expedency. Should be evolved in future to depend on |
| * "nestedContext". |
| */ |
| private class DOMJSPRegionContextsPrivateCopy { |
| private static final String JSP_CLOSE = "JSP_CLOSE"; //$NON-NLS-1$ |
| private static final String JSP_COMMENT_CLOSE = "JSP_COMMENT_CLOSE"; //$NON-NLS-1$ |
| |
| private static final String JSP_COMMENT_OPEN = "JSP_COMMENT_OPEN"; //$NON-NLS-1$ |
| private static final String JSP_DECLARATION_OPEN = "JSP_DECLARATION_OPEN"; //$NON-NLS-1$ |
| private static final String JSP_DIRECTIVE_CLOSE = "JSP_DIRECTIVE_CLOSE"; //$NON-NLS-1$ |
| private static final String JSP_DIRECTIVE_NAME = "JSP_DIRECTIVE_NAME"; //$NON-NLS-1$ |
| |
| private static final String JSP_DIRECTIVE_OPEN = "JSP_DIRECTIVE_OPEN"; //$NON-NLS-1$ |
| private static final String JSP_EXPRESSION_OPEN = "JSP_EXPRESSION_OPEN"; //$NON-NLS-1$ |
| |
| private static final String JSP_ROOT_TAG_NAME = "JSP_ROOT_TAG_NAME"; //$NON-NLS-1$ |
| |
| private static final String JSP_SCRIPTLET_OPEN = "JSP_SCRIPTLET_OPEN"; //$NON-NLS-1$ |
| } |
| |
| private String fErrorMessage; |
| private ITextViewer fTextViewer; |
| |
| public AbstractXMLCompletionProposalComputer() { |
| fErrorMessage = null; |
| fTextViewer = null; |
| } |
| |
| /** |
| * <p>Return a list of proposed code completions based on the specified |
| * location within the document that corresponds to the current cursor |
| * position within the text-editor control.</p> |
| * |
| * @see org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer#computeCompletionProposals(org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext, org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public List computeCompletionProposals( |
| CompletionProposalInvocationContext context, |
| IProgressMonitor monitor) { |
| |
| ITextViewer textViewer = context.getViewer(); |
| int documentPosition = context.getInvocationOffset(); |
| |
| setErrorMessage(null); |
| |
| fTextViewer = textViewer; |
| |
| IndexedRegion treeNode = ContentAssistUtils.getNodeAt(textViewer, documentPosition); |
| |
| Node node = (Node) treeNode; |
| while ((node != null) && (node.getNodeType() == Node.TEXT_NODE) && (node.getParentNode() != null)) { |
| node = node.getParentNode(); |
| } |
| IDOMNode xmlnode = (IDOMNode) node; |
| |
| ContentAssistRequest contentAssistRequest = null; |
| |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| ITextRegion completionRegion = getCompletionRegion(documentPosition, node); |
| |
| String matchString = getMatchString(sdRegion, completionRegion, documentPosition); |
| |
| // Handle empty Documents |
| if (completionRegion == null) { |
| if (((treeNode == null) || (((Node) treeNode).getNodeType() == Node.DOCUMENT_NODE)) && (completionRegion == null) && |
| ((xmlnode == null) || (xmlnode.getChildNodes() == null) || (xmlnode.getChildNodes().getLength() == 0))) { |
| |
| IStructuredModel sModel = null; |
| try { |
| if(textViewer.getDocument() instanceof IStructuredDocument) { |
| sModel = StructuredModelManager.getModelManager().getModelForRead((IStructuredDocument)textViewer.getDocument()); |
| } |
| if (sModel != null) { |
| IDOMDocument docNode = ((IDOMModel) sModel).getDocument(); |
| contentAssistRequest = new ContentAssistRequest(docNode, docNode, sdRegion, completionRegion, documentPosition, 0, null); |
| addEmptyDocumentProposals(contentAssistRequest, context); |
| } |
| } |
| finally { |
| if (sModel != null) { |
| sModel.releaseFromRead(); |
| } |
| } |
| if (contentAssistRequest == null) { |
| Logger.logException(new IllegalStateException("problem getting model")); //$NON-NLS-1$ |
| return new ArrayList(0); |
| } |
| |
| ICompletionProposal[] props = contentAssistRequest.getCompletionProposals(); |
| return (props != null) ? Arrays.asList(props) : new ArrayList(0); |
| } |
| // MASSIVE ERROR CONDITION |
| Logger.logException(new IllegalStateException("completion region was null")); //$NON-NLS-1$ |
| setErrorMessage(XMLUIMessages.SEVERE_internal_error_occu_UI_); |
| contentAssistRequest = new ContentAssistRequest((Node) treeNode, node.getParentNode(), sdRegion, completionRegion, documentPosition, 0, ""); //$NON-NLS-1$ |
| ICompletionProposal[] props = contentAssistRequest.getCompletionProposals(); |
| return (props != null) ? Arrays.asList(props) : new ArrayList(0); |
| } |
| |
| |
| // catch documents where no region can be determined |
| if ((xmlnode.getNodeType() == Node.DOCUMENT_NODE) && ((completionRegion == null) || |
| (xmlnode.getChildNodes() == null) || (xmlnode.getChildNodes().getLength() == 0))) { |
| |
| contentAssistRequest = computeStartDocumentProposals(matchString, completionRegion, |
| (IDOMNode) treeNode,xmlnode, context); |
| ICompletionProposal[] props = contentAssistRequest.getCompletionProposals(); |
| return (props != null) ? Arrays.asList(props) : new ArrayList(0); |
| } |
| |
| // compute normal proposals |
| contentAssistRequest = computeCompletionProposals(matchString, completionRegion, (IDOMNode) treeNode, xmlnode, context); |
| if (contentAssistRequest == null) { |
| contentAssistRequest = new ContentAssistRequest((Node) treeNode, node.getParentNode(), sdRegion, completionRegion, documentPosition, 0, ""); //$NON-NLS-1$ |
| setErrorMessage(XMLUIMessages.Content_Assist_not_availab_UI_); |
| } |
| |
| /* |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=123892 |
| * Only set this error message if nothing else was already set |
| **/ |
| if (contentAssistRequest.getProposals().size() == 0 && getErrorMessage() == null) { |
| setErrorMessage(XMLUIMessages.Content_Assist_not_availab_UI_); |
| } |
| |
| ICompletionProposal[] props = contentAssistRequest.getCompletionProposals(); |
| return (props != null) ? Arrays.asList(props) : new ArrayList(0); |
| } |
| |
| /** |
| * <p>Returns information about possible contexts based on the specified |
| * location within the document that corresponds to the current cursor |
| * position within the text viewer.</p> |
| * |
| * @see org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer#computeContextInformation(org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext, org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public List computeContextInformation( |
| CompletionProposalInvocationContext context, |
| IProgressMonitor monitor) { |
| |
| //no default context info |
| return Collections.EMPTY_LIST; |
| } |
| |
| /** |
| * @see org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer#getErrorMessage() |
| */ |
| public String getErrorMessage() { |
| return fErrorMessage; |
| } |
| |
| /** |
| * Add proposals for attribute names |
| * |
| * @param contentAssistRequest |
| * @param context |
| */ |
| protected abstract void addAttributeNameProposals(ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Add proposals for attribute values |
| * |
| * @param contentAssistRequest |
| * @param context |
| */ |
| protected abstract void addAttributeValueProposals(ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Add comment proposals |
| * |
| * @param contentAssistRequest |
| * @param context |
| */ |
| protected abstract void addCommentProposal(ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Add the proposals for a completely empty document |
| * |
| * @param contentAssistRequest |
| * @param context |
| */ |
| protected abstract void addEmptyDocumentProposals(ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Add the proposals for the name in an end tag |
| * |
| * @param contentAssistRequest |
| * @param context |
| */ |
| protected abstract void addEndTagNameProposals(ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Prompt for end tags to a non-empty Node that hasn't ended Handles these |
| * cases: <br> |
| * <tagOpen>| <br> |
| * <tagOpen>< |<br> |
| * <tagOpen></ | |
| * |
| * @param contentAssistRequest |
| * @param context |
| */ |
| protected abstract void addEndTagProposals(ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Add entity proposals |
| * |
| * @param contentAssistRequest |
| * @param completionRegion |
| * @param treeNode |
| * @param context |
| */ |
| protected abstract void addEntityProposals(ContentAssistRequest contentAssistRequest, |
| ITextRegion completionRegion, IDOMNode treeNode, CompletionProposalInvocationContext context); |
| |
| /** |
| * add entity proposals |
| * |
| * @param proposals |
| * @param map |
| * @param key |
| * @param nodeOffset |
| * @param sdRegion |
| * @param completionRegion |
| * @param context |
| */ |
| protected abstract void addEntityProposals(Vector proposals, Properties map, String key, |
| int nodeOffset, IStructuredDocumentRegion sdRegion, ITextRegion completionRegion, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Add PCData proposals |
| * |
| * @param nodeName |
| * @param contentAssistRequest |
| * @param context |
| */ |
| protected abstract void addPCDATAProposal(String nodeName, ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Add start document proposals |
| * |
| * @param contentAssistRequest |
| * @param context |
| */ |
| protected abstract void addStartDocumentProposals(ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Close an unclosed start tag |
| * |
| * @param contentAssistRequest |
| * @param context |
| */ |
| protected abstract void addTagCloseProposals(ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context); |
| |
| /** |
| * Add tag insertion proposals |
| * |
| * @param contentAssistRequest |
| * @param childPosition |
| * @param context |
| */ |
| protected abstract void addTagInsertionProposals(ContentAssistRequest contentAssistRequest, |
| int childPosition, CompletionProposalInvocationContext context); |
| |
| /** |
| * Add tag name proposals |
| * |
| * @param contentAssistRequest |
| * @param childPosition |
| * @param context |
| */ |
| protected abstract void addTagNameProposals(ContentAssistRequest contentAssistRequest, |
| int childPosition, CompletionProposalInvocationContext context); |
| |
| /** |
| * @param errorMessage the reason why computeProposals was not able to find any |
| * completions. |
| */ |
| protected void setErrorMessage(String errorMessage) { |
| fErrorMessage = errorMessage; |
| } |
| |
| /** |
| * <p>This does all the magic of figuring out where in the XML type document |
| * the content assist was invoked and then calling the corresponding method |
| * to add the correct proposals</p> |
| * |
| * <p><b>NOTE: </b>if overriding be sure to make super call back to this method otherwise |
| * you will loose all of the proposals generated by this method</p> |
| * |
| * @param matchString |
| * @param completionRegion |
| * @param treeNode |
| * @param xmlnode |
| * @param context |
| * |
| * @return {@link ContentAssistRequest} that now has all the proposals in it |
| */ |
| protected ContentAssistRequest computeCompletionProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode treeNode, IDOMNode xmlnode, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| |
| ContentAssistRequest contentAssistRequest = null; |
| String regionType = completionRegion.getType(); |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| |
| // Handle the most common and best supported cases |
| if ((xmlnode.getNodeType() == Node.ELEMENT_NODE) || (xmlnode.getNodeType() == Node.DOCUMENT_NODE)) { |
| if (regionType == DOMRegionContext.XML_TAG_OPEN) { |
| contentAssistRequest = computeTagOpenProposals(matchString, completionRegion, treeNode, xmlnode, context); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_NAME) { |
| contentAssistRequest = computeTagNameProposals(matchString, completionRegion, |
| treeNode, xmlnode, context); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) { |
| contentAssistRequest = computeAttributeProposals(matchString, completionRegion, treeNode, xmlnode, context); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { |
| contentAssistRequest = computeEqualsProposals(matchString, completionRegion, |
| treeNode, xmlnode, context); |
| } |
| else if ((regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) && (documentPosition == sdRegion.getTextEndOffset()) && |
| (sdRegion.getText(completionRegion).endsWith("\"") || sdRegion.getText(completionRegion).endsWith("\'"))) { //$NON-NLS-1$ //$NON-NLS-2$ |
| // this is for when the cursor is at the end of the closing |
| // quote for an attribute.. |
| IDOMNode actualNode = (IDOMNode) xmlnode.getModel().getIndexedRegion(sdRegion.getStartOffset(completionRegion)); |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode, sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagCloseProposals(contentAssistRequest, context); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) { |
| contentAssistRequest = computeAttributeValueProposals(matchString, completionRegion, treeNode, xmlnode, context); |
| } |
| else if ((regionType == DOMRegionContext.XML_TAG_CLOSE) || (regionType == DOMRegionContext.XML_EMPTY_TAG_CLOSE) || |
| (regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_CLOSE))) { |
| |
| contentAssistRequest = computeTagCloseProposals(matchString, completionRegion, treeNode, xmlnode, context); |
| } |
| else if (regionType == DOMRegionContext.XML_END_TAG_OPEN) { |
| contentAssistRequest = computeEndTagOpenProposals(matchString, completionRegion, |
| treeNode, xmlnode, context); |
| } |
| else if ((regionType == DOMRegionContext.XML_CONTENT) || (regionType == DOMRegionContext.XML_CHAR_REFERENCE) || |
| (regionType == DOMRegionContext.XML_ENTITY_REFERENCE) || (regionType == DOMRegionContext.XML_PE_REFERENCE)) { |
| |
| contentAssistRequest = computeContentProposals(matchString, completionRegion, |
| treeNode, xmlnode, context); |
| } |
| |
| // These ITextRegion types begin DOM Elements as well and although |
| // internally harder to assist, |
| // text insertions directly before them can be made |
| else if ((documentPosition == sdRegion.getStartOffset(completionRegion)) && |
| (regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_COMMENT_OPEN) || |
| regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DECLARATION_OPEN) || |
| regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_OPEN) || |
| regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_EXPRESSION_OPEN) || |
| regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_SCRIPTLET_OPEN) || |
| (regionType == DOMRegionContext.XML_DECLARATION_OPEN) || |
| (regionType == DOMRegionContext.XML_PI_OPEN) || |
| (regionType == DOMRegionContext.XML_COMMENT_OPEN) || |
| (regionType == DOMRegionContext.XML_CDATA_OPEN))) { |
| |
| contentAssistRequest = new ContentAssistRequest(treeNode, xmlnode.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagInsertionProposals(contentAssistRequest, getElementPosition(treeNode), context); |
| addStartDocumentProposals(contentAssistRequest, context); |
| } |
| } |
| // Not a Document or Element? (odd cases go here for now) |
| else if (isCloseRegion(completionRegion)) { |
| contentAssistRequest = new ContentAssistRequest(treeNode, xmlnode.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion) + completionRegion.getLength(), 0, matchString); |
| addStartDocumentProposals(contentAssistRequest, context); |
| if (documentPosition >= sdRegion.getTextEndOffset(completionRegion)) { |
| addTagInsertionProposals(contentAssistRequest, getElementPosition(treeNode) + 1, context); |
| } |
| } |
| else if ((documentPosition == sdRegion.getStartOffset(completionRegion)) && |
| (regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_COMMENT_OPEN) || |
| regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DECLARATION_OPEN) || |
| regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_OPEN) || |
| regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_EXPRESSION_OPEN) || |
| regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_SCRIPTLET_OPEN) || |
| (regionType == DOMRegionContext.XML_DECLARATION_OPEN) || |
| (regionType == DOMRegionContext.XML_PI_OPEN) || |
| (regionType == DOMRegionContext.XML_COMMENT_OPEN) || |
| (regionType == DOMRegionContext.XML_CDATA_OPEN))) { |
| |
| contentAssistRequest = new ContentAssistRequest(treeNode, xmlnode.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagInsertionProposals(contentAssistRequest, getElementPosition(treeNode), context); |
| addStartDocumentProposals(contentAssistRequest, context); |
| } |
| return contentAssistRequest; |
| } |
| |
| /** |
| * <p>Similar to {@link #computeCompletionProposals(CompletionProposalInvocationContext, IProgressMonitor)} only specificly for |
| * attribute proposals</p> |
| * |
| * <p>Implementers should not override this method, it is made available to implementers so that if they override |
| * {@link #computeCompletionProposals(String, ITextRegion, IDOMNode, IDOMNode, CompletionProposalInvocationContext)} |
| * they can call this method if needed</p> |
| * |
| * @param matchString |
| * @param completionRegion |
| * @param nodeAtOffset |
| * @param node |
| * @param context |
| * @return |
| */ |
| protected final ContentAssistRequest computeAttributeProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode nodeAtOffset, IDOMNode node, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| ITextViewer viewer = context.getViewer(); |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| // if the attribute name is selected, replace it instead of creating a new attribute |
| if (documentPosition <= sdRegion.getStartOffset(completionRegion) && (viewer != null && viewer.getSelectedRange().y != (sdRegion.getEndOffset(completionRegion) - sdRegion.getStartOffset(completionRegion)))) { |
| // setup to insert new attributes |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| else { |
| // Setup to replace an existing attribute name |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), completionRegion.getTextLength(), matchString); |
| } |
| addAttributeNameProposals(contentAssistRequest, context); |
| contentAssistRequest.setReplacementBeginPosition(documentPosition); |
| contentAssistRequest.setReplacementLength(0); |
| if ((node.getFirstStructuredDocumentRegion() != null) && (!node.getFirstStructuredDocumentRegion().isEnded())) { |
| addTagCloseProposals(contentAssistRequest, context); |
| } |
| return contentAssistRequest; |
| } |
| |
| /** |
| * <p>this is the position the cursor should be in after the proposal is |
| * applied</p> |
| * |
| * @param proposedText |
| * @return the position the cursor should be in after the proposal is |
| * applied |
| */ |
| protected static int getCursorPositionForProposedText(String proposedText) { |
| int cursorAdjustment; |
| cursorAdjustment = proposedText.indexOf("\"\"") + 1; //$NON-NLS-1$ |
| // otherwise, after the first tag |
| if (cursorAdjustment == 0) { |
| cursorAdjustment = proposedText.indexOf('>') + 1; |
| } |
| if (cursorAdjustment == 0) { |
| cursorAdjustment = proposedText.length() + 1; |
| } |
| |
| return cursorAdjustment; |
| } |
| |
| /** |
| * <p>helpful utility method for determining if one string starts with another one. |
| * This is case insensitive. If either are null then result is <code>true</code></p> |
| * |
| * @param aString the string to check to see if it starts with the given prefix |
| * @param prefix check that the given string starts with this prefix |
| * |
| * @return <code>true</code> if the given string starts with the given prefix, |
| * <code>false</code> otherwise |
| */ |
| protected static boolean beginsWith(String aString, String prefix) { |
| if ((aString == null) || (prefix == null)) { |
| return true; |
| } |
| return aString.toLowerCase().startsWith(prefix.toLowerCase()); |
| } |
| |
| private ContentAssistRequest computeAttributeValueProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode nodeAtOffset, IDOMNode node, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| if ((documentPosition > sdRegion.getStartOffset(completionRegion) + completionRegion.getTextLength()) && (sdRegion.getStartOffset(completionRegion) + completionRegion.getTextLength() != sdRegion.getStartOffset(completionRegion) + completionRegion.getLength())) { |
| // setup to add a new attribute at the documentPosition |
| IDOMNode actualNode = (IDOMNode) node.getModel().getIndexedRegion(sdRegion.getStartOffset(completionRegion)); |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode, sdRegion, completionRegion, documentPosition, 0, matchString); |
| addAttributeNameProposals(contentAssistRequest, context); |
| if ((actualNode.getFirstStructuredDocumentRegion() != null) && !actualNode.getFirstStructuredDocumentRegion().isEnded()) { |
| addTagCloseProposals(contentAssistRequest, context); |
| } |
| } |
| else { |
| // setup to replace the existing value |
| if (!nodeAtOffset.getFirstStructuredDocumentRegion().isEnded() && (documentPosition < sdRegion.getStartOffset(completionRegion))) { |
| // if the IStructuredDocumentRegion isn't closed and the |
| // cursor is in front of the value, add |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| addAttributeNameProposals(contentAssistRequest, context); |
| } |
| else { |
| int replaceLength = completionRegion.getTextLength(); |
| |
| //if container region, be sure replace length is only the attribute value region not the entire container |
| if(completionRegion instanceof ITextRegionContainer){ |
| ITextRegion openRegion = ((ITextRegionContainer) completionRegion).getFirstRegion(); |
| ITextRegion closeRegion = ((ITextRegionContainer) completionRegion).getLastRegion(); |
| |
| /* |
| * check to see if the container is opened the same way its closed. |
| * Such as: |
| * <img src=' ' |
| * But not: |
| * <img src=' |
| * |
| * </body> |
| * </html> |
| * In the latter case we only want to replace the opening text of the container |
| * Admittedly crude test, but effective. |
| */ |
| if(openRegion.getType() != closeRegion.getType()) { |
| replaceLength = openRegion.getTextLength(); |
| } |
| } |
| |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, sdRegion, |
| completionRegion, sdRegion.getStartOffset(completionRegion), |
| replaceLength, matchString); |
| |
| addAttributeValueProposals(contentAssistRequest, context); |
| } |
| } |
| return contentAssistRequest; |
| } |
| |
| private ContentAssistRequest computeContentProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode nodeAtOffset, IDOMNode node, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| ContentAssistRequest contentAssistRequest = null; |
| |
| // setup to add children at the content node's position |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, getStructuredDocumentRegion(documentPosition), completionRegion, documentPosition, 0, matchString); |
| if ((node != null) && (node.getNodeType() == Node.DOCUMENT_NODE) && (((Document) node).getDoctype() == null)) { |
| addStartDocumentProposals(contentAssistRequest, context); |
| } |
| addTagInsertionProposals(contentAssistRequest, getElementPosition(nodeAtOffset), context); |
| if (node.getNodeType() != Node.DOCUMENT_NODE) { |
| addEndTagProposals(contentAssistRequest, context); |
| } |
| // entities? |
| addEntityProposals(contentAssistRequest, completionRegion, node, context); |
| // addEntityProposals(contentAssistRequest); |
| return contentAssistRequest; |
| } |
| |
| private ContentAssistRequest computeEndTagOpenProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode nodeAtOffset, IDOMNode node, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| int completionRegionStart = sdRegion.getStartOffset(completionRegion); |
| int completionRegionLength = completionRegion.getLength(); |
| IStructuredDocumentRegion sdRegionAtCompletionOffset = node.getStructuredDocument().getRegionAtCharacterOffset(completionRegionStart + completionRegionLength); |
| ITextRegion regionAtEndOfCompletion = sdRegionAtCompletionOffset.getRegionAtCharacterOffset(completionRegionStart + completionRegionLength); |
| |
| if ((documentPosition != completionRegionStart) && (regionAtEndOfCompletion != null) && (regionAtEndOfCompletion.getType() == DOMRegionContext.XML_TAG_NAME)) { |
| ITextRegion nameRegion = regionAtEndOfCompletion; |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, nodeAtOffset.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(nameRegion), nameRegion.getTextLength(), matchString); |
| } |
| else { |
| if (nodeAtOffset.getFirstStructuredDocumentRegion() == sdRegion) { |
| // abnormal case, this unmatched end tag will be a sibling |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, nodeAtOffset.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| else { |
| // normal case, this end tag is the parent |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, nodeAtOffset, sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| } |
| // if (documentPosition >= sdRegion.getStartOffset(completionRegion) + |
| // completionRegion.getTextLength()) |
| addEndTagProposals(contentAssistRequest, context); |
| // else |
| if (completionRegionStart == documentPosition) { |
| // positioned at start of end tag |
| addTagInsertionProposals(contentAssistRequest, node.getChildNodes().getLength(), context); |
| } |
| return contentAssistRequest; |
| } |
| |
| |
| private ContentAssistRequest computeEqualsProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode nodeAtOffset, IDOMNode node, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| ITextRegion valueRegion = node.getStartStructuredDocumentRegion().getRegionAtCharacterOffset(sdRegion.getStartOffset(completionRegion) + completionRegion.getLength()); |
| if ((valueRegion != null) && (valueRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) && (sdRegion.getStartOffset(valueRegion) <= documentPosition)) { |
| // replace the adjacent attribute value |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, sdRegion, valueRegion, sdRegion.getStartOffset(valueRegion), valueRegion.getTextLength(), matchString); |
| } |
| else { |
| // append an attribute value after the '=' |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| addAttributeValueProposals(contentAssistRequest, context); |
| return contentAssistRequest; |
| } |
| |
| private ContentAssistRequest computeStartDocumentProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode nodeAtOffset, IDOMNode node, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| |
| // setup for a non-empty document, but one that hasn't been formally |
| // started |
| ContentAssistRequest contentAssistRequest = null; |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, |
| getStructuredDocumentRegion(documentPosition), completionRegion, |
| documentPosition, 0, matchString); |
| addStartDocumentProposals(contentAssistRequest, context); |
| return contentAssistRequest; |
| } |
| |
| private ContentAssistRequest computeTagCloseProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode nodeAtOffset, IDOMNode node, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| |
| if ((node.getNodeType() == Node.DOCUMENT_NODE) || (documentPosition >= sdRegion.getEndOffset())) { |
| // this is a content request as the documentPosition is AFTER the |
| // end of the closing region |
| if ((node == nodeAtOffset) && (node.getParentNode() != null)) { |
| node = (IDOMNode) node.getParentNode(); |
| } |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagInsertionProposals(contentAssistRequest, getElementPosition(nodeAtOffset), context); |
| if ((node.getNodeType() != Node.DOCUMENT_NODE) && (node.getEndStructuredDocumentRegion() == null)) { |
| addEndTagProposals(contentAssistRequest, context); |
| } |
| } |
| else { |
| // at the start of the tag's close or within it |
| ITextRegion closeRegion = sdRegion.getLastRegion(); |
| boolean insideTag = !sdRegion.isEnded() || (documentPosition <= sdRegion.getStartOffset(closeRegion)); |
| if (insideTag) { |
| // this is a request for completions within a tag |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| if ((node.getNodeType() != Node.DOCUMENT_NODE) && (node.getEndStructuredDocumentRegion() != null)) { |
| addTagCloseProposals(contentAssistRequest, context); |
| } |
| if (sdRegion == nodeAtOffset.getFirstStructuredDocumentRegion()) { |
| contentAssistRequest.setReplacementBeginPosition(documentPosition); |
| contentAssistRequest.setReplacementLength(0); |
| addAttributeNameProposals(contentAssistRequest, context); |
| } |
| } |
| } |
| return contentAssistRequest; |
| } |
| |
| private ContentAssistRequest computeTagNameProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode nodeAtOffset, IDOMNode node, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| |
| if (sdRegion != nodeAtOffset.getFirstStructuredDocumentRegion()) { |
| // completing the *first* tag in "<tagname1 |<tagname2" |
| IDOMNode actualNode = (IDOMNode) node.getModel().getIndexedRegion(sdRegion.getStartOffset(completionRegion)); |
| if (actualNode != null) { |
| if (actualNode.getFirstStructuredDocumentRegion() == sdRegion) { |
| // start tag |
| if (documentPosition > sdRegion.getStartOffset(completionRegion) + completionRegion.getLength()) { |
| // it's attributes |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode, sdRegion, completionRegion, documentPosition - matchString.length(), matchString.length(), matchString); |
| if (node.getStructuredDocument().getRegionAtCharacterOffset(sdRegion.getStartOffset(completionRegion) - 1).getRegionAtCharacterOffset(sdRegion.getStartOffset(completionRegion) - 1).getType() == DOMRegionContext.XML_TAG_OPEN) { |
| addAttributeNameProposals(contentAssistRequest, context); |
| } |
| addTagCloseProposals(contentAssistRequest, context); |
| } |
| else { |
| // it's name |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode.getParentNode(), |
| sdRegion, completionRegion, documentPosition - matchString.length(), |
| matchString.length(), matchString); |
| addTagNameProposals(contentAssistRequest, getElementPosition(actualNode), |
| context); |
| } |
| } |
| else { |
| if (documentPosition >= sdRegion.getStartOffset(completionRegion) + completionRegion.getLength()) { |
| // insert name |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode.getParentNode(), |
| sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| else { |
| // replace name |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode.getParentNode(), |
| sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), |
| completionRegion.getTextLength(), matchString); |
| } |
| addEndTagNameProposals(contentAssistRequest, context); |
| } |
| } |
| } |
| else { |
| if (documentPosition > sdRegion.getStartOffset(completionRegion) + completionRegion.getTextLength()) { |
| // unclosed tag with only a name; should prompt for attributes |
| // and a close instead |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, |
| documentPosition - matchString.length(), matchString.length(), matchString); |
| addAttributeNameProposals(contentAssistRequest, context); |
| addTagCloseProposals(contentAssistRequest, context); |
| } |
| else { |
| if (sdRegion.getRegions().get(0).getType() != DOMRegionContext.XML_END_TAG_OPEN) { |
| int replaceLength = documentPosition - sdRegion.getStartOffset(completionRegion); |
| contentAssistRequest = new ContentAssistRequest(node, node.getParentNode(), sdRegion, |
| completionRegion, sdRegion.getStartOffset(completionRegion), replaceLength, matchString); |
| addTagNameProposals(contentAssistRequest, getElementPosition(nodeAtOffset), context); |
| } |
| else { |
| IDOMNode actualNode = (IDOMNode) node.getModel().getIndexedRegion(documentPosition); |
| if (actualNode != null) { |
| if (documentPosition >= sdRegion.getStartOffset(completionRegion) + completionRegion.getTextLength()) { |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode.getParentNode(), |
| sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| else { |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode.getParentNode(), |
| sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), |
| completionRegion.getTextLength(), matchString); |
| } |
| addEndTagNameProposals(contentAssistRequest, context); |
| } |
| } |
| } |
| } |
| return contentAssistRequest; |
| } |
| |
| private ContentAssistRequest computeTagOpenProposals(String matchString, ITextRegion completionRegion, |
| IDOMNode nodeAtOffset, IDOMNode node, CompletionProposalInvocationContext context) { |
| |
| int documentPosition = context.getInvocationOffset(); |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| if (sdRegion != nodeAtOffset.getFirstStructuredDocumentRegion() || sdRegion.getPrevious() != null && sdRegion.getPrevious().getLastRegion().getType() == DOMRegionContext.XML_TAG_OPEN) { |
| // completing the *first* XML_TAG_OPEN in "<<tagname" |
| IDOMNode actualNode = (IDOMNode) node.getModel().getIndexedRegion(sdRegion.getStartOffset(completionRegion)); |
| if (actualNode != null) { |
| if(sdRegion.getFirstRegion().getType() == DOMRegionContext.XML_END_TAG_OPEN) { |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode, sdRegion, completionRegion, documentPosition, 0, matchString); |
| if(actualNode.hasChildNodes()) |
| addTagNameProposals(contentAssistRequest, |
| getElementPosition(actualNode.getLastChild()), context); |
| else |
| addTagNameProposals(contentAssistRequest, 0, context); |
| } |
| else { |
| contentAssistRequest = new ContentAssistRequest(actualNode, actualNode.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagNameProposals(contentAssistRequest, getElementPosition(actualNode), context); |
| } |
| addEndTagProposals(contentAssistRequest, context); // (pa) 220850 |
| } |
| } |
| else { |
| if (documentPosition == sdRegion.getStartOffset(completionRegion)) { |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| // at the start of an existing tag, right before the '<' |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node.getParentNode(), sdRegion, |
| completionRegion, documentPosition, 0, matchString); |
| addTagInsertionProposals(contentAssistRequest, getElementPosition(nodeAtOffset), context); |
| addEndTagProposals(contentAssistRequest, context); |
| } |
| else if (node.getNodeType() == Node.DOCUMENT_NODE) { |
| // at the opening of the VERY first tag with a '<' |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node.getParentNode(), sdRegion, |
| completionRegion, sdRegion.getStartOffset(completionRegion), completionRegion.getTextLength(), matchString); |
| addStartDocumentProposals(contentAssistRequest, context); |
| } |
| } |
| else { |
| // within the white space |
| ITextRegion name = getNameRegion(node.getStartStructuredDocumentRegion()); |
| // (pa) ITextRegion refactor |
| // if (name != null && name.containsOffset(documentPosition)) |
| // { |
| if ((name != null) && |
| ((sdRegion.getStartOffset(name) <= documentPosition) && (sdRegion.getEndOffset(name) >= documentPosition)) && |
| (sdRegion.getLastRegion().getType() == DOMRegionContext.XML_TAG_CLOSE || sdRegion.getLastRegion().getType() == DOMRegionContext.XML_EMPTY_TAG_CLOSE)){ |
| |
| // replace the existing name |
| contentAssistRequest = new ContentAssistRequest(node, node.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(name), name.getTextLength(), matchString); |
| } |
| else { |
| // insert a valid new name, or possibly an end tag |
| contentAssistRequest = new ContentAssistRequest(nodeAtOffset, ((Node) nodeAtOffset).getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| addEndTagProposals(contentAssistRequest, context); |
| contentAssistRequest.setReplacementBeginPosition(documentPosition); |
| contentAssistRequest.setReplacementLength(0); |
| } |
| addTagNameProposals(contentAssistRequest, getElementPosition(nodeAtOffset), context); |
| } |
| } |
| return contentAssistRequest; |
| } |
| |
| private ITextRegion getCompletionRegion(int offset, IStructuredDocumentRegion sdRegion) { |
| ITextRegion region = sdRegion.getRegionAtCharacterOffset(offset); |
| if (region == null) { |
| return null; |
| } |
| |
| if (sdRegion.getStartOffset(region) == offset) { |
| // The offset is at the beginning of the region |
| if ((sdRegion.getStartOffset(region) == sdRegion.getStartOffset()) && (sdRegion.getPrevious() != null) && (!sdRegion.getPrevious().isEnded())) { |
| // Is the region also the start of the node? If so, the |
| // previous IStructuredDocumentRegion is |
| // where to look for a useful region. |
| region = sdRegion.getPrevious().getRegionAtCharacterOffset(offset - 1); |
| } |
| else { |
| // Is there no separating whitespace from the previous region? |
| // If not, |
| // then that region is the important one |
| ITextRegion previousRegion = sdRegion.getRegionAtCharacterOffset(offset - 1); |
| if ((previousRegion != null) && (previousRegion != region) && (previousRegion.getTextLength() == previousRegion.getLength())) { |
| region = previousRegion; |
| } |
| } |
| } |
| else { |
| // The offset is NOT at the beginning of the region |
| if (offset > sdRegion.getStartOffset(region) + region.getTextLength()) { |
| // Is the offset within the whitespace after the text in this |
| // region? |
| // If so, use the next region |
| ITextRegion nextRegion = sdRegion.getRegionAtCharacterOffset(sdRegion.getStartOffset(region) + region.getLength()); |
| if (nextRegion != null) { |
| region = nextRegion; |
| } |
| } |
| else { |
| // Is the offset within the important text for this region? |
| // If so, then we've already got the right one. |
| } |
| } |
| |
| // valid WHITE_SPACE region handler (#179924) |
| if ((region != null) && (region.getType() == DOMRegionContext.WHITE_SPACE)) { |
| ITextRegion previousRegion = sdRegion.getRegionAtCharacterOffset(sdRegion.getStartOffset(region) - 1); |
| if (previousRegion != null) { |
| region = previousRegion; |
| } |
| } |
| |
| return region; |
| } |
| |
| /** |
| * Return the region whose content's require completion. This is something |
| * of a misnomer as sometimes the user wants to be prompted for contents |
| * of a non-existant ITextRegion, such as for enumerated attribute values |
| * following an '=' sign. |
| */ |
| private ITextRegion getCompletionRegion(int documentPosition, Node domnode) { |
| if (domnode == null) { |
| return null; |
| } |
| |
| ITextRegion region = null; |
| int offset = documentPosition; |
| IStructuredDocumentRegion flatNode = null; |
| IDOMNode node = (IDOMNode) domnode; |
| |
| if (node.getNodeType() == Node.DOCUMENT_NODE) { |
| if (node.getStructuredDocument().getLength() == 0) { |
| return null; |
| } |
| ITextRegion result = node.getStructuredDocument().getRegionAtCharacterOffset(offset).getRegionAtCharacterOffset(offset); |
| while (result == null) { |
| offset--; |
| result = node.getStructuredDocument().getRegionAtCharacterOffset(offset).getRegionAtCharacterOffset(offset); |
| } |
| return result; |
| } |
| |
| IStructuredDocumentRegion startTag = node.getStartStructuredDocumentRegion(); |
| IStructuredDocumentRegion endTag = node.getEndStructuredDocumentRegion(); |
| |
| // Determine if the offset is within the start |
| // IStructuredDocumentRegion, end IStructuredDocumentRegion, or |
| // somewhere within the Node's XML content. |
| if ((startTag != null) && (startTag.getStartOffset() <= offset) && (offset < startTag.getStartOffset() + startTag.getLength())) { |
| flatNode = startTag; |
| } |
| else if ((endTag != null) && (endTag.getStartOffset() <= offset) && (offset < endTag.getStartOffset() + endTag.getLength())) { |
| flatNode = endTag; |
| } |
| |
| if (flatNode != null) { |
| // the offset is definitely within the start or end tag, continue |
| // on and find the region |
| region = getCompletionRegion(offset, flatNode); |
| } |
| else { |
| // the docPosition is neither within the start nor the end, so it |
| // must be content |
| flatNode = node.getStructuredDocument().getRegionAtCharacterOffset(offset); |
| // (pa) ITextRegion refactor |
| // if (flatNode.contains(documentPosition)) { |
| if ((flatNode.getStartOffset() <= documentPosition) && (flatNode.getEndOffset() >= documentPosition)) { |
| // we're interesting in completing/extending the previous |
| // IStructuredDocumentRegion if the current |
| // IStructuredDocumentRegion isn't plain content or if it's |
| // preceded by an orphan '<' |
| if ((offset == flatNode.getStartOffset()) && |
| (flatNode.getPrevious() != null) && |
| (((flatNode.getRegionAtCharacterOffset(documentPosition) != null) && |
| (flatNode.getRegionAtCharacterOffset(documentPosition).getType() != DOMRegionContext.XML_CONTENT)) || |
| (flatNode.getPrevious().getLastRegion().getType() == DOMRegionContext.XML_TAG_OPEN) || |
| (flatNode.getPrevious().getLastRegion().getType() == DOMRegionContext.XML_END_TAG_OPEN))) { |
| |
| // Is the region also the start of the node? If so, the |
| // previous IStructuredDocumentRegion is |
| // where to look for a useful region. |
| region = flatNode.getPrevious().getLastRegion(); |
| } |
| else if (flatNode.getEndOffset() == documentPosition) { |
| region = flatNode.getLastRegion(); |
| } |
| else { |
| region = flatNode.getFirstRegion(); |
| } |
| } |
| else { |
| // catch end of document positions where the docPosition isn't |
| // in a IStructuredDocumentRegion |
| region = flatNode.getLastRegion(); |
| } |
| } |
| |
| return region; |
| } |
| |
| private int getElementPosition(Node child) { |
| Node parent = child.getParentNode(); |
| if (parent == null) { |
| return 0; |
| } |
| |
| NodeList children = parent.getChildNodes(); |
| if (children == null) { |
| return 0; |
| } |
| int count = 0; |
| |
| for (int i = 0; i < children.getLength(); i++) { |
| if (children.item(i) == child) { |
| return count; |
| } |
| else { |
| // if (children.item(i).getNodeType() == Node.ELEMENT_NODE) |
| count++; |
| } |
| } |
| return 0; |
| } |
| |
| private String getMatchString(IStructuredDocumentRegion parent, ITextRegion aRegion, int offset) { |
| if ((aRegion == null) || isCloseRegion(aRegion)) { |
| return ""; //$NON-NLS-1$ |
| } |
| String matchString = null; |
| String regionType = aRegion.getType(); |
| if ((regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) || (regionType == DOMRegionContext.XML_TAG_OPEN) || (offset > parent.getStartOffset(aRegion) + aRegion.getTextLength())) { |
| matchString = ""; //$NON-NLS-1$ |
| } |
| else if (regionType == DOMRegionContext.XML_CONTENT) { |
| matchString = ""; //$NON-NLS-1$ |
| } |
| else { |
| if ((parent.getText(aRegion).length() > 0) && (parent.getStartOffset(aRegion) < offset)) { |
| matchString = parent.getText(aRegion).substring(0, offset - parent.getStartOffset(aRegion)); |
| } |
| else { |
| matchString = ""; //$NON-NLS-1$ |
| } |
| } |
| return matchString; |
| } |
| |
| private ITextRegion getNameRegion(IStructuredDocumentRegion flatNode) { |
| if (flatNode == null) { |
| return null; |
| } |
| Iterator regionList = flatNode.getRegions().iterator(); |
| while (regionList.hasNext()) { |
| ITextRegion region = (ITextRegion) regionList.next(); |
| if (isNameRegion(region)) { |
| return region; |
| } |
| } |
| return null; |
| } |
| |
| private boolean isCloseRegion(ITextRegion region) { |
| String type = region.getType(); |
| return ((type == DOMRegionContext.XML_PI_CLOSE) || |
| (type == DOMRegionContext.XML_TAG_CLOSE) || |
| (type == DOMRegionContext.XML_EMPTY_TAG_CLOSE) || |
| (type == DOMRegionContext.XML_CDATA_CLOSE) || |
| (type == DOMRegionContext.XML_COMMENT_CLOSE) || |
| (type == DOMRegionContext.XML_ATTLIST_DECL_CLOSE) || |
| (type == DOMRegionContext.XML_ELEMENT_DECL_CLOSE) || |
| (type == DOMRegionContext.XML_DOCTYPE_DECLARATION_CLOSE) || |
| (type == DOMJSPRegionContextsPrivateCopy.JSP_CLOSE) || |
| (type == DOMJSPRegionContextsPrivateCopy.JSP_COMMENT_CLOSE) || |
| (type.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_CLOSE)) || |
| (type == DOMRegionContext.XML_DECLARATION_CLOSE)); |
| } |
| |
| private boolean isNameRegion(ITextRegion region) { |
| String type = region.getType(); |
| return ((type == DOMRegionContext.XML_TAG_NAME) || |
| (type == DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_NAME) || |
| (type == DOMRegionContext.XML_ELEMENT_DECL_NAME) || |
| (type == DOMRegionContext.XML_DOCTYPE_NAME) || |
| (type == DOMRegionContext.XML_ATTLIST_DECL_NAME) || |
| (type == DOMJSPRegionContextsPrivateCopy.JSP_ROOT_TAG_NAME) || |
| type.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_NAME)); |
| } |
| |
| /** |
| * StructuredTextViewer must be set before using this. |
| */ |
| private IStructuredDocumentRegion getStructuredDocumentRegion(int pos) { |
| return ContentAssistUtils.getStructuredDocumentRegion(fTextViewer, pos); |
| } |
| } |