2 * Copyright (C) 2011 The Android Open Source Project
4 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.eclipse.org/org/documents/epl-v10.php
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.android.ide.eclipse.adt.internal.lint;
18 import com.android.ide.eclipse.adt.AdtConstants;
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.adt.AdtUtils;
21 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
22 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
23 import com.android.tools.lint.detector.api.Issue;
25 import org.eclipse.core.resources.IFile;
26 import org.eclipse.core.resources.IMarker;
27 import org.eclipse.core.resources.IResource;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.jface.dialogs.MessageDialog;
30 import org.eclipse.jface.preference.IPreferenceStore;
31 import org.eclipse.jface.text.IDocument;
32 import org.eclipse.jface.text.contentassist.ICompletionProposal;
33 import org.eclipse.jface.text.contentassist.IContextInformation;
34 import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
35 import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
36 import org.eclipse.jface.text.source.Annotation;
37 import org.eclipse.jface.text.source.ISourceViewer;
38 import org.eclipse.swt.graphics.Image;
39 import org.eclipse.swt.graphics.Point;
40 import org.eclipse.ui.IEditorPart;
41 import org.eclipse.ui.IMarkerResolution;
42 import org.eclipse.ui.IMarkerResolution2;
43 import org.eclipse.ui.IMarkerResolutionGenerator2;
44 import org.eclipse.ui.ISharedImages;
45 import org.eclipse.ui.PlatformUI;
47 import java.util.ArrayList;
48 import java.util.List;
51 * A quickfix and marker resolution for disabling lint checks, and any
52 * IDE specific implementations for fixing the warnings.
54 * I would really like for this quickfix to show up as a light bulb on top of the error
55 * icon in the editor, and I've spent a whole day trying to make it work. I did not
56 * succeed, but here are the steps I tried in case I want to pick up the work again
60 * The WST has some support for quick fixes, and I came across some forum posts
61 * referencing the ability to show light bulbs. However, it turns out that the
62 * quickfix support for annotations in WST is hardcoded to source validation
65 * I tried defining my own editor annotations, and customizing the icon directly
66 * by either setting an icon or using the image provider. This works fine
67 * if I make my marker be a new independent marker type. However, whenever I
68 * switch the marker type back to extend the "Problem" type, then the icon reverts
69 * back to the standard error icon and it ignores my custom settings.
70 * And if I switch away from the Problems marker type, then the errors no longer
71 * show up in the Problems view. (I also tried extending the JDT marker but that
74 * It looks like only JDT handles quickfix icons. It has a bunch of custom code
75 * to handle this, along with its own Annotation subclass used by the editor.
76 * I tried duplicating some of this by subclassing StructuredTextEditor, but
77 * it was evident that I'd have to pull in a *huge* amount of duplicated code to
78 * make this work, which seems risky given that all this is internal code that
79 * can change from one Eclipse version to the next.
81 * It looks like our best bet would be to reconsider whether these should show up
82 * in the Problems view; perhaps we should use a custom view for these. That would also
83 * make marker management more obvious.
85 public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssistProcessor {
86 /** Constructs a new {@link LintFixGenerator} */
87 public LintFixGenerator() {
90 // ---- Implements IMarkerResolutionGenerator2 ----
92 public boolean hasResolutions(IMarker marker) {
94 assert marker.getType().equals(AdtConstants.MARKER_LINT);
95 } catch (CoreException e) {
101 public IMarkerResolution[] getResolutions(IMarker marker) {
102 String id = marker.getAttribute(LintRunner.MARKER_CHECKID_PROPERTY,
104 IResource resource = marker.getResource();
105 return new IMarkerResolution[] {
106 new MoreInfoProposal(id, marker.getAttribute(IMarker.MESSAGE, null)),
107 new SuppressProposal(id, true /* all */),
108 // Not yet implemented
109 //new SuppressProposal(id, false),
110 new ClearMarkersProposal(resource, false /* all */),
111 new ClearMarkersProposal(resource, true /* all */),
115 // ---- Implements IQuickAssistProcessor ----
117 public String getErrorMessage() {
118 return "Disable Lint Error";
121 public boolean canFix(Annotation annotation) {
125 public boolean canAssist(IQuickAssistInvocationContext invocationContext) {
129 public ICompletionProposal[] computeQuickAssistProposals(
130 IQuickAssistInvocationContext invocationContext) {
131 ISourceViewer sourceViewer = invocationContext.getSourceViewer();
132 AndroidXmlEditor editor = AndroidXmlEditor.getAndroidXmlEditor(sourceViewer);
133 if (editor != null) {
134 IFile file = editor.getInputFile();
135 IDocument document = sourceViewer.getDocument();
136 List<IMarker> markers = AdtUtils.findMarkersOnLine(AdtConstants.MARKER_LINT,
137 file, document, invocationContext.getOffset());
138 List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
139 if (markers.size() > 0) {
140 for (IMarker marker : markers) {
141 String id = marker.getAttribute(LintRunner.MARKER_CHECKID_PROPERTY,
143 // TODO: Allow for more than one fix?
144 ICompletionProposal fix = LintFix.getFix(id, marker);
149 String message = marker.getAttribute(IMarker.MESSAGE, null);
150 proposals.add(new MoreInfoProposal(id, message));
152 proposals.add(new SuppressProposal(id, true /* all */));
153 // Not yet implemented
154 //proposals.add(new SuppressProposal(id, false));
156 proposals.add(new ClearMarkersProposal(file, false /* all */));
157 proposals.add(new ClearMarkersProposal(file, true /* all */));
160 if (proposals.size() > 0) {
161 return proposals.toArray(new ICompletionProposal[proposals.size()]);
169 * Suppress the given detector, and rerun the checks on the file
171 * @param id the id of the detector to be suppressed
173 public static void suppressDetector(String id) {
175 IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
176 String value = store.getString(AdtPrefs.PREFS_DISABLED_ISSUES);
177 assert value == null || !value.contains(id);
178 if (value == null || value.length() == 0) {
181 value = value + ',' + id;
183 store.setValue(AdtPrefs.PREFS_DISABLED_ISSUES, value);
185 // Rerun analysis on the current file to remove this and related markers.
186 // TODO: if mGlobal, rerun on whole project?
187 IEditorPart activeEditor = AdtUtils.getActiveEditor();
188 if (activeEditor instanceof AndroidXmlEditor) {
189 AndroidXmlEditor editor = (AndroidXmlEditor) activeEditor;
190 LintRunner.startLint(editor.getInputFile(), editor.getStructuredDocument());
194 private static class SuppressProposal implements ICompletionProposal, IMarkerResolution2 {
195 private final String mId;
196 private final boolean mGlobal;
198 public SuppressProposal(String check, boolean global) {
204 private void perform() {
205 suppressDetector(mId);
208 public String getDisplayString() {
209 return mGlobal ? "Disable Check" : "Disable Check in this file only";
212 // ---- Implements MarkerResolution2 ----
214 public String getLabel() {
215 return getDisplayString();
218 public void run(IMarker marker) {
222 public String getDescription() {
223 return getAdditionalProposalInfo();
226 // ---- Implements ICompletionProposal ----
228 public void apply(IDocument document) {
232 public Point getSelection(IDocument document) {
236 public String getAdditionalProposalInfo() {
237 StringBuilder sb = new StringBuilder(200);
239 sb.append("Suppresses this type of lint warning in all files.");
241 sb.append("Suppresses this type of lint warning in the current file only.");
243 sb.append("<br><br>"); //$NON-NLS-1$
244 sb.append("You can re-enable checks from the \"Android > Lint Error Checking\" preference page.");
246 return sb.toString();
249 public Image getImage() {
250 ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
251 return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
254 public IContextInformation getContextInformation() {
259 private static class ClearMarkersProposal implements ICompletionProposal, IMarkerResolution2 {
260 private final boolean mGlobal;
261 private final IResource mResource;
263 public ClearMarkersProposal(IResource resource, boolean global) {
264 mResource = resource;
268 private void perform() {
269 IResource resource = mGlobal ? mResource.getProject() : mResource;
270 LintEclipseContext.clearMarkers(resource);
273 public String getDisplayString() {
274 return mGlobal ? "Clear All Lint Markers" : "Clear Markers in This File Only";
277 // ---- Implements MarkerResolution2 ----
279 public String getLabel() {
280 return getDisplayString();
283 public void run(IMarker marker) {
287 public String getDescription() {
288 return getAdditionalProposalInfo();
291 // ---- Implements ICompletionProposal ----
293 public void apply(IDocument document) {
297 public Point getSelection(IDocument document) {
301 public String getAdditionalProposalInfo() {
302 StringBuilder sb = new StringBuilder(200);
304 sb.append("Clears all lint warning markers from the project.");
306 sb.append("Clears all lint warnings from this file.");
308 sb.append("<br><br>"); //$NON-NLS-1$
309 sb.append("This temporarily hides the problem, but does not suppress it. " +
310 "Running Lint again can bring the error back.");
311 if (AdtPrefs.getPrefs().isLintOnSave()) {
313 sb.append("This will happen the next time the file is saved since lint-on-save " +
314 "is enabled. You can turn this off in the \"Lint Error Checking\" " +
318 return sb.toString();
321 public Image getImage() {
322 ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
323 return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
326 public IContextInformation getContextInformation() {
331 private static class MoreInfoProposal implements ICompletionProposal, IMarkerResolution2 {
332 private final String mId;
333 private final String mMessage;
335 public MoreInfoProposal(String id, String message) {
340 private void perform() {
341 Issue issue = LintEclipseContext.getRegistry().getIssue(mId);
342 assert issue != null : mId;
344 StringBuilder sb = new StringBuilder(300);
346 sb.append('\n').append('\n');
347 sb.append("Issue Explanation:");
349 if (issue.getExplanation() != null) {
351 sb.append(issue.getExplanation());
353 sb.append(issue.getDescription());
356 if (issue.getMoreInfo() != null) {
357 sb.append('\n').append('\n');
358 sb.append("More Information: ");
359 sb.append(issue.getMoreInfo());
362 MessageDialog.openInformation(AdtPlugin.getDisplay().getActiveShell(), "More Info",
366 public String getDisplayString() {
367 return "Explain Issue";
370 // ---- Implements MarkerResolution2 ----
372 public String getLabel() {
373 return getDisplayString();
376 public void run(IMarker marker) {
380 public String getDescription() {
381 return getAdditionalProposalInfo();
384 // ---- Implements ICompletionProposal ----
386 public void apply(IDocument document) {
390 public Point getSelection(IDocument document) {
394 public String getAdditionalProposalInfo() {
395 return "Provides more information about this issue";
398 public Image getImage() {
399 ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
400 return sharedImages.getImage(ISharedImages.IMG_OBJS_INFO_TSK);
403 public IContextInformation getContextInformation() {