blob: 6b675af20f7eed731b0a3ab5f436f614a616b6ee [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 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 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.IContextInformation;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedModeUI;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.link.LinkedPositionGroup;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal;
import org.eclipse.wst.xml.ui.internal.Logger;
/**
* A completion proposal that is capable of establishing linked positions within
* inserted markup.
*
*/
public class MarkupCompletionProposal extends CustomCompletionProposal {
private IRegion fSelectedRegion = null;
public MarkupCompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo, int relevance) {
super(replacementString, replacementOffset, replacementLength, cursorPosition, image, displayString, contextInformation, additionalProposalInfo, relevance);
}
public MarkupCompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo, int relevance, boolean updateReplacementLengthOnValidate) {
super(replacementString, replacementOffset, replacementLength, cursorPosition, image, displayString, null, contextInformation, additionalProposalInfo, relevance, updateReplacementLengthOnValidate);
}
public MarkupCompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, String alternateMatch, IContextInformation contextInformation, String additionalProposalInfo, int relevance, boolean updateReplacementLengthOnValidate) {
super(replacementString, replacementOffset, replacementLength, cursorPosition, image, displayString, alternateMatch, contextInformation, additionalProposalInfo, relevance, updateReplacementLengthOnValidate);
}
public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {
super.apply(viewer, trigger, stateMask, offset);
getLinkedPositions(viewer);
}
/**
* Sets up linked positions and installs them on the viewer.
*
*/
protected void getLinkedPositions(ITextViewer viewer) {
final String replacement = getReplacementString();
final IDocument document = viewer.getDocument();
final int length = replacement.length();
boolean inAttribute = false, hasGroup = false, inEndTag = false;
int offset = 0;
char attType = 0;
int exitPosition = -1;
LinkedModeModel model = new LinkedModeModel();
try {
for (int i = 0; i < length; i++) {
final char c = replacement.charAt(i);
switch (c) {
case '=':
break;
case '\'':
case '\"':
if (!inAttribute) {
offset = i;
attType = c;
inAttribute = true;
}
else {
// Found matching quotes establishing an attribute value region
if (attType == c && replacement.charAt(i - 1) != '\\') {
inAttribute = false; // Record position length
addPosition(model, document, getReplacementOffset() + offset + 1, i - offset - 1);
hasGroup = true;
}
}
break;
case '/':
if (!inAttribute) {
inEndTag = i > 0 && replacement.charAt(i - 1) == '<';
}
break;
case '>':
if (!inAttribute) {
if (i == length - 1) {
exitPosition = getReplacementOffset() + i + 1;
if (!inEndTag) { // Don't add a position within the end-tag
addPosition(model, document, getReplacementOffset() + i, 0); // position within start tag
hasGroup = true;
}
}
else {
addPosition(model, document, getReplacementOffset() + i, 0); // position within start tag
addPosition(model, document, getReplacementOffset() + i + 1, 0); // position after start tag
hasGroup = true;
}
}
break;
}
}
if (hasGroup) {
model.forceInstall();
final LinkedModeUI ui= new EditorLinkedModeUI(model, viewer);
ui.setExitPosition(viewer, exitPosition < 0 ? getReplacementOffset() + getReplacementLength() + replacement.length() - 1 : exitPosition, 0, Integer.MAX_VALUE);
ui.setCyclingMode(LinkedModeUI.CYCLE_WHEN_NO_PARENT);
ui.setDoContextInfo(true);
ui.enter();
fSelectedRegion = ui.getSelectedRegion();
}
}
catch (BadLocationException e) {
Logger.logException(e);
}
}
/**
* Adds a {@link LinkedPosition} to its own position group. This group is then added to the model
* @param model the linked model for this proposal
* @param document the document the content assist is operating upon
* @param offset the offset to establish the {@link LinkedPosition}
* @param length the length of the {@link LinkedPosition}
* @throws BadLocationException
*/
private void addPosition(LinkedModeModel model, IDocument document, int offset, int length) throws BadLocationException {
final LinkedPositionGroup group = new LinkedPositionGroup();
group.addPosition(new LinkedPosition(document, offset, length, LinkedPositionGroup.NO_STOP));
model.addGroup(group);
}
public Point getSelection(IDocument document) {
// Attempt to return the selection based on the selected region from the LinkedModeUI
if (fSelectedRegion == null)
return super.getSelection(document);
return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength());
}
}