1 unit SingleSpaceBefore;
2 
3 {(*}
4 (*------------------------------------------------------------------------------
5  Delphi Code formatter source code
6 
7 The Original Code is SingleSpaceBefore, released May 2003.
8 The Initial Developer of the Original Code is Anthony Steele.
9 Portions created by Anthony Steele are Copyright (C) 1999-2008 Anthony Steele.
10 All Rights Reserved.
11 Contributor(s): Anthony Steele.
12 
13 The contents of this file are subject to the Mozilla Public License Version 1.1
14 (the "License"). you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at http://www.mozilla.org/NPL/
16 
17 Software distributed under the License is distributed on an "AS IS" basis,
18 WITHOUT WARRANTY OF ANY KIND, either express or implied.
19 See the License for the specific language governing rights and limitations
20 under the License.
21 
22 Alternatively, the contents of this file may be used under the terms of
23 the GNU General Public License Version 2 or later (the "GPL")
24 See http://www.gnu.org/licenses/gpl.html
25 ------------------------------------------------------------------------------*)
26 {*)}
27 
28 {$I JcfGlobal.inc}
29 
30 interface
31 
32 
33 { AFS 7 Dec 1999
34   single space before certain tokens (e.g. ':='
35 
36   This process and SingleSpaceAfter must be carefull with directives:
37    words like "read" and "write" must be single-spaced in property defs
38    but in normal code these are valid procedure names, and
39      converting "Result := myObject.Read;" to
40      "Result := myObject. read ;" compiles, but looks all wrong
41 }
42 
43 uses SwitchableVisitor;
44 
45 type
46   TSingleSpaceBefore = class(TSwitchableVisitor)
47   private
48   protected
EnabledVisitSourceTokennull49     function EnabledVisitSourceToken(const pcNode: TObject): boolean; override;
50   public
51     constructor Create; override;
52 
IsIncludedInSettingsnull53     function IsIncludedInSettings: boolean; override;
54   end;
55 
56 
57 implementation
58 
59 uses
60   { local }
61   JcfStringUtils,
62   SourceToken, Tokens, ParseTreeNodeType, JcfSettings,
63   FormatFlags, TokenUtils, SettingsTypes;
64 
65 const
66   // space before all operators
67   SingleSpaceBeforeWords: TTokenTypeSet = [ttEquals, ttThen, ttOf, ttDo,
68     ttTo, ttDownTo];
69 
70   NoSpaceAfterTokens: TTokenTypeSet = [ttOpenBracket, ttOpenSquareBracket];
71 
NeedsSpaceBeforenull72 function NeedsSpaceBefore(const pt: TSourceToken): boolean;
73 var
74   lcPrev: TSourceToken;
75 begin
76   Result := False;
77 
78   if pt = nil then
79     exit;
80 
81   if pt.HasParentNode(nGeneric, 1) then
82     exit;
83 
84   if pt.TokenType = ttCloseBracket then
85   begin
86     if FormattingSettings.Spaces.SpaceBeforeCloseBrackets then
87     begin
88       Result := true;
89       exit;
90     end;
91   end;
92 
93   if (pt.TokenType = ttOpenBracket) then
94   begin
95     if FormattingSettings.Spaces.SpaceBeforeOpenBracketsInFunctionDeclaration then
96     begin
97       if pt.HasParentNode(nFormalParams, 1) then
98       begin
99         Result := true;
100         exit;
101       end;
102     end;
103 
104     if FormattingSettings.Spaces.SpaceBeforeOpenBracketsInFunctionCall then
105     begin
106       if pt.HasParentNode(nActualParams, 1) then
107       begin
108         Result := true;
109         exit;
110       end;
111     end;
112 
113   end
114   else if (pt.TokenType = ttOpenSquareBracket) then
115   begin
116     if FormattingSettings.Spaces.SpaceBeforeOpenSquareBracketsInExpression then
117     begin
118       if pt.HasParentNode(nExpression) then
119       begin
120         Result := true;
121         exit;
122       end;
123     end;
124   end;
125 
126 
127   if pt.HasParentNode(nLiteralString) then
128   begin
129     Result := False;
130     exit;
131   end;
132 
133   if IsClassHelperWords(pt) then
134   begin
135     Result := True;
136     exit;
137   end;
138 
139   { not in Asm block }
140   if pt.HasParentNode(nAsm) then
141     exit;
142 
143   if (pt.TokenType in AssignmentDirectives) then
144   begin
145     Result := True;
146     exit;
147   end;
148 
149   if IsHintDirective(pt) then
150   begin
151     Result := True;
152     exit;
153   end;
154 
155 
156   if (pt.TokenType in AllDirectives) and (pt.HasParentNode(DirectiveNodes)) then
157   begin
158     Result := True;
159     exit;
160   end;
161 
162   if (pt.TokenType in SingleSpaceBeforeWords) then
163   begin
164     Result := True;
165     exit;
166   end;
167 
168   if FormattingSettings.Spaces.SpaceForOperator = eAlways then
169   begin
170     if (pt.TokenType in SingleSpaceOperators) then
171     begin
172       Result := True;
173     end;
174 
175     { 'a := --3;' and 'lc := ptr^;'
176     are the only exceptions to the rule of a space before an operator }
177     if (pt.TokenType in Operators) then
178     begin
179       if (pt.TokenType = ttHat) or
180         (IsUnaryOperator(pt) and IsUnaryOperator(pt.PriorSolidToken)) then
181         Result := False
182       else
183         Result := True;
184 
185       exit;
186     end;
187 
188   end;
189 
190   { 'in' in the uses clause }
191   if ((pt.TokenType = ttIn) and (pt.HasParentNode(nUses))) then
192   begin
193     Result := True;
194     exit;
195   end;
196 
197   { comment just after uses clause, unless it's a compiler directive }
198   if (pt.TokenType = ttComment) and (pt.CommentStyle <> eCompilerDirective) then
199   begin
200     lcPrev := pt.PriorSolidToken;
201     if (lcPrev <> nil) and (lcPrev.TokenType = ttUses) then
202     begin
203       Result := True;
204       exit;
205     end;
206   end;
207 
208   { 'absolute' as a var directive }
209   if (pt.TokenType = ttAbsolute) and pt.HasParentNode(nAbsoluteVar) then
210   begin
211     Result := True;
212     exit;
213   end;
214 
215   { any token that starts a literal string }
216   if StartsLiteralString(pt) then
217   begin
218     Result := True;
219     exit;
220   end;
221 
222   if (pt.TokenType = ttDefault) and pt.HasParentNode(nPropertySpecifier) then
223   begin
224     Result := True;
225     exit;
226   end;
227 
228   { signle space before read, write etc in property }
229   if pt.HasParentNode(nProperty) then
230   begin
231     if (pt.TokenType in [ttProperty, ttRead, ttWrite, ttDefault,
232       ttStored, ttNoDefault, ttImplements]) then
233     begin
234       Result := True;
235       exit;
236     end;
237   end;
238 
239 
240   { program uses clauses has a form link comment }
241   if InFilesUses(pt) then
242   begin
243     if ((pt.TokenType = ttComment) and (pt.CommentStyle in CURLY_COMMENTS)) and
244       pt.IsOnRightOf(nUses, ttUses) then
245     begin
246       Result := True;
247       exit;
248     end;
249   end;
250 
251 end;
252 
253 
254 constructor TSingleSpaceBefore.Create;
255 begin
256   inherited;
257   FormatFlags := FormatFlags + [eAddSpace, eRemoveSpace, eRemoveReturn];
258 end;
259 
EnabledVisitSourceTokennull260 function TSingleSpaceBefore.EnabledVisitSourceToken(const pcNode: TObject): boolean;
261 var
262   lcSourceToken, lcNext, lcNew: TSourceToken;
263 begin
264   Result := False;
265   lcSourceToken := TSourceToken(pcNode);
266   lcNext := lcSourceToken.NextToken;
267 
268   if lcNext = nil then
269     exit;
270 
271   // suspend this rule after open brackets
272   // e.g. if there's a space before a '-' minus sign
273   // this applies to "x - 1" but not "(-1 + x)"
274   if lcSourceToken.TokenType in NoSpaceAfterTokens then
275   begin
276     exit;
277   end;
278 
279   if NeedsSpaceBefore(lcNext) then
280   begin
281     if (lcSourceToken.TokenType = ttWhiteSpace) then
282     begin
283       { one space }
284       lcSourceToken.SourceCode := NativeSpace;
285 
286       { empty any preceeding whitespace }
287       repeat
288         lcSourceToken := lcSourceToken.PriorToken;
289         if lcSourceToken.TokenType = ttWhiteSpace then
290           lcSourceToken.SourceCode := '';
291       until lcSourceToken.TokenType <> ttWhiteSpace;
292     end
293     else
294     begin
295       lcNew := TSourceToken.Create;
296       lcNew.TokenType := ttWhiteSpace;
297       lcNew.SourceCode := NativeSpace;
298 
299       InsertTokenAfter(lcSourceToken, lcNew);
300     end;
301   end;
302 
303 end;
304 
IsIncludedInSettingsnull305 function TSingleSpaceBefore.IsIncludedInSettings: boolean;
306 begin
307   Result := FormattingSettings.Spaces.FixSpacing;
308 end;
309 
310 end.
311