chore(developer): Merge branch 'master' into fix/developer/refactor-kmcmplib-compiler-messages-to-use-map

This commit is contained in:
Dr Mark C. Sinclair 2024-06-08 15:08:35 +01:00
commit d7e071f66d
29 changed files with 701 additions and 174 deletions

View file

@ -1,5 +1,21 @@
# Keyman Version History
## 18.0.51 alpha 2024-06-07
* fix(web): fix osk touch-focus tracking (#11705)
* fix(web): defer keyboard activation requests made during engine initialization (#11713)
* chore(developer): add context/character-map (#11656)
* chore(developer): add context/wordlist-editor (#11658)
* chore(developer): add context/new-model-project-parameters (#11677)
* fix(common): remove allowJs from web's tsconfig.base.json (#11718)
* change(web): precompile all TS-based tests (#11723)
* chore(developer): add extra logging for assertion failure when pressing backspace in debugger (#11707)
* chore: add cherry-pick information in commit messages (#11708)
* fix(developer): handle encoding errors when loading wordlists (#11711)
* chore(ios): remove dead Swift-side keyboard gesture code (#11672)
* fix(mac): change build configuration to prevent cycle error in Xcode 15 (#11730)
* refactor(web): Replace deprecated substr with substring (#11637)
## 18.0.50 alpha 2024-06-06
* chore(common): adds retry mechanism for build script npm ci calls (#11451)

View file

@ -1 +1 @@
18.0.51
18.0.52

View file

@ -2,6 +2,9 @@ import unwrap from '../unwrap.js';
import { LMLayerWorkerCode, LMLayerWorkerSourcemapComment } from "@keymanapp/lm-worker/worker-main.wrapped.js";
export default class SourcemappedWorker {
// the only difference to DefaultWorker is that this class uses
// the unminified LM* blobs
static constructInstance(): Worker {
return new Worker(this.asBlobURI(LMLayerWorkerCode));
}

View file

@ -13,7 +13,7 @@ project('keyman_core', 'cpp', 'c',
'b_vscrt=static_from_buildtype',
'warning_level=2',
'debug=true'],
meson_version: '>=0.57.0')
meson_version: '>=1.0')
# Import our standard compiler defines; this is copied from
# /resources/build/standard.meson.build by build.sh, because

View file

@ -21,7 +21,7 @@ if get_option('keyman_core_tests')
if get_option('default_library') != 'static'
ctypes_void_p_size = ['-c', 'import ctypes; print(ctypes.sizeof(ctypes.c_void_p))']
r = run_command(python, ctypes_void_p_size)
r = run_command(python, ctypes_void_p_size, check: true)
python_ctypes_compatible = r.stdout().to_int() == cpp_compiler.sizeof('void *')
if not python_ctypes_compatible
message('Python ctypes is incompatible with built shared object. Disabling some tests.')

View file

@ -0,0 +1,4 @@
@echo off
set infile=%1
set infileb=%infile:/=\%
type %infileb%

View file

@ -17,9 +17,9 @@ binary_tests = [
foreach kbd : binary_tests
configure_file(
command: copy_cmd + ['@INPUT@', '@OUTPUT@'],
input: kbd + '.kmn',
output: kbd + '.kmn'
output: kbd + '.kmn',
copy: true
)
configure_file(

View file

@ -89,10 +89,8 @@ kmc_root = meson.current_source_dir() / '../../../../developer/src/kmc/build/src
kmc_cmd = [node, '--enable-source-maps', kmc_root]
if build_machine.system() == 'windows'
copy_cmd = [find_program('cmd.exe', required: true), '/c', 'copy']
cat_cmd = [find_program('cmd.exe', required: true), '/c', 'type']
cat_cmd = [find_program(meson.current_source_dir() / 'cat.bat', required: true)]
else
copy_cmd = [find_program('cp', required: true)]
cat_cmd = [find_program('cat', required: true)]
endif
@ -127,7 +125,7 @@ foreach kbd : tests
kbd_src_path = common_test_keyboards_baseline / kbd + '.kmn'
content = run_command(
cat_cmd, files(kbd_src_path),
cat_cmd, files(kbd_src_path), check: true,
).stdout().strip()
cfg = configuration_data()

View file

@ -8,19 +8,13 @@ invalid_tests = [
'ik_000_null_invalid'
]
if build_machine.system() == 'windows'
copy_cmd = [find_program('cmd.exe', required: true), '/c', 'copy']
else
copy_cmd = [find_program('cp', required: true)]
endif
# Build all keyboards in output folder
foreach kbd : invalid_tests
configure_file(
command: copy_cmd + ['@INPUT@', '@OUTPUT@'],
input: kbd + '.xml',
output: kbd + '.xml'
output: kbd + '.xml',
copy: true
)
configure_file(

View file

@ -56,8 +56,8 @@ tests += tests_from_cldr
# Setup kmc
kmc_root = join_paths(meson.source_root(),'..','developer','src','kmc')
ldml_root = join_paths(meson.source_root(),'..','resources','standards-data','ldml-keyboards','45')
kmc_root = meson.global_source_root() / '../developer/src/kmc'
ldml_root = meson.global_source_root() / '../resources/standards-data/ldml-keyboards/45'
ldml_data = join_paths(ldml_root, '3.0')
ldml_testdata = join_paths(ldml_root, 'test')
kmc_cmd = [node, '--enable-source-maps', kmc_root]

View file

@ -22,12 +22,6 @@ invalid_tests = []
# Setup copying of source files, used in child subdir calls
if build_machine.system() == 'windows'
copy_cmd = [find_program('cmd.exe', required: true), '/c', 'copy']
else
copy_cmd = [find_program('cp', required: true)]
endif
if node.found()
# Note: if node is not available, we cannot build the keyboards; build.sh
# emits a warning that the ldml keyboard tests will be skipped

View file

@ -2,10 +2,10 @@ node = find_program('node', required: true)
common_test_files = [
meson.current_source_dir() / 'emscripten_filesystem.cpp',
meson.source_root() / '../common/include/test_color.cpp'
meson.global_source_root() / '../common/include/test_color.cpp'
]
hextobin_root = join_paths(meson.source_root(),'..','common','tools','hextobin','build','hextobin.js')
hextobin_root = meson.global_source_root() / '../common/tools/hextobin/build/hextobin.js'
hextobin_cmd = [node, hextobin_root]
subdir('json')

View file

@ -5,7 +5,7 @@
#
project('kmcmplib', 'cpp', 'c',
version: run_command(find_program('getversion.bat', 'getversion.sh')).stdout().strip(),
version: run_command(find_program('getversion.bat', 'getversion.sh'), check: true).stdout().strip(),
license: 'MIT',
default_options : ['buildtype=release',
'cpp_std=c++14',

View file

@ -1208,7 +1208,11 @@ int GetCompileTargetsFromTargetsStore(const KMX_WCHAR* store) {
}
KMX_BOOL IsValidKeyboardVersion(KMX_WCHAR *dpString) { // I4140
/* version format \d+(\.\d+)* e.g. 9.0.3, 1.0, 1.2.3.4, 6.2.1.4.6.4, blank is not allowed */
/**
version format: /^\d+(\.\d+)*$/
e.g. 9.0.3, 1.0, 1.2.3.4, 6.2.1.4.6.4, 11.22.3 are all ok;
empty string is not permitted; whitespace is not permitted
*/
do {
if (!iswdigit(*dpString)) {

View file

@ -105,7 +105,7 @@ if cpp_compiler.get_id() == 'emscripten'
cpp_args: defns,
include_directories: inc,
link_args: links + lib_links,
objects: lib.extract_all_objects(),
objects: lib.extract_all_objects(recursive: false),
dependencies: icuuc_dep)
if get_option('buildtype') == 'release'

View file

@ -2,4 +2,4 @@
/*.zip
/*.tgz
/packagecache
/googletest-1.14.0
/googletest-*

View file

@ -0,0 +1,460 @@
#include <gtest/gtest.h>
#include "..\include\kmcompx.h"
#include "..\include\kmcmplibapi.h"
#include "..\src\kmx_u16.h"
#include "..\src\compfile.h"
#include "..\src\CompMsg.h"
#include "..\..\common\include\kmn_compiler_errors.h"
#include "..\..\..\..\common\include\km_types.h"
#include "..\..\..\..\common\include\kmx_file.h"
PKMX_WCHAR strtowstr(PKMX_STR in);
PKMX_STR wstrtostr(PKMX_WCHAR in);
KMX_BOOL AddCompileError(KMX_DWORD msg);
KMX_DWORD ProcessBeginLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_DWORD ValidateMatchNomatchOutput(PKMX_WCHAR p);
KMX_BOOL IsValidKeyboardVersion(KMX_WCHAR *dpString);
KMX_DWORD GetXStringImpl(PKMX_WCHAR tstr, PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_WCHAR const * token,
PKMX_WCHAR output, int max, int offset, PKMX_WCHAR *newp, int isUnicode
);
KMX_DWORD GetRHS(PFILE_KEYBOARD fk, PKMX_WCHAR p, PKMX_WCHAR buf, int bufsize, int offset, int IsUnicode);
bool hasPreamble(std::u16string result);
extern kmcmp_CompilerMessageProc msgproc;
namespace kmcmp {
extern int nErrors;
extern int ErrChr;
extern int BeginLine[4];
KMX_BOOL AddCompileWarning(char* buf);
}
#define ERR_EXTRA_LIB_LEN 256
extern char ErrExtraLIB[ERR_EXTRA_LIB_LEN];
class CompilerTest : public testing::Test {
protected:
FILE_KEYBOARD fileKeyboard;
CompilerTest() {}
~CompilerTest() override {}
void SetUp() override {
initGlobals();
initFileKeyboard(fileKeyboard);
}
void TearDown() override {
deleteFileKeyboard(fileKeyboard);
}
void initGlobals() {
msgproc = NULL;
szText_stub[0] = '\0';
kmcmp::nErrors = 0;
kmcmp::ErrChr = 0;
ErrExtraLIB[0] = '\0';
kmcmp::BeginLine[BEGIN_ANSI] = -1;
kmcmp::BeginLine[BEGIN_UNICODE] = -1;
kmcmp::BeginLine[BEGIN_NEWCONTEXT] = -1;
kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = -1;
}
void initFileKeyboard(FILE_KEYBOARD &fk) {
fk.KeyboardID = 0;
fk.version = VERSION_90;
fk.dpStoreArray = nullptr;
fk.dpGroupArray = nullptr;
fk.cxStoreArray = 0;
fk.cxGroupArray = 0;
fk.StartGroup[0] = 0;
fk.StartGroup[1] = 0;
fk.dwHotKey = 0;
fk.szName[0] = u'\0';
fk.szLanguageName[0] = u'\0';
fk.szCopyright[0] = u'\0';
fk.szMessage[0] = u'\0';
fk.lpBitmap = nullptr;
fk.dwBitmapSize = 0;
fk.dwFlags = 0;
fk.currentGroup = 0;
fk.currentStore = 0;
fk.cxDeadKeyArray = 0;
fk.dpDeadKeyArray = nullptr;
fk.cxVKDictionary = 0;
fk.dpVKDictionary = nullptr;
fk.extra = nullptr;
}
void deleteFileKeyboard(FILE_KEYBOARD &fk) {
if (fk.dpStoreArray) { delete[] fk.dpStoreArray; }
if (fk.dpGroupArray) { delete[] fk.dpGroupArray; }
if (fk.lpBitmap) { delete fk.lpBitmap; }
if (fk.dpDeadKeyArray) { delete[] fk.dpDeadKeyArray; }
if (fk.dpVKDictionary) { delete fk.dpVKDictionary; }
if (fk.extra) { delete fk.extra; }
}
public:
static KMX_CHAR szText_stub[];
static int msgproc_true_stub(int line, uint32_t dwMsgCode, const char* szText, void* context) {
strcpy(szText_stub, szText);
return 1;
};
static int msgproc_false_stub(int line, uint32_t dwMsgCode, const char* szText, void* context) {
strcpy(szText_stub, szText);
return 0;
};
};
#define COMPILE_ERROR_MAX_LEN (SZMAX_ERRORTEXT + 1 + 280)
KMX_CHAR CompilerTest::szText_stub[COMPILE_ERROR_MAX_LEN];
TEST_F(CompilerTest, strtowstr_test) {
EXPECT_EQ(0, u16cmp(u"hello", strtowstr((PKMX_STR)"hello")));
EXPECT_EQ(0, u16cmp(u"", strtowstr((PKMX_STR)"")));
};
TEST_F(CompilerTest, wstrtostr_test) {
EXPECT_EQ(0, strcmp("hello", wstrtostr((PKMX_WCHAR)u"hello")));
EXPECT_EQ(0, strcmp("", wstrtostr((PKMX_WCHAR)u"")));
};
TEST_F(CompilerTest, AddCompileWarning_test) {
msgproc = msgproc_false_stub;
const char *const WARNING_TEXT = "warning";
EXPECT_EQ(0, kmcmp::nErrors);
EXPECT_FALSE(kmcmp::AddCompileWarning((PKMX_CHAR)WARNING_TEXT));
EXPECT_EQ(0, strcmp(WARNING_TEXT, szText_stub));
EXPECT_EQ(0, kmcmp::nErrors);
};
TEST_F(CompilerTest, AddCompileError_test) {
msgproc = msgproc_true_stub;
kmcmp::ErrChr = 0;
ErrExtraLIB[0] = '\0';
KMX_CHAR expected[COMPILE_ERROR_MAX_LEN];
// CERR_FATAL
EXPECT_EQ(0, kmcmp::nErrors);
EXPECT_EQ(CERR_FATAL, CERR_CannotCreateTempfile & CERR_FATAL);
EXPECT_TRUE(AddCompileError(CERR_CannotCreateTempfile));
EXPECT_EQ(0, strcmp(GetCompilerErrorString(CERR_CannotCreateTempfile), szText_stub));
EXPECT_EQ(1, kmcmp::nErrors);
// CERR_ERROR
EXPECT_EQ(CERR_ERROR, CERR_InvalidLayoutLine & CERR_ERROR);
EXPECT_FALSE(AddCompileError(CERR_InvalidLayoutLine));
EXPECT_EQ(0, strcmp(GetCompilerErrorString(CERR_InvalidLayoutLine), szText_stub));
EXPECT_EQ(2, kmcmp::nErrors);
// Unknown
const KMX_DWORD UNKNOWN_ERROR = 0x00004FFF; // top of range ERROR
EXPECT_EQ(CERR_ERROR, UNKNOWN_ERROR & CERR_ERROR);
EXPECT_FALSE(AddCompileError(UNKNOWN_ERROR));
sprintf(expected, "Unknown error %x", UNKNOWN_ERROR);
EXPECT_EQ(0, strcmp(expected, szText_stub));
EXPECT_EQ(3, kmcmp::nErrors);
// ErrChr
const int ERROR_CHAR_INDEX = 42;
kmcmp::ErrChr = ERROR_CHAR_INDEX ;
EXPECT_EQ(CERR_ERROR, CERR_InvalidLayoutLine & CERR_ERROR);
EXPECT_FALSE(AddCompileError(CERR_InvalidLayoutLine));
sprintf(expected, "%s character offset: %d", GetCompilerErrorString(CERR_InvalidLayoutLine), ERROR_CHAR_INDEX);
EXPECT_EQ(0, strcmp(expected, szText_stub));
kmcmp::ErrChr = 0;
EXPECT_EQ(4, kmcmp::nErrors);
// ErrExtraLIB
const char *const EXTRA_LIB_TEXT = " extra lib";
strcpy(ErrExtraLIB, EXTRA_LIB_TEXT);
EXPECT_EQ(CERR_ERROR, CERR_InvalidLayoutLine & CERR_ERROR);
EXPECT_FALSE(AddCompileError(CERR_InvalidLayoutLine));
sprintf(expected, "%s%s", GetCompilerErrorString(CERR_InvalidLayoutLine), EXTRA_LIB_TEXT);
EXPECT_EQ(0, strcmp(expected, szText_stub));
ErrExtraLIB[0] = '\0';
EXPECT_EQ(5, kmcmp::nErrors);
// msgproc returns FALSE
msgproc = msgproc_false_stub;
EXPECT_EQ(CERR_ERROR, CERR_InvalidLayoutLine & CERR_ERROR);
EXPECT_TRUE(AddCompileError(CERR_InvalidLayoutLine));
EXPECT_EQ(0, strcmp(GetCompilerErrorString(CERR_InvalidLayoutLine), szText_stub));
EXPECT_EQ(6, kmcmp::nErrors);
};
TEST_F(CompilerTest, ProcessBeginLine_test) {
KMX_WCHAR str[LINESIZE];
// CERR_NoTokensFound
str[0] = '\0';
EXPECT_EQ(CERR_NoTokensFound, ProcessBeginLine(&fileKeyboard, str));
// CERR_InvalidToken
u16cpy(str, u"abc >");
EXPECT_EQ(CERR_InvalidToken, ProcessBeginLine(&fileKeyboard, str));
// CERR_RepeatedBegin, BEGIN_UNICODE
kmcmp::BeginLine[BEGIN_UNICODE] = 0; // not -1
u16cpy(str, u" unicode>");
EXPECT_EQ(CERR_RepeatedBegin, ProcessBeginLine(&fileKeyboard, str));
kmcmp::BeginLine[BEGIN_UNICODE] = -1;
// CERR_RepeatedBegin, BEGIN_ANSI
kmcmp::BeginLine[BEGIN_ANSI] = 0; // not -1
u16cpy(str, u" ansi>");
EXPECT_EQ(CERR_RepeatedBegin, ProcessBeginLine(&fileKeyboard, str));
kmcmp::BeginLine[BEGIN_ANSI] = -1;
// CERR_RepeatedBegin, BEGIN_NEWCONTEXT
kmcmp::BeginLine[BEGIN_NEWCONTEXT] = 0; // not -1
u16cpy(str, u" newContext>");
EXPECT_EQ(CERR_RepeatedBegin, ProcessBeginLine(&fileKeyboard, str));
kmcmp::BeginLine[BEGIN_NEWCONTEXT] = -1;
// CERR_RepeatedBegin, BEGIN_POSTKEYSTROKE
kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = 0; // not -1
u16cpy(str, u" postKeystroke>");
EXPECT_EQ(CERR_RepeatedBegin, ProcessBeginLine(&fileKeyboard, str));
kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = -1;
};
TEST_F(CompilerTest, ValidateMatchNomatchOutput_test) {
EXPECT_EQ(CERR_None, ValidateMatchNomatchOutput(NULL));
EXPECT_EQ(CERR_None, ValidateMatchNomatchOutput((PKMX_WCHAR)u""));
const KMX_WCHAR context[] = { 'a', 'b', 'c', UC_SENTINEL, CODE_CONTEXT, 'd', 'e', 'f' };
EXPECT_EQ(CERR_ContextAndIndexInvalidInMatchNomatch, ValidateMatchNomatchOutput((PKMX_WCHAR)context));
const KMX_WCHAR contextex[] = { 'a', 'b', 'c', UC_SENTINEL, CODE_CONTEXTEX, 'd', 'e', 'f' };
EXPECT_EQ(CERR_ContextAndIndexInvalidInMatchNomatch, ValidateMatchNomatchOutput((PKMX_WCHAR)contextex));
const KMX_WCHAR index[] = { 'a', 'b', 'c', UC_SENTINEL, CODE_INDEX, 'd', 'e', 'f' };
EXPECT_EQ(CERR_ContextAndIndexInvalidInMatchNomatch, ValidateMatchNomatchOutput((PKMX_WCHAR)index));
const KMX_WCHAR sentinel[] = { 'a', 'b', 'c', UC_SENTINEL, 'd', 'e', 'f' };
EXPECT_EQ(CERR_None, ValidateMatchNomatchOutput((PKMX_WCHAR)sentinel));
};
// KMX_DWORD ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str)
// KMX_DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// int kmcmp::cmpkeys(const void *key, const void *elem)
// KMX_DWORD ProcessGroupFinish(PFILE_KEYBOARD fk)
// KMX_DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// bool resizeStoreArray(PFILE_KEYBOARD fk)
// bool resizeKeyArray(PFILE_GROUP gp, int increment)
// KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID)
// KMX_DWORD AddDebugStore(PFILE_KEYBOARD fk, KMX_WCHAR const * str)
// KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE sp)
// int GetCompileTargetsFromTargetsStore(const KMX_WCHAR* store)
TEST_F(CompilerTest, IsValidKeyboardVersion_test) {
EXPECT_FALSE(IsValidKeyboardVersion((KMX_WCHAR *)u""));
EXPECT_FALSE(IsValidKeyboardVersion((KMX_WCHAR *)u" "));
EXPECT_FALSE(IsValidKeyboardVersion((KMX_WCHAR *)u"\t"));
EXPECT_FALSE(IsValidKeyboardVersion((KMX_WCHAR *)u" 1.1"));
EXPECT_TRUE(IsValidKeyboardVersion((KMX_WCHAR *)u"1.1"));
EXPECT_TRUE(IsValidKeyboardVersion((KMX_WCHAR *)u"1.0"));
EXPECT_FALSE(IsValidKeyboardVersion((KMX_WCHAR *)u"1."));
EXPECT_TRUE(IsValidKeyboardVersion((KMX_WCHAR *)u"1.2.3"));
EXPECT_FALSE(IsValidKeyboardVersion((KMX_WCHAR *)u"a"));
EXPECT_FALSE(IsValidKeyboardVersion((KMX_WCHAR *)u"1.a"));
};
// KMX_DWORD kmcmp::AddCompilerVersionStore(PFILE_KEYBOARD fk)
// KMX_DWORD CheckStatementOffsets(PFILE_KEYBOARD fk, PFILE_GROUP gp, PKMX_WCHAR context, PKMX_WCHAR output, PKMX_WCHAR key)
// KMX_BOOL CheckContextStatementPositions(PKMX_WCHAR context)
// KMX_DWORD CheckUseStatementsInOutput(PKMX_WCHAR output)
// KMX_DWORD CheckVirtualKeysInOutput(PKMX_WCHAR output)
// KMX_DWORD InjectContextToReadonlyOutput(PKMX_WCHAR pklOut)
// KMX_DWORD CheckOutputIsReadonly(const PFILE_KEYBOARD fk, const PKMX_WCHAR output)
// KMX_DWORD ProcessKeyLine(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode)
// KMX_DWORD ProcessKeyLineImpl(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode, PKMX_WCHAR pklIn, PKMX_WCHAR pklKey, PKMX_WCHAR pklOut)
// KMX_DWORD ExpandKp_ReplaceIndex(PFILE_KEYBOARD fk, PFILE_KEY k, KMX_DWORD keyIndex, int nAnyIndex)
// KMX_DWORD ExpandKp(PFILE_KEYBOARD fk, PFILE_KEY kpp, KMX_DWORD storeIndex)
// PKMX_WCHAR GetDelimitedString(PKMX_WCHAR *p, KMX_WCHAR const * Delimiters, KMX_WORD Flags)
// LinePrefixType GetLinePrefixType(PKMX_WCHAR *p)
// int LineTokenType(PKMX_WCHAR *str)
// KMX_BOOL StrValidChrs(PKMX_WCHAR q, KMX_WCHAR const * chrs)
// KMX_DWORD GetXString(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_WCHAR const * token,
// PKMX_WCHAR output, int max, int offset, PKMX_WCHAR *newp, int /*isVKey*/, int isUnicode
// )
TEST_F(CompilerTest, GetXStringImpl_test) {
KMX_WCHAR tstr[128];
KMX_WCHAR str[LINESIZE];
KMX_WCHAR output[GLOBAL_BUFSIZE];
PKMX_WCHAR newp = NULL;
// CERR_BufferOverflow, max=0
EXPECT_EQ(CERR_BufferOverflow, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 0, 0, &newp, FALSE));
// CERR_None, no token
str[0] = '\0';
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_NoTokensFound, empty
u16cpy(str, u"");
EXPECT_EQ(CERR_NoTokensFound, GetXStringImpl(tstr, &fileKeyboard, str, u"c", output, 80, 0, &newp, FALSE));
// CERR_NoTokensFound, whitespace
u16cpy(str, u" ");
EXPECT_EQ(CERR_NoTokensFound, GetXStringImpl(tstr, &fileKeyboard, str, u"c", output, 80, 0, &newp, FALSE));
}
// tests strings starting with 'x' or 'd'
TEST_F(CompilerTest, GetXStringImpl_type_xd_test) {
KMX_WCHAR tstr[128];
KMX_WCHAR str[LINESIZE];
KMX_WCHAR output[GLOBAL_BUFSIZE];
PKMX_WCHAR newp = NULL;
// hex 32-bit
u16cpy(str, u"x10330"); // Gothic A
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_GothicA[] = { 0xD800, 0xDF30, 0 }; // see UTF32ToUTF16
EXPECT_EQ(0, u16cmp(tstr_GothicA, tstr));
// decimal 8-bit
u16cpy(str, u"d18");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(u"\u0012", tstr));
// hex capital 8-bit
u16cpy(str, u"X12");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(u"\u0012", tstr));
// hex 32-bit, CERR_InvalidCharacter
u16cpy(str, u"x110000");
EXPECT_EQ(CERR_InvalidCharacter, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// dk, valid
u16cpy(str, u"dk(A)");
EXPECT_EQ(0, (int)fileKeyboard.cxDeadKeyArray);
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_dk_valid[] = { UC_SENTINEL, CODE_DEADKEY, 1, 0 }; // setup deadkeys
EXPECT_EQ(0, u16cmp(tstr_dk_valid, tstr));
fileKeyboard.cxDeadKeyArray = 0;
// deadkey, valid
u16cpy(str, u"deadkey(A)");
EXPECT_EQ(0, (int)fileKeyboard.cxDeadKeyArray);
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_deadkey_valid[] = { UC_SENTINEL, CODE_DEADKEY, 1, 0 }; // setup deadkeys
EXPECT_EQ(0, u16cmp(tstr_deadkey_valid, tstr));
fileKeyboard.cxDeadKeyArray = 0;
// dk, CERR_InvalidDeadkey, bad character
u16cpy(str, u"dk(%)");
EXPECT_EQ(CERR_InvalidDeadkey, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// dk, CERR_InvalidDeadkey, no close delimiter => NULL
u16cpy(str, u"dk(");
EXPECT_EQ(CERR_InvalidDeadkey, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// dk, CERR_InvalidDeadkey, empty delimiters => empty string
u16cpy(str, u"dk()");
EXPECT_EQ(CERR_InvalidDeadkey, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
}
// tests strings starting with double quote
TEST_F(CompilerTest, GetXStringImpl_type_double_quote_test) {
KMX_WCHAR tstr[128];
KMX_WCHAR str[LINESIZE];
KMX_WCHAR output[GLOBAL_BUFSIZE];
PKMX_WCHAR newp = NULL;
// valid
u16cpy(str, u"\"abc\"");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(u"abc", tstr));
// CERR_UnterminatedString
u16cpy(str, u"\"abc");
EXPECT_EQ(CERR_UnterminatedString, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_ExtendedStringTooLong
u16cpy(str, u"\"abc\"");
EXPECT_EQ(CERR_ExtendedStringTooLong, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 2, 0, &newp, FALSE)); // max reduced to force error
// CERR_StringInVirtualKeySection *** TODO ***
}
// tests strings starting with single quote
TEST_F(CompilerTest, GetXStringImpl_type_single_quote_test) {
KMX_WCHAR tstr[128];
KMX_WCHAR str[LINESIZE];
KMX_WCHAR output[GLOBAL_BUFSIZE];
PKMX_WCHAR newp = NULL;
// valid
u16cpy(str, u"\'abc\'");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(u"abc", tstr));
// CERR_UnterminatedString
u16cpy(str, u"\'abc");
EXPECT_EQ(CERR_UnterminatedString, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_ExtendedStringTooLong
u16cpy(str, u"\'abc\'");
EXPECT_EQ(CERR_ExtendedStringTooLong, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 2, 0, &newp, FALSE)); // max reduced to force error
// CERR_StringInVirtualKeySection *** TODO ***
}
// KMX_DWORD process_baselayout(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// KMX_DWORD process_platform(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// KMX_DWORD process_if_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// KMX_DWORD process_if(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// KMX_DWORD process_reset(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// KMX_DWORD process_expansion(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx, int max)
// KMX_DWORD process_set_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// KMX_DWORD process_set(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// KMX_DWORD process_save(PFILE_KEYBOARD fk, PKMX_WCHAR q, PKMX_WCHAR tstr, int *mx)
// int xatoi(PKMX_WCHAR *p)
// int GetGroupNum(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// KMX_DWORD ProcessEthnologueStore(PKMX_WCHAR p)
// KMX_DWORD ProcessHotKey(PKMX_WCHAR p, KMX_DWORD *hk)
// void SetChecksum(PKMX_BYTE buf, PKMX_DWORD CheckSum, KMX_DWORD sz)
// KMX_BOOL kmcmp::CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, KMX_BOOL fIsStore, KMX_BOOL fIsOption, KMX_BOOL fIsCall)
// KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, KMX_BYTE**data, size_t& dataSize)
// KMX_DWORD ReadLine(KMX_BYTE* infile, int sz, int& offset, PKMX_WCHAR wstr, KMX_BOOL PreProcess)
TEST_F(CompilerTest, GetRHS_test) {
KMX_WCHAR str[LINESIZE];
KMX_WCHAR tstr[128];
// CERR_NoTokensFound, empty string
str[0] = '\0';
EXPECT_EQ(CERR_NoTokensFound, GetRHS(&fileKeyboard, str, tstr, 80, 0, FALSE));
// CERR_NoTokensFound, no '>'
u16cpy(str, u"abc");
EXPECT_EQ(CERR_NoTokensFound, GetRHS(&fileKeyboard, str, tstr, 80, 0, FALSE));
// CERR_None
u16cpy(str, u"> nul c\n");
EXPECT_EQ(CERR_None, GetRHS(&fileKeyboard, str, tstr, 80, 0, FALSE));
}
// void safe_wcsncpy(PKMX_WCHAR out, PKMX_WCHAR in, int cbMax)
// KMX_BOOL IsSameToken(PKMX_WCHAR *p, KMX_WCHAR const * token)
// static bool endsWith(const std::string& str, const std::string& suffix)
// KMX_DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PKMX_WCHAR szName, PKMX_DWORD FileSize, PKMX_BYTE *Buf)
// int atoiW(PKMX_WCHAR p)
// KMX_DWORD kmcmp::CheckUTF16(int n)
// KMX_DWORD kmcmp::UTF32ToUTF16(int n, int *n1, int *n2)
// KMX_DWORD BuildVKDictionary(PFILE_KEYBOARD fk)
// int GetVKCode(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// int GetDeadKey(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// void kmcmp::RecordDeadkeyNames(PFILE_KEYBOARD fk)
// KMX_BOOL kmcmp::IsValidCallStore(PFILE_STORE fs)
TEST_F(CompilerTest, hasPreamble_test) {
EXPECT_FALSE(hasPreamble(u""));
EXPECT_FALSE(hasPreamble(u"\uFEFE")); // not \uFEFF
EXPECT_TRUE(hasPreamble(u"\uFEFF"));
EXPECT_FALSE(hasPreamble(u"a\uFEFF"));
}
// bool UTF16TempFromUTF8(KMX_BYTE* infile, int sz, KMX_BYTE** tempfile, int *sz16)
// PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID)

View file

@ -20,7 +20,7 @@ kmcompxtest = executable('kmcompxtest', ['kmcompxtest.cpp','util_filesystem.cpp'
include_directories: inc,
name_suffix: name_suffix,
link_args: links + tests_links,
objects: lib.extract_all_objects(),
objects: lib.extract_all_objects(recursive: false),
dependencies: icuuc_dep,
)
@ -129,7 +129,7 @@ if get_option('full_test')
endif
common_test_files = [ meson.source_root() / '../../../common/include/test_color.cpp' ]
common_test_files = [ meson.global_source_root() / '../../../common/include/test_color.cpp' ]
# Test the API endpoints
@ -138,7 +138,7 @@ apitest = executable('api-test', ['api-test.cpp','util_filesystem.cpp','util_cal
include_directories: inc,
name_suffix: name_suffix,
link_args: links + tests_links,
objects: lib.extract_all_objects(),
objects: lib.extract_all_objects(recursive: false),
dependencies: icuuc_dep
)
@ -149,7 +149,7 @@ usetapitest = executable('uset-api-test', 'uset-api-test.cpp', common_test_files
include_directories: inc,
name_suffix: name_suffix,
link_args: links + tests_links,
objects: lib.extract_all_objects(),
objects: lib.extract_all_objects(recursive: false),
dependencies: icuuc_dep,
)
@ -157,6 +157,17 @@ test('uset-api-test', usetapitest)
# Google Test
gtestcompilertest = executable('gtest-compiler-test', 'gtest-compiler-test.cpp',
cpp_args: defns + flags,
include_directories: inc,
name_suffix: name_suffix,
link_args: links + tests_links,
objects: lib.extract_all_objects(),
dependencies: [ icuuc_dep, gtest_dep, gmock_dep ],
)
test('gtest-compiler-test', gtestcompilertest)
gtestcompmsgtest = executable('gtest-compmsg-test', 'gtest-compmsg-test.cpp',
cpp_args: defns + flags,
include_directories: inc,
@ -166,4 +177,4 @@ gtestcompmsgtest = executable('gtest-compmsg-test', 'gtest-compmsg-test.cpp',
dependencies: [ icuuc_dep, gtest_dep, gmock_dep ],
)
test('gtest-compmsg-test', gtestcompmsgtest)
test('gtest-compmsg-test', gtestcompmsgtest)

View file

@ -453,13 +453,13 @@ type
procedure ConfirmSaveOfOldEditorWindows;
procedure ConfirmSaveOfOldEditorWindow(FeatureID: TKeyboardParser_FeatureID;
FModified: Boolean; const FOldFilename: string; DoSave: TProc; DoLoad: TProc<String>);
procedure LoadFeature(ID: TKeyboardParser_FeatureID);
function LoadFeature(ID: TKeyboardParser_FeatureID): Boolean;
function FeatureTab(kf: TKeyboardParser_FeatureID): TTabSheet;
procedure InitFeatureTab(ID: TKeyboardParser_FeatureID);
procedure FeatureModified(Sender: TObject);
function SaveFeature(ID: TKeyboardParser_FeatureID): Boolean;
procedure SelectTouchLayoutTemplate(APromptChange: Boolean);
procedure LoadTouchLayout; // I4034
function LoadTouchLayout: Boolean; // I4034
function GetFontInfo(Index: TKeyboardFont): TKeyboardFontInfo; // I4057
procedure SetFontInfo(Index: TKeyboardFont; const Value: TKeyboardFontInfo); // I4057
@ -1628,7 +1628,7 @@ begin
FLayoutSetup := FOldLayoutSetup;
end;
procedure TfrmKeymanWizard.LoadFeature(ID: TKeyboardParser_FeatureID);
function TfrmKeymanWizard.LoadFeature(ID: TKeyboardParser_FeatureID): Boolean;
begin
if FKeyboardParser.Features.ContainsKey(ID) then
begin
@ -1651,7 +1651,8 @@ begin
end;
kfTouchLayout:
begin
LoadTouchLayout; // I4034
if not LoadTouchLayout then
Exit(False);
end;
else
begin
@ -1667,6 +1668,7 @@ begin
end;
end;
FFeature[ID].Modified := False;
Result := True;
end;
function TfrmKeymanWizard.SaveFeature(ID: TKeyboardParser_FeatureID): Boolean;
@ -2007,7 +2009,12 @@ begin
LoadSettings;
for kf in FKeyboardParser.Features.Keys do
LoadFeature(kf);
begin
if not LoadFeature(kf) then
begin
Exit(False);
end;
end;
if FKeyboardParser.IsComplex then // I4557
pagesLayout.ActivePage := pageLayoutCode;
@ -3163,18 +3170,17 @@ begin
FFeature[kfTouchLayout].Modified := True;
end;
procedure TfrmKeymanWizard.LoadTouchLayout; // I4034
function TfrmKeymanWizard.LoadTouchLayout: Boolean; // I4034
begin
if pagesTouchLayout.ActivePage = pageTouchLayoutDesign then
begin
if not frameTouchLayout.Load(FFeature[kfTouchLayout].Filename, False, False) then
begin
pagesTouchLayout.ActivePage := pageTouchLayoutCode;
frameTouchLayoutSource.LoadFromFile(FFeature[kfTouchLayout].Filename, tffUTF8);
end;
Result := frameTouchLayout.Load(FFeature[kfTouchLayout].Filename, False, False);
end
else
begin
frameTouchLayoutSource.LoadFromFile(FFeature[kfTouchLayout].Filename, tffUTF8);
Result := True;
end;
end;
procedure TfrmKeymanWizard.SaveTouchLayout; // I3885

View file

@ -1477,8 +1477,12 @@ begin
if n >= 0 then
Result.ProjectFile := FGlobalProject.Files[n];
(Result as frmClass).OpenFile(FFileName);
LockWindowUpdate(0);
if not (Result as frmClass).OpenFile(FFileName) then
begin
Result.Release;
end;
end;
procedure TfrmKeymanDeveloper.HelpTopic(s: string);

View file

@ -90,7 +90,7 @@ type
procedure cefCommand(Sender: TObject; const command: string; params: TStringList);
procedure cefLoadEnd(Sender: TObject);
procedure RegisterSource;
procedure RegisterSources(const AState: string);
procedure CharMapDragDrop(Sender, Source: TObject; X, Y: Integer);
procedure CharMapDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
@ -238,10 +238,12 @@ begin
modWebHttpServer.AppSource.UnregisterSource(FFilename+'#state');
end;
procedure TframeTouchLayoutBuilder.RegisterSource;
procedure TframeTouchLayoutBuilder.RegisterSources(const AState: string);
begin
if FFilename <> '' then
modWebHttpServer.AppSource.RegisterSource(FFilename, FSavedLayoutJS);
if (FFileName <> '') and (AState <> '') then
modWebHttpServer.AppSource.RegisterSource(FFilename + '#state', AState, True);
end;
procedure TframeTouchLayoutBuilder.ImportFromKVK(const KVKFileName: string); // I3945
@ -332,71 +334,85 @@ begin
end;
UnregisterSources;
try
if ALoadFromString then
if ALoadFromString then
begin
FNewLayoutJS := AFilename;
FFilename := GetNextFilename;
end
else
begin
if ALoadFromTemplate or (AFileName = '') or not FileExists(AFileName) then
begin
FNewLayoutJS := AFilename;
FBaseFileName := FTemplateFileName;
FFilename := GetNextFilename;
end
else
begin
if ALoadFromTemplate or (AFileName = '') or not FileExists(AFileName) then
begin
FBaseFileName := FTemplateFileName;
FFilename := GetNextFilename;
end
else
begin
FBaseFileName := AFileName;
FFilename := AFileName;
end;
with TStringList.Create do
try
LoadFromFile(FBaseFileName, TEncoding.UTF8);
FNewLayoutJS := Text;
finally
Free;
end;
FBaseFileName := AFileName;
FFilename := AFileName;
end;
FTouchLayout := TTouchLayout.Create; // I3642
with TStringList.Create do
try
if not FTouchLayout.Load(FNewLayoutJS) then
LoadFromFile(FBaseFileName, TEncoding.UTF8);
FNewLayoutJS := Text;
finally
Free;
end;
end;
FTouchLayout := TTouchLayout.Create; // I3642
try
if not FTouchLayout.Load(FNewLayoutJS) then
begin
FLastError := FTouchLayout.LoadError; // I4083
FLastErrorOffset := FTouchLayout.LoadErrorOffset; // I4083
FFilename := FLastFilename;
RegisterSources(FState);
Exit(False);
end
else
begin
if (FSavedLayoutJS <> '') and ALoadFromTemplate then
begin
FLastError := FTouchLayout.LoadError; // I4083
FLastErrorOffset := FTouchLayout.LoadErrorOffset; // I4083
FFilename := FLastFilename;
Exit(False);
FOldLayout := TTouchLayout.Create;
try
FOldLayout.Load(FSavedLayoutJS);
if FTouchLayout.Merge(FOldLayout)
then FSavedLayoutJS := FTouchLayout.Save(False)
else FSavedLayoutJS := FNewLayoutJS;
finally
FOldLayout.Free;
end;
end
else
begin
if (FSavedLayoutJS <> '') and ALoadFromTemplate then
begin
FOldLayout := TTouchLayout.Create;
try
FOldLayout.Load(FSavedLayoutJS);
if FTouchLayout.Merge(FOldLayout)
then FSavedLayoutJS := FTouchLayout.Save(False)
else FSavedLayoutJS := FNewLayoutJS;
finally
FOldLayout.Free;
end;
end
else
FSavedLayoutJS := FNewLayoutJS;
end;
finally
FTouchLayout.Free;
FSavedLayoutJS := FNewLayoutJS;
end;
finally
RegisterSource;
if (FFileName <> '') and (FState <> '') then
modWebHttpServer.AppSource.RegisterSource(FFilename + '#state', FState, True);
FTouchLayout.Free;
end;
if (FFileName <> '') and modWebHttpServer.AppSource.IsSourceRegistered(FFileName) then
begin
// If two .kmn files are loaded which both reference the same
// .keyman-touch-layout file, it's safest to just block it. This is a rare
// scenario, as most keyboard projects have a single .kmn, and it usually
// indicates a project may be in a bit of chaos anyway.
ShowMessage(
'The touch layout is already opened for editing in another keyboard '+
'editor. Please close the other keyboard editor before opening this one '+
'again.');
// We want to prevent this window unregistering the sources it doesn't own
// when it is destroyed immediately after this, which we can do by blanking
// the filename.
FFileName := '';
Exit(False);
end;
RegisterSources(FState);
try
DoLoad;
except

View file

@ -2,6 +2,10 @@
<?xml-stylesheet type="text/xsl" href="help.xsl" ?>
<ContextHelp>
<LanguageReference Name="group">
<p>(TODO: add documentation on group)</p>
</LanguageReference>
<Form Name="context/character-map">
<Control Name="editFilter" Title="Character Map Filter">
<p>The Filter allows a user to reduce the number of characters displayed in the character map. The standard filter options used are by font name or block name.</p>
@ -12,63 +16,63 @@
</Control>
<Control Name="cmdFilter" Title="Character Map Filter">
<p>The Filter allows a user to reduce the number of characters displayed in the
<p>The Filter allows a user to reduce the number of characters displayed in the
character map. The standard filter options used are by font name or block name.</p>
</Control>
<Control Name="rbRange" Title="Character Map Filter">
<p>The filter format for a range is: [U+]XXXX-[U+]YYYY, where U+ is optional,
<p>The filter format for a range is: [U+]XXXX-[U+]YYYY, where U+ is optional,
XXXX is the starting Unicode value and YYYY is the finishing Unicode value.</p>
</Control>
<Control Name="editRangeStart" Title="Character Map Filter">
<p>The filter format for a range is: [U+]XXXX-[U+]YYYY, where U+ is optional,
<p>The filter format for a range is: [U+]XXXX-[U+]YYYY, where U+ is optional,
XXXX is the starting Unicode value and YYYY is the finishing Unicode value.</p>
</Control>
<Control Name="editRangeStop" Title="Character Map Filter">
<p>The filter format for a range is: [U+]XXXX-[U+]YYYY, where U+ is optional,
<p>The filter format for a range is: [U+]XXXX-[U+]YYYY, where U+ is optional,
XXXX is the starting Unicode value and YYYY is the finishing Unicode value.</p>
</Control>
<Control Name="rbName" Title="Character Map Filter">
<p>The Filter allows a user to reduce the number of characters displayed in the
<p>The Filter allows a user to reduce the number of characters displayed in the
character map. The standard filter options used are by font name or block name.</p>
</Control>
<Control Name="cmdCharactersInCurrentFontOnly" Title="Character Map Filter">
<p> "&gt;" placed at the start of an entry will only show characters in the currently
selected Character Map font. This is helpful when trying to determine which characters
<p> "&gt;" placed at the start of an entry will only show characters in the currently
selected Character Map font. This is helpful when trying to determine which characters
a given font supports.</p><br></br>
<p>Example: &gt;LAO </p><br></br>
<p>finds all characters with names starting in "LAO" in the current font</p>
</Control>
<Control Name="cmdBlock" Title="Character Map Filter">
<p> "&lt;" placed at the start of an entry will search Unicode block names instead of
character names. This is helpful when searching for characters within related
<p> "&lt;" placed at the start of an entry will search Unicode block names instead of
character names. This is helpful when searching for characters within related
blocks</p><br></br>
<p>Example: &lt;Thai </p><br></br>
<p>finds the Thai Unicode block</p>
</Control>
<Control Name="cmdStar" Title="Character Map Filter">
<p>Using "*" in an entry serves as a wildcard for any number of places in that entry.
For example, searching for "greek*alpha" will find characters whose Unicode names begin
with the word "Greek" and contain the word "Alpha" any number of places later.
This is helpful when searching for characters that share a common element in
<p>Using "*" in an entry serves as a wildcard for any number of places in that entry.
For example, searching for "greek*alpha" will find characters whose Unicode names begin
with the word "Greek" and contain the word "Alpha" any number of places later.
This is helpful when searching for characters that share a common element in
their names (e.g. capital).</p>
</Control>
<Control Name="cmdQuestionMark" Title="Character Map Filter">
<p>Using "?" anywhere in an entry serves as a wildcard for that single place in the entry.
For example, searching for "s???e" will return both the SPACE and the SMILE characters,
<p>Using "?" anywhere in an entry serves as a wildcard for that single place in the entry.
For example, searching for "s???e" will return both the SPACE and the SMILE characters,
among others.</p><br></br>
</Control>
<Control Name="cmdRange" Title="Character Map Filter">
<p>Example: 1000-119F </p><br></br>
<p>finds all characters between U+1000 and U+119F (inclusive) -
<p>finds all characters between U+1000 and U+119F (inclusive) -
the Myanmar alphabet in this case</p>
</Control>
@ -78,7 +82,7 @@
</Control>
<Control Name="cmdDollar" Title="Character Map Filter">
<p>"$" placed at the end of an entry will match from the end of a Unicode character name.
<p>"$" placed at the end of an entry will match from the end of a Unicode character name.
This option works best when used with "*" or "?".</p><br></br>
<p>Example: LATIN * LETTER A$ </p><br></br>
<p>finds only "a" and "A"</p>
@ -471,18 +475,18 @@
<Form Name="context/new-model-project-parameters">
<Control Name="editAuthor" Title="Author Name">
<p>The name of the developer of the keyboard. This is either your full name or
<p>The name of the developer of the keyboard. This is either your full name or
the organization you're creating a model for.</p>
</Control>
<Control Name="editModelName" Title="Model Name">
<p>We recommend the name of the language, dialect, or community that this model is
<p>We recommend the name of the language, dialect, or community that this model is
intended for. The name must be written in all the Latin letters or Arabic numerals.</p>
</Control>
<Control Name="editCopyright" Title="Copyright">
<p>Who owns the rights to this model and its data? Typically,
you can use the automatically generated default value: © 2024 Your Full Name or
<p>Who owns the rights to this model and its data? Typically,
you can use the automatically generated default value: © 2024 Your Full Name or
Your Organization.</p>
</Control>
@ -491,18 +495,18 @@
</Control>
<Control Name="editVersion" Title="Version">
<p>If this is the first time you've created a lexical model for you language, you should
leave the version as 1.0. Otherwise, your version number must conform to the following
<p>If this is the first time you've created a lexical model for you language, you should
leave the version as 1.0. Otherwise, your version number must conform to the following
rules: A version string made of major revision number.minor revision number.</p>
</Control>
<Control Name="gridLanguages" Title="Languages">
<p>Specifies the default BCP 47 language tags which will be added to the package
<p>Specifies the default BCP 47 language tags which will be added to the package
metadata and project metadata.</p>
</Control>
<Control Name="cmdAddLanguage" Title="Add...">
<p>To add a language tag, click the Add button to bring up the “Select BCP 47 Tag”
<p>To add a language tag, click the Add button to bring up the “Select BCP 47 Tag”
dialog box.</p>
</Control>
@ -524,7 +528,7 @@
</Control>
<Control Name="editAuthorID" Title="Author ID">
<p>An Author ID is a unique identifier used to distinguish you from others
<p>An Author ID is a unique identifier used to distinguish you from others
who have the same or similar names. </p>
</Control>
@ -533,13 +537,13 @@
</Control>
<Control Name="editUniq" Title="Unique Name">
<p>Enter a unique name of the model. You can use the name of the language, dialect,
<p>Enter a unique name of the model. You can use the name of the language, dialect,
or community that this model is intended for.</p>
</Control>
<Control Name="editModelID" Title="Model ID">
<p>Keyman automatically generates a model ID for you, given all the
information already filled out. Model ID helps Keyman sorts and organizes
<p>Keyman automatically generates a model ID for you, given all the
information already filled out. Model ID helps Keyman sorts and organizes
different lexical models.</p>
</Control>
@ -558,15 +562,15 @@
<Form Name="context/wordlist-editor">
<Control Name="pages" Title="Wordlist tabs">
<p>Wordlist tabs have two views: Design, and Code. Changes to one view are reflected
immedaitely in the other view. Wordlist files should be stored in UTF-8 encoding
<p>Wordlist tabs have two views: Design, and Code. Changes to one view are reflected
immedaitely in the other view. Wordlist files should be stored in UTF-8 encoding
(preferably without BOM), and tab-separated format.</p>
</Control>
<Control Name="gridWordlist" Title="Wordlist tabs">
<p>Every line of the tab-separated format file is shown here, and can be edited directly.
For most wordlists, it will be more effective to use an external dictionary tool,
such as SIL Fieldworks or SIL PrimerPrep to generate the wordlist from a text corpus,
<p>Every line of the tab-separated format file is shown here, and can be edited directly.
For most wordlists, it will be more effective to use an external dictionary tool,
such as SIL Fieldworks or SIL PrimerPrep to generate the wordlist from a text corpus,
and use this tab just to preview the contents of the file.</p>
</Control>
@ -575,24 +579,24 @@
</Control>
<Control Name="cmdSortByFrequency" Title="Wordlist tabs">
<p>The Sort by frequency button has no effect on the functioning of the wordlist,
<p>The Sort by frequency button has no effect on the functioning of the wordlist,
but can help you, the editor, by showing more common words earlier in the list.</p>
</Control>
</Form>
<Form Name="context/editor">
<Control Name="cefwp" Title="Editor Window">
<p>Editor windows in Keyman Developer supports standard Windows editing keystrokes.
Many file formats, including .kmn, .kps, .xml, .html, .js and .json, support syntax
highlighting. The text editor in Keyman uses the Monaco component from Visual Studio Code,
<p>Editor windows in Keyman Developer supports standard Windows editing keystrokes.
Many file formats, including .kmn, .kps, .xml, .html, .js and .json, support syntax
highlighting. The text editor in Keyman uses the Monaco component from Visual Studio Code,
so all the functionality available in that editor is also available here.</p>
</Control>
</Form>
<Form Name="context/character-identifier">
<Control Name="gridFonts" Title="Character Identifier">
<p>Attempt to identify the fonts on your system that will support the
characters. You can quickly change fonts by clicking on a font name in the grid of
<p>Attempt to identify the fonts on your system that will support the
characters. You can quickly change fonts by clicking on a font name in the grid of
identified fonts.</p>
</Control>
@ -603,47 +607,47 @@
<Form Name="context/messages">
<Control Name="memoMessage" Title="Message Window">
<p>The message window appears at the bottom of the screen, or floating in a toolbar
window. It contains a list of error and warning messages returned from a compilation
<p>The message window appears at the bottom of the screen, or floating in a toolbar
window. It contains a list of error and warning messages returned from a compilation
session. You can undock and dock the window by dragging its title bar.</p>
</Control>
</Form>
<Form Name="context/debug">
<Control Name="memo" Title="Debugger input window">
<p>The debugger input window is used for typing input to test the keyboard.
In the top half of this window, input you type while testing your keyboard will be
displayed, exactly the same as in use, with one exception: deadkeys will be shown
<p>The debugger input window is used for typing input to test the keyboard.
In the top half of this window, input you type while testing your keyboard will be
displayed, exactly the same as in use, with one exception: deadkeys will be shown
visually with an OBJ symbol.</p>
</Control>
<Control Name="sgChars" Title="Debugger input window">
<p>The lower half of the window shows a grid of the characters to the virtual left
of the insertion point, or the selected characters if you make a selection. Deadkeys
will be identified in the grid. The grid will show characters in right-to-left scripts
in backing store order, from left to right. If there are more characters in your text
<p>The lower half of the window shows a grid of the characters to the virtual left
of the insertion point, or the selected characters if you make a selection. Deadkeys
will be identified in the grid. The grid will show characters in right-to-left scripts
in backing store order, from left to right. If there are more characters in your text
than can fit on the screen, then only those that fit will be shown in the grid.</p>
</Control>
</Form>
<Form Name="context/debug#toc-regression-testing">
<Control Name="lbRegTestLog" Title="Regression Testing">
<p>The idea in regression testing is to record a sequence of keystrokes and the
output the keyboard produced, in order to test for the same behaviour when you
<p>The idea in regression testing is to record a sequence of keystrokes and the
output the keyboard produced, in order to test for the same behaviour when you
make changes to the keyboard.</p>
</Control>
<Control Name="cmdRegTestStartStopLog" Title="Regression Testing">
<p>Use Start Log/Stop Log to record the input and output. You can then use Start Test
to run the test again, or go the Options menu to clear the log, or save or load a test,
<p>Use Start Log/Stop Log to record the input and output. You can then use Start Test
to run the test again, or go the Options menu to clear the log, or save or load a test,
or use the batch mode to run several tests in a row. </p><br></br>
<p>If the output produced while running a test is different to that stored when recording it,
Keyman will halt the test on the line where the failure occurred, and activate
<p>If the output produced while running a test is different to that stored when recording it,
Keyman will halt the test on the line where the failure occurred, and activate
Single Step mode.</p>
</Control>
<Control Name="cmdRegTestOptions" Title="Regression Testing">
<p>In the Options menu, you can clear the log, save or load a test,
<p>In the Options menu, you can clear the log, save or load a test,
or use the batch mode to run several tests in a row.</p>
</Control>
</Form>
@ -655,39 +659,39 @@
</Control>
<Control Name="lbKeystrokeLog" Title="State">
<p>This window shows the current keystroke state, and the sequence of
<p>This window shows the current keystroke state, and the sequence of
keystrokes that were typed to arrive at this state. </p>
</Control>
</Form>
<Form Name="context/debug#toc-elements">
<Control Name="lvElements" Title="Elements">
<p>This shows the elements that make up rule currently being processed: the context,
the key, and also what the output will be. If the rule uses stores, the contents of
<p>This shows the elements that make up rule currently being processed: the context,
the key, and also what the output will be. If the rule uses stores, the contents of
the store will be shown in the right-hand column, with the matched letter in red.</p>
</Control>
</Form>
<Form Name="context/debug#toc-call-stack">
<Control Name="lbCallStack" Title="Call Stack">
<p>Here all the lines that have been processed to this point are shown in a list.
You can double-click on any entry in the list to display the line in the
<p>Here all the lines that have been processed to this point are shown in a list.
You can double-click on any entry in the list to display the line in the
keyboard source.</p>
</Control>
</Form>
<Form Name="context/debug#toc-deadkeys">
<Control Name="lbDeadkeys" Title="Deadkeys">
<p>This lists all the deadkeys that are currently in the context.
You can select one from the list to see it highlighted in the debug input box.
This information can also be seen in the character grid in the lower half of
<p>This lists all the deadkeys that are currently in the context.
You can select one from the list to see it highlighted in the debug input box.
This information can also be seen in the character grid in the lower half of
the debugger input window.</p>
</Control>
</Form>
<Form Name="context/about-tike">
<Control Name="cmdOK" Title="About Dialog">
<p>The About dialog displays copyright and registration information for Keyman Developer,
<p>The About dialog displays copyright and registration information for Keyman Developer,
and has a link to the Keyman website.</p>
</Control>
</Form>

View file

@ -53,6 +53,7 @@
</script>
</head>
<body>
<xsl:apply-templates select="//LanguageReference" />
<xsl:apply-templates select="//Control" />
<div class="control" id="nohelp">
Context help is not available for <b><span id="nohelp_name"></span></b>.
@ -66,6 +67,18 @@
<xsl:apply-templates select="Control" />
</xsl:template>
<xsl:template match="//LanguageReference">
<div class="control">
<xsl:attribute name="id">language/reference/<xsl:value-of select="@Name"/></xsl:attribute>
<div class="title"><xsl:value-of select="@Title"/></div>
<xsl:copy-of select="." />
<div class="morehelp"><a>
<xsl:attribute name='href'>help:language/reference/<xsl:value-of select="@Name"/></xsl:attribute>
More help...
</a></div>
</div>
</xsl:template>
<xsl:template match="//Control">
<div class="control">
<xsl:attribute name="id"><xsl:value-of select="../@Name"/>-<xsl:value-of select="@Name"/></xsl:attribute>

View file

@ -6,7 +6,7 @@ Source code for Keyman engine for IBus is in [linux/ibus-keyman](../../linux/ibu
## Requirements
meson (>= 0.57)
meson (>= 1.0)
## Building

View file

@ -19,7 +19,7 @@ Build-Depends:
libjson-glib-dev (>= 1.4.0),
liblocale-gettext-perl,
libsystemd-dev,
meson (>= 0.53),
meson (>= 1.0),
metacity,
ninja-build,
perl,

View file

@ -1,7 +1,7 @@
project('ibus-keyman', 'c', 'cpp',
version: run_command('cat', '../../VERSION.md', check: true).stdout().strip(),
license: 'GPL-2+',
meson_version: '>=0.53.0')
meson_version: '>=1.0')
cc = meson.get_compiler('c')
conf = configuration_data()

View file

@ -1,7 +1,7 @@
project('keyman-system-service', 'c', 'cpp',
version: run_command('cat', '../../VERSION.md', check: true).stdout().strip(),
license: 'GPL-2+',
meson_version: '>=0.61')
meson_version: '>=1.0')
evdev = dependency('libevdev', version: '>= 1.9')
systemd = dependency('libsystemd')

View file

@ -8,9 +8,9 @@ import { SourcemappedWorker } from '@keymanapp/lexical-model-layer/web'
* This can only be done during load when the active script will be the
* last script loaded. Otherwise the script must be identified by name.
*/
var scripts = document.getElementsByTagName('script');
var ss = scripts[scripts.length-1].src;
var sPath = ss.substr(0,ss.lastIndexOf('/')+1);
const scripts = document.getElementsByTagName('script');
const ss = scripts[scripts.length-1].src;
const sPath = ss.substring(0,ss.lastIndexOf('/')+1);
// @ts-ignore
window['keyman'] = new KeymanEngine(SourcemappedWorker.constructInstance(), sPath);

View file

@ -8,9 +8,9 @@ import { Worker } from '@keymanapp/lexical-model-layer/web'
* This can only be done during load when the active script will be the
* last script loaded. Otherwise the script must be identified by name.
*/
var scripts = document.getElementsByTagName('script');
var ss = scripts[scripts.length-1].src;
var sPath = ss.substr(0,ss.lastIndexOf('/')+1);
const scripts = document.getElementsByTagName('script');
const ss = scripts[scripts.length-1].src;
const sPath = ss.substring(0,ss.lastIndexOf('/')+1);
// @ts-ignore
window['keyman'] = new KeymanEngine(Worker.constructInstance(), sPath);