TextContent (PsiElement root, TextContent.TextDomain domain, TextRange valueRange) { TextRange rootRange = root.getTextRange(); if (isWrongRange(valueRange, rootRange.getLength())) { LOG.error("The range " + valueRange + " is out of the PSI element, length " + rootRange.getLength()); return null; } int rootStart = rootRange.getStartOffset(); TextRange fileValueRange = valueRange.shiftRight(rootStart); CharSequence fileText = root.getContainingFile().getViewProvider().getContents(); if (isWrongRange(fileValueRange, fileText.length())) { LOG.error("The range " + fileValueRange + " is out of the file, length " + fileText.length()); return null; } TextContent content = new PsiRecursiveElementWalkingVisitor() { final List<TextContentImpl.TokenInfo> tokens = new ArrayList<>(); int currentStart = fileValueRange.getStartOffset(); @Override public void visitElement(@NotNull PsiElement element) { TextRange range = element.getTextRange().intersection(fileValueRange); if (range == null) return; @Nullable ExclusionKind kind = classifier.apply(element); if (kind == ExclusionKind.exclude) { exclusionStarted(range); } else if (kind != null) { exclusionStarted(range); var tokenKind = kind == ExclusionKind.markup ? TextContentImpl.TokenKind.markup : TextContentImpl.TokenKind.unknown; tokens.add(new TextContentImpl.PsiToken("", root, TextRange.from(range.getStartOffset(), 0).shiftLeft(rootStart), tokenKind)); } else { super.visitElement(element); } } private void exclusionStarted(TextRange range) { if (range.getStartOffset() != currentStart) { TextRange tokenRange = new TextRange(currentStart, range.getStartOffset()); String tokenText = tokenRange.subSequence(fileText).toString(); tokens.add(new TextContentImpl.PsiToken(tokenText, root, tokenRange.shiftLeft(rootStart), TextContentImpl.TokenKind.text)); } currentStart = range.getEndOffset(); } @Nullable TextContent walkPsiTree() { root.accept(this); exclusionStarted(TextRange.from(fileValueRange.getEndOffset(), 0)); return tokens.isEmpty() ? null : new TextContentImpl(domain, tokens); } }.walkPsiTree(); return content == null ? null : content.removeIndents(indentChars).removeLineSuffixes(suffixChars).trimWhitespace(); }