diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml
index 89a695f570c4..64adafd7c20d 100644
--- a/python/src/META-INF/python-plugin-common.xml
+++ b/python/src/META-INF/python-plugin-common.xml
@@ -232,6 +232,8 @@
+
+
diff --git a/python/src/com/jetbrains/python/editor/BaseQuoteHandler.java b/python/src/com/jetbrains/python/editor/BaseQuoteHandler.java
new file mode 100644
index 000000000000..de28ca9c3673
--- /dev/null
+++ b/python/src/com/jetbrains/python/editor/BaseQuoteHandler.java
@@ -0,0 +1,69 @@
+package com.jetbrains.python.editor;
+
+import com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.highlighter.HighlighterIterator;
+import com.intellij.psi.tree.TokenSet;
+import com.jetbrains.python.PyTokenTypes;
+
+import java.util.Arrays;
+
+/**
+ * @author yole
+ */
+public class BaseQuoteHandler extends SimpleTokenSetQuoteHandler {
+
+ private final char[] ourAutoClosingChars; // we add auto-close quotes before these
+
+ public BaseQuoteHandler(TokenSet tokenSet, char[] autoClosingChars) {
+ super(tokenSet);
+ ourAutoClosingChars = autoClosingChars;
+ Arrays.sort(ourAutoClosingChars);
+ }
+
+ @Override
+ public boolean isOpeningQuote(HighlighterIterator iterator, int offset) {
+ // don't assume an opening quote unless we're in an explicitly "blank" context
+ CharSequence text = iterator.getDocument().getCharsSequence();
+ if (offset+1 >= text.length() || Arrays.binarySearch(ourAutoClosingChars, text.charAt(offset+1)) >= 0) {
+ char the_quote = text.charAt(offset);
+ // if we're next to two same quotes, don't auto-close, the user may want a triple quote
+ if (
+ offset >= 2 &&
+ text.charAt(offset-1) == the_quote &&
+ text.charAt(offset-2) == the_quote &&
+ (offset < 3 || text.charAt(offset-3) != the_quote)
+ ) return false;
+ // handle string literal context
+ if (super.isOpeningQuote(iterator, offset)) {
+ return true;
+ }
+ if (myLiteralTokenSet.contains(iterator.getTokenType())) {
+ int start = iterator.getStart();
+ if (offset - start <= 2) {
+ if (getLiteralStartOffset(text, start) == offset) return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static int getLiteralStartOffset(CharSequence text, int start) {
+ char c = Character.toUpperCase(text.charAt(start));
+ if (c == 'U' || c == 'B') {
+ start++;
+ c = Character.toUpperCase(text.charAt(start));
+ }
+ if (c == 'R') {
+ start++;
+ }
+ return start;
+ }
+
+ @Override
+ protected boolean isNonClosedLiteral(HighlighterIterator iterator, CharSequence chars) {
+ if (getLiteralStartOffset(chars, iterator.getStart()) >= iterator.getEnd() - 1) return true;
+ if (chars.charAt(iterator.getEnd() - 1) != '\"' && chars.charAt(iterator.getEnd() - 1) != '\'') return true;
+ return false;
+ }
+}
diff --git a/python/src/com/jetbrains/python/editor/PythonQuoteHandler.java b/python/src/com/jetbrains/python/editor/PythonQuoteHandler.java
index de52b1b2837c..3ad479a24ae6 100644
--- a/python/src/com/jetbrains/python/editor/PythonQuoteHandler.java
+++ b/python/src/com/jetbrains/python/editor/PythonQuoteHandler.java
@@ -1,70 +1,13 @@
package com.jetbrains.python.editor;
-import com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.highlighter.HighlighterIterator;
+import com.intellij.psi.tree.TokenSet;
import com.jetbrains.python.PyTokenTypes;
-import java.util.Arrays;
-
/**
- * @author yole
+ * @author traff
*/
-public class PythonQuoteHandler extends SimpleTokenSetQuoteHandler {
-
- private static final char[] ourAutoClosingChars; // we add auto-close quotes before these
- static {
- ourAutoClosingChars = new char[]{'}', ']', ')', ',', ':', ';', ' ', '\t', '\n'};
- Arrays.sort(ourAutoClosingChars);
- }
-
+public class PythonQuoteHandler extends BaseQuoteHandler {
public PythonQuoteHandler() {
- super(PyTokenTypes.STRING_LITERAL);
- }
-
- @Override
- public boolean isOpeningQuote(HighlighterIterator iterator, int offset) {
- // don't assume an opening quote unless we're in an explicitly "blank" context
- CharSequence text = iterator.getDocument().getCharsSequence();
- if (offset+1 >= text.length() || Arrays.binarySearch(ourAutoClosingChars, text.charAt(offset+1)) >= 0) {
- char the_quote = text.charAt(offset);
- // if we're next to two same quotes, don't auto-close, the user may want a triple quote
- if (
- offset >= 2 &&
- text.charAt(offset-1) == the_quote &&
- text.charAt(offset-2) == the_quote &&
- (offset < 3 || text.charAt(offset-3) != the_quote)
- ) return false;
- // handle string literal context
- if (super.isOpeningQuote(iterator, offset)) {
- return true;
- }
- if (myLiteralTokenSet.contains(iterator.getTokenType())) {
- int start = iterator.getStart();
- if (offset - start <= 2) {
- if (getLiteralStartOffset(text, start) == offset) return true;
- }
- }
- }
- return false;
- }
-
- private static int getLiteralStartOffset(CharSequence text, int start) {
- char c = Character.toUpperCase(text.charAt(start));
- if (c == 'U' || c == 'B') {
- start++;
- c = Character.toUpperCase(text.charAt(start));
- }
- if (c == 'R') {
- start++;
- }
- return start;
- }
-
- @Override
- protected boolean isNonClosedLiteral(HighlighterIterator iterator, CharSequence chars) {
- if (getLiteralStartOffset(chars, iterator.getStart()) >= iterator.getEnd() - 1) return true;
- if (chars.charAt(iterator.getEnd() - 1) != '\"' && chars.charAt(iterator.getEnd() - 1) != '\'') return true;
- return false;
+ super(TokenSet.create(PyTokenTypes.STRING_LITERAL), new char[]{'}', ']', ')', ',', ':', ';', ' ', '\t', '\n'});
}
}