forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add blank lines to separate blocks of indented code (#1515)
Fixes #259
- Loading branch information
1 parent
5d6493b
commit da91e3f
Showing
14 changed files
with
226 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add blank lines to separate blocks of indented code (function defs, classes, and the like) so as to ensure the code can be run within a Python interactive prompt. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import ast | ||
import io | ||
import operator | ||
import os | ||
import sys | ||
import token | ||
import tokenize | ||
|
||
|
||
class Visitor(ast.NodeVisitor): | ||
def __init__(self, lines): | ||
self._lines = lines | ||
self.line_numbers_with_nodes = set() | ||
self.line_numbers_with_statements = [] | ||
|
||
def generic_visit(self, node): | ||
if hasattr(node, 'col_offset') and hasattr(node, 'lineno') and node.col_offset == 0: | ||
self.line_numbers_with_nodes.add(node.lineno) | ||
if isinstance(node, ast.stmt): | ||
self.line_numbers_with_statements.append(node.lineno) | ||
|
||
ast.NodeVisitor.generic_visit(self, node) | ||
|
||
|
||
def _tokenize(source): | ||
"""Tokenize Python source code.""" | ||
# Using an undocumented API as the documented one in Python 2.7 does not work as needed | ||
# cross-version. | ||
return tokenize.generate_tokens(io.StringIO(source).readline) | ||
|
||
|
||
def _indent_size(line): | ||
for index, char in enumerate(line): | ||
if not char.isspace(): | ||
return index | ||
|
||
|
||
def _get_global_statement_blocks(source, lines): | ||
"""Return a list of all global statement blocks. | ||
The list comprises of 3-item tuples that contain the starting line number, | ||
ending line number and whether the statement is a single line. | ||
""" | ||
tree = ast.parse(source) | ||
visitor = Visitor(lines) | ||
visitor.visit(tree) | ||
|
||
statement_ranges = [] | ||
for index, line_number in enumerate(visitor.line_numbers_with_statements): | ||
remaining_line_numbers = visitor.line_numbers_with_statements[index+1:] | ||
end_line_number = len(lines) if len(remaining_line_numbers) == 0 else min(remaining_line_numbers) - 1 | ||
current_statement_is_oneline = line_number == end_line_number | ||
|
||
if len(statement_ranges) == 0: | ||
statement_ranges.append((line_number, end_line_number, current_statement_is_oneline)) | ||
continue | ||
|
||
previous_statement = statement_ranges[-1] | ||
previous_statement_is_oneline = previous_statement[2] | ||
if previous_statement_is_oneline and current_statement_is_oneline: | ||
statement_ranges[-1] = previous_statement[0], end_line_number, True | ||
else: | ||
statement_ranges.append((line_number, end_line_number, current_statement_is_oneline)) | ||
|
||
return statement_ranges | ||
|
||
|
||
def normalize_lines(source): | ||
"""Normalize blank lines for sending to the terminal. | ||
Blank lines within a statement block are removed to prevent the REPL | ||
from thinking the block is finished. Newlines are added to separate | ||
top-level statements so that the REPL does not think there is a syntax | ||
error. | ||
""" | ||
lines = source.splitlines(False) | ||
# Find out if we have any trailing blank lines | ||
has_blank_lines = len(lines[-1].strip()) == 0 or source.endswith(os.linesep) | ||
|
||
# Step 1: Remove empty lines. | ||
tokens = _tokenize(source) | ||
newlines_indexes_to_remove = (spos[0] for (toknum, tokval, spos, epos, line) in tokens | ||
if len(line.strip()) == 0 and token.tok_name[toknum] == 'NL' and spos[0] == epos[0]) | ||
|
||
for line_number in reversed(list(newlines_indexes_to_remove)): | ||
del lines[line_number-1] | ||
|
||
# Step 2: Add blank lines between each global statement block. | ||
# A consequtive single lines blocks of code will be treated as a single statement, | ||
# just to ensure we do not unnecessarily add too many blank lines. | ||
source = os.linesep.join(lines) | ||
tokens = _tokenize(source) | ||
dedent_indexes = (spos[0] for (toknum, tokval, spos, epos, line) in tokens | ||
if toknum == token.DEDENT and _indent_size(line) == 0) | ||
|
||
global_statement_ranges = _get_global_statement_blocks(source, lines) | ||
|
||
for line_number in filter(lambda x: x > 1, map(operator.itemgetter(0), reversed(global_statement_ranges))): | ||
lines.insert(line_number-1, '') | ||
|
||
sys.stdout.write(os.linesep.join(lines) + (os.linesep if has_blank_lines else '')) | ||
sys.stdout.flush() | ||
|
||
|
||
if __name__ == '__main__': | ||
contents = sys.argv[1] | ||
try: | ||
default_encoding = sys.getdefaultencoding() | ||
contents = contents.encode(default_encoding, 'surrogateescape').decode(default_encoding, 'replace') | ||
except (UnicodeError, LookupError): | ||
pass | ||
if isinstance(contents, bytes): | ||
contents = contents.decode('utf8') | ||
normalize_lines(contents) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
if True: | ||
print(1) | ||
print(2) | ||
|
||
print(3) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ | |
print(1) | ||
|
||
print(2) | ||
|
||
print(3) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
if True: | ||
print(1) | ||
else: print(2) | ||
|
||
print('🔨') | ||
print(3) | ||
print(3) | ||
|
||
if True: | ||
print(1) | ||
else: print(2) | ||
|
||
if True: | ||
print(1) | ||
else: print(2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
if True: | ||
print(1) | ||
else: print(2) | ||
print('🔨') | ||
print(3) | ||
print(3) | ||
if True: | ||
print(1) | ||
else: print(2) | ||
if True: | ||
print(1) | ||
else: print(2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
if True: | ||
print(1) | ||
print(1) | ||
else: | ||
print(2) | ||
print(2) | ||
|
||
print(3) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
if True: | ||
print(1) | ||
|
||
print(1) | ||
else: | ||
print(2) | ||
|
||
print(2) | ||
print(3) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import sys | ||
print(sys.executable) | ||
print("1234") | ||
print(1) | ||
print(2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import sys | ||
|
||
print(sys.executable) | ||
|
||
print("1234") | ||
|
||
print(1) | ||
print(2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters