How can I add an Eclipse Quick Fix for a custom Java marker?

柔情痞子 提交于 2019-12-03 05:59:09

问题


I'd like to report custom problems for Java files to the Problems View of Eclipse and provide Quick Fixes for them.

The standard way to do is to use the extension point org.eclipse.core.resources.markers to declare a custom marker and add markers by calling org.eclipse.core.resources.IResource.createMarker(String). Then, one can use the extension point org.eclipse.ui.ide.markerResolution to provide a Quick Fix for the custom markers.

The above approach is a language-independent method of creating and resolving resource markers. The downside is that I have to write some boilerplate code to resolve my custom Java problems. Instead, I'd like to be reuse IQuickFixProcessor. That is, I'd like to resolve my custom Java markers using the extension point org.eclipse.jdt.ui.quickFixProcessors. Using this extension point, I no longer have to parse the Java file in which the marker is found, I don't have to build the bindings and find the AST node covering the marker. If I don't reuse org.eclipse.jdt.internal.ui.text.correction.CorrectionMarkerResolutionGenerator and its dependencies, I'll end up duplicating most of it.

How can I provide Quick Fixes for my custom Java markers using the JDT infrastructure?

Attempt 1:

I defined my custom marker as follows:

<extension
  id="custom.marker"
  name="Custom Java Problem"
  point="org.eclipse.core.resources.markers">
    <super type="org.eclipse.jdt.core.problem"/>
    <super type="org.eclipse.core.resources.problemmarker"/>
    <super type="org.eclipse.core.resources.textmarker"/>
    <persistent value="true"/>
</extension>

Then, I added instances of the above marker by invoking method IResource.createMarker("custom.marker").

Next, I defined a custom Quick Fix processor.

<extension
  point="org.eclipse.jdt.ui.quickFixProcessors">
  <quickFixProcessor
    class="quickfixes.CustomQuickFixProcessor"
    id="quickfixes.quickFixProcessor">
  </quickFixProcessor>
</extension>

My custom markers show up in the Problems View of Eclipse, but when I right-click on a custom problem, the Quick Fix menu item is disabled.

Attempt 2:

I repalced IMarker marker = resource.createMarker("custom.marker"); by IMarker marker = resource.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);. As a result of this change, when I right-click on a custom problem in the Problems View, the Quick Fix menu item becomes available, but, when I select it, a dialog pops up that says there is no fix available for the selected problem. However, I verified that CustomQuickFixProcessor.hasCorrections(ICompilationUnit, int) gets called and returns true, but, CustomQuickFixProcessor.getCorrections(IInvocationContext, IProblemLocation[]) doesn't get invoked.

Attempt 3:

Attempt 3 is a continuation of Attempt 2. I set the IJavaModelMarker.ID of the custom marker as follows:

marker.setAttribute(IJavaModelMarker.ID, IProblem.ExternalProblemFixable);

Consequently, CustomQuickFixProcessor.getCorrections gets called when I hover over my custom marker in the editor or click on the light-build on the left margin of the Java editor. However, when I select the marker in the Problems View, right-click on the marker, and select the Quick Fix menu item, CustomQuickFixProcessor.getCorrections doesn't get called and a dialog appears saying that no Quick Fixes are available.

I ran JDT in debug mode to see why it doesn't call CustomQuickFixProcessor.getCorrections when I invoke the Quick Fix from the Problems View. It turned out CorrectionMarkerResolutionGenerator.internalGetResolutions(IMarker) finds no resolutions because CorrectionMarkerResolutionGenerator.hasProblem (context.getASTRoot().getProblems(), location) doesn't find the custom problem in the AST of the compilation unit. I'm not sure how to associate my custom markers with the AST of the compilation unit.


回答1:


I got this working, with great help from this post and the debugger. Here is what you have to do:

Creating the marker

plugin.xml

Declare the marker so it extends these three existing markers (I think all are necessary)

<extension
       id="mymarker"
       name="My Problem"
       point="org.eclipse.core.resources.markers">
    <super
          type="org.eclipse.jdt.core.problem">
    </super>
    <super
          type="org.eclipse.core.resources.problemmarker">
    </super>
    <super
          type="org.eclipse.core.resources.textmarker">
    </super>
    <persistent
          value="true">
    </persistent>
 </extension>

