From 48bb33414849d67ebc3013d67857cd001f65b884 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Thu, 26 May 2016 14:01:03 +0300 Subject: [PATCH] Parse element indexes and attribute names in new style substitution chunk name fields Add tests --- .../inspections/PyStringFormatParser.java | 97 ++++++++++++------- .../python/PyStringFormatParserTest.java | 53 ++++++++++ 2 files changed, 117 insertions(+), 33 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java b/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java index 147118af4236..15fe786d6a53 100644 --- a/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java +++ b/python/src/com/jetbrains/python/inspections/PyStringFormatParser.java @@ -180,11 +180,9 @@ public class PyStringFormatParser { } } public static class NewStyleSubstitutionChunk extends SubstitutionChunk { - @Nullable private String myMappingKey; @Nullable private String myConversion; - @Nullable private String myWidth; - @Nullable private String myPrecision; - @Nullable private Integer myPosition; + @Nullable private String myFieldNameAttribute; + @Nullable private String myMappingKeyElementIndex; private char myConversionType; private boolean signOption; private boolean zeroPadding; @@ -195,15 +193,6 @@ public class PyStringFormatParser { super(startIndex, startIndex); } - @Nullable - public String getMappingKey() { - return myMappingKey; - } - - public void setMappingKey(@Nullable String mappingKey) { - myMappingKey = mappingKey; - } - @Nullable public String getConversion() { return myConversion; @@ -213,24 +202,6 @@ public class PyStringFormatParser { myConversion = conversion; } - @Nullable - public String getWidth() { - return myWidth; - } - - public void setWidth(@Nullable String width) { - myWidth = width; - } - - @Nullable - public String getPrecision() { - return myPrecision; - } - - public void setPrecision(@Nullable String precision) { - myPrecision = precision; - } - public boolean hasSignOption() { return signOption; } @@ -270,6 +241,24 @@ public class PyStringFormatParser { public void setThousandsSeparator(boolean thousandsSeparator) { this.thousandsSeparator = thousandsSeparator; } + + @Nullable + public String getFieldNameAttribute() { + return myFieldNameAttribute; + } + + public void setFieldNameAttribute(@NotNull String fieldNameAttribute) { + myFieldNameAttribute = fieldNameAttribute; + } + + @Nullable + public String getMappingKeyElementIndex() { + return myMappingKeyElementIndex; + } + + public void setMappingKeyElementIndex(@Nullable String mappingKeyElementIndex) { + myMappingKeyElementIndex = mappingKeyElementIndex; + } } @NotNull private final String myLiteral; @@ -371,6 +360,48 @@ public class PyStringFormatParser { autoPositionedFieldsCount++; } + // parse field name attribute name + if (isAt('.') ) { + myPos++; + + final int attributeEnd = StringUtil.indexOfAny(myLiteral, "!:.[}", myPos, end); + if (attributeEnd > 0 && myPos < attributeEnd) { + final String attributeName = myLiteral.substring(myPos, attributeEnd); + chunk.setFieldNameAttribute(attributeName); + myPos = attributeEnd; + } + } + + // parse field name element indexes + if (isAt('[')) { + myPos++; + + final int indexElementEnd = StringUtil.indexOfAny(myLiteral, "!:.]", myPos, end); + if (indexElementEnd > 0 && myPos < indexElementEnd) { + final String index = myLiteral.substring(myPos, indexElementEnd); + chunk.setMappingKeyElementIndex(index); + myPos = indexElementEnd + 1; + } + } + + // skip other attribute names and element indexes + while (isAt('.') || isAt('[')) { + if (isAt('.')) { + myPos++; + final int attributeEnd = StringUtil.indexOfAny(myLiteral, "!:.[", myPos, end); + if (attributeEnd > 0 && myPos < attributeEnd) { + myPos = attributeEnd; + } + } + else { + myPos++; + final int attributeEnd = StringUtil.indexOf(myLiteral, ']', myPos, end); + if (attributeEnd > 0 && myPos < attributeEnd) { + myPos = attributeEnd + 1; + } + }; + } + // conversion myPos = Math.max(myPos, StringUtil.indexOf(myLiteral, '!', myPos, end) + 1); final int conversionEnd = StringUtil.indexOfAny(myLiteral, ":}", myPos, end); @@ -436,12 +467,12 @@ public class PyStringFormatParser { int mappingEnd = myLiteral.indexOf(')', myPos+1); if (mappingEnd < 0) { chunk.setEndIndex(myLiteral.length()); - chunk.setMappingKey(myLiteral.substring(myPos+1)); + chunk.setMappingKey(myLiteral.substring(myPos + 1)); chunk.setUnclosedMapping(true); myPos = myLiteral.length(); return; } - chunk.setMappingKey(myLiteral.substring(myPos+1, mappingEnd)); + chunk.setMappingKey(myLiteral.substring(myPos + 1, mappingEnd)); myPos = mappingEnd+1; } else { diff --git a/python/testSrc/com/jetbrains/python/PyStringFormatParserTest.java b/python/testSrc/com/jetbrains/python/PyStringFormatParserTest.java index 9959ba7f285d..d0aacef445c8 100644 --- a/python/testSrc/com/jetbrains/python/PyStringFormatParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyStringFormatParserTest.java @@ -499,4 +499,57 @@ public class PyStringFormatParserTest extends TestCase { assertEquals("2", ((NewStyleSubstitutionChunk)chunks.get(0)).getPrecision()); assertEquals('d', ((NewStyleSubstitutionChunk)chunks.get(0)).getConversionType()); } + + public void testNewStyleFiledNameWithAttribute() { + final List chunks = parseNewStyleFormat("{foo.a}"); + assertEquals(1, chunks.size()); + final NewStyleSubstitutionChunk chunk = (NewStyleSubstitutionChunk)chunks.get(0); + assertEquals(TextRange.create(0, 7), chunk.getTextRange()); + assertEquals("foo", chunk.getMappingKey()); + assertNotNull(chunk.getFieldNameAttribute()); + assertEquals("a", chunk.getFieldNameAttribute()); + } + + public void testNewStyleFiledNameWithElementIndex() { + final List chunks = parseNewStyleFormat("{foo[a]}"); + assertEquals(1, chunks.size()); + final NewStyleSubstitutionChunk chunk = (NewStyleSubstitutionChunk)chunks.get(0); + assertEquals(TextRange.create(0, 8), chunk.getTextRange()); + assertEquals("foo", chunk.getMappingKey()); + assertNotNull(chunk.getMappingKeyElementIndex()); + assertEquals("a", chunk.getMappingKeyElementIndex()); + } + + public void testNewStyleFiledNameWithAttributeWithFormatSpec() { + final List chunks = parseNewStyleFormat("{foo.a:d}"); + assertEquals(1, chunks.size()); + final NewStyleSubstitutionChunk chunk = (NewStyleSubstitutionChunk)chunks.get(0); + assertEquals(TextRange.create(0, 9), chunk.getTextRange()); + assertEquals("foo", chunk.getMappingKey()); + assertNotNull(chunk.getFieldNameAttribute()); + assertEquals("a", chunk.getFieldNameAttribute()); + assertEquals('d', chunk.getConversionType()); + } + + public void testNewStyleFiledNameWithElementIndexWithFormatSpec() { + final List chunks = parseNewStyleFormat("{foo[a]:d}"); + assertEquals(1, chunks.size()); + final NewStyleSubstitutionChunk chunk = (NewStyleSubstitutionChunk)chunks.get(0); + assertEquals(TextRange.create(0, 10), chunk.getTextRange()); + assertEquals("foo", chunk.getMappingKey()); + assertNotNull(chunk.getMappingKeyElementIndex()); + assertEquals("a", chunk.getMappingKeyElementIndex()); + assertEquals('d', chunk.getConversionType()); + } + + public void testNewStyleFieldNameWithNestedElementIndex() { + final List chunks = parseNewStyleFormat("{foo[a][1].a[1]:d}"); + assertEquals(1, chunks.size()); + final NewStyleSubstitutionChunk chunk = (NewStyleSubstitutionChunk)chunks.get(0); + assertEquals(TextRange.create(0, 18), chunk.getTextRange()); + assertEquals("foo", chunk.getMappingKey()); + assertNotNull(chunk.getMappingKeyElementIndex()); + assertEquals("a", chunk.getMappingKeyElementIndex()); + assertEquals('d', chunk.getConversionType()); + } }