com.aptana.ide.editors.unified.UnifiedEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.aptana.ide.editors.unified.UnifiedEditor.java

Source

/**
 * This file Copyright (c) 2005-2008 Aptana, Inc. This program is
 * dual-licensed under both the Aptana Public License and the GNU General
 * Public license. You may elect to use one or the other of these licenses.
 * 
 * This program is distributed in the hope that it will be useful, but
 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 * NONINFRINGEMENT. Redistribution, except as permitted by whichever of
 * the GPL or APL you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or modify this
 * program under the terms of the GNU General Public License,
 * Version 3, as published by the Free Software Foundation.  You should
 * have received a copy of the GNU General Public License, Version 3 along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * Aptana provides a special exception to allow redistribution of this file
 * with certain other free and open source software ("FOSS") code and certain additional terms
 * pursuant to Section 7 of the GPL. You may view the exception and these
 * terms on the web at http://www.aptana.com/legal/gpl/.
 * 
 * 2. For the Aptana Public License (APL), this program and the
 * accompanying materials are made available under the terms of the APL
 * v1.0 which accompanies this distribution, and is available at
 * http://www.aptana.com/legal/apl/.
 * 
 * You may view the GPL, Aptana's exception and additional terms, and the
 * APL in the file titled license.html at the root of the corresponding
 * plugin containing this source file.
 * 
 * Any modifications to this file must keep this entire header intact.
 */
package com.aptana.ide.editors.unified;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.help.IContextProvider;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.WhitespaceCharacterPainter;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationRulerColumn;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.IVerticalRulerColumn;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.text.source.UnifiedLineNumberChangeRulerColumn;
import org.eclipse.jface.text.source.UnifiedOverviewRuler;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.text.source.projection.UnifiedProjectionSupport;
import org.eclipse.jface.text.source.projection.UnifiedRulerColumn;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.LineStyleEvent;
import org.eclipse.swt.custom.LineStyleListener;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.TextChangeListener;
import org.eclipse.swt.custom.TextChangedEvent;
import org.eclipse.swt.custom.TextChangingEvent;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Caret;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.editors.text.EditorsPlugin;
import org.eclipse.ui.internal.editors.text.NonExistingFileEditorInput;
import org.eclipse.ui.navigator.CommonNavigator;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.BookmarkRulerAction;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.eclipse.ui.texteditor.TaskRulerAction;
import org.eclipse.ui.texteditor.TextOperationAction;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import com.aptana.ide.core.IdeLog;
import com.aptana.ide.core.StringUtils;
import com.aptana.ide.core.ui.CoreUIUtils;
import com.aptana.ide.core.ui.editors.ISaveAsEvent;
import com.aptana.ide.core.ui.editors.ISaveEvent;
import com.aptana.ide.editors.UnifiedEditorsPlugin;
import com.aptana.ide.editors.actions.OpenDeclarationAction;
import com.aptana.ide.editors.formatting.UnifiedBracketInserterManager;
import com.aptana.ide.editors.managers.FileContextManager;
import com.aptana.ide.editors.preferences.IPreferenceConstants;
import com.aptana.ide.editors.unified.actions.CodeFormatAction;
import com.aptana.ide.editors.unified.actions.GotoMatchingBracketAction;
import com.aptana.ide.editors.unified.actions.UnifiedActionContributor;
import com.aptana.ide.editors.unified.colorizer.LanguageColorizer;
import com.aptana.ide.editors.unified.colorizer.UnifiedCursorLinePainter;
import com.aptana.ide.editors.unified.contentassist.ContentAssistAction;
import com.aptana.ide.editors.unified.context.ContextItem;
import com.aptana.ide.editors.unified.context.IContextAwareness;
import com.aptana.ide.editors.unified.errors.ExternalFileErrorListener;
import com.aptana.ide.editors.unified.errors.FileErrorListener;
import com.aptana.ide.editors.unified.errors.ProjectFileErrorListener;
import com.aptana.ide.editors.unified.folding.FoldingExtensionPointLoader;
import com.aptana.ide.editors.unified.folding.LanguageProjectAnnotation;
import com.aptana.ide.editors.unified.help.LexemeUIHelp;
import com.aptana.ide.editors.unified.messaging.UnifiedMessages;
import com.aptana.ide.editors.untitled.BaseTextEditor;
import com.aptana.ide.lexer.IRange;
import com.aptana.ide.lexer.Lexeme;
import com.aptana.ide.lexer.LexemeList;
import com.aptana.ide.lexer.TokenCategories;
import com.aptana.ide.parsing.IParseState;
import com.aptana.ide.views.outline.UnifiedOutlinePage;
import com.aptana.ide.views.outline.UnifiedQuickOutlinePage;

/**
 * UnifiedEditor
 */
