mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-12 13:23:08 +02:00
351 lines
9.1 KiB
ObjectPascal
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.
|