Strip leading indents from PyCode, and then restore when compiling.
This is only done for non-expr pycodes. Expr pycodes get the column information from the contained PyExpr, and so don't need to be changed.
This commit is contained in:
+70
-11
@@ -89,18 +89,22 @@ class PyCode(Object):
|
|||||||
hashcode : int
|
hashcode : int
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return (1, self.source, (self.filename, self.linenumber), self.mode, self.py, self.hashcode)
|
return (1, self.source, (self.filename, self.linenumber), self.mode, self.py, self.hashcode, self.col_offset)
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
|
col_offset = 0
|
||||||
|
py = 2
|
||||||
|
hashcode = None
|
||||||
|
|
||||||
match state:
|
match state:
|
||||||
case (_, source, location, mode):
|
case (_, source, location, mode, py, hashcode, col_offset):
|
||||||
py = 2
|
pass
|
||||||
hashcode = None
|
|
||||||
case (_, source, location, mode, py):
|
|
||||||
hashcode = None
|
|
||||||
case (_, source, location, mode, py, hashcode):
|
case (_, source, location, mode, py, hashcode):
|
||||||
pass
|
pass
|
||||||
|
case (_, source, location, mode, py):
|
||||||
|
pass
|
||||||
|
case (_, source, location, mode):
|
||||||
|
pass
|
||||||
case _:
|
case _:
|
||||||
raise Exception("Invalid state:", state)
|
raise Exception("Invalid state:", state)
|
||||||
|
|
||||||
@@ -108,6 +112,7 @@ class PyCode(Object):
|
|||||||
self.source = source
|
self.source = source
|
||||||
self.filename = location[0]
|
self.filename = location[0]
|
||||||
self.linenumber = location[1]
|
self.linenumber = location[1]
|
||||||
|
self.col_offset = col_offset
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|
||||||
if hashcode is None:
|
if hashcode is None:
|
||||||
@@ -121,14 +126,14 @@ class PyCode(Object):
|
|||||||
if renpy.game.script.record_pycode:
|
if renpy.game.script.record_pycode:
|
||||||
renpy.game.script.all_pycode.append(self)
|
renpy.game.script.all_pycode.append(self)
|
||||||
|
|
||||||
def __init__(self, source: str, loc: tuple[str, int] = ('<none>', 1),
|
def __init__(
|
||||||
mode: Literal["eval", "exec", "hide"] = 'exec'):
|
self,
|
||||||
|
source: str,
|
||||||
|
loc: tuple[str, int] = ('<none>', 1),
|
||||||
|
mode: Literal["eval", "exec", "hide"] = 'exec'):
|
||||||
|
|
||||||
self.py = 3
|
self.py = 3
|
||||||
|
|
||||||
# The source code.
|
|
||||||
self.source = source
|
|
||||||
|
|
||||||
if isinstance(source, PyExpr):
|
if isinstance(source, PyExpr):
|
||||||
self.filename = source.filename
|
self.filename = source.filename
|
||||||
self.linenumber = source.linenumber
|
self.linenumber = source.linenumber
|
||||||
@@ -138,6 +143,13 @@ class PyCode(Object):
|
|||||||
self.linenumber = loc[1]
|
self.linenumber = loc[1]
|
||||||
self.hashcode = hash32(source)
|
self.hashcode = hash32(source)
|
||||||
|
|
||||||
|
# The source code.
|
||||||
|
if self.mode != "eval":
|
||||||
|
self.source, self.col_offset = PyCode.dedent(source)
|
||||||
|
else:
|
||||||
|
self.source = source
|
||||||
|
self.col_offset = 0
|
||||||
|
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|
||||||
# This will be initialized later on, after we are serialized.
|
# This will be initialized later on, after we are serialized.
|
||||||
@@ -146,6 +158,53 @@ class PyCode(Object):
|
|||||||
if renpy.game.script.record_pycode:
|
if renpy.game.script.record_pycode:
|
||||||
renpy.game.script.all_pycode.append(self)
|
renpy.game.script.all_pycode.append(self)
|
||||||
|
|
||||||
|
|
||||||
|
_leading_whitespace_re = re.compile('(^[ ]*)(?:[^ ])', re.MULTILINE)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dedent(text: str):
|
||||||
|
"""
|
||||||
|
Removes leading whitespace from a block of text. Entirely blank lines
|
||||||
|
are normalized to a newline character.
|
||||||
|
|
||||||
|
This returns the dedented text, and the amount of whitespace removed,
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Look for the longest leading string of spaces and tabs common to
|
||||||
|
# all lines.
|
||||||
|
margin = None
|
||||||
|
indents = PyCode._leading_whitespace_re.findall(text)
|
||||||
|
for indent in indents:
|
||||||
|
if margin is None:
|
||||||
|
margin = indent
|
||||||
|
|
||||||
|
# Current line more deeply indented than previous winner:
|
||||||
|
# no change (previous winner is still on top).
|
||||||
|
elif indent.startswith(margin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Current line consistent with and no deeper than previous winner:
|
||||||
|
# it's the new winner.
|
||||||
|
elif margin.startswith(indent):
|
||||||
|
margin = indent
|
||||||
|
|
||||||
|
# Find the largest common whitespace between current line and previous
|
||||||
|
# winner.
|
||||||
|
else:
|
||||||
|
for i, (x, y) in enumerate(zip(margin, indent)):
|
||||||
|
if x != y:
|
||||||
|
margin = margin[:i]
|
||||||
|
break
|
||||||
|
|
||||||
|
if margin:
|
||||||
|
text = re.sub(r'(?m)^' + margin, '', text)
|
||||||
|
|
||||||
|
if margin:
|
||||||
|
return text, len(margin)
|
||||||
|
else:
|
||||||
|
return text, 0
|
||||||
|
|
||||||
|
|
||||||
DoesNotExtend = renpy.object.Sentinel("DoesNotExtend")
|
DoesNotExtend = renpy.object.Sentinel("DoesNotExtend")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -1047,7 +1047,6 @@ def py_compile(source, mode, filename='<none>', lineno=1, ast_node=False, cache=
|
|||||||
`column`
|
`column`
|
||||||
A column offset to add to the column numbers of the source.
|
A column offset to add to the column numbers of the source.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
global compile_warnings
|
global compile_warnings
|
||||||
|
|
||||||
first_line_column_delta = column
|
first_line_column_delta = column
|
||||||
@@ -1078,7 +1077,7 @@ def py_compile(source, mode, filename='<none>', lineno=1, ast_node=False, cache=
|
|||||||
|
|
||||||
# This determines if the lines are indented. If so, we adjust the
|
# This determines if the lines are indented. If so, we adjust the
|
||||||
# ast to match.
|
# ast to match.
|
||||||
indented = source and (source[0] == " ")
|
indented = source and (source[0] == " ") and (mode != "eval")
|
||||||
|
|
||||||
if indented:
|
if indented:
|
||||||
lineno -= 1
|
lineno -= 1
|
||||||
|
|||||||
+1
-1
@@ -1055,7 +1055,7 @@ class Script(object):
|
|||||||
|
|
||||||
renpy.game.exception_info = "While compiling python block starting at line %d of %s." % (i.linenumber, i.filename)
|
renpy.game.exception_info = "While compiling python block starting at line %d of %s." % (i.linenumber, i.filename)
|
||||||
|
|
||||||
i.bytecode = renpy.python.py_compile(i.source, i.mode, filename=i.filename, lineno=i.linenumber, py=i.py, hashcode=i.hashcode)
|
i.bytecode = renpy.python.py_compile(i.source, i.mode, filename=i.filename, lineno=i.linenumber, py=i.py, hashcode=i.hashcode, column=i.col_offset)
|
||||||
|
|
||||||
except SyntaxError as e:
|
except SyntaxError as e:
|
||||||
assert e.filename is not None
|
assert e.filename is not None
|
||||||
|
|||||||
Reference in New Issue
Block a user