codetools: implemented adding to uses sections with options

git-svn-id: trunk@19802 -
This commit is contained in:
mattias 2009-05-04 16:12:03 +00:00
parent 1312656bf4
commit 3d95973a6c
5 changed files with 259 additions and 35 deletions

View File

@ -3885,7 +3885,7 @@ begin
if not InitCurCodeTool(Code) then exit;
try
Result:=FCurCodeTool.AddUnitToMainUsesSection(NewUnitName, NewUnitInFile,
SourceChangeCache);
SourceChangeCache);
except
on e: Exception do Result:=HandleException(e);
end;

View File

@ -4948,7 +4948,7 @@ begin
ReadNextAtom;
UnitNameAtom:=CurPos;
ReadNextAtom;
if CurPos.Flag=cafPoint then begin
if UpAtomIs('IN') then begin
ReadNextAtom;
InAtom:=CurPos;
end else
@ -5991,7 +5991,7 @@ var
ReadNextAtom;
UnitNameAtom:=CurPos;
ReadNextAtom;
if CurPos.Flag=cafPoint then begin
if UpAtomIs('IN') then begin
ReadNextAtom;
InAtom:=CurPos;
end else

View File

@ -47,8 +47,6 @@ uses
KeywordFuncLists;
type
{ TBeautifyCodeOptions }
// Insert policy types for class parts (properties, variables, method defs)
TClassPartInsertPolicy = (
cpipAlphabetically,
@ -91,6 +89,11 @@ type
);
TBeautifyCodeFlags = set of TBeautifyCodeFlag;
const
DefaultUsesInsertPolicy = uipBehindRelated;
type
{ TBeautifyCodeOptions }
TBeautifyCodeOptions = class(TPersistent)
private
@ -360,7 +363,7 @@ function UsesInsertPolicyNameToPolicy(const s: string): TUsesInsertPolicy;
begin
for Result:=Low(TUsesInsertPolicy) to High(TUsesInsertPolicy) do
if SysUtils.CompareText(UsesInsertPolicyNames[Result],s)=0 then exit;
Result:=uipInFrontOfRelated;
Result:=DefaultUsesInsertPolicy;
end;
function CompareSourceChangeCacheEntry(NodeData1, NodeData2: pointer): integer;
@ -1000,7 +1003,7 @@ begin
PropertyWriteIdentPrefix:='Set';
PropertyStoredIdentPostfix:='IsStored';
PrivateVariablePrefix:='f';
UsesInsertPolicy:=uipInFrontOfRelated;
UsesInsertPolicy:=DefaultUsesInsertPolicy;
NestedComments:=true;
end;

View File

