Repository navigation
Expand file tree
/
Copy pathKM_StringUtils.pas
More file actions
163 lines (120 loc) · 4.51 KB
/
Copy pathKM_StringUtils.pas
File metadata and controls
163 lines (120 loc) · 4.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
unit KM_StringUtils;
interface
uses
System.Classes;
// These function were replacements for string functions introduced after XE2 (XE5 probably)
// Names are the same as in new Delphi versions, but with 'Str' prefix
// We kept them here to support pre-XE5 compilation
function RightStrAfter(const aStr, aSeparator: string): string;
function LeftStrBefore(const aStr, aSeparator: string): string;
function FirstStrBetween(const aStr, aFrom, aTo: string): string;
procedure FindRegionBounds(aStringList: TStringList; aMarker: string; out aLineFrom, aLineTo, aPadLevel: Integer);
function ExtractFunctionResultType(aStr: string): string;
function ParagraphWordWrap(const aStr: string; aMaxLength: Integer = 120): string;
function ParagraphPad(const aStr, aPad: string): string;
implementation
uses
System.SysUtils, System.Types, System.StrUtils;
// Copy everything on the left of the separator
// 123:mystring:3 -> 123
function LeftStrBefore(const aStr, aSeparator: string): string;
begin
Result := Copy(aStr, 1, Pos(aSeparator, aStr) - 1);
end;
// Copy everything on the right of the separator
// 123:mystring:3 -> mystring:3
function RightStrAfter(const aStr, aSeparator: string): string;
begin
Result := Copy(aStr, Pos(aSeparator, aStr) + Length(aSeparator), MaxInt);
end;
function FirstStrBetween(const aStr, aFrom, aTo: string): string;
begin
Result := LeftStrBefore(RightStrAfter(aStr, aFrom), aTo);
end;
procedure FindRegionBounds(aStringList: TStringList; aMarker: string; out aLineFrom, aLineTo, aPadLevel: Integer);
begin
aLineFrom := -1;
repeat
Inc(aLineFrom);
if aLineFrom >= aStringList.Count then
begin
aLineFrom := -1;
Exit;
end;
until (Trim(aStringList[aLineFrom]) = aMarker);
aPadLevel := Pos(aMarker, aStringList[aLineFrom]) - 1;
Inc(aLineFrom);
aLineTo := aLineFrom;
repeat
Inc(aLineTo);
if aLineTo >= aStringList.Count then
Exit;
until (Trim(aStringList[aLineTo]) = aMarker);
Dec(aLineTo);
end;
// "function MyMethod: Integer;" -> Integer
// "function MyMethod(a: Byte; B: string): Integer;" -> Integer
function ExtractFunctionResultType(aStr: string): string;
begin
var posColon := LastDelimiter(':', aStr);
var posSemicolon := Pos(';', aStr, posColon);
var tail := Copy(aStr, posColon + 1, posSemicolon - posColon - 1);
Result := Trim(tail);
end;
function ParagraphWordWrap(const aStr: string; aMaxLength: Integer = 120): string;
function WrapLine(const aLine: string): string;
begin
Result := '';
// Get indentation of the original line
var indentLen := 0;
while (indentLen < Length(aLine)) and (aLine[indentLen + 1] in [' ', #9]) do
Inc(indentLen);
var indent := Copy(aLine, 1, indentLen);
var startPos := 1;
while Length(aLine) - startPos + 1 > aMaxLength do
begin
// Continuation lines already contain the indentation,
// so account for it when calculating the available width
var wedgePos := startPos + aMaxLength - Length(indent) - 1;
// The first line already contains its indentation
if startPos = 1 then
wedgePos := startPos + aMaxLength - 1;
// Find the last space within the allowed line length
var spacePos := wedgePos;
while (spacePos >= startPos) and (aLine[spacePos] <> ' ') do
Dec(spacePos);
// No space found: force a break at the maximum length
if spacePos < startPos then
spacePos := wedgePos + 1;
Result := Result + Copy(aLine, startPos, spacePos - startPos) + sLineBreak + indent;
// Skip the space used for wrapping
if (spacePos <= Length(aLine)) and (aLine[spacePos] = ' ') then
startPos := spacePos + 1
else
startPos := spacePos;
end;
Result := Result + Copy(aLine, startPos, MaxInt);
end;
begin
// Preserve existing line breaks and wrap each line separately
var lines := aStr.Split([sLineBreak]);
Result := '';
for var I := 0 to High(lines) do
begin
if I > 0 then
Result := Result + sLineBreak;
Result := Result + WrapLine(lines[I]);
end;
end;
function ParagraphPad(const aStr, aPad: string): string;
begin
// Preserve existing line breaks and pad each line separately
var lines := aStr.Split([sLineBreak]);
for var I := 0 to High(lines) do
begin
if I > 0 then
Result := Result + sLineBreak;
Result := Result + IfThen(lines[I] <> '', aPad) + lines[I];
end;
end;
end.