[grazie] ignore sentence capitalization issues in various doc tags

GitOrigin-RevId: e1563f745ea25399d4b62ae3661f38a76d8f24b6
This commit is contained in:
Peter Gromov
2023-02-01 09:57:33 +00:00
committed by intellij-monorepo-bot
parent ca9047e41d
commit fc8165132e
17 changed files with 88 additions and 23 deletions
@@ -1,17 +1,19 @@
package com.intellij.grazie.ide.language.java;
import com.intellij.grazie.text.ProblemFilter;
import com.intellij.grazie.text.RuleGroup;
import com.intellij.grazie.text.TextContent;
import com.intellij.grazie.text.TextProblem;
import com.intellij.grazie.utils.Text;
import com.intellij.grazie.utils.ProblemFilterUtil;
import com.intellij.psi.javadoc.PsiDocTag;
import org.jetbrains.annotations.NotNull;
class JavadocProblemFilter extends ProblemFilter {
@Override
public boolean shouldIgnore(@NotNull TextProblem problem) {
if (problem.getText().getDomain() == TextContent.TextDomain.DOCUMENTATION && Text.isSingleSentence(problem.getText())) {
return problem.fitsGroup(RuleGroup.UNDECORATED_SINGLE_SENTENCE);
if (problem.getText().getDomain() == TextContent.TextDomain.DOCUMENTATION &&
problem.getText().getCommonParent() instanceof PsiDocTag &&
(ProblemFilterUtil.isUndecoratedSingleSentenceIssue(problem) || ProblemFilterUtil.isInitialCasingIssue(problem))) {
return true;
}
return false;
}
@@ -8,9 +8,10 @@ import java.util.stream.Stream;
/**
* An extension allowing to prevent some text problems from being reported,
* registered in {@code plugin.xml} under {@code "com.intellij.grazie.problemFilter"} qualified name.
* @see com.intellij.grazie.utils.ProblemFilterUtil
*/
public abstract class ProblemFilter {
private static final LanguageExtension<ProblemFilter> EP = new LanguageExtension<ProblemFilter>("com.intellij.grazie.problemFilter");
private static final LanguageExtension<ProblemFilter> EP = new LanguageExtension<>("com.intellij.grazie.problemFilter");
public static Stream<ProblemFilter> allIgnoringFilters(TextProblem problem) {
return EP.allForLanguageOrAny(problem.getText().getCommonParent().getLanguage()).stream().filter(f -> f.shouldIgnore(problem));
@@ -33,12 +33,15 @@ open class RuleGroup(rules: Set<String>) {
val EMPTY = RuleGroup()
/** Rules for checking casing errors */
@JvmField
val CASING = RuleGroup(SENTENCE_START_CASE)
/** Rules for checking punctuation errors */
@JvmField
val PUNCTUATION = RuleGroup(SENTENCE_END_PUNCTUATION, UNLIKELY_OPENING_PUNCTUATION)
/** Rules that are usually disabled for literal strings */
@JvmField
val LITERALS = CASING + PUNCTUATION
/**
@@ -0,0 +1,24 @@
package com.intellij.grazie.utils;
import com.intellij.grazie.text.RuleGroup;
import com.intellij.grazie.text.TextProblem;
import com.intellij.util.containers.ContainerUtil;
public final class ProblemFilterUtil {
/**
* Check if this problem reports a letter case issue at the very beginning of the text.
* Such issues are often ignored in documentation tags.
*/
public static boolean isInitialCasingIssue(TextProblem problem) {
return ContainerUtil.exists(problem.getHighlightRanges(), r -> r.getStartOffset() == 0) && problem.fitsGroup(RuleGroup.CASING);
}
/**
* Check if this problem reports a sentence capitalization or missing trailing punctuation issue in a single-sentence text fragment.
* Such issues are often ignored in documentation tags or comments.
*/
public static boolean isUndecoratedSingleSentenceIssue(TextProblem problem) {
return Text.isSingleSentence(problem.getText()) && problem.fitsGroup(RuleGroup.UNDECORATED_SINGLE_SENTENCE);
}
}
@@ -24,7 +24,7 @@ abstract class GrazieTestBase : BasePlatformTestCase() {
companion object {
val inspectionTools by lazy { arrayOf(GrazieInspection(), SpellCheckingInspection()) }
val enabledLanguages = setOf(Lang.AMERICAN_ENGLISH, Lang.GERMANY_GERMAN, Lang.RUSSIAN, Lang.ITALIAN)
val enabledRules = setOf("LanguageTool.EN.COMMA_WHICH")
val enabledRules = setOf("LanguageTool.EN.COMMA_WHICH", "LanguageTool.EN.UPPERCASE_SENTENCE_START")
}
protected open val additionalEnabledRules: Set<String> = emptySet()
@@ -255,7 +255,7 @@ public class TextExtractionTest extends BasePlatformTestCase {
assertEquals("|characters with markup\nand without it|",
unknownOffsets(extractText("a.xml", "<b><![CDATA[\n characters with markup\n]]>and without it</b>", 22)));
assertEquals("abcd", unknownOffsets(extractText("a.xml", "<tag attr=\"abcd\"/>", 14)));
assertEquals("abcd efg", unknownOffsets(extractText("a.xml", "<tag attr=\"abcd efg\"/>", 14)));
assertEquals("comment", extractText("a.xml", "<!-- comment -->", 10).toString());
assertEquals("top-level text", unknownOffsets(extractText("a.html", "top-level text", 2)));
@@ -10,7 +10,7 @@
* Item item = env.generateData(Generator.sampledFrom(sys.currentItems), "working on %s item");
* </code>
*
* @param T the type of member in this group.
* @param T the type of member in this group. And another sentence.
*/
class ExampleClassWithNoTypos<T> {
@@ -19,7 +19,7 @@ class ExampleClassWithNoTypos<T> {
/**
* Creates an empty group.
*
* @param name The name of the group.
* @param name The name of the group. And another sentence.
*/
public ExampleClassWithNoTypos(String name) {
this.name = name;
@@ -30,7 +30,7 @@ class ExampleClassWithNoTypos<T> {
*
* @param cancellable Whether the progress can be cancelled.
* @param member member to add
* @return the new size of the group.
* @return the new size of the group. And another sentence.
*/
Integer goodFunction(boolean cancellable, T member) {
return 1; // no error comment
@@ -70,8 +70,8 @@ class ExampleClassWithTypos<T> {
* It <warning descr="IT_VBZ">add</warning> a [member] to this <TYPO descr="Typo: In word 'grooup'">grooup</TYPO>.
* <warning descr="UPPERCASE_SENTENCE_START">second</warning> sentence.
*
* @param member member to add
* @return the new size of <warning descr="DT_DT">a the</warning> group.
* @param member member to add. And another sentence.
* @return the new size of <warning descr="DT_DT">a the</warning> group. <warning descr="UPPERCASE_SENTENCE_START">and</warning> another sentence.
*/
Integer badFunction(T member) {
return 1; // It <warning descr="IT_VBZ">are</warning> <TYPO descr="Typo: In word 'eror'">eror</TYPO> in the comment
@@ -11,7 +11,7 @@
class ExampleClassWithNoTypos {
/**
* Creates an empty group
* @param {String} name the name of the group
* @param {String} name the name of the group. And another sentence.
*/
constructor(name) {
/** @private */
@@ -6,7 +6,7 @@
</head>
<body>
<H1>Example <TYPO descr="Typo: In word 'hedder'">hedder</TYPO> with <warning descr="EN_A_VS_AN">a</warning> error, so what?</H1>\
<tag descr="it <warning descr="IT_VBZ">are</warning> error in tag <TYPO descr="Typo: In word 'atributee'">atributee</TYPO>"></tag>
<tag descr="<warning descr="UPPERCASE_SENTENCE_START">it</warning> <warning descr="IT_VBZ">are</warning> error in tag <TYPO descr="Typo: In word 'atributee'">atributee</TYPO>"></tag>
Some widely used comparison methods (e.g. <code>String.compareTo</code>)
actually return values.
@@ -3,7 +3,7 @@
xsi:noNamespaceSchemaLocation="Example.xsd">
<orderperson><TYPO descr="Typo: In word 'johnn'">johnn</TYPO> Smith</orderperson>
<shipto>
<name>First sentence. it <warning descr="EN_A_VS_AN">an</warning> friend. Last sentence.</name>
<name>First sentence. <warning descr="UPPERCASE_SENTENCE_START">it</warning> <warning descr="EN_A_VS_AN">an</warning> friend. Last sentence.</name>
<address>Some street 23</address>
</shipto>
<russian_with_newline>Не удалось авторизоваться.\nПопробуйте ещё раз. И <warning descr="Sklonenije_NUM_NN">пять карандаша</warning>.</russian_with_newline>
@@ -68,7 +68,10 @@ public class XmlTextExtractor extends TextExtractor {
}
if (type == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN && allowedDomains.contains(LITERALS) && hasSuitableDialect(element)) {
return builder.build(element, LITERALS);
TextContent content = builder.build(element, LITERALS);
if (content != null && seemsNatural(content)) {
return content;
}
}
return null;
@@ -157,6 +160,10 @@ public class XmlTextExtractor extends TextExtractor {
return visitor.result;
}
private static boolean seemsNatural(TextContent content) {
return content.toString().contains(" ");
}
private static TextContent extractRange(TextContent full, TextRange range) {
return full.excludeRange(new TextRange(range.getEndOffset(), full.length())).excludeRange(new TextRange(0, range.getStartOffset()));
}
@@ -6,6 +6,7 @@ import com.intellij.grazie.text.RuleGroup
import com.intellij.grazie.text.TextContent.TextDomain.DOCUMENTATION
import com.intellij.grazie.text.TextContent.TextDomain.LITERALS
import com.intellij.grazie.text.TextProblem
import com.intellij.grazie.utils.ProblemFilterUtil
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
class KotlinProblemFilter : ProblemFilter() {
@@ -14,8 +15,9 @@ class KotlinProblemFilter : ProblemFilter() {
if (domain == LITERALS) {
return problem.fitsGroup(RuleGroup.LITERALS)
}
if (domain == DOCUMENTATION && problem.text.commonParent::class == KDocTag::class) {
return problem.fitsGroup(RuleGroup.UNDECORATED_SINGLE_SENTENCE)
if (domain == DOCUMENTATION && problem.text.commonParent::class == KDocTag::class &&
(ProblemFilterUtil.isUndecoratedSingleSentenceIssue(problem) || ProblemFilterUtil.isInitialCasingIssue(problem))) {
return true
}
return false
}
@@ -8,7 +8,7 @@ package ide.language.kotlin
* This class has no useful logic; it's just a documentation example.
*
* @param T the type of member in this group.
* @property name the name of this group.
* @property name the name of this group. And another sentence.
* @constructor Creates an empty group.
*/
class ExampleClassWithNoTypos<T>(val name: String) {
@@ -25,7 +25,7 @@ class ExampleClassWithNoTypos<T>(val name: String) {
* It is <warning descr="EN_A_VS_AN">an</warning> friend of human
*
* @param T the <warning descr="KIND_OF_A">type of a</warning> <TYPO descr="Typo: In word 'membr'">membr</TYPO> in this group.
* @property name the <warning descr="COMMA_WHICH">name which</warning> group
* @property name the <warning descr="COMMA_WHICH">name which</warning> group. <warning descr="UPPERCASE_SENTENCE_START">and</warning> another sentence.
* @constructor Creates an empty group.
*/
class ExampleClassWithTypos<T>(val name: String) {
@@ -2,5 +2,6 @@
<extensions defaultExtensionNs="com.intellij.grazie">
<textExtractor language="Python" implementationClass="com.intellij.grazie.ide.language.python.PythonTextExtractor"/>
<problemFilter language="Python" implementationClass="com.intellij.grazie.text.LiteralProblemFilter"/>
<problemFilter language="Python" implementationClass="com.intellij.grazie.ide.language.python.PythonProblemFilter"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,25 @@
package com.intellij.grazie.ide.language.python
import com.intellij.grazie.text.ProblemFilter
import com.intellij.grazie.text.RuleGroup
import com.intellij.grazie.text.TextContent
import com.intellij.grazie.text.TextProblem
import com.intellij.grazie.utils.ProblemFilterUtil
class PythonProblemFilter : ProblemFilter() {
override fun shouldIgnore(problem: TextProblem): Boolean {
val domain = problem.text.domain
if (domain == TextContent.TextDomain.LITERALS) {
return problem.fitsGroup(RuleGroup.LITERALS)
}
if (domain == TextContent.TextDomain.DOCUMENTATION && seemsDocString(problem.text) &&
(ProblemFilterUtil.isUndecoratedSingleSentenceIssue(problem) || ProblemFilterUtil.isInitialCasingIssue(problem))) {
return true
}
return false
}
private fun seemsDocString(text: TextContent) =
text.containingFile.viewProvider.contents.subSequence(0, text.textOffsetToFile(0)).trim().endsWith(":")
}
+2 -2
View File
@@ -8,10 +8,10 @@ class ExampleClassWithNoTypos:
This class has no useful logic; it's just a documentation example.
Args:
name (str): the name of this group.
name (str): the name of this group. And another sentence.
Attributes:
name (str): the name of this group.
name (str): the name of this group. <warning descr="UPPERCASE_SENTENCE_START">and</warning> another sentence.
"""
@@ -2,6 +2,6 @@ def f(user):
"""
:param user: user
Loose punctuation mark.
:param user: user
:param user: <warning descr="UPPERCASE_SENTENCE_START">user</warning>
"""
pass