PY-72185 Implement incremental parsing for PSI leaves for Python

Merge-request: IJ-MR-138193
Merged-by: Daniil Kalinin <Daniil.Kalinin@jetbrains.com>

GitOrigin-RevId: 7d004677a5b7a4637ea956f13ed85abc0d88604e
This commit is contained in:
Daniil Kalinin
2024-07-17 12:15:36 +00:00
committed by intellij-monorepo-bot
parent 4f263103af
commit e9a6246df3
118 changed files with 967 additions and 23 deletions

View File

@@ -8,7 +8,6 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.QualifiedName;
import com.intellij.util.ObjectUtils;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.ast.impl.PyPsiUtilsCore;
@@ -43,7 +42,7 @@ public interface PyAstBinaryExpression extends PyAstQualifiedExpression, PyAstCa
@Nullable
default PsiElement getPsiOperator() {
ASTNode node = getNode();
final ASTNode child = node.findChildByType(PyElementTypes.BINARY_OPS);
final ASTNode child = node.findChildByType(PyTokenTypes.BINARY_OPS);
if (child != null) return child.getPsi();
return null;
}
@@ -53,7 +52,7 @@ public interface PyAstBinaryExpression extends PyAstQualifiedExpression, PyAstCa
StringBuilder buf = new StringBuilder();
while (child != null) {
IElementType elType = child.getElementType();
if (elType instanceof PyElementType && PyElementTypes.BINARY_OPS.contains(elType)) {
if (elType instanceof PyElementType && PyTokenTypes.BINARY_OPS.contains(elType)) {
buf.append(child.getText());
}
child = child.getTreeNext();

View File

@@ -4,7 +4,6 @@ package com.jetbrains.python.ast;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.QualifiedName;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
@@ -40,7 +39,7 @@ public interface PyAstPrefixExpression extends PyAstQualifiedExpression, PyAstRe
@Nullable
default PsiElement getPsiOperator() {
final ASTNode node = getNode();
final ASTNode child = node.findChildByType(PyElementTypes.UNARY_OPS);
final ASTNode child = node.findChildByType(PyTokenTypes.UNARY_OPS);
return child != null ? child.getPsi() : null;
}

View File

@@ -13,5 +13,6 @@
<orderEntry type="module" module-name="intellij.platform.core" />
<orderEntry type="module" module-name="intellij.platform.util" />
<orderEntry type="library" name="fastutil-min" level="project" />
<orderEntry type="module" module-name="intellij.platform.core.impl" />
</component>
</module>

View File

@@ -99,16 +99,6 @@ public interface PyElementTypes {
PyElementType DICT_COMP_EXPRESSION = new PyElementType("DICT_COMP_EXPRESSION", Companion.getINSTANCE().getDictCompExpressionConstructor());
TokenSet STATEMENT_LISTS = TokenSet.create(STATEMENT_LIST);
TokenSet BINARY_OPS = TokenSet.create(PyTokenTypes.OR_KEYWORD, PyTokenTypes.AND_KEYWORD, PyTokenTypes.LT, PyTokenTypes.GT,
PyTokenTypes.OR, PyTokenTypes.XOR, PyTokenTypes.AND, PyTokenTypes.LTLT, PyTokenTypes.GTGT,
PyTokenTypes.EQEQ, PyTokenTypes.GE, PyTokenTypes.LE, PyTokenTypes.NE, PyTokenTypes.NE_OLD,
PyTokenTypes.IN_KEYWORD, PyTokenTypes.IS_KEYWORD, PyTokenTypes.NOT_KEYWORD, PyTokenTypes.PLUS,
PyTokenTypes.MINUS, PyTokenTypes.MULT, PyTokenTypes.AT, PyTokenTypes.FLOORDIV, PyTokenTypes.DIV,
PyTokenTypes.PERC, PyTokenTypes.EXP);
TokenSet UNARY_OPS = TokenSet.create(PyTokenTypes.NOT_KEYWORD, PyTokenTypes.PLUS, PyTokenTypes.MINUS, PyTokenTypes.TILDE,
PyTokenTypes.AWAIT_KEYWORD);
// Parts
PyElementType IF_PART_IF = new PyElementType("IF_IF", Companion.getINSTANCE().getIfPartIfConstructor());
PyElementType IF_PART_ELIF = new PyElementType("IF_ELIF", Companion.getINSTANCE().getIfPartElifConstructor());

View File

@@ -4,13 +4,13 @@ package com.jetbrains.python;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.jetbrains.python.psi.PyElementType;
import com.jetbrains.python.psi.*;
public final class PyTokenTypes {
private PyTokenTypes() {
}
public static final PyElementType IDENTIFIER = new PyElementType("IDENTIFIER");
public static final PyElementType IDENTIFIER = new PyReparseableIdentifier("IDENTIFIER");
public static final PyElementType LINE_BREAK = new PyElementType("LINE_BREAK");
public static final PyElementType STATEMENT_BREAK = new PyElementType("STATEMENT_BREAK");
public static final PyElementType SPACE = new PyElementType("SPACE");
@@ -19,7 +19,7 @@ public final class PyTokenTypes {
public static final IElementType BAD_CHARACTER = TokenType.BAD_CHARACTER;
public static final PyElementType INCONSISTENT_DEDENT = new PyElementType("INCONSISTENT_DEDENT");
public static final PyElementType END_OF_LINE_COMMENT = new PyElementType("END_OF_LINE_COMMENT");
public static final PyElementType END_OF_LINE_COMMENT = new PyReparseableEndOfLineCommentType("END_OF_LINE_COMMENT");
public static final PyElementType AND_KEYWORD = new PyElementType("AND_KEYWORD");
public static final PyElementType AS_KEYWORD = new PyElementType("AS_KEYWORD");
@@ -69,12 +69,12 @@ public final class PyTokenTypes {
public static final PyElementType FLOAT_LITERAL = new PyElementType("FLOAT_LITERAL");
public static final PyElementType IMAGINARY_LITERAL = new PyElementType("IMAGINARY_LITERAL");
public static final PyElementType SINGLE_QUOTED_STRING = new PyElementType("SINGLE_QUOTED_STRING");
public static final PyElementType TRIPLE_QUOTED_STRING = new PyElementType("TRIPLE_QUOTED_STRING");
public static final PyElementType SINGLE_QUOTED_STRING = new PyReparseableSingleQuotedStringTokenType("SINGLE_QUOTED_STRING");
public static final PyElementType TRIPLE_QUOTED_STRING = new PyReparseableTripleQuotedStringTokenType("TRIPLE_QUOTED_STRING");
public static final PyElementType SINGLE_QUOTED_UNICODE = new PyElementType("SINGLE_QUOTED_UNICODE");
public static final PyElementType TRIPLE_QUOTED_UNICODE = new PyElementType("TRIPLE_QUOTED_UNICODE");
public static final PyElementType DOCSTRING = new PyElementType("DOCSTRING");
public static final PyElementType DOCSTRING = new PyReparseableTripleQuotedStringTokenType("DOCSTRING");
public static final TokenSet UNICODE_NODES = TokenSet.create(TRIPLE_QUOTED_UNICODE, SINGLE_QUOTED_UNICODE);
public static final TokenSet TRIPLE_NODES = TokenSet.create(TRIPLE_QUOTED_UNICODE, TRIPLE_QUOTED_STRING);
@@ -142,6 +142,15 @@ public final class PyTokenTypes {
public static final TokenSet COMPARISON_OPERATIONS = TokenSet.create(
LT, GT, EQEQ, GE, LE, NE, NE_OLD, IN_KEYWORD, IS_KEYWORD, NOT_KEYWORD);
public static final TokenSet UNARY_OPS = TokenSet.create(NOT_KEYWORD, PLUS, MINUS, TILDE, AWAIT_KEYWORD);
public static final TokenSet BINARY_OPS = TokenSet.create(OR_KEYWORD, AND_KEYWORD, LT, GT,
OR, XOR, AND, LTLT, GTGT,
EQEQ, GE, LE, NE, NE_OLD,
IN_KEYWORD, IS_KEYWORD, NOT_KEYWORD, PLUS,
MINUS, MULT, AT, FLOORDIV, DIV,
PERC, EXP);
public static final TokenSet SHIFT_OPERATIONS = TokenSet.create(LTLT, GTGT);
public static final TokenSet ADDITIVE_OPERATIONS = TokenSet.create(PLUS, MINUS);
public static final TokenSet MULTIPLICATIVE_OPERATIONS = TokenSet.create(MULT, AT, FLOORDIV, DIV, PERC);

View File

@@ -0,0 +1,15 @@
package com.jetbrains.python.psi;
import org.jetbrains.annotations.NotNull;
public class PyReparseableEndOfLineCommentType extends PyReparseableTokenTypeWithSimpleCheck {
public PyReparseableEndOfLineCommentType(@NotNull String debugName) {
super(debugName);
}
@Override
public boolean isReparseable(@NotNull String newText) {
return newText.startsWith("#");
}
}

View File

@@ -0,0 +1,99 @@
package com.jetbrains.python.psi;
import com.intellij.lang.ASTFactory;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.tree.TreeUtil;
import com.jetbrains.python.lexer.PythonLexer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PyReparseableIdentifier extends PyReparseableTokenType {
@SuppressWarnings("LoggerInitializedWithForeignClass")
private static final Logger LOG = Logger.getInstance(PyReparseableTokenType.class);
public PyReparseableIdentifier(@NotNull String debugName) {
super(debugName);
}
@Override
public @Nullable ASTNode reparseLeaf(@NotNull ASTNode leaf, @NotNull CharSequence newText) {
if (!Registry.is("python.ast.leaves.incremental.reparse")) {
return null;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Attempting to reparse leaf element of type" + this
+ "\nold text: \n" + leaf.getText()
+ "\n\nnew text: \n" + newText);
}
if (newText.isEmpty()) {
LOG.debug("New text is empty");
return null;
}
var lexingContainer = leaf.getTreeParent();
if (lexingContainer == null) {
LOG.debug("No re-lexing container for a leaf");
return null;
}
var originalCharSequence = lexingContainer.getChars();
var originalLeafRangeInLexingContainer = leaf.getTextRange().shiftLeft(lexingContainer.getStartOffset());
var updatedCharSequence = StringUtil.replaceSubSequence(
originalCharSequence, originalLeafRangeInLexingContainer.getStartOffset(), originalLeafRangeInLexingContainer.getEndOffset(),
newText);
var currentLeaf = TreeUtil.findFirstLeaf(lexingContainer);
PythonLexer lexer = new PythonLexer();
lexer.start(updatedCharSequence);
while (true) {
if (currentLeaf == null) {
LOG.debug("We are out of original leaves");
return null;
}
var tokenType = lexer.getTokenType();
if (currentLeaf instanceof PsiWhiteSpace) {
currentLeaf = TreeUtil.nextLeaf(currentLeaf);
lexer.advance();
continue;
}
if (tokenType != currentLeaf.getElementType()) {
LOG.debug("Wrong token type lexed: ", tokenType, " instead of ", currentLeaf.getElementType());
return null;
}
var currentLeafRangeInLexingContainer = currentLeaf.getTextRange().shiftLeft(lexingContainer.getStartOffset());
if (currentLeaf == leaf) {
var expectedEndOffset = currentLeafRangeInLexingContainer.getStartOffset() + newText.length();
if (lexer.getTokenEnd() != expectedEndOffset) {
LOG.debug("Wrong end offset, got ", lexer.getTokenEnd(), " instead of ", expectedEndOffset);
return null;
}
break;
}
else if (currentLeafRangeInLexingContainer.getEndOffset() != lexer.getTokenEnd()) {
LOG.debug("Wrong token end offset for: ", tokenType,
"; got ", lexer.getTokenEnd(),
" instead of ", currentLeafRangeInLexingContainer.getEndOffset());
return null;
}
currentLeaf = TreeUtil.nextLeaf(currentLeaf);
lexer.advance();
}
LOG.debug("Reparse is successful");
return ASTFactory.leaf(this, newText);
}
}

View File

@@ -0,0 +1,36 @@
package com.jetbrains.python.psi;
import com.intellij.psi.tree.IElementType;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.lexer.PythonLexer;
import org.jetbrains.annotations.NotNull;
public class PyReparseableSingleQuotedStringTokenType extends PyReparseableTokenTypeWithSimpleCheck {
public PyReparseableSingleQuotedStringTokenType(@NotNull String debugName) {
super(debugName);
}
@Override
public boolean isReparseable(@NotNull String newText) {
if (!isSingleQuotedString(newText) || isChangedToTripleQuoted(newText)) { // fail-fast
return false;
}
PythonLexer lexer = new PythonLexer();
lexer.start(newText);
IElementType firstTokenType = lexer.getTokenType();
lexer.advance();
IElementType nextTokenType = lexer.getTokenType();
return firstTokenType == PyTokenTypes.DOCSTRING && nextTokenType == null;
}
private static boolean isSingleQuotedString(@NotNull String newText) {
return (newText.startsWith("\"") && newText.endsWith("\"")) ||
(newText.startsWith("'") && newText.endsWith("'"));
}
private static boolean isChangedToTripleQuoted(@NotNull String newText) {
return (newText.startsWith("\"\"\"") && newText.endsWith("\"\"\"")) ||
(newText.startsWith("'''") && newText.endsWith("'''"));
}
}

View File

@@ -0,0 +1,12 @@
package com.jetbrains.python.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.tree.IReparseableLeafElementType;
import org.jetbrains.annotations.NotNull;
public abstract class PyReparseableTokenType extends PyElementType implements IReparseableLeafElementType<ASTNode> {
public PyReparseableTokenType(@NotNull String debugName) {
super(debugName);
}
}

View File

@@ -0,0 +1,39 @@
package com.jetbrains.python.psi;
import com.intellij.lang.ASTFactory;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.registry.Registry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class PyReparseableTokenTypeWithSimpleCheck extends PyReparseableTokenType {
@SuppressWarnings("LoggerInitializedWithForeignClass")
private static final Logger LOG = Logger.getInstance(PyReparseableTokenType.class);
public PyReparseableTokenTypeWithSimpleCheck(@NotNull String debugName) {
super(debugName);
}
@Override
public @Nullable ASTNode reparseLeaf(@NotNull ASTNode leaf, @NotNull CharSequence newText) {
if (!Registry.is("python.ast.leaves.incremental.reparse")) {
return null;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Attempting to reparse leaf element of type" + this
+ "\nold text: \n" + leaf.getText()
+ "\n\nnew text: \n" + newText);
}
if (isReparseable(newText.toString())) {
LOG.debug("Reparse is successful");
return ASTFactory.leaf(this, newText);
}
LOG.debug("Reparse is declined");
return null;
}
public abstract boolean isReparseable(@NotNull String newText);
}

View File

@@ -0,0 +1,31 @@
package com.jetbrains.python.psi;
import com.intellij.psi.tree.IElementType;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.lexer.PythonLexer;
import org.jetbrains.annotations.NotNull;
public class PyReparseableTripleQuotedStringTokenType extends PyReparseableTokenTypeWithSimpleCheck {
public PyReparseableTripleQuotedStringTokenType(@NotNull String debugName) {
super(debugName);
}
@Override
public boolean isReparseable(@NotNull String newText) {
if (!isTripleQuotedString(newText)) { // fail-fast
return false;
}
PythonLexer lexer = new PythonLexer();
lexer.start(newText);
IElementType firstTokenType = lexer.getTokenType();
lexer.advance();
IElementType nextTokenType = lexer.getTokenType();
return firstTokenType == PyTokenTypes.DOCSTRING && nextTokenType == null;
}
private static boolean isTripleQuotedString(@NotNull String newText) {
return (newText.startsWith("\"\"\"") && newText.endsWith("\"\"\"")) ||
(newText.startsWith("'''") && newText.endsWith("'''"));
}
}

View File

@@ -467,6 +467,8 @@
description="When enabled, activates LiteralString inference for Python string literals" />
<registryKey key="python.statement.lists.incremental.reparse" defaultValue="true"
description="Enables incremental reparse for statement lists"/>
<registryKey key="python.ast.leaves.incremental.reparse" defaultValue="true"
description="Enables incremental reparse for Python leaf elements (string literals, identifiers, comments)"/>
</extensions>

View File

@@ -0,0 +1,12 @@
Original text:
---------------
first = "first"
second = "second"
third = "third"
---------------
Subtree: Language: Python
---------------
first = "first"
second = <reparse>"secINSERTEDond"</reparse>
third = "third"
---------------

View File

@@ -0,0 +1,3 @@
first = "first"
second = "secINSERTEDond"
third = "third"

View File

@@ -0,0 +1,3 @@
first = "first"
second = "second"
third = "third"

View File

@@ -0,0 +1,12 @@
Original text:
---------------
first = """first"""
second = """second"""
third = """third"""
---------------
Subtree: Language: Python
---------------
first = """first"""
second = <reparse>"""secINSERTEDond"""</reparse>
third = """third"""
---------------

View File

@@ -0,0 +1,3 @@
first = """first"""
second = """secINSERTEDond"""
third = """third"""

View File

@@ -0,0 +1,10 @@
Original text:
---------------
def foo(self): ...
def bar(self): ...
---------------
Subtree: Language: Python
---------------
<reparse>def (self): ...
def bar(self): ...</reparse>
---------------

View File

@@ -0,0 +1,2 @@
def (self): ...
def bar(self): ...

View File

@@ -0,0 +1,2 @@
def foo(self): ...
def bar(self): ...

View File

@@ -0,0 +1,13 @@
Original text:
---------------
def foo():
# prints hello
print("Hello")
---------------
Subtree: Language: Python
---------------
def foo():
<reparse># prints hello
print("new")
print("Hello")</reparse>
---------------

View File

@@ -0,0 +1,12 @@
Original text:
---------------
hello = "Hello"
world = "World"
fstring = f"{hello}, {world}!"
---------------
Subtree: Language: Python
---------------
<reparse>hello = "Hello"
world = "World"
fstring = f"{hello}, {world}!!!"</reparse>
---------------

View File

@@ -0,0 +1,3 @@
hello = "Hello"
world = "World"
fstring = f"{hello}, {world}!!!"

View File

@@ -0,0 +1,3 @@
hello = "Hello"
world = "World"
fstring = f"{hello}, {world}!"

View File

@@ -0,0 +1,12 @@
Original text:
---------------
number_pi = 3.14159
number_e = 2.71828
fstring = f"{number_pi:.2f}"
---------------
Subtree: Language: Python
---------------
number_pi = 3.14159
number_e = 2.71828
fstring = f"{<reparse>number_e</reparse>:.2f}"
---------------

View File

@@ -0,0 +1,3 @@
number_pi = 3.14159
number_e = 2.71828
fstring = f"{number_e:.2f}"

View File

@@ -0,0 +1,3 @@
number_pi = 3.14159
number_e = 2.71828
fstring = f"{number_pi:.2f}"

View File

@@ -0,0 +1,12 @@
Original text:
---------------
first = 1
second = 2
third = 3
---------------
Subtree: Language: Python
---------------
first = 1
<reparse>secINSERTond</reparse> = 2
third = 3
---------------

View File

@@ -0,0 +1,3 @@
first = 1
secINSERTond = 2
third = 3

View File

@@ -0,0 +1,3 @@
first = 1
second = 2
third = 3

View File

@@ -0,0 +1,14 @@
Original text:
---------------
class ClassA():
def __init__(self):
print("HW!")
---------------
Subtree: Language: Python
---------------
class <reparse>ClassB</reparse>():
def __init__(self):
print("HW!")
---------------

View File

@@ -0,0 +1,4 @@
class ClassB():
def __init__(self):
print("HW!")

View File

@@ -0,0 +1,4 @@
class ClassA():
def __init__(self):
print("HW!")

View File

@@ -0,0 +1,10 @@
Original text:
---------------
def foo(self): ...
def bar(self): ...
---------------
Subtree: Language: Python
---------------
def <reparse>foOOOo</reparse>(self): ...
def bar(self): ...
---------------

View File

@@ -0,0 +1,2 @@
def foOOOo(self): ...
def bar(self): ...

View File

@@ -0,0 +1,2 @@
def foo(self): ...
def bar(self): ...

View File

@@ -0,0 +1,10 @@
Original text:
---------------
class Clazz():
def method(self, param_one, param_two): ...
---------------
Subtree: Language: Python
---------------
class Clazz():
def method(self, param_one, <reparse>paramINSERTED_two</reparse>): ...
---------------

View File

@@ -0,0 +1,2 @@
class Clazz():
def method(self, param_one, paramINSERTED_two): ...

View File

@@ -0,0 +1,2 @@
class Clazz():
def method(self, param_one, param_two): ...

View File

@@ -0,0 +1,10 @@
Original text:
---------------
class Clazz():
def method(self, param_one, param_two): ...
---------------
Subtree: Language: Python
---------------
class Clazz():
<reparse>def method(self, param_one, **param_two): ...</reparse>
---------------

View File

@@ -0,0 +1,2 @@
class Clazz():
def method(self, param_one, **param_two): ...

View File

@@ -0,0 +1,2 @@
class Clazz():
def method(self, param_one, param_two): ...

View File

@@ -0,0 +1,10 @@
Original text:
---------------
class Clazz():
def method(self, param_one, param_two): ...
---------------
Subtree: Language: Python
---------------
class Clazz():
<reparse>def method(self, param_one, *param_two): ...</reparse>
---------------

View File

@@ -0,0 +1,2 @@
class Clazz():
def method(self, param_one, *param_two): ...

View File

@@ -0,0 +1,2 @@
class Clazz():
def method(self, param_one, param_two): ...

View File

@@ -0,0 +1,14 @@
Original text:
---------------
class Clazz():
def method(self): ...
Clazz().method()
---------------
Subtree: Language: Python
---------------
class Clazz():
def method(self): ...
Clazz().<reparse>metINSERTEDhod</reparse>()
---------------

View File

@@ -0,0 +1,4 @@
class Clazz():
def method(self): ...
Clazz().metINSERTEDhod()

View File

@@ -0,0 +1,4 @@
class Clazz():
def method(self): ...
Clazz().method()

View File

@@ -0,0 +1,10 @@
Original text:
---------------
def foo(self): ...
def bar(self): ...
---------------
Subtree: Language: Python
---------------
<reparse>def async(self): ...
def bar(self): ...</reparse>
---------------

View File

@@ -0,0 +1,2 @@
def async(self): ...
def bar(self): ...

View File

@@ -0,0 +1,2 @@
def foo(self): ...
def bar(self): ...

View File

@@ -0,0 +1,12 @@
Original text:
---------------
var = "text"
var1 = "another text"
fstring = f"printing: {var}"
---------------
Subtree: Language: Python
---------------
var = "text"
var1 = "another text"
fstring = f"printing: {<reparse>var1</reparse>}"
---------------

View File

@@ -0,0 +1,3 @@
var = "text"
var1 = "another text"
fstring = f"printing: {var1}"

View File

@@ -0,0 +1,3 @@
var = "text"
var1 = "another text"
fstring = f"printing: {var}"

View File

@@ -0,0 +1,8 @@
Original text:
---------------
def foo(param1, param2): ...
---------------
Subtree: Language: Python
---------------
<reparse>def foo(param1 , param2): ...</reparse>
---------------

View File

@@ -0,0 +1,8 @@
Original text:
---------------
def foo(param1, param2): ...
---------------
Subtree: Language: Python
---------------
<reparse>def foo( param1, param2): ...</reparse>
---------------

View File

@@ -0,0 +1,8 @@
Original text:
---------------
def foo(param1, param2): ...
---------------
Subtree: Language: Python
---------------
<reparse>def foo(par am1, param2): ...</reparse>
---------------

View File

@@ -0,0 +1,14 @@
Original text:
---------------
first = """first"""
second = """this is a
multi-line
string"""
third = """third"""
---------------
Subtree: Language: Python
---------------
<reparse>first = """first"""
second = "this is a single-line string"
third = """third"""</reparse>
---------------

View File

@@ -0,0 +1,3 @@
first = """first"""
second = "this is a single-line string"
third = """third"""

View File

@@ -0,0 +1,5 @@
first = """first"""
second = """this is a
multi-line
string"""
third = """third"""

View File

@@ -0,0 +1,11 @@
Original text:
---------------
fstring = f"""test sting: {'''te-
-st'''}"""
---------------
Subtree: Language: Python
---------------
fstring = f"""test sting: {<reparse>'''te-
-
-st'''</reparse>}"""
---------------

View File

@@ -0,0 +1,18 @@
Original text:
---------------
def foo():
"""
This is a docstring
:return: nothing
"""
pass
---------------
Subtree: Language: Python
---------------
def foo():
<reparse>"""
This is a TEXT docstring
:return: nothing
"""</reparse>
pass
---------------

View File

@@ -0,0 +1,6 @@
def foo():
"""
This is a TEXT docstring
:return: nothing
"""
pass

View File

@@ -0,0 +1,6 @@
def foo():
"""
This is a docstring
:return: nothing
"""
pass

View File

@@ -0,0 +1,12 @@
Original text:
---------------
a = 1 # first
b = 2 # second
c = 3 # third
---------------
Subtree: Language: Python
---------------
a = 1 # first
b = 2 <reparse># secTEXTond</reparse>
c = 3 # third
---------------

View File

@@ -0,0 +1,3 @@
a = 1 # first
b = 2 # secTEXTond
c = 3 # third

View File

@@ -0,0 +1,3 @@
a = 1 # first
b = 2 # second
c = 3 # third

View File

@@ -0,0 +1,18 @@
Original text:
---------------
class Clazz:
# this function does nothing
def foo(): ...
---------------
Subtree: Language: Python
---------------
<reparse>class Clazz:
"""
This function does nothing
Accepts nothing
Returns nothing
"""
def foo(): ...</reparse>
---------------

View File

@@ -0,0 +1,8 @@
class Clazz:
"""
This function does nothing
Accepts nothing
Returns nothing
"""
def foo(): ...

View File

@@ -0,0 +1,12 @@
Original text:
---------------
first = 'first'
second = 'second'
third = 'third'
---------------
Subtree: Language: Python
---------------
first = 'first'
second = <reparse>'secINSERTEDond'</reparse>
third = 'third'
---------------

View File

@@ -0,0 +1,3 @@
first = 'first'
second = 'secINSERTEDond'
third = 'third'

View File

@@ -0,0 +1,3 @@
first = 'first'
second = 'second'
third = 'third'

View File

@@ -0,0 +1,3 @@
first = '''first'''
second = '''secINSERTEDond'''
third = '''third'''

View File

@@ -0,0 +1,8 @@
Original text:
---------------
print("Hello, World")
---------------
Subtree: Language: Python
---------------
<reparse>print("Hello,"); print(","); print("World")</reparse>
---------------

View File

@@ -0,0 +1,12 @@
Original text:
---------------
first = 'first'
second = 'second'
third = 'third'
---------------
Subtree: Language: Python
---------------
<reparse>first = 'first'
second = '''second'''
third = 'third'</reparse>
---------------

View File

@@ -0,0 +1,3 @@
first = 'first'
second = '''second'''
third = 'third'

View File

@@ -0,0 +1,3 @@
first = 'first'
second = 'second'
third = 'third'

View File

@@ -0,0 +1,12 @@
Original text:
---------------
first = "first"
second = "second"
third = "third"
---------------
Subtree: Language: Python
---------------
<reparse>first = "first"
second = """second"""
third = "third"</reparse>
---------------

View File

@@ -0,0 +1,8 @@
Original text:
---------------
fstring = f"test sting: {"test"}"
---------------
Subtree: Language: Python
---------------
fstring = f"test sting: {<reparse>"te st"</reparse>}"
---------------

View File

@@ -0,0 +1,12 @@
Original text:
---------------
first = "first"
second = "second"
third = "third"
---------------
Subtree: Language: Python
---------------
<reparse>first = "first"
second = first
third = "third"</reparse>
---------------

Some files were not shown because too many files have changed in this diff Show More