1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.text.template.contentassist;
13 import net.sourceforge.phpdt.internal.corext.template.php.CompilationUnitContext;
14 import net.sourceforge.phpdt.internal.corext.template.php.JavaContext;
15 import net.sourceforge.phpdt.internal.ui.text.java.IPHPCompletionProposal;
16 import net.sourceforge.phpdt.internal.ui.util.ExceptionHandler;
17 //import net.sourceforge.phpeclipse.PHPeclipsePlugin;
18 import net.sourceforge.phpeclipse.phpeditor.EditorHighlightingSynchronizer;
19 import net.sourceforge.phpeclipse.phpeditor.PHPEditor;
20 import net.sourceforge.phpeclipse.ui.WebUI;
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.Status;
25 import org.eclipse.jface.dialogs.MessageDialog;
26 import org.eclipse.jface.text.Assert;
27 import org.eclipse.jface.text.BadLocationException;
28 import org.eclipse.jface.text.BadPositionCategoryException;
29 import org.eclipse.jface.text.DocumentEvent;
30 import org.eclipse.jface.text.IDocument;
31 import org.eclipse.jface.text.IInformationControlCreator;
32 import org.eclipse.jface.text.IRegion;
33 import org.eclipse.jface.text.ITextViewer;
34 import org.eclipse.jface.text.Position;
35 import org.eclipse.jface.text.Region;
36 import org.eclipse.jface.text.contentassist.ICompletionProposal;
37 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
38 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
39 import org.eclipse.jface.text.contentassist.IContextInformation;
40 import org.eclipse.jface.text.link.ILinkedModeListener;
41 import org.eclipse.jface.text.link.LinkedModeModel;
42 import org.eclipse.jface.text.link.LinkedModeUI;
43 import org.eclipse.jface.text.link.LinkedPosition;
44 import org.eclipse.jface.text.link.LinkedPositionGroup;
45 import org.eclipse.jface.text.link.ProposalPosition;
46 import org.eclipse.jface.text.templates.DocumentTemplateContext;
47 import org.eclipse.jface.text.templates.GlobalTemplateVariables;
48 import org.eclipse.jface.text.templates.Template;
49 import org.eclipse.jface.text.templates.TemplateBuffer;
50 import org.eclipse.jface.text.templates.TemplateContext;
51 import org.eclipse.jface.text.templates.TemplateException;
52 import org.eclipse.jface.text.templates.TemplateVariable;
53 import org.eclipse.swt.graphics.Image;
54 import org.eclipse.swt.graphics.Point;
55 import org.eclipse.swt.widgets.Shell;
56 import org.eclipse.ui.IEditorPart;
57 import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
60 * A template proposal.
62 public class TemplateProposal implements IPHPCompletionProposal,
63 ICompletionProposalExtension2, ICompletionProposalExtension3 {
65 private final Template fTemplate;
67 private final TemplateContext fContext;
69 private final Image fImage;
71 private IRegion fRegion;
73 private int fRelevance;
75 private IRegion fSelectedRegion; // initialized by apply()
77 private String fDisplayString;
80 * Creates a template proposal with a template and its context.
85 * the context in which the template was requested
87 * the region this proposal applies to
89 * the icon of the proposal
91 public TemplateProposal(Template template, TemplateContext context,
92 IRegion region, Image image) {
93 Assert.isNotNull(template);
94 Assert.isNotNull(context);
95 Assert.isNotNull(region);
102 fDisplayString = null;
104 if (context instanceof JavaContext) {
105 switch (((JavaContext) context).getCharacterBeforeStart()) {
106 // high relevance after whitespace
122 * @see ICompletionProposal#apply(IDocument)
124 public final void apply(IDocument document) {
125 // not called anymore
129 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#apply(org.eclipse.jface.text.ITextViewer,
132 public void apply(ITextViewer viewer, char trigger, int stateMask,
137 fContext.setReadOnly(false);
138 TemplateBuffer templateBuffer;
140 templateBuffer = fContext.evaluate(fTemplate);
141 } catch (TemplateException e1) {
142 fSelectedRegion = fRegion;
146 int start = getReplaceOffset();
147 int end = getReplaceEndOffset();
148 end = Math.max(end, offset);
150 // insert template string
151 IDocument document = viewer.getDocument();
152 String templateString = templateBuffer.getString();
153 document.replace(start, end - start, templateString);
155 // translate positions
156 LinkedModeModel model = new LinkedModeModel();
157 TemplateVariable[] variables = templateBuffer.getVariables();
159 MultiVariableGuess guess = fContext instanceof CompilationUnitContext ? ((CompilationUnitContext) fContext)
160 .getMultiVariableGuess()
163 boolean hasPositions = false;
164 for (int i = 0; i != variables.length; i++) {
165 TemplateVariable variable = variables[i];
167 if (variable.isUnambiguous())
170 LinkedPositionGroup group = new LinkedPositionGroup();
172 int[] offsets = variable.getOffsets();
173 int length = variable.getLength();
175 LinkedPosition first;
176 if (guess != null && variable instanceof MultiVariable) {
177 first = new VariablePosition(document, offsets[0] + start,
178 length, guess, (MultiVariable) variable);
179 guess.addSlave((VariablePosition) first);
181 String[] values = variable.getValues();
182 ICompletionProposal[] proposals = new ICompletionProposal[values.length];
183 for (int j = 0; j < values.length; j++) {
184 ensurePositionCategoryInstalled(document, model);
185 Position pos = new Position(offsets[0] + start, length);
186 document.addPosition(getCategory(), pos);
187 proposals[j] = new PositionBasedCompletionProposal(
188 values[j], pos, length);
191 if (proposals.length > 1)
192 first = new ProposalPosition(document, offsets[0]
193 + start, length, proposals);
195 first = new LinkedPosition(document,
196 offsets[0] + start, length);
199 for (int j = 0; j != offsets.length; j++)
201 group.addPosition(first);
203 group.addPosition(new LinkedPosition(document,
204 offsets[j] + start, length));
206 model.addGroup(group);
211 model.forceInstall();
212 PHPEditor editor = getJavaEditor();
213 if (editor != null) {
215 .addLinkingListener(new EditorHighlightingSynchronizer(
219 LinkedModeUI ui = new EditorLinkedModeUI(model, viewer);
220 ui.setExitPosition(viewer, getCaretOffset(templateBuffer)
221 + start, 0, Integer.MAX_VALUE);
224 fSelectedRegion = ui.getSelectedRegion();
226 fSelectedRegion = new Region(getCaretOffset(templateBuffer)
229 } catch (BadLocationException e) {
231 openErrorDialog(viewer.getTextWidget().getShell(), e);
232 fSelectedRegion = fRegion;
233 } catch (BadPositionCategoryException e) {
235 openErrorDialog(viewer.getTextWidget().getShell(), e);
236 fSelectedRegion = fRegion;
242 * Returns the currently active java editor, or <code>null</code> if it
243 * cannot be determined.
245 * @return the currently active java editor, or <code>null</code>
247 private PHPEditor getJavaEditor() {
248 IEditorPart part = WebUI.getActivePage().getActiveEditor();
249 if (part instanceof PHPEditor)
250 return (PHPEditor) part;
256 * Returns the offset of the range in the document that will be replaced by
257 * applying this template.
259 * @return the offset of the range in the document that will be replaced by
260 * applying this template
262 private int getReplaceOffset() {
264 if (fContext instanceof DocumentTemplateContext) {
265 DocumentTemplateContext docContext = (DocumentTemplateContext) fContext;
266 start = docContext.getStart();
268 start = fRegion.getOffset();
274 * Returns the end offset of the range in the document that will be replaced
275 * by applying this template.
277 * @return the end offset of the range in the document that will be replaced
278 * by applying this template
280 private int getReplaceEndOffset() {
282 if (fContext instanceof DocumentTemplateContext) {
283 DocumentTemplateContext docContext = (DocumentTemplateContext) fContext;
284 end = docContext.getEnd();
286 end = fRegion.getOffset() + fRegion.getLength();
291 private void ensurePositionCategoryInstalled(final IDocument document,
292 LinkedModeModel model) {
293 if (!document.containsPositionCategory(getCategory())) {
294 document.addPositionCategory(getCategory());
295 final InclusivePositionUpdater updater = new InclusivePositionUpdater(
297 document.addPositionUpdater(updater);
299 model.addLinkingListener(new ILinkedModeListener() {
302 * @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel,
305 public void left(LinkedModeModel environment, int flags) {
307 document.removePositionCategory(getCategory());
308 } catch (BadPositionCategoryException e) {
311 document.removePositionUpdater(updater);
314 public void suspend(LinkedModeModel environment) {
317 public void resume(LinkedModeModel environment, int flags) {
323 private String getCategory() {
324 return "TemplateProposalCategory_" + toString(); //$NON-NLS-1$
327 private int getCaretOffset(TemplateBuffer buffer) {
329 TemplateVariable[] variables = buffer.getVariables();
330 for (int i = 0; i != variables.length; i++) {
331 TemplateVariable variable = variables[i];
332 if (variable.getType().equals(GlobalTemplateVariables.Cursor.NAME))
333 return variable.getOffsets()[0];
336 return buffer.getString().length();
340 * @see ICompletionProposal#getSelection(IDocument)
342 public Point getSelection(IDocument document) {
343 return new Point(fSelectedRegion.getOffset(), fSelectedRegion
348 * @see ICompletionProposal#getAdditionalProposalInfo()
350 public String getAdditionalProposalInfo() {
352 fContext.setReadOnly(true);
353 TemplateBuffer templateBuffer;
355 templateBuffer = fContext.evaluate(fTemplate);
356 } catch (TemplateException e1) {
360 return templateBuffer.getString();
362 } catch (BadLocationException e) {
363 handleException(WebUI.getActiveWorkbenchShell(),
364 new CoreException(new Status(IStatus.ERROR,
365 WebUI.getPluginId(), IStatus.OK, "", e))); //$NON-NLS-1$
371 * @see ICompletionProposal#getDisplayString()
373 public String getDisplayString() {
374 if (fDisplayString == null) {
375 fDisplayString = fTemplate.getName()
376 + TemplateContentAssistMessages
377 .getString("TemplateProposal.delimiter") + fTemplate.getDescription(); //$NON-NLS-1$
379 return fDisplayString;
382 public void setDisplayString(String displayString) {
383 fDisplayString = displayString;
387 * @see ICompletionProposal#getImage()
389 public Image getImage() {
394 * @see ICompletionProposal#getContextInformation()
396 public IContextInformation getContextInformation() {
400 private void openErrorDialog(Shell shell, Exception e) {
401 MessageDialog.openError(shell, TemplateContentAssistMessages
402 .getString("TemplateEvaluator.error.title"), e.getMessage()); //$NON-NLS-1$
405 private void handleException(Shell shell, CoreException e) {
406 ExceptionHandler.handle(e, shell, TemplateContentAssistMessages
407 .getString("TemplateEvaluator.error.title"), null); //$NON-NLS-1$
411 * @see IJavaCompletionProposal#getRelevance()
413 public int getRelevance() {
417 public void setRelevance(int relevance) {
418 fRelevance = relevance;
421 public Template getTemplate() {
426 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getInformationControlCreator()
428 public IInformationControlCreator getInformationControlCreator() {
429 return new TemplateInformationControlCreator();
433 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected(org.eclipse.jface.text.ITextViewer,
436 public void selected(ITextViewer viewer, boolean smartToggle) {
440 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected(org.eclipse.jface.text.ITextViewer)
442 public void unselected(ITextViewer viewer) {
446 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate(org.eclipse.jface.text.IDocument,
447 * int, org.eclipse.jface.text.DocumentEvent)
449 public boolean validate(IDocument document, int offset, DocumentEvent event) {
451 int replaceOffset = getReplaceOffset();
452 if (offset >= replaceOffset) {
453 String content = document.get(replaceOffset, offset
455 return fTemplate.getName().startsWith(content);
457 } catch (BadLocationException e) {
458 // concurrent modification - ignore
464 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementString()
466 public CharSequence getPrefixCompletionText(IDocument document,
467 int completionOffset) {
468 return fTemplate.getName();
472 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementOffset()
474 public int getPrefixCompletionStart(IDocument document, int completionOffset) {
475 return getReplaceOffset();