blob: 1da82757f6f5e7cd4f3d34d638f03c2d08f2cf3b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 org.apache.uima.ruta.ide.ui.text;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.uima.ruta.ide.RutaIdeUIPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.dltk.ui.PreferenceConstants;
import org.eclipse.dltk.ui.text.IColorManager;
import org.eclipse.jface.internal.text.html.HTMLTextPresenter;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
import org.eclipse.jface.text.quickassist.QuickAssistAssistant;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
public class RutaCorrectionAssistant extends QuickAssistAssistant {
private ITextViewer fViewer;
private ITextEditor fEditor;
private Position fPosition;
private Annotation[] fCurrentAnnotations;
// private RutaQuickAssistLightBulbUpdater fLightBulbUpdater;
/**
* Constructor for JavaCorrectionAssistant.
*
* @param editor
* the editor
*/
public RutaCorrectionAssistant(ITextEditor editor) {
super();
Assert.isNotNull(editor);
fEditor = editor;
RutaCorrectionProcessor processor = new RutaCorrectionProcessor(this);
setQuickAssistProcessor(processor);
setInformationControlCreator(getInformationControlCreator());
RutaTextTools textTools = RutaIdeUIPlugin.getDefault().getTextTools();
IColorManager manager = textTools.getColorManager();
IPreferenceStore store = RutaIdeUIPlugin.getDefault().getPreferenceStore();
Color c = getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_FOREGROUND, manager);
setProposalSelectorForeground(c);
c = getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_BACKGROUND, manager);
setProposalSelectorBackground(c);
}
public IEditorPart getEditor() {
return fEditor;
}
private IInformationControlCreator getInformationControlCreator() {
return new IInformationControlCreator() {
public IInformationControl createInformationControl(Shell parent) {
return new DefaultInformationControl(parent, new HTMLTextPresenter());
}
};
}
private static Color getColor(IPreferenceStore store, String key, IColorManager manager) {
RGB rgb = PreferenceConverter.getColor(store, key);
return manager.getColor(rgb);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.text.contentassist.IContentAssistant#install(org.eclipse
* .jface.text.ITextViewer)
*/
@Override
public void install(ISourceViewer sourceViewer) {
super.install(sourceViewer);
fViewer = sourceViewer;
// fLightBulbUpdater = new
// RutaQuickAssistLightBulbUpdater(fEditor,
// sourceViewer);
// fLightBulbUpdater.install();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.text.contentassist.ContentAssistant#uninstall()
*/
@Override
public void uninstall() {
// if (fLightBulbUpdater != null) {
// fLightBulbUpdater.uninstall();
// fLightBulbUpdater = null;
// }
super.uninstall();
}
/*
* @seeorg.eclipse.jface.text.quickassist.QuickAssistAssistant# showPossibleQuickAssists()
*
* @since 3.2
*/
/**
* Show completions at caret position. If current position does not contain quick fixes look for
* next quick fix on same line by moving from left to right and restarting at end of line if the
* beginning of the line is reached.
*
* @see IQuickAssistAssistant#showPossibleQuickAssists()
*/
@Override
public String showPossibleQuickAssists() {
fPosition = null;
fCurrentAnnotations = null;
if (fViewer == null || fViewer.getDocument() == null)
// Let superclass deal with this
return super.showPossibleQuickAssists();
ArrayList resultingAnnotations = new ArrayList(20);
try {
Point selectedRange = fViewer.getSelectedRange();
int currOffset = selectedRange.x;
int currLength = selectedRange.y;
boolean goToClosest = (currLength == 0);
int newOffset = collectQuickFixableAnnotations(fEditor, currOffset, goToClosest,
resultingAnnotations);
if (newOffset != currOffset) {
storePosition(currOffset, currLength);
fViewer.setSelectedRange(newOffset, 0);
fViewer.revealRange(newOffset, 0);
}
} catch (BadLocationException e) {
// JavaPlugin.log(e);
}
fCurrentAnnotations = (Annotation[]) resultingAnnotations
.toArray(new Annotation[resultingAnnotations.size()]);
return super.showPossibleQuickAssists();
}
private static IRegion getRegionOfInterest(ITextEditor editor, int invocationLocation)
throws BadLocationException {
IDocumentProvider documentProvider = editor.getDocumentProvider();
if (documentProvider == null) {
return null;
}
IDocument document = documentProvider.getDocument(editor.getEditorInput());
if (document == null) {
return null;
}
return document.getLineInformationOfOffset(invocationLocation);
}
public static int collectQuickFixableAnnotations(ITextEditor editor, int invocationLocation,
boolean goToClosest, ArrayList resultingAnnotations) throws BadLocationException {
IAnnotationModel model = DLTKUIPlugin.getDocumentProvider().getAnnotationModel(
editor.getEditorInput());
if (model == null) {
return invocationLocation;
}
ensureUpdatedAnnotations(editor);
Iterator iter = model.getAnnotationIterator();
if (goToClosest) {
IRegion lineInfo = getRegionOfInterest(editor, invocationLocation);
if (lineInfo == null) {
return invocationLocation;
}
int rangeStart = lineInfo.getOffset();
int rangeEnd = rangeStart + lineInfo.getLength();
ArrayList allAnnotations = new ArrayList();
ArrayList allPositions = new ArrayList();
int bestOffset = Integer.MAX_VALUE;
while (iter.hasNext()) {
Annotation annot = (Annotation) iter.next();
if (RutaCorrectionProcessor.isQuickFixableType(annot)) {
Position pos = model.getPosition(annot);
if (pos != null && isInside(pos.offset, rangeStart, rangeEnd)) { // inside
// our
// range?
allAnnotations.add(annot);
allPositions.add(pos);
bestOffset = processAnnotation(annot, pos, invocationLocation, bestOffset);
}
}
}
if (bestOffset == Integer.MAX_VALUE) {
return invocationLocation;
}
for (int i = 0; i < allPositions.size(); i++) {
Position pos = (Position) allPositions.get(i);
if (isInside(bestOffset, pos.offset, pos.offset + pos.length)) {
resultingAnnotations.add(allAnnotations.get(i));
}
}
return bestOffset;
} else {
while (iter.hasNext()) {
Annotation annot = (Annotation) iter.next();
if (RutaCorrectionProcessor.isQuickFixableType(annot)) {
Position pos = model.getPosition(annot);
if (pos != null && isInside(invocationLocation, pos.offset, pos.offset + pos.length)) {
resultingAnnotations.add(annot);
}
}
}
return invocationLocation;
}
}
private static void ensureUpdatedAnnotations(ITextEditor editor) {
// Object inputElement = editor.getEditorInput().getAdapter(
// IModelElement.class);
}
private static int processAnnotation(Annotation annot, Position pos, int invocationLocation,
int bestOffset) {
int posBegin = pos.offset;
int posEnd = posBegin + pos.length;
if (isInside(invocationLocation, posBegin, posEnd)) { // covers
// invocation
// location?
return invocationLocation;
} else if (bestOffset != invocationLocation) {
int newClosestPosition = computeBestOffset(posBegin, invocationLocation, bestOffset);
if (newClosestPosition != -1) {
if (newClosestPosition != bestOffset) { // new best
// if (JavaCorrectionProcessor.hasCorrections(annot)) { //
// only
// // jump
// // to
// // it
// // if
// // there
// // are
// // proposals
// return newClosestPosition;
// }
}
}
}
return bestOffset;
}
private static boolean isInside(int offset, int start, int end) {
return offset == start || offset == end || (offset > start && offset < end); // make sure to
// handle
// 0-length ranges
}
/**
* Computes and returns the invocation offset given a new position, the initial offset and the
* best invocation offset found so far.
* <p>
* The closest offset to the left of the initial offset is the best. If there is no offset on the
* left, the closest on the right is the best.
* </p>
*
* @param newOffset
* the offset to llok at
* @param invocationLocation
* the invocation location
* @param bestOffset
* the current best offset
* @return -1 is returned if the given offset is not closer or the new best offset
*/
private static int computeBestOffset(int newOffset, int invocationLocation, int bestOffset) {
if (newOffset <= invocationLocation) {
if (bestOffset > invocationLocation) {
return newOffset; // closest was on the right, prefer on the
// left
} else if (bestOffset <= newOffset) {
return newOffset; // we are closer or equal
}
return -1; // further away
}
if (newOffset <= bestOffset)
return newOffset; // we are closer or equal
return -1; // further away
}
/*
* @seeorg.eclipse.jface.text.contentassist.ContentAssistant# possibleCompletionsClosed()
*/
@Override
protected void possibleCompletionsClosed() {
super.possibleCompletionsClosed();
restorePosition();
}
private void storePosition(int currOffset, int currLength) {
fPosition = new Position(currOffset, currLength);
}
private void restorePosition() {
if (fPosition != null && !fPosition.isDeleted() && fViewer.getDocument() != null) {
fViewer.setSelectedRange(fPosition.offset, fPosition.length);
fViewer.revealRange(fPosition.offset, fPosition.length);
}
fPosition = null;
}
/**
* Returns true if the last invoked completion was called with an updated offset.
*
* @return <code> true</code> if the last invoked completion was called with an updated offset.
*/
public boolean isUpdatedOffset() {
return fPosition != null;
}
/**
* Returns the annotations at the current offset
*
* @return the annotations at the offset
*/
public Annotation[] getAnnotationsAtOffset() {
return fCurrentAnnotations;
}
}