com.google.dart.tools.ui.internal.text.editor.SemanticHighlightingManager_NEW.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.tools.ui.internal.text.editor.SemanticHighlightingManager_NEW.java

Source

/*
 * Copyright (c) 2014, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.dart.tools.ui.internal.text.editor;

import com.google.dart.server.generated.AnalysisServer;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.DartCoreDebug;
import com.google.dart.tools.core.analysis.model.AnalysisServerData;
import com.google.dart.tools.core.analysis.model.AnalysisServerHighlightsListener;
import com.google.dart.tools.ui.DartToolsPlugin;
import com.google.dart.tools.ui.DartUI;
import com.google.dart.tools.ui.internal.text.dart.DartReconcilingStrategy;
import com.google.dart.tools.ui.text.IColorManager;

import org.dartlang.analysis.server.protocol.HighlightRegion;
import org.dartlang.analysis.server.protocol.HighlightRegionType;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.jface.text.ITextPresentationListener;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;

/**
 * A helper for displaying {@link HighlightRegion} from {@link AnalysisServer}.
 */
public class SemanticHighlightingManager_NEW
        implements AnalysisServerHighlightsListener, ITextPresentationListener {
    /**
     * Semantic highlighting position updater.
     */
    private class HighlightingPositionUpdater implements IPositionUpdater {
        @Override
        public void update(DocumentEvent event) {
            synchronized (positionsLock) {
                if (positions == null) {
                    return;
                }
                // prepare event values
                int eventOffset = event.getOffset();
                int eventOldLength = event.getLength();
                int eventEnd = eventOffset + eventOldLength;
                // special states
                {
                    String documentText = document.get();
                    // check if the same text as for the last highlight regions
                    if (lastText != null && lastText.equals(documentText)) {
                        createPositions(lastRegions);
                        return;
                    }
                    // check if the whole document was replaced (e.g. GIT branch was switched)
                    String eventText = event.getText();
                    if (eventOffset == 0 && documentText.length() == eventText.length()) {
                        return;
                    }
                }
                // update positions
                for (HighlightPosition position : positions) {
                    int offset = position.getOffset();
                    int length = position.getLength();
                    int end = offset + length;
                    if (offset > eventEnd) {
                        updateWithPrecedingEvent(position, event);
                    } else if (end < eventOffset) {
                        updateWithSucceedingEvent(position, event);
                    } else if (offset <= eventOffset && end >= eventEnd) {
                        updateWithIncludedEvent(position, event);
                    } else if (offset <= eventOffset) {
                        updateWithOverEndEvent(position, event);
                    } else if (end >= eventEnd) {
                        updateWithOverStartEvent(position, event);
                    } else {
                        updateWithIncludingEvent(position, event);
                    }
                }
            }
        }

        private boolean isDartIdentifierPart(String text, int index) {
            char c = text.charAt(index);
            return Character.isJavaIdentifierPart(c);
        }

        /**
         * Update the given position with the given event.
         * <p>
         * The event is included by the position.
         */
        private void updateWithIncludedEvent(HighlightPosition position, DocumentEvent event) {
            String eventText = event.getText();
            if (eventText != null) {
                int length = position.getLength();
                int newLength = length + eventText.length();
                position.setLength(newLength);
            }
        }

        /**
         * Update the given position with the given event.
         * <p>
         * The event includes the position.
         */
        private void updateWithIncludingEvent(HighlightPosition position, DocumentEvent event) {
            position.delete();
            position.update(event.getOffset(), 0);
        }

        /**
         * Update the given position with the given event.
         * <p>
         * The event overlaps with the end of the position.
         */
        private void updateWithOverEndEvent(HighlightPosition position, DocumentEvent event) {
            String newText = event.getText();
            if (newText == null) {
                newText = "";
            }
            int eventNewLength = newText.length();

            int includedLength = 0;
            while (includedLength < eventNewLength && isDartIdentifierPart(newText, includedLength)) {
                includedLength++;
            }
            position.setLength(event.getOffset() - position.getOffset() + includedLength);
        }

        /**
         * Update the given position with the given event.
         * <p>
         * The event overlaps with the start of the position.
         */
        private void updateWithOverStartEvent(HighlightPosition position, DocumentEvent event) {
            int eventOffset = event.getOffset();
            int eventEnd = eventOffset + event.getLength();

            String newText = event.getText();
            if (newText == null) {
                newText = "";
            }
            int eventNewLength = newText.length();

            int excludedLength = eventNewLength;
            while (excludedLength > 0 && isDartIdentifierPart(newText, excludedLength - 1)) {
                excludedLength--;
            }
            int deleted = eventEnd - position.getOffset();
            int inserted = eventNewLength - excludedLength;
            position.update(eventOffset + excludedLength, position.getLength() - deleted + inserted);
        }

        /**
         * Update the given position with the given event.
         * <p>
         * The event precedes the position.
         */
        private void updateWithPrecedingEvent(HighlightPosition position, DocumentEvent event) {
            String newText = event.getText();
            int eventNewLength = newText != null ? newText.length() : 0;
            int deltaLength = eventNewLength - event.getLength();

            position.setOffset(position.getOffset() + deltaLength);
        }

        /**
         * Update the given position with the given event.
         * <p>
         * The event succeeds the position.
         */
        private void updateWithSucceedingEvent(HighlightPosition position, DocumentEvent event) {
        }
    }

    /**
     * A {@link Position} that can be tracked by a {@link Document} and contains the
     * {@link HighlightRegion}.
     */
    private static class HighlightPosition extends Position {
        private final HighlightRegion highlight;

        public HighlightPosition(HighlightRegion highlight) {
            super(highlight.getOffset(), highlight.getLength());
            this.highlight = highlight;
        }

        @Override
        public String toString() {
            return "[" + super.toString() + " " + highlight + "]";
        }

        public void update(int offset, int len) {
            setOffset(offset);
            setLength(len);
        }
    }

    private final DartSourceViewer viewer;
    private final String file;
    private final DartReconcilingStrategy reconcilingStrategy;
    private final IDocument document;
    private final IPositionUpdater positionUpdater = new HighlightingPositionUpdater();

    private final Object positionsLock = new Object();
    private HighlightPosition[] positions;
    private String lastText;
    private HighlightRegion[] lastRegions;

    public SemanticHighlightingManager_NEW(DartSourceViewer viewer, String file,
            DartReconcilingStrategy reconcilingStrategy) {
        this.viewer = viewer;
        this.file = file;
        this.reconcilingStrategy = reconcilingStrategy;
        this.document = viewer.getDocument();
        document.addPositionUpdater(positionUpdater);
        // subscribe
        AnalysisServerData analysisServerData = DartCore.getAnalysisServerData();
        analysisServerData.addHighlightsListener(file, this);
        viewer.prependTextPresentationListener(this);
    }

    @Override
    public void applyTextPresentation(TextPresentation textPresentation) {
        synchronized (positionsLock) {
            if (positions == null) {
                return;
            }
            // prepare damaged region
            IRegion damagedRegion = textPresentation.getExtent();
            int daOffset = damagedRegion.getOffset();
            int daEnd = daOffset + damagedRegion.getLength();
            // prepare theme access
            IPreferenceStore store = DartToolsPlugin.getDefault().getPreferenceStore();
            IColorManager colorManager = DartUI.getColorManager();
            // add style ranges
            for (HighlightPosition position : positions) {
                // skip if outside of the damaged region
                int hiOffset = position.getOffset();
                int hiLength = position.getLength();
                int hiEnd = hiOffset + hiLength;
                if (hiEnd < daOffset || hiOffset >= daEnd) {
                    continue;
                }
                if (hiEnd > daEnd) {
                    continue;
                }
                // prepare highlight key
                String highlightType = position.highlight.getType();
                String themeKey = getThemeKey(highlightType);
                if (themeKey == null) {
                    continue;
                }
                themeKey = "semanticHighlighting." + themeKey;
                // prepare color
                RGB foregroundRGB = PreferenceConverter.getColor(store, themeKey + ".color");
                if (foregroundRGB == PreferenceConverter.COLOR_DEFAULT_DEFAULT) {
                    continue;
                }
                Color foregroundColor = colorManager.getColor(foregroundRGB);
                // prepare font style
                boolean fontBold = store.getBoolean(themeKey + ".bold");
                boolean fontItalic = store.getBoolean(themeKey + ".italic");
                int fontStyle = 0;
                if (fontBold) {
                    fontStyle |= SWT.BOLD;
                }
                if (fontItalic) {
                    fontStyle |= SWT.ITALIC;
                }
                // merge style range
                textPresentation
                        .replaceStyleRange(new StyleRange(hiOffset, hiLength, foregroundColor, null, fontStyle));
            }
        }
    }

    @Override
    public void computedHighlights(String file, HighlightRegion[] highlights) {
        if (reconcilingStrategy != null && reconcilingStrategy.hasPendingContentChanges()) {
            if (!DartCoreDebug.DISABLE_SEMANTIC_HIGHLIGHT_FILTERING) {
                return;
            }
        }
        // create HighlightPosition(s)
        createPositions(highlights);
        // Invalidate presentation.
        // Delay it, so that in case of the code completion activation, we can display completions
        // before the semantic highlighting and catch up while user stares at the completion list.
        Display.getDefault().asyncExec(new Runnable() {
            @Override
            public void run() {
                Display.getDefault().timerExec(5, new Runnable() {
                    @Override
                    public void run() {
                        viewer.invalidateTextPresentation();
                    }
                });
            }
        });
    }

    public void dispose() {
        AnalysisServerData analysisServerData = DartCore.getAnalysisServerData();
        analysisServerData.removeHighlightsListener(file, this);
        viewer.removeTextPresentationListener(this);
        document.removePositionUpdater(positionUpdater);
    }

    private void createPositions(HighlightRegion[] highlights) {
        HighlightPosition[] newPositions = new HighlightPosition[highlights.length];
        for (int i = 0; i < highlights.length; i++) {
            HighlightRegion highlight = highlights[i];
            newPositions[i] = new HighlightPosition(highlight);
        }
        // HighlightingPositionUpdater works owns the document lock and the grabs positionsLock.
        // To avoid dead lock, we must grab lock in the same order - document, then positionsLock.
        synchronized (getDocumentLockObject()) {
            synchronized (positionsLock) {
                lastText = document.get();
                lastRegions = highlights;
                positions = newPositions;
            }
        }
    }

    /**
     * Returns the lock to use to synchronize {@link #document} access.
     */
    private Object getDocumentLockObject() {
        if (document instanceof ISynchronizable) {
            Object lock = ((ISynchronizable) document).getLockObject();
            if (lock != null) {
                return lock;
            }
        }
        return document;
    }

    /**
     * The type will be a {@link String} from {@link HighlightRegionType}.
     */
    private String getThemeKey(String type) {
        if (type.equals(HighlightRegionType.ANNOTATION)) {
            return "annotation";
        } else if (type.equals(HighlightRegionType.BUILT_IN) || type.equals(HighlightRegionType.KEYWORD)) {
            return "builtin";
        } else if (type.equals(HighlightRegionType.CLASS)) {
            return "class";
        } else if (type.equals(HighlightRegionType.CONSTRUCTOR)) {
            return "constructor";
        } else if (type.equals(HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_DECLARATION)
                || type.equals(HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_REFERENCE)
                || type.equals(HighlightRegionType.DYNAMIC_PARAMETER_DECLARATION)
                || type.equals(HighlightRegionType.DYNAMIC_PARAMETER_REFERENCE)) {
            return "dynamicType";
        } else if (type.equals(HighlightRegionType.ENUM)) {
            return "enum";
        } else if (type.equals(HighlightRegionType.ENUM_CONSTANT)) {
            return "enumConstant";
        } else if (type.equals(HighlightRegionType.FUNCTION_TYPE_ALIAS)) {
            return "functionTypeAlias";
        } else if (type.equals(HighlightRegionType.INSTANCE_FIELD_DECLARATION)
                || type.equals(HighlightRegionType.INSTANCE_FIELD_REFERENCE)
                || type.equals(HighlightRegionType.INSTANCE_GETTER_REFERENCE)
                || type.equals(HighlightRegionType.INSTANCE_SETTER_REFERENCE)) {
            return "field";
        } else if (type.equals(HighlightRegionType.INSTANCE_GETTER_DECLARATION)
                || type.equals(HighlightRegionType.STATIC_GETTER_DECLARATION)
                || type.equals(HighlightRegionType.TOP_LEVEL_GETTER_DECLARATION)) {
            return "getterDeclaration";
        } else if (type.equals(HighlightRegionType.INSTANCE_METHOD_DECLARATION)) {
            return "methodDeclarationName";
        } else if (type.equals(HighlightRegionType.INSTANCE_METHOD_REFERENCE)) {
            return "method";
        } else if (type.equals(HighlightRegionType.INSTANCE_SETTER_DECLARATION)
                || type.equals(HighlightRegionType.STATIC_SETTER_DECLARATION)
                || type.equals(HighlightRegionType.TOP_LEVEL_SETTER_DECLARATION)) {
            return "setterDeclaration";
        } else if (type.equals(HighlightRegionType.IMPORT_PREFIX)) {
            return "importPrefix";
        } else if (type.equals(HighlightRegionType.LABEL)) {
            return "label";
        } else if (type.equals(HighlightRegionType.LITERAL_BOOLEAN)) {
            return "builtin";
        } else if (type.equals(HighlightRegionType.LITERAL_DOUBLE)
                || type.equals(HighlightRegionType.LITERAL_INTEGER)) {
            return "number";
        } else if (type.equals(HighlightRegionType.LITERAL_STRING)) {
            return "string";
        } else if (type.equals(HighlightRegionType.LOCAL_FUNCTION_DECLARATION)
                || type.equals(HighlightRegionType.TOP_LEVEL_FUNCTION_DECLARATION)) {
            return "methodDeclarationName";
        } else if (type.equals(HighlightRegionType.LOCAL_FUNCTION_REFERENCE)
                || type.equals(HighlightRegionType.TOP_LEVEL_FUNCTION_REFERENCE)) {
            return "function";
        } else if (type.equals(HighlightRegionType.LOCAL_VARIABLE_DECLARATION)) {
            return "localVariableDeclaration";
        } else if (type.equals(HighlightRegionType.LOCAL_VARIABLE_REFERENCE)) {
            return "localVariable";
        } else if (type.equals(HighlightRegionType.STATIC_FIELD_DECLARATION)
                || type.equals(HighlightRegionType.STATIC_GETTER_REFERENCE)
                || type.equals(HighlightRegionType.STATIC_SETTER_REFERENCE)) {
            return "staticField";
        } else if (type.equals(HighlightRegionType.STATIC_METHOD_REFERENCE)) {
            return "staticMethod";
        } else if (type.equals(HighlightRegionType.STATIC_METHOD_DECLARATION)) {
            return "staticMethodDeclarationName";
        } else if (type.equals(HighlightRegionType.PARAMETER_DECLARATION)
                || type.equals(HighlightRegionType.PARAMETER_REFERENCE)) {
            return "parameterVariable";
        } else if (type.equals(HighlightRegionType.TOP_LEVEL_VARIABLE_DECLARATION)
                || type.equals(HighlightRegionType.TOP_LEVEL_GETTER_REFERENCE)
                || type.equals(HighlightRegionType.TOP_LEVEL_SETTER_REFERENCE)) {
            return "staticField";
        } else if (type.equals(HighlightRegionType.TYPE_NAME_DYNAMIC)) {
            return "builtin";
        } else if (type.equals(HighlightRegionType.TYPE_PARAMETER)) {
            return "typeParameter";
        } else {
            // unsupported:
            //    COMMENT_BLOCK:
            //    COMMENT_DOCUMENTATION:
            //    COMMENT_END_OF_LINE:
            //    DIRECTIVE:
            //    IDENTIFIER_DEFAULT:
            //    INVALID_STRING_ESCAPE:
            //    LITERAL_LIST:
            //    LITERAL_MAP:
            //    UNRESOLVED_INSTANCE_MEMBER_REFERENCE:
            //    VALID_STRING_ESCAPE:
            return null;
        }
    }
}