/* * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jetbrains.python; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.psi.LiteralTextEscaper; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.PyStringLiteralExpression; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @author yole */ public class PyStringLiteralTest extends PyTestCase { public void testEscaperDecode() { final PyStringLiteralExpression expr = createLiteralFromText("'\\nfoo'"); assertNotNull(expr); assertEquals("fo", decodeRange(expr, TextRange.create(3, 5))); assertEquals("\n", decodeRange(expr, TextRange.create(1, 3))); } public void testEscaperOffsetInHost() { final PyStringLiteralExpression expr = createLiteralFromText("'\\nfoo'"); assertNotNull(expr); final LiteralTextEscaper<? extends PsiLanguageInjectionHost> escaper = expr.createLiteralTextEscaper(); final TextRange newLineFoo = TextRange.create(1, 6); assertEquals(1, escaper.getOffsetInHost(0, newLineFoo)); assertEquals(3, escaper.getOffsetInHost(1, newLineFoo)); assertEquals(4, escaper.getOffsetInHost(2, newLineFoo)); assertEquals(5, escaper.getOffsetInHost(3, newLineFoo)); assertEquals(6, escaper.getOffsetInHost(4, newLineFoo)); assertEquals(-1, escaper.getOffsetInHost(5, newLineFoo)); } public void testEscaperOffsetInHostSubString() { final PyStringLiteralExpression expr = createLiteralFromText("'\\nfoo'"); assertNotNull(expr); final LiteralTextEscaper<? extends PsiLanguageInjectionHost> escaper = expr.createLiteralTextEscaper(); final TextRange fooOnly = TextRange.create(3, 6); assertEquals(3, escaper.getOffsetInHost(0, fooOnly)); assertEquals(4, escaper.getOffsetInHost(1, fooOnly)); assertEquals(5, escaper.getOffsetInHost(2, fooOnly)); assertEquals(6, escaper.getOffsetInHost(3, fooOnly)); assertEquals(-1, escaper.getOffsetInHost(4, fooOnly)); } public void testEscaperOffsetInSingleCharString() { final PyStringLiteralExpression expr = createLiteralFromText("'c'"); assertNotNull(expr); final LiteralTextEscaper<? extends PsiLanguageInjectionHost> escaper = expr.createLiteralTextEscaper(); final TextRange range = TextRange.create(1, 2); assertEquals(1, escaper.getOffsetInHost(0, range)); assertEquals(2, escaper.getOffsetInHost(1, range)); assertEquals(-1, escaper.getOffsetInHost(2, range)); } public void testEscaperOffsetInSingleEscapedCharString() { final PyStringLiteralExpression expr = createLiteralFromText("'\\n'"); assertNotNull(expr); final LiteralTextEscaper<? extends PsiLanguageInjectionHost> escaper = expr.createLiteralTextEscaper(); final TextRange range = TextRange.create(1, 3); assertEquals(1, escaper.getOffsetInHost(0, range)); assertEquals(3, escaper.getOffsetInHost(1, range)); assertEquals(-1, escaper.getOffsetInHost(2, range)); } public void testIterateCharacterRanges() { assertSameElements(getCharacterRanges("'\\nfoo' 'bar'"), Arrays.asList("\n", "foo", "bar")); } public void testIterateEscapedBackslash() { assertSameElements(getCharacterRanges("'''\n" + "foo.\\\\\n" + "bar\n" + "'''\n"), Arrays.asList("\nfoo.", "\\", "\nbar\n")); } public void testEscaperOffsetInEscapedBackslash() { final PyStringLiteralExpression expr = createLiteralFromText("'XXX foo.\\\\bar YYY'"); assertNotNull(expr); final LiteralTextEscaper<? extends PsiLanguageInjectionHost> escaper = expr.createLiteralTextEscaper(); final TextRange range = TextRange.create(5, 14); assertEquals(5, escaper.getOffsetInHost(0, range)); assertEquals(6, escaper.getOffsetInHost(1, range)); assertEquals(7, escaper.getOffsetInHost(2, range)); assertEquals(8, escaper.getOffsetInHost(3, range)); assertEquals(9, escaper.getOffsetInHost(4, range)); assertEquals(11, escaper.getOffsetInHost(5, range)); assertEquals(12, escaper.getOffsetInHost(6, range)); assertEquals(13, escaper.getOffsetInHost(7, range)); assertEquals(14, escaper.getOffsetInHost(8, range)); assertEquals(-1, escaper.getOffsetInHost(9, range)); } public void testEscaperOffsetInLongUnicodeEscape() { final PyStringLiteralExpression expr = createLiteralFromText("u'XXX a\\U0001F600\\U0001F600b YYY'"); final LiteralTextEscaper<? extends PsiLanguageInjectionHost> escaper = expr.createLiteralTextEscaper(); final TextRange range = TextRange.create(6, 28); assertEquals(6, escaper.getOffsetInHost(0, range)); assertEquals(7, escaper.getOffsetInHost(1, range)); // Each \\U0001F600 is represented as a surrogate pair, hence 2 characters-wide step in decoded text assertEquals(17, escaper.getOffsetInHost(3, range)); assertEquals(27, escaper.getOffsetInHost(5, range)); assertEquals(28, escaper.getOffsetInHost(6, range)); assertEquals(-1, escaper.getOffsetInHost(7, range)); } public void testStringValue() { assertEquals("foo", createLiteralFromText("\"\"\"foo\"\"\"").getStringValue()); assertEquals("foo", createLiteralFromText("u\"foo\"").getStringValue()); assertEquals("foo", createLiteralFromText("b\"foo\"").getStringValue()); assertEquals("\\b", createLiteralFromText("r'\\b'").getStringValue()); assertEquals("b\\n", createLiteralFromText("ur'\\u0062\\n'").getStringValue()); assertEquals("\\8", createLiteralFromText("'\\8'").getStringValue()); } public void testEscapedUnicodeInLiterals() { assertEquals("\\u0041", createLiteralFromText("'\\u0041'").getStringValue()); assertEquals("A", createLiteralFromText("u'\\u0041'").getStringValue()); assertEquals("\\u0041", createLiteralFromText("b'\\u0041'").getStringValue()); } public void testNonUnicodeCodePointValue() { assertEquals("\\U12345678", createLiteralFromText("u'\\U12345678'").getStringValue()); } private static String decodeRange(PyStringLiteralExpression expr, TextRange range) { final StringBuilder builder = new StringBuilder(); expr.createLiteralTextEscaper().decode(range, builder); return builder.toString(); } private PyStringLiteralExpression createLiteralFromText(final String text) { final PsiFile file = PsiFileFactory.getInstance(myFixture.getProject()).createFileFromText("test.py", "a = " + text); final PyStringLiteralExpression expr = PsiTreeUtil.getParentOfType(file.findElementAt(5), PyStringLiteralExpression.class); assert expr != null; return expr; } private List<String> getCharacterRanges(String text) { final PyStringLiteralExpression expr = createLiteralFromText(text); assertNotNull(expr); final List<String> characters = new ArrayList<>(); for (Pair<TextRange, String> fragment : expr.getDecodedFragments()) { characters.add(fragment.getSecond()); } return characters; } }