lazarus/components/jcf2/Process/Transform/SortUses.pas
paul 2c018868d2 jcf: reduce amount of hints
git-svn-id: trunk@17917 -
2008-12-24 05:37:30 +00:00

351 lines
9.1 KiB
ObjectPascal

unit SortUses;
{(*}
(*------------------------------------------------------------------------------
Delphi Code formatter source code
The Original Code is SortUses.pas, released July 2004.
The Initial Developer of the Original Code is Anthony Steele.
Portions created by Anthony Steele are Copyright (C) 1999-2008 Anthony Steele.
All Rights Reserved.
Contributor(s): Anthony Steele.
The contents of this file are subject to the Mozilla Public License Version 1.1
(the "License"). you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.mozilla.org/NPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied.
See the License for the specific language governing rights and limitations
under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 2 or later (the "GPL")
See http://www.gnu.org/licenses/gpl.html
------------------------------------------------------------------------------*)
{*)}
{$I JcfGlobal.inc}
interface
{ unit to sort the uses clause }
uses BaseVisitor, ParseTreeNode;
type
TSortUses = class(TBaseTreeNodeVisitor)
private
procedure SortUsesInSections(const pcUsesIdentList: TParseTreeNode);
protected
public
constructor Create; override;
procedure PostVisitParseTreeNode(const pcNode: TObject); override;
function IsIncludedInSettings: boolean; override;
end;
implementation
uses
{ delphi }
Contnrs, Classes,
{ local }
ParseTreeNodeType, SourceToken, Tokens, JcfSettings,
SetTransform, TokenUtils, SortUsesData;
function GetSortableToken(pcNode: TParseTreeNode): TSourceToken;
var
lcTest: TParseTreeNode;
begin
Result := nil;
if pcNode is TSourceToken then
begin
Result := TSourceToken(pcNode);
end
else
begin
lcTest := pcNode.GetImmediateChild(nUsesItem);
if lcTest <> nil then
pcNode := lcTest;
lcTest := pcNode.GetImmediateChild(nIdentifier);
if lcTest <> nil then
pcNode := lcTest;
lcTest := pcNode.FirstSolidLeaf;
if lcTest <> nil then
Result := TSourceToken(lcTest);
end;
end;
constructor TSortUses.Create;
begin
inherited;
HasPostVisit := True;
HasSourceTokenVisit := False;
end;
function TSortUses.IsIncludedInSettings: boolean;
begin
with FormatSettings.Transform do
Result := SortInterfaceUses or SortImplementationUses;
end;
procedure TSortUses.PostVisitParseTreeNode(const pcNode: TObject);
var
lcNode: TParseTreeNode;
lcIdentList: TParseTreeNode;
begin
lcNode := TParseTreeNode(pcNode);
{ is this a uses clause? }
if lcNode.NodeType <> nUses then
exit;
if FormatSettings.Transform.SortUsesNoComments and lcNode.HasChildNode([ttComment]) then
exit;
{ is it turned on in this section? }
if lcNode.HasParentNode(nInterfaceSection) then
begin
{ interface section }
if not FormatSettings.Transform.SortInterfaceUses then
exit;
end
else if lcNode.HasParentNode(nImplementationSection) then
begin
{ implentation section }
if not FormatSettings.Transform.SortImplementationUses then
exit;
end
else
begin
{ other - program, package or library section }
if not FormatSettings.Transform.SortProgramUses then
exit;
end;
lcIdentList := lcNode.GetImmediateChild(nIdentList);
if lcIdentList <> nil then
begin
SortUsesInSections(lcIdentList);
end;
end;
procedure CleanEnd(const pcRootNode: TParseTreeNode);
var
lcLastLeaf, lcRemove: TSourceToken;
begin
lcLastLeaf := TSourceToken(pcRootNode.LastLeaf);
while (lcLastLeaf <> nil) and (lcLastLeaf.TokenType in [ttComma, ttWhiteSpace, ttReturn]) do
begin
lcRemove := lcLastLeaf;
lcLastLeaf := lcLastLeaf.PriorToken;
{ abort after a // comment }
if (lcRemove.TokenType = ttReturn) and
(lcLastLeaf.TokenType = ttComment) and (lcLastLeaf.CommentStyle = eDoubleSlash) then
break;
lcRemove.Parent.RemoveChild(lcRemove);
end;
end;
procedure RemoveAllChildrenOnwards(const pcParent, pcChild: TParseTreeNode);
var
lcChild, lcLast: TParseTreeNode;
begin
if pcChild = nil then
lcChild := pcParent.FirstLeaf
else
lcChild := pcChild;
while (lcChild <> nil) and lcChild.HasParentNode(pcParent) do
begin
lcLast := lcChild;
lcChild := lcChild.NextLeafNode;
lcLast.Parent.ExtractChild(lcLast);
end;
end;
function EndsWithComma(const pcUsesIdentList: TParseTreeNode): boolean;
var
lcToken: TSourceToken;
begin
lcToken := pcUsesIdentList.LastLeaf as TSourceToken;
if (lcToken <> nil) and (not lcToken.IsSolid) then
lcToken := lcToken.PriorSolidToken;
Result := (lcToken <> nil) and (lcToken.TokenType = ttComma);
end;
procedure TSortUses.SortUsesInSections(const pcUsesIdentList: TParseTreeNode);
var
lcToken: TSourceToken;
lcSections: TObjectList;
lcCurrentSection: TUsesSection;
leBreakTokens: TTokenTypeSet;
procedure CollectWhiteSpace;
begin
{ start with a breaking section for the starting white space and comments where they are }
while (lcToken <> nil) and (not lcToken.IsSolid) do
begin
lcCurrentSection.AddToken(lcToken);
lcToken := lcToken.NextToken;
end;
end;
procedure NewSection(const pbSorted: Boolean);
begin
lcCurrentSection := TUsesSection.Create;
lcCurrentSection.Sorted := pbSorted;
lcSections.Add(lcCurrentSection);
end;
function IsBreakToken(const pcToken: TSourceToken): Boolean;
begin
Result := (pcToken.TokenType in leBreakTokens) or
((pcToken.TokenType = ttComment) and (pcToken.CommentStyle = eCompilerDirective));
end;
var
lbStartFirstSection: boolean;
lbStartUsesItem: boolean;
liSectionLoop: integer;
lbEndsWithComma: boolean;
begin
leBreakTokens := [];
if FormatSettings.Transform.BreakUsesSortOnReturn then
Include(leBreakTokens, ttReturn);
if FormatSettings.Transform.BreakUsesSortOnComment then
Include(leBreakTokens, ttComment);
{ sections is a list of lists
each section is a list of parse tree nodes
Each section is sorted }
lcSections := TObjectList.Create;
lcSections.OwnsObjects := True;
lbStartFirstSection := True;
lbStartUsesItem := True;
lbEndsWithComma := False;
try
{ at least one section, but can be more }
NewSection(True);
{ each child node is in a section }
lcToken := TSourceToken(pcUsesIdentList.FirstLeaf);
if (lcToken <> nil) and (not lcToken.IsSolid) then
begin
lcCurrentSection.Sorted := False;
CollectWhiteSpace;
NewSection(True);
end;
while (lcToken <> nil) and lcToken.HasParentNode(nIdentList) and lcToken.HasParentNode(nUses) do
begin
{ end of section, start of the next }
if IsBreakToken(lcToken) and (not lbStartUsesItem) then
begin
{ the break token is in a non-sorted section }
NewSection(False);
lcCurrentSection.AddToken(lcToken);
lcToken := lcToken.NextToken;
CollectWhiteSpace;
NewSection(True);
continue;
end;
if IsIdentifier(lcToken, idAny) and (lcToken.HasParentNode(nUsesItem, 2)) then
begin
if not lbStartUsesItem then
begin
lcCurrentSection.AddUsesItem;
lbStartUsesItem := True;
end;
end
else
begin
{ a uses item contains exactly one identifier.
but in Delphi.Net it can be dotted, e.g.
"System.Runtime.Remoting"
}
lbStartUsesItem := False;
end;
lcCurrentSection.AddToken(lcToken);
{ don't end the first section until it's begun }
if lbStartFirstSection and (not (lcToken.TokenType in NotSolidTokens)) then
lbStartFirstSection := False;
lcToken := lcToken.NextToken;
if lcToken.TokenType = ttDot then
begin
// delphi.net dotted unit name
while (lcToken.TokenType = ttDot) or IsIdentifier(lcToken, idAny) do
begin
lcCurrentSection.AddToken(lcToken);
lcToken := lcToken.NextToken;
end;
end;
end;
{ remove the unsorted tokens }
RemoveAllChildrenOnwards(pcUsesIdentList, nil);
{ sort each section, and reattach it }
for liSectionLoop := 0 to lcSections.Count - 1 do
begin
lcCurrentSection := TUsesSection(lcSections.Items[liSectionLoop]);
if lcCurrentSection.Sorted then
begin
{ sort this section }
case FormatSettings.Transform.UsesSortOrder of
eAlpha:
lcCurrentSection.Sort(AlphaNameSort);
eReverseAlpha:
lcCurrentSection.Sort(ReverseAlphaNameSort);
eShortToLong:
lcCurrentSection.Sort(LengthNameSort);
eLongToShort:
lcCurrentSection.Sort(ReverseLengthNameSort);
else
Assert(False);
end;
end;
{ repopulate the original parent with sorted items
if the last one ends with a comma, this one must not start with a comma }
lcCurrentSection.AttachTo(pcUsesIdentList, not lbEndsWithComma);
lbEndsWithComma := EndsWithComma(pcUsesIdentList);
end;
finally
lcSections.Free;
end;
{ no comma or space before the semicolon }
CleanEnd(pcUsesIdentList);
end;
end.