fix(jdoc): inline mk structs break detection

GitOrigin-RevId: 0cb1adbd59262523ce59767fee5f33e78234bd9f
This commit is contained in:
Mathias Boulay
2024-09-18 13:23:41 +02:00
committed by intellij-monorepo-bot
parent 3b13ef3ec1
commit 0cdf89293d
7 changed files with 120 additions and 4 deletions

View File

@@ -388,7 +388,7 @@ public final class BasicJavaDocParser {
while (!builder.eof()) {
builder.advanceLexer();
previousToken = token;
token = getTokenType(builder);
token = getTokenType(builder, false);
if (token == needle) {
return token;
}
@@ -398,7 +398,7 @@ public final class BasicJavaDocParser {
}
// Markdown specific, check for EOL
if (token == JavaDocTokenType.DOC_SPACE && previousToken == JavaDocTokenType.DOC_SPACE) {
if (token == TokenType.WHITE_SPACE && previousToken == TokenType.WHITE_SPACE) {
break;
}
}
@@ -605,10 +605,15 @@ public final class BasicJavaDocParser {
@Nullable
private static IElementType getTokenType(PsiBuilder builder) {
return getTokenType(builder, true);
}
@Nullable
private static IElementType getTokenType(PsiBuilder builder, boolean skipWhitespace) {
IElementType tokenType;
while ((tokenType = builder.getTokenType()) == JavaDocTokenType.DOC_SPACE) {
builder.remapCurrentToken(TokenType.WHITE_SPACE);
builder.advanceLexer();
if (skipWhitespace) builder.advanceLexer();
}
return tokenType;
}

View File

@@ -0,0 +1,68 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.javadoc.markdown;
import com.intellij.openapi.util.text.StringUtil;
import kotlin.ranges.IntRange;
import org.intellij.markdown.MarkdownTokenTypes;
import org.intellij.markdown.parser.sequentialparsers.RangesListBuilder;
import org.intellij.markdown.parser.sequentialparsers.SequentialParser;
import org.intellij.markdown.parser.sequentialparsers.TokensCache;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/// Inline parser dedicated to finding `<code>` tags.
///
/// Only purpose is to exclude the range from further markdown processing
public class CodeTagParser implements SequentialParser {
@Override
public @NotNull ParsingResult parse(@NotNull TokensCache tokens, @NotNull List<IntRange> rangesToGlue) {
var result = new SequentialParser.ParsingResultBuilder();
var delegateIndices = new RangesListBuilder();
TokensCache.Iterator iterator = tokens.new RangesListIterator(rangesToGlue);
while (iterator.getType() != null) {
if (iterator.getType() == MarkdownTokenTypes.HTML_TAG && hasOpeningTag(getText(tokens, iterator), "code")) {
int startIndex = iterator.getIndex();
var endIterator = findEnd(tokens, iterator.advance());
if (endIterator != null) {
result.withNode(new SequentialParser.Node(new IntRange(startIndex, endIterator.getIndex() + 1), JavaDocMarkdownFlavourDescriptor.RAW_TYPE));
iterator = endIterator.advance();
continue;
}
}
delegateIndices.put(iterator.getIndex());
iterator = iterator.advance();
}
return result.withFurtherProcessing(delegateIndices.get());
}
/// @return The iterator located at the end tag, or `null` if the end isn't found
private static TokensCache.Iterator findEnd(@NotNull TokensCache tokens, TokensCache.Iterator iterator) {
while (iterator.getType() != null) {
if (iterator.getType() == MarkdownTokenTypes.HTML_TAG) {
if (hasClosingTag(getText(tokens, iterator), "code")) {
return iterator;
}
}
iterator = iterator.advance();
}
return null;
}
private static String getText(@NotNull TokensCache tokens, TokensCache.Iterator iterator) {
return tokens.getOriginalText().subSequence(iterator.getStart(), iterator.getEnd()).toString();
}
private static boolean hasOpeningTag(String tag, String tagName) {
return StringUtil.trimEnd(StringUtil.trimStart(tag, "<"), ">").equals(tagName);
}
private static boolean hasClosingTag(String tag, String tagName) {
return StringUtil.trimEnd(StringUtil.trimStart(tag, "</"), ">").equals(tagName);
}
}

View File

@@ -6,11 +6,18 @@ import org.intellij.markdown.MarkdownElementTypes;
import org.intellij.markdown.MarkdownTokenTypes;
import org.intellij.markdown.ast.ASTNode;
import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor;
import org.intellij.markdown.flavours.gfm.GFMTokenTypes;
import org.intellij.markdown.flavours.gfm.StrikeThroughDelimiterParser;
import org.intellij.markdown.html.GeneratingProvider;
import org.intellij.markdown.html.HtmlGenerator;
import org.intellij.markdown.html.TransparentInlineHolderProvider;
import org.intellij.markdown.html.TrimmingInlineHolderProvider;
import org.intellij.markdown.parser.LinkMap;
import org.intellij.markdown.parser.MarkerProcessorFactory;
import org.intellij.markdown.parser.sequentialparsers.EmphasisLikeParser;
import org.intellij.markdown.parser.sequentialparsers.SequentialParser;
import org.intellij.markdown.parser.sequentialparsers.SequentialParserManager;
import org.intellij.markdown.parser.sequentialparsers.impl.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -23,10 +30,31 @@ import java.util.Map;
* Flavour descriptor which describes how to generate html text from markdown documentation
*/
public class JavaDocMarkdownFlavourDescriptor extends GFMFlavourDescriptor {
public static final IElementType RAW_TYPE = new IElementType("RAW_TYPE");
public JavaDocMarkdownFlavourDescriptor() {
super(true, false, true);
}
@Override
public @NotNull SequentialParserManager getSequentialParserManager() {
return new SequentialParserManager() {
@Override
public @NotNull List<SequentialParser> getParserSequence() {
return List.of(
new AutolinkParser(List.of(MarkdownTokenTypes.AUTOLINK, GFMTokenTypes.GFM_AUTOLINK)),
new BacktickParser(),
new CodeTagParser(), // Custom parser excluding <code> blocks content to be processed as markdown
new MathParser(),
new ImageParser(),
new InlineLinkParser(),
new ReferenceLinkParser(),
new EmphasisLikeParser(new EmphStrongDelimiterParser(), new StrikeThroughDelimiterParser())
);
}
};
}
@NotNull
@Override
public Map<IElementType, GeneratingProvider> createHtmlGeneratingProviders(@NotNull LinkMap linkMap, @Nullable URI baseURI) {
@@ -38,6 +66,9 @@ public class JavaDocMarkdownFlavourDescriptor extends GFMFlavourDescriptor {
// Paragraphs may need to be inlined
result.put(MarkdownElementTypes.PARAGRAPH, new JavaDocParagraphProvider());
// Custom type, no processing
result.put(RAW_TYPE, new TransparentInlineHolderProvider());
return result;
}

View File

@@ -1,3 +1,5 @@
<html><head><base href="placeholder"></head><body><div class='definition'><pre><span style="color:#000080;font-weight:bold;">class</span> <span style="color:#000000;">MarkdownCodeBlock</span></pre></div><div class='content'><p>Single liner code block:
<code><span style="">Hello world</span></code></p><p>No tags are interpreted inside them
<code><span style="">{@link java.lang.String niceLink}</span></code></p></div><table class='sections'><p></table>
<code><span style="">{@link java.lang.String niceLink}</span></code></p><p>No markdown markup is interpreted inside them
<code><span style="">_Hello_ &lt;code&gt;</span></code></p><p>This is a broken inline link
`Start of broken link</p>end of broken link`</div><table class='sections'><p></table>

View File

@@ -4,4 +4,12 @@
///
/// No tags are interpreted inside them
/// `{@link java.lang.String niceLink}`
///
/// No markdown markup is interpreted inside them
/// `_Hello_ <code>`
///
/// This is a broken inline link
/// `Start of broken link
///
/// end of broken link`
class MarkdownCodeBlock {}

View File

@@ -3,4 +3,5 @@
Some text with inline <code><span style="">code</span></code>
</pre>
<pre><code><span style="">String&#32;fullLine&#32;=&#32;</span><span style="color:#008000;font-weight:bold;">""</span><span style="">;</span></code></pre> </pre>
<pre><code><span style="">String&#32;secondFullLine&#32;=&#32;</span><span style="color:#008000;font-weight:bold;">""</span><span style="">;</span></code></pre>
</div><table class='sections'><p></table>

View File

@@ -7,6 +7,7 @@
* String fullLine = "";
* }
* </pre>
* <pre> {@code String secondFullLine = ""; }</pre>
*/
class Test {
public String field = null;