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

View File

@ -30,12 +30,35 @@
<PackageName Value="CodeTools"/> <PackageName Value="CodeTools"/>
</Item1> </Item1>
</RequiredPackages> </RequiredPackages>
<Units Count="1"> <Units Count="6">
<Unit0> <Unit0>
<Filename Value="completeblock.pas"/> <Filename Value="completeblock.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="CompleteBlock"/> <UnitName Value="CompleteBlock"/>
</Unit0> </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> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>

View File

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

View File

@ -5256,6 +5256,12 @@ var
AfterGap:=gtNone; AfterGap:=gtNone;
inc(ToPos); inc(ToPos);
end; 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 // beautify
NewCode:=SourceChangeCache.BeautifyCodeOptions.BeautifyStatement( NewCode:=SourceChangeCache.BeautifyCodeOptions.BeautifyStatement(
NewCode,Indent,BeautifyFlags); NewCode,Indent,BeautifyFlags);
@ -5542,7 +5548,6 @@ var
NewCode:=';'; NewCode:=';';
FrontGap:=gtNone; FrontGap:=gtNone;
AfterGap:=gtNone; AfterGap:=gtNone;
Include(BeautifyFlags,bcfDoNotIndentFirstLine);
end; end;
else else
exit; exit;