/* * 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.common.collect.Maps; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.core.analysis.model.AnalysisServerOccurrencesListener; import com.google.dart.tools.ui.internal.text.dart.DartReconcilingStrategy; import com.google.dart.tools.ui.internal.text.functions.DartWordFinder; import org.dartlang.analysis.server.protocol.Occurrences; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension4; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ISynchronizable; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IAnnotationModelExtension; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.texteditor.IDocumentProvider; import java.util.Map; import java.util.Map.Entry; public class MarkOccurrencesManager_NEW implements AnalysisServerOccurrencesListener { private static final String TYPE = "com.google.dart.tools.ui.occurrences"; private final DartEditor editor; private final IDocument document; private final DartReconcilingStrategy reconcilingStrategy; private final IEditorInput editorInput; private final String file; private int selectionOffset; private int selectionLength; private ISelectionChangedListener occurrencesResponder; private Annotation[] fOccurrenceAnnotations; private IRegion fMarkOccurrenceTargetRegion; public MarkOccurrencesManager_NEW(DartEditor editor, DartSourceViewer viewer, DartReconcilingStrategy reconcilingStrategy) { this.editor = editor; this.document = viewer.getDocument(); this.reconcilingStrategy = reconcilingStrategy; this.editorInput = editor.getEditorInput(); this.file = editor.getInputFilePath(); DartCore.getAnalysisServerData().addOccurrencesListener(file, this); // track selection occurrencesResponder = new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); if (selection instanceof ITextSelection) { updateOccurrenceAnnotations((ITextSelection) selection); } } }; viewer.getSelectionProvider(); editor.addDartSelectionListener(occurrencesResponder); // force update { ISelection selection = editor.getSelectionProvider().getSelection(); updateOccurrenceAnnotations((ITextSelection) selection); } } @Override public void computedOccurrences(String file, Occurrences[] occurrences) { fMarkOccurrenceTargetRegion = null; updateOccurrenceAnnotations(); } public void dispose() { DartCore.getAnalysisServerData().removeOccurrencesListener(file, this); if (occurrencesResponder != null) { editor.removeDartSelectionListener(occurrencesResponder); occurrencesResponder = null; } removeOccurrenceAnnotations(); } private void addAnnotations(IDocument document, Position[] positions) { // prepare annotations Map<Annotation, Position> annotationMap = Maps.newHashMap(); for (Position position : positions) { String message; try { message = document.get(position.offset, position.length); } catch (BadLocationException ex) { continue; } annotationMap.put(new Annotation(TYPE, false, message), position); } // prepare IAnnotationModel IAnnotationModel annotationModel = getAnnotationModel(); if (annotationModel == null) { return; } // add annotations synchronized (getLockObject(annotationModel)) { if (annotationModel instanceof IAnnotationModelExtension) { IAnnotationModelExtension extension = (IAnnotationModelExtension) annotationModel; extension.replaceAnnotations(fOccurrenceAnnotations, annotationMap); } else { removeOccurrenceAnnotations(); for (Entry<Annotation, Position> entry : annotationMap.entrySet()) { Annotation annotation = entry.getKey(); Position position = entry.getValue(); annotationModel.addAnnotation(annotation, position); } } fOccurrenceAnnotations = annotationMap.keySet().toArray( new Annotation[annotationMap.keySet().size()]); } } private IAnnotationModel getAnnotationModel() { IDocumentProvider documentProvider = editor.getDocumentProvider(); if (documentProvider == null) { return null; } return documentProvider.getAnnotationModel(editorInput); } /** * Returns the lock object for the given annotation model. */ private Object getLockObject(IAnnotationModel annotationModel) { if (annotationModel instanceof ISynchronizable) { Object lock = ((ISynchronizable) annotationModel).getLockObject(); if (lock != null) { return lock; } } return annotationModel; } private void removeOccurrenceAnnotations() { // prepare IAnnotationModel IAnnotationModel annotationModel = getAnnotationModel(); if (annotationModel == null) { return; } // do remove synchronized (getLockObject(annotationModel)) { IAnnotationModelExtension extension = (IAnnotationModelExtension) annotationModel; if (annotationModel instanceof IAnnotationModelExtension) { extension.replaceAnnotations(fOccurrenceAnnotations, null); } else { for (Annotation annotation : fOccurrenceAnnotations) { annotationModel.removeAnnotation(annotation); } } fOccurrenceAnnotations = null; } } private void updateOccurrenceAnnotations() { if (document instanceof IDocumentExtension4) { IRegion markOccurrenceTargetRegion = fMarkOccurrenceTargetRegion; if (markOccurrenceTargetRegion != null) { if (markOccurrenceTargetRegion.getOffset() <= selectionOffset && selectionOffset <= markOccurrenceTargetRegion.getOffset() + markOccurrenceTargetRegion.getLength()) { if (selectionLength > 0 && selectionLength != fMarkOccurrenceTargetRegion.getLength()) { removeOccurrenceAnnotations(); } return; } } fMarkOccurrenceTargetRegion = DartWordFinder.findWord(document, selectionOffset); if (fMarkOccurrenceTargetRegion == null) { removeOccurrenceAnnotations(); return; } if (selectionLength > 0 && selectionLength != fMarkOccurrenceTargetRegion.getLength()) { removeOccurrenceAnnotations(); return; } } Occurrences targetOccurrences = null; if (!reconcilingStrategy.hasPendingContentChanges()) { Occurrences[] occurrencesArray = DartCore.getAnalysisServerData().getOccurrences(file); for (Occurrences occurrences : occurrencesArray) { if (occurrences.containsInclusive(selectionOffset)) { targetOccurrences = occurrences; break; } } } if (targetOccurrences == null) { removeOccurrenceAnnotations(); return; } int[] offsets = targetOccurrences.getOffsets(); Position[] positions = new Position[offsets.length]; for (int i = 0; i < offsets.length; i++) { int offset = offsets[i]; positions[i] = new Position(offset, targetOccurrences.getLength()); } // Add occurrence annotations addAnnotations(document, positions); } /** * Updates the occurrences annotations based on the current selection. */ private void updateOccurrenceAnnotations(ITextSelection selection) { if (selection == null) { return; } selectionOffset = selection.getOffset(); selectionLength = selection.getLength(); updateOccurrenceAnnotations(); } }