Jedi Code Format: Improve formatting of preprocessor blocks and preprocessor directives. Issue #39662, patch by Domingo Galmés.

This commit is contained in:
Juha 2022-11-02 19:29:33 +02:00
parent 4c6414f837
commit f4d0eeb099
9 changed files with 166 additions and 69 deletions

View File

@ -90,8 +90,8 @@ implementation
uses
{ local }
PreProcessorExpressionTokenise, PreProcessorExpressionParser,
ParseError, JcfSettings, JcfUiTools;
PreProcessorExpressionTokenise, PreProcessorExpressionParser, StrUtils,
ParseError, JcfSettings, JcfUiTools, JcfStringUtils, TokenUtils, SettingsTypes;
procedure RemoveConditionalCompilation(const pcTokens: TSourceTokenList);
var
@ -420,13 +420,16 @@ begin
NextToken;
end;
// parse preprocessor block until finds ENDIF IFEND, ELSE ELSEIF.
// if the block is inactive then include all nested preprocessor blocks.
procedure TPreProcessorParseTree.ParseNonPreProc(
const peEndTokens: TPreProcessorSymbolTypeSet);
var
lcToken: TSourceToken;
liNestLevel: integer;
begin
{ go forward until another preprocessor tag is found. or end of file }
liNestLevel := 1;
while (fiCurrentTokenIndex < fcTokens.Count) do
begin
lcToken := CurrentToken;
@ -437,14 +440,21 @@ begin
if (lcToken.CommentStyle = eCompilerDirective) then
begin
if lcToken.PreprocessorSymbol in peEndTokens then
//inactive nested {$IFxxxx}
if (not fbPreprocessorIncluded) and (lcToken.PreprocessorSymbol in [ppIfDef, ppIfNotDef, ppIfOpt, ppIfExpr]) then
Inc(liNestLevel)
else if (liNestLevel > 1) and (lcToken.PreprocessorSymbol in [ppIfEnd, ppEndIf]) then
Dec(liNestLevel)
else if (liNestLevel = 1) and (lcToken.PreprocessorSymbol in peEndTokens) then
begin
lcToken.PreProcessedOut := False;
break;
end;
// if we are preproc'd in then parse these
ParseProcessorBlock;
if fbPreprocessorIncluded then
// if we are preproc'd in then parse these
ParseProcessorBlock
else
NextToken;
end
else
NextToken;
@ -569,6 +579,7 @@ var
liLoop: integer;
lsOutText: string;
lcCurrentToken, lcExcludedText: TSourceToken;
lbLFAfter,lbLFBefore:TTriOptionStyle;
begin
// right, what's out?
liLoop := 0;
@ -579,7 +590,8 @@ begin
if lcCurrentToken.PreprocessedOut then
begin
lsOutText := '';
lbLFBefore:= CompilerDirectiveLineBreak(lcCurrentToken, True);
lbLFAfter:= CompilerDirectiveLineBreak(lcCurrentToken, False);
while (lcCurrentToken <> nil) and lcCurrentToken.PreprocessedOut do
begin
lsOutText := lsOutText + lcCurrentToken.SourceCode;
@ -589,12 +601,19 @@ begin
{ next one will have shifted up }
lcCurrentToken := fcTokens.SourceTokens[liLoop];
end;
lcExcludedText := TSourceToken.Create;
lcExcludedText.FileName := lcCurrentToken.FileName;
lcExcludedText.TokenType := ttConditionalCompilationRemoved;
lcExcludedText.SourceCode := lsOutText;
case lbLFBefore of
eAlways:lcExcludedText.SourceCode := TrimRightSet(lsOutText,NativeTabSpace); //only spaces not returns
eLeave: lcExcludedText.SourceCode := lsOutText;
eNever: lcExcludedText.SourceCode := TrimRight(lsOutText); // spaces and returns
end;
case lbLFAfter of
eAlways: lcExcludedText.SourceCode := TrimLeft(lcExcludedText.SourceCode);
eLeave: ;
eNever: lcExcludedText.SourceCode := TrimLeft(lcExcludedText.SourceCode);
end;
fcTokens.Insert(liLoop, lcExcludedText);
end
else

View File

