@@ -413,129 +413,60 @@ def shorten(text, width, **kwargs):
413413
414414# -- Loosely related functionality -------------------------------------
415415
416- def dedent (text , only_whitespace = True ):
417- """Remove any common leading whitespace from every line in `text`.
418-
419- This can be used to make triple-quoted strings line up with the left
420- edge of the display, while still presenting them in the source code
421- in indented form.
422-
423- Note that tabs and spaces are both treated as whitespace, but they
424- are not equal: the lines " hello" and "\\ thello" are
425- considered to have no common leading whitespace.
426-
427- If `only_whitespace` is `True`, the leading whitespaces are removed from the text. Otherwise, all the common leading text is removed.
416+ def dedent (text ):
417+ """
418+ Remove any common leading whitespace from every line in text.
428419
429420 Entirely blank lines are normalized to a newline character.
421+ Tabs and spaces are treated as distinct.
422+
423+ This implementation uses os.path.commonprefix (implemented in C)
424+ to compute the common margin of non-blank lines for maximal performance.
430425 """
431- # Early return for empty input
426+ # Fast paths for empty or simple text
432427 if not text :
433428 return text
434429
435- # Split into lines
436- lines = text . splitlines ( True )
430+ if " \n " not in text :
431+ return text # Single line has no dedent
437432
438- # Fast path for single line - but make sure we still dedent!
439- if len (lines ) == 1 :
440- line = lines [0 ]
441- stripped = line .strip ()
442- if not stripped : # Blank line
443- return "\n " if line .endswith ("\n " ) else ""
433+ # Split text into lines, preserving line endings
434+ lines : List [str ] = text .splitlines (keepends = True )
435+
436+ # Process in a single pass to find:
437+ # 1. Leading whitespace of non-blank lines
438+ # 2. Whether a line has zero leading whitespace (optimization)
439+ non_blank_whites = []
440+ has_zero_margin = False
444441
445- # Find leading whitespace for a single line
446- if only_whitespace :
447- i = 0
448- while i < len (line ) and line [i ] in " \t " :
449- i += 1
450- if i > 0 : # Has leading whitespace to remove
451- return line [i :]
452- else :
453- lead_size = len (line ) - len (line .lstrip ())
454- if lead_size > 0 : # Has leading whitespace to remove
455- return line [lead_size :]
456- return line # No whitespace to remove
457-
458- # Cache method lookups for faster access
459- _strip = str .strip
460- _startswith = str .startswith
461- _endswith = str .endswith
462-
463- # Find first two non-blank lines
464- non_blank = []
465442 for line in lines :
466- if _strip (line ):
467- non_blank .append (line )
468- if len (non_blank ) == 2 :
469- break
470-
471- # All lines are blank
472- if not non_blank :
473- result = []
474- append = result .append
475- for line in lines :
476- append ("\n " if _endswith (line , "\n " ) else "" )
477- return "" .join (result )
478-
479- # Calculate margin length efficiently
480- if len (non_blank ) == 1 :
481- # Single non-blank line
482- line = non_blank [0 ]
483- if only_whitespace :
484- # Manually find leading whitespace (faster than regex)
485- i = 0
486- line_len = len (line )
487- while i < line_len and line [i ] in " \t " :
488- i += 1
489- margin_len = i
490- else :
491- # Use built-in lstrip for non-whitespace case
492- margin_len = len (line ) - len (line .lstrip ())
443+ stripped = line .strip ()
444+ if stripped : # Non-blank line
445+ leading = line [:len (line ) - len (line .lstrip ())]
446+ non_blank_whites .append (leading )
447+ # Early detection of zero margin case
448+ if not leading :
449+ has_zero_margin = True
450+ break # No need to check more lines
451+
452+ # If all lines are blank, normalize them
453+ if not non_blank_whites :
454+ # Preallocate result list
455+ return "" .join (["\n " if line .endswith ("\n " ) else "" for line in lines ])
456+
457+ # Skip commonprefix calculation if we already know there's no margin
458+ if has_zero_margin :
459+ margin_len = 0
493460 else :
494- # Find common prefix of first two non-blank lines
495- a , b = non_blank
496- min_len = min (len (a ), len (b ))
497- i = 0
498-
499- if only_whitespace :
500- # Manual loop is faster than character-by-character comparison
501- while i < min_len and a [i ] == b [i ] and a [i ] in " \t " :
502- i += 1
503- else :
504- while i < min_len and a [i ] == b [i ]:
505- i += 1
506-
507- margin_len = i
461+ common = os .path .commonprefix (non_blank_whites )
462+ margin_len = len (common )
508463
509- # No margin to remove - return original with blank line normalization
464+ # No common margin case - just normalize blank lines
510465 if margin_len == 0 :
511- result = []
512- append = result .append
513- for line in lines :
514- if _strip (line ): # Non-blank line
515- append (line )
516- else : # Blank line
517- append ("\n " if _endswith (line , "\n " ) else "" )
518- return "" .join (result )
519-
520- # Get margin string once for repeated comparison
521- margin = non_blank [0 ][:margin_len ]
522-
523- # Pre-allocate result list with a size hint for better memory efficiency
524- result = []
525- append = result .append
526-
527- # Process all lines with optimized operations
528- for line in lines :
529- if not _strip (line ): # Blank line (including whitespace-only lines)
530- append ("\n " if _endswith (line , "\n " ) else "" )
531- elif _startswith (line , margin ): # Has margin
532- # Slice operation is very fast in Python
533- append (line [margin_len :])
534- else : # No matching margin
535- append (line )
536-
537- # Single join is faster than incremental string building
538- return "" .join (result )
466+ return "" .join ([line if line .strip () else "\n " if line .endswith ("\n " ) else "" for line in lines ])
467+
468+ # Apply margin removal (most common case) with minimal operations
469+ return "" .join ([line [margin_len :] if line .strip () else "\n " if line .endswith ("\n " ) else "" for line in lines ])
539470
540471
541472def indent (text , prefix , predicate = None ):
0 commit comments