1+ // <auto-generated />
2+ #region License
3+ // MIT License
4+ //
5+ // Copyright (c) Daniel Cazzulino
6+ //
7+ // Permission is hereby granted, free of charge, to any person obtaining a copy
8+ // of this software and associated documentation files (the "Software"), to deal
9+ // in the Software without restriction, including without limitation the rights
10+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+ // copies of the Software, and to permit persons to whom the Software is
12+ // furnished to do so, subject to the following conditions:
13+ //
14+ // The above copyright notice and this permission notice shall be included in all
15+ // copies or substantial portions of the Software.
16+ //
17+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+ // SOFTWARE.
24+ #endregion
25+
26+ #nullable enable
27+ using System . Text ;
28+ using System . Text . RegularExpressions ;
29+
30+ namespace System
31+ {
32+ /// <summary>
33+ /// String extension methods for text processing.
34+ /// </summary>
35+ static partial class StringExtensions
36+ {
37+ /// <summary>
38+ /// Remove leading whitespace from each line of a multi-line string that is common
39+ /// to all non-empty lines. This is equivalent to Python's textwrap.dedent().
40+ /// </summary>
41+ /// <param name="text">The text to dedent.</param>
42+ /// <returns>The dedented text.</returns>
43+ /// <example>
44+ /// <code>
45+ /// var text = """
46+ /// Line 1
47+ /// Line 2
48+ /// Line 3
49+ /// """;
50+ /// var dedented = text.Dedent();
51+ /// // Result:
52+ /// // Line 1
53+ /// // Line 2
54+ /// // Line 3
55+ /// </code>
56+ /// </example>
57+ public static string Dedent ( this string text )
58+ {
59+ if ( string . IsNullOrEmpty ( text ) )
60+ return text ;
61+
62+ // Detect the line ending style used in the input
63+ var lineEnding = Environment . NewLine ;
64+ if ( text . Contains ( "\r \n " ) )
65+ lineEnding = "\r \n " ;
66+ else if ( text . Contains ( '\r ' ) )
67+ lineEnding = "\r " ;
68+ else if ( text . Contains ( '\n ' ) )
69+ lineEnding = "\n " ;
70+
71+ // Split using regex to properly handle different line endings
72+ var lines = NewLineExpr ( ) . Split ( text ) ;
73+
74+ // Remove leading and trailing empty lines
75+ int start = 0 ;
76+ int end = lines . Length - 1 ;
77+
78+ while ( start < lines . Length && string . IsNullOrWhiteSpace ( lines [ start ] ) )
79+ start ++ ;
80+
81+ while ( end >= 0 && string . IsNullOrWhiteSpace ( lines [ end ] ) )
82+ end -- ;
83+
84+ if ( start > end )
85+ return string . Empty ;
86+
87+ // Find the minimum indentation (ignoring empty lines)
88+ int minIndent = int . MaxValue ;
89+ for ( int i = start ; i <= end ; i ++ )
90+ {
91+ var line = lines [ i ] ;
92+ if ( ! string . IsNullOrWhiteSpace ( line ) )
93+ {
94+ int indent = 0 ;
95+ while ( indent < line . Length && char . IsWhiteSpace ( line [ indent ] ) )
96+ indent ++ ;
97+
98+ minIndent = Math . Min ( minIndent , indent ) ;
99+ }
100+ }
101+
102+ if ( minIndent == int . MaxValue || minIndent == 0 )
103+ minIndent = 0 ;
104+
105+ // Remove the common indentation from all lines
106+ var result = new StringBuilder ( ) ;
107+ for ( int i = start ; i <= end ; i ++ )
108+ {
109+ var line = lines [ i ] ;
110+ if ( string . IsNullOrWhiteSpace ( line ) )
111+ {
112+ if ( i < end ) // Don't add newline for last empty line
113+ result . Append ( lineEnding ) ;
114+ }
115+ else
116+ {
117+ var dedentedLine = line . Length > minIndent ? line [ minIndent ..] : string . Empty ;
118+ result . Append ( dedentedLine ) ;
119+ if ( i < end )
120+ result . Append ( lineEnding ) ;
121+ }
122+ }
123+
124+ return result . ToString ( ) ;
125+ }
126+
127+ [ GeneratedRegex ( @"\r\n|\r|\n" ) ]
128+ private static partial Regex NewLineExpr ( ) ;
129+ }
130+ }
0 commit comments