codetools: complete block: insert needed spaces in front of first line

git-svn-id: trunk@20291 -
This commit is contained in:
mattias 2009-05-29 07:59:13 +00:00
parent 6509c0e7d0
commit 65968ad5bf
4 changed files with 115 additions and 173 deletions

View File

@ -117,25 +117,27 @@ const
bbtAllStatements = [bbtMainBegin,bbtCommentaryBegin,bbtRepeat,bbtProcedureBegin,
bbtCaseColon,bbtCaseBegin,bbtCaseElse,
bbtTry,bbtFinally,bbtExcept,
bbtIfThen,bbtIfElse];
bbtIfThen,bbtIfElse,bbtIfBegin];
type
TOnGetFABExamples = procedure(Sender: TObject; Code: TCodeBuffer;
out CodeBuffers: TFPList) of object;
TFABIndentation = record
Indent: integer;
UseTabs: boolean;
InsertEmptyLines: integer;
TFABIndentationPolicy = record
IndentBefore: integer;
IndentBeforeValid: boolean;
IndentAfter: integer;
IndentAfterValid: boolean;
end;
{ TFABPolicies }
TFABPolicies = class
public
Indentations: array[TFABBlockType] of TFABIndentation;
Indentations: array[TFABBlockType] of TFABIndentationPolicy;
IndentationsFound: array[TFABBlockType] of boolean;
constructor Create;
destructor Destroy; override;
procedure Clear;
end;
{ TFullyAutomaticBeautifier }
@ -143,25 +145,16 @@ type
TFullyAutomaticBeautifier = class
private
FOnGetExamples: TOnGetFABExamples;
FAtomStarts: PInteger;
FAtomCapacity: integer;
FAtomCount: integer;
procedure ParseSource(const Source: string; NewSrcLen: integer;
NewNestedComments: boolean);
function IndexOfAtomInFront(CleanPos: integer): integer;
function FindContext(CleanPos: integer; out AtomInFront: integer
): TFABBlockType;
procedure ParseSource(const Src: string; SrcLen: integer;
NestedComments: boolean; Policies: TFABPolicies);
procedure FindPolicies(Types: TFABBlockTypes; Policies: TFABPolicies);
public
Src: string;
SrcLen: integer;
NestedComments: boolean;
constructor Create;
destructor Destroy; override;
procedure Clear;
function GetIndent(const Source: string; CleanPos: integer;
NewNestedComments: boolean;
out Indent: TFABIndentation): boolean;
out Indent: TFABIndentationPolicy): boolean;
{ ToDo:
- indent on paste (position + new source)
- indent auto generated code (several snippets)
@ -217,6 +210,7 @@ type
TBlock = record
Typ: TFABBlockType;
StartPos: integer;
InnerIdent: integer;
end;
PBlock = ^TBlock;
@ -273,6 +267,7 @@ begin
Block:=@Stack[Top];
Block^.Typ:=Typ;
Block^.StartPos:=StartPos;
Block^.InnerIdent:=-1;
TopType:=Typ;
end;
@ -320,8 +315,8 @@ end;
{ TFullyAutomaticBeautifier }
procedure TFullyAutomaticBeautifier.ParseSource(const Source: string;
NewSrcLen: integer; NewNestedComments: boolean);
procedure TFullyAutomaticBeautifier.ParseSource(const Src: string;
SrcLen: integer; NestedComments: boolean; Policies: TFABPolicies);
var
Stack: TBlockStack;
p: Integer;
@ -333,7 +328,7 @@ var
X: integer;
Y: LongInt;
begin
Y:=LineEndCount(Source,1,p,X)+1;
Y:=LineEndCount(Src,1,p,X)+1;
Result:='Line='+dbgs(Y)+' Col='+dbgs(X);
end;
{$ENDIF}
@ -363,8 +358,14 @@ var
while Stack.Top>=i do EndBlock;
end;
procedure EndStatements;
begin
while Stack.TopType in bbtAllStatements do EndBlock;
end;
procedure StartIdentifierSection(Section: TFABBlockType);
begin
EndStatements; // fix dangling statements
if Stack.TopType in [bbtProcedure,bbtFunction] then begin
if (Stack.Top=0) or (Stack.Stack[Stack.Top-1].Typ in [bbtImplementation])
then begin
@ -382,6 +383,7 @@ var
procedure StartProcedure(Typ: TFABBlockType);
begin
EndStatements; // fix dangling statements
if Stack.TopType in [bbtProcedure,bbtFunction] then begin
if (Stack.Top=0) or (Stack.Stack[Stack.Top-1].Typ in [bbtImplementation])
then begin
@ -398,20 +400,10 @@ var
end;
var
MinAtomCapacity: Integer;
r: PChar;
Block: PBlock;
Indent: Integer;
begin
Src:=Source;
SrcLen:=NewSrcLen;
NestedComments:=NewNestedComments;
FAtomCount:=0;
MinAtomCapacity:=SrcLen div 4;
if MinAtomCapacity<1024 then
MinAtomCapacity:=1024;
if FAtomCapacity<MinAtomCapacity then begin
FAtomCapacity:=MinAtomCapacity;
ReAllocMem(FAtomStarts,FAtomCapacity*SizeOf(integer));
end;
Stack:=TBlockStack.Create;
try
p:=1;
@ -419,12 +411,25 @@ begin
ReadRawNextPascalAtom(Src,p,AtomStart,NestedComments);
DebugLn(['TFullyAutomaticBeautifier.ParseSource ',copy(Src,AtomStart,p-AtomStart)]);
if p>SrcLen then break;
FAtomStarts[FAtomCount]:=AtomStart;
inc(FAtomCount);
if FAtomCount>FAtomCapacity then begin
FAtomCapacity:=FAtomCapacity*2;
ReAllocMem(FAtomStarts,FAtomCapacity*SizeOf(integer));
if (Stack.Top>=0) then begin
Block:=@Stack.Stack[Stack.Top];
if (Policies<>nil)
and (not Policies.Indentations[Block^.Typ].IndentAfterValid) then begin
// set block InnerIdent
if (Block^.InnerIdent<0)
and (not PositionsInSameLine(Src,Block^.StartPos,AtomStart)) then begin
Block^.InnerIdent:=GetLineIndent(Src,AtomStart);
if Block^.Typ in [bbtIfThen,bbtIfElse] then
Indent:=Block^.InnerIdent-GetLineIndent(Src,Stack.Stack[Stack.Top-1].StartPos)
else
Indent:=Block^.InnerIdent-GetLineIndent(Src,Block^.StartPos);
Policies.Indentations[Block^.Typ].IndentAfter:=Indent;
Policies.Indentations[Block^.Typ].IndentAfterValid:=true;
end;
end;
end;
r:=@Src[AtomStart];
case UpChars[r^] of
'B':
@ -479,6 +484,9 @@ begin
end;
'N': // EN
if CompareIdentifiers('END',r)=0 then begin
// if statements can be closed by end without semicolon
while Stack.TopType in [bbtIf,bbtIfThen,bbtIfElse] do EndBlock;
case Stack.TopType of
bbtMainBegin,bbtCommentaryBegin,
bbtRecord,bbtClass,bbtClassInterface,bbtTry,bbtFinally,bbtExcept,
@ -496,6 +504,8 @@ begin
if Stack.TopType in [bbtProcedure,bbtFunction] then
EndBlock;
end;
bbtInterface,bbtImplementation,bbtInitialization,bbtFinalization:
EndBlock;
end;
end;
'X': // EX
@ -510,7 +520,7 @@ begin
case UpChars[r[1]] of
'I': // FI
if CompareIdentifiers('FINALIZATION',r)=0 then begin
while Stack.TopType in (bbtAllCodeSections+bbtAllIdentifierSections)
while Stack.TopType in (bbtAllCodeSections+bbtAllIdentifierSections+bbtAllStatements)
do
EndBlock;
if Stack.TopType=bbtNone then
@ -543,7 +553,7 @@ begin
case UpChars[r[2]] of
'I': // INI
if CompareIdentifiers('INITIALIZATION',r)=0 then begin
while Stack.TopType in (bbtAllCodeSections+bbtAllIdentifierSections)
while Stack.TopType in (bbtAllCodeSections+bbtAllIdentifierSections+bbtAllStatements)
do
EndBlock;
if Stack.TopType=bbtNone then
@ -561,7 +571,7 @@ begin
end;
'M': // IM
if CompareIdentifiers('IMPLEMENTATION',r)=0 then begin
while Stack.TopType in (bbtAllCodeSections+bbtAllIdentifierSections)
while Stack.TopType in (bbtAllCodeSections+bbtAllIdentifierSections+bbtAllStatements)
do
EndBlock;
if Stack.TopType=bbtNone then
@ -641,6 +651,9 @@ begin
EndBlock;
BeginBlock(bbtCaseOf);
end;
bbtIfThen,bbtIfElse:
while Stack.TopType in [bbtIf,bbtIfThen,bbtIfElse] do
EndBlock;
end;
':':
if p-AtomStart=1 then begin
@ -668,120 +681,6 @@ begin
end;
end;
function TFullyAutomaticBeautifier.IndexOfAtomInFront(CleanPos: integer
): integer;
// returns index in FAtomStarts of atom in front
// if CleanPos is start of an atom the atom in front is returned
// default: -1
var
l: Integer;
r: LongInt;
m: Integer;
p: LongInt;
begin
l:=0;
r:=FAtomCount-1;
while l<=r do begin
m:=(l+r) shr 1;
p:=FAtomStarts[m];
if p>CleanPos then
r:=m-1
else if p<CleanPos then begin
if l=r then exit(m);
l:=m+1;
end else
exit(m-1);
end;
Result:=-1;
end;
function TFullyAutomaticBeautifier.FindContext(CleanPos: integer; out
AtomInFront: integer): TFABBlockType;
{ Examples:
repeat
|
procedure DoSomething;
...
begin
|
if expr then
begin
|
procedure DoSomething(...;...;
|
if expr then
begin
end
else
begin
end
|
TMyClass = class
end;
|
case expr of
|
case expr of
1:
|
case expr of
else
|
}
var
i: LongInt;
p: PChar;
Stack: TBlockStack;
begin
Result:=bbtNone;
AtomInFront:=IndexOfAtomInFront(CleanPos);
if AtomInFront<0 then exit;
Stack:=TBlockStack.Create;
try
i:=0;
p:=@Src[FAtomStarts[i]];
//DebugLn(['TFullyAutomaticBeautifier.FindContext Atom=',GetAtomString(p,NestedComments)]);
case UpChars[p^] of
'B':
if CompareIdentifiers('BEGIN',p)=0 then
// procedure-begin
// then-begin
// do-begin
// semicolon-begin
;
'E':
if CompareIdentifiers('ELSE',p)=0 then
// case-else
// if-else
;
'O':
if CompareIdentifiers('OF',p)=0 then
// case-of, array-of, class-of
;
'R':
if CompareIdentifiers('REPEAT',p)=0 then
Result:=bbtRepeat;
':':
// case-colon
;
';':
// statement or parameter
;
end;
finally
Stack.Free;
end;
end;
procedure TFullyAutomaticBeautifier.FindPolicies(Types: TFABBlockTypes;
Policies: TFABPolicies);
begin
@ -796,38 +695,33 @@ end;
destructor TFullyAutomaticBeautifier.Destroy;
begin
Clear;
ReAllocMem(FAtomStarts,0);
FAtomCapacity:=0;
inherited Destroy;
end;
procedure TFullyAutomaticBeautifier.Clear;
begin
FAtomCount:=0;
end;
function TFullyAutomaticBeautifier.GetIndent(const Source: string;
CleanPos: integer; NewNestedComments: boolean;
out Indent: TFABIndentation): boolean;
out Indent: TFABIndentationPolicy): boolean;
var
AtomInFront: LongInt;
BlockType: TFABBlockType;
Policies: TFABPolicies;
begin
Result:=false;
FillByte(Indent,SizeOf(Indent),0);
// parse source
ParseSource(Source,length(Source),NewNestedComments);
DebugLn(['TFullyAutomaticBeautifier.GetIndent FAtomCount=',FAtomCount]);
Policies:=TFABPolicies.Create;
try
// parse source
ParseSource(Source,length(Source),NewNestedComments,Policies);
BlockType:=FindContext(CleanPos,AtomInFront);
if AtomInFront<0 then begin
// in comments/space in front of any code
exit(false);
finally
Policies.Free;
end;
DebugLn(['TFullyAutomaticBeautifier.GetIndent BlockType=',FABBlockTypeNames[BlockType]]);
//Policies:=
//SrcPolicies:=
end;
{ TFABPolicies }
@ -842,5 +736,13 @@ begin
inherited Destroy;
end;
procedure TFABPolicies.Clear;
var
i: TFABBlockType;
begin
for i:=low(Indentations) to High(Indentations) do
IndentationsFound[i]:=false;
end;
end.

View File

@ -30,12 +30,35 @@
<PackageName Value="CodeTools"/>
</Item1>
</RequiredPackages>
<Units Count="1">
<Units Count="6">
<Unit0>
<Filename Value="completeblock.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="CompleteBlock"/>
</Unit0>
<Unit1>
<Filename Value="scanexamples/brokenfilenames.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="BrokenFilenames"/>
</Unit1>
<Unit2>
<Filename Value="scanexamples/brokenincfiles.inc"/>
<IsPartOfProject Value="True"/>
</Unit2>
<Unit3>
<Filename Value="scanexamples/empty.inc"/>
<IsPartOfProject Value="True"/>
</Unit3>
<Unit4>
<Filename Value="scanexamples/indentation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="Indentation"/>
</Unit4>
<Unit5>
<Filename Value="completeblocktests/unit1.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="unit1"/>
</Unit5>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -17,5 +17,17 @@ implementation
{$I whilebegin1.inc}
{$ENDIF}
{$IFDEF caseend}
{$I caseend1.inc}
{$ENDIF}
{$IFDEF caseelseend}
{$I caseelseend1.inc}
{$ENDIF}
{$IFDEF casecolon}
{$I casecolon1.inc}
{$ENDIF}
end.

View File

@ -5256,6 +5256,12 @@ var
AfterGap:=gtNone;
inc(ToPos);
end;
// adjust indent of first line
if FrontGap in [gtNone,gtSpace] then begin
BeautifyFlags:=BeautifyFlags+[bcfDoNotIndentFirstLine];
NewCode:=GetIndentStr(Indent-GetPosInLine(Src,FromPos))+NewCode;
end;
debugln(['Replace Indent=',Indent,' NewCode=',NewCode]);
// beautify
NewCode:=SourceChangeCache.BeautifyCodeOptions.BeautifyStatement(
NewCode,Indent,BeautifyFlags);
@ -5542,7 +5548,6 @@ var
NewCode:=';';
FrontGap:=gtNone;
AfterGap:=gtNone;
Include(BeautifyFlags,bcfDoNotIndentFirstLine);
end;
else
exit;