mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
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:
committed by
intellij-monorepo-bot
parent
4f263103af
commit
e9a6246df3
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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("#");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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("'''"));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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("'''"));
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
Original text:
|
||||
---------------
|
||||
first = "first"
|
||||
second = "second"
|
||||
third = "third"
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
first = "first"
|
||||
second = <reparse>"secINSERTEDond"</reparse>
|
||||
third = "third"
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
first = "first"
|
||||
second = "secINSERTEDond"
|
||||
third = "third"
|
||||
@@ -0,0 +1,3 @@
|
||||
first = "first"
|
||||
second = "second"
|
||||
third = "third"
|
||||
@@ -0,0 +1,12 @@
|
||||
Original text:
|
||||
---------------
|
||||
first = """first"""
|
||||
second = """second"""
|
||||
third = """third"""
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
first = """first"""
|
||||
second = <reparse>"""secINSERTEDond"""</reparse>
|
||||
third = """third"""
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
first = """first"""
|
||||
second = """secINSERTEDond"""
|
||||
third = """third"""
|
||||
@@ -0,0 +1,3 @@
|
||||
first = """first"""
|
||||
second = """second"""
|
||||
third = """third"""
|
||||
@@ -0,0 +1,10 @@
|
||||
Original text:
|
||||
---------------
|
||||
def foo(self): ...
|
||||
def bar(self): ...
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
<reparse>def (self): ...
|
||||
def bar(self): ...</reparse>
|
||||
---------------
|
||||
@@ -0,0 +1,2 @@
|
||||
def (self): ...
|
||||
def bar(self): ...
|
||||
@@ -0,0 +1,2 @@
|
||||
def foo(self): ...
|
||||
def bar(self): ...
|
||||
@@ -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>
|
||||
---------------
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo():
|
||||
# prints hello
|
||||
print("new")
|
||||
print("Hello")
|
||||
@@ -0,0 +1,3 @@
|
||||
def foo():
|
||||
# prints hello
|
||||
print("Hello")
|
||||
@@ -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>
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
hello = "Hello"
|
||||
world = "World"
|
||||
fstring = f"{hello}, {world}!!!"
|
||||
@@ -0,0 +1,3 @@
|
||||
hello = "Hello"
|
||||
world = "World"
|
||||
fstring = f"{hello}, {world}!"
|
||||
@@ -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}"
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
number_pi = 3.14159
|
||||
number_e = 2.71828
|
||||
fstring = f"{number_e:.2f}"
|
||||
@@ -0,0 +1,3 @@
|
||||
number_pi = 3.14159
|
||||
number_e = 2.71828
|
||||
fstring = f"{number_pi:.2f}"
|
||||
@@ -0,0 +1,12 @@
|
||||
Original text:
|
||||
---------------
|
||||
first = 1
|
||||
second = 2
|
||||
third = 3
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
first = 1
|
||||
<reparse>secINSERTond</reparse> = 2
|
||||
third = 3
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
first = 1
|
||||
secINSERTond = 2
|
||||
third = 3
|
||||
@@ -0,0 +1,3 @@
|
||||
first = 1
|
||||
second = 2
|
||||
third = 3
|
||||
@@ -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!")
|
||||
---------------
|
||||
@@ -0,0 +1,4 @@
|
||||
class ClassB():
|
||||
|
||||
def __init__(self):
|
||||
print("HW!")
|
||||
@@ -0,0 +1,4 @@
|
||||
class ClassA():
|
||||
|
||||
def __init__(self):
|
||||
print("HW!")
|
||||
@@ -0,0 +1,10 @@
|
||||
Original text:
|
||||
---------------
|
||||
def foo(self): ...
|
||||
def bar(self): ...
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
def <reparse>foOOOo</reparse>(self): ...
|
||||
def bar(self): ...
|
||||
---------------
|
||||
@@ -0,0 +1,2 @@
|
||||
def foOOOo(self): ...
|
||||
def bar(self): ...
|
||||
@@ -0,0 +1,2 @@
|
||||
def foo(self): ...
|
||||
def bar(self): ...
|
||||
@@ -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>): ...
|
||||
---------------
|
||||
@@ -0,0 +1,2 @@
|
||||
class Clazz():
|
||||
def method(self, param_one, paramINSERTED_two): ...
|
||||
@@ -0,0 +1,2 @@
|
||||
class Clazz():
|
||||
def method(self, param_one, param_two): ...
|
||||
@@ -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>
|
||||
---------------
|
||||
@@ -0,0 +1,2 @@
|
||||
class Clazz():
|
||||
def method(self, param_one, **param_two): ...
|
||||
@@ -0,0 +1,2 @@
|
||||
class Clazz():
|
||||
def method(self, param_one, param_two): ...
|
||||
@@ -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>
|
||||
---------------
|
||||
@@ -0,0 +1,2 @@
|
||||
class Clazz():
|
||||
def method(self, param_one, *param_two): ...
|
||||
@@ -0,0 +1,2 @@
|
||||
class Clazz():
|
||||
def method(self, param_one, param_two): ...
|
||||
@@ -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>()
|
||||
---------------
|
||||
@@ -0,0 +1,4 @@
|
||||
class Clazz():
|
||||
def method(self): ...
|
||||
|
||||
Clazz().metINSERTEDhod()
|
||||
@@ -0,0 +1,4 @@
|
||||
class Clazz():
|
||||
def method(self): ...
|
||||
|
||||
Clazz().method()
|
||||
@@ -0,0 +1,10 @@
|
||||
Original text:
|
||||
---------------
|
||||
def foo(self): ...
|
||||
def bar(self): ...
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
<reparse>def async(self): ...
|
||||
def bar(self): ...</reparse>
|
||||
---------------
|
||||
@@ -0,0 +1,2 @@
|
||||
def async(self): ...
|
||||
def bar(self): ...
|
||||
@@ -0,0 +1,2 @@
|
||||
def foo(self): ...
|
||||
def bar(self): ...
|
||||
@@ -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>}"
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
var = "text"
|
||||
var1 = "another text"
|
||||
fstring = f"printing: {var1}"
|
||||
@@ -0,0 +1,3 @@
|
||||
var = "text"
|
||||
var1 = "another text"
|
||||
fstring = f"printing: {var}"
|
||||
@@ -0,0 +1,8 @@
|
||||
Original text:
|
||||
---------------
|
||||
def foo(param1, param2): ...
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
<reparse>def foo(param1 , param2): ...</reparse>
|
||||
---------------
|
||||
@@ -0,0 +1 @@
|
||||
def foo(param1 , param2): ...
|
||||
@@ -0,0 +1 @@
|
||||
def foo(param1, param2): ...
|
||||
@@ -0,0 +1,8 @@
|
||||
Original text:
|
||||
---------------
|
||||
def foo(param1, param2): ...
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
<reparse>def foo( param1, param2): ...</reparse>
|
||||
---------------
|
||||
@@ -0,0 +1 @@
|
||||
def foo( param1, param2): ...
|
||||
@@ -0,0 +1 @@
|
||||
def foo(param1, param2): ...
|
||||
@@ -0,0 +1,8 @@
|
||||
Original text:
|
||||
---------------
|
||||
def foo(param1, param2): ...
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
<reparse>def foo(par am1, param2): ...</reparse>
|
||||
---------------
|
||||
@@ -0,0 +1 @@
|
||||
def foo(par am1, param2): ...
|
||||
@@ -0,0 +1 @@
|
||||
def foo(param1, param2): ...
|
||||
@@ -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>
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
first = """first"""
|
||||
second = "this is a single-line string"
|
||||
third = """third"""
|
||||
@@ -0,0 +1,5 @@
|
||||
first = """first"""
|
||||
second = """this is a
|
||||
multi-line
|
||||
string"""
|
||||
third = """third"""
|
||||
@@ -0,0 +1,11 @@
|
||||
Original text:
|
||||
---------------
|
||||
fstring = f"""test sting: {'''te-
|
||||
-st'''}"""
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
fstring = f"""test sting: {<reparse>'''te-
|
||||
-
|
||||
-st'''</reparse>}"""
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
fstring = f"""test sting: {'''te-
|
||||
-
|
||||
-st'''}"""
|
||||
@@ -0,0 +1,2 @@
|
||||
fstring = f"""test sting: {'''te-
|
||||
-st'''}"""
|
||||
@@ -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
|
||||
---------------
|
||||
@@ -0,0 +1,6 @@
|
||||
def foo():
|
||||
"""
|
||||
This is a TEXT docstring
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,6 @@
|
||||
def foo():
|
||||
"""
|
||||
This is a docstring
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
@@ -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
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
a = 1 # first
|
||||
b = 2 # secTEXTond
|
||||
c = 3 # third
|
||||
@@ -0,0 +1,3 @@
|
||||
a = 1 # first
|
||||
b = 2 # second
|
||||
c = 3 # third
|
||||
@@ -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>
|
||||
---------------
|
||||
@@ -0,0 +1,8 @@
|
||||
class Clazz:
|
||||
|
||||
"""
|
||||
This function does nothing
|
||||
Accepts nothing
|
||||
Returns nothing
|
||||
"""
|
||||
def foo(): ...
|
||||
@@ -0,0 +1,4 @@
|
||||
class Clazz:
|
||||
|
||||
# this function does nothing
|
||||
def foo(): ...
|
||||
@@ -0,0 +1,12 @@
|
||||
Original text:
|
||||
---------------
|
||||
first = 'first'
|
||||
second = 'second'
|
||||
third = 'third'
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
first = 'first'
|
||||
second = <reparse>'secINSERTEDond'</reparse>
|
||||
third = 'third'
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
first = 'first'
|
||||
second = 'secINSERTEDond'
|
||||
third = 'third'
|
||||
@@ -0,0 +1,3 @@
|
||||
first = 'first'
|
||||
second = 'second'
|
||||
third = 'third'
|
||||
@@ -0,0 +1,3 @@
|
||||
first = '''first'''
|
||||
second = '''secINSERTEDond'''
|
||||
third = '''third'''
|
||||
@@ -0,0 +1,3 @@
|
||||
first = '''first'''
|
||||
second = '''second'''
|
||||
third = '''third'''
|
||||
@@ -0,0 +1,8 @@
|
||||
Original text:
|
||||
---------------
|
||||
print("Hello, World")
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
<reparse>print("Hello,"); print(","); print("World")</reparse>
|
||||
---------------
|
||||
@@ -0,0 +1 @@
|
||||
print("Hello,"); print(","); print("World")
|
||||
@@ -0,0 +1 @@
|
||||
print("Hello, World")
|
||||
@@ -0,0 +1,12 @@
|
||||
Original text:
|
||||
---------------
|
||||
first = 'first'
|
||||
second = 'second'
|
||||
third = 'third'
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
<reparse>first = 'first'
|
||||
second = '''second'''
|
||||
third = 'third'</reparse>
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
first = 'first'
|
||||
second = '''second'''
|
||||
third = 'third'
|
||||
@@ -0,0 +1,3 @@
|
||||
first = 'first'
|
||||
second = 'second'
|
||||
third = 'third'
|
||||
@@ -0,0 +1,12 @@
|
||||
Original text:
|
||||
---------------
|
||||
first = "first"
|
||||
second = "second"
|
||||
third = "third"
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
<reparse>first = "first"
|
||||
second = """second"""
|
||||
third = "third"</reparse>
|
||||
---------------
|
||||
@@ -0,0 +1,3 @@
|
||||
first = "first"
|
||||
second = """second"""
|
||||
third = "third"
|
||||
@@ -0,0 +1,3 @@
|
||||
first = "first"
|
||||
second = "second"
|
||||
third = "third"
|
||||
@@ -0,0 +1,8 @@
|
||||
Original text:
|
||||
---------------
|
||||
fstring = f"test sting: {"test"}"
|
||||
---------------
|
||||
Subtree: Language: Python
|
||||
---------------
|
||||
fstring = f"test sting: {<reparse>"te st"</reparse>}"
|
||||
---------------
|
||||
@@ -0,0 +1 @@
|
||||
fstring = f"test sting: {"te st"}"
|
||||
@@ -0,0 +1 @@
|
||||
fstring = f"test sting: {"test"}"
|
||||
@@ -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
Reference in New Issue
Block a user