Skip to content

Commit 151b03e

Browse files
authored
[Java.Interop.Tools.JavaSource] Improve <code> parsing (#1125)
Fixes: #1071 The latest API docs update contained a couple dozen parsing issues due to `<code/>` parsing, including: * Closing element doesn't match opening element: `<code>null</null>` * Content including `@`: `<code>android:label="@string/resolve_title"</code>` * Closing element is actually an opening element: `<code>Activity.RESULT_OK<code>` * Improper element nesting: `<code><pre><p>content</code></pre></p>` * Use of attributes: `<code class=prettyprint>content<code>` Fix this by replacing `CodeElementDeclaration` to use a new `CodeElementContentTerm` terminal, which is a "greedy regex" which grabs `<code` until one of: * `</code>` * `</null>` * `<code>` The result of `CodeElementDeclaration` is the end of the `<code>` element until the beginning of one of the above terminators: * `<code>null</null>` becomes `<c>null</c>` * `<code>android:label="@string/resolve_title"</code>` becomes `<c>android:label="@string/resolve_title"</c>`.` * `<code>Activity.RESULT_OK<code>` becomes `<c>Activity.RESULT_OK</c>`. * `<code><pre><p>content</code></pre></p>` becomes the mess `<c>&lt;pre&gt;&lt;p&gt;some content</c>&lt;/pre&gt;&lt;/p&gt;` 🤷‍♂️ * `<code class=prettyprint>content<code>` becomes `<c>content</c>`.`
1 parent 6a9f5cb commit 151b03e

File tree

3 files changed

+72
-7
lines changed

3 files changed

+72
-7
lines changed

src/Java.Interop.Tools.JavaSource/Java.Interop.Tools.JavaSource/SourceJavadocToXmldocGrammar.HtmlBnfTerms.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,17 @@ internal void CreateRules (SourceJavadocToXmldocGrammar grammar)
131131
}
132132
};
133133

134-
CodeElementDeclaration.Rule = CreateStartElement ("code", grammar) + InlineDeclarations + CreateEndElement ("code", grammar);
134+
CodeElementDeclaration.Rule = CodeElementContentTerm;
135135
CodeElementDeclaration.AstConfig.NodeCreator = (context, parseNode) => {
136-
var target = parseNode.ChildNodes [1].AstNode;
136+
// Parse the entire <code> element captured in the token
137+
var codeElementText = parseNode.ChildNodes [0].Token.Text;
138+
int startIndex = codeElementText.IndexOf ('>');
139+
int stopIndex = codeElementText.LastIndexOf ('<');
140+
if (startIndex == -1 || stopIndex == -1) {
141+
parseNode.AstNode = new XText (codeElementText);
142+
return;
143+
}
144+
var target = codeElementText.Substring (startIndex + 1, stopIndex - startIndex - 1);
137145
parseNode.AstNode = new XElement ("c", target);
138146
};
139147
}
@@ -232,6 +240,12 @@ static string GetChildNodesAsString (ParseTreeNode parseNode)
232240
public readonly NonTerminal InlineHyperLinkDeclaration = new NonTerminal (nameof (InlineHyperLinkDeclaration), ConcatChildNodes);
233241
public readonly NonTerminal CodeElementDeclaration = new NonTerminal (nameof (CodeElementDeclaration), ConcatChildNodes);
234242