Java

When you create the marker it is important that you set the IJavaModelMarker.ID field, and I think all the other fields listed here as well.

// Must match the "id" attribute from plugin.xml
String MY_MARKER_ID = "com.example.my.plugin.mymarker"
// Must not be -1 or any of the values in org.eclipse.jdt.core.compiler.IProblem
int MY_JDT_PROBLEM_ID = 1234

// ....
IMarker marker = resource.createMarker(MY_MARKER_ID);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING);
marker.setAttribute(IMarker.MESSAGE, msg);
marker.setAttribute(IMarker.CHAR_START, start);
marker.setAttribute(IMarker.CHAR_END, end);
marker.setAttribute(IJavaModelMarker.ID, MY_JDT_PROBLEM_ID);

Creating the QuickFixProcessor

plugin.xml

First declare it in plugin.xml. Be sure your declare the right id in handledMarkerTypes

<extension
      point="org.eclipse.jdt.ui.quickFixProcessors">
   <quickFixProcessor
         class="com.example.my.plugin.ui.MyQuickFixProcessor"
         id="org.eclipse.jdt.ui.text.correction.QuickFixProcessor"
         name="My Quick Fix Processor">
      <handledMarkerTypes>
         <markerType
               id="com.example.my.plugin.mymarker">
         </markerType>
      </handledMarkerTypes>
   </quickFixProcessor>
</extension>

Java

Here is the basic skeleton for the quick fix processor. Note that it is important to check that locations actually has contents.

If you make your own marker type (as described above) I think you can just hard-code hasCorrections to return true. But to be save and to follow the convention check that it matches your jdt problem id.

public class MyQuickFixProcessor implements IQuickFixProcessor {

  @Override
  public IJavaCompletionProposal[] getCorrections(IInvocationContext context, IProblemLocation[] locations) throws CoreException {
    if (locations == null || locations.length == 0) {
      // https://bugs.eclipse.org/444120 Eclipse can call this method without
      // any locations, if a quick fix is requested without any problems.
      return null;
    }

    IJavaCompletionProposal[] proposals = ...
    //...
    return proposals;
  }

  @Override
  public boolean hasCorrections(ICompilationUnit unit, int problemId) {
    return problemId == MY_JDT_PROBLEM_ID;
  }
}

Finding a good JDT ID

You need a MY_JDT_PROBLEM_ID that is unique! Run the code below, to print all the current ID's defined in IProblem. Pick a sizeable range in these numbers, and select you ID in that range.

Field[] fields = org.eclipse.jdt.core.compiler.IProblem.class.getFields();
List<Integer> ints = new ArrayList<>();
for (Field field : fields) {
  ints.add(field.getInt(null));
}
sort(ints);
for (Integer integer : ints) {
  System.out.printf("%16d %16o %16x%n", integer, integer, integer);
}

I hope I have remembered everything. Good Luck.




回答2:


Your Attempt 1 will not work, reason:

JDT UI defines the following extension.

   <extension
         point="org.eclipse.ui.ide.markerResolution">
      <markerResolutionGenerator
            markerType="org.eclipse.jdt.core.problem"
            class="org.eclipse.jdt.internal.ui.text.correction.CorrectionMarkerResolutionGenerator">
      </markerResolutionGenerator>

i.e. the quick fixes coming from JDT will work only for markerType="org.eclipse.jdt.core.problem".

Attempt 2 : Even in JDT UI implementation there are cases, when QuickFixProcessor#hasCorrections(...) returns true but QuickFixProcessor#getCorrections(...) may not return a fix. This is because we always return true or false for a particular marker. However, all instances of a particular marker may not be 'fixable'. Maybe you run into something similar ?

Attempt 3 : What all attributes are you setting on the marker? Since CorrectionMarkerResolutionGenerator.hasProblem(...) checks for it, you would need to set at least IMarker.CHAR_START attribute. Have a look at org.eclipse.jdt.internal.core.eval.RequestorWrapper.acceptProblem(CategorizedProblem, char[], int), this is where markers in JDT Core are created.



来源:https://stackoverflow.com/questions/13436440/how-can-i-add-an-eclipse-quick-fix-for-a-custom-java-marker

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!