1) Added missing strings for italic, underline and strike through.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / text / template / contentassist / TemplateProposal.java
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
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.text.template.contentassist;
12
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
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.core.runtime.IStatus;
23 import org.eclipse.core.runtime.Status;
24 import org.eclipse.jface.dialogs.MessageDialog;
25 //incastrix
26 //import org.eclipse.jface.text.Assert;
27 import org.eclipse.core.runtime.Assert;
28 import org.eclipse.jface.text.BadLocationException;
29 import org.eclipse.jface.text.BadPositionCategoryException;
30 import org.eclipse.jface.text.DocumentEvent;
31 import org.eclipse.jface.text.IDocument;
32 import org.eclipse.jface.text.IInformationControlCreator;
33 import org.eclipse.jface.text.IRegion;
34 import org.eclipse.jface.text.ITextViewer;
35 import org.eclipse.jface.text.Position;
36 import org.eclipse.jface.text.Region;
37 import org.eclipse.jface.text.contentassist.ICompletionProposal;
38 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
39 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
40 import org.eclipse.jface.text.contentassist.IContextInformation;
41 import org.eclipse.jface.text.link.ILinkedModeListener;
42 import org.eclipse.jface.text.link.LinkedModeModel;
43 import org.eclipse.jface.text.link.LinkedModeUI;
44 import org.eclipse.jface.text.link.LinkedPosition;
45 import org.eclipse.jface.text.link.LinkedPositionGroup;
46 import org.eclipse.jface.text.link.ProposalPosition;
47 import org.eclipse.jface.text.templates.DocumentTemplateContext;
48 import org.eclipse.jface.text.templates.GlobalTemplateVariables;
49 import org.eclipse.jface.text.templates.Template;
50 import org.eclipse.jface.text.templates.TemplateBuffer;
51 import org.eclipse.jface.text.templates.TemplateContext;
52 import org.eclipse.jface.text.templates.TemplateException;
53 import org.eclipse.jface.text.templates.TemplateVariable;
54 import org.eclipse.swt.graphics.Image;
55 import org.eclipse.swt.graphics.Point;
56 import org.eclipse.swt.widgets.Shell;
57 import org.eclipse.ui.IEditorPart;
58 import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
59
60 /**
61  * A template proposal.
62  */
63 public class TemplateProposal implements IPHPCompletionProposal,
64                 ICompletionProposalExtension2, ICompletionProposalExtension3 {
65
66         private final Template fTemplate;
67
68         private final TemplateContext fContext;
69
70         private final Image fImage;
71
72         private IRegion fRegion;
73
74         private int fRelevance;
75
76         private IRegion fSelectedRegion; // initialized by apply()
77
78         private String fDisplayString;
79
80         /**
81          * Creates a template proposal with a template and its context.
82          * 
83          * @param template
84          *            the template
85          * @param context
86          *            the context in which the template was requested
87          * @param region
88          *            the region this proposal applies to
89          * @param image
90          *            the icon of the proposal
91          */
92         public TemplateProposal(Template template, TemplateContext context,
93                         IRegion region, Image image) {
94                 Assert.isNotNull(template);
95                 Assert.isNotNull(context);
96                 Assert.isNotNull(region);
97
98                 fTemplate = template;
99                 fContext = context;
100                 fImage = image;
101                 fRegion = region;
102
103                 fDisplayString = null;
104
105                 if (context instanceof JavaContext) {
106                         switch (((JavaContext) context).getCharacterBeforeStart()) {
107                         // high relevance after whitespace
108                         case ' ':
109                         case '\r':
110                         case '\n':
111                         case '\t':
112                                 fRelevance = 90;
113                                 break;
114                         default:
115                                 fRelevance = 0;
116                         }
117                 } else {
118                         fRelevance = 90;
119                 }
120         }
121
122         /*
123          * @see ICompletionProposal#apply(IDocument)
124          */
125         public final void apply(IDocument document) {
126                 // not called anymore
127         }
128
129         /*
130          * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#apply(org.eclipse.jface.text.ITextViewer,
131          *      char, int, int)
132          */
133         public void apply(ITextViewer viewer, char trigger, int stateMask,
134                         int offset) {
135
136                 try {
137
138                         fContext.setReadOnly(false);
139                         TemplateBuffer templateBuffer;
140                         try {
141                                 templateBuffer = fContext.evaluate(fTemplate);
142                         } catch (TemplateException e1) {
143                                 fSelectedRegion = fRegion;
144                                 return;
145                         }
146
147                         int start = getReplaceOffset();
148                         int end = getReplaceEndOffset();
149                         end = Math.max(end, offset);
150
151                         // insert template string
152                         IDocument document = viewer.getDocument();
153                         String templateString = templateBuffer.getString();
154                         document.replace(start, end - start, templateString);
155
156                         // translate positions
157                         LinkedModeModel model = new LinkedModeModel();
158                         TemplateVariable[] variables = templateBuffer.getVariables();
159
160                         MultiVariableGuess guess = fContext instanceof CompilationUnitContext ? ((CompilationUnitContext) fContext)
161                                         .getMultiVariableGuess()
162                                         : null;
163
164                         boolean hasPositions = false;
165                         for (int i = 0; i != variables.length; i++) {
166                                 TemplateVariable variable = variables[i];
167
168                                 if (variable.isUnambiguous())
169                                         continue;
170
171                                 LinkedPositionGroup group = new LinkedPositionGroup();
172
173                                 int[] offsets = variable.getOffsets();
174                                 int length = variable.getLength();
175
176                                 LinkedPosition first;
177                                 if (guess != null && variable instanceof MultiVariable) {
178                                         first = new VariablePosition(document, offsets[0] + start,
179                                                         length, guess, (MultiVariable) variable);
180                                         guess.addSlave((VariablePosition) first);
181                                 } else {
182                                         String[] values = variable.getValues();
183                                         ICompletionProposal[] proposals = new ICompletionProposal[values.length];
184                                         for (int j = 0; j < values.length; j++) {
185                                                 ensurePositionCategoryInstalled(document, model);
186                                                 Position pos = new Position(offsets[0] + start, length);
187                                                 document.addPosition(getCategory(), pos);
188                                                 proposals[j] = new PositionBasedCompletionProposal(
189                                                                 values[j], pos, length);
190                                         }
191
192                                         if (proposals.length > 1)
193                                                 first = new ProposalPosition(document, offsets[0]
194                                                                 + start, length, proposals);
195                                         else
196                                                 first = new LinkedPosition(document,
197                                                                 offsets[0] + start, length);
198                                 }
199
200                                 for (int j = 0; j != offsets.length; j++)
201                                         if (j == 0)
202                                                 group.addPosition(first);
203                                         else
204                                                 group.addPosition(new LinkedPosition(document,
205                                                                 offsets[j] + start, length));
206
207                                 model.addGroup(group);
208                                 hasPositions = true;
209                         }
210
211                         if (hasPositions) {
212                                 model.forceInstall();
213                                 PHPEditor editor = getJavaEditor();
214                                 if (editor != null) {
215                                         model
216                                                         .addLinkingListener(new EditorHighlightingSynchronizer(
217                                                                         editor));
218                                 }
219
220                                 LinkedModeUI ui = new EditorLinkedModeUI(model, viewer);
221                                 ui.setExitPosition(viewer, getCaretOffset(templateBuffer)
222                                                 + start, 0, Integer.MAX_VALUE);
223                                 ui.enter();
224
225                                 fSelectedRegion = ui.getSelectedRegion();
226                         } else
227                                 fSelectedRegion = new Region(getCaretOffset(templateBuffer)
228                                                 + start, 0);
229
230                 } catch (BadLocationException e) {
231                         PHPeclipsePlugin.log(e);
232                         openErrorDialog(viewer.getTextWidget().getShell(), e);
233                         fSelectedRegion = fRegion;
234                 } catch (BadPositionCategoryException e) {
235                         PHPeclipsePlugin.log(e);
236                         openErrorDialog(viewer.getTextWidget().getShell(), e);
237                         fSelectedRegion = fRegion;
238                 }
239
240         }
241
242         /**
243          * Returns the currently active java editor, or <code>null</code> if it
244          * cannot be determined.
245          * 
246          * @return the currently active java editor, or <code>null</code>
247          */
248         private PHPEditor getJavaEditor() {
249                 IEditorPart part = PHPeclipsePlugin.getActivePage().getActiveEditor();
250                 if (part instanceof PHPEditor)
251                         return (PHPEditor) part;
252                 else
253                         return null;
254         }
255
256         /**
257          * Returns the offset of the range in the document that will be replaced by
258          * applying this template.
259          * 
260          * @return the offset of the range in the document that will be replaced by
261          *         applying this template
262          */
263         private int getReplaceOffset() {
264                 int start;
265                 if (fContext instanceof DocumentTemplateContext) {
266                         DocumentTemplateContext docContext = (DocumentTemplateContext) fContext;
267                         start = docContext.getStart();
268                 } else {
269                         start = fRegion.getOffset();
270                 }
271                 return start;
272         }
273
274         /**
275          * Returns the end offset of the range in the document that will be replaced
276          * by applying this template.
277          * 
278          * @return the end offset of the range in the document that will be replaced
279          *         by applying this template
280          */
281         private int getReplaceEndOffset() {
282                 int end;
283                 if (fContext instanceof DocumentTemplateContext) {
284                         DocumentTemplateContext docContext = (DocumentTemplateContext) fContext;
285                         end = docContext.getEnd();
286                 } else {
287                         end = fRegion.getOffset() + fRegion.getLength();
288                 }
289                 return end;
290         }
291
292         private void ensurePositionCategoryInstalled(final IDocument document,
293                         LinkedModeModel model) {
294                 if (!document.containsPositionCategory(getCategory())) {
295                         document.addPositionCategory(getCategory());
296                         final InclusivePositionUpdater updater = new InclusivePositionUpdater(
297                                         getCategory());
298                         document.addPositionUpdater(updater);
299
300                         model.addLinkingListener(new ILinkedModeListener() {
301
302                                 /*
303                                  * @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel,
304                                  *      int)
305                                  */
306                                 public void left(LinkedModeModel environment, int flags) {
307                                         try {
308                                                 document.removePositionCategory(getCategory());
309                                         } catch (BadPositionCategoryException e) {
310                                                 // ignore
311                                         }
312                                         document.removePositionUpdater(updater);
313                                 }
314
315                                 public void suspend(LinkedModeModel environment) {
316                                 }
317
318                                 public void resume(LinkedModeModel environment, int flags) {
319                                 }
320                         });
321                 }
322         }
323
324         private String getCategory() {
325                 return "TemplateProposalCategory_" + toString(); //$NON-NLS-1$
326         }
327
328         private int getCaretOffset(TemplateBuffer buffer) {
329
330                 TemplateVariable[] variables = buffer.getVariables();
331                 for (int i = 0; i != variables.length; i++) {
332                         TemplateVariable variable = variables[i];
333                         if (variable.getType().equals(GlobalTemplateVariables.Cursor.NAME))
334                                 return variable.getOffsets()[0];
335                 }
336
337                 return buffer.getString().length();
338         }
339
340         /*
341          * @see ICompletionProposal#getSelection(IDocument)
342          */
343         public Point getSelection(IDocument document) {
344                 return new Point(fSelectedRegion.getOffset(), fSelectedRegion
345                                 .getLength());
346         }
347
348         /*
349          * @see ICompletionProposal#getAdditionalProposalInfo()
350          */
351         public String getAdditionalProposalInfo() {
352                 try {
353                         fContext.setReadOnly(true);
354                         TemplateBuffer templateBuffer;
355                         try {
356                                 templateBuffer = fContext.evaluate(fTemplate);
357                         } catch (TemplateException e1) {
358                                 return null;
359                         }
360
361                         return templateBuffer.getString();
362
363                 } catch (BadLocationException e) {
364                         handleException(PHPeclipsePlugin.getActiveWorkbenchShell(),
365                                         new CoreException(new Status(IStatus.ERROR,
366                                                         PHPeclipsePlugin.getPluginId(), IStatus.OK, "", e))); //$NON-NLS-1$
367                         return null;
368                 }
369         }
370
371         /*
372          * @see ICompletionProposal#getDisplayString()
373          */
374         public String getDisplayString() {
375                 if (fDisplayString == null) {
376                         fDisplayString = fTemplate.getName()
377                                         + TemplateContentAssistMessages
378                                                         .getString("TemplateProposal.delimiter") + fTemplate.getDescription(); //$NON-NLS-1$
379                 }
380                 return fDisplayString;
381         }
382
383 //      public void setDisplayString(String displayString) {
384 //              fDisplayString = displayString;
385 //      }
386
387         /*
388          * @see ICompletionProposal#getImage()
389          */
390         public Image getImage() {
391                 return fImage;
392         }
393
394         /*
395          * @see ICompletionProposal#getContextInformation()
396          */
397         public IContextInformation getContextInformation() {
398                 return null;
399         }
400
401         private void openErrorDialog(Shell shell, Exception e) {
402                 MessageDialog.openError(shell, TemplateContentAssistMessages
403                                 .getString("TemplateEvaluator.error.title"), e.getMessage()); //$NON-NLS-1$
404         }
405
406         private void handleException(Shell shell, CoreException e) {
407                 ExceptionHandler.handle(e, shell, TemplateContentAssistMessages
408                                 .getString("TemplateEvaluator.error.title"), null); //$NON-NLS-1$
409         }
410
411         /*
412          * @see IJavaCompletionProposal#getRelevance()
413          */
414         public int getRelevance() {
415                 return fRelevance;
416         }
417
418 //      public void setRelevance(int relevance) {
419 //              fRelevance = relevance;
420 //      }
421
422 //      public Template getTemplate() {
423 //              return fTemplate;
424 //      }
425
426         /*
427          * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getInformationControlCreator()
428          */
429         public IInformationControlCreator getInformationControlCreator() {
430                 return new TemplateInformationControlCreator();
431         }
432
433         /*
434          * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected(org.eclipse.jface.text.ITextViewer,
435          *      boolean)
436          */
437         public void selected(ITextViewer viewer, boolean smartToggle) {
438         }
439
440         /*
441          * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected(org.eclipse.jface.text.ITextViewer)
442          */
443         public void unselected(ITextViewer viewer) {
444         }
445
446         /*
447          * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate(org.eclipse.jface.text.IDocument,
448          *      int, org.eclipse.jface.text.DocumentEvent)
449          */
450         public boolean validate(IDocument document, int offset, DocumentEvent event) {
451                 try {
452                         int replaceOffset = getReplaceOffset();
453                         if (offset >= replaceOffset) {
454                                 String content = document.get(replaceOffset, offset
455                                                 - replaceOffset);
456                                 return fTemplate.getName().startsWith(content);
457                         }
458                 } catch (BadLocationException e) {
459                         // concurrent modification - ignore
460                 }
461                 return false;
462         }
463
464         /*
465          * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementString()
466          */
467         public CharSequence getPrefixCompletionText(IDocument document,
468                         int completionOffset) {
469                 return fTemplate.getName();
470         }
471
472         /*
473          * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementOffset()
474          */
475         public int getPrefixCompletionStart(IDocument document, int completionOffset) {
476                 return getReplaceOffset();
477         }
478 }