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.List;
50 * A quickfix and marker resolution for disabling lint checks, and any
51 * IDE specific implementations for fixing the warnings.
53 * I would really like for this quickfix to show up as a light bulb on top of the error
54 * icon in the editor, and I've spent a whole day trying to make it work. I did not
55 * succeed, but here are the steps I tried in case I want to pick up the work again
59 * The WST has some support for quick fixes, and I came across some forum posts
60 * referencing the ability to show light bulbs. However, it turns out that the
61 * quickfix support for annotations in WST is hardcoded to source validation
64 * I tried defining my own editor annotations, and customizing the icon directly
65 * by either setting an icon or using the image provider. This works fine
66 * if I make my marker be a new independent marker type. However, whenever I
67 * switch the marker type back to extend the "Problem" type, then the icon reverts
68 * back to the standard error icon and it ignores my custom settings.
69 * And if I switch away from the Problems marker type, then the errors no longer
70 * show up in the Problems view. (I also tried extending the JDT marker but that
73 * It looks like only JDT handles quickfix icons. It has a bunch of custom code
74 * to handle this, along with its own Annotation subclass used by the editor.
75 * I tried duplicating some of this by subclassing StructuredTextEditor, but
76 * it was evident that I'd have to pull in a *huge* amount of duplicated code to
77 * make this work, which seems risky given that all this is internal code that
78 * can change from one Eclipse version to the next.
80 * It looks like our best bet would be to reconsider whether these should show up
81 * in the Problems view; perhaps we should use a custom view for these. That would also
82 * make marker management more obvious.
84 public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssistProcessor {
85 /** Constructs a new {@link LintFixGenerator} */
86 public LintFixGenerator() {
89 // ---- Implements IMarkerResolutionGenerator2 ----
91 public boolean hasResolutions(IMarker marker) {
93 assert marker.getType().equals(AdtConstants.MARKER_LINT);
94 } catch (CoreException e) {
100 public IMarkerResolution[] getResolutions(IMarker marker) {
101 String id = marker.getAttribute(LintRunner.MARKER_CHECKID_PROPERTY,
103 IResource resource = marker.getResource();
104 return new IMarkerResolution[] {
105 new MoreInfoProposal(id, marker.getAttribute(IMarker.MESSAGE, null)),
106 new SuppressProposal(id, true /* all */),
107 // Not yet implemented
108 //new SuppressProposal(id, false),
109 new ClearMarkersProposal(resource, false /* all */),
110 new ClearMarkersProposal(resource, true /* all */),
114 // ---- Implements IQuickAssistProcessor ----
116 public String getErrorMessage() {
117 return "Disable Lint Error";
120 public boolean canFix(Annotation annotation) {
124 public boolean canAssist(IQuickAssistInvocationContext invocationContext) {
128 public ICompletionProposal[] computeQuickAssistProposals(
129 IQuickAssistInvocationContext invocationContext) {
130 ISourceViewer sourceViewer = invocationContext.getSourceViewer();
131 AndroidXmlEditor editor = AndroidXmlEditor.getAndroidXmlEditor(sourceViewer);
132 if (editor != null) {
133 IFile file = editor.getInputFile();
134 IDocument document = sourceViewer.getDocument();
135 List<IMarker> markers = AdtUtils.findMarkersOnLine(AdtConstants.MARKER_LINT,
136 file, document, invocationContext.getOffset());
137 if (markers.size() > 0) {
138 for (IMarker marker : markers) {
139 String id = marker.getAttribute(LintRunner.MARKER_CHECKID_PROPERTY,
141 return new ICompletionProposal[] {
142 new MoreInfoProposal(id, marker.getAttribute(IMarker.MESSAGE, null)),
143 new SuppressProposal(id, true /* all */),
144 // Not yet implemented
145 //new SuppressProposal(id, false),
146 new ClearMarkersProposal(file, false /* all */),
147 new ClearMarkersProposal(file, true /* all */),
157 * Suppress the given detector, and rerun the checks on the file
159 * @param id the id of the detector to be suppressed
161 public static void suppressDetector(String id) {
163 IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
164 String value = store.getString(AdtPrefs.PREFS_DISABLED_ISSUES);
165 assert value == null || !value.contains(id);
166 if (value == null || value.length() == 0) {
169 value = value + ',' + id;
171 store.setValue(AdtPrefs.PREFS_DISABLED_ISSUES, value);
173 // Rerun analysis on the current file to remove this and related markers.
174 // TODO: if mGlobal, rerun on whole project?
175 IEditorPart activeEditor = AdtUtils.getActiveEditor();
176 if (activeEditor instanceof AndroidXmlEditor) {
177 AndroidXmlEditor editor = (AndroidXmlEditor) activeEditor;
178 LintRunner.startLint(editor.getInputFile(), editor.getStructuredDocument());
182 private static class SuppressProposal implements ICompletionProposal, IMarkerResolution2 {
183 private final String mId;
184 private final boolean mGlobal;
186 public SuppressProposal(String check, boolean global) {
192 private void perform() {
193 suppressDetector(mId);
196 public String getDisplayString() {
197 return mGlobal ? "Disable Check" : "Disable Check in this file only";
200 // ---- Implements MarkerResolution2 ----
202 public String getLabel() {
203 return getDisplayString();
206 public void run(IMarker marker) {
210 public String getDescription() {
211 return getAdditionalProposalInfo();
214 // ---- Implements ICompletionProposal ----
216 public void apply(IDocument document) {
220 public Point getSelection(IDocument document) {
224 public String getAdditionalProposalInfo() {
225 StringBuilder sb = new StringBuilder(200);
227 sb.append("Suppresses this type of lint warning in all files.");
229 sb.append("Suppresses this type of lint warning in the current file only.");
231 sb.append("<br><br>"); //$NON-NLS-1$
232 sb.append("You can re-enable checks from the \"Android > Lint Error Checking\" preference page.");
234 return sb.toString();
237 public Image getImage() {
238 ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
239 return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
242 public IContextInformation getContextInformation() {
247 private static class ClearMarkersProposal implements ICompletionProposal, IMarkerResolution2 {
248 private final boolean mGlobal;
249 private final IResource mResource;
251 public ClearMarkersProposal(IResource resource, boolean global) {
252 mResource = resource;
256 private void perform() {
257 IResource resource = mGlobal ? mResource.getProject() : mResource;
258 LintEclipseContext.clearMarkers(resource);
261 public String getDisplayString() {
262 return mGlobal ? "Clear All Lint Markers" : "Clear Markers in This File Only";
265 // ---- Implements MarkerResolution2 ----
267 public String getLabel() {
268 return getDisplayString();
271 public void run(IMarker marker) {
275 public String getDescription() {
276 return getAdditionalProposalInfo();
279 // ---- Implements ICompletionProposal ----
281 public void apply(IDocument document) {
285 public Point getSelection(IDocument document) {
289 public String getAdditionalProposalInfo() {
290 StringBuilder sb = new StringBuilder(200);
292 sb.append("Clears all lint warning markers from the project.");
294 sb.append("Clears all lint warnings from this file.");
296 sb.append("<br><br>"); //$NON-NLS-1$
297 sb.append("This temporarily hides the problem, but does not suppress it. " +
298 "Running Lint again can bring the error back.");
299 if (AdtPrefs.getPrefs().isLintOnSave()) {
301 sb.append("This will happen the next time the file is saved since lint-on-save " +
302 "is enabled. You can turn this off in the \"Lint Error Checking\" " +
306 return sb.toString();
309 public Image getImage() {
310 ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
311 return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
314 public IContextInformation getContextInformation() {
319 private static class MoreInfoProposal implements ICompletionProposal, IMarkerResolution2 {
320 private final String mId;
321 private final String mMessage;
323 public MoreInfoProposal(String id, String message) {
328 private void perform() {
329 Issue issue = LintEclipseContext.getRegistry().getIssue(mId);
330 assert issue != null : mId;
332 StringBuilder sb = new StringBuilder(300);
334 sb.append('\n').append('\n');
335 sb.append("Issue Explanation:");
337 if (issue.getExplanation() != null) {
339 sb.append(issue.getExplanation());
341 sb.append(issue.getDescription());
344 if (issue.getMoreInfo() != null) {
345 sb.append('\n').append('\n');
346 sb.append("More Information: ");
347 sb.append(issue.getMoreInfo());
350 MessageDialog.openInformation(AdtPlugin.getDisplay().getActiveShell(), "More Info",
354 public String getDisplayString() {
355 return "Explain Issue";
358 // ---- Implements MarkerResolution2 ----
360 public String getLabel() {
361 return getDisplayString();
364 public void run(IMarker marker) {
368 public String getDescription() {
369 return getAdditionalProposalInfo();
372 // ---- Implements ICompletionProposal ----
374 public void apply(IDocument document) {
378 public Point getSelection(IDocument document) {
382 public String getAdditionalProposalInfo() {
383 return "Provides more information about this issue";
386 public Image getImage() {
387 ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
388 return sharedImages.getImage(ISharedImages.IMG_OBJS_INFO_TSK);
391 public IContextInformation getContextInformation() {