【问题标题】:Eclipse Plugin: Implement Quick Fix proposals on hovering over errorsEclipse 插件:实施关于悬停在错误上的快速修复建议
【发布时间】:2016-06-02 09:16:10
【问题描述】:

我有什么:我在编辑器中将错误显示为红色下划线,并在问题视图中显示。我为此使用标记,但也通过此代码创建注释:

@TextEditorScoped
public class ErrorHighlighter {

  private final IAnnotationModel annotationModel;

  private String content = "";


  private final Set<Annotation> annotations = Sets.newConcurrentHashSet();

  private static final String ERRORMARKERID  = "org.eclipse.rwth.syntaxerror";
  private static final String WARNINGMARKERID = "org.eclipse.rwth.syntaxwarning"; 

  @Inject
  public ErrorHighlighter(@Nullable IAnnotationModel annotationModel, IStorage storage,
      ObservableModelStates observableModelStates) {
    this.annotationModel = annotationModel;
    if (annotationModel != null) {
      observableModelStates.getModelStates().stream()
          .filter(modelState -> modelState.getStorage().equals(storage))
          .forEach(this::acceptModelState);
      observableModelStates.addStorageObserver(storage, this::acceptModelState);
    }
  }

  public void acceptModelState(ModelState modelState) {
    for (Annotation annotation : annotations) {
      annotationModel.removeAnnotation(annotation);
      annotations.remove(annotation);
    }
    IMarker[] problems = null;
    int depth = IResource.DEPTH_INFINITE;
    IFile file = Misc.getEditorInput(modelState.getStorage()).getAdapter(IFile.class);
    try { //Remove all problem Markers when rebuilding the Model
       problems = file.findMarkers(ERRORMARKERID, true, depth);
       for(IMarker m: problems){
           m.delete();
       }
       problems = file.findMarkers(WARNINGMARKERID, true, depth);
       for(IMarker m: problems){
           m.delete();
       }
    } catch (CoreException e) {
       e.printStackTrace();
    }
    try {
        content = IOUtils.toString(modelState.getStorage().getContents(), "UTF-8");
    } catch (IOException e) {
        e.printStackTrace();
    } catch (CoreException e) {
        e.printStackTrace();
    }
    displaySyntaxErrors(modelState);
    displayAdditionalErrors(modelState);
  }

  private void displaySyntaxErrors(ModelState modelState) {
    ImmutableMultimap<Interval, String> syntaxErrors = modelState.getSyntaxErrors();
    for (Interval interval: syntaxErrors.keys()) {
      for (String message : syntaxErrors.get(interval)) {
        Display.getDefault().asyncExec(() -> displayError(interval, message));
      }
    }
  }

  private void displayAdditionalErrors(ModelState modelState) {
    Multimap<Interval, String> additionalErrors = modelState.getAdditionalErrors();
    for (Interval interval: additionalErrors.keys()) {
      for (String message : additionalErrors.get(interval)) {
        Display.getDefault().asyncExec(() -> displayError(interval, message));
      }
    }
  }

  private void displayError(Interval interval, String message) {
    int startIndex = interval.a;
    int stopIndex = interval.b + 1;
    Annotation annotation = null;
//    Annotation annotation =
//        new Annotation("org.eclipse.ui.workbench.texteditor.error", false, message);
//    annotations.add(annotation);
//    annotationModel.addAnnotation(annotation, new Position(startIndex, stopIndex - startIndex));
    IMarker marker = null;
    try { //create Marker to display Syntax Errors in Problems View
        IFile file = (IFile) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getEditorInput().getAdapter(IFile.class);
        if (file != null) {
            if(message.charAt(message.length()-1) == 'W'){
                marker = file.createMarker(WARNINGMARKERID);
                marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING);
            } else {
                marker = file.createMarker(ERRORMARKERID);
                marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
            }
            marker.setAttribute(IMarker.MESSAGE, message);
            marker.setAttribute(IMarker.CHAR_START, startIndex);
            marker.setAttribute(IMarker.CHAR_END, stopIndex);
            marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
            int lineNumber = 0;
            if(!content.isEmpty() && content.length()>=stopIndex){  //Convert StartIndex to Line Number
                String[] lines = content.substring(0, stopIndex).split("\r\n|\r|\n");
                lineNumber = lines.length;
            }
            marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
        }
    } catch (CoreException e) {
        e.printStackTrace();
    }
    IMarker[] problems = null;
    int depth = IResource.DEPTH_INFINITE;
    IFile file = (IFile) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getEditorInput().getAdapter(IFile.class);
    try { //Remove all problem Markers when rebuilding the Model
       problems = file.findMarkers(ERRORMARKERID, true, depth);
       for(IMarker m: problems){
           for(IMarker n: problems){
               if(MarkerUtilities.getCharStart(m) == MarkerUtilities.getCharStart(n) && m != n && MarkerUtilities.getMessage(m).equals(MarkerUtilities.getMessage(n))){
                   m.delete();
               }
           }
       }
    } catch (CoreException e) {
       e.printStackTrace();
    }
    if(marker != null){
        Annotation a = new MarkerAnnotation(marker);
        annotations.add(a);
        annotationModel.addAnnotation(a, new Position(startIndex, stopIndex - startIndex));
    }

  }
}