243+
public readonly Terminal CodeElementContentTerm = new RegexBasedTerminal ("<code>", $@"(?i)<code\s*[^>]*>(.|\s)*?(<\/code>|<\/null>|<code>)") {
244+
AstConfig = new AstNodeConfig {
245+
NodeCreator = (context, parseNode) => parseNode.AstNode = "",
246+
},
247+
};
248+
235249
public readonly Terminal InlineHyperLinkOpenTerm = new RegexBasedTerminal ("<a attr=", @"(?i)<a\s*.*=") {
236250
AstConfig = new AstNodeConfig {
237251
NodeCreator = (context, parseNode) => parseNode.AstNode = "",

tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocGrammar.HtmlBnfTermsTests.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,38 @@ public void CodeElementDeclaration ()
101101
var r = p.Parse ("<code>input.position()</code>");
102102
Assert.IsFalse (r.HasErrors (), DumpMessages (r, p));
103103
Assert.AreEqual ("<c>input.position()</c>", r.Root.AstNode.ToString ());
104-
}
105104

105+
r = p.Parse ("<code>null</null>");
106+
Assert.IsFalse (r.HasErrors (), DumpMessages (r, p));
107+
Assert.AreEqual ("<c>null</c>", r.Root.AstNode.ToString ());
108+
109+
r = p.Parse ("<code>android:label=\"@string/resolve_title\"</code>");
110+
Assert.IsFalse (r.HasErrors (), DumpMessages (r, p));
111+
Assert.AreEqual ("<c>android:label=\"@string/resolve_title\"</c>", r.Root.AstNode.ToString ());
112+
113+
r = p.Parse ("<code>Activity.RESULT_OK<code>");
114+
Assert.IsFalse (r.HasErrors (), DumpMessages (r, p));
115+
Assert.AreEqual ("<c>Activity.RESULT_OK</c>", r.Root.AstNode.ToString ());
116+
117+
r = p.Parse ("<code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code>");
118+
Assert.IsFalse (r.HasErrors (), DumpMessages (r, p));
119+
Assert.AreEqual ("<c>format.setString(MediaFormat.KEY_FRAME_RATE, null)</c>", r.Root.AstNode.ToString ());
120+
121+
r = p.Parse (@"<code>
122+
<p> [ 0, 0, 0, 0, 0 ]
123+
<p> [ 0, 0, 0, 0, 0 ]
124+
<p> [ 0, 0, 1, 0, 0 ]
125+
<p> [ 0, 0, 0, 0, 0 ]
126+
<p> [ 0, 0, 0, 0, 0 ]
127+
</code>");
128+
Assert.IsFalse (r.HasErrors (), DumpMessages (r, p));
129+
Assert.AreEqual (@"<c>
130+
&lt;p&gt; [ 0, 0, 0, 0, 0 ]
131+
&lt;p&gt; [ 0, 0, 0, 0, 0 ]
132+
&lt;p&gt; [ 0, 0, 1, 0, 0 ]
133+
&lt;p&gt; [ 0, 0, 0, 0, 0 ]
134+
&lt;p&gt; [ 0, 0, 0, 0, 0 ]
135+
</c>", r.Root.AstNode.ToString ());
136+
}
106137
}
107138
}

tests/Java.Interop.Tools.JavaSource-Tests/SourceJavadocToXmldocParserTests.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,36 @@ more description here.</para>
144144
</member>",
145145
},
146146
new ParseResult {
147-
Javadoc = "Something {@link #method}: description, \"<code>declaration</code>\" or \"<code>another declaration</code>\".\n\n@apiSince 1\n",
147+
Javadoc = "Something {@link #method}: description, \"<code>declaration</code>\" or <code><pre><p>some content</code></pre></p>.\n\n@apiSince 1\n",
148148
FullXml = @"<member>
149-
<summary>Something <c>#method</c>: description, ""<c>declaration</c>"" or ""<c>another declaration</c>"".</summary>
149+
<summary>Something <c>#method</c>: description, ""<c>declaration</c>"" or <c>&lt;pre&gt;&lt;p&gt;some content</c>&lt;/pre&gt;&lt;/p&gt;.</summary>
150150
<remarks>
151-
<para>Something <c>#method</c>: description, ""<c>declaration</c>"" or ""<c>another declaration</c>"".</para>
151+
<para>Something <c>#method</c>: description, ""<c>declaration</c>"" or <c>&lt;pre&gt;&lt;p&gt;some content</c>&lt;/pre&gt;&lt;/p&gt;.</para>
152152
<para>Added in API level 1.</para>
153153
</remarks>
154154
</member>",
155155
IntelliSenseXml = @"<member>
156-
<summary>Something <c>#method</c>: description, ""<c>declaration</c>"" or ""<c>another declaration</c>"".</summary>
156+
<summary>Something <c>#method</c>: description, ""<c>declaration</c>"" or <c>&lt;pre&gt;&lt;p&gt;some content</c>&lt;/pre&gt;&lt;/p&gt;.</summary>
157+
</member>",
158+
},
159+
new ParseResult {
160+
Javadoc = @"The result code will be <code>Activity.RESULT_OK<code> for success,
161+
or one of these errors:
162+
<code>RESULT_ERROR_GENERIC_FAILURE</code>",
163+
FullXml = @"<member>
164+
<summary>The result code will be <c>Activity.RESULT_OK</c> for success,
165+
or one of these errors:
166+
<c>RESULT_ERROR_GENERIC_FAILURE</c></summary>
167+
<remarks>
168+
<para>The result code will be <c>Activity.RESULT_OK</c> for success,
169+
or one of these errors:
170+
<c>RESULT_ERROR_GENERIC_FAILURE</c></para>
171+
</remarks>
172+
</member>",
173+
IntelliSenseXml = @"<member>
174+
<summary>The result code will be <c>Activity.RESULT_OK</c> for success,
175+
or one of these errors:
176+
<c>RESULT_ERROR_GENERIC_FAILURE</c></summary>
157177
</member>",
158178
},
159179
new ParseResult {

0 commit comments

Comments
 (0)