@ -852,20 +852,20 @@ end;
function CompilerDirectiveLineBreak(const pt: TSourceToken; const pbBefore: Boolean): TTriOptionStyle;
begin
if InStatements(pt) then
begin
if pbBefore then
Result := FormattingSettings.Returns.BeforeCompilerDirectStatements
else
Result := FormattingSettings.Returns.AfterCompilerDirectStatements;
end
else if pt.HasParentNode(nUses) then
if pt.HasParentNode(nUses) then
begin
if pbBefore then
Result := FormattingSettings.Returns.BeforeCompilerDirectUses
else
Result := FormattingSettings.Returns.AfterCompilerDirectUses;
end
else if InStatements(pt) then
begin
if pbBefore then
Result := FormattingSettings.Returns.BeforeCompilerDirectStatements
else
Result := FormattingSettings.Returns.AfterCompilerDirectStatements;
end
else
begin
if pbBefore then

View File

@ -56,7 +56,7 @@ uses
{ local }
JcfStringUtils,
SourceToken, Nesting, FormatFlags, JcfSettings, TokenUtils,
Tokens, ParseTreeNode, ParseTreeNodeType;
Tokens, ParseTreeNode, ParseTreeNodeType, SettingsTypes;
{ true if the specified token type occurs before pt on the line }
function HasPreceedingTokenTypeOnLine(const pt: TSourceToken; const ptt: TTokenTypeSet): Boolean;
@ -159,6 +159,11 @@ begin
else
Result := FormattingSettings.Indent.KeepCommentsWithCodeElsewhere;
end;
end
else if (pt.TokenType = ttComment) and (pt.CommentStyle = eCompilerDirective)
and (CompilerDirectiveLineBreak(pt,True)=eAlways) then
begin
Result:=true;
end;
end;

View File