public abstract class UnifiedEditor extends BaseTextEditor
        implements IUnifiedEditor, IPropertyChangeListener, ISelectionChangedListener, IRedrawRangeListener {
    /**
     * ADD_BOOKMARK
     */
    public static final String ADD_BOOKMARK = "AddBookmark"; //$NON-NLS-1$

    /**
     * ADD_TASK
     */
    public static final String ADD_TASK = "AddTask"; //$NON-NLS-1$

    /**
     * ctrlDown
     */
    public static boolean ctrlDown;

    /**
     * outlinePage
     */
    protected UnifiedOutlinePage outlinePage;

    /**
     * Auto activate for code assist, used for ContentAssistant AutoActivation listener
     */
    protected boolean autoActivateCodeAssist = true;

    /**
     * File service change listeners
     */
    protected ListenerList fileServiceChangeListeners = new ListenerList();

    private UnifiedColorizer _colorizer;
    private IUnifiedEditorContributor _baseContributor;
    private FileErrorListener _errorListener;
    private WhitespaceCharacterPainter _whitespacePainter;
    private EditorFileContext _fileContextWrapper;
    // private UnifiedBracketMatcher bracketMatcher;
    private ArrayList<ISaveAsEvent> _saveAsListeners;
    private ArrayList<ISaveEvent> _saveListeners;
    private CommonNavigator _fileExplorerView;
    // private TextColorer textColorer;
    private IPreferenceStore _prefStore;
    private IPartListener _partListener;
    private UnifiedProjectionSupport _projectionSupport;
    private Listener _keyUpListener;
    private VerifyKeyListener _verifyKeyListener;
    private UnifiedViewer _viewer;
    private IContextAwareness _contextAwareness;
    private boolean _isDisposing = false;
    private boolean _hasKeyBeenPressed = false;
    private LineStyleListener _lineStyleListener;
    private TextChangeListener _textChangeListener;
    private PairMatcher _pairMatcher;
    private Image _caretImage;
    private UnifiedCursorLinePainter _painter;
    private int _maxColorizingColumns;
    private boolean _extendedStylesEnabled;
    private SelectionAdapter _selectionListener;
    private FocusAdapter _focusListener;
    private ITextListener _textListener;
    private IPreferenceStore _runtimeStore = new PreferenceStore();
    private UnifiedBracketInserterManager _bracketInserterManager;
    private CodeFormatAction _formatAction;

    /**
     * UnifiedEditor
     */
    public UnifiedEditor() {
        super();
        addPluginToPreferenceStore(UnifiedEditorsPlugin.getDefault());

        this._saveListeners = new ArrayList<ISaveEvent>();
        this._saveAsListeners = new ArrayList<ISaveAsEvent>();
        // bracketMatcher = new UnifiedBracketMatcher();

        /**
         * See UnifiedEditor.init for comments regarding the movement of object creation/abstract method calling from
         * this constructor to that method.
         */
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
     */
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        /**
         * The following lines of code were moved from the default constructor to allow anonymous UnifiedEditors to be
         * created from the GenericTextEditor. This code was moved to this specific method since the calling of this
         * method signifies the beginning of the editors life-cycle and it called following the call to the constructor.
         * See comments for AbstractTextEditor.init for more details about the timing of this method call.
         */

        this._baseContributor = createLocalContributor();
        this._colorizer = UnifiedColorizer.getInstance();

        setSourceViewerConfiguration(new UnifiedConfiguration(this, getPreferenceStore()));

        // Keeping in, as it may have side effects
        getFileServiceFactory();

        this._prefStore = UnifiedEditorsPlugin.getDefault().getPreferenceStore();
        this._prefStore = (this._prefStore == null) ? null : this._prefStore; // make compiler happy

        // TODO: Actuate change
        // setDocumentProvider(UnifiedDocumentProvider.getInstance());
        setDocumentProvider(createDocumentProvider());

        this._fileContextWrapper = new EditorFileContext();

        // Update the base contributor with the file context
        this._baseContributor.setFileContext(this._fileContextWrapper);

        /**
         * This marks the end of the moved lines from the default UnifiedEditor constructor
         */

        syncFoldingPreferenceStore();

        super.init(site, input);
        setEditorContextMenuId(getSite().getId() + "#UnifiedEditorContext"); //$NON-NLS-1$
        setRulerContextMenuId(getSite().getId() + "#UnifiedRulerContext"); //$NON-NLS-1$

        IPreferenceStore localPreferenceStore = this.getPreferenceStore();
        if (localPreferenceStore != null) {
            autoActivateCodeAssist = localPreferenceStore
                    .getBoolean(IPreferenceConstants.CODE_ASSIST_AUTO_ACTIVATION);
        }

        IUnifiedEditorContributor[] contributors = this._baseContributor.getChildContributors();
        // Process children for auto activate code assist if the parent is not
        // already requesting it
        if (contributors != null && !autoActivateCodeAssist) {
            for (int i = 0; i < contributors.length; i++) {
                autoActivateCodeAssist = autoActivateCodeAssist || contributors[i].isAutoActivateContentAssist();
                // Once we find a language that wants it we just break and it
                // will be setup to listen
                if (autoActivateCodeAssist) {
                    break;
                }
            }
        }
    }

    /**
     * This method circumvents that the line change ruler on AbstractDecoratedTextEditor is private. A placeholder pref
     * store is used to sync our colorization values with the Eclipse values that this ruler pulls from.
     */
    private void syncFoldingPreferenceStore() {
        if (this._baseContributor != null) {
            String language = this._baseContributor.getLocalContentType();
            LanguageColorizer colorizer = LanguageRegistry.getLanguageColorizer(language);
            if (colorizer != null && colorizer.getFoldingBg() != null) {
                this._runtimeStore.setDefault(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT, true);
                this._runtimeStore.setValue(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT, false);
                PreferenceConverter.setValue(this._runtimeStore, PREFERENCE_COLOR_BACKGROUND,
                        colorizer.getFoldingBg().getRGB());
            }
        }
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#rulerContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
     */
    protected void rulerContextMenuAboutToShow(IMenuManager menu) {
        menu.add(getAction(ADD_BOOKMARK));
        menu.add(getAction(ADD_TASK));
        menu.add(new Separator("source"));// TODO: add this constant //$NON-NLS-1$
        menu.add(_formatAction);
        _formatAction.setActiveEditor(null, this);
        menu.add(new Separator());
        super.rulerContextMenuAboutToShow(menu);
        FoldingExtensionPointLoader.fillRulerContextMenu(this, menu);
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#editorContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
     */
    protected void editorContextMenuAboutToShow(IMenuManager menu) {
        menu.add(new Separator("debug")); // TODO: find where this constant is defined //$NON-NLS-1$

        super.editorContextMenuAboutToShow(menu);
        IUnifiedEditorContributor contributor = getBaseContributor();
        if (contributor != null) {
            String mimeType = contributor.getLocalContentType();
            if (mimeType != null && LanguageRegistry.getCodeFormatter(mimeType) != null) {
                menu.add(new Separator("source")); // TODO: add this constant //$NON-NLS-1$
                menu.add(_formatAction);
                _formatAction.setActiveEditor(null, this);
            }
            OpenDeclarationAction openDelcaration = new OpenDeclarationAction();
            openDelcaration.setActiveEditor(this, this.getFileContext().getLanguageService(mimeType));
            menu.add(openDelcaration);
        }
    }

    /**
     * Adds the specified plugin to the list of plugin stores to check when searching for preference
     * 
     * @param plugin
     *            The plugin to add
     */
    protected void addPluginToPreferenceStore(AbstractUIPlugin plugin) {

        setPreferenceStore(new ChainedPreferenceStore(
                new IPreferenceStore[] { this._runtimeStore, getPreferenceStore(), plugin.getPreferenceStore() }));
    }

    /**
     * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
     */
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);

        ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();

        if (viewer.canDoOperation(ProjectionViewer.TOGGLE)) {
            viewer.doOperation(ProjectionViewer.TOGGLE);
        }

        // TODO refactor to new colorizer system

        this._maxColorizingColumns = getPreferenceStore().getInt(IPreferenceConstants.COLORIZER_MAXCOLUMNS);
        linkColorer();

        linkPairMatcher();

        // Keyword/variable highlight
        this._extendedStylesEnabled = getPreferenceStore()
                .getBoolean(IPreferenceConstants.COLORIZER_TEXT_HIGHLIGHT_ENABLED);
        if (this._extendedStylesEnabled) {
            installTextOccurrenceHighlightSupport();
        }

        // Overridden here, as this matches what JavaEditor does. Can be done at
        // any point after
        // text widget is created.
        LexemeUIHelp.setHelp(this, this.getViewer().getTextWidget(), getFileContext());

        // Add support for custom colors for background, selection, line
        // highlight, and caret
        setEditorOptions();
    }

    private void setEditorOptions() {
        // Check in case init hasn't been called yet or creating the contributor
        // failed
        if (getBaseContributor() == null || this.getViewer() == null || this.getViewer().getTextWidget() == null) {
            return;
        }

        String language = getBaseContributor().getLocalContentType();
        LanguageColorizer colorizer = LanguageRegistry.getLanguageColorizer(language);
        if (colorizer != null && colorizer.getLineHighlightColor() != null && colorizer.getCaretColor() != null
                && colorizer.getBackground() != null && colorizer.getSelectionForeground() != null
                && colorizer.getSelectionBackground() != null) {
            if (this.getViewer() instanceof ITextViewerExtension2) {
                ITextViewerExtension2 highlightEditor = (ITextViewerExtension2) this.getViewer();
                if (this.getViewer().getTextWidget() != null) {
                    if (this._painter != null) {
                        highlightEditor.removePainter(this._painter);
                    }
                    this._painter = new UnifiedCursorLinePainter(this, this.getViewer());
                    this._painter.setHighlightColor(colorizer.getLineHighlightColor());
                    highlightEditor.addPainter(this._painter);

                    if (this._selectionListener != null) {
                        this.getViewer().getTextWidget().removeSelectionListener(this._selectionListener);
                    }
                    if (this._focusListener != null) {
                        this.getViewer().getTextWidget().removeFocusListener(this._focusListener);
                    }
                    if (this._textListener != null) {
                        this.getViewer().removeTextListener(this._textListener);
                    }

                    this._selectionListener = new SelectionAdapter() {
                        public void widgetSelected(SelectionEvent e) {
                            StyledText text = getViewer().getTextWidget();

                            if (_painter != null && text != null && !text.isDisposed()) {
                                Point p = text.getSelectionRange();
                                if (p.y == 0) {
                                    _painter.paintLines(0, text.getLineCount());
                                    text.redraw();
                                    text.update();
                                }
                            }
                        }
                    };
                    this._focusListener = new FocusAdapter() {
                        public void focusGained(FocusEvent e) {
                            StyledText text = getViewer().getTextWidget();

                            if (_painter != null && text != null && !text.isDisposed()) {
                                Point p = text.getSelectionRange();
                                if (p.y > 0) {
                                    _painter.paintLines(0, text.getLineCount());
                                }
                            }
                        }
                    };
                    this._textListener = new ITextListener() {

                        public void textChanged(TextEvent event) {
                            StyledText text = getViewer().getTextWidget();

                            if (_painter != null && text != null && !text.isDisposed()) {
                                Point p = text.getSelectionRange();
                                if (p.y > 0) {
                                    _painter.paintLines(0, text.getLineCount());
                                }
                            }
                        }
                    };

                    this.getViewer().addTextListener(this._textListener);
                    this.getViewer().getTextWidget().addFocusListener(this._focusListener);
                    this.getViewer().getTextWidget().addSelectionListener(this._selectionListener);

                    if (isWordWrapEnabled()) {
                        this.getViewer().getTextWidget().setWordWrap(true);
                        this.getViewer().getTextWidget().addControlListener(new ControlAdapter() {

                            public void controlResized(ControlEvent e) {
                                if (_projectionSupport != null) {
                                    UnifiedRulerColumn column = _projectionSupport.getRulerColumn();
                                    if (column != null) {
                                        column.redraw();
                                    }
                                }
                                IVerticalRuler ruler = getVerticalRuler();
                                if (ruler instanceof CompositeRuler) {
                                    Iterator columnIter = ((CompositeRuler) ruler).getDecoratorIterator();
                                    while (columnIter.hasNext()) {
                                        Object column = columnIter.next();
                                        if (column instanceof AnnotationRulerColumn) {
                                            ((AnnotationRulerColumn) column).redraw();
                                        }
                                    }
                                }
                                if (fLineNumberRulerColumn != null) {
                                    fLineNumberRulerColumn.redraw();
                                }
                                IOverviewRuler overviewRuler = getOverviewRuler();
                                if (overviewRuler != null) {
                                    overviewRuler.update();
                                }
                            }

                        });
                    }
                }
            }
            // Apply the folding background and forground colors
            setFoldingColors(colorizer);

            // Removing caret support on Carbon since it causes lag
            if (!Platform.getWS().equals(Platform.WS_CARBON)) {
                if (this.getViewer().getTextWidget() != null) {
                    Caret caret = this.getViewer().getTextWidget().getCaret();

                    // Correct cursor color accordining to line highlight color
                    RGB converted = new RGB(0, 0, 0);
                    converted.red = Math
                            .abs(colorizer.getCaretColor().getRed() - colorizer.getLineHighlightColor().getRed());
                    converted.blue = Math
                            .abs(colorizer.getCaretColor().getBlue() - colorizer.getLineHighlightColor().getBlue());
                    converted.green = Math.abs(
                            colorizer.getCaretColor().getGreen() - colorizer.getLineHighlightColor().getGreen());

                    PaletteData data = new PaletteData(new RGB[] { converted });
                    int x = caret.getSize().x;
                    int y = caret.getSize().y;
                    // Apparently the current caret may have invalid sizings
                    // that will cause errors when an attempt to
                    // change the color is made. So perform the check and catch
                    // errors and exceptions so caret coloring
                    // doesn't affect opening the editor.
                    if (x > 0 && y > 0) {
                        try {
                            ImageData iData = new ImageData(x, y, 1, data);
                            caret.setImage(null);
                            if (this._caretImage != null) {
                                this._caretImage.dispose();
                                this._caretImage = null;
                            }
                            this._caretImage = new Image(caret.getDisplay(), iData);
                            caret.setImage(this._caretImage);
                        } catch (Error e) {
                        } catch (Exception e) {
                        }
                    }
                }
            }

            Color bg = colorizer.getBackground();
            Color deadSpace = createDeadSpaceColor(bg);
            if (deadSpace != null && !deadSpace.isDisposed()) {
                this.getViewer().getTextWidget().setBackground(deadSpace);
            } else {
                this.getViewer().getTextWidget().setBackground(bg);
            }

            this.getViewer().getTextWidget().setSelectionBackground(colorizer.getSelectionBackground());
            this.getViewer().getTextWidget().setSelectionForeground(colorizer.getSelectionForeground());
        } else {
            IPreferenceStore store = EditorsPlugin.getDefault().getPreferenceStore();
            RGB background = PreferenceConverter.getColor(store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND);
            RGB selectionFg = PreferenceConverter.getColor(store,
                    AbstractTextEditor.PREFERENCE_COLOR_SELECTION_FOREGROUND);
            RGB selectionBg = PreferenceConverter.getColor(store,
                    AbstractTextEditor.PREFERENCE_COLOR_SELECTION_BACKGROUND);
            Color eclipseBackgroundColor = UnifiedColorManager.getInstance().getColor(background);
            if (store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)) {
                eclipseBackgroundColor = null;
            }
            Color eclipseSelectionFgColor = UnifiedColorManager.getInstance().getColor(selectionFg);
            if (store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT)) {
                eclipseSelectionFgColor = null;
            }
            Color eclipseSelectionBgColor = UnifiedColorManager.getInstance().getColor(selectionBg);
            if (store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT)) {
                eclipseSelectionBgColor = null;
            }

            Color deadSpace = createDeadSpaceColor(eclipseBackgroundColor);
            if (deadSpace != null) {
                this.getViewer().getTextWidget().setBackground(deadSpace);
            } else {
                this.getViewer().getTextWidget().setBackground(eclipseBackgroundColor);
            }

            this.getViewer().getTextWidget().setSelectionBackground(eclipseSelectionBgColor);
            this.getViewer().getTextWidget().setSelectionForeground(eclipseSelectionFgColor);
            Caret caret = this.getViewer().getTextWidget().getCaret();
            caret.setImage(null);
            if (this._caretImage != null) {
                this._caretImage.dispose();
                this._caretImage = null;
            }
            if (this.getViewer() instanceof ITextViewerExtension2) {
                ITextViewerExtension2 highlightEditor = (ITextViewerExtension2) this.getViewer();
                if (this._painter != null) {
                    highlightEditor.removePainter(this._painter);
                }
            }
        }
    }

    /**
     * Apply the forground and the background colors of the folding markers and bar.
     * 
     * @param colorizer
     */
    protected void setFoldingColors(LanguageColorizer colorizer) {
        if (colorizer.getFoldingFg() != null) {
            SourceViewerConfiguration config = this.getSourceViewerConfiguration();
            if (config instanceof UnifiedConfiguration) {
                UnifiedReconcilingStrategy strategy = ((UnifiedConfiguration) config).getStrategy();
                if (strategy != null) {
                    strategy.setFoldingAnnotationHoverColor(colorizer.getFoldingFg());
                }
            }
        }
        if (colorizer.getFoldingBg() != null) {
            if (this._projectionSupport != null) {
                UnifiedRulerColumn column = this._projectionSupport.getRulerColumn();
                if (column != null) {
                    column.setBackground(colorizer.getFoldingBg());
                }
            }
            if (fLineNumberRulerColumn != null && fLineNumberRulerColumn.getControl() != null
                    && !fLineNumberRulerColumn.getControl().isDisposed()) {
                if (fLineNumberRulerColumn instanceof UnifiedLineNumberChangeRulerColumn) {
                    ((UnifiedLineNumberChangeRulerColumn) fLineNumberRulerColumn)
                            .setTextWidget(getViewer().getTextWidget());
                    ((UnifiedLineNumberChangeRulerColumn) fLineNumberRulerColumn).setSourceViewer(getViewer());
                }
                fLineNumberRulerColumn.setBackground(colorizer.getFoldingBg());
                fLineNumberRulerColumn.redraw();
            }
        }
    }

    private boolean isWordWrapEnabled() {
        return UnifiedEditorsPlugin.getDefault().getPreferenceStore()
                .getBoolean(IPreferenceConstants.ENABLE_WORD_WRAP);
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#createLineNumberRulerColumn()
     */
    protected IVerticalRulerColumn createLineNumberRulerColumn() {
        if (isWordWrapEnabled()) {
            if (isPrefQuickDiffAlwaysOn()) {
                UnifiedLineNumberChangeRulerColumn column = new UnifiedLineNumberChangeRulerColumn(
                        getSharedColors());
                if (getViewer() != null) {
                    column.setSourceViewer(getViewer());
                    column.setTextWidget(getViewer().getTextWidget());
                }
                column.setHover(createChangeHover());
                // initializeChangeRulerColumn(column);
                fLineNumberRulerColumn = column;
            } else {
                fLineNumberRulerColumn = new UnifiedLineNumberChangeRulerColumn(getSharedColors());
                if (getViewer() != null) {
                    ((UnifiedLineNumberChangeRulerColumn) fLineNumberRulerColumn).setSourceViewer(getViewer());
                    ((UnifiedLineNumberChangeRulerColumn) fLineNumberRulerColumn)
                            .setTextWidget(getViewer().getTextWidget());
                }
            }
            initializeLineNumberRulerColumn(fLineNumberRulerColumn);
            return fLineNumberRulerColumn;
        } else {
            return super.createLineNumberRulerColumn();
        }
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#createOverviewRuler(org.eclipse.jface.text.source.ISharedTextColors)
     */
    protected IOverviewRuler createOverviewRuler(ISharedTextColors sharedColors) {
        if (isWordWrapEnabled()) {
            IOverviewRuler ruler = new UnifiedOverviewRuler(getAnnotationAccess(), VERTICAL_RULER_WIDTH,
                    sharedColors);
            Iterator e = EditorsPlugin.getDefault().getMarkerAnnotationPreferences().getAnnotationPreferences()
                    .iterator();
            while (e.hasNext()) {
                AnnotationPreference preference = (AnnotationPreference) e.next();
                if (preference.contributesToHeader())
                    ruler.addHeaderAnnotationType(preference.getAnnotationType());
            }
            return ruler;
        } else {
            return super.createOverviewRuler(sharedColors);
        }
    }

    private Color createDeadSpaceColor(Color bg) {
        if (bg != null) {
            int factor = 4;
            boolean canGoDarker = bg.getRed() - factor > 0 || bg.getGreen() - factor > 0
                    || bg.getBlue() - factor > 0;
            boolean canGoLighter = bg.getRed() + factor > 0 || bg.getGreen() + factor > 0
                    || bg.getBlue() + factor > 0;
            int red = 0;
            int green = 0;
            int blue = 0;
            if (canGoDarker) {
                red = bg.getRed() - factor > 0 ? (int) (bg.getRed() - factor) : bg.getRed();
                green = bg.getGreen() - factor > 0 ? (int) (bg.getGreen() - factor) : bg.getGreen();
                blue = bg.getBlue() - factor > 0 ? (int) (bg.getBlue() - factor) : bg.getBlue();
            } else if (canGoLighter) {
                red = bg.getRed() + factor > 0 ? (int) (bg.getRed() + factor) : bg.getRed();
                green = bg.getGreen() + factor > 0 ? (int) (bg.getGreen() + factor) : bg.getGreen();
                blue = bg.getBlue() + factor > 0 ? (int) (bg.getBlue() + factor) : bg.getBlue();
            }
            if (canGoDarker || canGoLighter) {
                Color deadSpace = UnifiedColorManager.getInstance().getColor(new RGB(red, green, blue));
                return deadSpace;
            }
        }

        return null;
    }

    private void installTextOccurrenceHighlightSupport() {
        ISelectionProvider selectionProvider = getSelectionProvider();
        if (selectionProvider instanceof IPostSelectionProvider) {
            IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider;
            provider.addPostSelectionChangedListener(this);
        } else {
            selectionProvider.addSelectionChangedListener(this);
        }
        this._extendedStylesEnabled = true;
    }

    private void uninstallTextOccurrenceHighlightSupport() {
        ISelectionProvider selectionProvider = getSelectionProvider();
        if (selectionProvider != null) {
            if (selectionProvider instanceof IPostSelectionProvider) {
                IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider;
                provider.removePostSelectionChangedListener(this);
            } else {
                selectionProvider.removeSelectionChangedListener(this);
            }
        }
        removeMarkedOccurrences();
        this._extendedStylesEnabled = false;
    }

    private IRange underLine;

    private Annotation[] fOccurrenceAnnotations;

    /**
     * removing given range from underlined state
     * 
     * @param range
     */
    void removeUnderlinedRange(IRange range) {
        underLine = range;
        StyledText textWidget = getViewer().getTextWidget();
        try {
            textWidget.redrawRange(range.getStartingOffset(), range.getLength(), true);
        } catch (IllegalArgumentException e) {
            // Do nothing if the range being invalid causes an exception
        }
    }

    /**
     * @see com.aptana.ide.editors.unified.IRedrawRangeListener#redrawRange(com.aptana.ide.lexer.IRange)
     */
    public void redrawRange(final IRange range) {
        if (range != null) {
            final StyledText textWidget = getViewer().getTextWidget();
            Display display = textWidget.getDisplay();

            display.asyncExec(new Runnable() {
                public void run() {
                    try {
                        textWidget.redrawRange(range.getStartingOffset(), range.getLength(), true);
                    } catch (IllegalArgumentException e) {
                        // Do nothing if the range being invalid causes an exception
                    }
                }
            });
        }
    }

    /**
     * sets current range to be underlined
     * 
     * @param range
     */
    void setUnderlinedRange(IRange range) {
        if (range != null) {
            underLine = null;
            StyledText textWidget = getViewer().getTextWidget();
            try {
                textWidget.redrawRange(range.getStartingOffset(), range.getLength(), true);
            } catch (IllegalArgumentException e) {
                // Do nothing if the range being invalid causes an exception
            }
        }
    }

    private void linkColorer() {
        if (this._lineStyleListener == null) {
            this._lineStyleListener = new LineStyleListener() {
                /**
                 * @see org.eclipse.swt.custom.LineStyleListener#lineGetStyle(org.eclipse.swt.custom.LineStyleEvent)
                 */
                public void lineGetStyle(LineStyleEvent e) {
                    EditorFileContext fileContext = getFileContext();

                    // wrapper could be non-null, but interior is.
                    if (fileContext == null || fileContext.getFileContext() == null) {
                        return;
                    }

                    IParseState parseState = fileContext.getParseState();

                    if (parseState == null) {
                        return;
                    }

                    LexemeList lexemeList = parseState.getLexemeList();

                    if (lexemeList == null) {
                        IdeLog.logError(UnifiedEditorsPlugin.getDefault(), Messages.UnifiedEditor_LexemeListIsNull);
                        return;
                    }

                    int orgOffset = e.lineOffset;
                    int offset = orgOffset;
                    int extra = 0;
                    int lineLength = e.lineText.length();

                    // need to get actual offset values in the doc,
                    // as widget offsets do not include potentially folded code
                    if (_viewer instanceof ITextViewerExtension5) {
                        ITextViewerExtension5 v5 = (ITextViewerExtension5) _viewer;
                        offset = v5.widgetOffset2ModelOffset(e.lineOffset);
                        extra = offset - e.lineOffset;
                    }

                    int maxLineLength = !isWordWrapEnabled() && lineLength > _maxColorizingColumns
                            ? _maxColorizingColumns
                            : lineLength;
                    Lexeme[] lexemes = null;

                    synchronized (lexemeList) {
                        int startingIndex = lexemeList.getLexemeCeilingIndex(offset);
                        int endingIndex = lexemeList.getLexemeFloorIndex(offset + maxLineLength);

                        if (startingIndex == -1 && endingIndex != -1) {
                            startingIndex = endingIndex;
                        }

                        if (endingIndex == -1 && startingIndex != -1) {
                            endingIndex = startingIndex;
                        }

                        if (startingIndex != -1 && endingIndex != -1) {
                            lexemes = lexemeList.cloneRange(startingIndex, endingIndex);
                        }
                    }

                    if (lexemes != null) {
                        Vector<StyleRange> styles = new Vector<StyleRange>();

                        _colorizer.createStyles(parseState, styles, lexemes, _extendedStylesEnabled);

                        StyleRange[] styleResults = styles.toArray(new StyleRange[] {});

                        // move styles back to actual widget offsets in case of
                        // folding
                        if (extra > 0) {
                            for (int i = 0; i < styleResults.length; i++) {
                                StyleRange range = styleResults[i];
                                range.start -= extra;
                            }
                        }
                        if (underLine != null) {
                            for (int a = 0; a < styleResults.length; a++) {
                                StyleRange styleRange = styleResults[a];
                                if (styleRange.start == underLine.getStartingOffset()
                                        && (styleRange.start + styleRange.length) == underLine.getEndingOffset()) {
                                    styleRange.underline = true;
                                }

                            }
                        } else {
                            for (int a = 0; a < styleResults.length; a++) {
                                StyleRange styleRange = styleResults[a];
                                styleRange.underline = false;

                            }
                        }
                        e.styles = styleResults;
                    }
                    UnifiedViewer rr = (UnifiedViewer) getViewer();
                    TextPresentation textPresentation = rr.getTextPresentation();
                    if (textPresentation != null) {
                        TextPresentation ps = new TextPresentation();
                        if (e.styles != null) {
                            ps.replaceStyleRanges(e.styles);
                        }
                        Iterator<?> nonDefaultStyleRangeIterator = textPresentation
                                .getNonDefaultStyleRangeIterator();
                        while (nonDefaultStyleRangeIterator.hasNext()) {
                            // Make sure that the StyleRange is at a visible location in case we have folded blocks in
                            // the code.
                            StyleRange clone = (StyleRange) ((StyleRange) nonDefaultStyleRangeIterator.next())
                                    .clone();
                            IRegion region = new Region(clone.start, clone.length);
                            region = rr.modelRange2WidgetRange(region);
                            if (region != null) {
                                clone.start = region.getOffset();
                                clone.length = region.getLength();
                                ps.mergeStyleRange(clone);
                            }
                        }
                        Iterator<?> nonDefaultStyleRangeIterator2 = ps.getNonDefaultStyleRangeIterator();
                        ArrayList<StyleRange> rs = new ArrayList<StyleRange>();
                        while (nonDefaultStyleRangeIterator2.hasNext()) {
                            StyleRange next = (StyleRange) nonDefaultStyleRangeIterator2.next();
                            rs.add(next);
                        }
                        e.styles = rs.toArray(new StyleRange[rs.size()]);
                    }

                }
            };
        }

        // Repaint lines if the user is making changes
        if (this._textChangeListener == null) {
            this._textChangeListener = new TextChangeListener() {

                public void textChanging(TextChangingEvent event) {
                }

                public void textChanged(TextChangedEvent event) {
                    StyledText text = getViewer().getTextWidget();
                    redrawFrom(text, text.getLineAtOffset(text.getCaretOffset()));
                }

                // Used with complex text change events (tabbing, replacement,
                // etc.)
                public void textSet(TextChangedEvent event) {
                    StyledText text = getViewer().getTextWidget();
                    redrawFrom(text, 0);
                }

                private void redrawFrom(StyledText text, int lno) {
                    if (lno < 0 || lno >= text.getLineCount()) {
                        return;
                    }

                    int height = text.getClientArea().height;
                    int width = text.getClientArea().width + text.getHorizontalPixel();
                    try {
                        text.redraw(0, 0, width, height, true);
                    } catch (Exception e) {
                        // Catch errors in redraw as they may be intermittent
                        // and the subsequent redraw will complete
                        // successfully
                    }
                }
            };
        }

        getViewer().getTextWidget().addLineStyleListener(this._lineStyleListener);
        getViewer().getTextWidget().getContent().addTextChangeListener(this._textChangeListener);
    }

    private void linkPairMatcher() {
        if (this._pairMatcher == null) {
            this._pairMatcher = new PairMatcher();

            StyledText text = getViewer().getTextWidget();

            text.addPaintListener(this._pairMatcher);
            text.addKeyListener(this._pairMatcher);
            text.addTraverseListener(this._pairMatcher);
            text.addMouseListener(this._pairMatcher);
            text.addSelectionListener(this._pairMatcher);
            ScrollBar sb = text.getVerticalBar();
            if (sb != null) {
                sb.addSelectionListener(this._pairMatcher);
            }
        }
    }

    private void unlinkPairMatcher() {
        if (this._pairMatcher != null) {
            if (getViewer() == null) {
                return;
            }

            StyledText text = getViewer().getTextWidget();

            if (text == null || text.isDisposed() == true) {
                return;
            }

            text.removePaintListener(this._pairMatcher);
            text.removeKeyListener(this._pairMatcher);
            text.removeTraverseListener(this._pairMatcher);
            text.removeMouseListener(this._pairMatcher);
            text.removeSelectionListener(this._pairMatcher);
            ScrollBar sb = text.getVerticalBar();
            if (sb != null) {
                sb.removeSelectionListener(this._pairMatcher);
            }
        }

    }

    /**
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#getPairMatch(int)
     */
    public PairMatch getPairMatch(int offset) {
        IParseState parseState = this.getFileContext().getParseState();
        if (parseState == null) {
            return null;
        }
        LexemeList lexemeList = parseState.getLexemeList();
        Lexeme cursorLexeme = lexemeList.getLexemeFromOffset(offset);

        if (cursorLexeme == null && offset > 0) {
            cursorLexeme = lexemeList.getLexemeFromOffset(offset - 1);
        }
        if (cursorLexeme == null) {
            return null;
        }

        String language = cursorLexeme.getLanguage();
        IPairFinder finder = LanguageRegistry.getPairFinder(language);
        PairMatch match = null;

        if (finder != null && !finder.doNotDisplay()) {
            if (finder instanceof AbstractPairFinder) {
                // NOTE: [KEL] loop count relates to each pair finder
                // implementation and should not be passed in
                // as an argument
                int loopCount = 2;

                match = ((AbstractPairFinder) finder).findPairMatch(offset, parseState, cursorLexeme, loopCount);
            } else {
                match = finder.findPairMatch(offset, parseState);
            }
        }

        if (match != null) {
            match.setColor(finder.getPairFinderColor());
            match.setDisplayOnlyMatch(finder.displayOnlyMatch());
            match.offset = offset;

            if (match instanceof IPairMatchExt) {
                List<PairMatch> subsequentMatches = ((IPairMatchExt) match).getSubsequentMatches();
                for (PairMatch subMatch : subsequentMatches) {
                    subMatch.setColor(finder.getPairFinderColor());
                    subMatch.setDisplayOnlyMatch(finder.displayOnlyMatch());
                    subMatch.offset = offset;
                }
            }
            // NOTE: [KEL] The following is subjective and therefore should
            // become a preference. Turning off for now
            // since this breaks the case where the matched pair on the right
            // touches but the matcher pair to the left
            // does not. In that scenario, we show no matching when we should
            // show the pair to the left

            // // This check is for the case when the pair match will border
            // each other which present a cluttered UI
            // with
            // // the match and the cursor
            // if (match.beginEnd == match.endStart || match.endEnd ==
            // match.beginStart)
            // {
            // match = null;
            // }
        }

        return match;
    }

    /**
     * findBalancingLexeme
     * 
     * @param startIndex
     * @param language
     * @param startType
     * @param endType
     * @param direction
     * @return Lexeme
     */
    protected Lexeme findBalancingLexeme(int startIndex, String language, int startType, int endType,
            int direction) {
        LexemeList lexemeList = this.getFileContext().getParseState().getLexemeList();
        return findBalancingLexeme(lexemeList, startIndex, language, startType, endType, direction);
    }

    /**
     * @param lexemeList
     * @param startIndex
     * @param language
     * @param startType
     * @param endType
     * @param direction
     * @return Lexeme
     */
    public static Lexeme findBalancingLexeme(LexemeList lexemeList, int startIndex, String language, int startType,
            int endType, int direction) {
        Lexeme result = null;

        int count = 0;

        while (0 <= startIndex && startIndex < lexemeList.size()) {
            result = lexemeList.get(startIndex);

            if (result.getLanguage().equals(language)) {
                if (result.typeIndex == endType) {
                    count--;

                    if (count == 0) {
                        break;
                    }
                } else if (result.typeIndex == startType) {
                    count++;
                }
            }

            startIndex += direction;
        }

        if (count != 0) {
            result = null;
        }

        return result;
    }

    /**
     * @author Paul Colton
     */
    class PairMatcher implements SelectionListener, MouseListener, KeyListener, PaintListener, TraverseListener {

        private PairMatch _currentPair;

        /**
         * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
         */
        public void mouseDoubleClick(MouseEvent e) {
        }

        /**
         * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
         */
        public void mouseDown(MouseEvent e) {
            stateChanged();
        }

        /**
         * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
         */
        public void mouseUp(MouseEvent e) {
        }

        /**
         * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
         */
        public void keyPressed(KeyEvent e) {
            stateChanged();
        }

        /**
         * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
         */
        public void keyReleased(KeyEvent e) {
        }

        /**
         * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
         */
        public void paintControl(PaintEvent e) {
            stateChanged();
            pairsDraw(e.gc, this._currentPair);
        }

        /**
         * @see org.eclipse.swt.events.TraverseListener#keyTraversed(org.eclipse.swt.events.TraverseEvent)
         */
        public void keyTraversed(TraverseEvent e) {
            stateChanged();
        }

        /**
         * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
         */
        public void widgetSelected(SelectionEvent e) {
            stateChanged();
        }

        /**
         * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
         */
        public void widgetDefaultSelected(SelectionEvent e) {
        }

        void stateChanged() {
            StyledText text = getViewer().getTextWidget();
            if (text != null) {
                int offset = text.getCaretOffset();
                if (getViewer() instanceof ITextViewerExtension5) {
                    int resolved = ((ITextViewerExtension5) getViewer()).widgetOffset2ModelOffset(offset);
                    if (resolved > -1) {
                        offset = resolved;
                    }
                }
                PairMatch newmatch = getPairMatch(offset);

                if ((newmatch == null && this._currentPair != null)
                        || (newmatch != null && !newmatch.equals(this._currentPair))) {
                    pairsDraw(null, this._currentPair);
                    pairsDraw(null, newmatch);
                }

                this._currentPair = newmatch;
            }
        }

        void pairsDraw(GC gc, PairMatch pm) {
            if (pm == null) {
                return;
            }
            pairsDrawSingle(gc, pm);
            if (pm instanceof IPairMatchExt) {
                List<PairMatch> subsequentMatches = ((IPairMatchExt) pm).getSubsequentMatches();
                if (subsequentMatches != null && subsequentMatches.size() > 0) {
                    for (PairMatch subsequentMatch : subsequentMatches) {
                        pairsDrawSingle(gc, subsequentMatch);
                    }
                }
            }

        }

        void pairsDrawSingle(GC gc, PairMatch pm) {
            if (pm == null) {
                return;
            }
            StyledText text = getViewer().getTextWidget();
            if (text != null) {
                int cursor = text.getCaretOffset();
                if (pm.displayOnlyMatch() && gc != null) {
                    if (cursor >= pm.beginStart && cursor <= pm.beginEnd) {
                        pairDraw(gc, pm.endStart, pm.endEnd, pm.getColor());
                    } else if (cursor >= pm.endStart && cursor <= pm.endEnd) {
                        pairDraw(gc, pm.beginStart, pm.beginEnd, pm.getColor());
                    }
                } else {
                    pairDraw(gc, pm.beginStart, pm.beginEnd, pm.getColor());
                    pairDraw(gc, pm.endStart, pm.endEnd, pm.getColor());
                }
            }

        }

        void pairDraw(GC gc, int start, int end, Color color) {
            StyledText text = getViewer().getTextWidget();
            if (getViewer() instanceof ITextViewerExtension5) {
                ITextViewerExtension5 v5 = (ITextViewerExtension5) getViewer();
                start = v5.modelOffset2WidgetOffset(start);
                end = v5.modelOffset2WidgetOffset(end);
            }
            if (start < 0 || end < 0 || start > text.getCharCount() || end > text.getCharCount()) {
                return;
            }

            if (gc != null) {
                try {
                    Point left = text.getLocationAtOffset(start);
                    Point right = text.getLocationAtOffset(end);

                    gc.setForeground(color);
                    gc.setLineWidth(1);
                    gc.drawRectangle(left.x + 1, left.y + 1, right.x - left.x - 2,
                            gc.getFontMetrics().getHeight() - 2);
                } catch (Exception e) {
                    IdeLog.logError(UnifiedEditorsPlugin.getDefault(),
                            StringUtils.format(Messages.UnifiedEditor_PairDraw, e.getMessage()));
                }
            } else {
                text.redrawRange(start, end - start, true);
            }
        }

    }

    /**
     * getDefaultFileExtension
     * 
     * @return String
     */
    public abstract String getDefaultFileExtension();

    /**
     * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent e) {
        // if (textColorer == null)
        // return;
        //        
        // if (e == null ||
        // e.getProperty().equals(ColorizerPreferencePage.USE_BACK) ||
        // e.getProperty().equals(ColorizerPreferencePage.HRD_SET))
        // {
        // textColorer.setRegionMapper(
        // prefStore.getString(ColorizerPreferencePage.HRD_SET),
        // prefStore.getBoolean(ColorizerPreferencePage.USE_BACK));
        // }
        // if (e == null ||
        // e.getProperty().equals(ColorizerPreferencePage.FULL_BACK)){
        // textColorer.setFullBackground(prefStore.getBoolean(ColorizerPreferencePage.FULL_BACK));
        // }
        // if (e == null ||
        // e.getProperty().equals(ColorizerPreferencePage.HORZ_CROSS) ||
        // e.getProperty().equals(ColorizerPreferencePage.VERT_CROSS))
        // {
        // textColorer.setCross(
        // prefStore.getBoolean(ColorizerPreferencePage.HORZ_CROSS),
        // prefStore.getBoolean(ColorizerPreferencePage.VERT_CROSS));
        // }
        //
        // if (e == null ||
        // e.getProperty().equals(ColorizerPreferencePage.PAIRS_MATCH)) {
        // String pairs =
        // prefStore.getString(ColorizerPreferencePage.PAIRS_MATCH);
        // int pmode = TextColorer.HLS_XOR;
        // if (pairs.equals("PAIRS_OUTLINE"))
        // pmode = TextColorer.HLS_OUTLINE;
        // if (pairs.equals("PAIRS_OUTLINE2"))
        // pmode = TextColorer.HLS_OUTLINE2;
        // textColorer.setPairsPainter(!pairs.equals("PAIRS_NO"), pmode);
        // }

        // if (e == null ||
        // e.getProperty().equals(ColorizerPreferencePage.TEXT_FONT)){
        // Font textFont =
        // JFaceResources.getFont(ColorizerPreferencePage.TEXT_FONT);
        // getViewer().getTextWidget().setFont(textFont);
        // }
    }

    /**
     * Are we currently the active editor?
     * 
     * @return boolean
     */
    public boolean isActiveEditor() {
        IEditorPart part = getEditorSite().getPage().getActiveEditor();

        if (part == null) {
            return false;
        } else if (part instanceof IUnifiedEditor) {
            IUnifiedEditor editor = (IUnifiedEditor) part;

            return editor.getEditor() == this;
        } else {
            return part == this;
        }
    }

    /**
     * onKeyPressed
     * 
     * @param event
     */
    private void onKeyPressed(VerifyEvent event) {
        char c = event.character;

        StyledText styledText = (StyledText) event.widget;
        int keyCode = event.keyCode;

        // when undoing a template, the caret is not reset at this point
        ITypedRegion reg = this._fileContextWrapper.getPartitionAtOffset(styledText.getCaretOffset());

        if (reg == null) {
            return;
        }

        final String contentType = reg.getType();
        IUnifiedEditorContributor contributor = this._baseContributor.findChildContributor(contentType);

        if (contributor != null && contributor.isAutoActivateContentAssist()
                && contributor.isValidIdentifier(c, keyCode)
                && isLeftCharacterWhitespace(contributor, styledText, c, keyCode)) {
            getViewer().getTextWidget().setData(IContentAssistConstants.ASSIST_FORCE_ACTIVATION, true);
            showContentAssist();
        }
    }

    /**
     * isLeftCharacterWhitespace
     * 
     * @param styledText
     * @param c
     * @param keyCode
     * @return boolean
     */
    private boolean isLeftCharacterWhitespace(IUnifiedEditorContributor contributor, StyledText styledText, char c,
            int keyCode) {
        int offset = styledText.getCaretOffset();

        // Are we at beginning of file?
        if (offset == 0) {
            return true;
        }

        String line = styledText.getText(offset - 1, offset - 1);

        if (line.length() > 0) {
            return contributor.isValidActivationCharacter(line.charAt(0), keyCode);
        } else {
            return false;
        }
    }

    /**
     * showContentAssist
     */
    private void showContentAssist() {
        final IWorkbench workbench = PlatformUI.getWorkbench();
        final Display display = workbench.getDisplay();

        display.asyncExec(new Runnable() {
            public void run() {
                try {
                    final SourceViewer sv = (SourceViewer) getSourceViewer();

                    if (sv == null) {
                        return; // guard against already closed editor
                    }

                    // In case you switched away
                    Control c = display.getFocusControl();
                    if (c == null || c != sv.getTextWidget()) {
                        return;
                    }

                    // Check if source viewer is able to perform operation
                    if (sv.canDoOperation(SourceViewer.CONTENTASSIST_PROPOSALS)) {
                        // Perform operation
                        sv.doOperation(SourceViewer.CONTENTASSIST_PROPOSALS);
                    }
                } catch (Exception e) {
                    IdeLog.logError(UnifiedEditorsPlugin.getDefault(), Messages.UnifiedEditor_ErrorContentProposals,
                            e);
                }
            }
        });
    }

    /**
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#showWhitespace(boolean)
     */
    public void showWhitespace(boolean state) {
        ISourceViewer fSourceViewer = this.getSourceViewer();

        if (state) {
            if (this._whitespacePainter == null) {
                if (fSourceViewer instanceof ITextViewerExtension2) {
                    this._whitespacePainter = new WhitespaceCharacterPainter(fSourceViewer);
                    ITextViewerExtension2 extension = (ITextViewerExtension2) fSourceViewer;
                    extension.addPainter(this._whitespacePainter);
                }
            }
        } else {
            if (this._whitespacePainter != null) {

                if (fSourceViewer instanceof ITextViewerExtension2) {
                    ITextViewerExtension2 extension = (ITextViewerExtension2) fSourceViewer;
                    extension.removePainter(this._whitespacePainter);
                    this._whitespacePainter.deactivate(true);
                    this._whitespacePainter.dispose();
                    this._whitespacePainter = null;
                    // this.getFileContext().removeLongDelayedFileListener(fWhitespacePainter);
                }
            }
        }
    }

    /**
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#showPianoKeys(boolean)
     */
    public void showPianoKeys(boolean state) {
        if (this._painter != null) {
            StyledText text = getViewer().getTextWidget();
            this._painter.paintLines(0, text.getLineCount());
        }
    }

    /**
     * Updates the folding structure
     * 
     * @param annotations
     */
    public void updateFoldingStructure(Map annotations) {
        List<Annotation> deletions = new ArrayList<Annotation>();
        Collection additions = annotations.values();
        ProjectionAnnotationModel currentModel = getProjectionAnnotationModel();
        if (currentModel == null) {
            return;
        }
        for (Iterator iter = currentModel.getAnnotationIterator(); iter.hasNext();) {
            Object annotation = iter.next();
            if (annotation instanceof ProjectionAnnotation) {
                Position position = currentModel.getPosition((Annotation) annotation);
                if (additions.contains(position)) {
                    additions.remove(position);
                } else {
                    deletions.add((Annotation) annotation);
                }
            }
        }
        if (annotations.size() != 0 || deletions.size() != 0) {
            currentModel.modifyAnnotations(deletions.toArray(new Annotation[deletions.size()]), annotations, null);
        }
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#createSourceViewer(org.eclipse.swt.widgets.Composite,
     *      org.eclipse.jface.text.source.IVerticalRuler, int)
     */
    protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
        // **** NOTE ****
        // we are not using Projection Viewer in order to get shift left working
        // (to shift until no
        // whitespace)
        // if this is problematic or 3.2 exposes that in a more intelligent way
        // (or fixes that
        // issue)
        // we can get rid of UnifiedViewer, and revert this one line of code
        // here (move back to
        // using
        // projectionViewer).

        // ISourceViewer viewer = new ProjectionViewer(parent, ruler,
        // getOverviewRuler(),
        // isOverviewRulerVisible(),
        // styles);
        this._viewer = new UnifiedViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles);
        // ensure decoration support has been created and configured.
        getSourceViewerDecorationSupport(this._viewer);

        this._projectionSupport = new UnifiedProjectionSupport(this._viewer, getAnnotationAccess(),
                getSharedColors());
        this._projectionSupport.install();

        SourceViewer sv = this._viewer;
        Display d = this.getSite().getShell().getDisplay();
        this._keyUpListener = new Listener() {
            public void handleEvent(Event e) {
                UnifiedEditor.ctrlDown = false;
            }
        };
        d.addFilter(SWT.KeyUp, this._keyUpListener);

        this._verifyKeyListener = new VerifyKeyListener() {
            public void verifyKey(VerifyEvent event) {
                _hasKeyBeenPressed = true;

                onKeyPressed(event);

                // Check for Ctrl
                // note: state mask doesn't work for some reason
                if (event.keyCode == SWT.CTRL) {
                    UnifiedEditor.ctrlDown = true;
                } else {
                    UnifiedEditor.ctrlDown = false;
                }
            }
        };

        sv.prependVerifyKeyListener(this._verifyKeyListener);

        SourceViewerConfiguration svc = this.getSourceViewerConfiguration();
        if (svc instanceof UnifiedConfiguration) {
            UnifiedConfiguration uc = (UnifiedConfiguration) svc;
            this._bracketInserterManager = uc.getBracketInserterManager(sv);
            sv.prependVerifyKeyListener(this._bracketInserterManager);
        }

        return this._viewer;
    }

    // private IPreferenceStore fPreferenceStore;

    /**
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#doSetInput(org.eclipse.ui.IEditorInput)
     */
    protected void doSetInput(IEditorInput input) throws CoreException {
        // After Save As, we are getting a new IEditorInput each time
        // implicitly enters it into the hash of the document provider, using
        // input as the key
        super.doSetInput(input);

        try {
            // Get the document provider (which is a singleton)
            IDocumentProvider dp = getDocumentProvider();
            if (dp == null) {
                throw new Exception(Messages.UnifiedEditor_DocumentProviderNull);
            }

            // Get document from input
            IDocument document = dp.getDocument(input);

            // TODO: Actuate change
            // UnifiedFileInfo cuInfo = (UnifiedFileInfo)
            // dp.getFileInfoPublic(input);
            //
            // if (cuInfo == null)
            // {
            // throw new Exception(Messages.UnifiedEditor_CuInfoIsNull);
            // }

            DocumentSourceProvider provider = new DocumentSourceProvider(document, input);
            if (provider == null) {
                throw new Exception(Messages.UnifiedEditor_ProviderIsNull);
            }

            // TODO: Actuate change
            // boolean isNewInput = (cuInfo.sourceProvider == null ||
            // cuInfo.sourceProvider.equals(provider) == false);
            boolean isNewInput = isNewInput(input);

            // update various stuffs and more :-)
            // TODO: Actuate changes
            // updateFileInfo(input, provider, cuInfo, document, isNewInput);
            updateFileInfo(input, provider, document);

            // Commented out for now, as this code now lives in
            // UnifiedDocumentProvider
            // updateAnnotationModel(cuInfo);

            // TODO: Actuate changes
            // updatePartitioner(provider, cuInfo, document, isNewInput);
            updatePartitioner(provider, document, isNewInput);

            // Set the actual file context onto the wrapper
            this._fileContextWrapper.setFileContext(FileContextManager.get(provider.getSourceURI()));

            // Set up error listeners based on the the input file type
            setErrorListeners(input);

            fireNewFileServiceEvent();

            // Refresh colorization
            if (this.getViewer() != null && this.getViewer().getTextWidget() != null) {
                this.getViewer().getTextWidget().redraw();
                this.getViewer().getTextWidget().update();
            }
        } catch (Exception e) {
            IdeLog.logError(UnifiedEditorsPlugin.getDefault(), "Unable to set input in UnifiedEditor: " //$NON-NLS-1$
                    + e.getMessage(), e);
            IStatus status = new Status(IStatus.ERROR, UnifiedEditorsPlugin.ID, IStatus.OK,
                    "Unable to set input in Unified Editor", e);//$NON-NLS-1$
            throw new CoreException(status);
        }
    }

    /**
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#addFileServiceChangeListener(com.aptana.ide.editors.unified.IFileServiceChangeListener)
     */
    public void addFileServiceChangeListener(IFileServiceChangeListener listener) {
        fileServiceChangeListeners.add(listener);
    }

    /**
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#removeFileServiceChangeListener(com.aptana.ide.editors.unified.IFileServiceChangeListener)
     */
    public void removeFileServiceChangeListener(IFileServiceChangeListener listener) {
        fileServiceChangeListeners.remove(listener);
    }

    /**
     * Updates the file service listeners with the new service
     */
    private void fireNewFileServiceEvent() {
        if (this._fileContextWrapper != null && this._fileContextWrapper.getFileContext() != null) {
            final IFileService newService = this._fileContextWrapper.getFileContext();
            // Notify file service listeners
            Object[] listeners = fileServiceChangeListeners.getListeners();
            for (int i = 0; i < listeners.length; ++i) {
                final IFileServiceChangeListener l = (IFileServiceChangeListener) listeners[i];
                SafeRunnable.run(new SafeRunnable() {
                    public void run() {
                        l.fileServiceChanged(newService);
                    }
                });
            }
        }
    }

    private void setErrorListeners(IEditorInput input) {
        // Project file
        if (input instanceof IFileEditorInput) {
            IFileEditorInput fileInput = (IFileEditorInput) input;
            IFile file = fileInput.getFile();
            this._errorListener = new ProjectFileErrorListener(file);
        }
        // External file or Untitled file (not saved)
        else if (input instanceof IPathEditorInput || input instanceof IURIEditorInput
                || input instanceof NonExistingFileEditorInput) {
            IDocument doc = this.getDocumentProvider().getDocument(input);
            IAnnotationModel ann = this.getDocumentProvider().getAnnotationModel(input);
            this._errorListener = new ExternalFileErrorListener(ann, doc);
        }

        if (this._errorListener != null) {
            getFileContext().addErrorListener(this._errorListener);
        }
    }

    /**
     * updateFileInfo
     * 
     * @param input
     * @param provider
     * @param document
     */
    protected void updateFileInfo(IEditorInput input, DocumentSourceProvider provider, IDocument document) {
        if (isNewInput(input)) {
            // TODO: Actuate change
            // save reference to provider
            // cuInfo.sourceProvider = provider;

            IFileServiceFactory fileServiceFactory = this.getFileServiceFactory();

            if (fileServiceFactory != null) {
                FileService context = fileServiceFactory.createFileService(provider, false);

                FileContextManager.add(provider.getSourceURI(), context);

                context.setRedrawRangeListener(this);
            }

            FileContextManager.connectSourceProvider(provider.getSourceURI(), provider);
        }
    }

    // private void updateAnnotationModel(UnifiedFileInfo cuInfo)
    // {
    // // Provide a simple annotation model if none has been provided
    // // fixes bug 156 - line numbers are not displayed for external files
    // // PC: not needed? cuInfo.fModel = new AnnotationModel();
    //
    // if (cuInfo.fTextFileBuffer.getAnnotationModel() == null)
    // {
    // // when editing external files, there will not be an annotation model
    // attached, so we
    // // attach a simple one.
    // // This is necessary so that the source viewer's annotation bar (which
    // displays line
    // // numbers, etc) will be displayed
    // IUniformResource uniformResource = null;
    //
    // if (cuInfo.fElement instanceof IAdaptable)
    // {
    // uniformResource = (IUniformResource) ((IAdaptable)
    // cuInfo.fElement).getAdapter(IUniformResource.class);
    // }
    //
    // if (uniformResource != null)
    // {
    // cuInfo.fModel = new
    // UniformResourceMarkerAnnotationModel(uniformResource);
    // }
    // else
    // {
    // cuInfo.fModel = new AnnotationModel();
    // }
    // }
    // else
    // {
    // cuInfo.fModel = cuInfo.fTextFileBuffer.getAnnotationModel();
    // }
    // }

    private void updatePartitioner(DocumentSourceProvider provider, IDocument document, boolean isNewInput) {
        if (isNewInput) {
            UnifiedDocumentPartitioner partitioner = new UnifiedDocumentPartitioner(provider.getSourceURI());

            // NOTE: The content types (and other stuff) will change when a user
            // does a Save As to another file type (i.e. JS to HTML)
            partitioner.setLegalContentTypes(this._baseContributor.getContentTypes());
            partitioner.setPartitions();

            if (document instanceof IDocumentExtension3) {
                // this should always be the once called
                ((IDocumentExtension3) document).setDocumentPartitioner(UnifiedConfiguration.UNIFIED_PARTITIONING,
                        partitioner);
            } else {
                // document.setDocumentPartitioner(partitioner);
                throw new IllegalStateException(Messages.UnifiedEditor_DocumentMustBe);
            }
        }
    }

    /**
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#getFileContext()
     */
    public EditorFileContext getFileContext() {
        return this._fileContextWrapper;
    }

    /**
     * createLocalContributor
     * 
     * @return IUnifiedEditorContributor
     */
    protected abstract IUnifiedEditorContributor createLocalContributor();

    /**
     * @see org.eclipse.ui.IWorkbenchPart#dispose()
     */
    public void dispose() {
        if (this._isDisposing) {
            return;
        }

        this._isDisposing = true;

        if (this._lineStyleListener != null) {
            if (getViewer().getTextWidget() != null && getViewer().getTextWidget().isDisposed() == false) {
                getViewer().getTextWidget().removeLineStyleListener(this._lineStyleListener);
            }
        }

        if (this._textChangeListener != null) {
            if (getViewer().getTextWidget() != null && getViewer().getTextWidget().isDisposed() == false) {
                getViewer().getTextWidget().getContent().removeTextChangeListener(this._textChangeListener);
            }
        }

        unlinkPairMatcher();

        if (this._errorListener != null) {
            getFileContext().removeErrorListener(this._errorListener);
        }

        if (this._partListener != null) {
            this.getEditorSite().getPage().removePartListener(this._partListener);
            this._partListener = null;
        }

        if (this._keyUpListener != null) {
            Display d = this.getSite().getShell().getDisplay();
            d.removeFilter(SWT.KeyUp, this._keyUpListener);
            this._keyUpListener = null;
        }
        if (_verifyKeyListener != null) {
            ((SourceViewer) this._viewer).removeVerifyKeyListener(this._verifyKeyListener);
            this._verifyKeyListener = null;
        }
        if (this._bracketInserterManager != null) {
            ((SourceViewer) this._viewer).removeVerifyKeyListener(this._bracketInserterManager);
            this._bracketInserterManager = null;
        }

        SourceViewerConfiguration svc = this.getConfiguration();
        if (svc instanceof UnifiedConfiguration) {
            ((UnifiedConfiguration) svc).dispose();
        }

        if (outlinePage != null) {
            outlinePage.dispose();
            outlinePage = null;
        }

        showWhitespace(false);
        if (this._whitespacePainter != null) {
            this._whitespacePainter.dispose();
        }

        this._saveListeners.clear();
        this._saveAsListeners.clear();

        this._lineStyleListener = null;
        outlinePage = null;

        if (this._baseContributor != null) {
            this._baseContributor.setParentConfiguration(null);
            this._baseContributor = null;
        }

        this._errorListener = null;
        this._whitespacePainter = null;
        this._fileExplorerView = null;
        this._prefStore = null;

        this._contextAwareness = null;

        this.disposeDocumentProvider();

        if (this._fileContextWrapper != null) {
            this._fileContextWrapper.deactivateForEditing();
            // The following was commented out since:
            // it disconnects regardless if another file is using the file
            // context (two editors around same document)
            // Looking at this.disposeDocumentProvider() shows the proper checks
            // before disconnecting and so the
            // following line was redundant as well since the document provider
            // handles it correctly and so calling it
            // here will just ruin any other open editors using the file context
            // being disposed
            // fileContextWrapper.disconnectSourceProvider(null);
        }

        if (this._extendedStylesEnabled) {
            uninstallTextOccurrenceHighlightSupport();
        }

        if (this._caretImage != null) {
            this._caretImage.dispose();
            this._caretImage = null;
        }

        super.dispose();
    }

    /**
     * getFileServiceFactory
     * 
     * @return IFileServiceFactory
     */
    public abstract IFileServiceFactory getFileServiceFactory();

    /**
     * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {
        // Context is just plugin ID + name of class. Matches contexts.xml file
        if (adapter == IContextProvider.class) {
            return LexemeUIHelp.getHelpContextProvider(this, getFileContext());
        }

        // Return our adapter for the content outline page
        if (IContentOutlinePage.class.equals(adapter)) {
            return getOutlinePage();
        }

        return super.getAdapter(adapter);
    }

    /**
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#getViewer()
     */
    public ISourceViewer getViewer() {
        return this.getSourceViewer();
    }

    /**
     * // exposing this as public for scripting access
     * 
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#getConfiguration()
     */
    public SourceViewerConfiguration getConfiguration() {
        return this.getSourceViewerConfiguration();
    }

    /**
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#getContextAwareness()
     */
    public IContextAwareness getContextAwareness() {
        // For editors that don't have/need a ContextAwareness service
        if (this._contextAwareness == null) {
            this._contextAwareness = new IContextAwareness() {
                public void update(IFileService fileService) {
                }

                public ContextItem getFileContext() {
                    return new ContextItem();
                }
            };
        }
        return this._contextAwareness;
    }

    /**
     * Returns the content outline page adapter
     * 
     * @return Returns the content outline page adapter.
     */
    public UnifiedOutlinePage getOutlinePage() {
        if (this.outlinePage == null
                || (this.outlinePage.getControl() != null && this.outlinePage.getControl().isDisposed())) {
            this.outlinePage = new UnifiedOutlinePage(this);
        }

        return outlinePage;
    }

    /**
     * {@inheritDoc}
     */
    public UnifiedQuickOutlinePage createQuickOutlinePage() {
        return new UnifiedQuickOutlinePage(this);
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#handlePreferenceStoreChanged(org.eclipse.jface.util.PropertyChangeEvent)
     */
    protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
        try {
            ISourceViewer sourceViewer = getSourceViewer();

            if (sourceViewer == null) {
                return;
            }

            String property = event.getProperty();

            if (IPreferenceConstants.COLORIZER_MAXCOLUMNS.equals(property)) {
                this._maxColorizingColumns = getPreferenceStore().getInt(IPreferenceConstants.COLORIZER_MAXCOLUMNS);
            } else if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)
                    || IPreferenceConstants.INSERT_SPACES_FOR_TABS.equals(property)) {
                IPreferenceStore store = getPreferenceStore();

                if (store == null) {
                    throw new Exception(Messages.UnifiedEditor_UnableToRetrievePreferenceStore);
                }

                int prefs = store.getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH);
                SourceViewerConfiguration sv = getSourceViewerConfiguration();

                boolean tabsOrSpaces = store.getBoolean(IPreferenceConstants.INSERT_SPACES_FOR_TABS);

                if (sv != null && sv instanceof UnifiedConfiguration) {
                    UnifiedConfiguration uc = (UnifiedConfiguration) sv;
                    uc.setTabWidth(prefs, tabsOrSpaces, sourceViewer);
                } else {
                    IdeLog.logInfo(UnifiedEditorsPlugin.getDefault(), Messages.UnifiedEditor_ErrorUpdateTabWidth);
                }
            } else if (IPreferenceConstants.COLORIZER_TEXT_HIGHLIGHT_ENABLED.equals(property)) {
                boolean textHighlightEnabled = getPreferenceStore()
                        .getBoolean(IPreferenceConstants.COLORIZER_TEXT_HIGHLIGHT_ENABLED);

                if (textHighlightEnabled) {
                    installTextOccurrenceHighlightSupport();
                } else {
                    uninstallTextOccurrenceHighlightSupport();
                    // remove highlights
                }

            } else if (property != null && property.startsWith(IPreferenceConstants.EDITOR_FOLDING_ENABLED)) {
                String language = property.substring(IPreferenceConstants.EDITOR_FOLDING_ENABLED.length() + 1,
                        property.length());
                if (this.getViewer() != null && this.getViewer() instanceof ProjectionViewer) {
                    ProjectionViewer viewer = (ProjectionViewer) this.getViewer();
                    if (UnifiedEditorsPlugin.getDefault().getPreferenceStore().getBoolean(property)) {
                        if (this.getConfiguration() instanceof UnifiedConfiguration) {
                            UnifiedReconcilingStrategy reconciler = ((UnifiedConfiguration) this.getConfiguration())
                                    .getStrategy();
                            if (reconciler != null) {
                                reconciler.initialReconcile();
                            }
                        }
                    } else if (viewer.getProjectionAnnotationModel() != null) {
                        List<Annotation> mods = new ArrayList<Annotation>();
                        Iterator annotationIterator = viewer.getProjectionAnnotationModel().getAnnotationIterator();
                        if (annotationIterator != null) {
                            while (annotationIterator.hasNext()) {
                                Annotation annotation = (Annotation) annotationIterator.next();
                                if (annotation instanceof LanguageProjectAnnotation) {
                                    LanguageProjectAnnotation lpa = (LanguageProjectAnnotation) annotation;
                                    if (language.equals(lpa.getLanguage())) {
                                        lpa.markDeleted(true);
                                        mods.add(lpa);
                                    }
                                }
                            }
                            viewer.getProjectionAnnotationModel()
                                    .modifyAnnotations(mods.toArray(new Annotation[mods.size()]), null, null);
                        }
                    }
                }
            }
            // This updates the folding icons when the colorization changes
            else if (property != null && property.startsWith("Colorization")) //$NON-NLS-1$
            {
                syncFoldingPreferenceStore();
                if (this.getConfiguration() instanceof UnifiedConfiguration) {
                    UnifiedReconcilingStrategy reconciler = ((UnifiedConfiguration) this.getConfiguration())
                            .getStrategy();
                    ProjectionAnnotationModel currentModel = getProjectionAnnotationModel();
                    if (reconciler != null && currentModel != null) {
                        currentModel.removeAllAnnotations();
                        reconciler.initialReconcile();
                    }
                }

            }
            // boolean wordWrap =
            // getPreferenceStore().getBoolean(IPreferenceConstants.ENABLE_WORD_WRAP);
            // this.getViewer().getTextWidget().setWordWrap(wordWrap);

            // this can be null (? either when closing or when sourceViewer is
            // UndefinedViewer)
            StyledText viewer = sourceViewer.getTextWidget();
            if (viewer != null) {
                sourceViewer.getTextWidget().redraw();
            }
        } catch (Exception ex) {
            IdeLog.logError(UnifiedEditorsPlugin.getDefault(), Messages.UnifiedEditor_ErrorHandlingPreferenceChange,
                    ex);
        } finally {
            super.handlePreferenceStoreChanged(event);

            // Editor options must be changed after super is called so that the
            // Eclipse editor prefs don't affect the
            // Aptana color prefs if the Eclipse prefs are overriden by the
            // language editor

            // Refresh editor options in case changed
            // Always run in UI thread!
            Display display = Display.getCurrent();
            if (display == null)
                display = Display.getDefault();
            display.asyncExec(new Runnable() {

                public void run() {
                    setEditorOptions();
                }
            });
        }
    }

    /**
     * Gets the projection annotation model ( folding annotations stored here)
     * 
     * @return - model or null if the viewer or model is null
     */
    private ProjectionAnnotationModel getProjectionAnnotationModel() {
        ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
        if (viewer != null) {
            return viewer.getProjectionAnnotationModel();
        }
        return null;
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#handleCursorPositionChanged()
     */
    protected void handleCursorPositionChanged() {
        // update current lexeme and index based on new cursor position
        StyledText styledText = getSourceViewer().getTextWidget();
        int offset = styledText.getCaretOffset();

        try {
            if (getSourceViewer() instanceof ITextViewerExtension5) {
                offset = ((ITextViewerExtension5) getSourceViewer()).widgetOffset2ModelOffset(offset);
            }
        } catch (Exception e) {
            // If can't convert offset then use caret offset uncoverted
        } catch (Error e) {
            // If can't convert offset then use caret offset uncoverted
        }

        IFileService fs = this.getFileContext();
        if (fs != null) {
            // RD: must always account for whitespace in lexemelist
            LexemeList list = fs.getLexemeList();
            if (list == null) {
                return;
            }

            int index = list.getLexemeFloorIndex(offset);
            if (index == -1) {
                index = list.getLexemeCeilingIndex(offset);
                if (index == -1) {
                    return;
                }
            }

            Lexeme l = list.get(index);
            if (l != null) {
                IFileLanguageService ls = fs.getLanguageService(l.getLanguage());
                if (ls != null && ls.getOffsetMapper() != null) {
                    ls.getOffsetMapper().calculateCurrentLexeme(offset);
                }
            }
        }

        super.handleCursorPositionChanged();
    }

    /**
     * Retrieves the current IEditorPart
     * 
     * @return The current EditorPart
     */
    public IEditorPart getEditor() {
        return this;
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#initializeKeyBindingScopes()
     */
    protected void initializeKeyBindingScopes() {
        setKeyBindingScopes(new String[] { "com.aptana.ide.editors.UnifiedEditorsScope" }); //$NON-NLS-1$
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#configureSourceViewerDecorationSupport(org.eclipse.ui.texteditor.SourceViewerDecorationSupport)
     */
    protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
        // support.setCharacterPairMatcher(bracketMatcher);
        // support.setMatchingCharacterPainterPreferenceKeys("matchBrackets",
        // "matchBracketsColor");
        super.configureSourceViewerDecorationSupport(support);
    }

    // public void addBracketMatchListener(IUnifiedBracketMatcherListener obj)
    // {
    // bracketMatcher.addBracketMatchListener(obj);
    // }
    //
    // public void removeBracketMatchListener(IUnifiedBracketMatcherListener
    // obj)
    // {
    // bracketMatcher.removeBracketMatchListener(obj);
    // }

    /**
     * 
     */
    protected void onSaveComplete() {
        ISaveEvent[] listeners = null;

        synchronized (this._saveListeners) {
            listeners = this._saveListeners.toArray(new ISaveEvent[this._saveListeners.size()]);
        }

        for (int i = 0; i < listeners.length; i++) {
            ISaveEvent element = listeners[i];
            element.onSave(this.getEditor());
        }
    }

    /**
     * @see com.aptana.ide.editors.untitled.BaseTextEditor#onSaveAsComplete(java.io.File, java.io.File)
     */
    protected void onSaveAsComplete(File oldFile, File newFile) {
        ISaveAsEvent[] listeners = null;

        synchronized (this._saveAsListeners) {
            listeners = this._saveAsListeners.toArray(new ISaveAsEvent[this._saveAsListeners.size()]);
        }

        for (int i = 0; i < listeners.length; i++) {
            ISaveAsEvent element = listeners[i];
            element.onSaveAs(this.getEditor(), oldFile, newFile);
        }

        updateFileExplorer();
    }

    private void updateFileExplorer() {
        if (this._fileExplorerView == null) {
            this._fileExplorerView = (CommonNavigator) CoreUIUtils
                    .getViewInternal("com.aptana.ide.ui.io.fileExplorerView", null); //$NON-NLS-1$
        }

        if (this._fileExplorerView == null) {
            return;
        }

        Display.getDefault().asyncExec(new Runnable() {

            public void run() {
                _fileExplorerView.getCommonViewer().refresh();
            }

        });
    }

    /**
     * @param listener
     */
    public void addSaveListener(ISaveEvent listener) {
        this._saveListeners.add(listener);
    }

    /**
     * @param listener
     */
    public void removeSaveListener(ISaveEvent listener) {
        if (this._saveListeners.contains(listener)) {
            this._saveListeners.remove(listener);
        }
    }

    /**
     * @param listener
     */
    public void addSaveAsListener(ISaveAsEvent listener) {
        this._saveAsListeners.add(listener);
    }

    /**
     * @param listener
     */
    public void removeSaveAsListener(ISaveAsEvent listener) {
        if (this._saveAsListeners.contains(listener)) {
            this._saveAsListeners.remove(listener);
        }
    }

    void showPairError() {
        MessageDialog.openInformation(null, Messages.UnifiedEditor_MatchingPairError,
                Messages.UnifiedEditor_MatchingPairErrorMessage);
    }

    /**
     * isHasKeyBeenPressed
     * 
     * @return boolean
     */
    public boolean isHasKeyBeenPressed() {
        return this._hasKeyBeenPressed;
    }

    /**
     * setHasKeyBeenPressed
     * 
     * @param hasKeyBeenPressed
     */
    public void setHasKeyBeenPressed(boolean hasKeyBeenPressed) {
        this._hasKeyBeenPressed = hasKeyBeenPressed;
    }

    /**
     * @see org.eclipse.ui.editors.text.TextEditor#createActions()
     */
    protected void createActions() {
        super.createActions();

        // Add bookmark action
        final BookmarkRulerAction bra = new BookmarkRulerAction();
        Action bookmarkAction = new Action() {

            public void run() {
                bra.run(this);
            }

        };
        bra.setActiveEditor(bookmarkAction, this);
        setAction(ADD_BOOKMARK, bookmarkAction);

        // Add task action
        final TaskRulerAction tra = new TaskRulerAction();
        Action taskAction = new Action() {
            public void run() {
                tra.run(this);
            }

        };
        tra.setActiveEditor(taskAction, this);
        setAction(ADD_TASK, taskAction);

        Action action = new ContentAssistAction(UnifiedMessages.getResourceBundle(), "ContentAssistProposal.", //$NON-NLS-1$
                this);
        String id = ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS;
        action.setActionDefinitionId(id);
        setAction("ContentAssistProposal", action); //$NON-NLS-1$
        markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$

        action = new GotoMatchingBracketAction(this);
        action.setActionDefinitionId(UnifiedActionContributor.GOTO_MATCHING_BRACKET_ID);
        setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action);

        Action actionContext = new TextOperationAction(UnifiedMessages.getResourceBundle(),
                "ContentAssistContextInformation.", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$
        actionContext.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION);
        setAction("ContentAssistContextInformation", actionContext); //$NON-NLS-1$
        markAsStateDependentAction("ContentAssistContextInformation", true); //$NON-NLS-1$
        // PlatformUI.getWorkbench().getHelpSystem().setHelp(action,
        // IJavaHelpContextIds.PARAMETER_HINTS_ACTION);

        FoldingExtensionPointLoader.createFoldingActions(this);

        _formatAction = new CodeFormatAction();
    }

    /**
     * @see com.aptana.ide.editors.unified.IUnifiedEditor#getBaseContributor()
     */
    public IUnifiedEditorContributor getBaseContributor() {
        return this._baseContributor;
    }

    /**
     * isNewInput
     * 
     * @param input
     * @return boolean
     */
    protected boolean isNewInput(IEditorInput input) {
        return true;
    }

    /**
     * @param event
     */
    public void selectionChanged(SelectionChangedEvent event) {

        ISourceViewer sourceViewer = getSourceViewer();
        if (sourceViewer == null) {
            return;
        }

        StyledText styledText = sourceViewer.getTextWidget();
        if (styledText == null) {
            return;
        }

        int offset = -1;
        // If there is a selection, we consider selection range instead of caret
        // position.
        Point selectionRange = sourceViewer.getSelectedRange();
        if (selectionRange.x > -1 && selectionRange.y > 0) {
            offset = selectionRange.x;
        }

        if (offset < 0) {
            if (sourceViewer instanceof ITextViewerExtension5) {
                ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer;
                offset = extension.widgetOffset2ModelOffset(styledText.getCaretOffset());
            } else {
                offset = sourceViewer.getVisibleRegion().getOffset();
                offset += styledText.getCaretOffset();
            }
        }

        if (offset < 0) {
            return;
        }

        LexemeList lexemeList = getLexemeList();

        if (lexemeList == null) {
            IdeLog.logError(UnifiedEditorsPlugin.getDefault(), Messages.UnifiedEditor_LexemeListIsNull);
            return;
        }

        Lexeme selectedLexeme = lexemeList.getLexemeFromOffset(offset);

        if (selectedLexeme == null) {
            return;
        }

        if (!canMarkOccurrences(selectedLexeme)) {
            return;
        }

        String selectedText = selectedLexeme.getText();

        if (selectedText == null || selectedText.length() == 0) {
            return;
        }

        markOccurences(lexemeList, selectedLexeme);

        Job job = new UIJob("Redraw") {

            @Override
            public IStatus runInUIThread(IProgressMonitor monitor) {
                // TODO: Measure time to see if a precisely calculated redraw area yields significant savings
                getSourceViewer().getTextWidget().redraw();
                return Status.OK_STATUS;
            }
        };
        job.setPriority(Job.INTERACTIVE);
        job.setSystem(true);
        job.schedule();
    }

    /**
     * Mark matching lexemes in the lexeme list
     * 
     * @param lexemeList
     * @param selectedLexeme
     */
    protected void markOccurences(LexemeList lexemeList, Lexeme selectedLexeme) {
        String selectedText = selectedLexeme.getText();
        IAnnotationModel model = getDocumentProvider().getAnnotationModel(getEditorInput());
        Map<Annotation, Position> toAdd = new HashMap<Annotation, Position>();
        for (int i = 0; i < lexemeList.size(); i++) {
            Lexeme lexeme = lexemeList.get(i);
            if (lexeme != null) {
                if (lexeme.isHighlighted()) {
                    lexeme.setHighlighted(false);
                }

                if (lexeme.length == selectedLexeme.length && selectedText.equals(lexeme.getText())) {
                    lexeme.setHighlighted(true);
                    if (model != null) {
                        Position pos = new Position(lexeme.offset, lexeme.length);
                        Annotation occurence = new Annotation("com.aptana.ide.annotation.occurence", false, lexeme //$NON-NLS-1$
                                .getText());
                        toAdd.put(occurence, pos);
                    }
                }
            }
        }

        synchronized (getLockObject(model)) {
            if (model instanceof IAnnotationModelExtension) {
                ((IAnnotationModelExtension) model).replaceAnnotations(fOccurrenceAnnotations, toAdd);
            } else {
                removeOccurrenceAnnotations();
                Iterator iter = toAdd.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry mapEntry = (Map.Entry) iter.next();
                    model.addAnnotation((Annotation) mapEntry.getKey(), (Position) mapEntry.getValue());
                }
            }
            fOccurrenceAnnotations = (Annotation[]) toAdd.keySet().toArray(new Annotation[toAdd.keySet().size()]);
        }
    }

    /**
     * Gets the lexeme list for this editor
     * 
     * @return - lexeme list
     */
    protected LexemeList getLexemeList() {
        EditorFileContext fileContext = getFileContext();

        // wrapper could be non-null, but interior is.
        if (fileContext == null || fileContext.getFileContext() == null) {
            return null;
        }

        IParseState parseState = fileContext.getParseState();

        if (parseState == null) {
            return null;
        }

        LexemeList lexemeList = parseState.getLexemeList();

        return lexemeList;
    }

    /**
     * Returns language MIME type for offset position
     * 
     * @param offset
     * @return - language
     */
    public String getLanguageAtOffset(int offset) {
        LexemeList lexemeList = getLexemeList();
        if (lexemeList == null) {
            return null;
        }

        Lexeme lexemeAtOffset = lexemeList.getCeilingLexeme(offset);
        if (lexemeAtOffset == null) {
            return null;
        }
        return lexemeAtOffset.getLanguage();
    }

    private void removeOccurrenceAnnotations() {

        IDocumentProvider documentProvider = getDocumentProvider();
        if (documentProvider == null)
            return;

        IAnnotationModel annotationModel = documentProvider.getAnnotationModel(getEditorInput());
        if (annotationModel == null || fOccurrenceAnnotations == null)
            return;

        synchronized (getLockObject(annotationModel)) {
            if (annotationModel instanceof IAnnotationModelExtension) {
                ((IAnnotationModelExtension) annotationModel).replaceAnnotations(fOccurrenceAnnotations, null);
            } else {
                for (int i = 0, length = fOccurrenceAnnotations.length; i < length; i++)
                    annotationModel.removeAnnotation(fOccurrenceAnnotations[i]);
            }
            fOccurrenceAnnotations = null;
        }
    }

    /**
     * Returns the lock object for the given annotation model.
     *
     * @param annotationModel the annotation model
     * @return the annotation model's lock object
     * @since 3.0
     */
    private Object getLockObject(IAnnotationModel annotationModel) {
        if (annotationModel instanceof ISynchronizable) {
            Object lock = ((ISynchronizable) annotationModel).getLockObject();
            if (lock != null)
                return lock;
        }
        return annotationModel;
    }

    /**
     * 
     * 
     */
    public void removeMarkedOccurrences() {
        removeOccurrenceAnnotations();
        LexemeList lexemeList = getLexemeList();
        if (lexemeList == null) {
            return;
        }

        for (int i = 0; i < lexemeList.size(); i++) {
            Lexeme lexeme = lexemeList.get(i);
            if (lexeme != null && lexeme.isHighlighted()) {
                lexeme.setHighlighted(false);
            }
        }
    }

    /**
     * @param lexeme
     * @return - true if occurrence should be marked
     */
    public boolean canMarkOccurrences(Lexeme lexeme) {
        if (lexeme.getCategoryIndex() == TokenCategories.WHITESPACE) {
            return false;
        }

        if (lexeme.getCategoryIndex() == TokenCategories.PUNCTUATOR) {
            return false;
        }

        return true;
    }

    /**
     * Creates the docment provider
     * 
     * @return IDocumentProvider
     */
    public abstract IDocumentProvider createDocumentProvider();

    /**
     * Is code assist available for auto-activation
     * 
     * @return - true if activating, false otherwise
     */
    public boolean autoActivateCodeAssist() {
        return autoActivateCodeAssist;
    }

    public void gotoMatchingBracket() {
        ISourceViewer sourceViewer = getSourceViewer();
        IDocument document = sourceViewer.getDocument();
        if (document == null)
            return;

        Point selectedPoint = sourceViewer.getSelectedRange();
        IRegion selection = new Region(selectedPoint.x, selectedPoint.y);

        int selectionLength = Math.abs(selection.getLength());
        if (selectionLength > 1) {
            setStatusLineErrorMessage(
                    com.aptana.ide.editors.unified.actions.Messages.GotoMatchingBracket_error_invalidSelection);
            sourceViewer.getTextWidget().getDisplay().beep();
            return;
        }

        int sourceCaretOffset = selection.getOffset() + selection.getLength();
        PairMatch pair = getPairMatch(sourceCaretOffset);

        if (pair == null) {
            setStatusLineErrorMessage(
                    com.aptana.ide.editors.unified.actions.Messages.GotoMatchingBracket_error_noMatchingBracket);
            sourceViewer.getTextWidget().getDisplay().beep();
            return;
        }

        int start = 0;
        int end = 0;
        if (sourceCaretOffset <= pair.beginEnd && sourceCaretOffset >= pair.beginStart) {
            start = pair.endStart;
            end = pair.endEnd;
        } else {
            start = pair.beginStart;
            end = pair.beginEnd;
        }

        int length = end - start;
        sourceViewer.setSelectedRange(start, length);
        sourceViewer.revealRange(start, length);
    }
}