codetools: RemoveUnitFromUsesSection: when removing the last unit remove empty lines in front too, bug #18055

git-svn-id: trunk@28498 -
This commit is contained in:
mattias 2010-11-26 12:23:25 +00:00
parent 23252ff1fe
commit 94948dde5e
6 changed files with 172 additions and 34 deletions

View File

@ -109,7 +109,8 @@ function IsFirstNonSpaceCharInLine(const Source: string;
Position: integer): boolean; Position: integer): boolean;
function FindLineEndOrCodeInFrontOfPosition(const Source: string; function FindLineEndOrCodeInFrontOfPosition(const Source: string;
Position, MinPosition: integer; NestedComments: boolean; Position, MinPosition: integer; NestedComments: boolean;
StopAtDirectives: boolean = true; SkipSemicolonComma: boolean = true): integer; StopAtDirectives: boolean = true; SkipSemicolonComma: boolean = true;
SkipEmptyLines: boolean = false): integer;
function FindLineEndOrCodeAfterPosition(const Source: string; function FindLineEndOrCodeAfterPosition(const Source: string;
Position, MaxPosition: integer; NestedComments: boolean; Position, MaxPosition: integer; NestedComments: boolean;
StopAtDirectives: boolean = true; SkipEmptyLines: boolean = false; StopAtDirectives: boolean = true; SkipEmptyLines: boolean = false;
@ -2464,10 +2465,13 @@ end;
function FindLineEndOrCodeInFrontOfPosition(const Source: string; function FindLineEndOrCodeInFrontOfPosition(const Source: string;
Position, MinPosition: integer; NestedComments: boolean; Position, MinPosition: integer; NestedComments: boolean;
StopAtDirectives: boolean; SkipSemicolonComma: boolean): integer; StopAtDirectives: boolean; SkipSemicolonComma: boolean;
SkipEmptyLines: boolean): integer;
{ search backward for a line end or code { search backward for a line end or code
ignore line ends in comments or at the end of comment lines ignore line ends in comments or at the end of comment lines
(comment lines are lines without code and at least one comment) (comment lines are lines without code and at least one comment)
comment lines directly in front are skipped too
if SkipEmptyLines=true then empty lines are skipped too.
Result is Position of Start of Line End Result is Position of Start of Line End
examples: Position points at char 'a' examples: Position points at char 'a'
@ -2585,6 +2589,28 @@ begin
if IsEmpty then begin if IsEmpty then begin
// the line is empty => return start of line end // the line is empty => return start of line end
Result:=LineEndPos; Result:=LineEndPos;
if SkipEmptyLines then begin
// skip all empty lines
LineStartPos:=Result;
while (LineStartPos>SrcStart) do begin
case Source[LineStartPos-1] of
#10,#13:
begin
// empty line
LineEndPos:=LineStartPos-1;
if (LineEndPos>SrcStart) and (Source[LineEndPos-1] in [#10,#13])
and (Source[LineEndPos]<>Source[LineEndPos-1]) then
dec(LineEndPos);
Result:=LineEndPos;
end;
' ',#9: ;
else
// not empty
break;
end;
dec(LineStartPos);
end;
end;
exit; exit;
end; end;
TestPos:=LineStartPos; TestPos:=LineStartPos;

View File

@ -231,7 +231,7 @@ type
function FindLineEndOrCodeAfterPosition(StartPos: integer; function FindLineEndOrCodeAfterPosition(StartPos: integer;
SkipEmptyLines: boolean = false; IncludeLineEnd: boolean = false): integer; SkipEmptyLines: boolean = false; IncludeLineEnd: boolean = false): integer;
function FindLineEndOrCodeInFrontOfPosition(StartPos: integer; function FindLineEndOrCodeInFrontOfPosition(StartPos: integer;
StopAtDirectives: boolean = true): integer; StopAtDirectives: boolean = true; SkipEmptyLines: boolean = false): integer;
function UpdateNeeded(OnlyInterfaceNeeded: boolean): boolean; function UpdateNeeded(OnlyInterfaceNeeded: boolean): boolean;
procedure BeginParsing(DeleteNodes, OnlyInterfaceNeeded: boolean); virtual; procedure BeginParsing(DeleteNodes, OnlyInterfaceNeeded: boolean); virtual;
@ -2601,7 +2601,7 @@ begin
end; end;
function TCustomCodeTool.FindLineEndOrCodeInFrontOfPosition(StartPos: integer; function TCustomCodeTool.FindLineEndOrCodeInFrontOfPosition(StartPos: integer;
StopAtDirectives: boolean): integer; StopAtDirectives: boolean; SkipEmptyLines: boolean): integer;
{ Searches a nice position in the cleaned source in front of StartPos. { Searches a nice position in the cleaned source in front of StartPos.
It will skip any space or comments (not directives) till next It will skip any space or comments (not directives) till next
line end or compiler directive or code or include file end. line end or compiler directive or code or include file end.
@ -2612,7 +2612,8 @@ begin
LinkIndex:=Scanner.LinkIndexAtCleanPos(StartPos); LinkIndex:=Scanner.LinkIndexAtCleanPos(StartPos);
LinkStart:=Scanner.Links[LinkIndex].CleanedPos; LinkStart:=Scanner.Links[LinkIndex].CleanedPos;
Result:=BasicCodeTools.FindLineEndOrCodeInFrontOfPosition(Src, Result:=BasicCodeTools.FindLineEndOrCodeInFrontOfPosition(Src,
StartPos,LinkStart,Scanner.NestedComments,StopAtDirectives,false); StartPos,LinkStart,Scanner.NestedComments,StopAtDirectives,false,
SkipEmptyLines);
end; end;
procedure TCustomCodeTool.ClearIgnoreErrorAfter; procedure TCustomCodeTool.ClearIgnoreErrorAfter;

View File

@ -1,19 +1,18 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<CONFIG> <CONFIG>
<ProjectOptions> <ProjectOptions>
<Version Value="7"/> <Version Value="9"/>
<General> <General>
<Flags> <Flags>
<LRSInOutputDirectory Value="False"/> <LRSInOutputDirectory Value="False"/>
</Flags> </Flags>
<SessionStorage Value="InProjectDir"/> <SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/> <MainUnit Value="0"/>
<TargetFileExt Value=""/>
<Title Value="finddeclaration"/> <Title Value="finddeclaration"/>
</General> </General>
<VersionInfo> <BuildModes Count="1">
<StringTable Comments="" CompanyName="" FileDescription="" FileVersion="0.0.0.0" InternalName="" LegalCopyright="" LegalTrademarks="" OriginalFilename="" ProductName="" ProductVersion="0.0.0.0"/> <Item1 Name="default" Default="True"/>
</VersionInfo> </BuildModes>
<PublishOptions> <PublishOptions>
<Version Value="2"/> <Version Value="2"/>
<IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/> <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
@ -50,7 +49,7 @@
<CompilerOptions> <CompilerOptions>
<Version Value="9"/> <Version Value="9"/>
<SearchPaths> <SearchPaths>
<OtherUnitFiles Value="scanexamples/"/> <OtherUnitFiles Value="scanexamples"/>
</SearchPaths> </SearchPaths>
<Parsing> <Parsing>
<SyntaxOptions> <SyntaxOptions>

View File

@ -1086,8 +1086,10 @@ begin
// first unit in uses section // first unit in uses section
if AtomIsChar(';') then begin if AtomIsChar(';') then begin
// last unit in uses section -> delete whole uses section // last unit in uses section -> delete whole uses section
StartPos:=FindLineEndOrCodeInFrontOfPosition(UsesNode.StartPos,true,true);
debugln(['TStandardCodeTool.RemoveUnitFromUsesSection AAA1 "',dbgstr(copy(Src,StartPos,UsesNode.EndPos-StartPos)),'"']);
if not SourceChangeCache.Replace(gtNone,gtNone, if not SourceChangeCache.Replace(gtNone,gtNone,
UsesNode.StartPos,UsesNode.EndPos,'') then exit; StartPos,UsesNode.EndPos,'') then exit;
end else begin end else begin
// not last unit -> delete with comma behind // not last unit -> delete with comma behind
EndPos:=FindLineEndOrCodeAfterPosition(CurPos.EndPos); EndPos:=FindLineEndOrCodeAfterPosition(CurPos.EndPos);

View File

@ -27,10 +27,14 @@
<LaunchingApplication PathPlusParams="/usr/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/> <LaunchingApplication PathPlusParams="/usr/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
</local> </local>
</RunParams> </RunParams>
<RequiredPackages Count="1"> <RequiredPackages Count="2">
<Item1> <Item1>
<PackageName Value="LCL"/> <PackageName Value="CodeTools"/>
<MinVersion Major="1" Release="1" Valid="True"/>
</Item1> </Item1>
<Item2>
<PackageName Value="LCL"/>
</Item2>
</RequiredPackages> </RequiredPackages>
<Units Count="2"> <Units Count="2">
<Unit0> <Unit0>

View File

@ -6,7 +6,7 @@ interface
uses uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, LCLProc, Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, LCLProc,
contnrs; contnrs, XMLRead;
type type
@ -41,16 +41,12 @@ type
procedure FixFPDocFragment(var Fragment: string; Fix: boolean; procedure FixFPDocFragment(var Fragment: string; Fix: boolean;
out ErrorList: TObjectList); out ErrorList: TObjectList);
{ - Fix all tags to lowercase to reduce svn commits { - Fix all tags to lowercase to reduce svn commits
- remove unneeded spaces
< b => <b
b > => b>
a b => a b
- auto close comments - auto close comments
- remove #0 from comments - remove #0 from comments
- auto close unclosed tags
- fix & without ;
- convert special characters to &x; - convert special characters to &x;
- fix &name; lower case
- fix unclosed attribute values - fix unclosed attribute values
- auto close unclosed tags
} }
type type
TStackItemTyp = ( TStackItemTyp = (
@ -292,6 +288,125 @@ var
HandleSpecialChar; HandleSpecialChar;
end; end;
procedure LowerCaseName;
var
Start: PChar;
NeedLowercase: PChar;
begin
Start:=p;
NeedLowercase:=nil;
repeat
case p^ of
'a'..'z': inc(p);
'A'..'Z':
begin
inc(p);
if NeedLowercase=nil then
NeedLowercase:=p;
end;
else break;
end;
until false;
if NeedLowercase<>nil then begin
if Fix then begin
Replace(Rel(Start),p-Start,lowercase(copy(Fragment,Rel(Start),p-Start)));
end else begin
// all current tags must be lower case
Error(NeedLowercase,'tags must be lower case');
end;
end;
end;
procedure ParseAttribute;
begin
// attribute name
LowerCaseName;
while p^ in [' ',#9,#10,#13] do inc(p); // skip space
if p^<>'=' then begin
// missing value
if Fix then begin
Replace(Rel(p),0,'=');
end else begin
Error(p,'expected =');
end;
end;
if p^='=' then begin
inc(p);
while p^ in [' ',#9,#10,#13] do inc(p); // skip space
if p^<>'"' then begin
// missing quotes
if Fix then begin
Replace(Rel(p),0,'"');
end else begin
Error(p,'expected "');
end;
end;
end;
if p^='"' then begin
// read value
inc(p);
repeat
case p^ of
'>',#0..#8,#11,#12,#14..#31,#127:
// the > is not allowed in the quotes, probably the ending quot is missing
if Fix then begin
Replace(Rel(p),0,'"');
end else begin
Error(p,'expected ending "');
break;
end;
'&':
ParseAmpersand;
'"':
begin
inc(p);
break;
end
else
inc(p);
end;
until false;
end;
end;
procedure ParseLowerThan;
begin
// comment, tag or 'lower than'
if (p[1]='!') and (p[2]='-') and (p[3]='-') then begin
// comment
ParseComment;
exit;
end;
if p[1] in ['a'..'z','A'..'Z'] then begin
// open tag
Push(sitTag);
TopItem^.StartPos:=Rel(p);
inc(p);
TopItem^.NameStartPos:=Rel(p);
LowerCaseName;
TopItem^.NameEndPos:=Rel(p);
while p^ in [' ',#9,#10,#13] do inc(p); // skip space
case p^ of
'a'..'z','A'..'Z':
ParseAttribute;
'/':
begin
// ToDo: close tag
RaiseGDBException('ToDo');
end;
'>':
begin
// ToDo:
end;
end;
end else if p[1]='/' then begin
// ToDo: close tag
RaiseGDBException('ToDo');
end;
// invalid character => convert or skip
HandleSpecialChar;
end;
begin begin
ErrorList:=nil; ErrorList:=nil;
if Fragment='' then exit; if Fragment='' then exit;
@ -314,16 +429,7 @@ begin
end; end;
end; end;
'<': '<':
begin ParseLowerThan;
// comment, tag or 'lower than'
if (p[1]='!') and (p[2]='-') and (p[3]='-') then
// comment
ParseComment
else begin
// invalid character => convert or skip
HandleSpecialChar;
end;
end;
'>': '>':
// invalid character => convert or skip // invalid character => convert or skip
HandleSpecialChar; HandleSpecialChar;
@ -346,8 +452,8 @@ end;
procedure TForm1.FormCreate(Sender: TObject); procedure TForm1.FormCreate(Sender: TObject);
begin begin
TestComment; //TestComment;
TestInvalidCharacters; //TestInvalidCharacters;
end; end;
procedure TForm1.TestComment; procedure TForm1.TestComment;