在我的 SourceViewerConfiguration 中,我使用以下代码覆盖 getTextHover 和 getAnnotationHover:

@Override
  public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) {
      return new DefaultAnnotationHover(true);
  }

  public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
      return new DefaultTextHover(sourceViewer);
  }

我还用此代码覆盖 getQuickAssistAssistant:

public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) {
      IQuickAssistAssistant quickAssist = new QuickAssistAssistant();
      quickAssist.setQuickAssistProcessor(new TFQuickAssistProcessor());
      quickAssist.setInformationControlCreator(getInformationControlCreator(sourceViewer));
      return quickAssist;
}

这样我可以右键单击代码中的错误并选择 QuickFix,这将导致出现一个框,其中显示我的 Quick Fix Proposals。

我想要什么:当我将鼠标悬停在错误上方时,如何让此框出现? 提前致谢

【问题讨论】:

    标签: java eclipse eclipse-plugin eclipse-rcp


    【解决方案1】:

    回答我自己的问题: 我用这个替换了getTextHover 方法:

    public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {      
          return new AbstractAnnotationHover(true) {
              public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
                  IAnnotationModel model = ((SourceViewer) textViewer).getAnnotationModel();
                  @SuppressWarnings("unchecked")
                  Iterator<Annotation> parent = 
                          ((IAnnotationModelExtension2)model).getAnnotationIterator(hoverRegion.getOffset(), 
                                  hoverRegion.getLength(), true, true);
                  Iterator<?> iter = new JavaAnnotationIterator(parent, false); 
                  Annotation annotation = null;
                  Position position = null;
                  while (iter.hasNext()) {
                      Annotation a = (Annotation) iter.next();
                      Position p = model.getPosition(a);
                      annotation = a;
                      position = p;
                  }
                  return new AnnotationInfo(annotation, position, textViewer) {
                      public ICompletionProposal[] getCompletionProposals() {
                          ICompletionProposal proposal1 = null;
                          IMarkerResolution [] resolutions = null;
                          ICompletionProposal [] com = null;
                          if (annotation instanceof MarkerAnnotation) {
                              resolutions = new ErrorResolution().getResolutions(((MarkerAnnotation) annotation).getMarker());
                              if(resolutions.length != 0){
                                  proposal1 = new MarkerResolutionProposal(resolutions[0], 
                                          ((MarkerAnnotation) annotation).getMarker());
                                  return new ICompletionProposal[] { proposal1 };
                              }
                          }
                          return com ;
                      }
                  };
              } 
        };
    }
    

    这将导致一个悬停框出现在错误上,提供快速修复。希望这会有所帮助!

    【讨论】:

    • 我正在做类似的事情,但出现的 hiver 控件不可滚动,即使我的文本多于窗口的大小。你有同样的行为吗?你必须有办法让它工作,因为 JDT QuickFix 悬停在需要时是可滚动的。
    • 滚动条没有被绘制,因为我有一个错误,其中我的提案列表为空。如果设置为输入的 AnnotationInfo 没有完成建议,则悬停控件会跳过设置滚动条。
    • 说得太早了。即使包含提案,悬停也不会像 JDT QuickFix 提案那样绘制任何滚动条。我正在调试器中查看 AbstractInformationControlManager 中的代码,但我看不出为什么在显示信息控件时未正确设置可滚动。
    • 我试图使用 JDT 悬停来提供常规的文本帮助信息,但这不是正确的方法。我发布了一个单独的答案,其中包含委托悬停实现的代码,适用于任何其他想要提供多种悬停信息的人。
    【解决方案2】:

    选择的答案解决了我的问题,但除了问题解决建议之外,我还需要提供文字悬停帮助。文本帮助和问题解决建议需要不同的悬停实现。

    这是我的委托 Hover 控件的实现,如果用户将鼠标悬停在问题标记上并且有可用的解决方案建议,它将调用 JDT Annotation 悬停控件。否则,它会调用 ITextHover 实现,该实现提供文本帮助(如果可用)。

    这大致基于BestMatchHover 的JDT 实现,这就是JDT 如何通过依次检查配置的悬停并使用返回信息的第一个悬停来委派不同的悬停。我在从选定的悬停中获得正确的演示时遇到了一些麻烦,而 JDT 实现有助于展示如何提供 IInformationControlCreator 的适当实现。

    /**
     * This class provides a TextHover implementation which delegates to one of two different kinds of
     * TextHovers, depending on the current hover contents.
     * 
     * If the mouse is hovering over text for which
     * a problem was detected by the parser and a resolution proposal is available, presentation of the
     * hover info will be delegated to the JDT Hover control which presents the problem description and
     * Hyperlinks for the available proposals. Otherwise an ITextHover implementation will provide textual
     * hover help..
     * 
     * The JDT Hover implementation is non-API, and could therefore break in future RCP versions.
     */
    
    public class DelegatingTextHover implements ITextHover, ITextHoverExtension, ITextHoverExtension2 {
    
        private ICompletionProposal[] proposals = new ICompletionProposal[0];
    
        protected IRegion currentHoverRegion;
    
        //Provides help info as text
        private ITextHover helpHover = new ITextHover() {
    
            @Override
            public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
                return DelegatingTextHover.this.getHoverRegion(textViewer, offset);
            }
    
    
            @Override
            public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
                String hoverHelp = ...
                return hoverHelp;
    
        };
    
    
        @SuppressWarnings("restriction")
        private AbstractAnnotationHover quickFixHover = new AbstractAnnotationHover(true) {
    
            @Override
            public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
                return null;
            }
    
            @Override
            public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
                return DelegatingTextHover.this.getHoverRegion(textViewer, offset);
            }
    
            @Override
            public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
                currentHoverRegion = hoverRegion;
                IMarker marker = ((MarkerResolutionProposal) proposals[0]).getMarker();
                String msg;
                try {
                    msg = (String) marker.getAttribute(IMarker.MESSAGE);
                } catch (CoreException e) {
                    msg = "Parsing Error";
                }
                return new AnnotationInfo(new Annotation(IMarker.PROBLEM, true, msg),
                        new Position(hoverRegion.getOffset(), msg.length()),
                        textViewer) {
                    @Override
                    public ICompletionProposal[] getCompletionProposals() {
                        return proposals;
                    }
                };
            }
    
            @Override
            public IInformationControlCreator getHoverControlCreator() {
                // if we're using the annotation hover, delegate to superclass. Otherwise, use the default
                if (hoveringOverProblemAnnotation(currentHoverRegion)) {
                    return super.getHoverControlCreator();
                }
                return new IInformationControlCreator() {
                    @Override
                    public IInformationControl createInformationControl(Shell shell) {
                        return new DefaultInformationControl(shell, true);
                    }
                };
            }
    
        };
    
        @SuppressWarnings({ "restriction", "deprecation" })
        @Override
        public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
            proposals = ...
            if (hoveringOverProblemAnnotation(hoverRegion)) {
                return quickFixHover.getHoverInfo2(textViewer, hoverRegion);
            }
            currentHoverRegion = hoverRegion;
            return helpHover.getHoverInfo(textViewer, hoverRegion);
        }
    
        @SuppressWarnings("restriction")
        @Override
        public IInformationControlCreator getHoverControlCreator() {
            if (hoveringOverProblemAnnotation(currentHoverRegion)) {
                return quickFixHover.getHoverControlCreator();
            }
            return null;
        }
    
        public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
            Point selection= textViewer.getSelectedRange();
            if (selection.x <= offset && offset < selection.x + selection.y)
                return new Region(selection.x, selection.y);
            return new Region(offset, 0);
        }
    
        @Override
        public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
            return null;
        }
    
        public boolean hoveringOverProblemAnnotation(IRegion hoverRegion) {
            List<Point> ranges = new ArrayList<>();
            for (ICompletionProposal proposal : proposals) {
                //MarkerResolutionProposal is an implementation of ICompletionProposal which wraps a Marker
                if (proposal instanceof MarkerResolutionProposal) {
                    MarkerResolutionProposal markerProposal = (MarkerResolutionProposal) proposal;
                    IMarker marker = markerProposal.getMarker();
                    try {
                        Integer begin =  (Integer) marker.getAttribute(IMarker.CHAR_START);
                        Integer end = (Integer) marker.getAttribute(IMarker.CHAR_END);
                        ranges.add(new Point(begin, end));
                    } catch (CoreException e) {
                        //PASS
                    }
                }
            }
            boolean found = false;
            for (Point p : ranges) {
                if (hoverRegion.getOffset() >= p.x && hoverRegion.getOffset() <= p.y) {
                    found = true;
                }
            }
            return proposals.length > 0 && found;
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-28
      • 2010-11-03
      • 1970-01-01
      相关资源
      最近更新 更多