Improve source conversion. Edits in uses section are first collected to lists.

git-svn-id: trunk@23784 -
This commit is contained in:
juha 2010-02-25 02:19:24 +00:00
parent e930e50b5e
commit 9f7ad44770
2 changed files with 318 additions and 171 deletions

View File

@ -6,17 +6,18 @@ interface
uses
// LCL+FCL
Classes, SysUtils, FileProcs, Forms, Controls, DialogProcs,
// TypInfo, CodeToolsStrConsts, AVL_Tree, LFMTrees,
Classes, SysUtils, FileProcs, Forms, Controls, DialogProcs, Dialogs,
// IDE
LazarusIDEStrConsts, LazIDEIntf,
// codetools
CodeToolManager, StdCodeTools, CodeTree, CodeAtom, //IdentCompletionTool,
CodeToolManager, StdCodeTools, CodeTree, CodeAtom,
FindDeclarationTool, PascalReaderTool, PascalParserTool,
CodeBeautifier, ExprEval, KeywordFuncLists, BasicCodeTools, LinkScanner,
CodeCache, SourceChanger, CustomCodeTool, CodeToolsStructs, EventCodeTool;
type
{ TConvCodeTool }
{ TConvDelphiCodeTool }
TConvDelphiCodeTool = class // (TStandardCodeTool)
private
@ -26,36 +27,58 @@ type
fScanner: TLinkScanner;
fAsk: Boolean;
fAddLRSCode: boolean;
fMakeLowerCaseRes: boolean;
fLowerCaseRes: boolean;
// List of units to remove.
fUnitsToRemove: TStringList;
// Units to rename. Map of unit name -> real unit name.
fUnitsToRename: TStringToStringTree;
// List of units to add.
fUnitsToAdd: TStringList;
// List of units to be commented.
fUnitsToComment: TStringList;
function AddModeDelphiDirective: boolean;
function ConvertUsedUnits: boolean;
function RemoveDFMResourceDirective: boolean;
function LowerCaseMainResourceDirective: boolean;
function AddLRSIncludeDirective: boolean;
function RemoveUnits: boolean;
function RenameUnits: boolean;
function AddUnits: boolean;
function CommentOutUnits: boolean;
// function ConvertUsedUnits: boolean;
function HandleCodetoolError: TModalResult;
public
constructor Create(Code: TCodeBuffer; Ask, MakeLowerCaseRes, AddLRSCode: boolean);
constructor Create(Code: TCodeBuffer);
destructor Destroy; override;
function Convert: TModalResult;
public
property Ask: Boolean read fAsk write fAsk;
property AddLRSCode: boolean read fAddLRSCode write fAddLRSCode;
property LowerCaseRes: boolean read fLowerCaseRes write fLowerCaseRes;
property UnitsToRemove: TStringList read fUnitsToRemove write fUnitsToRemove;
property UnitsToRename: TStringToStringTree read fUnitsToRename write fUnitsToRename;
property UnitsToAdd: TStringList read fUnitsToAdd write fUnitsToAdd;
property UnitsToComment: TStringList read fUnitsToComment write fUnitsToComment;
end;
implementation
{ TConvDelphiCodeTool }
constructor TConvDelphiCodeTool.Create(Code: TCodeBuffer;
Ask, MakeLowerCaseRes, AddLRSCode: boolean);
constructor TConvDelphiCodeTool.Create(Code: TCodeBuffer);
begin
fCode:=Code;
fAsk:=Ask;
fMakeLowerCaseRes:=MakeLowerCaseRes;
fAddLRSCode:=AddLRSCode;
// Default values for vars.
fAsk:=true;
fLowerCaseRes:=false;
fAddLRSCode:=false;
fUnitsToComment:=nil;
fUnitsToRename:=nil;
// Initialize codetools. (Copied from TCodeToolManager.)
if not CodeToolBoss.InitCurCodeTool(Code) then exit;
try
fCodeTool:=CodeToolBoss.CurCodeTool;
fSrcCache:=CodeToolBoss.SourceChangeCache;
if fSrcCache=nil then exit;
// if fSrcCache=nil then exit;
fScanner:=fCodeTool.Scanner;
fSrcCache.MainScanner:=fScanner;
except
@ -69,7 +92,33 @@ begin
inherited Destroy;
end;
function TConvDelphiCodeTool.HandleCodetoolError: TModalResult;
// returns mrOk or mrAbort
const
CodetoolsFoundError='The codetools found an error in unit %s:%s%s%s';
var
ErrMsg: String;
begin
ErrMsg:=CodeToolBoss.ErrorMessage;
LazarusIDE.DoJumpToCodeToolBossError;
if fAsk then begin
Result:=QuestionDlg(lisCCOErrorCaption,
Format(CodetoolsFoundError, [ExtractFileName(fCode.Filename), #13, ErrMsg, #13]),
mtWarning, [mrIgnore, lisIgnoreAndContinue, mrAbort], 0);
if Result=mrIgnore then Result:=mrOK;
end else begin
Result:=mrOK;
end;
end;
function TConvDelphiCodeTool.Convert: TModalResult;
// add {$mode delphi} directive
// remove windows unit and add LResources, LCLIntf
// remove {$R *.dfm} or {$R *.xfm} directive
// Change {$R *.RES} to {$R *.res} if needed
// add initialization
// add {$i unit.lrs} directive
// TODO: fix delphi ambiguousities like incomplete proc implementation headers
begin
Result:=mrCancel;
try
@ -84,11 +133,18 @@ begin
finally
fSrcCache.EndUpdate;
end;
if not ConvertUsedUnits then exit;
if not RemoveUnits then exit;
if not RenameUnits then exit;
if not AddUnits then exit;
if not CommentOutUnits then exit;
if not fCodeTool.FixUsedUnitCase(fSrcCache) then exit;
if not fSrcCache.Apply then exit;
Result:=mrOK;
except
Result:=JumpToCodetoolErrorAndAskToAbort(fAsk);
on e: Exception do begin
CodeToolBoss.HandleException(e);
Result:=HandleCodetoolError;
end;
end;
end;
@ -108,9 +164,8 @@ begin
ReadNextAtom; // name
ReadNextAtom; // semicolon
InsertPos:=CurPos.EndPos;
fSrcCache.Replace(gtEmptyLine,gtEmptyLine,InsertPos,InsertPos,
'{$MODE Delphi}');
if not fSrcCache.Apply then exit;
fSrcCache.Replace(gtEmptyLine,gtEmptyLine,InsertPos,InsertPos,'{$MODE Delphi}');
// if not fSrcCache.Apply then exit;
end;
// changing mode requires rescan
BuildTree(false);
@ -118,40 +173,6 @@ begin
Result:=true;
end;
function TConvDelphiCodeTool.ConvertUsedUnits: boolean;
// replace unit 'Windows' with 'LCLIntf' and add 'LResources'
// rename 'in' filenames to case sensitive filename
var
NamePos, InPos: TAtomPosition;
begin
Result:=false;
if fCodeTool.FindUnitInAllUsesSections('WINDOWS',NamePos,InPos)
and (InPos.StartPos<1) then begin
if not fSrcCache.Replace(gtNone,gtNone,
NamePos.StartPos,NamePos.EndPos,'LCLIntf') then
begin
exit;
end;
if not fSrcCache.Apply then exit;
end;
if fAddLRSCode then
begin
if not fCodeTool.AddUnitToMainUsesSection('LResources','',fSrcCache) then
begin
exit;
end;
end;
if not fCodeTool.RemoveUnitFromAllUsesSections('VARIANTS',fSrcCache) then
begin
exit;
end;
if not fCodeTool.FixUsedUnitCase(fSrcCache) then
begin
exit;
end;
Result:=true;
end;
function TConvDelphiCodeTool.RemoveDFMResourceDirective: boolean;
// remove {$R *.dfm} or {$R *.xfm} directive
var
@ -189,7 +210,7 @@ var
ACleanPos: Integer;
s: String;
begin
if fMakeLowerCaseRes then begin
if fLowerCaseRes then begin
Result:=false;
// find $R directive
ACleanPos:=1;
@ -257,6 +278,54 @@ begin
Result:=true;
end;
function TConvDelphiCodeTool.RemoveUnits: boolean;
// Remove units
var
i: Integer;
begin
Result:=false;
if Assigned(fUnitsToRemove) then begin
for i := 0 to fUnitsToRemove.Count-1 do
if not fCodeTool.RemoveUnitFromAllUsesSections(fUnitsToRemove[i], fSrcCache) then
exit;
end;
Result:=true;
end;
function TConvDelphiCodeTool.RenameUnits: boolean;
// Rename units
begin
Result:=false;
if Assigned(fUnitsToRename) then
if not fCodeTool.ReplaceUsedUnits(fUnitsToRename, fSrcCache) then
exit;
Result:=true;
end;
function TConvDelphiCodeTool.AddUnits: boolean;
// Add units
var
i: Integer;
begin
Result:=false;
if Assigned(fUnitsToAdd) then
for i := 0 to fUnitsToAdd.Count-1 do
if not fCodeTool.AddUnitToMainUsesSection(fUnitsToAdd[i],'',fSrcCache) then
exit;
Result:=true;
end;
function TConvDelphiCodeTool.CommentOutUnits: boolean;
// Comment out missing units
begin
Result:=false;
if Assigned(fUnitsToComment) and (fUnitsToComment.Count>0) then
if not fCodeTool.CommentUnitsInUsesSections(fUnitsToComment, fSrcCache) then
exit;
// IDEMessagesWindow.AddMsg('Error="'+CodeToolBoss.ErrorMessage+'"','',-1);
Result:=true;
end;
end.

View File

@ -33,10 +33,11 @@ interface
uses
// LCL+FCL
Classes, SysUtils, LCLProc, Forms, Controls, Dialogs, FileProcs, LResources,
FileUtil, IniFiles, contnrs,
Classes, SysUtils, LCLProc, Forms, Controls, Dialogs, LResources,
FileUtil, contnrs, IniFiles,
// codetools
CodeToolManager, DefineTemplates, CodeAtom, CodeCache, LinkScanner,
FileProcs, CodeToolsStructs,
// IDEIntf
ComponentReg, IDEMsgIntf, MainIntf, LazIDEIntf, PackageIntf, ProjectIntf,
// IDE
@ -75,6 +76,17 @@ type
// Actual code for unit and form file.
fUnitCode, fLfmCode: TCodeBuffer;
fFlags: TConvertUnitFlags;
// Units not found in project dir or packages.
fMissingUnits: TStrings;
// Units to remove.
fUnitsToRemove: TStringList;
// Units to rename. Map of unit name -> real unit name.
fUnitsToRename: TStringToStringTree;
// Units to add.
fUnitsToAdd: TStringList;
// Units collected to be commented later.
fUnitsToComment: TStringList;
fSettings: TConvertSettings;
function GetDfmFileName: string;
function CopyAndLoadFile: TModalResult;
@ -82,9 +94,10 @@ type
function ConvertFormFile: TModalResult;
function ConvertDfmToLfm(const LfmFilename: string): TModalResult;
function MissingUnitToMsg(MissingUnit: string): string;
function CommentAutomatically(MissingUnits: TStrings): integer;
function AskUnitPathFromUser(MissingUnits: TStrings): TModalResult;
function FixMissingUnits(ShowAbort: boolean): TModalResult;
function CommentAutomatically: integer;
function AskUnitPathFromUser: TModalResult;
function FixIncludeFiles: TModalResult;
function FixMissingUnits: TModalResult;
protected
public
constructor Create(AOwnerConverter: TConvertDelphiPBase; const AFilename: string;
@ -114,9 +127,13 @@ type
fDelphiPFilename: string; // .dpr or .dpk file name
fLazPSuffix: string; // '.lpi' or '.lpk'
fDelphiPSuffix: string; // '.dpr' or '.dpk'
fCachedUnitNames: THashedStringList;
fSettings: TConvertSettings;
// Units found in user defined paths.
fCachedUnitNames: TStringToStringTree;
// Map of case incorrect unit name -> real unit name.
fCachedRealUnitNames: TStringToStringTree;
// Missing units that are commented automatically in all units.
fAllMissingUnits: TStringList;
fSettings: TConvertSettings;
function ConvertSub: TModalResult;
procedure CleanUpCompilerOptionsSearchPaths(Options: TBaseCompilerOptions);
procedure SetCompilerModeForDefineTempl(DefTempl: TDefineTemplate);
@ -125,10 +142,14 @@ type
function ReadDelphiConfigFiles: TModalResult;
function ExtractOptionsFromDOF(const DOFFilename: string): TModalResult;
function ExtractOptionsFromCFG(const CFGFilename: string): TModalResult;
function LocateMissingUnits(MissingUnits: TStrings): integer;
function DoMissingUnits(MissingUnits: TStrings): integer;
function DoCaseErrorUnits(MissingUnits: TStrings;
UnitsToRename: TStringToStringTree): integer;
procedure CacheUnitsInPath(const APath, ABasePath: string);
procedure CacheUnitsInPath(const APath: string);
function GetCachedUnitPath(const AUnitName: string): string;
function NeedsRenameUnit(const AUnitName: string;
out RealUnitName: string): Boolean;
protected
function CreateInstance: TModalResult; virtual; abstract;
function CreateMainSourceFile: TModalResult; virtual;
@ -534,69 +555,88 @@ var
LrsFilename: string; // Resource file name.
ConvTool: TConvDelphiCodeTool;
begin
fLfmCode:=nil;
LrsFilename:='';
// rename files (.pas,.dfm) lowercase. TODO: rename files in project
LfmFilename:=fSettings.DelphiToLazFilename(fOrigUnitFilename, '.lfm',
cdtlufRenameLowercase in fFlags);
// Get DFM file name and close it in editor.
DfmFilename:=GetDfmFileName;
if DfmFilename<>'' then begin
Result:=LazarusIDE.DoCloseEditorFile(DfmFilename,[cfSaveFirst]);
if Result<>mrOk then exit;
if FileExistsUTF8(LfmFilename) then begin
if (FileAgeUTF8(LfmFilename)>=FileAgeUTF8(DfmFilename)) then begin
// .lfm is not older than .dfm -> keep .lfm (it could be the same file)
end else begin
// .lfm is older than .dfm -> remove .lfm
DeleteFileUTF8(LfmFilename);
fUnitsToRemove:=TStringList.Create;
fUnitsToRename:=TStringToStringTree.Create(false);
fUnitsToAdd:=TStringList.Create;
fUnitsToComment:=TStringList.Create;
ConvTool:=TConvDelphiCodeTool.Create(fUnitCode);
try
fLfmCode:=nil;
LrsFilename:='';
// rename files (.pas,.dfm) lowercase. TODO: rename files in project
LfmFilename:=fSettings.DelphiToLazFilename(fOrigUnitFilename, '.lfm',
cdtlufRenameLowercase in fFlags);
// Get DFM file name and close it in editor.
DfmFilename:=GetDfmFileName;
if DfmFilename<>'' then begin
Result:=LazarusIDE.DoCloseEditorFile(DfmFilename,[cfSaveFirst]);
if Result<>mrOk then exit;
if FileExistsUTF8(LfmFilename) then begin
if (FileAgeUTF8(LfmFilename)>=FileAgeUTF8(DfmFilename)) then begin
// .lfm is not older than .dfm -> keep .lfm (it could be the same file)
end else begin
// .lfm is older than .dfm -> remove .lfm
DeleteFileUTF8(LfmFilename);
end;
end;
if not FileExistsUTF8(LfmFilename) then begin
// TODO: update project
Result:=fSettings.RenameFile(DfmFilename,LfmFilename);
if Result<>mrOK then exit;
end;
end;
if not FileExistsUTF8(LfmFilename) then begin
// TODO: update project
Result:=fSettings.RenameFile(DfmFilename,LfmFilename);
if Result<>mrOK then exit;
// convert .dfm file to .lfm file (without context type checking)
if FileExistsUTF8(LfmFilename) then begin
IDEMessagesWindow.AddMsg('Converting DFM to LFM file '+LfmFilename,'',-1);
Result:=ConvertDfmToLfm(LfmFilename);
if Result<>mrOk then exit;
// create empty .lrs file
LrsFilename:=ChangeFileExt(fLazUnitFilename,'.lrs');
IDEMessagesWindow.AddMsg('Creating resource file '+LrsFilename,'',-1);
Result:=CreateEmptyFile(LrsFilename,[mbAbort,mbRetry]);
if Result<>mrOk then exit;
// Read form file code in.
Result:=LoadCodeBuffer(fLfmCode,LfmFilename,
[lbfCheckIfText,lbfUpdateFromDisk],true);
if Result<>mrOk then exit;
end;
end;
// convert .dfm file to .lfm file (without context type checking)
if FileExistsUTF8(LfmFilename) then begin
IDEMessagesWindow.AddMsg('Converting DFM to LFM file '+LfmFilename,'',-1);
Result:=ConvertDfmToLfm(LfmFilename);
if Result<>mrOk then exit;
// create empty .lrs file
LrsFilename:=ChangeFileExt(fLazUnitFilename,'.lrs');
IDEMessagesWindow.AddMsg('Creating resource file '+LrsFilename,'',-1);
Result:=CreateEmptyFile(LrsFilename,[mbAbort,mbRetry]);
if Result<>mrOk then exit;
// Read form file code in.
Result:=LoadCodeBuffer(fLfmCode,LfmFilename,
[lbfCheckIfText,lbfUpdateFromDisk],true);
if Result<>mrOk then exit;
end;
// check LCL path only for projects/packages.
if Assigned(fOwnerConverter) then begin
Result:=CheckFilenameForLCLPaths(fLazUnitFilename);
// check LCL path only for projects/packages.
if Assigned(fOwnerConverter) then begin
Result:=CheckFilenameForLCLPaths(fLazUnitFilename);
if Result<>mrOk then exit;
end;
// Fix include file names.
Result:=FixIncludeFiles;
if Result<>mrOk then exit;
end;
// add {$mode delphi} directive
// remove windows unit and add LResources, LCLIntf
// remove {$R *.dfm} or {$R *.xfm} directive
// Change {$R *.RES} to {$R *.res} if needed
// add initialization
// add {$i unit.lrs} directive
// TODO: fix delphi ambiguousities like incomplete proc implementation headers
ConvTool:=TConvDelphiCodeTool.Create(fUnitCode, Assigned(fOwnerConverter),
FileExistsUTF8(ChangeFileExt(fLazUnitFilename, '.res')),
LrsFilename<>'');
try
// Fix or comment missing units, show error messages.
Result:=FixMissingUnits;
if Result<>mrOk then exit;
// Some units to remove, rename and add.
fUnitsToRename['WINDOWS']:='LCLIntf';
if LrsFilename<>'' then
fUnitsToAdd.Append('LResources');
fUnitsToRemove.Append('VARIANTS');
// Do the actual code conversion.
ConvTool.Ask:=Assigned(fOwnerConverter);
ConvTool.LowerCaseRes:=FileExistsUTF8(ChangeFileExt(fLazUnitFilename, '.res'));
ConvTool.AddLRSCode:=LrsFilename<>'';
ConvTool.UnitsToRemove:=fUnitsToRemove;
ConvTool.UnitsToRename:=fUnitsToRename;
ConvTool.UnitsToAdd:=fUnitsToAdd;
ConvTool.UnitsToComment:=fUnitsToComment;
Result:=ConvTool.Convert;
if Result=mrAbort then exit;
// if Result=mrAbort then exit;
finally
ConvTool.Free;
fUnitsToComment.Free;
fUnitsToAdd.Free;
fUnitsToRename.Free;
fUnitsToRemove.Free;
end;
// Fix or comment missing units, FixMissingUnits shows error messages.
Result:=FixMissingUnits(true);
end;
function TConvertDelphiUnit.ConvertFormFile: TModalResult;
@ -700,33 +740,24 @@ begin
+'Can''t find unit '+MissingUnit;
end;
function TConvertDelphiUnit.CommentAutomatically(MissingUnits: TStrings): integer;
function TConvertDelphiUnit.CommentAutomatically: integer;
// Comment automatically unit names that were commented in other files.
// Return the number of missing units still left.
var
AutoUnits: TStringList;
i, x: Integer;
s: string;
begin
AutoUnits:=TStringList.Create;
try
for i:=MissingUnits.Count-1 downto 0 do begin
s:=MissingUnits[i];
if fOwnerConverter.fAllMissingUnits.Find(s, x) then begin
AutoUnits.Append(s);
MissingUnits.Delete(i);
end;
for i:=fMissingUnits.Count-1 downto 0 do begin
s:=fMissingUnits[i];
if fOwnerConverter.fAllMissingUnits.Find(s, x) then begin
fUnitsToComment.Append(s);
fMissingUnits.Delete(i);
end;
if AutoUnits.Count>0 then
if not CodeToolBoss.CommentUnitsInUsesSections(fUnitCode,AutoUnits) then
IDEMessagesWindow.AddMsg('Error="'+CodeToolBoss.ErrorMessage+'"','',-1);
Result:=MissingUnits.Count;
finally
AutoUnits.Free;
end;
Result:=fMissingUnits.Count;
end;
function TConvertDelphiUnit.AskUnitPathFromUser(MissingUnits: TStrings): TModalResult;
function TConvertDelphiUnit.AskUnitPathFromUser: TModalResult;
var
TryAgain: Boolean;
UnitDirDialog: TSelectDirectoryDialog;
@ -735,16 +766,14 @@ begin
// ask user what to do
repeat
TryAgain:=False;
Result:=AskMissingUnits(MissingUnits, ExtractFileName(fLazUnitFilename));
Result:=AskMissingUnits(fMissingUnits, ExtractFileName(fLazUnitFilename));
case Result of
// mrOK means: comment out.
mrOK: begin
// These units will be commented automatically in this project/package.
if Assigned(fOwnerConverter) then
fOwnerConverter.fAllMissingUnits.AddStrings(MissingUnits);
// comment missing units
if not CodeToolBoss.CommentUnitsInUsesSections(fUnitCode,MissingUnits) then
IDEMessagesWindow.AddMsg('Error="'+CodeToolBoss.ErrorMessage+'"','',-1);
fOwnerConverter.fAllMissingUnits.AddStrings(fMissingUnits);
fUnitsToComment.AddStrings(fMissingUnits);
end;
// mrYes means: Search for unit path.
mrYes: begin
@ -753,11 +782,13 @@ begin
UnitDirDialog.InitialDir:=fSettings.MainPath;
UnitDirDialog.Title:='All sub-directories will be scanned for unit files';
if UnitDirDialog.Execute and Assigned(fOwnerConverter) then begin
PrevMiss:=MissingUnits.Count;
PrevMiss:=fMissingUnits.Count;
// Add the new path to project if missing units are found.
fOwnerConverter.CacheUnitsInPath(UnitDirDialog.Filename);
TryAgain:=fOwnerConverter.LocateMissingUnits(MissingUnits)>0;
if TryAgain and (PrevMiss<>MissingUnits.Count) then
TryAgain:=fOwnerConverter.DoMissingUnits(fMissingUnits)>0;
if TryAgain then
TryAgain:=fOwnerConverter.DoCaseErrorUnits(fMissingUnits, fUnitsToRename)>0;
if TryAgain and (PrevMiss<>fMissingUnits.Count) then
ShowMessage('Some units were found but not all.');
end;
finally
@ -771,20 +802,18 @@ begin
until not TryAgain;
end;
function TConvertDelphiUnit.FixMissingUnits(ShowAbort: boolean): TModalResult;
function TConvertDelphiUnit.FixIncludeFiles: TModalResult;
// fix include filenames
var
CTResult: Boolean;
i: Integer;
Msg: string;
MissingUnits: TStrings;
CodePos: PCodeXYPosition;
MissingIncludeFilesCodeXYPos: TFPList;
CodePos: PCodeXYPosition;
Msg: string;
i: Integer;
begin
// fix include filenames
Result:=mrOk;
MissingIncludeFilesCodeXYPos:=nil;
try
if not CodeToolBoss.FixIncludeFilenames(fUnitCode,true,
MissingIncludeFilesCodeXYPos)
if not CodeToolBoss.FixIncludeFilenames(fUnitCode,true,MissingIncludeFilesCodeXYPos)
then begin
if MissingIncludeFilesCodeXYPos<>nil then begin
for i:=0 to MissingIncludeFilesCodeXYPos.Count-1 do begin
@ -792,44 +821,59 @@ begin
Msg:=CodePos^.Code.Filename
+'('+IntToStr(CodePos^.y)+','+IntToStr(CodePos^.x)+')'
+' missing include file';
IDEMessagesWindow.AddMsg(Msg,'',-1);
IDEMessagesWindow.AddMsg(Msg, '', -1);
end;
end;
IDEMessagesWindow.AddMsg('Error="'+CodeToolBoss.ErrorMessage+'"','',-1);
IDEMessagesWindow.AddMsg('Error="'+CodeToolBoss.ErrorMessage+'"', '', -1);
exit;
end;
finally
CodeToolBoss.FreeListOfPCodeXYPosition(MissingIncludeFilesCodeXYPos);
end;
end;
function TConvertDelphiUnit.FixMissingUnits: TModalResult;
var
CTResult: Boolean;
i: Integer;
begin
Result:=mrOk;
MissingUnits:=nil;
fMissingUnits:=nil; // Will be created in CodeToolBoss.FindMissingUnits.
try
// find missing units
CTResult:=CodeToolBoss.FindMissingUnits(fUnitCode,MissingUnits,true);
CTResult:=CodeToolBoss.FindMissingUnits(fUnitCode,fMissingUnits,true);
if not CTResult then begin
IDEMessagesWindow.AddMsg('Error="'+CodeToolBoss.ErrorMessage+'"','',-1);
exit;
end;
// no missing units -> good
if (MissingUnits=nil) or (MissingUnits.Count=0) then exit;
if (fMissingUnits=nil) or (fMissingUnits.Count=0) then exit;
// Remove Windows unit from missing list. It will be changed later.
for i:=fMissingUnits.Count-1 downto 0 do begin
if UpperCase(fMissingUnits[i])='WINDOWS' then begin
fMissingUnits.Delete(i);
break;
end;
end;
if fMissingUnits.Count=0 then exit;
if Assigned(fOwnerConverter) then begin
// Try to find from subdirectories above project path first.
if fOwnerConverter.LocateMissingUnits(MissingUnits)=0 then exit;
// Try to find from subdirectories scanned earlier.
if fOwnerConverter.DoMissingUnits(fMissingUnits)=0 then exit;
// Comment out automatically units that were commented in other files.
if CommentAutomatically(MissingUnits)=0 then exit;
if CommentAutomatically=0 then exit;
end;
// Interactive dialog for searching unit.
Result:=AskUnitPathFromUser(MissingUnits);
Result:=AskUnitPathFromUser;
if Result<>mrOK then exit;
// add error messages, so the user can click on them
for i:=0 to MissingUnits.Count-1 do
IDEMessagesWindow.AddMsg(MissingUnitToMsg(MissingUnits[i]),'',-1);
for i:=0 to fMissingUnits.Count-1 do
IDEMessagesWindow.AddMsg(MissingUnitToMsg(fMissingUnits[i]),'',-1);
finally
MissingUnits.Free;
fMissingUnits.Free;
end;
end;
@ -851,7 +895,8 @@ end;
constructor TConvertDelphiPBase.Create(const AFilename, ADescription: string);
begin
fOrigPFilename:=AFilename;
fCachedUnitNames:=THashedStringList.Create;
fCachedUnitNames:=TStringToStringTree.Create(true);
fCachedRealUnitNames:=TStringToStringTree.Create(true);
fSettings:=TConvertSettings.Create('Convert Delphi '+ADescription);
fSettings.MainFilename:=fOrigPFilename;
fAllMissingUnits:=TStringList.Create;
@ -863,6 +908,7 @@ end;
destructor TConvertDelphiPBase.Destroy;
begin
fAllMissingUnits.Free;
fCachedRealUnitNames.Free;
fCachedUnitNames.Free;
inherited Destroy;
end;
@ -1139,9 +1185,9 @@ begin
Options.SrcPath:=CleanProjectSearchPath(Options.SrcPath);
end;
function TConvertDelphiPBase.LocateMissingUnits(MissingUnits: TStrings): integer;
function TConvertDelphiPBase.DoMissingUnits(MissingUnits: TStrings): integer;
// Locate unit names in earlier cached list and remove them from MissingUnits.
// Return the number of missing units still left.
// Return the number of units still missing.
var
i: Integer;
sUnitPath: string;
@ -1149,7 +1195,7 @@ begin
for i:=MissingUnits.Count-1 downto 0 do begin
sUnitPath:=GetCachedUnitPath(MissingUnits[i]);
if sUnitPath<>'' then begin
// Add unit path to project's settings.
// Found: add unit path to project's settings.
with CompOpts do
OtherUnitFiles:=MergeSearchPaths(OtherUnitFiles,sUnitPath);
// No more missing, delete from list.
@ -1159,8 +1205,29 @@ begin
Result:=MissingUnits.Count;
end;
function TConvertDelphiPBase.DoCaseErrorUnits(MissingUnits: TStrings;
UnitsToRename: TStringToStringTree): integer;
// Locate existing unit names with different case add them to UnitsToRename.
// Return the number of units still missing.
var
i: Integer;
sUnitPath, mUnit, RealUnitName: string;
begin
for i:=MissingUnits.Count-1 downto 0 do begin
mUnit:=MissingUnits[i];
sUnitPath:=GetCachedUnitPath(mUnit);
Assert(sUnitPath='', 'sUnitPath should be empty');
if NeedsRenameUnit(mUnit, RealUnitName) then begin
// Add to rename unit list, delete from missing unit list.
UnitsToRename[mUnit]:=RealUnitName;
MissingUnits.Delete(i);
end;
end;
Result:=MissingUnits.Count;
end;
procedure TConvertDelphiPBase.CacheUnitsInPath(const APath, ABasePath: string);
// Search all pascal units in APath and save them in fCachedUnitNames
// Search all pascal units in APath and store them in fCachedUnitNames
// with a path relative to ABasePath.
var
PasFileList: TStringList;
@ -1173,8 +1240,12 @@ begin
RelPath:=FileUtil.CreateRelativePath(PasFile, ABasePath);
SubPath:=ExtractFilePath(RelPath);
sUnitName:=ExtractFileNameOnly(RelPath);
if (SubPath<>'') and (sUnitName<>'') then
fCachedUnitNames.Values[UpperCase(sUnitName)]:=SubPath;
if (SubPath<>'') and (sUnitName<>'') then begin
// Map path by unit name.
fCachedUnitNames[sUnitName]:=SubPath;
// Map real unit name by uppercase unit name.
fCachedRealUnitNames[UpperCase(sUnitName)]:=sUnitName;
end;
end;
end;
@ -1186,7 +1257,14 @@ end;
function TConvertDelphiPBase.GetCachedUnitPath(const AUnitName: string): string;
begin
Result:=fCachedUnitNames.Values[UpperCase(AUnitName)];
Result:=fCachedUnitNames[AUnitName];
end;
function TConvertDelphiPBase.NeedsRenameUnit(const AUnitName: string;
out RealUnitName: string): Boolean;
begin
RealUnitName:=fCachedRealUnitNames[UpperCase(AUnitName)];
Result := (RealUnitName<>'') and (RealUnitName<>AUnitName);
end;
function TConvertDelphiPBase.CreateMainSourceFile: TModalResult;
@ -1407,7 +1485,7 @@ begin
Result:=Converter.CheckFailed(Result);
if Result<>mrOK then Break;
Result:=Converter.ConvertUnitFile;
Result:=Converter.CheckFailed(Result);
// Result:=Converter.CheckFailed(Result);
if Result<>mrOK then Break;
end;
end;