| /* |
| * 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.server.generated.types.Occurrences; |
| 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.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(); |
| } |
| } |