From 55c0c6fd08223719edbba32c36d75c41794e6932 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 16 Nov 2023 11:47:35 +0700 Subject: [PATCH 1/2] fix(developer): projects 2.0 internal path enumeration Restricts enumeration of files for the project to the project folder and the SourcePath folder. This prevents problems where a project may be in a folder with many subfolders which would take a long time to enumerate, and avoids confusion where there are source-type files in other folders. At the same time, sorts out forward slash vs backslash in paths. While forward slash works in many scenarios, there are several filename manipulation functions, such as ExpandFileName, which would build valid but non-optimal paths when forward slashes were encountered, which cascaded into files appearing to be different and presentation issues. --- common/windows/delphi/general/utildir.pas | 8 +++++- ...n.Developer.System.Project.ProjectFile.pas | 28 ++++++++----------- ...Developer.System.Project.ProjectLoader.pas | 5 ++-- ...loper.UI.Project.UfrmProjectSettings20.pas | 23 +++++++-------- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/common/windows/delphi/general/utildir.pas b/common/windows/delphi/general/utildir.pas index 727b9f30dcb..b0f22f56578 100644 --- a/common/windows/delphi/general/utildir.pas +++ b/common/windows/delphi/general/utildir.pas @@ -45,13 +45,19 @@ function KGetTempPath: string; function GetLongFileName(const fname: string): string; +function DosSlashes(const filename: string): string; + implementation uses + System.StrUtils, System.SysUtils, Winapi.Windows; - +function DosSlashes(const filename: string): string; +begin + Result := ReplaceStr(filename, '/', '\'); +end; function DirectoryEmpty(dir: WideString): Boolean; var diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFile.pas b/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFile.pas index e5ddbf9e316..a5f55a04709 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFile.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFile.pas @@ -134,8 +134,8 @@ TProjectOptions = class ProjectType: ptKeyboard; Version: pv10 ), ( // 2.0 - BuildPath: '$PROJECTPATH/build'; - SourcePath: '$PROJECTPATH/source'; + BuildPath: '$PROJECTPATH\build'; + SourcePath: '$PROJECTPATH\source'; CompilerWarningsAsErrors: False; WarnDeprecatedCode: True; CheckFilenameConventions: False; @@ -621,8 +621,8 @@ function TProjectFile.IsSourceFile: Boolean; Exit(True); // Only return true if the file is directly in the ProjectOptions.SourcePath folder - SourcePath := ReplaceStr(IncludeTrailingPathDelimiter(FProject.ResolveProjectPath(FProject.Options.SourcePath)), '/', '\'); - FilePath := ReplaceStr(ExtractFilePath(FFileName), '/', '\'); + SourcePath := DosSlashes(FProject.ResolveProjectPath(FProject.Options.SourcePath)); + FilePath := DosSlashes(ExtractFilePath(FFileName)); Result := SameFileName(SourcePath, FilePath); end; @@ -662,7 +662,7 @@ procedure TProjectFile.Save(node: IXMLNode); // I4698 begin node.AddChild('ID').NodeValue := FID; node.AddChild('Filename').NodeValue := ExtractFileName(FFileName); - node.AddChild('Filepath').NodeValue := ExtractRelativePath(FProject.FileName, FFileName); + node.AddChild('Filepath').NodeValue := ExtractRelativePath(FProject.FileName, DosSlashes(FFileName)); node.AddChild('FileVersion').NodeValue := FFileVersion; // I4701 // Note: FileType is only ever written in Delphi code; it is used by xsl @@ -978,18 +978,21 @@ function TProject.IsDefaultProject(Version: TProjectVersion): Boolean; /// function TProject.PopulateFiles: Boolean; var - ProjectPath: string; + SourcePath, ProjectPath: string; begin if FOptions.Version <> pv20 then raise EProjectLoader.Create('PopulateFiles can only be called on a v2.0 project'); FFiles.Clear; - ProjectPath := ExtractFilePath(FileName); + ProjectPath := ExpandFileName(ExtractFilePath(FileName)); if not DirectoryExists(ProjectPath) then Exit(False); PopulateFolder(ProjectPath); + SourcePath := ResolveProjectPath(FOptions.SourcePath); + if not SameFileName(ProjectPath, SourcePath) and DirectoryExists(SourcePath) then + PopulateFolder(SourcePath); Result := True; end; @@ -999,7 +1002,7 @@ procedure TProject.PopulateFolder(const path: string); ff: string; f: TSearchRec; begin - if FindFirst(path + '*', faDirectory, f) = 0 then + if FindFirst(path + '*', 0, f) = 0 then begin repeat ff := path + f.Name; @@ -1009,12 +1012,6 @@ procedure TProject.PopulateFolder(const path: string); Continue; end; - if (f.Attr and faDirectory) = faDirectory then - begin - PopulateFolder(ff + '\'); - Continue; - end; - CreateProjectFile(Self, ff, nil); until FindNext(f) <> 0; System.SysUtils.FindClose(f); @@ -1231,7 +1228,7 @@ function TProject.GetUserFileName: string; function TProject.ResolveProjectPath(APath: string): string; begin - Result := ReplaceText(APath, '$PROJECTPATH', ExtractFileDir(ExpandFileName(FFileName))); + Result := IncludeTrailingPathDelimiter(ReplaceText(APath, '$PROJECTPATH', ExtractFileDir(ExpandFileName(FFileName)))); end; function TProject.GetTargetFilename10(ATargetFile, ASourceFile, AVersion: string): string; // I4688 @@ -1256,7 +1253,6 @@ function TProject.GetTargetFilename20(ATargetFile, ASourceFile, AVersion: string Exit(ExtractFilePath(ExpandFileName(ASourceFile)) + ExtractFileName(ATargetFile)); end; - Result := IncludeTrailingPathDelimiter(Result); Result := ResolveProjectPath(Result); Result := Result + ExtractFileName(ATargetFile); end; diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.ProjectLoader.pas b/developer/src/tike/project/Keyman.Developer.System.Project.ProjectLoader.pas index c68339ab861..1c8d0c425da 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.ProjectLoader.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.ProjectLoader.pas @@ -66,6 +66,7 @@ implementation Keyman.Developer.System.Project.ProjectFiles, Keyman.Developer.System.Project.ProjectFileType, + utildir, utilfiletypes; { TProjectLoader } @@ -131,10 +132,10 @@ procedure TProjectLoader.LoadProjectFromFile; FProject.Options.Assign(DefaultProjectOptions[FProject.Options.Version]); if not VarIsNull(node.ChildValues['BuildPath']) then - FProject.Options.BuildPath := VarToStr(node.ChildValues['BuildPath']); + FProject.Options.BuildPath := DosSlashes(VarToStr(node.ChildValues['BuildPath'])); if not VarIsNull(node.ChildValues['SourcePath']) then - FProject.Options.SourcePath := VarToStr(node.ChildValues['SourcePath']); + FProject.Options.SourcePath := DosSlashes(VarToStr(node.ChildValues['SourcePath'])); if not VarIsNull(node.ChildValues['CompilerWarningsAsErrors']) then FProject.Options.CompilerWarningsAsErrors := node.ChildValues['CompilerWarningsAsErrors']; diff --git a/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProjectSettings20.pas b/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProjectSettings20.pas index 2bda4a3bb66..78c945a6935 100644 --- a/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProjectSettings20.pas +++ b/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProjectSettings20.pas @@ -1,22 +1,22 @@ (* Name: Keyman.Developer.UI.Project.UfrmProjectSettings Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 4 May 2015 Modified Date: 24 Aug 2015 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 04 May 2015 - mcdurdin - I4688 - V9.0 - Add build path to project settings 24 Aug 2015 - mcdurdin - I4865 - Add treat hints and warnings as errors into project 24 Aug 2015 - mcdurdin - I4866 - Add warn on deprecated features to project and compile - + *) unit Keyman.Developer.UI.Project.UfrmProjectSettings20; // I4688 @@ -54,12 +54,13 @@ implementation {$R *.dfm} uses - Keyman.Developer.System.Project.Project; + Keyman.Developer.System.Project.Project, + utildir; procedure TfrmProjectSettings20.cmdOKClick(Sender: TObject); begin - FGlobalProject.Options.BuildPath := Trim(editOutputPath.Text); - FGlobalProject.Options.SourcePath := Trim(editSourcePath.Text); + FGlobalProject.Options.BuildPath := Trim(DosSlashes(editOutputPath.Text)); + FGlobalProject.Options.SourcePath := Trim(DosSlashes(editSourcePath.Text)); FGlobalProject.Options.SkipMetadataFiles := not chkBuildMetadataFiles.Checked; FGlobalProject.Options.CompilerWarningsAsErrors := chkCompilerWarningsAsErrors.Checked; // I4865 FGlobalProject.Options.WarnDeprecatedCode := chkWarnDeprecatedCode.Checked; // I4866 From 38f9b29cc985319b9e5aee3bffb50c23a83fba50 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 16 Nov 2023 11:50:54 +0700 Subject: [PATCH 2/2] fix(developer): show relative path in Distribution tab For the Project view, the Distribution tab now shows file relative paths, which helps with organization. Have opted _not_ to show the relative paths in the other tabs, because that information is visible when the file details are expanded, and because those files should always be in SourcePath anyway. --- developer/src/tike/xml/project/distribution.xsl | 1 + developer/src/tike/xml/project/elements.xsl | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/developer/src/tike/xml/project/distribution.xsl b/developer/src/tike/xml/project/distribution.xsl index 840a3287c06..d30403e84a9 100644 --- a/developer/src/tike/xml/project/distribution.xsl +++ b/developer/src/tike/xml/project/distribution.xsl @@ -83,6 +83,7 @@ false + true diff --git a/developer/src/tike/xml/project/elements.xsl b/developer/src/tike/xml/project/elements.xsl index 0bcf796cc29..ba5a41fdee6 100644 --- a/developer/src/tike/xml/project/elements.xsl +++ b/developer/src/tike/xml/project/elements.xsl @@ -76,6 +76,7 @@ + file @@ -103,7 +104,10 @@