/******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package net.sourceforge.phpdt.internal.ui.text.template.contentassist; import net.sourceforge.phpdt.internal.corext.template.php.CompilationUnitContext; import net.sourceforge.phpdt.internal.corext.template.php.JavaContext; import net.sourceforge.phpdt.internal.ui.text.java.IPHPCompletionProposal; import net.sourceforge.phpdt.internal.ui.util.ExceptionHandler; //import net.sourceforge.phpeclipse.PHPeclipsePlugin; import net.sourceforge.phpeclipse.phpeditor.EditorHighlightingSynchronizer; import net.sourceforge.phpeclipse.phpeditor.PHPEditor; import net.sourceforge.phpeclipse.ui.WebUI; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.MessageDialog; //incastrix //import org.eclipse.jface.text.Assert; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.link.ILinkedModeListener; 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.jface.text.link.ProposalPosition; import org.eclipse.jface.text.templates.DocumentTemplateContext; import org.eclipse.jface.text.templates.GlobalTemplateVariables; import org.eclipse.jface.text.templates.Template; import org.eclipse.jface.text.templates.TemplateBuffer; import org.eclipse.jface.text.templates.TemplateContext; import org.eclipse.jface.text.templates.TemplateException; import org.eclipse.jface.text.templates.TemplateVariable; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.texteditor.link.EditorLinkedModeUI; /** * A template proposal. */ public class TemplateProposal implements IPHPCompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension3 { private final Template fTemplate; private final TemplateContext fContext; private final Image fImage; private IRegion fRegion; private int fRelevance; private IRegion fSelectedRegion; // initialized by apply() private String fDisplayString; /** * Creates a template proposal with a template and its context. * * @param template * the template * @param context * the context in which the template was requested * @param region * the region this proposal applies to * @param image * the icon of the proposal */ public TemplateProposal(Template template, TemplateContext context, IRegion region, Image image) { Assert.isNotNull(template); Assert.isNotNull(context); Assert.isNotNull(region); fTemplate = template; fContext = context; fImage = image; fRegion = region; fDisplayString = null; if (context instanceof JavaContext) { switch (((JavaContext) context).getCharacterBeforeStart()) { // high relevance after whitespace case ' ': case '\r': case '\n': case '\t': fRelevance = 90; break; default: fRelevance = 0; } } else { fRelevance = 90; } } /* * @see ICompletionProposal#apply(IDocument) */ public final void apply(IDocument document) { // not called anymore } /* * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#apply(org.eclipse.jface.text.ITextViewer, * char, int, int) */ public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) { try { fContext.setReadOnly(false); TemplateBuffer templateBuffer; try { templateBuffer = fContext.evaluate(fTemplate); } catch (TemplateException e1) { fSelectedRegion = fRegion; return; } int start = getReplaceOffset(); int end = getReplaceEndOffset(); end = Math.max(end, offset); // insert template string IDocument document = viewer.getDocument(); String templateString = templateBuffer.getString(); document.replace(start, end - start, templateString); // translate positions LinkedModeModel model = new LinkedModeModel(); TemplateVariable[] variables = templateBuffer.getVariables(); MultiVariableGuess guess = fContext instanceof CompilationUnitContext ? ((CompilationUnitContext) fContext) .getMultiVariableGuess() : null; boolean hasPositions = false; for (int i = 0; i != variables.length; i++) { TemplateVariable variable = variables[i]; if (variable.isUnambiguous()) continue; LinkedPositionGroup group = new LinkedPositionGroup(); int[] offsets = variable.getOffsets(); int length = variable.getLength(); LinkedPosition first; if (guess != null && variable instanceof MultiVariable) { first = new VariablePosition(document, offsets[0] + start, length, guess, (MultiVariable) variable); guess.addSlave((VariablePosition) first); } else { String[] values = variable.getValues(); ICompletionProposal[] proposals = new ICompletionProposal[values.length]; for (int j = 0; j < values.length; j++) { ensurePositionCategoryInstalled(document, model); Position pos = new Position(offsets[0] + start, length); document.addPosition(getCategory(), pos); proposals[j] = new PositionBasedCompletionProposal( values[j], pos, length); } if (proposals.length > 1) first = new ProposalPosition(document, offsets[0] + start, length, proposals); else first = new LinkedPosition(document, offsets[0] + start, length); } for (int j = 0; j != offsets.length; j++) if (j == 0) group.addPosition(first); else group.addPosition(new LinkedPosition(document, offsets[j] + start, length)); model.addGroup(group); hasPositions = true; } if (hasPositions) { model.forceInstall(); PHPEditor editor = getJavaEditor(); if (editor != null) { model .addLinkingListener(new EditorHighlightingSynchronizer( editor)); } LinkedModeUI ui = new EditorLinkedModeUI(model, viewer); ui.setExitPosition(viewer, getCaretOffset(templateBuffer) + start, 0, Integer.MAX_VALUE); ui.enter(); fSelectedRegion = ui.getSelectedRegion(); } else fSelectedRegion = new Region(getCaretOffset(templateBuffer) + start, 0); } catch (BadLocationException e) { WebUI.log(e); openErrorDialog(viewer.getTextWidget().getShell(), e); fSelectedRegion = fRegion; } catch (BadPositionCategoryException e) { WebUI.log(e); openErrorDialog(viewer.getTextWidget().getShell(), e); fSelectedRegion = fRegion; } } /** * Returns the currently active java editor, or null if it * cannot be determined. * * @return the currently active java editor, or null */ private PHPEditor getJavaEditor() { IEditorPart part = WebUI.getActivePage().getActiveEditor(); if (part instanceof PHPEditor) return (PHPEditor) part; else return null; } /** * Returns the offset of the range in the document that will be replaced by * applying this template. * * @return the offset of the range in the document that will be replaced by * applying this template */ private int getReplaceOffset() { int start; if (fContext instanceof DocumentTemplateContext) { DocumentTemplateContext docContext = (DocumentTemplateContext) fContext; start = docContext.getStart(); } else { start = fRegion.getOffset(); } return start; } /** * Returns the end offset of the range in the document that will be replaced * by applying this template. * * @return the end offset of the range in the document that will be replaced * by applying this template */ private int getReplaceEndOffset() { int end; if (fContext instanceof DocumentTemplateContext) { DocumentTemplateContext docContext = (DocumentTemplateContext) fContext; end = docContext.getEnd(); } else { end = fRegion.getOffset() + fRegion.getLength(); } return end; } private void ensurePositionCategoryInstalled(final IDocument document, LinkedModeModel model) { if (!document.containsPositionCategory(getCategory())) { document.addPositionCategory(getCategory()); final InclusivePositionUpdater updater = new InclusivePositionUpdater( getCategory()); document.addPositionUpdater(updater); model.addLinkingListener(new ILinkedModeListener() { /* * @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel, * int) */ public void left(LinkedModeModel environment, int flags) { try { document.removePositionCategory(getCategory()); } catch (BadPositionCategoryException e) { // ignore } document.removePositionUpdater(updater); } public void suspend(LinkedModeModel environment) { } public void resume(LinkedModeModel environment, int flags) { } }); } } private String getCategory() { return "TemplateProposalCategory_" + toString(); //$NON-NLS-1$ } private int getCaretOffset(TemplateBuffer buffer) { TemplateVariable[] variables = buffer.getVariables(); for (int i = 0; i != variables.length; i++) { TemplateVariable variable = variables[i]; if (variable.getType().equals(GlobalTemplateVariables.Cursor.NAME)) return variable.getOffsets()[0]; } return buffer.getString().length(); } /* * @see ICompletionProposal#getSelection(IDocument) */ public Point getSelection(IDocument document) { return new Point(fSelectedRegion.getOffset(), fSelectedRegion .getLength()); } /* * @see ICompletionProposal#getAdditionalProposalInfo() */ public String getAdditionalProposalInfo() { try { fContext.setReadOnly(true); TemplateBuffer templateBuffer; try { templateBuffer = fContext.evaluate(fTemplate); } catch (TemplateException e1) { return null; } return templateBuffer.getString(); } catch (BadLocationException e) { handleException(WebUI.getActiveWorkbenchShell(), new CoreException(new Status(IStatus.ERROR, WebUI.getPluginId(), IStatus.OK, "", e))); //$NON-NLS-1$ return null; } } /* * @see ICompletionProposal#getDisplayString() */ public String getDisplayString() { if (fDisplayString == null) { fDisplayString = fTemplate.getName() + TemplateContentAssistMessages .getString("TemplateProposal.delimiter") + fTemplate.getDescription(); //$NON-NLS-1$ } return fDisplayString; } // public void setDisplayString(String displayString) { // fDisplayString = displayString; // } /* * @see ICompletionProposal#getImage() */ public Image getImage() { return fImage; } /* * @see ICompletionProposal#getContextInformation() */ public IContextInformation getContextInformation() { return null; } private void openErrorDialog(Shell shell, Exception e) { MessageDialog.openError(shell, TemplateContentAssistMessages .getString("TemplateEvaluator.error.title"), e.getMessage()); //$NON-NLS-1$ } private void handleException(Shell shell, CoreException e) { ExceptionHandler.handle(e, shell, TemplateContentAssistMessages .getString("TemplateEvaluator.error.title"), null); //$NON-NLS-1$ } /* * @see IJavaCompletionProposal#getRelevance() */ public int getRelevance() { return fRelevance; } // public void setRelevance(int relevance) { // fRelevance = relevance; // } // public Template getTemplate() { // return fTemplate; // } /* * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getInformationControlCreator() */ public IInformationControlCreator getInformationControlCreator() { return new TemplateInformationControlCreator(); } /* * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected(org.eclipse.jface.text.ITextViewer, * boolean) */ public void selected(ITextViewer viewer, boolean smartToggle) { } /* * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected(org.eclipse.jface.text.ITextViewer) */ public void unselected(ITextViewer viewer) { } /* * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate(org.eclipse.jface.text.IDocument, * int, org.eclipse.jface.text.DocumentEvent) */ public boolean validate(IDocument document, int offset, DocumentEvent event) { try { int replaceOffset = getReplaceOffset(); if (offset >= replaceOffset) { String content = document.get(replaceOffset, offset - replaceOffset); return fTemplate.getName().startsWith(content); } } catch (BadLocationException e) { // concurrent modification - ignore } return false; } /* * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementString() */ public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) { return fTemplate.getName(); } /* * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementOffset() */ public int getPrefixCompletionStart(IDocument document, int completionOffset) { return getReplaceOffset(); } }