mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[grazie] ignore sentence capitalization issues in various doc tags
GitOrigin-RevId: e1563f745ea25399d4b62ae3661f38a76d8f24b6
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ca9047e41d
commit
fc8165132e
+6
-4
@@ -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>
|
||||
|
||||
+8
-1
@@ -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>
|
||||
+25
@@ -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(":")
|
||||
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user