diff --git a/python/psi-api/src/com/jetbrains/python/PyNames.java b/python/psi-api/src/com/jetbrains/python/PyNames.java
index a35b3c3f60c5..ae6438bd0770 100644
--- a/python/psi-api/src/com/jetbrains/python/PyNames.java
+++ b/python/psi-api/src/com/jetbrains/python/PyNames.java
@@ -119,6 +119,8 @@ public class PyNames {
public static final String COLLECTIONS = "collections";
public static final String COLLECTIONS_NAMEDTUPLE = COLLECTIONS + "." + NAMEDTUPLE;
+ public static final String FORMAT = "format";
+
public static final String ABSTRACTMETHOD = "abc.abstractmethod";
public static final String ABSTRACTPROPERTY = "abc.abstractproperty";
diff --git a/python/rest/resources/META-INF/plugin.xml b/python/rest/resources/META-INF/plugin.xml
index 5a75d1bd0033..06cd1f494960 100644
--- a/python/rest/resources/META-INF/plugin.xml
+++ b/python/rest/resources/META-INF/plugin.xml
@@ -3,7 +3,7 @@
org.jetbrains.plugins.rest
This plugin enables support for reStructuredText files (*.rst)
JetBrains
- 130.SNAPSHOT
+ 132.SNAPSHOT
com.intellij.modules.lang
diff --git a/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java
new file mode 100644
index 000000000000..e43be27b6fc2
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java
@@ -0,0 +1,138 @@
+package com.jetbrains.python.codeInsight;
+
+import com.intellij.lang.injection.MultiHostRegistrar;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+import static com.jetbrains.python.inspections.PyStringFormatParser.*;
+
+/**
+ * @author vlan
+ */
+public class PyInjectionUtil {
+ private PyInjectionUtil() {}
+
+ /**
+ * Returns true if the element is the largest expression that represents a string literal, possibly with concatenation, parentheses,
+ * or formatting.
+ */
+ public static boolean isLargestStringLiteral(@NotNull PsiElement element) {
+ final PsiElement parent = element.getParent();
+ return isStringLiteralPart(element) && (parent == null || !isStringLiteralPart(parent));
+ }
+
+ /**
+ * Registers language injections in the given registrar for the specified string literal element or its ancestor that contains
+ * string concatenations or formatting.
+ */
+ public static void registerStringLiteralInjection(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar) {
+ processStringLiteral(element, registrar, "", "", Formatting.NONE);
+ }
+
+ private static boolean isStringLiteralPart(@NotNull PsiElement element) {
+ if (element instanceof PyStringLiteralExpression) {
+ return true;
+ }
+ else if (element instanceof PyParenthesizedExpression) {
+ final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression();
+ return contained != null && isStringLiteralPart(contained);
+ }
+ else if (element instanceof PyBinaryExpression) {
+ final PyBinaryExpression expr = (PyBinaryExpression)element;
+ final PyExpression left = expr.getLeftExpression();
+ final PyExpression right = expr.getRightExpression();
+ return (expr.isOperator("+") && (isStringLiteralPart(left) || right != null && isStringLiteralPart(right))) ||
+ expr.isOperator("%") && isStringLiteralPart(left);
+ }
+ else if (element instanceof PyCallExpression) {
+ final PyExpression qualifier = getFormatCallQualifier((PyCallExpression)element);
+ return qualifier != null && isStringLiteralPart(qualifier);
+ }
+ return false;
+ }
+
+ @Nullable
+ private static PyExpression getFormatCallQualifier(@NotNull PyCallExpression element) {
+ final PyExpression callee = element.getCallee();
+ if (callee instanceof PyQualifiedExpression) {
+ final PyQualifiedExpression qualifiedExpr = (PyQualifiedExpression)callee;
+ final PyExpression qualifier = qualifiedExpr.getQualifier();
+ if (qualifier != null && PyNames.FORMAT.equals(qualifiedExpr.getReferencedName())) {
+ return qualifier;
+ }
+ }
+ return null;
+ }
+
+ private static void processStringLiteral(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar, @NotNull String prefix,
+ @NotNull String suffix, @NotNull Formatting formatting) {
+ final String missingValue = "missing";
+ if (element instanceof PyStringLiteralExpression) {
+ final PyStringLiteralExpression expr = (PyStringLiteralExpression)element;
+ final List ranges = expr.getStringValueTextRanges();
+ final String text = expr.getText();
+ for (TextRange range : ranges) {
+ if (formatting != Formatting.NONE) {
+ final String part = range.substring(text);
+ final List chunks = formatting == Formatting.NEW_STYLE ? parseNewStyleFormat(part) : parsePercentFormat(part);
+ for (int i = 0; i < chunks.size(); i++) {
+ final FormatStringChunk chunk = chunks.get(i);
+ if (chunk instanceof ConstantChunk) {
+ final int nextIndex = i + 1;
+ final String chunkPrefix = i == 1 && chunks.get(0) instanceof SubstitutionChunk ? missingValue : "";
+ final String chunkSuffix = nextIndex < chunks.size() &&
+ chunks.get(nextIndex) instanceof SubstitutionChunk ? missingValue : "";
+ final TextRange chunkRange = chunk.getTextRange().shiftRight(range.getStartOffset());
+ registrar.addPlace(chunkPrefix, chunkSuffix, expr, chunkRange);
+ }
+ }
+ }
+ else {
+ registrar.addPlace(prefix, suffix, expr, range);
+ }
+ }
+ }
+ else if (element instanceof PyParenthesizedExpression) {
+ final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression();
+ if (contained != null) {
+ processStringLiteral(contained, registrar, prefix, suffix, formatting);
+ }
+ }
+ else if (element instanceof PyBinaryExpression) {
+ final PyBinaryExpression expr = (PyBinaryExpression)element;
+ final PyExpression left = expr.getLeftExpression();
+ final PyExpression right = expr.getRightExpression();
+ final boolean isLeftString = isStringLiteralPart(left);
+ if (expr.isOperator("+")) {
+ final boolean isRightString = right != null && isStringLiteralPart(right);
+ if (isLeftString) {
+ processStringLiteral(left, registrar, prefix, isRightString ? "" : missingValue, formatting);
+ }
+ if (isRightString) {
+ processStringLiteral(right, registrar, isLeftString ? "" : missingValue, suffix, formatting);
+ }
+ }
+ else if (expr.isOperator("%")) {
+ processStringLiteral(left, registrar, prefix, suffix, Formatting.PERCENT);
+ }
+ }
+ else if (element instanceof PyCallExpression) {
+ final PyExpression qualifier = getFormatCallQualifier((PyCallExpression)element);
+ if (qualifier != null) {
+ processStringLiteral(qualifier, registrar, prefix, suffix, Formatting.NEW_STYLE);
+ }
+ }
+ }
+
+ private enum Formatting {
+ NONE,
+ PERCENT,
+ NEW_STYLE
+ }
+}
diff --git a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java
index 123ac49c8b1c..b6cdfd738b9a 100644
--- a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java
+++ b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java
@@ -3,11 +3,11 @@ package com.jetbrains.python.codeInsight.regexp;
import com.intellij.lang.Language;
import com.intellij.lang.injection.MultiHostInjector;
import com.intellij.lang.injection.MultiHostRegistrar;
-import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiPolyVariantReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
+import com.jetbrains.python.codeInsight.PyInjectionUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import org.jetbrains.annotations.NotNull;
@@ -51,7 +51,7 @@ public class PythonRegexpInjector implements MultiHostInjector {
@Override
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
final PsiElement contextParent = context.getParent();
- if (isStringLiteral(context) && contextParent instanceof PyArgumentList) {
+ if (PyInjectionUtil.isLargestStringLiteral(context) && contextParent instanceof PyArgumentList) {
final PyExpression[] args = ((PyArgumentList)contextParent).getArguments();
int index = ArrayUtil.indexOf(args, context);
PyCallExpression call = PsiTreeUtil.getParentOfType(context, PyCallExpression.class);
@@ -64,74 +64,20 @@ public class PythonRegexpInjector implements MultiHostInjector {
if (element != null && element.getContainingFile().getName().equals("re.py") && isRegexpMethod(element, index)) {
final Language language = isVerbose(call) ? PythonVerboseRegexpLanguage.INSTANCE : PythonRegexpLanguage.INSTANCE;
registrar.startInjecting(language);
- processStringLiteral(context, registrar);
+ PyInjectionUtil.registerStringLiteralInjection(context, registrar);
registrar.doneInjecting();
}
}
}
}
}
-
- }
-
- private static boolean isStringLiteral(@NotNull PsiElement element) {
- if (element instanceof PyStringLiteralExpression) {
- return true;
- }
- else if (element instanceof PyParenthesizedExpression) {
- final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression();
- return contained != null && isStringLiteral(contained);
- }
- else if (element instanceof PyBinaryExpression) {
- final PyBinaryExpression expr = (PyBinaryExpression)element;
- final PyExpression left = expr.getLeftExpression();
- final PyExpression right = expr.getRightExpression();
- return expr.isOperator("+") && (isStringLiteral(left) || right != null && isStringLiteral(right));
- }
- return false;
- }
-
- private static void processStringLiteral(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar) {
- processStringLiteral(element, registrar, "", "");
- }
-
- private static void processStringLiteral(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar, @NotNull String prefix,
- @NotNull String suffix) {
- final String missingValue = "missing";
- if (element instanceof PyStringLiteralExpression) {
- final PyStringLiteralExpression expr = (PyStringLiteralExpression)element;
- final List ranges = expr.getStringValueTextRanges();
- for (TextRange range : ranges) {
- registrar.addPlace(prefix, suffix, expr, range);
- }
- }
- else if (element instanceof PyParenthesizedExpression) {
- final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression();
- if (contained != null) {
- processStringLiteral(contained, registrar, prefix, suffix);
- }
- }
- else if (element instanceof PyBinaryExpression) {
- final PyBinaryExpression expr = (PyBinaryExpression)element;
- if (expr.isOperator("+")) {
- final PyExpression left = expr.getLeftExpression();
- final PyExpression right = expr.getRightExpression();
- final boolean isLeftString = isStringLiteral(left);
- final boolean isRightString = right != null && isStringLiteral(right);
- if (isLeftString) {
- processStringLiteral(left, registrar, prefix, isRightString ? "" : missingValue);
- }
- if (isRightString) {
- processStringLiteral(right, registrar, isLeftString ? "" : missingValue, suffix);
- }
- }
- }
}
@NotNull
@Override
public List extends Class extends PsiElement>> elementsToInjectIn() {
- return Arrays.asList(PyStringLiteralExpression.class, PyParenthesizedExpression.class, PyBinaryExpression.class);
+ return Arrays.asList(PyStringLiteralExpression.class, PyParenthesizedExpression.class, PyBinaryExpression.class,
+ PyCallExpression.class);
}
private static boolean isVerbose(@NotNull PyCallExpression call) {
diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java
index 979a5f0d7fa9..b61d2a8ffb6d 100644
--- a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java
@@ -23,6 +23,9 @@ import java.math.BigInteger;
import java.util.List;
import java.util.Map;
+import static com.jetbrains.python.inspections.PyStringFormatParser.filterSubstitutions;
+import static com.jetbrains.python.inspections.PyStringFormatParser.parsePercentFormat;
+
/**
* @author Alexey.Ivanov
*/
@@ -332,8 +335,8 @@ public class PyStringFormatInspection extends PyInspection {
}
private void inspectFormat(@NotNull final PyStringLiteralExpression formatExpression) {
- PyStringFormatParser parser = new PyStringFormatParser(formatExpression.getStringValue());
- final List chunks = parser.parseSubstitutions();
+ final String value = formatExpression.getStringValue();
+ final List chunks = filterSubstitutions(parsePercentFormat(value));
// 1. The '%' character
// Skip the first item in the sections, it's always empty
diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java b/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java
index 97a34fa72a38..8d4ee00a39ca 100644
--- a/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java
+++ b/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java
@@ -4,6 +4,7 @@ import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.HashMap;
+import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl;
import org.jetbrains.annotations.NotNull;
@@ -145,10 +146,15 @@ public class PyStringFormatParser {
private static final String LENGTH_MODIFIERS = "hlL";
private static final String VALID_CONVERSION_TYPES = "diouxXeEfFgGcrs";
- public PyStringFormatParser(@NotNull String literal) {
+ private PyStringFormatParser(@NotNull String literal) {
myLiteral = literal;
}
+ @NotNull
+ public static List parsePercentFormat(@NotNull String s) {
+ return new PyStringFormatParser(s).parse();
+ }
+
@NotNull
public static List parseNewStyleFormat(@NotNull String s) {
final List results = new ArrayList();
@@ -191,13 +197,13 @@ public class PyStringFormatParser {
pos = next + 1;
}
if (pos < n) {
- results.add(new ConstantChunk(pos, n + 1));
+ results.add(new ConstantChunk(pos, n));
}
return results;
}
@NotNull
- public List parse() {
+ private List parse() {
myPos = 0;
while(myPos < myLiteral.length()) {
int next = myLiteral.indexOf('%', myPos);
@@ -277,11 +283,6 @@ public class PyStringFormatParser {
return myLiteral.substring(flagStart, myPos);
}
- @NotNull
- public List parseSubstitutions() {
- return filterSubstitutions(parse());
- }
-
@NotNull
public static List filterSubstitutions(@NotNull List chunks) {
final List results = new ArrayList();
@@ -353,7 +354,7 @@ public class PyStringFormatParser {
if (parent instanceof PyQualifiedExpression) {
final PyQualifiedExpression qualifiedExpr = (PyQualifiedExpression)parent;
final String name = qualifiedExpr.getReferencedName();
- if ("format".equals(name)) {
+ if (PyNames.FORMAT.equals(name)) {
final PsiElement parent2 = qualifiedExpr.getParent();
if (parent2 instanceof PyCallExpression) {
final PyCallExpression callExpr = (PyCallExpression)parent2;
diff --git a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java
index 166b18b82749..379835231e7c 100644
--- a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java
+++ b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java
@@ -24,6 +24,9 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import static com.jetbrains.python.PyTokenTypes.*;
+import static com.jetbrains.python.inspections.PyStringFormatParser.filterSubstitutions;
+import static com.jetbrains.python.inspections.PyStringFormatParser.parseNewStyleFormat;
+import static com.jetbrains.python.inspections.PyStringFormatParser.parsePercentFormat;
/**
* @author Dennis.Ushakov
@@ -94,10 +97,10 @@ public class PyReplaceExpressionUtil implements PyElementTypes {
final List substitutions;
if (newStyleFormatValue != null) {
- substitutions = PyStringFormatParser.filterSubstitutions(PyStringFormatParser.parseNewStyleFormat(fullText));
+ substitutions = filterSubstitutions(parseNewStyleFormat(fullText));
}
else {
- substitutions = new PyStringFormatParser(fullText).parseSubstitutions();
+ substitutions = filterSubstitutions(parsePercentFormat(fullText));
}
final boolean hasSubstitutions = substitutions.size() > 0;
diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java
index 411c34396743..0217a936b278 100644
--- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java
@@ -31,7 +31,6 @@ import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonStringUtil;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
-import com.jetbrains.python.inspections.PyStringFormatParser;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.types.PyNoneType;
@@ -306,7 +305,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
}
private boolean breaksStringFormatting(@NotNull String s, @NotNull TextRange range) {
- return breaksRanges(substitutionsToRanges(new PyStringFormatParser(s).parseSubstitutions()), range);
+ return breaksRanges(substitutionsToRanges(filterSubstitutions(parsePercentFormat(s))), range);
}
private boolean breaksNewStyleStringFormatting(@NotNull String s, @NotNull TextRange range) {
diff --git a/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java b/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java
index 7be161979cd2..aa4e081bef8b 100644
--- a/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java
+++ b/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java
@@ -3,6 +3,7 @@ package com.jetbrains.python.sdk;
import com.google.common.collect.Lists;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
@@ -130,7 +131,9 @@ public class InterpreterPathChooser extends BaseListPopupStep {
remoteInterpreterManager.addRemoteSdk(myProject, myOwnerComponent, Lists.newArrayList(myExistingSdks), myCallback);
}
else {
- Messages.showErrorDialog("The Remote Hosts Access plugin is missing. Please enable the plugin in Settings | Plugins.", "Add Remote Interpreter");
+ Messages.showErrorDialog("The Remote Hosts Access plugin is missing. Please enable the plugin in " +
+ ShowSettingsUtil.getSettingsMenuName() +
+ " | Plugins.", "Add Remote Interpreter");
}
}
diff --git a/python/src/com/jetbrains/python/spellchecker/PythonSpellcheckerStrategy.java b/python/src/com/jetbrains/python/spellchecker/PythonSpellcheckerStrategy.java
index 669ff4b001ba..645a6a26c4a5 100644
--- a/python/src/com/jetbrains/python/spellchecker/PythonSpellcheckerStrategy.java
+++ b/python/src/com/jetbrains/python/spellchecker/PythonSpellcheckerStrategy.java
@@ -82,7 +82,7 @@ public class PythonSpellcheckerStrategy extends SpellcheckingStrategy {
@Override
public void tokenize(@NotNull PyStringLiteralExpression element, TokenConsumer consumer) {
String stringValue = element.getStringValue();
- List chunks = new PyStringFormatParser(stringValue).parse();
+ List chunks = PyStringFormatParser.parsePercentFormat(stringValue);
Splitter splitter = PlainTextSplitter.getInstance();
for (PyStringFormatParser.FormatStringChunk chunk : chunks) {
if (chunk instanceof PyStringFormatParser.ConstantChunk) {
diff --git a/python/testSrc/com/jetbrains/python/PyRegexpTest.java b/python/testSrc/com/jetbrains/python/PyRegexpTest.java
index 2b730f9c0ecc..9dddbff96492 100644
--- a/python/testSrc/com/jetbrains/python/PyRegexpTest.java
+++ b/python/testSrc/com/jetbrains/python/PyRegexpTest.java
@@ -111,6 +111,30 @@ public class PyRegexpTest extends PyTestCase {
".*(missing)missing");
}
+ public void testPercentFormattingRegexpAutoInjection() {
+ doTestInjectedText("import re \n" +
+ "\n" +
+ "def f(x, y):\n" +
+ " re.search('.*%s-%d' % (x, y), 'foo')\n",
+ ".*missing-missing");
+ }
+
+ public void testNewStyleFormattingRegexpAutoInjection() {
+ doTestInjectedText("import re\n" +
+ "\n" +
+ "def f(x, y):\n" +
+ " re.search('.*{foo}-{}'.format(x, foo=y), 'foo')\n",
+ ".*missing-missing");
+ }
+
+ public void testNewStyleFormattingEndsWithConstant() {
+ doTestInjectedText("import re\n" +
+ "\n" +
+ "def f(**kwargs):" +
+ " re.search('(foo{bar}baz$)'.format(**kwargs), 'foo')\n",
+ "(foomissingbaz$)");
+ }
+
private void doTestInjectedText(@NotNull String text, @NotNull String expected) {
myFixture.configureByText(PythonFileType.INSTANCE, text);
final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject());
diff --git a/python/testSrc/com/jetbrains/python/PyStringFormatParserTest.java b/python/testSrc/com/jetbrains/python/PyStringFormatParserTest.java
index b64593674850..b2ed8f5f608d 100644
--- a/python/testSrc/com/jetbrains/python/PyStringFormatParserTest.java
+++ b/python/testSrc/com/jetbrains/python/PyStringFormatParserTest.java
@@ -1,7 +1,6 @@
package com.jetbrains.python;
import com.intellij.openapi.util.TextRange;
-import com.jetbrains.python.inspections.PyStringFormatParser;
import junit.framework.TestCase;
import java.util.List;
@@ -13,7 +12,7 @@ import static com.jetbrains.python.inspections.PyStringFormatParser.*;
*/
public class PyStringFormatParserTest extends TestCase {
public void testSimple() {
- List chunks = new PyStringFormatParser("abc").parse();
+ List chunks = parsePercentFormat("abc");
assertEquals(1, chunks.size());
assertConstant(chunks.get(0), 0, 3);
}
@@ -25,13 +24,13 @@ public class PyStringFormatParserTest extends TestCase {
}
public void testDoublePercent() {
- List chunks = new PyStringFormatParser("abc%%def").parse();
+ List chunks = parsePercentFormat("abc%%def");
assertEquals(1, chunks.size());
assertConstant(chunks.get(0), 0, 8);
}
public void testFormat() {
- List chunks = new PyStringFormatParser("%s").parse();
+ List chunks = parsePercentFormat("%s");
assertEquals(1, chunks.size());
SubstitutionChunk chunk = (SubstitutionChunk) chunks.get(0);
assertEquals(0, chunk.getStartIndex());
@@ -40,13 +39,13 @@ public class PyStringFormatParserTest extends TestCase {
}
public void testSubstitutionAfterFormat() {
- List chunks = new PyStringFormatParser("Hello, %s").parse();
+ List chunks = parsePercentFormat("Hello, %s");
assertEquals(2, chunks.size());
assertConstant(chunks.get(0), 0, 7);
}
public void testMappingKey() {
- List chunks = new PyStringFormatParser("%(language)s").parse();
+ List chunks = parsePercentFormat("%(language)s");
assertEquals(1, chunks.size());
SubstitutionChunk chunk = (SubstitutionChunk) chunks.get(0);
assertEquals("language", chunk.getMappingKey());
@@ -54,35 +53,35 @@ public class PyStringFormatParserTest extends TestCase {
}
public void testConversionFlags() {
- List chunks = new PyStringFormatParser("%#0d").parse();
+ List chunks = parsePercentFormat("%#0d");
assertEquals(1, chunks.size());
SubstitutionChunk chunk = (SubstitutionChunk) chunks.get(0);
assertEquals("#0", chunk.getConversionFlags());
}
public void testWidth() {
- List chunks = new PyStringFormatParser("%345d").parse();
+ List chunks = parsePercentFormat("%345d");
assertEquals(1, chunks.size());
SubstitutionChunk chunk = (SubstitutionChunk) chunks.get(0);
assertEquals("345", chunk.getWidth());
}
public void testPrecision() {
- List chunks = new PyStringFormatParser("%.2d").parse();
+ List chunks = parsePercentFormat("%.2d");
assertEquals(1, chunks.size());
SubstitutionChunk chunk = (SubstitutionChunk) chunks.get(0);
assertEquals("2", chunk.getPrecision());
}
public void testLengthModifier() {
- List chunks = new PyStringFormatParser("%ld").parse();
+ List chunks = parsePercentFormat("%ld");
assertEquals(1, chunks.size());
SubstitutionChunk chunk = (SubstitutionChunk) chunks.get(0);
assertEquals('l', chunk.getLengthModifier());
}
public void testDoubleAsterisk() {
- List chunks = new PyStringFormatParser("%**d").parse();
+ List chunks = parsePercentFormat("%**d");
assertEquals(2, chunks.size());
SubstitutionChunk chunk = (SubstitutionChunk) chunks.get(0);
assertEquals(2, chunk.getEndIndex());
@@ -90,7 +89,7 @@ public class PyStringFormatParserTest extends TestCase {
}
public void testUnclosedMapping() {
- List chunks = new PyStringFormatParser("%(name1s").parse();
+ List chunks = parsePercentFormat("%(name1s");
SubstitutionChunk chunk = (SubstitutionChunk) chunks.get(0);
assertEquals("name1s", chunk.getMappingKey());
assertTrue(chunk.isUnclosedMapping());
@@ -126,4 +125,10 @@ public class PyStringFormatParserTest extends TestCase {
assertEquals(1, chunks.size());
assertEquals(TextRange.create(1, 4), chunks.get(0).getTextRange());
}
+
+ public void testNewStyleConstant() {
+ List chunks = parseNewStyleFormat("a");
+ assertEquals(1, chunks.size());
+ assertConstant(chunks.get(0), 0, 1);
+ }
}