@@ -461,6 +461,77 @@ def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
461
461
return node
462
462
463
463
464
+ def _splitlines_no_ff (source ):
465
+ """Split a string into lines ignoring form feed and other chars.
466
+
467
+ This mimics how the Python parser splits source code.
468
+ """
469
+ idx = 0
470
+ lines = []
471
+ next_line = ''
472
+ while idx < len (source ):
473
+ c = source [idx ]
474
+ next_line += c
475
+ idx += 1
476
+ # Keep \r\n together
477
+ if c == '\r ' and idx < len (source ) and source [idx ] == '\n ' :
478
+ next_line += '\n '
479
+ idx += 1
480
+ if c in '\r \n ' :
481
+ lines .append (next_line )
482
+ next_line = ''
483
+
484
+ if next_line :
485
+ lines .append (next_line )
486
+ return lines
487
+
488
+
489
+ def _pad_whitespace (source ):
490
+ """Replace all chars except '\f \t ' in a line with spaces."""
491
+ result = ''
492
+ for c in source :
493
+ if c in '\f \t ' :
494
+ result += c
495
+ else :
496
+ result += ' '
497
+ return result
498
+
499
+
500
+ def get_source_segment (source , node , padded = False ):
501
+ """Get source code segment of the *source* that generated *node*.
502
+
503
+ If some location information (`lineno`, `end_lineno`, `col_offset`,
504
+ or `end_col_offset`) is missing, return None.
505
+
506
+ If *padded* is `True`, the first line of a multi-line statement will
507
+ be padded with spaces to match its original position.
508
+ """
509
+ try :
510
+ lineno = node .lineno - 1
511
+ end_lineno = node .end_lineno - 1
512
+ col_offset = node .col_offset
513
+ end_col_offset = node .end_col_offset
514
+ except AttributeError :
515
+ return None
516
+
517
+ lines = _splitlines_no_ff (source )
518
+ if end_lineno == lineno :
519
+ return lines [lineno ].encode ()[col_offset :end_col_offset ].decode ()
520
+
521
+ if padded :
522
+ padding = _pad_whitespace (lines [lineno ].encode ()[:col_offset ].decode ())
523
+ else :
524
+ padding = ''
525
+
526
+ first = padding + lines [lineno ].encode ()[col_offset :].decode ()
527
+ last = lines [end_lineno ].encode ()[:end_col_offset ].decode ()
528
+ lines = lines [lineno + 1 :end_lineno ]
529
+
530
+ lines .insert (0 , first )
531
+ lines .append (last )
532
+ return '' .join (lines )
533
+
534
+
464
535
def increment_lineno (node , n = 1 ):
465
536
"""
466
537
Increment the line number and end line number of each node in the tree
0 commit comments