IDEA-50496 Spellchecker doesn't honor character escapes in Java/Groovy string literals

This commit is contained in:
peter
2012-07-03 19:33:39 +02:00
parent 4b98bf2c5c
commit 488b0bc3e2
4 changed files with 38 additions and 16 deletions
@@ -16,10 +16,10 @@
package org.jetbrains.plugins.groovy.codeInspection.spellchecker;
import com.intellij.codeInspection.SuppressIntentionAction;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.spellchecker.inspections.PlainTextSplitter;
import com.intellij.spellchecker.tokenizer.EscapeSequenceTokenizer;
import com.intellij.spellchecker.tokenizer.SuppressibleSpellcheckingStrategy;
import com.intellij.spellchecker.tokenizer.TokenConsumer;
import com.intellij.spellchecker.tokenizer.Tokenizer;
@@ -27,31 +27,43 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.codeInspection.GroovySuppressableInspectionTool;
import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement;
import org.jetbrains.plugins.groovy.lang.resolve.GroovyStringLiteralManipulator;
import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
/**
* @author peter
*/
public class GroovySpellcheckingStrategy extends SuppressibleSpellcheckingStrategy {
private final GrDocCommentTokenizer myDocCommentTokenizer = new GrDocCommentTokenizer();
private Tokenizer<PsiElement> myStringTokenizer = new Tokenizer<PsiElement>() {
@Override
public void tokenize(@NotNull PsiElement literal, TokenConsumer consumer) {
String text = GrStringUtil.removeQuotes(literal.getText());
if (!text.contains("\\")) {
consumer.consumeToken(literal, PlainTextSplitter.getInstance());
}
else {
StringBuilder unescapedText = new StringBuilder();
int[] offsets = new int[text.length() + 1];
GrStringUtil.parseStringCharacters(text, unescapedText, offsets);
EscapeSequenceTokenizer.processTextWithOffsets(literal, consumer, unescapedText, offsets, GrStringUtil.getStartQuote(literal.getText()).length());
}
}
};
@NotNull
@Override
public Tokenizer getTokenizer(PsiElement element) {
if (TokenSets.STRING_LITERAL_SET.contains(element.getNode().getElementType())) {
return myStringTokenizer;
}
if (element instanceof GrNamedElement) {
final PsiElement name = ((GrNamedElement)element).getNameIdentifierGroovy();
if (TokenSets.STRING_LITERAL_SET.contains(name.getNode().getElementType())) {
return new Tokenizer<GrNamedElement>() {
@Override
public void tokenize(@NotNull GrNamedElement element, TokenConsumer consumer) {
String text = name.getText();
TextRange range = GroovyStringLiteralManipulator.getLiteralRange(text);
consumer.consumeToken(name, text, false, 0, range, PlainTextSplitter.getInstance());
}
};
return EMPTY_TOKENIZER;
}
}
if (element instanceof PsiDocComment) return myDocCommentTokenizer;
//if (element instanceof GrLiteralImpl && ((GrLiteralImpl)element).isStringLiteral()) return myStringTokenizer;
return super.getTokenizer(element);
}
@@ -45,5 +45,15 @@ class SpockTest {
'''
checkTypos()
}
public void testStringEscapes() {
myFixture.configureByText 'a.groovy', '''
def foo = "\\ntest \\n<TYPO descr="Typo: In word 'dddd'">dddd</TYPO>"
def foo1 = '\\ntest \\n<TYPO descr="Typo: In word 'dddd'">dddd</TYPO>'
def bar = """\\ntest \\n<TYPO descr="Typo: In word 'dddd'">dddd</TYPO>"""
def bar1 = \'''\\ntest \\n<TYPO descr="Typo: In word 'dddd'">dddd</TYPO>\'''
'''
checkTypos()
}
}
@@ -64,6 +64,6 @@ public class LiteralExpressionTokenizer extends Tokenizer<PsiLiteralExpression>
int[] offsets = new int[text.length()+1];
PsiLiteralExpressionImpl.parseStringCharacters(text, unescapedText, offsets);
EscapeSequenceTokenizer.processTextWithOffsets(element, consumer, unescapedText, offsets);
EscapeSequenceTokenizer.processTextWithOffsets(element, consumer, unescapedText, offsets, 1);
}
}
@@ -24,13 +24,13 @@ import com.intellij.spellchecker.inspections.PlainTextSplitter;
*/
public class EscapeSequenceTokenizer {
public static void processTextWithOffsets(PsiElement element, TokenConsumer consumer, StringBuilder unescapedText,
int[] offsets) {
int[] offsets, int startOffset) {
StringBuilder currentToken = new StringBuilder();
int currentTokenStart = 0;
int currentTokenStart = startOffset;
for (int i = 0; i < unescapedText.length(); i++) {
if (offsets[i+1]-offsets[i] == 1) {
if (currentToken.length() == 0) {
currentTokenStart = offsets[i];
currentTokenStart = offsets[i] + startOffset;
}
currentToken.append(unescapedText.charAt(i));
}
@@ -51,6 +51,6 @@ public class EscapeSequenceTokenizer {
int currentTokenStart, TokenConsumer consumer) {
final String token = currentToken.toString();
// +1 for the starting quote of the string literal
consumer.consumeToken(element, token, false, currentTokenStart+1, TextRange.allOf(token), PlainTextSplitter.getInstance());
consumer.consumeToken(element, token, false, currentTokenStart, TextRange.allOf(token), PlainTextSplitter.getInstance());
}
}