1 /*******************************************************************************
2 * Copyright (c) 2000, 2012 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.ui.text.correction;
13 import java.util.ArrayList;
14 import java.util.Iterator;
16 import org.eclipse.swt.graphics.Point;
17 import org.eclipse.swt.widgets.Shell;
19 import org.eclipse.core.runtime.Assert;
20 import org.eclipse.core.runtime.IStatus;
22 import org.eclipse.jface.text.BadLocationException;
23 import org.eclipse.jface.text.DefaultInformationControl;
24 import org.eclipse.jface.text.IDocument;
25 import org.eclipse.jface.text.IInformationControl;
26 import org.eclipse.jface.text.IInformationControlCreator;
27 import org.eclipse.jface.text.IRegion;
28 import org.eclipse.jface.text.ITextViewer;
29 import org.eclipse.jface.text.Position;
30 import org.eclipse.jface.text.contentassist.ContentAssistEvent;
31 import org.eclipse.jface.text.contentassist.ICompletionListener;
32 import org.eclipse.jface.text.contentassist.ICompletionProposal;
33 import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
34 import org.eclipse.jface.text.quickassist.QuickAssistAssistant;
35 import org.eclipse.jface.text.source.Annotation;
36 import org.eclipse.jface.text.source.IAnnotationModel;
37 import org.eclipse.jface.text.source.ISourceViewer;
39 import org.eclipse.ui.IEditorPart;
40 import org.eclipse.ui.IWorkbenchPreferenceConstants;
41 import org.eclipse.ui.PlatformUI;
42 import org.eclipse.ui.keys.IBindingService;
44 import org.eclipse.ui.texteditor.IDocumentProvider;
45 import org.eclipse.ui.texteditor.ITextEditor;
46 import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
48 import org.eclipse.jdt.core.ICompilationUnit;
49 import org.eclipse.jdt.core.IJavaElement;
51 import org.eclipse.jdt.internal.corext.util.Messages;
53 import org.eclipse.jdt.ui.JavaUI;
54 import org.eclipse.jdt.ui.SharedASTProvider;
55 import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
56 import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
58 import org.eclipse.jdt.internal.ui.JavaPlugin;
61 public class JavaCorrectionAssistant extends QuickAssistAssistant {
63 private ITextViewer fViewer;
64 private ITextEditor fEditor;
65 private Position fPosition;
66 private Annotation[] fCurrentAnnotations;
68 private QuickAssistLightBulbUpdater fLightBulbUpdater;
69 private boolean fIsCompletionActive;
70 private boolean fIsProblemLocationAvailable;
74 * Constructor for JavaCorrectionAssistant.
75 * @param editor the editor
77 public JavaCorrectionAssistant(ITextEditor editor) {
79 Assert.isNotNull(editor);
82 JavaCorrectionProcessor processor= new JavaCorrectionProcessor(this);
84 setQuickAssistProcessor(processor);
85 enableColoredLabels(PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS));
87 setInformationControlCreator(getInformationControlCreator());
89 addCompletionListener(new ICompletionListener() {
90 public void assistSessionEnded(ContentAssistEvent event) {
91 fIsCompletionActive= false;
94 public void assistSessionStarted(ContentAssistEvent event) {
95 fIsCompletionActive= true;
98 public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
103 public IEditorPart getEditor() {
108 private IInformationControlCreator getInformationControlCreator() {
109 return new IInformationControlCreator() {
110 public IInformationControl createInformationControl(Shell parent) {
111 return new DefaultInformationControl(parent, JavaPlugin.getAdditionalInfoAffordanceString());
117 * @see org.eclipse.jface.text.contentassist.IContentAssistant#install(org.eclipse.jface.text.ITextViewer)
120 public void install(ISourceViewer sourceViewer) {
121 super.install(sourceViewer);
122 fViewer= sourceViewer;
124 fLightBulbUpdater= new QuickAssistLightBulbUpdater(fEditor, sourceViewer);
125 fLightBulbUpdater.install();
131 * @see org.eclipse.jface.text.contentassist.ContentAssistant#uninstall()
134 public void uninstall() {
135 if (fLightBulbUpdater != null) {
136 fLightBulbUpdater.uninstall();
137 fLightBulbUpdater= null;
143 * @see org.eclipse.jface.text.quickassist.QuickAssistAssistant#showPossibleQuickAssists()
148 * Show completions at caret position. If current
149 * position does not contain quick fixes look for
150 * next quick fix on same line by moving from left
151 * to right and restarting at end of line if the
152 * beginning of the line is reached.
154 * @see IQuickAssistAssistant#showPossibleQuickAssists()
157 public String showPossibleQuickAssists() {
158 boolean isReinvoked= false;
159 fIsProblemLocationAvailable= false;
161 if (fIsCompletionActive) {
162 if (isUpdatedOffset()) {
166 fIsProblemLocationAvailable= true;
171 fCurrentAnnotations= null;
173 if (fViewer == null || fViewer.getDocument() == null)
174 // Let superclass deal with this
175 return super.showPossibleQuickAssists();
178 ArrayList<Annotation> resultingAnnotations= new ArrayList<Annotation>(20);
180 Point selectedRange= fViewer.getSelectedRange();
181 int currOffset= selectedRange.x;
182 int currLength= selectedRange.y;
183 boolean goToClosest= (currLength == 0) && !isReinvoked;
185 int newOffset= collectQuickFixableAnnotations(fEditor, currOffset, goToClosest, resultingAnnotations);
186 if (newOffset != currOffset) {
187 storePosition(currOffset, currLength);
188 fViewer.setSelectedRange(newOffset, 0);
189 fViewer.revealRange(newOffset, 0);
190 fIsProblemLocationAvailable= true;
191 if (fIsCompletionActive) {
195 } catch (BadLocationException e) {
198 fCurrentAnnotations= resultingAnnotations.toArray(new Annotation[resultingAnnotations.size()]);
200 return super.showPossibleQuickAssists();
204 private static IRegion getRegionOfInterest(ITextEditor editor, int invocationLocation) throws BadLocationException {
205 IDocumentProvider documentProvider= editor.getDocumentProvider();
206 if (documentProvider == null) {
209 IDocument document= documentProvider.getDocument(editor.getEditorInput());
210 if (document == null) {
213 return document.getLineInformationOfOffset(invocationLocation);
216 public static int collectQuickFixableAnnotations(ITextEditor editor, int invocationLocation, boolean goToClosest, ArrayList<Annotation> resultingAnnotations) throws BadLocationException {
217 IAnnotationModel model= JavaUI.getDocumentProvider().getAnnotationModel(editor.getEditorInput());
219 return invocationLocation;
222 ensureUpdatedAnnotations(editor);
224 Iterator<Annotation> iter= model.getAnnotationIterator();
226 IRegion lineInfo= getRegionOfInterest(editor, invocationLocation);
227 if (lineInfo == null) {
228 return invocationLocation;
230 int rangeStart= lineInfo.getOffset();
231 int rangeEnd= rangeStart + lineInfo.getLength();
233 ArrayList<Annotation> allAnnotations= new ArrayList<Annotation>();
234 ArrayList<Position> allPositions= new ArrayList<Position>();
235 int bestOffset= Integer.MAX_VALUE;
236 while (iter.hasNext()) {
237 Annotation annot= iter.next();
238 if (JavaCorrectionProcessor.isQuickFixableType(annot)) {
239 Position pos= model.getPosition(annot);
240 if (pos != null && isInside(pos.offset, rangeStart, rangeEnd)) { // inside our range?
241 allAnnotations.add(annot);
242 allPositions.add(pos);
243 bestOffset= processAnnotation(annot, pos, invocationLocation, bestOffset);
247 if (bestOffset == Integer.MAX_VALUE) {
248 return invocationLocation;
250 for (int i= 0; i < allPositions.size(); i++) {
251 Position pos= allPositions.get(i);
252 if (isInside(bestOffset, pos.offset, pos.offset + pos.length)) {
253 resultingAnnotations.add(allAnnotations.get(i));
258 while (iter.hasNext()) {
259 Annotation annot= iter.next();
260 if (JavaCorrectionProcessor.isQuickFixableType(annot)) {
261 Position pos= model.getPosition(annot);
262 if (pos != null && isInside(invocationLocation, pos.offset, pos.offset + pos.length)) {
263 resultingAnnotations.add(annot);
267 return invocationLocation;
271 private static void ensureUpdatedAnnotations(ITextEditor editor) {
272 Object inputElement= editor.getEditorInput().getAdapter(IJavaElement.class);
273 if (inputElement instanceof ICompilationUnit) {
274 SharedASTProvider.getAST((ICompilationUnit) inputElement, SharedASTProvider.WAIT_ACTIVE_ONLY, null);
278 private static int processAnnotation(Annotation annot, Position pos, int invocationLocation, int bestOffset) {
279 int posBegin= pos.offset;
280 int posEnd= posBegin + pos.length;
281 if (isInside(invocationLocation, posBegin, posEnd) && JavaCorrectionProcessor.hasCorrections(annot)) { // covers invocation location?
282 return invocationLocation;
283 } else if (bestOffset != invocationLocation) {
284 int newClosestPosition= computeBestOffset(posBegin, invocationLocation, bestOffset);
285 if (newClosestPosition != -1) {
286 if (newClosestPosition != bestOffset) { // new best
287 if (JavaCorrectionProcessor.hasCorrections(annot)) { // only jump to it if there are proposals
288 return newClosestPosition;
297 private static boolean isInside(int offset, int start, int end) {
298 return offset == start || offset == end || (offset > start && offset < end); // make sure to handle 0-length ranges
302 * Computes and returns the invocation offset given a new
303 * position, the initial offset and the best invocation offset
306 * The closest offset to the left of the initial offset is the
307 * best. If there is no offset on the left, the closest on the
308 * right is the best.</p>
309 * @param newOffset the offset to llok at
310 * @param invocationLocation the invocation location
311 * @param bestOffset the current best offset
312 * @return -1 is returned if the given offset is not closer or the new best offset
314 private static int computeBestOffset(int newOffset, int invocationLocation, int bestOffset) {
315 if (newOffset <= invocationLocation) {
316 if (bestOffset > invocationLocation) {
317 return newOffset; // closest was on the right, prefer on the left
318 } else if (bestOffset <= newOffset) {
319 return newOffset; // we are closer or equal
321 return -1; // further away
324 if (newOffset <= bestOffset)
325 return newOffset; // we are closer or equal
327 return -1; // further away
331 * @see org.eclipse.jface.text.contentassist.ContentAssistant#possibleCompletionsClosed()
334 protected void possibleCompletionsClosed() {
335 super.possibleCompletionsClosed();
339 private void storePosition(int currOffset, int currLength) {
340 fPosition= new Position(currOffset, currLength);
343 private void restorePosition() {
344 if (fPosition != null && !fPosition.isDeleted() && fViewer.getDocument() != null) {
345 fViewer.setSelectedRange(fPosition.offset, fPosition.length);
346 fViewer.revealRange(fPosition.offset, fPosition.length);
352 * Returns true if the last invoked completion was called with an updated offset.
353 * @return <code> true</code> if the last invoked completion was called with an updated offset.
355 public boolean isUpdatedOffset() {
356 return fPosition != null;
360 * @return <code>true</code> if a problem exist on the current line and the completion was not invoked at the problem location
363 public boolean isProblemLocationAvailable() {
364 return fIsProblemLocationAvailable;
368 * Returns the annotations at the current offset
369 * @return the annotations at the offset
371 public Annotation[] getAnnotationsAtOffset() {
372 return fCurrentAnnotations;
375 public void generated_3649133358608219912(final JavaCorrectionProcessor javacorrectionprocessor) {
376 addCompletionListener(new ICompletionListener() {
378 public void assistSessionEnded(ContentAssistEvent event) {
379 setStatusLineVisible(false);
382 public void assistSessionStarted(ContentAssistEvent event) {
383 setStatusLineVisible(true);
384 setStatusMessage(javacorrectionprocessor.getJumpHintStatusLineMessage());
387 public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
388 if (proposal instanceof IStatusLineProposal) {
389 IStatusLineProposal statusLineProposal= (IStatusLineProposal)proposal;
390 String message= statusLineProposal.getStatusMessage();
391 if (message != null) {
392 setStatusMessage(message);
396 setStatusMessage(javacorrectionprocessor.getJumpHintStatusLineMessage());
399 private String getJumpHintStatusLineMessage() {
400 if (isUpdatedOffset()) {
401 String key= javacorrectionprocessor.getQuickAssistBinding();
403 return CorrectionMessages.JavaCorrectionProcessor_go_to_original_using_menu;
405 return Messages.format(CorrectionMessages.JavaCorrectionProcessor_go_to_original_using_key, key);
406 } else if (isProblemLocationAvailable()) {
407 String key= javacorrectionprocessor.getQuickAssistBinding();
409 return CorrectionMessages.JavaCorrectionProcessor_go_to_closest_using_menu;
411 return Messages.format(CorrectionMessages.JavaCorrectionProcessor_go_to_closest_using_key, key);
413 return ""; //$NON-NLS-1$
416 private String getQuickAssistBinding() {
417 final IBindingService bindingSvc= (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
418 return bindingSvc.getBestActiveBindingFormattedFor(ITextEditorActionDefinitionIds.QUICK_ASSIST);
423 public ICompletionProposal[] generated_1438141222580661224(JavaCorrectionProcessor javacorrectionprocessor, ISourceViewer viewer, int documentOffset) {
424 IEditorPart part= getEditor();
426 ICompilationUnit cu= JavaUI.getWorkingCopyManager().getWorkingCopy(part.getEditorInput());
427 IAnnotationModel model= JavaUI.getDocumentProvider().getAnnotationModel(part.getEditorInput());
429 AssistContext context= null;
431 int length= viewer != null ? viewer.getSelectedRange().y : 0;
432 context= new AssistContext(cu, viewer, part, documentOffset, length);
435 Annotation[] annotations= getAnnotationsAtOffset();
437 javacorrectionprocessor.fErrorMessage= null;
439 ICompletionProposal[] res= null;
440 if (model != null && context != null && annotations != null) {
441 ArrayList<IJavaCompletionProposal> proposals= new ArrayList<IJavaCompletionProposal>(10);
442 IStatus status= JavaCorrectionProcessor.collectProposals(context, model, annotations, true, !isUpdatedOffset(), proposals);
443 res= proposals.toArray(new ICompletionProposal[proposals.size()]);
444 if (!status.isOK()) {
445 javacorrectionprocessor.fErrorMessage= status.getMessage();
446 JavaPlugin.log(status);
452 public IQuickAssistAssistant generated_4265095813573642237(JavaSourceViewerConfiguration javasourceviewerconfiguration) {
453 setRestoreCompletionProposalSize(javasourceviewerconfiguration.getSettings("quick_assist_proposal_size")); //$NON-NLS-1$