@NotNull
  public static HighlightInfo fromAnnotation(
      @NotNull Annotation annotation, @Nullable TextRange fixedRange, boolean batchMode) {
    final TextAttributes forcedAttributes = annotation.getEnforcedTextAttributes();
    final TextAttributesKey forcedAttributesKey =
        forcedAttributes == null ? annotation.getTextAttributes() : null;

    HighlightInfo info =
        new HighlightInfo(
            forcedAttributes,
            forcedAttributesKey,
            convertType(annotation),
            fixedRange != null ? fixedRange.getStartOffset() : annotation.getStartOffset(),
            fixedRange != null ? fixedRange.getEndOffset() : annotation.getEndOffset(),
            annotation.getMessage(),
            annotation.getTooltip(),
            annotation.getSeverity(),
            annotation.isAfterEndOfLine(),
            annotation.needsUpdateOnTyping(),
            annotation.isFileLevelAnnotation(),
            0);
    info.setGutterIconRenderer(annotation.getGutterIconRenderer());
    info.setProblemGroup(annotation.getProblemGroup());
    appendFixes(
        fixedRange, info, batchMode ? annotation.getBatchFixes() : annotation.getQuickFixes());
    return info;
  }
  private static void addPatchedInfos(
      @NotNull HighlightInfo info,
      @NotNull PsiFile injectedPsi,
      @NotNull DocumentWindow documentWindow,
      @NotNull InjectedLanguageManager injectedLanguageManager,
      @Nullable TextRange fixedTextRange,
      @NotNull Collection<HighlightInfo> out) {
    ProperTextRange textRange = new ProperTextRange(info.startOffset, info.endOffset);
    List<TextRange> editables =
        injectedLanguageManager.intersectWithAllEditableFragments(injectedPsi, textRange);
    for (TextRange editable : editables) {
      TextRange hostRange =
          fixedTextRange == null ? documentWindow.injectedToHost(editable) : fixedTextRange;

      boolean isAfterEndOfLine = info.isAfterEndOfLine;
      if (isAfterEndOfLine) {
        // convert injected afterEndOfLine to either host' afterEndOfLine or not-afterEndOfLine
        // highlight of the injected fragment boundary
        int hostEndOffset = hostRange.getEndOffset();
        int lineNumber = documentWindow.getDelegate().getLineNumber(hostEndOffset);
        int hostLineEndOffset = documentWindow.getDelegate().getLineEndOffset(lineNumber);
        if (hostEndOffset < hostLineEndOffset) {
          // convert to non-afterEndOfLine
          isAfterEndOfLine = false;
          hostRange = new ProperTextRange(hostRange.getStartOffset(), hostEndOffset + 1);
        }
      }

      HighlightInfo patched =
          new HighlightInfo(
              info.forcedTextAttributes,
              info.forcedTextAttributesKey,
              info.type,
              hostRange.getStartOffset(),
              hostRange.getEndOffset(),
              info.description,
              info.toolTip,
              info.type.getSeverity(null),
              isAfterEndOfLine,
              null,
              false);
      patched.setHint(info.hasHint());
      patched.setGutterIconRenderer(info.getGutterIconRenderer());

      if (info.quickFixActionRanges != null) {
        for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair :
            info.quickFixActionRanges) {
          TextRange quickfixTextRange = pair.getSecond();
          List<TextRange> editableQF =
              injectedLanguageManager.intersectWithAllEditableFragments(
                  injectedPsi, quickfixTextRange);
          for (TextRange editableRange : editableQF) {
            HighlightInfo.IntentionActionDescriptor descriptor = pair.getFirst();
            if (patched.quickFixActionRanges == null)
              patched.quickFixActionRanges =
                  new ArrayList<Pair<HighlightInfo.IntentionActionDescriptor, TextRange>>();
            TextRange hostEditableRange = documentWindow.injectedToHost(editableRange);
            patched.quickFixActionRanges.add(Pair.create(descriptor, hostEditableRange));
          }
        }
      }
      patched.fromInjection = true;
      out.add(patched);
    }
  }