948d14aaf5419833e0eaecdbefed44bc6bfbb36d
[idea/community.git] / python / src / com / jetbrains / python / spellchecker / PythonSpellcheckerStrategy.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.spellchecker;
17
18 import com.intellij.openapi.util.Pair;
19 import com.intellij.openapi.util.TextRange;
20 import com.intellij.psi.PsiElement;
21 import com.intellij.spellchecker.inspections.PlainTextSplitter;
22 import com.intellij.spellchecker.inspections.Splitter;
23 import com.intellij.spellchecker.tokenizer.SpellcheckingStrategy;
24 import com.intellij.spellchecker.tokenizer.TokenConsumer;
25 import com.intellij.spellchecker.tokenizer.Tokenizer;
26 import com.jetbrains.python.PyTokenTypes;
27 import com.jetbrains.python.PythonStringUtil;
28 import com.jetbrains.python.inspections.PyStringFormatParser;
29 import com.jetbrains.python.psi.PyBinaryExpression;
30 import com.jetbrains.python.psi.PyStringLiteralExpression;
31 import org.jetbrains.annotations.NotNull;
32
33 import java.util.List;
34
35 /**
36  * @author yole
37  */
38 public class PythonSpellcheckerStrategy extends SpellcheckingStrategy {
39   private static class StringLiteralTokenizer extends Tokenizer<PyStringLiteralExpression> {
40     @Override
41     public void tokenize(@NotNull PyStringLiteralExpression element, TokenConsumer consumer) {
42       final Splitter splitter = PlainTextSplitter.getInstance();
43       final String text = element.getText();
44       final String prefix = PythonStringUtil.getPrefix(text);
45       if (text.indexOf('\\') >= 0 && !PythonStringUtil.isRawPrefix(prefix)) {
46         for (Pair<TextRange, String> fragment : element.getDecodedFragments()) {
47           final String value = fragment.getSecond();
48           final int startOffset = fragment.getFirst().getStartOffset();
49           consumer.consumeToken(element, value, false, startOffset, TextRange.allOf(value), splitter);
50         }
51       }
52       else if (!prefix.isEmpty()) {
53         for (TextRange valueTextRange : element.getStringValueTextRanges()) {
54           final String value = valueTextRange.substring(element.getText());
55           final int startOffset = valueTextRange.getStartOffset();
56           consumer.consumeToken(element, value, false, startOffset, TextRange.allOf(value), splitter);
57         }
58       }
59       else {
60         consumer.consumeToken(element, splitter);
61       }
62     }
63   }
64
65   private static class FormatStringTokenizer extends Tokenizer<PyStringLiteralExpression> {
66     @Override
67     public void tokenize(@NotNull PyStringLiteralExpression element, TokenConsumer consumer) {
68       String stringValue = element.getStringValue();
69       List<PyStringFormatParser.FormatStringChunk> chunks = PyStringFormatParser.parsePercentFormat(stringValue);
70       Splitter splitter = PlainTextSplitter.getInstance();
71       for (PyStringFormatParser.FormatStringChunk chunk : chunks) {
72         if (chunk instanceof PyStringFormatParser.ConstantChunk) {
73           int startIndex = element.valueOffsetToTextOffset(chunk.getStartIndex());
74           int endIndex = element.valueOffsetToTextOffset(chunk.getEndIndex());
75           String text = element.getText().substring(startIndex, endIndex);
76           consumer.consumeToken(element, text, false, startIndex, TextRange.allOf(text), splitter);
77         }
78       }
79     }
80   }
81
82   private StringLiteralTokenizer myStringLiteralTokenizer = new StringLiteralTokenizer();
83   private FormatStringTokenizer myFormatStringTokenizer = new FormatStringTokenizer();
84
85   @NotNull
86   @Override
87   public Tokenizer getTokenizer(PsiElement element) {
88     if (element instanceof PyStringLiteralExpression) {
89       PsiElement parent = element.getParent();
90       if (parent instanceof PyBinaryExpression) {
91         PyBinaryExpression binaryExpression = (PyBinaryExpression)parent;
92         if (element == binaryExpression.getLeftExpression() && binaryExpression.getOperator() == PyTokenTypes.PERC) {
93           return myFormatStringTokenizer;
94         }
95       }
96       return myStringLiteralTokenizer;
97     }
98     return super.getTokenizer(element);
99   }
100 }