Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(developer): Support loading XML LDML keyboards in TIKE 🦕 #9963

Merged
merged 7 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/web/types/src/kpj/keyman-developer-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class KeymanDeveloperProject {
for(let filename of files) {
let fullPath = this.callbacks.path.join(sourcePath, filename);
if(KeymanFileTypes.filenameIs(filename, KeymanFileTypes.Source.LdmlKeyboard)) {
if(!this.callbacks.fs.readFileSync(fullPath, 'utf-8').match(/ldmlKeyboard\.dtd/)) {
if(!this.callbacks.fs.readFileSync(fullPath, 'utf-8').match(/ldmlKeyboard3\.dtd/)) {
// Skip this .xml because we assume it isn't really a keyboard .xml
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion common/web/types/test/fixtures/invalid-conforms-to.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
DOCTYPE keyboard SYSTEM "../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard.dtd"
DOCTYPE keyboard SYSTEM "../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.dtd"
Disabling doctype for this invalid file, not used by compiler, and avoids complaints in IDEs etc.
-->
<keyboard3 locale="mt" conformsTo="nothing-anyone-ever-heard-of"> <!-- invalid -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ procedure TAppHttpResponder.RespondProject(doc: string; AContext: TIdContext;

path := CrackUTF8ZeroExtendedString(ARequestInfo.CommandType, ARequestInfo.Params.Values['path']);

if (Path <> '') and (not FileExists(path) or not SameText(ExtractFileExt(path), Ext_ProjectSource)) then
if (Path <> '') and (not DirectoryExists(ExtractFileDir(path)) or not SameText(ExtractFileExt(path), Ext_ProjectSource)) then
begin
AResponseInfo.ResponseNo := 404;
AResponseInfo.ResponseText := 'Project file '+path+' does not exist.';
Expand Down Expand Up @@ -191,7 +191,7 @@ procedure TAppHttpResponder.RespondProject(doc: string; AContext: TIdContext;

// Saving state

if (Path <> '') and (not FileExists(path) or not SameText(ExtractFileExt(path), Ext_ProjectSource)) then
if (Path <> '') and (not DirectoryExists(ExtractFileDir(path)) or not SameText(ExtractFileExt(path), Ext_ProjectSource)) then
begin
AResponseInfo.ResponseNo := 404;
AResponseInfo.ResponseText := 'Project file '+path+' does not exist.';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,12 @@ TProjectFile = class
constructor Create(AProject: TProject; AFileName: string; AParent: TProjectFile); virtual;
destructor Destroy; override;

procedure Load(node: IXMLNode; LoadState: Boolean); virtual; // I4698
function IsCompilable: Boolean; virtual;
class function IsFileTypeSupported(const Filename: string): Boolean; virtual;

procedure Load(node: IXMLNode); virtual; // I4698
procedure LoadState(node: IXMLNode); virtual; // I4698
procedure Save(node: IXMLNode; SaveState: Boolean); virtual; // I4698
procedure Save(node: IXMLNode); virtual; // I4698
procedure SaveState(node: IXMLNode); virtual; // I4698

procedure AddFreeNotification(AClient: IProjectFileFreeNotification);
Expand Down Expand Up @@ -571,25 +574,27 @@ function TProjectFile.GetOwnerProject: TProject;
end;
end;

procedure TProjectFile.Load(node: IXMLNode; LoadState: Boolean); // I4698
function TProjectFile.IsCompilable: Boolean;
begin
Result := False;
end;

class function TProjectFile.IsFileTypeSupported(
const Filename: string): Boolean;
begin
// assumes that if we are registered for the file type extension, then we can
// handle the file. For example, .xml LDML keyboards
Result := True;
end;

procedure TProjectFile.Load(node: IXMLNode); // I4698
var
i: Integer;
begin
if node.ChildNodes.IndexOf('ID') >= 0 then
FID := CleanID(VarToWideStr(node.ChildValues['ID']));
if node.ChildNodes.IndexOf('ParentFileID') >= 0 then
FParentFileID := CleanID(VarToWideStr(node.ChildValues['ParentFileID']));

if LoadState then
begin
FIDEState.Clear;
if node.ChildNodes.IndexOf('IDEState') >= 0 then
begin
node := node.ChildNodes.Nodes['IDEState'];
for i := 0 to node.ChildNodes.Count - 1 do
FIDEState[node.ChildNodes[i].NodeName] := node.ChildNodes[i].NodeValue;
end;
end;
end;

procedure TProjectFile.LoadState(node: IXMLNode); // I4698
Expand All @@ -616,7 +621,7 @@ procedure TProjectFile.RemoveFreeNotification(
FNotifiers.Remove(AClient);
end;

procedure TProjectFile.Save(node: IXMLNode; SaveState: Boolean); // I4698
procedure TProjectFile.Save(node: IXMLNode); // I4698
var
I: Integer;
begin
Expand All @@ -627,21 +632,6 @@ procedure TProjectFile.Save(node: IXMLNode; SaveState: Boolean); // I4698
node.AddChild('FileType').NodeValue := ExtractFileExt(FFileName);;
if Assigned(FParent) then
node.AddChild('ParentFileID').NodeValue := FParent.ID;

if SaveState then
begin
node.AddChild('FullPath').NodeValue := FFileName;

if FIDEState.Count > 0 then
begin
node := node.AddChild('IDEState');
for I := 0 to FIDEState.Count - 1 do
begin
with node.AddChild(TProjectFileState(FIDEState.Get(I)).Name) do
NodeValue := TProjectFileState(FIDEState.Get(I)).Value;
end;
end;
end;
end;

procedure TProjectFile.SaveState(node: IXMLNode); // I4698
Expand Down Expand Up @@ -995,7 +985,7 @@ procedure TProject.PopulateFolder(const path: string);
function IsLMDLKeyboardFile(filename: string): Boolean;
if EndsText('.xml', filename) then
begin
Result := Pos('ldmlKeyboard.dtd', ReadUtf8FileText(ff)) > 0;
Result := Pos('ldmlKeyboard3.dtd', ReadUtf8FileText(ff)) > 0;
end
else
Result := False;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ function CreateProjectFile(AProject: TProject; AFileName: string; AParent: TProj
ni := -1;
for i := 0 to FRegisteredFileTypes.Count - 1 do
if FRegisteredFileTypes[i].Extension = '*' then ni := i
else if FRegisteredFileTypes[i].Extension = Ext then
else if (FRegisteredFileTypes[i].Extension = Ext) and
FRegisteredFileTypes[i].ProjectFileClass.IsFileTypeSupported(AFileName) then
begin
Result := FRegisteredFileTypes[i].ProjectFileClass.Create(AProject, AFileName, AParent);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ procedure TProjectLoader.LoadProjectFromFile;
doc: IXMLDocument;
node, root: IXMLNode;
pf: TProjectFile;
hasUserState: Boolean;
begin
try
doc := LoadXMLDocument(FFileName);
Expand All @@ -108,6 +109,8 @@ procedure TProjectLoader.LoadProjectFromFile;
raise EProjectLoader.Create('Error loading project file: '+E.Message);
end;

hasUserState := FileExists(ChangeFileExt(FFileName, Ext_ProjectSourceUser));

root := doc.DocumentElement;
if root.NodeName <> 'KeymanDeveloperProject' then
raise EProjectLoader.Create('Not a Keyman Developer project file');
Expand Down Expand Up @@ -164,7 +167,9 @@ procedure TProjectLoader.LoadProjectFromFile;
begin
// I1152 - Avoid crashes when .kpj file is invalid
pf := CreateProjectFile(FProject, ExpandFileNameClean(FFileName, node.ChildValues['Filepath']), nil);
pf.Load(node, True);
pf.Load(node);
if not hasUserState then
pf.LoadState(node);
end;
end;
end;
Expand All @@ -181,7 +186,9 @@ procedure TProjectLoader.LoadProjectFromFile;
n := FProject.Files.IndexOfID(node.ChildValues['ParentFileID']);
if n < 0 then Continue;
pf := CreateProjectFile(FProject, ExpandFileNameClean(FFileName, node.ChildValues['Filepath']), FProject.Files[n]);
pf.Load(node, True);
pf.Load(node);
if not hasUserState then
pf.LoadState(node);
end;
end;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ procedure TProjectSaver.Execute; // I4698
node, root: IXMLNode;
defopts: TProjectOptionsRecord;
begin
if FProject.IsDefaultProject(pv20) then
if FProject.IsDefaultProject(pv20) and (FFileName <> '') then
begin
if FileExists(FFileName) then
System.SysUtils.DeleteFile(FFileName);
Expand Down Expand Up @@ -136,7 +136,7 @@ procedure TProjectSaver.Execute; // I4698
begin
node := root.AddChild('Files');
for i := 0 to FProject.Files.Count - 1 do
FProject.Files[i].Save(node.AddChild('File'), False);
FProject.Files[i].Save(node.AddChild('File'));
end;

if FFileName <> '' then
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
(*
Name: Keyman.Developer.System.Project.kmnProjectFile
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 1 Aug 2006

Modified Date: 24 Aug 2015
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:

Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 01 Aug 2006 - mcdurdin - Add loading and saving from XML
23 Aug 2006 - mcdurdin - Add CompileKeyboardToWeb function
28 Sep 2006 - mcdurdin - Editions
Expand Down Expand Up @@ -47,7 +47,7 @@
03 Aug 2015 - mcdurdin - I4823 - Note in compile log if symbols are included in build
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.System.Project.kmnProjectFile; // I3306 // I4687 // I4688 // I4692

Expand All @@ -73,7 +73,6 @@ TkmnProjectFile = class(TOpenableProjectFile)
FHeader_Copyright: WideString;
FTargets: TKeymanTargets;
FKVKFileName: string;
FWarnAsError: Boolean; // I4706

function GetOutputFilename: string;
function GetTargetFilename: string;
Expand All @@ -85,13 +84,13 @@ TkmnProjectFile = class(TOpenableProjectFile)
property KVKFileName: string read FKVKFileName;
property IsDebug: Boolean read FDebug;
public
procedure Load(node: IXMLNode; LoadState: Boolean); override; // I4698
procedure Save(node: IXMLNode; SaveState: Boolean); override; // I4698
function IsCompilable: Boolean; override;
procedure Load(node: IXMLNode); override; // I4698
procedure Save(node: IXMLNode); override; // I4698
procedure LoadState(node: IXMLNode); override; // I4698
procedure SaveState(node: IXMLNode); override; // I4698

property Debug: Boolean read FDebug write FDebug;
property WarnAsError: Boolean read FWarnAsError write FWarnAsError; // I4706

property OutputFilename: string read GetOutputFilename;
property TargetFilename: string read GetTargetFilename;
Expand Down Expand Up @@ -119,15 +118,13 @@ implementation
- TkmnProjectFile -
-------------------------------------------------------------------------------}

procedure TkmnProjectFile.Save(node: IXMLNode; SaveState: Boolean); // I4698
procedure TkmnProjectFile.Save(node: IXMLNode); // I4698
begin
inherited Save(node, SaveState); // I4698
inherited Save(node); // I4698
node := node.AddChild('Details');
if FHeader_Name <> '' then node.AddChild('Name').NodeValue := FHeader_Name;
if FHeader_Copyright <> '' then node.AddChild('Copyright').NodeValue := FHeader_Copyright;
if FHeader_Message <> '' then node.AddChild('Message').NodeValue := FHeader_Message;

if SaveState then node.AddChild('Debug').NodeValue := FDebug; // I4698
end;

procedure TkmnProjectFile.SaveState(node: IXMLNode); // I4698
Expand All @@ -136,22 +133,15 @@ procedure TkmnProjectFile.SaveState(node: IXMLNode); // I4698
node.AddChild('Debug').NodeValue := FDebug;
end;

procedure TkmnProjectFile.Load(node: IXMLNode; LoadState: Boolean); // I4698
procedure TkmnProjectFile.Load(node: IXMLNode); // I4698
begin
inherited Load(node, LoadState);
inherited Load(node);

if node.ChildNodes.IndexOf('Details') < 0 then Exit;
node := node.ChildNodes['Details'];
if node.ChildNodes.IndexOf('Name') >= 0 then FHeader_Name := VarToWideStr(node.ChildValues['Name']);
if node.ChildNodes.IndexOf('Copyright') >= 0 then FHeader_Copyright := VarToWideStr(node.ChildValues['Copyright']);
if node.ChildNodes.IndexOf('Message') >= 0 then FHeader_Message := VarToWideStr(node.ChildValues['Message']);

if LoadState then
try
if node.ChildNodes.IndexOf('Debug') >= 0 then FDebug := node.ChildValues['Debug'];
except
FDebug := False;
end;
end;

procedure TkmnProjectFile.LoadState(node: IXMLNode); // I4698
Expand Down Expand Up @@ -181,6 +171,11 @@ function TkmnProjectFile.GetTargetFilename: string;
Result := OwnerProject.GetTargetFilename(OutputFileName, FileName, FTempFileVersion);
end;

function TkmnProjectFile.IsCompilable: Boolean;
begin
Result := True;
end;

procedure TkmnProjectFile.GetFileParameters;
var
j: Integer;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
(*
Name: Keyman.Developer.System.Project.kpsProjectFile
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 20 Jun 2006

Modified Date: 6 Jun 2015
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:

Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 20 Jun 2006 - mcdurdin - Initial version
01 Aug 2006 - mcdurdin - Add loading and saving from XML
23 Aug 2006 - mcdurdin - Refactor to use actions
Expand Down Expand Up @@ -54,18 +54,16 @@ TkpsProjectFile = class(TOpenableProjectFile)
FHeader_Copyright: string;
FHeader_Version: string;
FMSIFileName: WideString;
FWarnAsError: Boolean; // I4706
function GetOutputFilename: string;
function GetTargetFilename: string;
function GetTargetInstallerFilename: string;
protected
function GetRelativeOrder: Integer; override;
procedure GetFileParameters; override;
public
procedure Load(node: IXMLNode; LoadState: Boolean); override; // I4698
procedure Save(node: IXMLNode; SaveState: Boolean); override; // I4698

property WarnAsError: Boolean read FWarnAsError write FWarnAsError; // I4706
function IsCompilable: Boolean; override;
procedure Load(node: IXMLNode); override; // I4698
procedure Save(node: IXMLNode); override; // I4698

property OutputFilename: string read GetOutputFilename;
property TargetFilename: string read GetTargetFilename;
Expand Down Expand Up @@ -163,9 +161,14 @@ function TkpsProjectFile.GetTargetInstallerFilename: string;
Result := OwnerProject.GetTargetFilename(ChangeFileExt(ExtractFileName(FMSIFileName),'') + '-' + ChangeFileExt(ExtractFileName(OutputFilename), '') + '.exe', Filename, FileVersion);
end;

procedure TkpsProjectFile.Load(node: IXMLNode; LoadState: Boolean); // I4698
function TkpsProjectFile.IsCompilable: Boolean;
begin
Result := True;
end;

procedure TkpsProjectFile.Load(node: IXMLNode); // I4698
begin
inherited Load(node, LoadState);
inherited Load(node);

if node.ChildNodes.IndexOf('Details') < 0 then Exit;
node := node.ChildNodes['Details'];
Expand All @@ -174,9 +177,9 @@ procedure TkpsProjectFile.Load(node: IXMLNode; LoadState: Boolean); // I4698
if node.ChildNodes.IndexOf('Version') >= 0 then FHeader_Version := VarToWideStr(node.ChildValues['Version']);
end;

procedure TkpsProjectFile.Save(node: IXMLNode; SaveState: Boolean); // I4698
procedure TkpsProjectFile.Save(node: IXMLNode); // I4698
begin
inherited Save(node, SaveState);
inherited Save(node);
node := node.AddChild('Details');
if FHeader_Name <> '' then node.AddChild('Name').NodeValue := FHeader_Name;
if FHeader_Copyright <> '' then node.AddChild('Copyright').NodeValue := FHeader_Copyright;
Expand Down
Loading