mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-16 05:39:24 +00:00
145 lines
5.8 KiB
ObjectPascal
145 lines
5.8 KiB
ObjectPascal
unit Keyman.System.LexicalModelUtils;
|
|
|
|
interface
|
|
|
|
type
|
|
TLexicalModelUtils = class
|
|
private
|
|
public
|
|
const SPackageNameDoesNotFollowLexicalModelConventions_Message: string =
|
|
'The package file %0:s does not follow the recommended model filename conventions. The name should be all lower case, '+
|
|
'include only alphanumeric characters and underscore (_), and should have the structure <author>.<bcp47>.<uniq>.model.kps';
|
|
|
|
const SModelFileNameDoesNotFollowConventions_Message: string =
|
|
'The model source file %0:s does not follow the recommended model filename conventions. The name should be all lower case, '+
|
|
'include only alphanumeric characters and underscore (_), and should have the structure <author>.<bcp47>.<uniq>.model.ts';
|
|
|
|
const SProjectFileNameDoesNotFollowLexicalModelConventions_Message: string =
|
|
'The model project file %0:s does not follow the recommended model filename conventions. The name should be all lower case, '+
|
|
'include only alphanumeric characters and underscore (_), and should have the structure <author>.<bcp47>.<uniq>.model.kpj';
|
|
|
|
class function LexicalModelFileNameToID(filename: string): string;
|
|
class function LexicalModelIDToFileName(id: string): string;
|
|
class function DoesPackageFilenameFollowLexicalModelConventions(
|
|
const Name: string): Boolean; static;
|
|
class function DoesJSFilenameFollowLexicalModelConventions(
|
|
const Name: string): Boolean; static;
|
|
class function DoesTSFilenameFollowLexicalModelConventions(
|
|
const Name: string): Boolean; static;
|
|
class function DoesProjectFilenameFollowLexicalModelConventions(
|
|
const Name: string): Boolean; static;
|
|
class function CleanLexicalModelIDComponent(const Name: string): string; static;
|
|
class function IsCleanLexicalModelIDComponent(const Name: string): Boolean; static;
|
|
|
|
class function ExtractFileExt(filename: string): string; static;
|
|
class function RemoveFileExt(filename: string): string; static;
|
|
class function ChangeFileExt(filename, ext: string): string; static;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.RegularExpressions,
|
|
System.SysUtils,
|
|
|
|
utilfiletypes;
|
|
|
|
{ TLexicalModelUtils }
|
|
|
|
const
|
|
SLexicalModelExtension = '.model.js';
|
|
|
|
// The model ID SHOULD adhere to this pattern (see also developer/src/kmlmc/index.ts):
|
|
// author .bcp47 .uniq
|
|
MODEL_ID_PATTERN_JS = '^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.js$';
|
|
MODEL_ID_PATTERN_TS = '^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.ts$';
|
|
MODEL_ID_PATTERN_PACKAGE = '^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.(kps|kmp)$';
|
|
MODEL_ID_PATTERN_PROJECT = '^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.kpj$';
|
|
|
|
class function TLexicalModelUtils.DoesJSFilenameFollowLexicalModelConventions(
|
|
const Name: string): Boolean;
|
|
begin
|
|
Result := TRegEx.IsMatch(ExtractFileName(Name), MODEL_ID_PATTERN_JS);
|
|
end;
|
|
|
|
class function TLexicalModelUtils.DoesTSFilenameFollowLexicalModelConventions(
|
|
const Name: string): Boolean;
|
|
begin
|
|
Result := TRegEx.IsMatch(ExtractFileName(Name), MODEL_ID_PATTERN_TS);
|
|
end;
|
|
|
|
class function TLexicalModelUtils.DoesPackageFilenameFollowLexicalModelConventions(
|
|
const Name: string): Boolean;
|
|
// The model ID SHOULD adhere to this pattern (see also developer/src/kmlmc/index.ts):
|
|
// author .bcp47 .uniq
|
|
begin
|
|
Result := TRegEx.IsMatch(ExtractFileName(Name), MODEL_ID_PATTERN_PACKAGE);
|
|
end;
|
|
|
|
class function TLexicalModelUtils.DoesProjectFilenameFollowLexicalModelConventions(
|
|
const Name: string): Boolean;
|
|
begin
|
|
Result := TRegEx.IsMatch(ExtractFileName(Name), MODEL_ID_PATTERN_PROJECT);
|
|
end;
|
|
|
|
class function TLexicalModelUtils.LexicalModelFileNameToID(filename: string): string;
|
|
begin
|
|
Result := ExtractFileName(filename);
|
|
if Result.EndsWith(SLexicalModelExtension) then
|
|
Result := Result.Remove(Result.Length - SLexicalModelExtension.Length);
|
|
end;
|
|
|
|
class function TLexicalModelUtils.LexicalModelIDToFileName(id: string): string;
|
|
begin
|
|
Result := id + SLexicalModelExtension;
|
|
end;
|
|
|
|
// Note: This isn't ideal. The dotted a.b.c.model.blah filename convention makes it impossible to
|
|
// generically extract an extension. This is the best we can do, adding special handling for specific
|
|
// file extensions
|
|
|
|
class function TLexicalModelUtils.RemoveFileExt(filename: string): string;
|
|
begin
|
|
Result := Copy(filename, 1, Length(filename) - Length(TLexicalModelUtils.ExtractFileExt(filename)));
|
|
end;
|
|
|
|
class function TLexicalModelUtils.ChangeFileExt(filename, ext: string): string;
|
|
begin
|
|
Result := Copy(filename, 1, Length(filename) - Length(TLexicalModelUtils.ExtractFileExt(filename))) + ext;
|
|
end;
|
|
|
|
class function TLexicalModelUtils.ExtractFileExt(filename: string): string;
|
|
var
|
|
Second: string;
|
|
begin
|
|
Result := System.SysUtils.ExtractFileExt(filename);
|
|
if Result = '' then
|
|
Exit;
|
|
Second := System.SysUtils.ExtractFileExt(Copy(filename, 1, Length(filename)-Length(Result)));
|
|
if (SameText(Result, Ext_LexicalModelSource) or
|
|
SameText(Result, Ext_PackageSource) or
|
|
SameText(Result, Ext_ProjectSource)) and
|
|
SameText(Second, Ext_Model_Component) then
|
|
Result := Second + Result;
|
|
end;
|
|
|
|
class function TLexicalModelUtils.IsCleanLexicalModelIDComponent(const Name: string): Boolean;
|
|
begin
|
|
Result := Name = CleanLexicalModelIDComponent(Name);
|
|
end;
|
|
|
|
class function TLexicalModelUtils.CleanLexicalModelIDComponent(const Name: string): string;
|
|
var
|
|
i: Integer;
|
|
const
|
|
ValidChars: string = 'abcdefghijklmnopqrstuvwxyz0123456789_';
|
|
begin
|
|
// Cloned from TKeyboardUtils.CleanKeyboardID
|
|
Result := LowerCase(Name);
|
|
if Length(Result) = 0 then Exit;
|
|
if Pos(Result[1], '0123456789') > 0 then Result := '_'+Result; // Can't have a number as initial
|
|
for i := 1 to Length(Result) do
|
|
if Pos(Result[i], ValidChars) = 0 then Result[i] := '_';
|
|
end;
|
|
|
|
end.
|