@ -625,41 +625,259 @@ end;
function TStandardCodeTool.AddUnitToUsesSection(UsesNode: TCodeTreeNode;
const NewUnitName, NewUnitInFile: string;
SourceChangeCache: TSourceChangeCache): boolean;
var LineStart, LineEnd, Indent, InsertPos: integer;
var
Options: TBeautifyCodeOptions;
function NextUseUnitNodeInSameBlock(Node: TCodeTreeNode): boolean;
var
p: LongInt;
begin
if Node.NextBrother=nil then exit(false);
if PositionsInSameLine(Src,Node.EndPos,Node.NextBrother.StartPos) then
begin
// uses on same line belongs to the same formatting block
exit(true);
end;
// check that there is no comment/directive between
p:=FindPrevNonSpace(Src,Node.NextBrother.StartPos-1);
if Src[p]<>',' then exit(false);
p:=FindPrevNonSpace(Src,p-1);
if p>Node.EndPos then exit(false);
if LineEndCount(Src,Node.EndPos,Node.NextBrother.StartPos,p)>1 then exit(false);
Result:=true;
end;
procedure AddUseUnit(Lines: TStrings; FirstIndent, Indent: integer;
const NewUses: string);
var
Line: string;
l: Integer;
begin
if Lines.Count=0 then begin
Lines.Add(NewUses);
exit;
end;
Line:=Lines[Lines.Count-1];
if (atIdentifier in Options.DoInsertSpaceAfter)
or (atComma in Options.DoInsertSpaceInFront) then
Line:=Line+' ';
Line:=Line+',';
l:=length(Line)+length(NewUses)+1;
if (atComma in Options.DoInsertSpaceAfter)
or (atIdentifier in Options.DoInsertSpaceInFront) then
inc(l);
if Lines.Count=0 then
inc(l,FirstIndent);
if l<=Options.LineLength then begin
// append to last line
if (atComma in Options.DoInsertSpaceAfter)
or (atIdentifier in Options.DoInsertSpaceInFront) then
Line:=Line+' ';
Line:=Line+NewUses;
Lines[Lines.Count-1]:=Line;
end else begin
// add new line
Lines[Lines.Count-1]:=Line;
Line:=GetIndentStr(Indent)+NewUses;
Lines.Add(Line);
end;
end;
var LineStart, LineEnd, Indent, InsertPos, InsertToPos, InsertLen: integer;
NewUsesTerm: string;
InsertBehind: Boolean;
InsertNode: TCodeTreeNode;
Node: TCodeTreeNode;
NewCode: TCodeBuffer;
DiffPath: String;
DiffCnt: Integer;
BestDiffCnt: LongInt;
AnUnitName: String;
AnUnitInFilename: String;
i: Integer;
NewFilename: String;
NewComma: string;
Lines: TStringList;
FirstIndent: Integer;
InsertCode: String;
begin
Result:=false;
if (UsesNode=nil) or (UsesNode.Desc<>ctnUsesSection) or (NewUnitName='')
or (length(NewUnitName)>255) or (UsesNode.StartPos<1)
or (UsesNode.EndPos<1) then exit;
SourceChangeCache.MainScanner:=Scanner;
MoveCursorToNodeStart(UsesNode); // for nice error position
InsertPos:=UsesNode.EndPos-1; // position of semicolon at end of uses section
Options:=SourceChangeCache.BeautifyCodeOptions;
// find nice insert position
InsertBehind:=false;
InsertNode:=UsesNode.FirstChild;
case Options.UsesInsertPolicy of
uipFirst:
begin
InsertBehind:=false;
InsertNode:=UsesNode.FirstChild;
end;
uipInFrontOfRelated,uipBehindRelated:
begin
NewCode:=FindUnitSource(NewUnitName,'',false);
if NewCode<>nil then begin
NewFilename:=NewCode.Filename;
BestDiffCnt:=High(integer);
Node:=UsesNode.FirstChild;
while Node<>nil do begin
MoveCursorToCleanPos(Node.StartPos);
ReadNextAtom;
AnUnitName:=GetAtom;
ReadNextAtom;
if UpAtomIs('IN') then begin
ReadNextAtom;
AnUnitInFilename:=copy(Src,CurPos.StartPos+1,CurPos.EndPos-CurPos.StartPos-2);
end else
AnUnitInFilename:='';
// search unit
//DebugLn(['TStandardCodeTool.AddUnitToUsesSection Unit=',AnUnitName,' in "',AnUnitInFilename,'"']);
NewCode:=FindUnitSource(AnUnitName,AnUnitInFilename,false);
if NewCode<>nil then begin
// used unit found -> compute distance
DiffPath:=CreateRelativePath(NewCode.Filename,ExtractFilePath(NewFilename));
DiffCnt:=0;
for i:=0 to length(DiffPath) do
if DiffPath[i]=PathDelim then
inc(DiffCnt);
//DebugLn(['TStandardCodeTool.AddUnitToUsesSection DiffCnt=',DiffCnt,' "',NewCode.Filename,'" "',NewFilename,'"']);
if options.UsesInsertPolicy=uipInFrontOfRelated then begin
// insert in front of the first node with the lowest DiffCnt
if BestDiffCnt>DiffCnt then begin
BestDiffCnt:=DiffCnt;
InsertNode:=Node;
InsertBehind:=false;
end;
end else begin
// insert behind the last node with the lowest DiffCnt
if BestDiffCnt>=DiffCnt then begin
BestDiffCnt:=DiffCnt;
InsertNode:=Node;
InsertBehind:=true;
end;
end;
end;
Node:=Node.NextBrother;
end;
end;
end;
uipLast:
begin
InsertNode:=UsesNode.LastChild;
InsertBehind:=true;
end;
uipAlphabetically:
begin
InsertNode:=UsesNode.FirstChild;
InsertBehind:=false;
while (InsertNode<>nil)
and (CompareIdentifiers(PChar(NewUnitName),@Src[InsertNode.StartPos])<0) do
InsertNode:=InsertNode.NextBrother;
if InsertNode=nil then begin
InsertNode:=UsesNode.LastChild;
InsertBehind:=true;
end;
end;
end;
// build insert text "unitname in 'file'"
NewUsesTerm:=NewUnitName;
if NewUnitInFile<>'' then
NewUsesTerm:=NewUsesTerm+' in '''+NewUnitInFile+'''';
// check if insertion would expand the line over the max LineLength
NewUsesTerm:=NewUsesTerm+' '
+Options.BeautifyKeyWord('in')
+' '''+NewUnitInFile+'''';
NewComma:=',';
if (atComma in Options.DoInsertSpaceInFront)
or (atIdentifier in Options.DoInsertSpaceAfter)
then
NewComma:=' '+NewComma;
if (atComma in Options.DoInsertSpaceAfter)
or (atIdentifier in Options.DoInsertSpaceInFront)
then
NewComma:=NewComma+' ';
if InsertBehind then begin
// insert behind unitname, in front of semicolon or comma
// for example: uses unit1|, unit2 in 'unit2.pp'|;
InsertPos:=InsertNode.EndPos;
InsertCode:=NewComma+NewUsesTerm;
end else begin
// insert in front of unitname, behind 'uses' or comma
// for example: uses |unit1, |unit2;
InsertPos:=InsertNode.StartPos;
InsertCode:=NewUsesTerm+NewComma;
end;
InsertToPos:=InsertPos;
//DebugLn(['TStandardCodeTool.AddUnitToUsesSection InsertNode=',ExtractNode(InsertNode,[]),' InsertBehind=',InsertBehind]);
// check if addition fits into the line
// if not, rebuild the uses section
GetLineStartEndAtPosition(Src,InsertPos,LineStart,LineEnd);
if InsertPos-LineStart+length(NewUsesTerm)+2>=
SourceChangeCache.BeautifyCodeOptions.LineLength then
begin
// split line
// calculate the indent
InsertLen:=length(NewUsesTerm);
//DebugLn(['TStandardCodeTool.AddUnitToUsesSection Line=',copy(Src,LineStart,InsertPos-LineStart),'<InsertPos>',copy(Src,InsertPos,LineEnd-InsertPos)]);
if (LineEnd-LineStart+InsertLen > Options.LineLength) then begin
// line too long => reformat block of used units
// find start of block of used units
Node:=InsertNode;
while (Node.PriorBrother<>nil)
and NextUseUnitNodeInSameBlock(Node.PriorBrother) do
Node:=Node.PriorBrother;
InsertPos:=Node.StartPos;
GetLineStartEndAtPosition(Src,InsertPos,LineStart,LineEnd);
FirstIndent:=InsertPos-LineStart;
Indent:=GetLineIndent(Src,InsertPos);
// if the 'uses' keyword is not in the same line of the insertion position,
// then indent the new line
// else keep the indentation.
if (UsesNode.StartPos>=LineStart)
and (UsesNode.StartPos<LineEnd) then
inc(Indent,SourceChangeCache.BeautifyCodeOptions.Indent);
NewUsesTerm:=','+SourceChangeCache.BeautifyCodeOptions.LineEnd+
GetIndentStr(Indent)+NewUsesTerm;
end else
// simply insert
NewUsesTerm:=', '+NewUsesTerm;
if not SourceChangeCache.Replace(gtNone,gtNone,InsertPos,InsertPos,
NewUsesTerm) then exit;
if PositionsInSameLine(Src,UsesNode.StartPos,InsertPos) then begin
// for example: uses |unit1;
inc(Indent,Options.Indent);
end;
// create new block of used units
Lines:=TStringList.Create;
try
while Node<>nil do begin
InsertToPos:=Node.EndPos;
if (Node=InsertNode) and (not InsertBehind) then
AddUseUnit(Lines,FirstIndent,Indent,NewUsesTerm);
MoveCursorToCleanPos(Node.StartPos);
ReadNextAtom;
InsertCode:=GetAtom;
ReadNextAtom;
if UpAtomIs('IN') then begin
ReadNextAtom;
InsertCode:=InsertCode+' '+Options.BeautifyKeyWord('in')+' '+GetAtom;
end;
AddUseUnit(Lines,FirstIndent,Indent,InsertCode);
if (Node=InsertNode) and InsertBehind then
AddUseUnit(Lines,FirstIndent,Indent,NewUsesTerm);
if not NextUseUnitNodeInSameBlock(Node) then break;
Node:=Node.NextBrother;
end;
InsertCode:='';
for i:=0 to Lines.Count-1 do begin
if i>0 then
InsertCode:=InsertCode+Options.LineEnd;
InsertCode:=InsertCode+Lines[i];
end;
finally
Lines.Free;
end;
end;
//DebugLn(['TStandardCodeTool.AddUnitToUsesSection Replace="',copy(Src,InsertPos,InsertToPos-InsertPos),'" with "',InsertCode,'"']);
if not SourceChangeCache.Replace(gtNone,gtNone,InsertPos,InsertToPos,
InsertCode) then exit;
if not SourceChangeCache.Apply then exit;
Result:=true;
end;
@ -679,9 +897,12 @@ begin
if UsesNode<>nil then begin
// add unit to existing uses section
if not (FindUnitInUsesSection(UsesNode,UpperCaseStr(NewUnitName),Junk,Junk))
then
then begin
if not AddUnitToUsesSection(UsesNode,NewUnitName,NewUnitInFile,
SourceChangeCache) then exit;
SourceChangeCache)
then
exit;
end;
end else begin
// create a new uses section
if Tree.Root=nil then exit;

View File

@ -370,7 +370,7 @@ begin
'CodeToolsOptions/SetPropertyVariablename/Value',''),'AValue');
FUsesInsertPolicy:=UsesInsertPolicyNameToPolicy(XMLConfig.GetValue(
'CodeToolsOptions/UsesInsertPolicy/Value',
UsesInsertPolicyNames[uipInFrontOfRelated]));
UsesInsertPolicyNames[DefaultUsesInsertPolicy]));
// identifier completion
FIdentComplAddSemicolon:=XMLConfig.GetValue(
@ -472,7 +472,7 @@ begin
FSetPropertyVariablename,'AValue');
XMLConfig.SetDeleteValue('CodeToolsOptions/UsesInsertPolicy/Value',
UsesInsertPolicyNames[FUsesInsertPolicy],
UsesInsertPolicyNames[uipInFrontOfRelated]);
UsesInsertPolicyNames[DefaultUsesInsertPolicy]);
// identifier completion
XMLConfig.SetDeleteValue('CodeToolsOptions/IdentifierCompletion/AddSemicolon',
@ -602,7 +602,7 @@ begin
FPropertyStoredIdentPostfix:='IsStored';
FPrivateVariablePrefix:='f';
FSetPropertyVariablename:='AValue';
FUsesInsertPolicy:=uipInFrontOfRelated;
FUsesInsertPolicy:=DefaultUsesInsertPolicy;
// identifier completion
FIdentComplAddSemicolon:=true;