@ -176,21 +176,22 @@ begin
exit;
end;
end;
if (pt.CommentStyle = eCompilerDirective) and (CompilerDirectiveLineBreak(pt, False) = eNever) then
begin
Result := True;
exit;
end;
end;
{ remove returns based on options }
{ the options don't apply after comments }
if (pt.TokenType = ttComment) then
if (pt.TokenType = ttComment) and (pt.CommentStyle = eCompilerDirective) then
begin
Result := False;
Result:=CompilerDirectiveLineBreak(pt, False) = eNever;
if Result then // remove spaces
begin
lcNext:=pt.NextToken;
while (lcNext<>nil) and (lcNext.TokenType in [ttReturn,ttWhiteSpace]) do
begin
BlankToken(lcNext);
lcNext:=lcNext.NextToken;
end;
end;
exit;
end;

View File

@ -61,6 +61,7 @@ const
ProcNoReturnWords: TTokenTypeSet = [ttThen, ttDo];
var
lcPrev: TParseTreeNode;
lcP:TSourceToken;
begin
Result := False;
@ -132,18 +133,31 @@ begin
if pt.HasParentNode(nAttribute) then
exit(True);
end;
if (pt.CommentStyle = eCompilerDirective) then
begin
if (CompilerDirectiveLineBreak(pt, True) = eNever) then
begin
// remove spaces
lcP:=pt.PriorToken;
while (lcP<>nil) and (lcP.TokenType in [ttReturn,ttWhiteSpace]) do
begin
BlankToken(lcP);
lcP:=lcP.PriorToken;
end;
exit(True);
end
else
exit(False);
end;
// "foo in Foo.pas, " has return only after the comma
if InFilesUses(pt) then
begin
if (pt.TokenType in [ttComma, ttWord, ttQuotedLiteralString]) or
((pt.TokenType = ttComment) and (pt.CommentStyle in CURLY_COMMENTS)) then
((pt.TokenType = ttComment) and (pt.CommentStyle = eCurlyBrace)) then
exit(True);
end;
if (pt.CommentStyle = eCompilerDirective) and (CompilerDirectiveLineBreak(pt, True) = eNever) then
exit(True);
end;
constructor TNoReturnBefore.Create;
@ -162,9 +176,18 @@ begin
lcSourceToken := TSourceToken(pcNode);
// not safe to remove return at a comment like this
if (lcSourceToken.TokenType = ttComment) and
(lcSourceToken.CommentStyle = eDoubleSlash) then
fbSafeToRemoveReturn := False
if (lcSourceToken.TokenType = ttComment)then
begin
if (lcSourceToken.CommentStyle = eDoubleSlash) then
fbSafeToRemoveReturn := False
else if lcSourceToken.CommentStyle = eCompilerDirective then
begin
if CompilerDirectiveLineBreak(lcSourceToken, True) = eNever then
fbSafeToRemoveReturn := true
else
fbSafeToRemoveReturn := false;
end;
end
else if (lcSourceToken.TokenType <> ttReturn) then
fbSafeToRemoveReturn := True;
// safe again after the next return

View File

@ -54,7 +54,7 @@ implementation
uses
{ local }
TokenUtils, SourceToken, Tokens,
TokenUtils, SourceToken, Tokens, JcfStringUtils,
ParseTreeNodeType, ParseTreeNode, JcfSettings, FormatFlags, SettingsTypes;
const
@ -75,8 +75,14 @@ const
5) as 4, in a procedure type in a type def
}
function SemicolonHasReturn(const pt, ptNext: TSourceToken): boolean;
var
lcN:TSourceToken;
begin
Result := True;
{before compiler directive }
lcN:=pt.NextTokenWithExclusions([ttWhiteSpace]); // include comments.
if (lcN<>nil) and (lcN.TokenType=ttComment) and (lcN.CommentStyle=eCompilerDirective)then
exit(false);
{ point 1 }
if ptNext.HasParentNode(nProcedureDirectives) then
exit(False);
@ -368,16 +374,6 @@ begin
exit(True);
end;
{ return after compiler directives
NB use lcNext as ptNext is the next *solid* token
cond comp removed is not solid }
if (pt.CommentStyle = eCompilerDirective) and (CompilerDirectiveLineBreak(pt, False) = eAlways) then
begin
lcNext := pt.NextTokenWithExclusions([ttWhiteSpace]);
if (lcNext <> nil) and (lcNext.TokenType <> ttConditionalCompilationRemoved)
then
exit(True);
end;
end;
function NeedsReturn(const pt, ptNext: TSourceToken): boolean;
@ -389,6 +385,25 @@ begin
{ these can include returns }
if pt.TokenType = ttConditionalCompilationRemoved then
exit;
if (pt.CommentStyle = eCompilerDirective) and (CompilerDirectiveLineBreak(pt, False) = eAlways) then
begin
Result:=false;
lcNext:=pt.NextTokenWithExclusions([ttWhiteSpace]);
if (lcNext<>nil) and (lcNext.TokenType<>ttReturn) then
begin
if (lcNext <> nil) then
begin
if (lcNext.TokenType <> ttConditionalCompilationRemoved) then
result:=true
else
begin
if StrStartsWithLineEnd(lcNext.SourceCode)=false then
result:=true;
end;
end;
end;
exit;
end;
{ option to Break After Uses }
if pt.HasParentNode(nUses) and (pt.TokenType = ttUses) and
@ -511,10 +526,6 @@ begin
if lcCommentTest = nil then
exit;
if (lcCommentTest.TokenType = ttComment) and
(lcCommentTest.CommentStyle = eDoubleSlash) then
exit;
{ white space that was on the end of the line shouldn't be carried over
to indent the next line }
lcNextSpace := lcSourceToken.NextToken;

View File

