e0e5151378afb99e33e20fcdc9ffe5fd330969e5
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpeclipse / phpeditor / PHPUnitEditor.java
1 package net.sourceforge.phpeclipse.phpeditor;
2
3 import java.text.MessageFormat;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.Map;
9
10 import net.sourceforge.phpdt.core.ICompilationUnit;
11 import net.sourceforge.phpdt.core.IJavaElement;
12 import net.sourceforge.phpdt.core.IJavaProject;
13 import net.sourceforge.phpdt.core.IMember;
14 import net.sourceforge.phpdt.core.ISourceRange;
15 import net.sourceforge.phpdt.core.ISourceReference;
16 import net.sourceforge.phpdt.core.JavaCore;
17 import net.sourceforge.phpdt.core.JavaModelException;
18 import net.sourceforge.phpdt.core.dom.CompilationUnit;
19 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
20 import net.sourceforge.phpdt.internal.corext.codemanipulation.StubUtility;
21 import net.sourceforge.phpdt.internal.ui.actions.AddBlockCommentAction;
22 import net.sourceforge.phpdt.internal.ui.actions.CompositeActionGroup;
23 import net.sourceforge.phpdt.internal.ui.actions.IndentAction;
24 import net.sourceforge.phpdt.internal.ui.actions.RemoveBlockCommentAction;
25 import net.sourceforge.phpdt.internal.ui.text.ContentAssistPreference;
26 import net.sourceforge.phpdt.internal.ui.text.IPHPPartitions;
27 import net.sourceforge.phpdt.internal.ui.text.JavaHeuristicScanner;
28 import net.sourceforge.phpdt.internal.ui.text.JavaIndenter;
29 import net.sourceforge.phpdt.internal.ui.text.PHPPairMatcher;
30 import net.sourceforge.phpdt.internal.ui.text.SmartBackspaceManager;
31 import net.sourceforge.phpdt.internal.ui.text.SmartSemicolonAutoEditStrategy;
32 import net.sourceforge.phpdt.internal.ui.text.comment.CommentFormattingContext;
33 import net.sourceforge.phpdt.internal.ui.text.java.IJavaReconcilingListener;
34 import net.sourceforge.phpdt.internal.ui.text.link.LinkedPositionManager;
35 import net.sourceforge.phpdt.internal.ui.text.link.LinkedPositionUI;
36 import net.sourceforge.phpdt.internal.ui.text.link.LinkedPositionUI.ExitFlags;
37 import net.sourceforge.phpdt.ui.IWorkingCopyManager;
38 import net.sourceforge.phpdt.ui.PreferenceConstants;
39 import net.sourceforge.phpdt.ui.actions.GenerateActionGroup;
40 import net.sourceforge.phpdt.ui.text.JavaTextTools;
41 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
42 import net.sourceforge.phpeclipse.phpeditor.actions.RTrimAction;
43 import net.sourceforge.phpeclipse.ui.WebUI;
44 import net.sourceforge.phpeclipse.ui.editor.ShowExternalPreviewAction;
45
46 import org.eclipse.core.resources.IFile;
47 import org.eclipse.core.resources.IWorkspaceRoot;
48 import org.eclipse.core.resources.ResourcesPlugin;
49 import org.eclipse.core.runtime.CoreException;
50 import org.eclipse.core.runtime.IPath;
51 import org.eclipse.core.runtime.IProgressMonitor;
52 import org.eclipse.core.runtime.IStatus;
53 import org.eclipse.core.runtime.Preferences;
54 import org.eclipse.jface.action.Action;
55 import org.eclipse.jface.action.IAction;
56 import org.eclipse.jface.action.IMenuManager;
57 import org.eclipse.jface.dialogs.ErrorDialog;
58 import org.eclipse.jface.dialogs.IMessageProvider;
59 import org.eclipse.jface.dialogs.MessageDialog;
60 import org.eclipse.jface.preference.IPreferenceStore;
61 import org.eclipse.jface.preference.PreferenceConverter;
62 import org.eclipse.jface.text.BadLocationException;
63 import org.eclipse.jface.text.DocumentCommand;
64 import org.eclipse.jface.text.IAutoEditStrategy;
65 import org.eclipse.jface.text.IDocument;
66 import org.eclipse.jface.text.ILineTracker;
67 import org.eclipse.jface.text.IRegion;
68 import org.eclipse.jface.text.ITextOperationTarget;
69 import org.eclipse.jface.text.ITextViewerExtension;
70 import org.eclipse.jface.text.ITypedRegion;
71 import org.eclipse.jface.text.IWidgetTokenKeeper;
72 import org.eclipse.jface.text.contentassist.ContentAssistant;
73 import org.eclipse.jface.text.contentassist.IContentAssistant;
74 import org.eclipse.jface.text.formatter.FormattingContextProperties;
75 import org.eclipse.jface.text.formatter.IFormattingContext;
76 import org.eclipse.jface.text.source.IOverviewRuler;
77 import org.eclipse.jface.text.source.ISourceViewer;
78 import org.eclipse.jface.text.source.IVerticalRuler;
79 import org.eclipse.jface.text.source.SourceViewerConfiguration;
80 import org.eclipse.jface.util.ListenerList;
81 import org.eclipse.jface.util.PropertyChangeEvent;
82 import org.eclipse.jface.window.Window;
83 import org.eclipse.swt.SWT;
84 import org.eclipse.swt.custom.VerifyKeyListener;
85 import org.eclipse.swt.events.VerifyEvent;
86 import org.eclipse.swt.graphics.Color;
87 import org.eclipse.swt.graphics.Point;
88 import org.eclipse.swt.graphics.RGB;
89 import org.eclipse.swt.widgets.Composite;
90 import org.eclipse.swt.widgets.Display;
91 import org.eclipse.swt.widgets.Shell;
92 import org.eclipse.ui.IEditorInput;
93 import org.eclipse.ui.IEditorPart;
94 import org.eclipse.ui.IFileEditorInput;
95 import org.eclipse.ui.IWorkbenchPage;
96 import org.eclipse.ui.IWorkbenchWindow;
97 import org.eclipse.ui.PlatformUI;
98 import org.eclipse.ui.actions.ActionContext;
99 import org.eclipse.ui.actions.ActionGroup;
100 import org.eclipse.ui.dialogs.SaveAsDialog;
101 import org.eclipse.ui.editors.text.IStorageDocumentProvider;
102 import org.eclipse.ui.part.FileEditorInput;
103 import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
104 import org.eclipse.ui.texteditor.ContentAssistAction;
105 import org.eclipse.ui.texteditor.IDocumentProvider;
106 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
107 import org.eclipse.ui.texteditor.TextOperationAction;
108
109 /*******************************************************************************
110  * Copyright (c) 2000, 2002 IBM Corp. and others. All rights reserved. This
111  * program and the accompanying materials are made available under the terms of
112  * the Common Public License v1.0 which accompanies this distribution, and is
113  * available at http://www.eclipse.org/legal/cpl-v10.html
114  *
115  * Contributors: IBM Corporation - Initial implementation
116  * www.phpeclipse.de
117  ******************************************************************************/
118 /**
119  * PHP specific text editor.
120  */
121 public class PHPUnitEditor extends PHPEditor { // implements
122         // IJavaReconcilingListener {
123         interface ITextConverter {
124                 void customizeDocumentCommand(IDocument document,
125                                 DocumentCommand command);
126         };
127
128         // class AdaptedSourceViewer extends JavaSourceViewer {
129         // private List fTextConverters;
130         //
131         // private boolean fIgnoreTextConverters = false;
132         //
133         // // private JavaCorrectionAssistant fCorrectionAssistant;
134         // public AdaptedSourceViewer(Composite parent, IVerticalRuler
135         // verticalRuler,
136         // IOverviewRuler overviewRuler, boolean showAnnotationsOverview,
137         // int styles, IPreferenceStore store) {
138         // super(parent, verticalRuler, overviewRuler, showAnnotationsOverview,
139         // styles, store);
140         // }
141         //
142         // // public AdaptedSourceViewer(Composite parent,
143         // // IVerticalRuler verticalRuler, IOverviewRuler overviewRuler,
144         // // boolean showAnnotationsOverview, int styles) {
145         // // super(parent, verticalRuler, overviewRuler,
146         // // showAnnotationsOverview, styles);
147         // // }
148         // public IContentAssistant getContentAssistant() {
149         // return fContentAssistant;
150         // }
151         //
152         // /*
153         // * @see ITextOperationTarget#doOperation(int)
154         // */
155         // public void doOperation(int operation) {
156         // if (getTextWidget() == null)
157         // return;
158         // switch (operation) {
159         // case CONTENTASSIST_PROPOSALS:
160         // String msg = fContentAssistant.showPossibleCompletions();
161         // setStatusLineErrorMessage(msg);
162         // return;
163         // // case CORRECTIONASSIST_PROPOSALS:
164         // // fCorrectionAssistant.showPossibleCompletions();
165         // // return;
166         // case UNDO:
167         // fIgnoreTextConverters = true;
168         // break;
169         // case REDO:
170         // fIgnoreTextConverters = true;
171         // break;
172         // }
173         // super.doOperation(operation);
174         // }
175         //
176         // /*
177         // * @see ITextOperationTarget#canDoOperation(int)
178         // */
179         // public boolean canDoOperation(int operation) {
180         // // if (operation == CORRECTIONASSIST_PROPOSALS)
181         // // return isEditable();
182         // return super.canDoOperation(operation);
183         // }
184         //
185         // /*
186         // * @see TextViewer#handleDispose()
187         // */
188         // protected void handleDispose() {
189         // // if (fCorrectionAssistant != null) {
190         // // fCorrectionAssistant.uninstall();
191         // // fCorrectionAssistant= null;
192         // // }
193         // super.handleDispose();
194         // }
195         //
196         // public void insertTextConverter(ITextConverter textConverter, int index)
197         // {
198         // throw new UnsupportedOperationException();
199         // }
200         //
201         // public void addTextConverter(ITextConverter textConverter) {
202         // if (fTextConverters == null) {
203         // fTextConverters = new ArrayList(1);
204         // fTextConverters.add(textConverter);
205         // } else if (!fTextConverters.contains(textConverter))
206         // fTextConverters.add(textConverter);
207         // }
208         //
209         // public void removeTextConverter(ITextConverter textConverter) {
210         // if (fTextConverters != null) {
211         // fTextConverters.remove(textConverter);
212         // if (fTextConverters.size() == 0)
213         // fTextConverters = null;
214         // }
215         // }
216         //
217         // /*
218         // * @see TextViewer#customizeDocumentCommand(DocumentCommand)
219         // */
220         // protected void customizeDocumentCommand(DocumentCommand command) {
221         // super.customizeDocumentCommand(command);
222         // if (!fIgnoreTextConverters && fTextConverters != null) {
223         // for (Iterator e = fTextConverters.iterator(); e.hasNext();)
224         // ((ITextConverter) e.next()).customizeDocumentCommand(getDocument(),
225         // command);
226         // }
227         // fIgnoreTextConverters = false;
228         // }
229         //
230         // // http://dev.eclipse.org/bugs/show_bug.cgi?id=19270
231         // public void updateIndentationPrefixes() {
232         // SourceViewerConfiguration configuration = getSourceViewerConfiguration();
233         // String[] types = configuration.getConfiguredContentTypes(this);
234         // for (int i = 0; i < types.length; i++) {
235         // String[] prefixes = configuration.getIndentPrefixes(this, types[i]);
236         // if (prefixes != null && prefixes.length > 0)
237         // setIndentPrefixes(prefixes, types[i]);
238         // }
239         // }
240         //
241         // /*
242         // * @see IWidgetTokenOwner#requestWidgetToken(IWidgetTokenKeeper)
243         // */
244         // public boolean requestWidgetToken(IWidgetTokenKeeper requester) {
245         // if (WorkbenchHelp.isContextHelpDisplayed())
246         // return false;
247         // return super.requestWidgetToken(requester);
248         // }
249         //
250         // // /*
251         // // * @see
252         // org.eclipse.jface.text.source.ISourceViewer#configure(org.eclipse.jface.text.source.SourceViewerConfiguration)
253         // // */
254         // // public void configure(SourceViewerConfiguration configuration) {
255         // // super.configure(configuration);
256         // // // fCorrectionAssistant= new
257         // // // JavaCorrectionAssistant(CompilationUnitEditor.this);
258         // // // fCorrectionAssistant.install(this);
259         // // //TODO install SmartBracesAutoEditStrategy
260         // // // prependAutoEditStrategy(new SmartBracesAutoEditStrategy(this),
261         // // // IDocument.DEFAULT_CONTENT_TYPE);
262         // // }
263         // public void configure(SourceViewerConfiguration configuration) {
264         // super.configure(configuration);
265         // // fCorrectionAssistant= new
266         // JavaCorrectionAssistant(CompilationUnitEditor.this);
267         // // fCorrectionAssistant.install(this);
268         // IAutoEditStrategy smartSemi= new
269         // SmartSemicolonAutoEditStrategy(IPHPPartitions.PHP_PARTITIONING);
270         // prependAutoEditStrategy(smartSemi, IDocument.DEFAULT_CONTENT_TYPE);
271         // prependAutoEditStrategy(smartSemi, IPHPPartitions.PHP_STRING_DQ);
272         // prependAutoEditStrategy(smartSemi, IPHPPartitions.PHP_STRING_SQ);
273         // // prependAutoEditStrategy(smartSemi, IPHPPartitions.JAVA_CHARACTER);
274         // }
275         // };
276         class AdaptedSourceViewer extends JavaSourceViewer {
277
278                 private List fTextConverters;
279
280                 private boolean fIgnoreTextConverters = false;
281
282                 // private JavaCorrectionAssistant fCorrectionAssistant;
283
284                 public AdaptedSourceViewer(Composite parent,
285                                 IVerticalRuler verticalRuler, IOverviewRuler overviewRuler,
286                                 boolean showAnnotationsOverview, int styles,
287                                 IPreferenceStore store) {
288                         super(parent, verticalRuler, overviewRuler,
289                                         showAnnotationsOverview, styles, store);
290                 }
291
292                 public IContentAssistant getContentAssistant() {
293                         return fContentAssistant;
294                 }
295
296                 /*
297                  * @see ITextOperationTarget#doOperation(int)
298                  */
299                 public void doOperation(int operation) {
300
301                         if (getTextWidget() == null)
302                                 return;
303
304                         switch (operation) {
305                         case CONTENTASSIST_PROPOSALS:
306                                 String msg = fContentAssistant.showPossibleCompletions();
307                                 setStatusLineErrorMessage(msg);
308                                 return;
309                                 // case CORRECTIONASSIST_PROPOSALS:
310                                 // msg = fCorrectionAssistant.showPossibleCompletions();
311                                 // setStatusLineErrorMessage(msg);
312                                 // return;
313                         case UNDO:
314                                 fIgnoreTextConverters = true;
315                                 super.doOperation(operation);
316                                 fIgnoreTextConverters = false;
317                                 return;
318                         case REDO:
319                                 fIgnoreTextConverters = true;
320                                 super.doOperation(operation);
321                                 fIgnoreTextConverters = false;
322                                 return;
323                         }
324
325                         super.doOperation(operation);
326                 }
327
328                 /*
329                  * @see ITextOperationTarget#canDoOperation(int)
330                  */
331                 public boolean canDoOperation(int operation) {
332                         // if (operation == CORRECTIONASSIST_PROPOSALS)
333                         // return isEditable();
334
335                         return super.canDoOperation(operation);
336                 }
337
338                 /*
339                  * @see org.eclipse.jface.text.source.ISourceViewerExtension2#unconfigure()
340                  * @since 3.0
341                  */
342                 public void unconfigure() {
343                         // if (fCorrectionAssistant != null) {
344                         // fCorrectionAssistant.uninstall();
345                         // fCorrectionAssistant = null;
346                         // }
347                         super.unconfigure();
348                 }
349
350                 public void insertTextConverter(ITextConverter textConverter, int index) {
351                         throw new UnsupportedOperationException();
352                 }
353
354                 public void addTextConverter(ITextConverter textConverter) {
355                         if (fTextConverters == null) {
356                                 fTextConverters = new ArrayList(1);
357                                 fTextConverters.add(textConverter);
358                         } else if (!fTextConverters.contains(textConverter))
359                                 fTextConverters.add(textConverter);
360                 }
361
362                 public void removeTextConverter(ITextConverter textConverter) {
363                         if (fTextConverters != null) {
364                                 fTextConverters.remove(textConverter);
365                                 if (fTextConverters.size() == 0)
366                                         fTextConverters = null;
367                         }
368                 }
369
370                 /*
371                  * @see TextViewer#customizeDocumentCommand(DocumentCommand)
372                  */
373                 protected void customizeDocumentCommand(DocumentCommand command) {
374                         super.customizeDocumentCommand(command);
375                         if (!fIgnoreTextConverters && fTextConverters != null) {
376                                 for (Iterator e = fTextConverters.iterator(); e.hasNext();)
377                                         ((ITextConverter) e.next()).customizeDocumentCommand(
378                                                         getDocument(), command);
379                         }
380                 }
381
382                 // http://dev.eclipse.org/bugs/show_bug.cgi?id=19270
383                 public void updateIndentationPrefixes() {
384                         SourceViewerConfiguration configuration = getSourceViewerConfiguration();
385                         String[] types = configuration.getConfiguredContentTypes(this);
386                         for (int i = 0; i < types.length; i++) {
387                                 String[] prefixes = configuration.getIndentPrefixes(this,
388                                                 types[i]);
389                                 if (prefixes != null && prefixes.length > 0)
390                                         setIndentPrefixes(prefixes, types[i]);
391                         }
392                 }
393
394                 /*
395                  * @see IWidgetTokenOwner#requestWidgetToken(IWidgetTokenKeeper)
396                  */
397                 public boolean requestWidgetToken(IWidgetTokenKeeper requester) {
398                         if (PlatformUI.getWorkbench().getHelpSystem()
399                                         .isContextHelpDisplayed())
400                                 return false;
401                         return super.requestWidgetToken(requester);
402                 }
403
404                 /*
405                  * @see IWidgetTokenOwnerExtension#requestWidgetToken(IWidgetTokenKeeper,
406                  *      int)
407                  * @since 3.0
408                  */
409                 public boolean requestWidgetToken(IWidgetTokenKeeper requester,
410                                 int priority) {
411                         if (PlatformUI.getWorkbench().getHelpSystem()
412                                         .isContextHelpDisplayed())
413                                 return false;
414                         return super.requestWidgetToken(requester, priority);
415                 }
416
417                 /*
418                  * @see org.eclipse.jface.text.source.ISourceViewer#configure(org.eclipse.jface.text.source.SourceViewerConfiguration)
419                  */
420                 public void configure(SourceViewerConfiguration configuration) {
421                         super.configure(configuration);
422                         // fCorrectionAssistant = new
423                         // JavaCorrectionAssistant(CompilationUnitEditor.this);
424                         // fCorrectionAssistant.install(this);
425                         IAutoEditStrategy smartSemi = new SmartSemicolonAutoEditStrategy(
426                                         IPHPPartitions.PHP_PARTITIONING);
427                         prependAutoEditStrategy(smartSemi, IDocument.DEFAULT_CONTENT_TYPE);
428                         prependAutoEditStrategy(smartSemi, IPHPPartitions.PHP_STRING_DQ);
429                         prependAutoEditStrategy(smartSemi, IPHPPartitions.PHP_STRING_SQ);
430                         prependAutoEditStrategy(smartSemi,
431                                         IPHPPartitions.PHP_STRING_HEREDOC);
432                 }
433
434                 /*
435                  * @see org.eclipse.jface.text.source.SourceViewer#createFormattingContext()
436                  * @since 3.0
437                  */
438                 public IFormattingContext createFormattingContext() {
439                         IFormattingContext context = new CommentFormattingContext();
440
441                         Map preferences;
442                         IJavaElement inputJavaElement = getInputJavaElement();
443                         IJavaProject javaProject = inputJavaElement != null ? inputJavaElement
444                                         .getJavaProject()
445                                         : null;
446                         if (javaProject == null)
447                                 preferences = new HashMap(JavaCore.getOptions());
448                         else
449                                 preferences = new HashMap(javaProject.getOptions(true));
450
451                         context.storeToMap(PreferenceConstants.getPreferenceStore(),
452                                         preferences, false);
453                         context.setProperty(
454                                         FormattingContextProperties.CONTEXT_PREFERENCES,
455                                         preferences);
456
457                         return context;
458                 }
459         }
460
461         /**
462          * Remembers data related to the current selection to be able to restore it
463          * later.
464          * 
465          * @since 3.0
466          */
467         private class RememberedSelection {
468                 /** The remembered selection start. */
469                 private RememberedOffset fStartOffset = new RememberedOffset();
470
471                 /** The remembered selection end. */
472                 private RememberedOffset fEndOffset = new RememberedOffset();
473
474                 /**
475                  * Remember current selection.
476                  */
477                 public void remember() {
478                         /*
479                          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=52257 This method
480                          * may be called inside an async call posted to the UI thread, so
481                          * protect against intermediate disposal of the editor.
482                          */
483                         ISourceViewer viewer = getSourceViewer();
484                         if (viewer != null) {
485                                 IRegion selection = getSignedSelection(viewer);
486                                 int startOffset = selection.getOffset();
487                                 int endOffset = startOffset + selection.getLength();
488
489                                 fStartOffset.setOffset(startOffset);
490                                 fEndOffset.setOffset(endOffset);
491                         }
492                 }
493
494                 /**
495                  * Restore remembered selection.
496                  */
497                 public void restore() {
498                         /*
499                          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=52257 This method
500                          * may be called inside an async call posted to the UI thread, so
501                          * protect against intermediate disposal of the editor.
502                          */
503                         if (getSourceViewer() == null)
504                                 return;
505
506                         try {
507
508                                 int startOffset, endOffset;
509                                 int revealStartOffset, revealEndOffset;
510                                 if (showsHighlightRangeOnly()) {
511                                         IJavaElement newStartElement = fStartOffset.getElement();
512                                         startOffset = fStartOffset
513                                                         .getRememberedOffset(newStartElement);
514                                         revealStartOffset = fStartOffset.getRevealOffset(
515                                                         newStartElement, startOffset);
516                                         if (revealStartOffset == -1)
517                                                 startOffset = -1;
518
519                                         IJavaElement newEndElement = fEndOffset.getElement();
520                                         endOffset = fEndOffset.getRememberedOffset(newEndElement);
521                                         revealEndOffset = fEndOffset.getRevealOffset(newEndElement,
522                                                         endOffset);
523                                         if (revealEndOffset == -1)
524                                                 endOffset = -1;
525                                 } else {
526                                         startOffset = fStartOffset.getOffset();
527                                         revealStartOffset = startOffset;
528                                         endOffset = fEndOffset.getOffset();
529                                         revealEndOffset = endOffset;
530                                 }
531
532                                 if (startOffset == -1) {
533                                         startOffset = endOffset; // fallback to caret offset
534                                         revealStartOffset = revealEndOffset;
535                                 }
536
537                                 if (endOffset == -1) {
538                                         endOffset = startOffset; // fallback to other offset
539                                         revealEndOffset = revealStartOffset;
540                                 }
541
542                                 IJavaElement element;
543                                 if (endOffset == -1) {
544                                         // fallback to element selection
545                                         element = fEndOffset.getElement();
546                                         if (element == null)
547                                                 element = fStartOffset.getElement();
548                                         if (element != null)
549                                                 setSelection(element);
550                                         return;
551                                 }
552
553                                 if (isValidSelection(revealStartOffset, revealEndOffset
554                                                 - revealStartOffset)
555                                                 && isValidSelection(startOffset, endOffset
556                                                                 - startOffset))
557                                         selectAndReveal(startOffset, endOffset - startOffset,
558                                                         revealStartOffset, revealEndOffset
559                                                                         - revealStartOffset);
560                         } finally {
561                                 fStartOffset.clear();
562                                 fEndOffset.clear();
563                         }
564                 }
565
566                 private boolean isValidSelection(int offset, int length) {
567                         IDocumentProvider provider = getDocumentProvider();
568                         if (provider != null) {
569                                 IDocument document = provider.getDocument(getEditorInput());
570                                 if (document != null) {
571                                         int end = offset + length;
572                                         int documentLength = document.getLength();
573                                         return 0 <= offset && offset <= documentLength && 0 <= end
574                                                         && end <= documentLength;
575                                 }
576                         }
577                         return false;
578                 }
579
580         }
581
582         /**
583          * Remembers additional data for a given offset to be able restore it later.
584          * 
585          * @since 3.0
586          */
587         private class RememberedOffset {
588                 /** Remembered line for the given offset */
589                 private int fLine;
590
591                 /** Remembered column for the given offset */
592                 private int fColumn;
593
594                 /** Remembered Java element for the given offset */
595                 private IJavaElement fElement;
596
597                 /** Remembered Java element line for the given offset */
598                 private int fElementLine;
599
600                 /**
601                  * Store visual properties of the given offset.
602                  * 
603                  * @param offset
604                  *            Offset in the document
605                  */
606                 public void setOffset(int offset) {
607                         try {
608                                 IDocument document = getSourceViewer().getDocument();
609                                 fLine = document.getLineOfOffset(offset);
610                                 fColumn = offset - document.getLineOffset(fLine);
611                                 fElement = getElementAt(offset, true);
612
613                                 fElementLine = -1;
614                                 if (fElement instanceof IMember) {
615                                         ISourceRange range = ((IMember) fElement).getNameRange();
616                                         if (range != null)
617                                                 fElementLine = document.getLineOfOffset(range
618                                                                 .getOffset());
619                                 }
620                                 if (fElementLine == -1)
621                                         fElementLine = document
622                                                         .getLineOfOffset(getOffset(fElement));
623                         } catch (BadLocationException e) {
624                                 // should not happen
625                                 PHPeclipsePlugin.log(e);
626                                 clear();
627                         } catch (JavaModelException e) {
628                                 // should not happen
629                                 PHPeclipsePlugin.log(e.getStatus());
630                                 clear();
631                         }
632                 }
633
634                 /**
635                  * Return offset recomputed from stored visual properties.
636                  * 
637                  * @return Offset in the document
638                  */
639                 public int getOffset() {
640                         IJavaElement newElement = getElement();
641
642                         int offset = getRememberedOffset(newElement);
643
644                         if (offset != -1 && !containsOffset(newElement, offset)
645                                         && (offset == 0 || !containsOffset(newElement, offset - 1)))
646                                 return -1;
647
648                         return offset;
649                 }
650
651                 /**
652                  * Return offset recomputed from stored visual properties.
653                  * 
654                  * @param newElement
655                  *            Enclosing element
656                  * @return Offset in the document
657                  */
658                 public int getRememberedOffset(IJavaElement newElement) {
659                         try {
660                                 if (newElement == null)
661                                         return -1;
662
663                                 IDocument document = getSourceViewer().getDocument();
664                                 int newElementLine = -1;
665                                 if (newElement instanceof IMember) {
666                                         ISourceRange range = ((IMember) newElement).getNameRange();
667                                         if (range != null)
668                                                 newElementLine = document.getLineOfOffset(range
669                                                                 .getOffset());
670                                 }
671                                 if (newElementLine == -1)
672                                         newElementLine = document
673                                                         .getLineOfOffset(getOffset(newElement));
674                                 if (newElementLine == -1)
675                                         return -1;
676
677                                 int newLine = fLine + newElementLine - fElementLine;
678                                 if (newLine < 0 || newLine >= document.getNumberOfLines())
679                                         return -1;
680                                 int maxColumn = document.getLineLength(newLine);
681                                 String lineDelimiter = document.getLineDelimiter(newLine);
682                                 if (lineDelimiter != null)
683                                         maxColumn = maxColumn - lineDelimiter.length();
684                                 int offset;
685                                 if (fColumn > maxColumn)
686                                         offset = document.getLineOffset(newLine) + maxColumn;
687                                 else
688                                         offset = document.getLineOffset(newLine) + fColumn;
689
690                                 return offset;
691                         } catch (BadLocationException e) {
692                                 // should not happen
693                                 PHPeclipsePlugin.log(e);
694                                 return -1;
695                         } catch (JavaModelException e) {
696                                 // should not happen
697                                 PHPeclipsePlugin.log(e.getStatus());
698                                 return -1;
699                         }
700                 }
701
702                 /**
703                  * Returns the offset used to reveal the given element based on the
704                  * given selection offset.
705                  * 
706                  * @param element
707                  *            the element
708                  * @param offset
709                  *            the selection offset
710                  * @return the offset to reveal the given element based on the given
711                  *         selection offset
712                  */
713                 public int getRevealOffset(IJavaElement element, int offset) {
714                         if (element == null || offset == -1)
715                                 return -1;
716
717                         if (containsOffset(element, offset)) {
718                                 if (offset > 0) {
719                                         IJavaElement alternateElement = getElementAt(offset, false);
720                                         if (element.getHandleIdentifier().equals(
721                                                         alternateElement.getParent().getHandleIdentifier()))
722                                                 return offset - 1; // Solves test case 2 from
723                                                                                         // https://bugs.eclipse.org/bugs/show_bug.cgi?id=47727#c3
724                                 }
725                                 return offset;
726                         } else if (offset > 0 && containsOffset(element, offset - 1))
727                                 return offset - 1; // Solves test case 1 from
728                                                                         // https://bugs.eclipse.org/bugs/show_bug.cgi?id=47727#c3
729
730                         return -1;
731                 }
732
733                 /**
734                  * Return Java element recomputed from stored visual properties.
735                  * 
736                  * @return Java element
737                  */
738                 public IJavaElement getElement() {
739                         if (fElement == null)
740                                 return null;
741
742                         return findElement(fElement);
743                 }
744
745                 /**
746                  * Clears the stored position
747                  */
748                 public void clear() {
749                         fLine = -1;
750                         fColumn = -1;
751                         fElement = null;
752                         fElementLine = -1;
753                 }
754
755                 /**
756                  * Does the given Java element contain the given offset?
757                  * 
758                  * @param element
759                  *            Java element
760                  * @param offset
761                  *            Offset
762                  * @return <code>true</code> iff the Java element contains the offset
763                  */
764                 private boolean containsOffset(IJavaElement element, int offset) {
765                         int elementOffset = getOffset(element);
766                         int elementLength = getLength(element);
767                         return (elementOffset > -1 && elementLength > -1) ? (offset >= elementOffset && offset < elementOffset
768                                         + elementLength)
769                                         : false;
770                 }
771
772                 /**
773                  * Returns the offset of the given Java element.
774                  * 
775                  * @param element
776                  *            Java element
777                  * @return Offset of the given Java element
778                  */
779                 private int getOffset(IJavaElement element) {
780                         if (element instanceof ISourceReference) {
781                                 ISourceReference sr = (ISourceReference) element;
782                                 try {
783                                         ISourceRange srcRange = sr.getSourceRange();
784                                         if (srcRange != null)
785                                                 return srcRange.getOffset();
786                                 } catch (JavaModelException e) {
787                                 }
788                         }
789                         return -1;
790                 }
791
792                 /**
793                  * Returns the length of the given Java element.
794                  * 
795                  * @param element
796                  *            Java element
797                  * @return Length of the given Java element
798                  */
799                 private int getLength(IJavaElement element) {
800                         if (element instanceof ISourceReference) {
801                                 ISourceReference sr = (ISourceReference) element;
802                                 try {
803                                         ISourceRange srcRange = sr.getSourceRange();
804                                         if (srcRange != null)
805                                                 return srcRange.getLength();
806                                 } catch (JavaModelException e) {
807                                 }
808                         }
809                         return -1;
810                 }
811
812                 /**
813                  * Returns the updated java element for the old java element.
814                  * 
815                  * @param element
816                  *            Old Java element
817                  * @return Updated Java element
818                  */
819                 private IJavaElement findElement(IJavaElement element) {
820
821                         if (element == null)
822                                 return null;
823
824                         IWorkingCopyManager manager = WebUI.getDefault()
825                                         .getWorkingCopyManager();
826                         ICompilationUnit unit = manager.getWorkingCopy(getEditorInput());
827
828                         if (unit != null) {
829                                 try {
830
831                                         synchronized (unit) {
832                                                 // unit.reconcile(ICompilationUnit.NO_AST, false, null,
833                                                 // null);
834                                                 unit.reconcile();
835                                         }
836                                         IJavaElement[] findings = unit.findElements(element);
837                                         if (findings != null && findings.length > 0)
838                                                 return findings[0];
839
840                                 } catch (JavaModelException x) {
841                                         PHPeclipsePlugin.log(x.getStatus());
842                                         // nothing found, be tolerant and go on
843                                 }
844                         }
845
846                         return null;
847                 }
848
849         }
850
851         static class TabConverter implements ITextConverter {
852                 private int fTabRatio;
853
854                 private ILineTracker fLineTracker;
855
856                 public TabConverter() {
857                 }
858
859                 public void setNumberOfSpacesPerTab(int ratio) {
860                         fTabRatio = ratio;
861                 }
862
863                 public void setLineTracker(ILineTracker lineTracker) {
864                         fLineTracker = lineTracker;
865                 }
866
867                 private int insertTabString(StringBuffer buffer, int offsetInLine) {
868                         if (fTabRatio == 0)
869                                 return 0;
870                         int remainder = offsetInLine % fTabRatio;
871                         remainder = fTabRatio - remainder;
872                         for (int i = 0; i < remainder; i++)
873                                 buffer.append(' ');
874                         return remainder;
875                 }
876
877                 public void customizeDocumentCommand(IDocument document,
878                                 DocumentCommand command) {
879                         String text = command.text;
880                         if (text == null)
881                                 return;
882                         int index = text.indexOf('\t');
883                         if (index > -1) {
884                                 StringBuffer buffer = new StringBuffer();
885                                 fLineTracker.set(command.text);
886                                 int lines = fLineTracker.getNumberOfLines();
887                                 try {
888                                         for (int i = 0; i < lines; i++) {
889                                                 int offset = fLineTracker.getLineOffset(i);
890                                                 int endOffset = offset + fLineTracker.getLineLength(i);
891                                                 String line = text.substring(offset, endOffset);
892                                                 int position = 0;
893                                                 if (i == 0) {
894                                                         IRegion firstLine = document
895                                                                         .getLineInformationOfOffset(command.offset);
896                                                         position = command.offset - firstLine.getOffset();
897                                                 }
898                                                 int length = line.length();
899                                                 for (int j = 0; j < length; j++) {
900                                                         char c = line.charAt(j);
901                                                         if (c == '\t') {
902                                                                 position += insertTabString(buffer, position);
903                                                         } else {
904                                                                 buffer.append(c);
905                                                                 ++position;
906                                                         }
907                                                 }
908                                         }
909                                         command.text = buffer.toString();
910                                 } catch (BadLocationException x) {
911                                 }
912                         }
913                 }
914         };
915
916         private static class ExitPolicy implements LinkedPositionUI.ExitPolicy {
917                 final char fExitCharacter;
918
919                 public ExitPolicy(char exitCharacter) {
920                         fExitCharacter = exitCharacter;
921                 }
922
923                 /*
924                  * @see org.phpeclipse.phpdt.internal.ui.text.link.LinkedPositionUI.ExitPolicy#doExit(org.phpeclipse.phpdt.internal.ui.text.link.LinkedPositionManager,
925                  *      org.eclipse.swt.events.VerifyEvent, int, int)
926                  */
927                 public ExitFlags doExit(LinkedPositionManager manager,
928                                 VerifyEvent event, int offset, int length) {
929                         if (event.character == fExitCharacter) {
930                                 if (manager.anyPositionIncludes(offset, length))
931                                         return new ExitFlags(LinkedPositionUI.COMMIT
932                                                         | LinkedPositionUI.UPDATE_CARET, false);
933                                 else
934                                         return new ExitFlags(LinkedPositionUI.COMMIT, true);
935                         }
936                         // Fix for #1380415 (toshihiro) start 
937                     switch (event.keyCode) {
938                     case SWT.ARROW_UP:
939                     case SWT.ARROW_DOWN:
940                       return new ExitFlags(LinkedPositionUI.COMMIT, true);
941                     case SWT.ARROW_LEFT:
942                     case SWT.ARROW_RIGHT:
943                        if (!manager.anyPositionIncludes(offset, length))
944                          return new ExitFlags(LinkedPositionUI.COMMIT, true);
945                        break;
946                     }
947                     // #1380415 end 
948                         switch (event.character) {
949                         case '\b':
950                                 if (manager.getFirstPosition().length == 0)
951                                         return new ExitFlags(0, false);
952                                 else
953                                         return null;
954                         case '\n':
955                         case '\r':
956                                 return new ExitFlags(LinkedPositionUI.COMMIT, true);
957                         default:
958                                 return null;
959                         }
960                 }
961         }
962
963         private static class BracketLevel {
964                 int fOffset;
965
966                 int fLength;
967
968                 LinkedPositionManager fManager;
969
970                 LinkedPositionUI fEditor;
971         };
972
973         private class BracketInserter implements VerifyKeyListener,
974                         LinkedPositionUI.ExitListener {
975                 private boolean fCloseBracketsPHP = true;
976
977                 private boolean fCloseStringsPHPDQ = true;
978
979                 private boolean fCloseStringsPHPSQ = true;
980
981                 private int fOffset;
982
983                 private int fLength;
984
985                 public void setCloseBracketsPHPEnabled(boolean enabled) {
986                         fCloseBracketsPHP = enabled;
987                 }
988
989                 public void setCloseStringsPHPDQEnabled(boolean enabled) {
990                         fCloseStringsPHPDQ = enabled;
991                 }
992
993                 public void setCloseStringsPHPSQEnabled(boolean enabled) {
994                         fCloseStringsPHPSQ = enabled;
995                 }
996
997                 private boolean hasIdentifierToTheRight(IDocument document, int offset) {
998                         try {
999                                 int end = offset;
1000                                 IRegion endLine = document.getLineInformationOfOffset(end);
1001                                 int maxEnd = endLine.getOffset() + endLine.getLength();
1002                                 while (end != maxEnd
1003                                                 && Character.isWhitespace(document.getChar(end)))
1004                                         ++end;
1005                                 return end != maxEnd
1006                                                 && Scanner.isPHPIdentifierPart(document.getChar(end));
1007                         } catch (BadLocationException e) {
1008                                 // be conservative
1009                                 return true;
1010                         }
1011                 }
1012
1013                 private boolean hasIdentifierToTheLeft(IDocument document, int offset) {
1014                         try {
1015                                 int start = offset;
1016                                 IRegion startLine = document.getLineInformationOfOffset(start);
1017                                 int minStart = startLine.getOffset();
1018                                 while (start != minStart
1019                                                 && Character.isWhitespace(document.getChar(start - 1)))
1020                                         --start;
1021                                 return start != minStart
1022                                                 && Scanner.isPHPIdentifierPart(document
1023                                                                 .getChar(start - 1));
1024                         } catch (BadLocationException e) {
1025                                 return true;
1026                         }
1027                 }
1028
1029                 private boolean hasCharacterToTheLeft(IDocument document, int offset,
1030                                 char character) {
1031                         try {
1032                                 int start = offset;
1033                                 IRegion startLine = document.getLineInformationOfOffset(start);
1034                                 int minStart = startLine.getOffset();
1035                                 while (start != minStart
1036                                                 && Character.isWhitespace(document.getChar(start - 1)))
1037                                         --start;
1038                                 return start != minStart
1039                                                 && document.getChar(start - 1) == character;
1040                         } catch (BadLocationException e) {
1041                                 return false;
1042                         }
1043                 }
1044
1045                 private boolean hasCharacterToTheRight(IDocument document, int offset,
1046                                 char character) {
1047                         try {
1048                                 int end = offset;
1049                                 IRegion endLine = document.getLineInformationOfOffset(end);
1050                                 int maxEnd = endLine.getOffset() + endLine.getLength();
1051                                 while (end != maxEnd
1052                                                 && Character.isWhitespace(document.getChar(end)))
1053                                         ++end;
1054                                 return end != maxEnd && document.getChar(end) == character;
1055                         } catch (BadLocationException e) {
1056                                 // be conservative
1057                                 return true;
1058                         }
1059                 }
1060
1061                 /*
1062                  * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
1063                  */
1064                 public void verifyKey(VerifyEvent event) {
1065                         if (!event.doit)
1066                                 return;
1067                         final ISourceViewer sourceViewer = getSourceViewer();
1068                         IDocument document = sourceViewer.getDocument();
1069                         final Point selection = sourceViewer.getSelectedRange();
1070                         final int offset = selection.x;
1071                         final int length = selection.y;
1072                         try {
1073                                 ITypedRegion partition = document.getPartition(offset);
1074                                 String type = partition.getType();
1075                                 if (type.equals(IPHPPartitions.PHP_PARTITIONING)
1076                                                 || type.equals(IDocument.DEFAULT_CONTENT_TYPE)) {
1077                                         // you will get IDocument.DEFAULT_CONTENT_TYPE for both PHP
1078                                         // and HTML area
1079                                         switch (event.character) {
1080                                         case '(':
1081                                                 if (hasCharacterToTheRight(document, offset + length,
1082                                                                 '('))
1083                                                         return;
1084                                                 // fall through
1085                                         case '[':
1086                                                 if (!fCloseBracketsPHP)
1087                                                         return;
1088                                                 if (hasIdentifierToTheRight(document, offset + length))
1089                                                         return;
1090                                                 // fall through
1091                                         case '{':
1092                                                 if (!fCloseBracketsPHP)
1093                                                         return;
1094                                                 if (hasIdentifierToTheRight(document, offset + length))
1095                                                         return;
1096                                                 // fall through
1097                                         case '"':
1098                                                 if (event.character == '"') {
1099                                                         if (!fCloseStringsPHPDQ)
1100                                                                 return;
1101                                                         // changed for statements like echo "" print ""
1102                                                         // if (hasIdentifierToTheLeft(document, offset)
1103                                                         // ||
1104                                                         // hasIdentifierToTheRight(document, offset +
1105                                                         // length))
1106                                                         if (hasIdentifierToTheRight(document, offset
1107                                                                         + length))
1108                                                                 return;
1109                                                 }
1110                                                 // ITypedRegion partition=
1111                                                 // document.getPartition(offset);
1112                                                 // if (!
1113                                                 // IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType())
1114                                                 // &&
1115                                                 // (partition.getOffset() != offset))
1116                                                 // return;
1117                                                 final char characterDQ = event.character;
1118                                                 final char closingCharacterDQ = getPeerCharacter(characterDQ);
1119                                                 final StringBuffer bufferDQ = new StringBuffer();
1120                                                 bufferDQ.append(characterDQ);
1121                                                 bufferDQ.append(closingCharacterDQ);
1122                                                 document.replace(offset, length, bufferDQ.toString());
1123                                                 LinkedPositionManager managerDQ = new LinkedPositionManager(
1124                                                                 document);
1125                                                 managerDQ.addPosition(offset + 1, 0);
1126                                                 fOffset = offset;
1127                                                 fLength = 2;
1128                                                 LinkedPositionUI editorDQ = new LinkedPositionUI(
1129                                                                 sourceViewer, managerDQ);
1130                                                 editorDQ.setCancelListener(this);
1131                                                 editorDQ.setExitPolicy(new ExitPolicy(
1132                                                                 closingCharacterDQ));
1133                                                 editorDQ.setFinalCaretOffset(offset + 2);
1134                                                 editorDQ.enter();
1135                                                 IRegion newSelectionDQ = editorDQ.getSelectedRegion();
1136                                                 sourceViewer.setSelectedRange(newSelectionDQ
1137                                                                 .getOffset(), newSelectionDQ.getLength());
1138                                                 event.doit = false;
1139                                                 break;
1140                                         case '\'':
1141                                                 if (event.character == '\'') {
1142                                                         if (!fCloseStringsPHPSQ)
1143                                                                 return;
1144                                                         // changed for statements like echo "" print ""
1145                                                         // if (hasIdentifierToTheLeft(document, offset)
1146                                                         // ||
1147                                                         // hasIdentifierToTheRight(document, offset +
1148                                                         // length))
1149                                                         if (hasIdentifierToTheRight(document, offset
1150                                                                         + length))
1151                                                                 return;
1152                                                 }
1153                                                 // ITypedRegion partition=
1154                                                 // document.getPartition(offset);
1155                                                 // if (!
1156                                                 // IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType())
1157                                                 // &&
1158                                                 // (partition.getOffset() != offset))
1159                                                 // return;
1160                                                 final char characterSQ = event.character;
1161                                                 final char closingCharacterSQ = getPeerCharacter(characterSQ);
1162                                                 final StringBuffer bufferSQ = new StringBuffer();
1163                                                 bufferSQ.append(characterSQ);
1164                                                 bufferSQ.append(closingCharacterSQ);
1165                                                 document.replace(offset, length, bufferSQ.toString());
1166                                                 LinkedPositionManager managerSQ = new LinkedPositionManager(
1167                                                                 document);
1168                                                 managerSQ.addPosition(offset + 1, 0);
1169                                                 fOffset = offset;
1170                                                 fLength = 2;
1171                                                 LinkedPositionUI editorSQ = new LinkedPositionUI(
1172                                                                 sourceViewer, managerSQ);
1173                                                 editorSQ.setCancelListener(this);
1174                                                 editorSQ.setExitPolicy(new ExitPolicy(
1175                                                                 closingCharacterSQ));
1176                                                 editorSQ.setFinalCaretOffset(offset + 2);
1177                                                 editorSQ.enter();
1178                                                 IRegion newSelectionSQ = editorSQ.getSelectedRegion();
1179                                                 sourceViewer.setSelectedRange(newSelectionSQ
1180                                                                 .getOffset(), newSelectionSQ.getLength());
1181                                                 event.doit = false;
1182                                         case '\r': { // insert linebreaks and new closing brace
1183                                                                         // after brace and return
1184                                                 if (!fCloseBracketsPHP) {
1185                                                         return;
1186                                                 }
1187                                                 if (hasCharacterToTheLeft(document, offset, '{')
1188                                                                 && hasCharacterToTheRight(document, offset, '}')) {
1189                                                         String lineDelimiter = StubUtility
1190                                                                         .getLineDelimiterFor(document);
1191                                                         int caretPos = sourceViewer.getTextWidget()
1192                                                                         .getCaretOffset();
1193                                                         final StringBuffer buffer = new StringBuffer(
1194                                                                         lineDelimiter);
1195                                                         // get indentation
1196                                                         IRegion line = document
1197                                                                         .getLineInformationOfOffset(offset);
1198                                                         String currentLine = document.get(line.getOffset(),
1199                                                                         line.getLength());
1200                                                         int index = 0;
1201                                                         int max = currentLine.length();
1202                                                         StringBuffer indent = new StringBuffer();
1203                                                         while (index < max
1204                                                                         && Character.isWhitespace(currentLine
1205                                                                                         .charAt(index))) {
1206                                                                 indent.append(currentLine.charAt(index));
1207                                                                 index++;
1208                                                         }
1209                                                         buffer.append(indent);
1210                                                         JavaHeuristicScanner scanner = new JavaHeuristicScanner(
1211                                                                         document);
1212                                                         JavaIndenter indenter = new JavaIndenter(document,
1213                                                                         scanner);
1214                                                         buffer.append(indenter.createIndent(1));
1215                                                         int cursorPos = buffer.length();
1216                                                         buffer.append(lineDelimiter);
1217                                                         buffer.append(indent);
1218                                                         document.replace(offset, length, buffer.toString());
1219                                                         sourceViewer.getTextWidget().setCaretOffset(
1220                                                                         caretPos + cursorPos);
1221                                                         event.doit = false;
1222                                                 }
1223                                         }
1224                                         }
1225                                 }
1226                         } catch (BadLocationException e) {
1227                         }
1228                 }
1229
1230                 /*
1231                  * @see org.phpeclipse.phpdt.internal.ui.text.link.LinkedPositionUI.ExitListener#exit(boolean)
1232                  */
1233                 public void exit(boolean accept) {
1234                         if (accept)
1235                                 return;
1236                         // remove brackets
1237                         try {
1238                                 final ISourceViewer sourceViewer = getSourceViewer();
1239                                 IDocument document = sourceViewer.getDocument();
1240                                 document.replace(fOffset, fLength, null);
1241                         } catch (BadLocationException e) {
1242                         }
1243                 }
1244         }
1245
1246         /** The editor's save policy */
1247         protected ISavePolicy fSavePolicy;
1248
1249         /**
1250          * Listener to annotation model changes that updates the error tick in the
1251          * tab image
1252          */
1253         private JavaEditorErrorTickUpdater fJavaEditorErrorTickUpdater;
1254
1255         /** The editor's paint manager */
1256         // private PaintManager fPaintManager;
1257         /** The editor's bracket painter */
1258         // private BracketPainter fBracketPainter;
1259         /** The editor's bracket matcher */
1260         private PHPPairMatcher fBracketMatcher;
1261
1262         /** The editor's line painter */
1263         // private LinePainter fLinePainter;
1264         /** The editor's print margin ruler painter */
1265         // private PrintMarginPainter fPrintMarginPainter;
1266         /** The editor's problem painter */
1267         // private ProblemPainter fProblemPainter;
1268         /** The editor's tab converter */
1269         private TabConverter fTabConverter;
1270
1271         /** History for structure select action */
1272         // private SelectionHistory fSelectionHistory;
1273         /** The preference property change listener for php core. */
1274         // private IPropertyChangeListener fPropertyChangeListener = new
1275         // PropertyChangeListener();
1276         /** The remembered java element */
1277         private IJavaElement fRememberedElement;
1278
1279         /**
1280          * The remembered selection.
1281          * 
1282          * @since 3.0
1283          */
1284         private RememberedSelection fRememberedSelection = new RememberedSelection();
1285
1286         /** The remembered php element offset */
1287         private int fRememberedElementOffset;
1288
1289         /** The bracket inserter. */
1290         private BracketInserter fBracketInserter = new BracketInserter();
1291
1292         /** The standard action groups added to the menu */
1293         private GenerateActionGroup fGenerateActionGroup;
1294
1295         private CompositeActionGroup fContextMenuGroup;
1296
1297         // private class PropertyChangeListener implements IPropertyChangeListener {
1298         // /*
1299         // * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
1300         // */
1301         // public void
1302         // propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent
1303         // event) {
1304         // handlePreferencePropertyChanged(event);
1305         // }
1306         // }
1307         /* Preference key for code formatter tab size */
1308         private final static String CODE_FORMATTER_TAB_SIZE = JavaCore.FORMATTER_TAB_SIZE;
1309
1310         /** Preference key for matching brackets */
1311         // private final static String MATCHING_BRACKETS =
1312         // PreferenceConstants.EDITOR_MATCHING_BRACKETS;
1313         /** Preference key for matching brackets color */
1314         // private final static String MATCHING_BRACKETS_COLOR =
1315         // PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR;
1316         /** Preference key for highlighting current line */
1317         // private final static String CURRENT_LINE =
1318         // PreferenceConstants.EDITOR_CURRENT_LINE;
1319         /** Preference key for highlight color of current line */
1320         // private final static String CURRENT_LINE_COLOR =
1321         // PreferenceConstants.EDITOR_CURRENT_LINE_COLOR;
1322         /** Preference key for showing print marging ruler */
1323         // private final static String PRINT_MARGIN =
1324         // PreferenceConstants.EDITOR_PRINT_MARGIN;
1325         /** Preference key for print margin ruler color */
1326         // private final static String PRINT_MARGIN_COLOR =
1327         // PreferenceConstants.EDITOR_PRINT_MARGIN_COLOR;
1328         /** Preference key for print margin ruler column */
1329         // private final static String PRINT_MARGIN_COLUMN =
1330         // PreferenceConstants.EDITOR_PRINT_MARGIN_COLUMN;
1331         /** Preference key for inserting spaces rather than tabs */
1332         private final static String SPACES_FOR_TABS = PreferenceConstants.EDITOR_SPACES_FOR_TABS;
1333
1334         /** Preference key for error indication */
1335         // private final static String ERROR_INDICATION =
1336         // PreferenceConstants.EDITOR_PROBLEM_INDICATION;
1337         /** Preference key for error color */
1338         // private final static String ERROR_INDICATION_COLOR =
1339         // PreferenceConstants.EDITOR_PROBLEM_INDICATION_COLOR;
1340         /** Preference key for warning indication */
1341         // private final static String WARNING_INDICATION =
1342         // PreferenceConstants.EDITOR_WARNING_INDICATION;
1343         /** Preference key for warning color */
1344         // private final static String WARNING_INDICATION_COLOR =
1345         // PreferenceConstants.EDITOR_WARNING_INDICATION_COLOR;
1346         /** Preference key for task indication */
1347         private final static String TASK_INDICATION = PreferenceConstants.EDITOR_TASK_INDICATION;
1348
1349         /** Preference key for task color */
1350         private final static String TASK_INDICATION_COLOR = PreferenceConstants.EDITOR_TASK_INDICATION_COLOR;
1351
1352         /** Preference key for bookmark indication */
1353         private final static String BOOKMARK_INDICATION = PreferenceConstants.EDITOR_BOOKMARK_INDICATION;
1354
1355         /** Preference key for bookmark color */
1356         private final static String BOOKMARK_INDICATION_COLOR = PreferenceConstants.EDITOR_BOOKMARK_INDICATION_COLOR;
1357
1358         /** Preference key for search result indication */
1359         private final static String SEARCH_RESULT_INDICATION = PreferenceConstants.EDITOR_SEARCH_RESULT_INDICATION;
1360
1361         /** Preference key for search result color */
1362         private final static String SEARCH_RESULT_INDICATION_COLOR = PreferenceConstants.EDITOR_SEARCH_RESULT_INDICATION_COLOR;
1363
1364         /** Preference key for unknown annotation indication */
1365         private final static String UNKNOWN_INDICATION = PreferenceConstants.EDITOR_UNKNOWN_INDICATION;
1366
1367         /** Preference key for unknown annotation color */
1368         private final static String UNKNOWN_INDICATION_COLOR = PreferenceConstants.EDITOR_UNKNOWN_INDICATION_COLOR;
1369
1370         /** Preference key for linked position color */
1371         private final static String LINKED_POSITION_COLOR = PreferenceConstants.EDITOR_LINKED_POSITION_COLOR;
1372
1373         /** Preference key for shwoing the overview ruler */
1374         private final static String OVERVIEW_RULER = PreferenceConstants.EDITOR_OVERVIEW_RULER;
1375
1376         /** Preference key for error indication in overview ruler */
1377         private final static String ERROR_INDICATION_IN_OVERVIEW_RULER = PreferenceConstants.EDITOR_ERROR_INDICATION_IN_OVERVIEW_RULER;
1378
1379         /** Preference key for warning indication in overview ruler */
1380         private final static String WARNING_INDICATION_IN_OVERVIEW_RULER = PreferenceConstants.EDITOR_WARNING_INDICATION_IN_OVERVIEW_RULER;
1381
1382         /** Preference key for task indication in overview ruler */
1383         private final static String TASK_INDICATION_IN_OVERVIEW_RULER = PreferenceConstants.EDITOR_TASK_INDICATION_IN_OVERVIEW_RULER;
1384
1385         /** Preference key for bookmark indication in overview ruler */
1386         private final static String BOOKMARK_INDICATION_IN_OVERVIEW_RULER = PreferenceConstants.EDITOR_BOOKMARK_INDICATION_IN_OVERVIEW_RULER;
1387
1388         /** Preference key for search result indication in overview ruler */
1389         private final static String SEARCH_RESULT_INDICATION_IN_OVERVIEW_RULER = PreferenceConstants.EDITOR_SEARCH_RESULT_INDICATION_IN_OVERVIEW_RULER;
1390
1391         /** Preference key for unknown annotation indication in overview ruler */
1392         private final static String UNKNOWN_INDICATION_IN_OVERVIEW_RULER = PreferenceConstants.EDITOR_UNKNOWN_INDICATION_IN_OVERVIEW_RULER;
1393
1394         /** Preference key for automatically closing double quoted strings */
1395         private final static String CLOSE_STRINGS_DQ_PHP = PreferenceConstants.EDITOR_CLOSE_STRINGS_DQ_PHP;
1396
1397         /** Preference key for automatically closing single quoted strings */
1398         private final static String CLOSE_STRINGS_SQ_PHP = PreferenceConstants.EDITOR_CLOSE_STRINGS_SQ_PHP;
1399
1400         /** Preference key for automatically wrapping Java strings */
1401         // private final static String WRAP_STRINGS =
1402         // PreferenceConstants.EDITOR_WRAP_STRINGS_DQ;
1403         /** Preference key for automatically closing brackets and parenthesis */
1404         private final static String CLOSE_BRACKETS_PHP = PreferenceConstants.EDITOR_CLOSE_BRACKETS_PHP;
1405
1406         /** Preference key for automatically closing phpdocs and comments */
1407         private final static String CLOSE_JAVADOCS = PreferenceConstants.EDITOR_CLOSE_JAVADOCS;
1408
1409         /** Preference key for automatically adding phpdoc tags */
1410         private final static String ADD_JAVADOC_TAGS = PreferenceConstants.EDITOR_ADD_JAVADOC_TAGS;
1411
1412         /** Preference key for automatically formatting phpdocs */
1413         // private final static String FORMAT_JAVADOCS =
1414         // PreferenceConstants.EDITOR_FORMAT_JAVADOCS;
1415         /** Preference key for automatically closing strings */
1416         private final static String CLOSE_STRINGS_HTML = PreferenceConstants.EDITOR_CLOSE_STRINGS_HTML;
1417
1418         /** Preference key for automatically closing brackets and parenthesis */
1419         private final static String CLOSE_BRACKETS_HTML = PreferenceConstants.EDITOR_CLOSE_BRACKETS_HTML;
1420
1421         /** Preference key for smart paste */
1422         private final static String SMART_PASTE = PreferenceConstants.EDITOR_SMART_PASTE;
1423
1424         // private final static class AnnotationInfo {
1425         // public String fColorPreference;
1426         // public String fOverviewRulerPreference;
1427         // public String fEditorPreference;
1428         // };
1429         // private final static Map ANNOTATION_MAP;
1430         // static {
1431         //
1432         // AnnotationInfo info;
1433         // ANNOTATION_MAP = new HashMap();
1434         //
1435         // info = new AnnotationInfo();
1436         // info.fColorPreference = TASK_INDICATION_COLOR;
1437         // info.fOverviewRulerPreference = TASK_INDICATION_IN_OVERVIEW_RULER;
1438         // info.fEditorPreference = TASK_INDICATION;
1439         // ANNOTATION_MAP.put(AnnotationType.TASK, info);
1440         //
1441         // info = new AnnotationInfo();
1442         // info.fColorPreference = ERROR_INDICATION_COLOR;
1443         // info.fOverviewRulerPreference = ERROR_INDICATION_IN_OVERVIEW_RULER;
1444         // info.fEditorPreference = ERROR_INDICATION;
1445         // ANNOTATION_MAP.put(AnnotationType.ERROR, info);
1446         //
1447         // info = new AnnotationInfo();
1448         // info.fColorPreference = WARNING_INDICATION_COLOR;
1449         // info.fOverviewRulerPreference = WARNING_INDICATION_IN_OVERVIEW_RULER;
1450         // info.fEditorPreference = WARNING_INDICATION;
1451         // ANNOTATION_MAP.put(AnnotationType.WARNING, info);
1452         //
1453         // info = new AnnotationInfo();
1454         // info.fColorPreference = BOOKMARK_INDICATION_COLOR;
1455         // info.fOverviewRulerPreference = BOOKMARK_INDICATION_IN_OVERVIEW_RULER;
1456         // info.fEditorPreference = BOOKMARK_INDICATION;
1457         // ANNOTATION_MAP.put(AnnotationType.BOOKMARK, info);
1458         //
1459         // info = new AnnotationInfo();
1460         // info.fColorPreference = SEARCH_RESULT_INDICATION_COLOR;
1461         // info.fOverviewRulerPreference =
1462         // SEARCH_RESULT_INDICATION_IN_OVERVIEW_RULER;
1463         // info.fEditorPreference = SEARCH_RESULT_INDICATION;
1464         // ANNOTATION_MAP.put(AnnotationType.SEARCH, info);
1465         //
1466         // info = new AnnotationInfo();
1467         // info.fColorPreference = UNKNOWN_INDICATION_COLOR;
1468         // info.fOverviewRulerPreference = UNKNOWN_INDICATION_IN_OVERVIEW_RULER;
1469         // info.fEditorPreference = UNKNOWN_INDICATION;
1470         // ANNOTATION_MAP.put(AnnotationType.UNKNOWN, info);
1471         // };
1472         //
1473         // private final static AnnotationType[] ANNOTATION_LAYERS =
1474         // new AnnotationType[] {
1475         // AnnotationType.UNKNOWN,
1476         // AnnotationType.BOOKMARK,
1477         // AnnotationType.TASK,
1478         // AnnotationType.SEARCH,
1479         // AnnotationType.WARNING,
1480         // AnnotationType.ERROR };
1481         /**
1482          * Creates a new php unit editor.
1483          */
1484
1485         /**
1486          * Reconciling listeners.
1487          * 
1488          * @since 3.0
1489          */
1490         private ListenerList fReconcilingListeners = new ListenerList();
1491
1492         /**
1493          * Mutex for the reconciler. See
1494          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898 for a description of
1495          * the problem.
1496          * <p>
1497          * TODO remove once the underlying problem is solved.
1498          * </p>
1499          */
1500         private final Object fReconcilerLock = new Object();
1501
1502         public PHPUnitEditor() {
1503                 super();
1504                 setDocumentProvider(WebUI.getDefault()
1505                                 .getCompilationUnitDocumentProvider());
1506                 setEditorContextMenuId("#PHPEditorContext"); //$NON-NLS-1$
1507                 setRulerContextMenuId("#PHPRulerContext"); //$NON-NLS-1$
1508                 setOutlinerContextMenuId("#PHPOutlinerContext"); //$NON-NLS-1$
1509                 // don't set help contextId, we install our own help context
1510                 fSavePolicy = null;
1511                 fJavaEditorErrorTickUpdater = new JavaEditorErrorTickUpdater(this);
1512         }
1513
1514         /*
1515          * @see AbstractTextEditor#createActions()
1516          */
1517         protected void createActions() {
1518                 super.createActions();
1519                 Action action;
1520                 // Action action= new
1521                 // TextOperationAction(PHPEditorMessages.getResourceBundle(),
1522                 // "CorrectionAssistProposal.", this, CORRECTIONASSIST_PROPOSALS);
1523                 // //$NON-NLS-1$
1524                 // action.setActionDefinitionId(PHPEditorActionDefinitionIds.CORRECTION_ASSIST_PROPOSALS);
1525                 // setAction("CorrectionAssistProposal", action); //$NON-NLS-1$
1526                 // markAsStateDependentAction("CorrectionAssistProposal", true);
1527                 // //$NON-NLS-1$
1528                 // // WorkbenchHelp.setHelp(action,
1529                 // IJavaHelpContextIds.QUICK_FIX_ACTION);
1530                 action = new ContentAssistAction(PHPEditorMessages.getResourceBundle(),
1531                                 "ContentAssistProposal.", this); //$NON-NLS-1$
1532                 action
1533                                 .setActionDefinitionId(PHPEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
1534                 setAction("ContentAssistProposal", action); //$NON-NLS-1$
1535                 markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$
1536                 // WorkbenchHelp.setHelp(action,
1537                 // IJavaHelpContextIds.CONTENT_ASSIST_ACTION);
1538                 // action = new
1539                 // TextOperationAction(PHPEditorMessages.getResourceBundle(),
1540                 // "ContentAssistContextInformation.", this,
1541                 // ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$
1542                 // action
1543                 // .setActionDefinitionId(PHPEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION);
1544                 // setAction("ContentAssistContextInformation", action); //$NON-NLS-1$
1545                 // markAsStateDependentAction("ContentAssistContextInformation", true);
1546                 // //$NON-NLS-1$
1547                 // WorkbenchHelp.setHelp(action,
1548                 // IJavaHelpContextIds.PARAMETER_HINTS_ACTION);
1549                 // action= new
1550                 // TextOperationAction(PHPEditorMessages.getResourceBundle(),
1551                 // "ContentAssistCompletePrefix.", this, CONTENTASSIST_COMPLETE_PREFIX);
1552                 // //$NON-NLS-1$
1553                 // action.setActionDefinitionId(PHPEditorActionDefinitionIds.CONTENT_ASSIST_COMPLETE_PREFIX);
1554                 // setAction("ContentAssistCompletePrefix", action); //$NON-NLS-1$
1555                 // markAsStateDependentAction("ContentAssistCompletePrefix", true);
1556                 // //$NON-NLS-1$
1557                 // // WorkbenchHelp.setHelp(action,
1558                 // IJavaHelpContextIds.PARAMETER_HINTS_ACTION);
1559                 action = new TextOperationAction(PHPEditorMessages.getResourceBundle(),
1560                                 "Comment.", this, ITextOperationTarget.PREFIX); //$NON-NLS-1$
1561                 action.setActionDefinitionId(PHPEditorActionDefinitionIds.COMMENT);
1562                 setAction("Comment", action); //$NON-NLS-1$
1563                 markAsStateDependentAction("Comment", true); //$NON-NLS-1$
1564                 // WorkbenchHelp.setHelp(action, IJavaHelpContextIds.COMMENT_ACTION);
1565                 action = new TextOperationAction(PHPEditorMessages.getResourceBundle(),
1566                                 "Uncomment.", this, ITextOperationTarget.STRIP_PREFIX); //$NON-NLS-1$
1567                 action.setActionDefinitionId(PHPEditorActionDefinitionIds.UNCOMMENT);
1568                 setAction("Uncomment", action); //$NON-NLS-1$
1569                 markAsStateDependentAction("Uncomment", true); //$NON-NLS-1$
1570                 // WorkbenchHelp.setHelp(action, IJavaHelpContextIds.UNCOMMENT_ACTION);
1571
1572                 action = new ToggleCommentAction(PHPEditorMessages.getResourceBundle(),
1573                                 "ToggleComment.", this); //$NON-NLS-1$
1574                 action
1575                                 .setActionDefinitionId(PHPEditorActionDefinitionIds.TOGGLE_COMMENT);
1576                 setAction("ToggleComment", action); //$NON-NLS-1$
1577                 markAsStateDependentAction("ToggleComment", true); //$NON-NLS-1$
1578                 // WorkbenchHelp.setHelp(action,
1579                 // IJavaHelpContextIds.TOGGLE_COMMENT_ACTION);
1580                 configureToggleCommentAction();
1581
1582                 action = new TextOperationAction(PHPEditorMessages.getResourceBundle(),
1583                                 "Format.", this, ISourceViewer.FORMAT); //$NON-NLS-1$
1584                 action.setActionDefinitionId(PHPEditorActionDefinitionIds.FORMAT);
1585                 setAction("Format", action); //$NON-NLS-1$
1586                 markAsStateDependentAction("Format", true); //$NON-NLS-1$
1587                 markAsSelectionDependentAction("Format", true); //$NON-NLS-1$
1588                 // WorkbenchHelp.setHelp(action, IJavaHelpContextIds.FORMAT_ACTION);
1589
1590                 // action = new
1591                 // AddBlockCommentAction(PHPEditorMessages.getResourceBundle(),
1592                 // "AddBlockComment.", this); //$NON-NLS-1$
1593                 // action
1594                 // .setActionDefinitionId(PHPEditorActionDefinitionIds.ADD_BLOCK_COMMENT);
1595                 // setAction("AddBlockComment", action); //$NON-NLS-1$
1596                 // markAsStateDependentAction("AddBlockComment", true); //$NON-NLS-1$
1597                 // markAsSelectionDependentAction("AddBlockComment", true);
1598                 // //$NON-NLS-1$
1599                 // // WorkbenchHelp.setHelp(action,
1600                 // // IJavaHelpContextIds.ADD_BLOCK_COMMENT_ACTION);
1601                 // action = new RemoveBlockCommentAction(
1602                 // PHPEditorMessages.getResourceBundle(), "RemoveBlockComment.", this);
1603                 // //$NON-NLS-1$
1604                 // action
1605                 // .setActionDefinitionId(PHPEditorActionDefinitionIds.REMOVE_BLOCK_COMMENT);
1606                 // setAction("RemoveBlockComment", action); //$NON-NLS-1$
1607                 // markAsStateDependentAction("RemoveBlockComment", true); //$NON-NLS-1$
1608                 // markAsSelectionDependentAction("RemoveBlockComment", true);
1609                 // //$NON-NLS-1$
1610                 // WorkbenchHelp.setHelp(action,
1611                 // IJavaHelpContextIds.ADD_BLOCK_COMMENT_ACTION);
1612                 action = new IndentAction(PHPEditorMessages.getResourceBundle(),
1613                                 "Indent.", this, false); //$NON-NLS-1$
1614                 action.setActionDefinitionId(PHPEditorActionDefinitionIds.INDENT);
1615                 setAction("Indent", action); //$NON-NLS-1$
1616                 markAsStateDependentAction("Indent", true); //$NON-NLS-1$
1617                 markAsSelectionDependentAction("Indent", true); //$NON-NLS-1$
1618                 // WorkbenchHelp.setHelp(action, IJavaHelpContextIds.INDENT_ACTION);
1619                 //
1620                 // action= new IndentAction(PHPEditorMessages.getResourceBundle(),
1621                 // "Indent.", this, true); //$NON-NLS-1$
1622                 // setAction("IndentOnTab", action); //$NON-NLS-1$
1623                 // markAsStateDependentAction("IndentOnTab", true); //$NON-NLS-1$
1624                 // markAsSelectionDependentAction("IndentOnTab", true); //$NON-NLS-1$
1625                 //
1626
1627                 action = new AddBlockCommentAction(PHPEditorMessages
1628                                 .getResourceBundle(), "AddBlockComment.", this); //$NON-NLS-1$
1629                 action
1630                                 .setActionDefinitionId(PHPEditorActionDefinitionIds.ADD_BLOCK_COMMENT);
1631                 setAction("AddBlockComment", action); //$NON-NLS-1$
1632                 markAsStateDependentAction("AddBlockComment", true); //$NON-NLS-1$
1633                 markAsSelectionDependentAction("AddBlockComment", true); //$NON-NLS-1$
1634                 // WorkbenchHelp.setHelp(action,
1635                 // IJavaHelpContextIds.ADD_BLOCK_COMMENT_ACTION);
1636
1637                 action = new RemoveBlockCommentAction(PHPEditorMessages
1638                                 .getResourceBundle(), "RemoveBlockComment.", this); //$NON-NLS-1$
1639                 action
1640                                 .setActionDefinitionId(PHPEditorActionDefinitionIds.REMOVE_BLOCK_COMMENT);
1641                 setAction("RemoveBlockComment", action); //$NON-NLS-1$
1642                 markAsStateDependentAction("RemoveBlockComment", true); //$NON-NLS-1$
1643                 markAsSelectionDependentAction("RemoveBlockComment", true); //$NON-NLS-1$
1644                 // WorkbenchHelp.setHelp(action,
1645                 // IJavaHelpContextIds.REMOVE_BLOCK_COMMENT_ACTION);
1646
1647                 // action= new IndentAction(PHPEditorMessages.getResourceBundle(),
1648                 // "Indent.", this, false); //$NON-NLS-1$
1649                 // action.setActionDefinitionId(PHPEditorActionDefinitionIds.INDENT);
1650                 // setAction("Indent", action); //$NON-NLS-1$
1651                 // markAsStateDependentAction("Indent", true); //$NON-NLS-1$
1652                 // markAsSelectionDependentAction("Indent", true); //$NON-NLS-1$
1653                 // // WorkbenchHelp.setHelp(action, IJavaHelpContextIds.INDENT_ACTION);
1654                 //
1655                 action = new IndentAction(PHPEditorMessages.getResourceBundle(),
1656                                 "Indent.", this, true); //$NON-NLS-1$
1657                 setAction("IndentOnTab", action); //$NON-NLS-1$
1658                 markAsStateDependentAction("IndentOnTab", true); //$NON-NLS-1$
1659                 markAsSelectionDependentAction("IndentOnTab", true); //$NON-NLS-1$
1660
1661                 if (getPreferenceStore().getBoolean(
1662                                 PreferenceConstants.EDITOR_SMART_TAB)) {
1663                         // don't replace Shift Right - have to make sure their enablement is
1664                         // mutually exclusive
1665                         // removeActionActivationCode(ITextEditorActionConstants.SHIFT_RIGHT);
1666                         setActionActivationCode("IndentOnTab", '\t', -1, SWT.NONE); //$NON-NLS-1$
1667                 }
1668                 fGenerateActionGroup = new GenerateActionGroup(this,
1669                                 ITextEditorActionConstants.GROUP_EDIT);
1670                 // ActionGroup rg= new RefactorActionGroup(this,
1671                 // ITextEditorActionConstants.GROUP_EDIT);
1672
1673                 // fActionGroups.addGroup(rg);
1674                 fActionGroups.addGroup(fGenerateActionGroup);
1675
1676                 // We have to keep the context menu group separate to have better
1677                 // control over positioning
1678                 fContextMenuGroup = new CompositeActionGroup(
1679                                 new ActionGroup[] { fGenerateActionGroup
1680                                 // rg,
1681                                 // new LocalHistoryActionGroup(this,
1682                                 // ITextEditorActionConstants.GROUP_EDIT)
1683                                 });
1684
1685         }
1686
1687         /*
1688          * @see JavaEditor#getElementAt(int)
1689          */
1690         protected IJavaElement getElementAt(int offset) {
1691                 return getElementAt(offset, true);
1692         }
1693
1694         /**
1695          * Returns the most narrow element including the given offset. If
1696          * <code>reconcile</code> is <code>true</code> the editor's input
1697          * element is reconciled in advance. If it is <code>false</code> this
1698          * method only returns a result if the editor's input element does not need
1699          * to be reconciled.
1700          * 
1701          * @param offset
1702          *            the offset included by the retrieved element
1703          * @param reconcile
1704          *            <code>true</code> if working copy should be reconciled
1705          */
1706         protected IJavaElement getElementAt(int offset, boolean reconcile) {
1707                 IWorkingCopyManager manager = WebUI.getDefault()
1708                                 .getWorkingCopyManager();
1709                 ICompilationUnit unit = manager.getWorkingCopy(getEditorInput());
1710                 if (unit != null) {
1711                         try {
1712                                 if (reconcile) {
1713                                         synchronized (unit) {
1714                                                 unit.reconcile();
1715                                         }
1716                                         return unit.getElementAt(offset);
1717                                 } else if (unit.isConsistent())
1718                                         return unit.getElementAt(offset);
1719                         } catch (JavaModelException x) {
1720                                 PHPeclipsePlugin.log(x.getStatus());
1721                                 // nothing found, be tolerant and go on
1722                         }
1723                 }
1724                 return null;
1725         }
1726
1727         /*
1728          * @see JavaEditor#getCorrespondingElement(IJavaElement)
1729          */
1730         protected IJavaElement getCorrespondingElement(IJavaElement element) {
1731                 try {
1732                         return EditorUtility.getWorkingCopy(element, true);
1733                 } catch (JavaModelException x) {
1734                         PHPeclipsePlugin.log(x.getStatus());
1735                         // nothing found, be tolerant and go on
1736                 }
1737                 return null;
1738         }
1739
1740         public void createPartControl(Composite parent) {
1741                 super.createPartControl(parent);
1742                 // fPaintManager = new PaintManager(getSourceViewer());
1743                 LinePainter linePainter;
1744                 linePainter = new LinePainter(getSourceViewer());
1745                 linePainter.setHighlightColor(new Color(Display.getCurrent(), 225, 235,
1746                                 224));
1747                 // fPaintManager.addPainter(linePainter);
1748                 // if (isBracketHighlightingEnabled())
1749                 // startBracketHighlighting();
1750                 // if (isLineHighlightingEnabled())
1751                 // startLineHighlighting();
1752                 // if (isPrintMarginVisible())
1753                 // showPrintMargin();
1754                 // Iterator e = ANNOTATION_MAP.keySet().iterator();
1755                 // while (e.hasNext()) {
1756                 // AnnotationType type = (AnnotationType) e.next();
1757                 // if (isAnnotationIndicationEnabled(type))
1758                 // startAnnotationIndication(type);
1759                 // }
1760                 if (isTabConversionEnabled())
1761                         startTabConversion();
1762                 // if (isOverviewRulerVisible())
1763                 // showOverviewRuler();
1764                 //
1765                 // Preferences preferences =
1766                 // PHPeclipsePlugin.getDefault().getPluginPreferences();
1767                 // preferences.addPropertyChangeListener(fPropertyChangeListener);
1768                 IPreferenceStore preferenceStore = getPreferenceStore();
1769                 boolean closeBracketsPHP = preferenceStore
1770                                 .getBoolean(CLOSE_BRACKETS_PHP);
1771                 boolean closeStringsPHPDQ = preferenceStore
1772                                 .getBoolean(CLOSE_STRINGS_DQ_PHP);
1773                 boolean closeStringsPHPSQ = preferenceStore
1774                                 .getBoolean(CLOSE_STRINGS_SQ_PHP);
1775                 fBracketInserter.setCloseBracketsPHPEnabled(closeBracketsPHP);
1776                 fBracketInserter.setCloseStringsPHPDQEnabled(closeStringsPHPDQ);
1777                 fBracketInserter.setCloseStringsPHPSQEnabled(closeStringsPHPSQ);
1778                 ISourceViewer sourceViewer = getSourceViewer();
1779                 if (sourceViewer instanceof ITextViewerExtension)
1780                         ((ITextViewerExtension) sourceViewer)
1781                                         .prependVerifyKeyListener(fBracketInserter);
1782         }
1783
1784         private static char getPeerCharacter(char character) {
1785                 switch (character) {
1786                 case '(':
1787                         return ')';
1788                 case ')':
1789                         return '(';
1790                 case '[':
1791                         return ']';
1792                 case ']':
1793                         return '[';
1794                 case '"':
1795                         return character;
1796                 case '\'':
1797                         return character;
1798                 case '{':
1799                         return '}';
1800                 default:
1801                         throw new IllegalArgumentException();
1802                 }
1803         }
1804
1805         // private void startBracketHighlighting() {
1806         // if (fBracketPainter == null) {
1807         // ISourceViewer sourceViewer = getSourceViewer();
1808         // fBracketPainter = new BracketPainter(sourceViewer);
1809         // fBracketPainter.setHighlightColor(getColor(MATCHING_BRACKETS_COLOR));
1810         // // fPaintManager.addPainter(fBracketPainter);
1811         // }
1812         // }
1813         //
1814         // private void stopBracketHighlighting() {
1815         // if (fBracketPainter != null) {
1816         // // fPaintManager.removePainter(fBracketPainter);
1817         // fBracketPainter.deactivate(true);
1818         // fBracketPainter.dispose();
1819         // fBracketPainter = null;
1820         // }
1821         // }
1822
1823         // private boolean isBracketHighlightingEnabled() {
1824         // IPreferenceStore store = getPreferenceStore();
1825         // return store.getBoolean(MATCHING_BRACKETS);
1826         // }
1827
1828         // private void startLineHighlighting() {
1829         // if (fLinePainter == null) {
1830         // ISourceViewer sourceViewer = getSourceViewer();
1831         // fLinePainter = new LinePainter(sourceViewer);
1832         // fLinePainter.setHighlightColor(getColor(CURRENT_LINE_COLOR));
1833         // // fPaintManager.addPainter(fLinePainter);
1834         // }
1835         // }
1836
1837         // private void stopLineHighlighting() {
1838         // if (fLinePainter != null) {
1839         // // fPaintManager.removePainter(fLinePainter);
1840         // fLinePainter.deactivate(true);
1841         // fLinePainter.dispose();
1842         // fLinePainter = null;
1843         // }
1844         // }
1845
1846         // private boolean isLineHighlightingEnabled() {
1847         // IPreferenceStore store = getPreferenceStore();
1848         // return store.getBoolean(CURRENT_LINE);
1849         // }
1850
1851         // private void showPrintMargin() {
1852         // if (fPrintMarginPainter == null) {
1853         // fPrintMarginPainter = new PrintMarginPainter(getSourceViewer());
1854         // fPrintMarginPainter.setMarginRulerColor(getColor(PRINT_MARGIN_COLOR));
1855         // fPrintMarginPainter.setMarginRulerColumn(getPreferenceStore().getInt(PRINT_MARGIN_COLUMN));
1856         // // fPaintManager.addPainter(fPrintMarginPainter);
1857         // }
1858         // }
1859
1860         // private void hidePrintMargin() {
1861         // if (fPrintMarginPainter != null) {
1862         // // fPaintManager.removePainter(fPrintMarginPainter);
1863         // fPrintMarginPainter.deactivate(true);
1864         // fPrintMarginPainter.dispose();
1865         // fPrintMarginPainter = null;
1866         // }
1867         // }
1868
1869         // private boolean isPrintMarginVisible() {
1870         // IPreferenceStore store = getPreferenceStore();
1871         // return store.getBoolean(PRINT_MARGIN);
1872         // }
1873
1874         private int getTabSize() {
1875                 Preferences preferences = PHPeclipsePlugin.getDefault()
1876                                 .getPluginPreferences();
1877                 return preferences.getInt(CODE_FORMATTER_TAB_SIZE);
1878         }
1879
1880         private boolean isTabConversionEnabled() {
1881                 IPreferenceStore store = getPreferenceStore();
1882                 return store.getBoolean(SPACES_FOR_TABS);
1883         }
1884
1885         private Color getColor(String key) {
1886                 RGB rgb = PreferenceConverter.getColor(getPreferenceStore(), key);
1887                 return getColor(rgb);
1888         }
1889
1890         private Color getColor(RGB rgb) {
1891                 JavaTextTools textTools = WebUI.getDefault()
1892                                 .getJavaTextTools();
1893                 return textTools.getColorManager().getColor(rgb);
1894         }
1895
1896         // private Color getColor(AnnotationType annotationType) {
1897         // AnnotationInfo info = (AnnotationInfo)
1898         // ANNOTATION_MAP.get(annotationType);
1899         // if (info != null)
1900         // return getColor(info.fColorPreference);
1901         // return null;
1902         // }
1903         public void dispose() {
1904                 ISourceViewer sourceViewer = getSourceViewer();
1905                 if (sourceViewer instanceof ITextViewerExtension)
1906                         ((ITextViewerExtension) sourceViewer)
1907                                         .removeVerifyKeyListener(fBracketInserter);
1908                 // if (fPropertyChangeListener != null) {
1909                 // Preferences preferences =
1910                 // PHPeclipsePlugin.getDefault().getPluginPreferences();
1911                 // preferences.removePropertyChangeListener(fPropertyChangeListener);
1912                 // fPropertyChangeListener = null;
1913                 // }
1914                 if (fJavaEditorErrorTickUpdater != null) {
1915                         fJavaEditorErrorTickUpdater.dispose();
1916                         fJavaEditorErrorTickUpdater = null;
1917                 }
1918                 // if (fSelectionHistory != null)
1919                 // fSelectionHistory.dispose();
1920                 // if (fPaintManager != null) {
1921                 // fPaintManager.dispose();
1922                 // fPaintManager = null;
1923                 // }
1924                 if (fActionGroups != null) {
1925                         fActionGroups.dispose();
1926                         fActionGroups = null;
1927                 }
1928                 super.dispose();
1929         }
1930
1931         // protected AnnotationType getAnnotationType(String preferenceKey) {
1932         // Iterator e = ANNOTATION_MAP.keySet().iterator();
1933         // while (e.hasNext()) {
1934         // AnnotationType type = (AnnotationType) e.next();
1935         // AnnotationInfo info = (AnnotationInfo) ANNOTATION_MAP.get(type);
1936         // if (info != null) {
1937         // if (preferenceKey.equals(info.fColorPreference)
1938         // || preferenceKey.equals(info.fEditorPreference)
1939         // || preferenceKey.equals(info.fOverviewRulerPreference))
1940         // return type;
1941         // }
1942         // }
1943         // return null;
1944         // }
1945         /*
1946          * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
1947          */
1948         protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
1949                 try {
1950                         AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer();
1951                         if (asv != null) {
1952                                 String p = event.getProperty();
1953                                 if (CLOSE_BRACKETS_PHP.equals(p)) {
1954                                         fBracketInserter
1955                                                         .setCloseBracketsPHPEnabled(getPreferenceStore()
1956                                                                         .getBoolean(p));
1957                                         return;
1958                                 }
1959                                 if (CLOSE_STRINGS_DQ_PHP.equals(p)) {
1960                                         fBracketInserter
1961                                                         .setCloseStringsPHPDQEnabled(getPreferenceStore()
1962                                                                         .getBoolean(p));
1963                                         return;
1964                                 }
1965                                 if (CLOSE_STRINGS_SQ_PHP.equals(p)) {
1966                                         fBracketInserter
1967                                                         .setCloseStringsPHPSQEnabled(getPreferenceStore()
1968                                                                         .getBoolean(p));
1969                                         return;
1970                                 }
1971                                 if (SPACES_FOR_TABS.equals(p)) {
1972                                         if (isTabConversionEnabled())
1973                                                 startTabConversion();
1974                                         else
1975                                                 stopTabConversion();
1976                                         return;
1977                                 }
1978                                 // if (MATCHING_BRACKETS.equals(p)) {
1979                                 // if (isBracketHighlightingEnabled())
1980                                 // startBracketHighlighting();
1981                                 // else
1982                                 // stopBracketHighlighting();
1983                                 // return;
1984                                 // }
1985                                 // if (MATCHING_BRACKETS_COLOR.equals(p)) {
1986                                 // if (fBracketPainter != null)
1987                                 // fBracketPainter.setHighlightColor(getColor(MATCHING_BRACKETS_COLOR));
1988                                 // return;
1989                                 // }
1990                                 // if (CURRENT_LINE.equals(p)) {
1991                                 // if (isLineHighlightingEnabled())
1992                                 // startLineHighlighting();
1993                                 // else
1994                                 // stopLineHighlighting();
1995                                 // return;
1996                                 // }
1997                                 // if (CURRENT_LINE_COLOR.equals(p)) {
1998                                 // if (fLinePainter != null) {
1999                                 // stopLineHighlighting();
2000                                 // startLineHighlighting();
2001                                 // }
2002                                 // return;
2003                                 // }
2004                                 // if (PRINT_MARGIN.equals(p)) {
2005                                 // if (isPrintMarginVisible())
2006                                 // showPrintMargin();
2007                                 // else
2008                                 // hidePrintMargin();
2009                                 // return;
2010                                 // }
2011                                 // if (PRINT_MARGIN_COLOR.equals(p)) {
2012                                 // if (fPrintMarginPainter != null)
2013                                 // fPrintMarginPainter.setMarginRulerColor(getColor(PRINT_MARGIN_COLOR));
2014                                 // return;
2015                                 // }
2016                                 // if (PRINT_MARGIN_COLUMN.equals(p)) {
2017                                 // if (fPrintMarginPainter != null)
2018                                 // fPrintMarginPainter.setMarginRulerColumn(getPreferenceStore().getInt(PRINT_MARGIN_COLUMN));
2019                                 // return;
2020                                 // }
2021                                 // if (OVERVIEW_RULER.equals(p)) {
2022                                 // if (isOverviewRulerVisible())
2023                                 // showOverviewRuler();
2024                                 // else
2025                                 // hideOverviewRuler();
2026                                 // return;
2027                                 // }
2028                                 // AnnotationType type = getAnnotationType(p);
2029                                 // if (type != null) {
2030                                 //
2031                                 // AnnotationInfo info = (AnnotationInfo)
2032                                 // ANNOTATION_MAP.get(type);
2033                                 // if (info.fColorPreference.equals(p)) {
2034                                 // Color color = getColor(type);
2035                                 // if (fProblemPainter != null) {
2036                                 // fProblemPainter.setColor(type, color);
2037                                 // fProblemPainter.paint(IPainter.CONFIGURATION);
2038                                 // }
2039                                 // setColorInOverviewRuler(type, color);
2040                                 // return;
2041                                 // }
2042                                 //
2043                                 // if (info.fEditorPreference.equals(p)) {
2044                                 // if (isAnnotationIndicationEnabled(type))
2045                                 // startAnnotationIndication(type);
2046                                 // else
2047                                 // stopAnnotationIndication(type);
2048                                 // return;
2049                                 // }
2050                                 //
2051                                 // if (info.fOverviewRulerPreference.equals(p)) {
2052                                 // if (isAnnotationIndicationInOverviewRulerEnabled(type))
2053                                 // showAnnotationIndicationInOverviewRuler(type, true);
2054                                 // else
2055                                 // showAnnotationIndicationInOverviewRuler(type, false);
2056                                 // return;
2057                                 // }
2058                                 // }
2059                                 IContentAssistant c = asv.getContentAssistant();
2060                                 if (c instanceof ContentAssistant)
2061                                         ContentAssistPreference.changeConfiguration(
2062                                                         (ContentAssistant) c, getPreferenceStore(), event);
2063                         }
2064                 } finally {
2065                         super.handlePreferenceStoreChanged(event);
2066                 }
2067         }
2068
2069         /*
2070          * @see net.sourceforge.phpdt.internal.ui.javaeditor.JavaEditor#handlePreferencePropertyChanged(org.eclipse.core.runtime.Preferences.PropertyChangeEvent)
2071          */
2072         protected void handlePreferencePropertyChanged(
2073                         org.eclipse.core.runtime.Preferences.PropertyChangeEvent event) {
2074                 AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer();
2075                 if (asv != null) {
2076                         String p = event.getProperty();
2077                         if (CODE_FORMATTER_TAB_SIZE.equals(p)) {
2078                                 asv.updateIndentationPrefixes();
2079                                 if (fTabConverter != null)
2080                                         fTabConverter.setNumberOfSpacesPerTab(getTabSize());
2081                         }
2082                 }
2083                 super.handlePreferencePropertyChanged(event);
2084         }
2085
2086         /**
2087          * Handles a property change event describing a change of the php core's
2088          * preferences and updates the preference related editor properties.
2089          * 
2090          * @param event
2091          *            the property change event
2092          */
2093         // protected void
2094         // handlePreferencePropertyChanged(org.eclipse.core.runtime.Preferences.PropertyChangeEvent
2095         // event) {
2096         // AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer();
2097         // if (asv != null) {
2098         // String p = event.getProperty();
2099         // if (CODE_FORMATTER_TAB_SIZE.equals(p)) {
2100         // asv.updateIndentationPrefixes();
2101         // if (fTabConverter != null)
2102         // fTabConverter.setNumberOfSpacesPerTab(getTabSize());
2103         // }
2104         // }
2105         // }
2106         /*
2107          * @see net.sourceforge.phpdt.internal.ui.javaeditor.JavaEditor#createJavaSourceViewer(org.eclipse.swt.widgets.Composite,
2108          *      org.eclipse.jface.text.source.IVerticalRuler,
2109          *      org.eclipse.jface.text.source.IOverviewRuler, boolean, int)
2110          */
2111         protected ISourceViewer createJavaSourceViewer(Composite parent,
2112                         IVerticalRuler verticalRuler, IOverviewRuler overviewRuler,
2113                         boolean isOverviewRulerVisible, int styles, IPreferenceStore store) {
2114                 return new AdaptedSourceViewer(parent, verticalRuler, overviewRuler,
2115                                 isOverviewRulerVisible, styles, store);
2116         }
2117
2118         // protected ISourceViewer createJavaSourceViewer(Composite parent,
2119         // IVerticalRuler ruler, int styles) {
2120         // return new AdaptedSourceViewer(parent, ruler, styles);
2121         // }
2122         private boolean isValidSelection(int offset, int length) {
2123                 IDocumentProvider provider = getDocumentProvider();
2124                 if (provider != null) {
2125                         IDocument document = provider.getDocument(getEditorInput());
2126                         if (document != null) {
2127                                 int end = offset + length;
2128                                 int documentLength = document.getLength();
2129                                 return 0 <= offset && offset <= documentLength && 0 <= end
2130                                                 && end <= documentLength;
2131                         }
2132                 }
2133                 return false;
2134         }
2135
2136         /*
2137          * @see net.sourceforge.phpdt.internal.ui.javaeditor.JavaEditor#getInputElement()
2138          */
2139         protected IJavaElement getInputJavaElement() {
2140                 return WebUI.getDefault().getWorkingCopyManager()
2141                                 .getWorkingCopy(getEditorInput());
2142         }
2143
2144         /*
2145          * @see AbstractTextEditor#editorContextMenuAboutToShow(IMenuManager)
2146          */
2147         public void editorContextMenuAboutToShow(IMenuManager menu) {
2148                 super.editorContextMenuAboutToShow(menu);
2149                 ActionContext context = new ActionContext(getSelectionProvider()
2150                                 .getSelection());
2151                 fContextMenuGroup.setContext(context);
2152                 fContextMenuGroup.fillContextMenu(menu);
2153                 fContextMenuGroup.setContext(null);
2154         }
2155
2156         /*
2157          * @see JavaEditor#setOutlinePageInput(JavaOutlinePage, IEditorInput)
2158          */
2159         protected void setOutlinePageInput(JavaOutlinePage page, IEditorInput input) {
2160                 if (page != null) {
2161                         IWorkingCopyManager manager = WebUI.getDefault()
2162                                         .getWorkingCopyManager();
2163                         page.setInput(manager.getWorkingCopy(input));
2164                 }
2165         }
2166
2167         /*
2168          * @see AbstractTextEditor#performSaveOperation(WorkspaceModifyOperation,
2169          *      IProgressMonitor)
2170          */
2171         // protected void performSaveOperation(WorkspaceModifyOperation operation,
2172         // IProgressMonitor progressMonitor) {
2173         // IDocumentProvider p = getDocumentProvider();
2174         // if (p instanceof PHPDocumentProvider) {
2175         // PHPDocumentProvider cp = (PHPDocumentProvider) p;
2176         // cp.setSavePolicy(fSavePolicy);
2177         // }
2178         //
2179         // try {
2180         // super.performSaveOperation(operation, progressMonitor);
2181         // } finally {
2182         // if (p instanceof PHPDocumentProvider) {
2183         // PHPDocumentProvider cp = (PHPDocumentProvider) p;
2184         // cp.setSavePolicy(null);
2185         // }
2186         // }
2187         // }
2188         /*
2189          * @see AbstractTextEditor#doSave(IProgressMonitor)
2190          */
2191         public void doSave(IProgressMonitor progressMonitor) {
2192
2193                 IDocumentProvider p = getDocumentProvider();
2194                 if (p == null) {
2195                         // editor has been closed
2196                         return;
2197                 }
2198
2199                 if (p.isDeleted(getEditorInput())) {
2200
2201                         if (isSaveAsAllowed()) {
2202
2203                                 /*
2204                                  * 1GEUSSR: ITPUI:ALL - User should never loose changes made in
2205                                  * the editors. Changed Behavior to make sure that if called
2206                                  * inside a regular save (because of deletion of input element)
2207                                  * there is a way to report back to the caller.
2208                                  */
2209                                 performSaveAs(progressMonitor);
2210
2211                         } else {
2212
2213                                 /*
2214                                  * 1GF5YOX: ITPJUI:ALL - Save of delete file claims it's still
2215                                  * there Missing resources.
2216                                  */
2217                                 Shell shell = getSite().getShell();
2218                                 MessageDialog
2219                                                 .openError(
2220                                                                 shell,
2221                                                                 PHPEditorMessages
2222                                                                                 .getString("PHPUnitEditor.error.saving.title1"), PHPEditorMessages.getString("PHPUnitEditor.error.saving.message1")); //$NON-NLS-1$ //$NON-NLS-2$
2223                         }
2224
2225                 } else {
2226                         if (getPreferenceStore().getBoolean(
2227                                         PreferenceConstants.EDITOR_P_RTRIM_ON_SAVE)) {
2228                                 RTrimAction trimAction = new RTrimAction();
2229                                 trimAction.setActiveEditor(null, getSite().getPage()
2230                                                 .getActiveEditor());
2231                                 trimAction.run(null);
2232                         }
2233
2234                         setStatusLineErrorMessage(null);
2235
2236                         updateState(getEditorInput());
2237                         validateState(getEditorInput());
2238
2239                         IWorkingCopyManager manager = WebUI.getDefault()
2240                                         .getWorkingCopyManager();
2241                         ICompilationUnit unit = manager.getWorkingCopy(getEditorInput());
2242
2243                         if (unit != null) {
2244                                 synchronized (unit) {
2245                                         performSave(false, progressMonitor);
2246                                 }
2247                         } else
2248                                 performSave(false, progressMonitor);
2249                 }
2250         }
2251
2252         public boolean isSaveAsAllowed() {
2253                 return true;
2254         }
2255
2256         /**
2257          * The compilation unit editor implementation of this
2258          * <code>AbstractTextEditor</code> method asks the user for the workspace
2259          * path of a file resource and saves the document there. See
2260          * http://dev.eclipse.org/bugs/show_bug.cgi?id=6295
2261          * 
2262          * @param progressMonitor
2263          *            the progress monitor
2264          */
2265         protected void performSaveAs(IProgressMonitor progressMonitor) {
2266
2267                 Shell shell = getSite().getShell();
2268                 IEditorInput input = getEditorInput();
2269
2270                 SaveAsDialog dialog = new SaveAsDialog(shell);
2271
2272                 IFile original = (input instanceof IFileEditorInput) ? ((IFileEditorInput) input)
2273                                 .getFile()
2274                                 : null;
2275                 if (original != null)
2276                         dialog.setOriginalFile(original);
2277
2278                 dialog.create();
2279
2280                 IDocumentProvider provider = getDocumentProvider();
2281                 if (provider == null) {
2282                         // editor has been programmatically closed while the dialog was open
2283                         return;
2284                 }
2285
2286                 if (provider.isDeleted(input) && original != null) {
2287                         String message = PHPEditorMessages
2288                                         .getFormattedString(
2289                                                         "CompilationUnitEditor.warning.save.delete", new Object[] { original.getName() }); //$NON-NLS-1$
2290                         dialog.setErrorMessage(null);
2291                         dialog.setMessage(message, IMessageProvider.WARNING);
2292                 }
2293
2294                 if (dialog.open() == Window.CANCEL) {
2295                         if (progressMonitor != null)
2296                                 progressMonitor.setCanceled(true);
2297                         return;
2298                 }
2299
2300                 IPath filePath = dialog.getResult();
2301                 if (filePath == null) {
2302                         if (progressMonitor != null)
2303                                 progressMonitor.setCanceled(true);
2304                         return;
2305                 }
2306
2307                 IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
2308                 IFile file = workspaceRoot.getFile(filePath);
2309                 final IEditorInput newInput = new FileEditorInput(file);
2310
2311                 boolean success = false;
2312                 try {
2313
2314                         provider.aboutToChange(newInput);
2315                         getDocumentProvider().saveDocument(progressMonitor, newInput,
2316                                         getDocumentProvider().getDocument(getEditorInput()), true);
2317                         success = true;
2318
2319                 } catch (CoreException x) {
2320                         IStatus status = x.getStatus();
2321                         if (status == null || status.getSeverity() != IStatus.CANCEL)
2322                                 ErrorDialog
2323                                                 .openError(
2324                                                                 shell,
2325                                                                 PHPEditorMessages
2326                                                                                 .getString("CompilationUnitEditor.error.saving.title2"), PHPEditorMessages.getString("CompilationUnitEditor.error.saving.message2"), x.getStatus()); //$NON-NLS-1$ //$NON-NLS-2$
2327                 } finally {
2328                         provider.changed(newInput);
2329                         if (success)
2330                                 setInput(newInput);
2331                 }
2332
2333                 if (progressMonitor != null)
2334                         progressMonitor.setCanceled(!success);
2335         }
2336
2337         /*
2338          * @see AbstractTextEditor#doSetInput(IEditorInput)
2339          */
2340         protected void doSetInput(IEditorInput input) throws CoreException {
2341                 super.doSetInput(input);
2342                 configureTabConverter();
2343                 configureToggleCommentAction();
2344         }
2345
2346         // /*
2347         // * @see
2348         // net.sourceforge.phpdt.internal.ui.javaeditor.JavaEditor#installOverrideIndicator(boolean)
2349         // * @since 3.0
2350         // */
2351         // protected void installOverrideIndicator(boolean waitForReconcilation) {
2352         // IAnnotationModel model=
2353         // getDocumentProvider().getAnnotationModel(getEditorInput());
2354         // if (!waitForReconcilation)
2355         // super.installOverrideIndicator(false);
2356         // else {
2357         // uninstallOverrideIndicator();
2358         // IJavaElement inputElement= getInputJavaElement();
2359         // if (model == null || inputElement == null)
2360         // return;
2361         //
2362         // fOverrideIndicatorManager= new OverrideIndicatorManager(model,
2363         // inputElement, null);
2364         // addReconcileListener(fOverrideIndicatorManager);
2365         // }
2366         // }
2367         //
2368         // /*
2369         // * @see
2370         // net.sourceforge.phpdt.internal.ui.javaeditor.JavaEditor#uninstallOverrideIndicator()
2371         // * @since 3.0
2372         // */
2373         // protected void uninstallOverrideIndicator() {
2374         // if (fOverrideIndicatorManager != null)
2375         // removeReconcileListener(fOverrideIndicatorManager);
2376         // super.uninstallOverrideIndicator();
2377         // }
2378
2379         /**
2380          * Configures the toggle comment action
2381          * 
2382          * @since 3.0
2383          */
2384         private void configureToggleCommentAction() {
2385                 IAction action = getAction("ToggleComment"); //$NON-NLS-1$
2386                 if (action instanceof ToggleCommentAction) {
2387                         ISourceViewer sourceViewer = getSourceViewer();
2388                         SourceViewerConfiguration configuration = getSourceViewerConfiguration();
2389                         ((ToggleCommentAction) action).configure(sourceViewer,
2390                                         configuration);
2391                 }
2392         }
2393
2394         // private void configureTabConverter() {
2395         // if (fTabConverter != null) {
2396         // IDocumentProvider provider = getDocumentProvider();
2397         // if (provider instanceof PHPDocumentProvider) {
2398         // PHPDocumentProvider cup = (PHPDocumentProvider) provider;
2399         // fTabConverter.setLineTracker(cup.createLineTracker(getEditorInput()));
2400         // }
2401         // }
2402         // }
2403         private void configureTabConverter() {
2404                 if (fTabConverter != null) {
2405                         IDocumentProvider provider = getDocumentProvider();
2406                         if (provider instanceof ICompilationUnitDocumentProvider) {
2407                                 ICompilationUnitDocumentProvider cup = (ICompilationUnitDocumentProvider) provider;
2408                                 fTabConverter.setLineTracker(cup
2409                                                 .createLineTracker(getEditorInput()));
2410                         }
2411                 }
2412         }
2413
2414         private void startTabConversion() {
2415                 if (fTabConverter == null) {
2416                         fTabConverter = new TabConverter();
2417                         configureTabConverter();
2418                         fTabConverter.setNumberOfSpacesPerTab(getTabSize());
2419                         AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer();
2420                         asv.addTextConverter(fTabConverter);
2421                         // http://dev.eclipse.org/bugs/show_bug.cgi?id=19270
2422                         asv.updateIndentationPrefixes();
2423                 }
2424         }
2425
2426         private void stopTabConversion() {
2427                 if (fTabConverter != null) {
2428                         AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer();
2429                         asv.removeTextConverter(fTabConverter);
2430                         // http://dev.eclipse.org/bugs/show_bug.cgi?id=19270
2431                         asv.updateIndentationPrefixes();
2432                         fTabConverter = null;
2433                 }
2434         }
2435
2436         /*
2437          * @see org.eclipse.ui.texteditor.AbstractTextEditor#performSave(boolean,
2438          *      org.eclipse.core.runtime.IProgressMonitor)
2439          */
2440         protected void performSave(boolean overwrite,
2441                         IProgressMonitor progressMonitor) {
2442                 // IDocumentProvider p = getDocumentProvider();
2443                 // if (p instanceof PHPDocumentProvider) {
2444                 // PHPDocumentProvider cp = (PHPDocumentProvider) p;
2445                 // cp.setSavePolicy(fSavePolicy);
2446                 // }
2447                 // try {
2448                 // super.performSave(overwrite, progressMonitor);
2449                 // } finally {
2450                 // if (p instanceof PHPDocumentProvider) {
2451                 // PHPDocumentProvider cp = (PHPDocumentProvider) p;
2452                 // cp.setSavePolicy(null);
2453                 // }
2454                 // }
2455
2456                 IDocumentProvider p = getDocumentProvider();
2457                 if (p instanceof ICompilationUnitDocumentProvider) {
2458                         ICompilationUnitDocumentProvider cp = (ICompilationUnitDocumentProvider) p;
2459                         cp.setSavePolicy(fSavePolicy);
2460                 }
2461                 try {
2462                         super.performSave(overwrite, progressMonitor);
2463                 } finally {
2464                         if (p instanceof ICompilationUnitDocumentProvider) {
2465                                 ICompilationUnitDocumentProvider cp = (ICompilationUnitDocumentProvider) p;
2466                                 cp.setSavePolicy(null);
2467                         }
2468                 }
2469         }
2470
2471         /*
2472          * @see AbstractTextEditor#doSaveAs
2473          */
2474         public void doSaveAs() {
2475                 if (askIfNonWorkbenchEncodingIsOk()) {
2476                         super.doSaveAs();
2477                 }
2478         }
2479
2480         /**
2481          * Asks the user if it is ok to store in non-workbench encoding.
2482          * 
2483          * @return <true>if the user wants to continue
2484          */
2485         private boolean askIfNonWorkbenchEncodingIsOk() {
2486                 IDocumentProvider provider = getDocumentProvider();
2487                 if (provider instanceof IStorageDocumentProvider) {
2488                         IEditorInput input = getEditorInput();
2489                         IStorageDocumentProvider storageProvider = (IStorageDocumentProvider) provider;
2490                         String encoding = storageProvider.getEncoding(input);
2491                         String defaultEncoding = storageProvider.getDefaultEncoding();
2492                         if (encoding != null && !encoding.equals(defaultEncoding)) {
2493                                 Shell shell = getSite().getShell();
2494                                 String title = PHPEditorMessages
2495                                                 .getString("PHPUnitEditor.warning.save.nonWorkbenchEncoding.title"); //$NON-NLS-1$
2496                                 String msg;
2497                                 if (input != null)
2498                                         msg = MessageFormat
2499                                                         .format(
2500                                                                         PHPEditorMessages
2501                                                                                         .getString("PHPUnitEditor.warning.save.nonWorkbenchEncoding.message1"),
2502                                                                         new String[] { input.getName(), encoding }); //$NON-NLS-1$
2503                                 else
2504                                         msg = MessageFormat
2505                                                         .format(
2506                                                                         PHPEditorMessages
2507                                                                                         .getString("PHPUnitEditor.warning.save.nonWorkbenchEncoding.message2"),
2508                                                                         new String[] { encoding }); //$NON-NLS-1$
2509                                 return MessageDialog.openQuestion(shell, title, msg);
2510                         }
2511                 }
2512                 return true;
2513         }
2514
2515         /*
2516          * @see net.sourceforge.phpdt.internal.ui.text.java.IJavaReconcilingListener#aboutToBeReconciled()
2517          * @since 3.0
2518          */
2519         public void aboutToBeReconciled() {
2520
2521                 // Notify AST provider
2522                 // PHPeclipsePlugin.getDefault().getASTProvider().aboutToBeReconciled(getInputJavaElement());
2523
2524                 // Notify listeners
2525                 Object[] listeners = fReconcilingListeners.getListeners();
2526                 for (int i = 0, length = listeners.length; i < length; ++i)
2527                         ((IJavaReconcilingListener) listeners[i]).aboutToBeReconciled();
2528         }
2529
2530         /*
2531          * @see net.sourceforge.phpdt.internal.ui.text.java.IJavaReconcilingListener#reconciled(CompilationUnit,
2532          *      boolean, IProgressMonitor)
2533          * @since 3.0
2534          */
2535         public void reconciled(CompilationUnit ast, boolean forced,
2536                         IProgressMonitor progressMonitor) {
2537
2538                 // Always notify AST provider
2539                 // PHPeclipsePlugin.getDefault().getASTProvider().reconciled(ast,
2540                 // getInputJavaElement());
2541
2542                 // Notify listeners
2543                 // Object[] listeners = fReconcilingListeners.getListeners();
2544                 // for (int i = 0, length= listeners.length; i < length; ++i)
2545                 // ((IJavaReconcilingListener)listeners[i]).reconciled(ast, forced,
2546                 // progressMonitor);
2547
2548                 // Update Java Outline page selection
2549                 if (!forced && !progressMonitor.isCanceled()) {
2550                         Shell shell = getSite().getShell();
2551                         if (shell != null && !shell.isDisposed()) {
2552                                 shell.getDisplay().asyncExec(new Runnable() {
2553                                         public void run() {
2554                                                 selectionChanged();
2555                                         }
2556                                 });
2557                         }
2558                 }
2559         }
2560
2561         /**
2562          * Returns the updated java element for the old java element.
2563          */
2564         private IJavaElement findElement(IJavaElement element) {
2565                 if (element == null)
2566                         return null;
2567                 IWorkingCopyManager manager = WebUI.getDefault()
2568                                 .getWorkingCopyManager();
2569                 ICompilationUnit unit = manager.getWorkingCopy(getEditorInput());
2570                 if (unit != null) {
2571                         try {
2572                                 synchronized (unit) {
2573                                         unit.reconcile();
2574                                 }
2575                                 IJavaElement[] findings = unit.findElements(element);
2576                                 if (findings != null && findings.length > 0)
2577                                         return findings[0];
2578                         } catch (JavaModelException x) {
2579                                 PHPeclipsePlugin.log(x.getStatus());
2580                                 // nothing found, be tolerant and go on
2581                         }
2582                 }
2583                 return null;
2584         }
2585
2586         /**
2587          * Returns the offset of the given Java element.
2588          */
2589         private int getOffset(IJavaElement element) {
2590                 if (element instanceof ISourceReference) {
2591                         ISourceReference sr = (ISourceReference) element;
2592                         try {
2593                                 ISourceRange srcRange = sr.getSourceRange();
2594                                 if (srcRange != null)
2595                                         return srcRange.getOffset();
2596                         } catch (JavaModelException e) {
2597                         }
2598                 }
2599                 return -1;
2600         }
2601
2602         /*
2603          * @see AbstractTextEditor#restoreSelection()
2604          */
2605         // protected void restoreSelection() {
2606         // try {
2607         // if (getSourceViewer() == null || fRememberedSelection == null)
2608         // return;
2609         // IJavaElement newElement = findElement(fRememberedElement);
2610         // int newOffset = getOffset(newElement);
2611         // int delta = (newOffset > -1 && fRememberedElementOffset > -1) ? newOffset
2612         // - fRememberedElementOffset : 0;
2613         // if (isValidSelection(delta + fRememberedSelection.getOffset(),
2614         // fRememberedSelection.getLength()))
2615         // selectAndReveal(delta + fRememberedSelection.getOffset(),
2616         // fRememberedSelection.getLength());
2617         // } finally {
2618         // fRememberedSelection = null;
2619         // fRememberedElement = null;
2620         // fRememberedElementOffset = -1;
2621         // }
2622         // }
2623         /**
2624          * Tells whether this is the active editor in the active page.
2625          * 
2626          * @return <code>true</code> if this is the active editor in the active
2627          *         page
2628          * @see IWorkbenchPage#getActiveEditor();
2629          */
2630         protected final boolean isActiveEditor() {
2631                 IWorkbenchWindow window = getSite().getWorkbenchWindow();
2632                 IWorkbenchPage page = window.getActivePage();
2633                 if (page == null)
2634                         return false;
2635                 IEditorPart activeEditor = page.getActiveEditor();
2636                 return activeEditor != null && activeEditor.equals(this);
2637         }
2638
2639         /**
2640          * Adds the given listener. Has no effect if an identical listener was not
2641          * already registered.
2642          * 
2643          * @param listener
2644          *            The reconcile listener to be added
2645          * @since 3.0
2646          */
2647         final void addReconcileListener(IJavaReconcilingListener listener) {
2648                 synchronized (fReconcilingListeners) {
2649                         fReconcilingListeners.add(listener);
2650                 }
2651         }
2652
2653         /**
2654          * Removes the given listener. Has no effect if an identical listener was
2655          * not already registered.
2656          * 
2657          * @param listener
2658          *            the reconcile listener to be removed
2659          * @since 3.0
2660          */
2661         final void removeReconcileListener(IJavaReconcilingListener listener) {
2662                 synchronized (fReconcilingListeners) {
2663                         fReconcilingListeners.remove(listener);
2664                 }
2665         }
2666
2667         protected void updateStateDependentActions() {
2668                 super.updateStateDependentActions();
2669                 fGenerateActionGroup.editorStateChanged();
2670         }
2671
2672         /*
2673          * @see AbstractTextEditor#rememberSelection()
2674          */
2675         protected void rememberSelection() {
2676                 fRememberedSelection.remember();
2677         }
2678
2679         /*
2680          * @see AbstractTextEditor#restoreSelection()
2681          */
2682         protected void restoreSelection() {
2683                 fRememberedSelection.restore();
2684         }
2685
2686         /*
2687          * @see AbstractTextEditor#canHandleMove(IEditorInput, IEditorInput)
2688          */
2689         protected boolean canHandleMove(IEditorInput originalElement,
2690                         IEditorInput movedElement) {
2691
2692                 String oldExtension = ""; //$NON-NLS-1$
2693                 if (originalElement instanceof IFileEditorInput) {
2694                         IFile file = ((IFileEditorInput) originalElement).getFile();
2695                         if (file != null) {
2696                                 String ext = file.getFileExtension();
2697                                 if (ext != null)
2698                                         oldExtension = ext;
2699                         }
2700                 }
2701
2702                 String newExtension = ""; //$NON-NLS-1$
2703                 if (movedElement instanceof IFileEditorInput) {
2704                         IFile file = ((IFileEditorInput) movedElement).getFile();
2705                         if (file != null)
2706                                 newExtension = file.getFileExtension();
2707                 }
2708
2709                 return oldExtension.equals(newExtension);
2710         }
2711
2712         /*
2713          * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#isPrefQuickDiffAlwaysOn()
2714          */
2715         protected boolean isPrefQuickDiffAlwaysOn() {
2716                 // reestablishes the behaviour from AbstractDecoratedTextEditor which
2717                 // was hacked by JavaEditor
2718                 // to disable the change bar for the class file (attached source) java
2719                 // editor.
2720                 IPreferenceStore store = getPreferenceStore();
2721                 return store
2722                                 .getBoolean(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_ALWAYS_ON);
2723         }
2724
2725         /*
2726          * @see net.sourceforge.phpdt.internal.ui.javaeditor.JavaEditor#getAdapter(java.lang.Class)
2727          */
2728         public Object getAdapter(Class required) {
2729                 if (SmartBackspaceManager.class.equals(required)) {
2730                         if (getSourceViewer() instanceof JavaSourceViewer) {
2731                                 return ((JavaSourceViewer) getSourceViewer())
2732                                                 .getBackspaceManager();
2733                         }
2734                 }
2735
2736                 return super.getAdapter(required);
2737         }
2738
2739         /**
2740          * Returns the mutex for the reconciler. See
2741          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898 for a description of
2742          * the problem.
2743          * <p>
2744          * TODO remove once the underlying problem is solved.
2745          * </p>
2746          * 
2747          * @return the lock reconcilers may use to synchronize on
2748          */
2749         public Object getReconcilerLock() {
2750                 return fReconcilerLock;
2751         }
2752
2753         /*
2754          * (non-Javadoc)
2755          * 
2756          * @see org.eclipse.ui.texteditor.AbstractTextEditor#editorSaved()
2757          */
2758         protected void editorSaved() {
2759                 super.editorSaved();
2760                 ShowExternalPreviewAction a = ShowExternalPreviewAction.getInstance();
2761                 if (a != null) {
2762                         //a.refresh(ShowExternalPreviewAction.PHP_TYPE);
2763                         a.doRun(ShowExternalPreviewAction.PHP_TYPE);
2764                 }
2765         }
2766 }