String (@NotNull Map<String, ExpectedHighlightingSet> types, @NotNull Collection<? extends HighlightInfo> infos, @NotNull String text) { // filter highlighting data and map each highlighting to a tag name List<Pair<String, ? extends HighlightInfo>> list = infos.stream() .map(info -> pair(findTag(types, info), info)) .filter(p -> p.first != null) .collect(Collectors.toList()); boolean showAttributesKeys = types.values().stream().flatMap(set -> set.infos.stream()).anyMatch(info -> info.forcedTextAttributesKey != null); String anyWrappedInHtml = XmlStringUtil.wrapInHtml(ANY_TEXT); boolean showTooltips = types.values().stream().flatMap(set -> set.infos.stream()).anyMatch(info -> !anyWrappedInHtml.equals(info.getToolTip())); // sort filtered highlighting data by end offset in descending order list.sort((o1, o2) -> { HighlightInfo i1 = o1.second; HighlightInfo i2 = o2.second; int byEnds = i2.endOffset - i1.endOffset; if (byEnds != 0) return byEnds; if (!i1.isAfterEndOfLine() && !i2.isAfterEndOfLine()) { int byStarts = i1.startOffset - i2.startOffset; if (byStarts != 0) return byStarts; } else { int byEOL = Boolean.compare(i2.isAfterEndOfLine(), i1.isAfterEndOfLine()); if (byEOL != 0) return byEOL; } int bySeverity = i2.getSeverity().compareTo(i1.getSeverity()); if (bySeverity != 0) return bySeverity; return Comparing.compare(i1.getDescription(), i2.getDescription()); }); // combine highlighting data with original text StringBuilder sb = new StringBuilder(); int[] offsets = composeText(sb, list, 0, text, text.length(), -1, showAttributesKeys, showTooltips); sb.insert(0, text.substring(0, offsets[1])); return sb.toString(); }