@ -59,7 +59,7 @@ implementation
uses
TokenUtils,
SourceToken, Tokens, ParseTreeNode,
ParseTreeNodeType, JcfSettings,
ParseTreeNodeType, JcfSettings, JcfStringUtils,
FormatFlags, SettingsTypes;
const
@ -250,14 +250,6 @@ begin
Result := True;
end;
{ return before compiler directives }
if (pt.CommentStyle = eCompilerDirective) and (CompilerDirectiveLineBreak(pt, True) = eAlways) then
begin
lcPrev := pt.PriorTokenWithExclusions([ttWhiteSpace]);
if (lcPrev <> nil) and (lcPrev.TokenType <> ttConditionalCompilationRemoved) then
exit(True);
end;
{ there is not always a return before 'type'
e.g.
type TMyInteger = type Integer;
@ -405,6 +397,27 @@ begin
{ number to insert = needed - actual }
liReturnsNeeded := liReturnsNeeded - fiReturnsBefore;
//preprocessor directives
if (lcSourceToken.TokenType = ttComment) and (lcSourceToken.CommentStyle=eCompilerDirective) then
begin
liReturnsNeeded := 0;
if CompilerDirectiveLineBreak(lcSourceToken, True) = eAlways then
begin
lcPrev:=lcSourceToken.PriorTokenWithExclusions([ttWhiteSpace]);
if (lcPrev<>nil) and (lcPrev.TokenType<>ttReturn) then
begin
if (lcPrev.TokenType <> ttConditionalCompilationRemoved) then
liReturnsNeeded:=1
else
begin
if StrEndsWithLineEnd(lcPrev.SourceCode)=false then
liReturnsNeeded:=1;
end;
end;
end;
end;
//var
// Test : function: Integer; // No New Line
//type
@ -421,7 +434,7 @@ begin
Result := True;
lcPrev := lcSourceToken.PriorToken;
if lcPrev.TokenType = ttWhiteSpace then
if (lcPrev<>nil) and (lcPrev.TokenType = ttWhiteSpace) then
BlankToken(lcPrev);
for liLoop := 0 to liReturnsNeeded - 1 do
@ -452,7 +465,6 @@ begin
Inc(fiNextReturnsBefore)
else if not (lcSourceToken.TokenType in [ttReturn, ttWhiteSpace, ttComment]) then
fiNextReturnsBefore := 0;
end;
function TReturnBefore.IsIncludedInSettings: boolean;

View File

@ -200,8 +200,8 @@ begin
{ program uses clauses has a form link comment }
if InFilesUses(pt) then
begin
if ((pt.TokenType = ttComment) and (pt.CommentStyle in CURLY_COMMENTS)) and
pt.IsOnRightOf(nUses, ttUses) then
if ((pt.TokenType = ttComment) and (pt.CommentStyle=eCurlyBrace)) and
pt.IsOnRightOf(nUses, ttUses) then
exit(True);
end;

View File

@ -89,6 +89,7 @@ const
NativeWhiteSpace = [NativeTab, NativeLineFeed, NativeVerticalTab,
NativeFormFeed, NativeCarriageReturn, NativeSpace];
NativeTabSpace =[NativeSpace,NativeTab];
NativeDoubleQuote = Char('"');
NativeSingleQuote = Char('''');
@ -125,7 +126,7 @@ function StrCharCount(const S: string; C: Char): Integer;
function StrStrCount(const S, SubS: string): Integer;
function StrRepeat(const S: string; Count: Integer): string;
procedure StrReplace(var S: string; const Search, Replace: string; Flags: TReplaceFlags = []);
function StrSearch(const Substr, S: string; const Index: Integer = 1): Integer;
function StrSearch(const Substr, S: string; const Index: Integer = 1): Integer; inline;
function StrFind(const Substr, S: string; const Index: Integer = 1): Integer;
function BooleanToStr(B: Boolean): string;
@ -155,6 +156,10 @@ function SkipLeftSpaces(const aStr: string; aPos: integer): integer;
function SkipToNextLine(const aStr: string; aPos: integer): integer;
function HasStringAtLineStart(const aSourceCode: string; const aStr: string): boolean;
function StrTrimLastEndOfLine(const aStr:string):string;
// string starts with LF, CR, ignores prior spaces
function StrStartsWithLineEnd(const aStr:string):boolean;
// string ends with LF, CR, ignores trailing spaces
function StrEndsWithLineEnd(const aStr:string):boolean;
type
EJcfConversionError = class(Exception)
@ -406,14 +411,14 @@ end;
function StrSearch(const Substr, S: string; const Index: Integer = 1): Integer;
begin
Result := Pos(SubStr, Copy(S, Index, Length(S)));
if Result > 0 then
Result := Result + Index - 1;
Result := Pos(SubStr, S ,Index);
end;
function StrFind(const Substr, S: string; const Index: Integer = 1): Integer;
// Case-insensitive version of StrSearch.
begin
if Index=1 then
exit(PosI(Substr,S));
Result := PosI(SubStr, Copy(S, Index, Length(S)));
if Result > 0 then
Result := Result + Index - 1;
@ -730,4 +735,25 @@ begin
result:=aStr;
end;
function StrStartsWithLineEnd(const aStr:string):boolean;
var
i,len:integer;
begin
len := length(aStr);
i:=1;
while (i<=len) and (aStr[i] in NativeTabSpace) do //skip spaces
inc(i);
Result := (i<=len) and CharIsReturn(aStr[i]);
end;
function StrEndsWithLineEnd(const aStr:string):boolean;
var
i:integer;
begin
i := length(aStr);
while (i>0) and (aStr[i] in NativeTabSpace) do //skip spaces
dec(i);
Result := (i>0) and CharIsReturn(aStr[i]);
end;
end.