1 {
2  ***************************************************************************
3  *                                                                         *
4  *   This source is free software; you can redistribute it and/or modify   *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This code is distributed in the hope that it will be useful, but      *
10  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
12  *   General Public License for more details.                              *
13  *                                                                         *
14  *   A copy of the GNU General Public License is available on the World    *
15  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
16  *   obtain it by writing to the Free Software Foundation,                 *
17  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
18  *                                                                         *
19  ***************************************************************************
20 
21   Author: Sérgio Marcelo S. Gomes <smace at smace.com.br>
22   Modified by Andrew Haines and Juha Manninen
23 
24   Abstract: Invert Assignment Code.
25 
26   Example: AValue := BValue  ->  BValue := AValue;
27            AValue := True    ->  AValue := False;
28 
29 }
30 unit InvertAssignTool;
31 
32 {$mode objfpc}{$H+}
33 
34 interface
35 
36 uses
37   Classes, SysUtils,
38   LazStringUtils;
39 
InvertAssignmentnull40 function InvertAssignment(InText: string): string;
41 
42 implementation
43 
44 
GetIndentnull45 function GetIndent(ALine: String):Integer;
46 begin
47   Result := Length(Aline) - Length(TrimLeft(ALine));
48 end;
49 
50 procedure DivideLines(Lines: TStrings; var PreList, AList, BList, PostList: TStrings);
51 var
52   ALine, TrueFalse: String;
53   t, f: Boolean;
54   X, I, EqPos, SemiPos, WordEndPos, BracketCount: Integer;
55 begin
56   for X := 0 to Lines.Count-1 do begin
57     ALine := Trim(Lines[X]);
58     EqPos := Pos(':=', ALine);
59     if EqPos > 0 then begin
60       SemiPos := Pos(';', ALine);
61       if SemiPos = 0 then
62         SemiPos:=Length(ALine)+1;
63       I := EqPos-1;
64       while (I > 0) and (ALine[I] = ' ') do      // Skip initial spaces
65         Dec(I);
66       WordEndPos := I+1;
67       BracketCount := 0;
68       // Get the word before :=
69       while I > 0 do begin
70         if ALine[I] = ']' then
71           Inc(BracketCount)
72         else if ALine[I] = '[' then
73           Dec(BracketCount);
74         if (BracketCount = 0) and (ALine[I] = ' ') then
75           Break;
76         Dec(I);
77       end;
78       // I points now at beginning of word - 1
79       Alist.Add(Copy(ALine, I+1, WordEndPos-(I+1)));
80       BList.Add(Trim(Copy(ALine, EqPos+2, SemiPos-EqPos-2)));
81       PreList.Add(Trim(Copy(ALine,1, I)));
82       PostList.Add(Trim(Copy(ALine, SemiPos, Length(ALine)-(SemiPos-1))));
83       if Length(PreList[X]) > 0 then
84         PreList[X] := PreList[X] + ' ';
85     end
86     else begin  // not a valid line
87       PreList.Add('');
88       AList.Add(ALine);
89       Blist.Add('');
90       PostList.Add('');
91     end;
92     // Check if is being assigned true or false
93     t := CompareText(BList[X], 'True') = 0;
94     f := CompareText(BList[X], 'False') = 0;
95     if t or f then begin
96       TrueFalse := AList[X];
97       AList[X] := BoolToStr(not t, 'True', 'False');
98       BList[X] := TrueFalse;
99     end;
100   end;
101 end;
102 
InvertLinenull103 function InvertLine(PreVar, VarA, VarB, PostVar: String; LineStart, EqualPosition: Integer): String;
104 var
105   fLength: Integer;
106   X: Integer;
107 begin
108   Result := '';
109   for X := 1 to LineStart do
110     Result := Result + ' ';
111   if Length(Trim(VarB)) = 0 then   // is not a line with a ':='
112     Result := Result + VarA
113   else begin
114     Result := Result + PreVar + VarB;
115     fLength := Length(Trim(Result));
116     if fLength < EqualPosition then
117       for X := fLength+1 to EqualPosition do
118         Result := Result + ' ';
119     Result := Result + ' := ' + VarA + PostVar;
120   end;
121 end;
122 
IsAWholeLinenull123 function IsAWholeLine(const ALine: String): Boolean;
124 begin
isnull125   // This function is useful for when the text is put back
126   // in the synedit, things like this don't happen:
127   // begin
128   //   if CallSomeFunction > 0
129   //   then
130   //     DoThis
131   //   else
132   //     Exit;
133   // end;
134   //
135   // Would otherwise become this
136   //
137   // begin if CallSomeFunction > 0 then DoThis else exit;
138   // end;
139   Result := False;
140   if (Pos(';', ALine) > 0)
141   or (PosI('if ', ALine) > 0)
142   or (PosI('begin', ALine) > 0)
143   or (PosI('end', ALine) > 0)
144   or (PosI('then', ALine) > 0)
145   or (PosI('else', ALine) > 0)
146   or (PosI('and', ALine) > 0)
147   or (PosI('or', ALine) > 0)
148   or (Pos('//', ALine) > 0)
149   then Result := True;
150 end;
151 
152 
153 // This function inverts all Assignments operation.
154 // like valuea := valueb; to valueb := valuea;
155 // or valuea := False; to valuea := True;
156 function InvertAssignment(InText: string): string;
157 var
158   InLines, TempLines: TStringList;
159   PreList, AList, BList, PostList: TStrings;
160   ALine: String;
161   Indents: array of integer;
162   X, Y, EqPos: Integer;
163   HasLinefeed: Boolean;
164 begin
165   if InText = '' then
166     Exit('');
167   HasLinefeed := InText[Length(InText)] in [#10,#13];
168   InLines := TStringList.Create;
169   InLines.Text := InText;
170   SetLength(Indents, InLines.Count);
171   TempLines := TStringList.Create;
172 
173   // Join many lines to one
174   ALine := '';
175   for X := 0 to InLines.Count-1 do begin
176     ALine := ALine + InLines[X];
177     if IsAWholeLine(ALine) then begin
178       Indents[TempLines.Add(ALine)] := GetIndent(ALine);
179       ALine := '';
180     end;
181   end;
182   if Length(ALine) > 0 then
183     Indents[TempLines.Add(ALine)] := GetIndent(ALine);
184 
185   InLines.Clear;
186   PreList := TStringList.Create;
187   AList := TStringList.Create;
188   BList := TStringList.Create;
189   PostList := TStringList.Create;
190 
191   DivideLines(TempLines, PreList, AList, BList, PostList);
192   TempLines.Free;
193 
194   // Find where the ':=' should be
195   EqPos := 0;
196   for X := 0 to BList.Count-1 do begin
197     Y := Length(BList[X]);
198     if Y > EqPos then
199       EqPos := Y;
200   end;
201 
202   for X := 0 to AList.Count-1 do
203     InLines.Add(InvertLine(PreList[X],Alist[X],BList[X],PostList[X],Indents[X],EqPos));
204   PreList.Free;
205   AList.Free;
206   BList.Free;
207   PostList.Free;
208   Result := InLines.Text;
209   InLines.Free;
210   if not HasLinefeed then begin
211     while Result[Length(Result)] in [#10,#13] do
212       SetLength(Result, Length(Result)-1);
213   end;
214 end;
215 
216 end.
217 
218